Fossil SCM
Sync with trunk.
Commit
611f20e81708ee77c0d6a7b6103cb0723aa51731e8a6ec154fd4e0d45e6f74cd
Parent
00f7466addc2521…
157 files changed
+1
-1
+4
-1
+1
-1
+7
+5
-3
+95
-28
+722
-241
+3
-3
+1
-1
+3370
-1991
+2361
-1405
+80
-13
+22
-17
+4
-1
+7
-6
+1
-1
+23
-6
+8
-1
+12
-4
+49
-1
+4
-3
-17
+3
-1
+9
-3
+114
-14
+221
-71
+28
-9
+13
-14
+52
-10
+52
-10
+50
-48
+146
-7
+1
-1
+8
-8
+26
-1
+2
+1
-1
+99
-9
+13
-5
+63
-35
+25
-5
+3
-1
+1
-1
+15
-3
+54
-6
+2
-2
+6
+4
-6
+4
-1
+152
-17
+2
-1
+16
-4
+13
-10
+2
-1
+7
-3
+465
-6
+581
+804
-168
+6
-2
+1
+12
-2
+9
-3
+4
-5
+7
-7
+83
-64
+16
-4
+7
-5
+5
-40
+9
+2
-1
+2
-1
+1
-1
+55
-3
+4
-4
+229
+40
-1
+20
+20
-20
+6
+2
+1
+2
-1
+1
-1
+1
+2
+4
-4
+3
-3
+2
-2
+2
-2
+3
-3
+2
-2
+6
-6
+2
-2
+1
-1
+1
-1
+2
-2
+26
-4
+2
-2
+1
-1
+4
-4
+1
-1
+10
-8
+13
-5
+3
-3
+1
-1
+3
-3
+6
-6
+1
-1
+1
-1
+9
-10
+1
-1
+3
-3
+2
-2
+1
-1
+1
-1
+1
-1
+1
-1
+4
-4
+4
-4
+2
-2
+5
-5
+1
-1
+3
-3
+1
-1
+1
-1
+2
-2
+2
-2
+8
-8
+7
-7
+10
-9
+1
-1
+2
-1
+6
-2
+4
-4
+2
-2
+1
-1
+1
-1
+2
-2
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+2
-3
+2
-2
+2
-2
+6
-5
+33
-13
+1
-1
+5
-5
+2
-2
+2
-2
+2
-2
+1
-1
+5
-5
+7
-4
+1
-1
~
BUILD.txt
~
Makefile.in
~
VERSION
~
auto.def
~
autosetup/autosetup
~
autosetup/autosetup-config.guess
~
autosetup/autosetup-config.sub
~
autosetup/autosetup-find-tclsh
~
autosetup/cc.tcl
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
fossil.1
~
skins/darkmode/css.txt
~
skins/default/css.txt
~
src/add.c
~
src/allrepo.c
~
src/attach.c
~
src/bisect.c
~
src/blob.c
~
src/branch.c
~
src/browse.c
~
src/builtin.c
~
src/chat.c
~
src/checkin.c
~
src/comformat.c
~
src/configure.c
~
src/db.c
~
src/default.css
~
src/default.css
~
src/delta.c
~
src/diff.c
~
src/diff.tcl
~
src/diffcmd.c
~
src/dispatch.c
~
src/doc.c
~
src/event.c
~
src/file.c
~
src/finfo.c
~
src/forum.c
~
src/fossil.diff.js
~
src/fossil.dom.js
~
src/fossil.page.chat.js
~
src/fossil.page.pikchrshowasm.js
~
src/fossil.pikchr.js
~
src/glob.c
~
src/http.c
~
src/http_ssl.c
~
src/http_transport.c
~
src/info.c
~
src/interwiki.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/manifest.c
~
src/merge.c
~
src/merge.tcl
~
src/merge3.c
~
src/patch.c
~
src/path.c
~
src/pikchrshow.c
~
src/rebuild.c
~
src/rss.c
~
src/schema.c
~
src/search.c
~
src/setup.c
~
src/setupuser.c
~
src/sitemap.c
~
src/th_tcl.c
~
src/timeline.c
~
src/timeline.c
~
src/tkt.c
~
src/update.c
~
src/wiki.c
~
src/winfile.c
~
src/xfer.c
~
test/comment.test
~
test/glob.test
~
test/settings.test
~
test/tester.tcl
~
test/utf8-comment.txt
~
tools/makemake.tcl
~
tools/man_page_command_list.tcl
~
win/Makefile.mingw
~
win/Makefile.msc
~
www/aboutcgi.wiki
~
www/adding_code.wiki
~
www/alerts.md
~
www/antibot.wiki
~
www/backoffice.md
~
www/blame.wiki
~
www/bugtheory.wiki
~
www/build.wiki
~
www/caps/index.md
~
www/caps/ref.html
~
www/cgi.wiki
~
www/changes.wiki
~
www/chat.md
~
www/checkin_names.wiki
~
www/concepts.wiki
~
www/contribute.wiki
~
www/custom_ticket.wiki
~
www/embeddeddoc.wiki
~
www/env-opts.md
~
www/event.wiki
~
www/fileedit-page.md
~
www/fileformat.wiki
~
www/forum.wiki
~
www/fossil-is-not-relational.md
~
www/fossil-v-git.wiki
~
www/fossil_prompt.wiki
~
www/gitusers.md
~
www/glossary.md
~
www/grep.md
~
www/hashpolicy.wiki
~
www/hints.wiki
~
www/history.md
~
www/hooks.md
~
www/index.wiki
~
www/inout.wiki
~
www/interwiki.md
~
www/json-api/api-artifact.md
~
www/json-api/api-auth.md
~
www/json-api/api-checkout.md
~
www/json-api/api-finfo.md
~
www/json-api/api-query.md
~
www/json-api/api-tag.md
~
www/json-api/conventions.md
~
www/json-api/hacking.md
~
www/makefile.wiki
~
www/mdtest/test1.md
~
www/newrepo.wiki
~
www/password.wiki
~
www/patchcmd.md
~
www/pikchr.md
~
www/qandc.wiki
~
www/quickstart.wiki
~
www/rebaseharm.md
~
www/selfcheck.wiki
~
www/selfhost.wiki
~
www/server/debian/service.md
~
www/server/openbsd/fastcgi.md
~
www/server/windows/service.md
~
www/settings.wiki
~
www/shunning.wiki
~
www/stats.wiki
~
www/style.wiki
~
www/sync.wiki
~
www/tech_overview.wiki
~
www/th1.md
~
www/theory1.wiki
~
www/tickets.wiki
~
www/unvers.wiki
~
www/userlinks.wiki
~
www/webui.wiki
~
www/whyallinone.md
~
www/wikitheory.wiki
+1
-1
| --- BUILD.txt | ||
| +++ BUILD.txt | ||
| @@ -42,11 +42,11 @@ | ||
| 42 | 42 | mkdir build |
| 43 | 43 | cd build |
| 44 | 44 | ../configure |
| 45 | 45 | make |
| 46 | 46 | |
| 47 | -This will now keep all generates files separate from the maintained | |
| 47 | +This will now keep all generated files separate from the maintained | |
| 48 | 48 | source code. |
| 49 | 49 | |
| 50 | 50 | -------------------------------------------------------------------------- |
| 51 | 51 | |
| 52 | 52 | Here are some notes on what is happening behind the scenes: |
| 53 | 53 |
| --- BUILD.txt | |
| +++ BUILD.txt | |
| @@ -42,11 +42,11 @@ | |
| 42 | mkdir build |
| 43 | cd build |
| 44 | ../configure |
| 45 | make |
| 46 | |
| 47 | This will now keep all generates files separate from the maintained |
| 48 | source code. |
| 49 | |
| 50 | -------------------------------------------------------------------------- |
| 51 | |
| 52 | Here are some notes on what is happening behind the scenes: |
| 53 |
| --- BUILD.txt | |
| +++ BUILD.txt | |
| @@ -42,11 +42,11 @@ | |
| 42 | mkdir build |
| 43 | cd build |
| 44 | ../configure |
| 45 | make |
| 46 | |
| 47 | This will now keep all generated files separate from the maintained |
| 48 | source code. |
| 49 | |
| 50 | -------------------------------------------------------------------------- |
| 51 | |
| 52 | Here are some notes on what is happening behind the scenes: |
| 53 |
+4
-1
| --- Makefile.in | ||
| +++ Makefile.in | ||
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | BCCFLAGS = @CPPFLAGS@ $(CFLAGS) |
| 52 | 52 | TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H |
| 53 | 53 | # |
| 54 | 54 | # Fuzzing may be enable by appending -fsanitize=fuzzer -DFOSSIL_FUZZ |
| 55 | 55 | # to the TCCFLAGS variable. |
| 56 | -# For more thorouth (but also slower) investigation | |
| 56 | +# For more thorough (but also slower) investigation | |
| 57 | 57 | # -fsanitize=fuzzer,undefined,address |
| 58 | 58 | # might be more useful. |
| 59 | 59 | |
| 60 | 60 | INSTALLDIR = $(DESTDIR)@prefix@/bin |
| 61 | 61 | USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ |
| @@ -92,10 +92,13 @@ | ||
| 92 | 92 | MAKE_COMPILATION_DB = @MAKE_COMPILATION_DB@ |
| 93 | 93 | |
| 94 | 94 | .PHONY: all tags |
| 95 | 95 | |
| 96 | 96 | include $(SRCDIR)/main.mk |
| 97 | +SQLITE_OPTIONS += @SQLITE_OPTIONS_EXT@ | |
| 98 | +SHELL_OPTIONS += @SQLITE_OPTIONS_EXT@ | |
| 99 | +# ^^^ must come after main.mk is included | |
| 97 | 100 | |
| 98 | 101 | distclean: clean |
| 99 | 102 | -rm -f autoconfig.h config.log Makefile |
| 100 | 103 | -rm -f cscope.out tags |
| 101 | 104 | |
| 102 | 105 |
| --- Makefile.in | |
| +++ Makefile.in | |
| @@ -51,11 +51,11 @@ | |
| 51 | BCCFLAGS = @CPPFLAGS@ $(CFLAGS) |
| 52 | TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H |
| 53 | # |
| 54 | # Fuzzing may be enable by appending -fsanitize=fuzzer -DFOSSIL_FUZZ |
| 55 | # to the TCCFLAGS variable. |
| 56 | # For more thorouth (but also slower) investigation |
| 57 | # -fsanitize=fuzzer,undefined,address |
| 58 | # might be more useful. |
| 59 | |
| 60 | INSTALLDIR = $(DESTDIR)@prefix@/bin |
| 61 | USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ |
| @@ -92,10 +92,13 @@ | |
| 92 | MAKE_COMPILATION_DB = @MAKE_COMPILATION_DB@ |
| 93 | |
| 94 | .PHONY: all tags |
| 95 | |
| 96 | include $(SRCDIR)/main.mk |
| 97 | |
| 98 | distclean: clean |
| 99 | -rm -f autoconfig.h config.log Makefile |
| 100 | -rm -f cscope.out tags |
| 101 | |
| 102 |
| --- Makefile.in | |
| +++ Makefile.in | |
| @@ -51,11 +51,11 @@ | |
| 51 | BCCFLAGS = @CPPFLAGS@ $(CFLAGS) |
| 52 | TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H |
| 53 | # |
| 54 | # Fuzzing may be enable by appending -fsanitize=fuzzer -DFOSSIL_FUZZ |
| 55 | # to the TCCFLAGS variable. |
| 56 | # For more thorough (but also slower) investigation |
| 57 | # -fsanitize=fuzzer,undefined,address |
| 58 | # might be more useful. |
| 59 | |
| 60 | INSTALLDIR = $(DESTDIR)@prefix@/bin |
| 61 | USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ |
| @@ -92,10 +92,13 @@ | |
| 92 | MAKE_COMPILATION_DB = @MAKE_COMPILATION_DB@ |
| 93 | |
| 94 | .PHONY: all tags |
| 95 | |
| 96 | include $(SRCDIR)/main.mk |
| 97 | SQLITE_OPTIONS += @SQLITE_OPTIONS_EXT@ |
| 98 | SHELL_OPTIONS += @SQLITE_OPTIONS_EXT@ |
| 99 | # ^^^ must come after main.mk is included |
| 100 | |
| 101 | distclean: clean |
| 102 | -rm -f autoconfig.h config.log Makefile |
| 103 | -rm -f cscope.out tags |
| 104 | |
| 105 |
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -2.25 | |
| 1 | +2.26 | |
| 2 | 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.25 |
| 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.26 |
| 2 |
M
auto.def
+7
| --- auto.def | ||
| +++ auto.def | ||
| @@ -104,18 +104,21 @@ | ||
| 104 | 104 | msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." |
| 105 | 105 | define TCLSH false ;# force "make test" failure via /usr/bin/false |
| 106 | 106 | } |
| 107 | 107 | } |
| 108 | 108 | |
| 109 | +define CFLAGS [get-env CFLAGS "-g -O2"] | |
| 109 | 110 | define EXTRA_CFLAGS "-Wall" |
| 110 | 111 | define EXTRA_LDFLAGS "" |
| 111 | 112 | define USE_SYSTEM_SQLITE 0 |
| 112 | 113 | define USE_LINENOISE 0 |
| 113 | 114 | define USE_MMAN_H 0 |
| 114 | 115 | define USE_SEE 0 |
| 115 | 116 | define SQLITE3_ORIGIN 0 |
| 116 | 117 | # SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided |
| 118 | +define SQLITE_OPTIONS_EXT "" | |
| 119 | +# SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c | |
| 117 | 120 | |
| 118 | 121 | # Maintain the C89/C90-style order of variable declarations before statements. |
| 119 | 122 | # Check if the compiler supports the respective warning flag. |
| 120 | 123 | if {[cctest -cflags -Wdeclaration-after-statement]} { |
| 121 | 124 | define-append EXTRA_CFLAGS -Wdeclaration-after-statement |
| @@ -401,10 +404,14 @@ | ||
| 401 | 404 | msg-result "Linked to zlib via default library path" |
| 402 | 405 | } else { |
| 403 | 406 | define-append EXTRA_LDFLAGS "$lcheck" |
| 404 | 407 | msg-result "Linked to zlib via $lcheck" |
| 405 | 408 | } |
| 409 | + if {![check-function-in-lib compressBound z]} { | |
| 410 | + puts "Notice: disabling zlib compression in the SQL shell" | |
| 411 | + define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB} | |
| 412 | + } | |
| 406 | 413 | break |
| 407 | 414 | } |
| 408 | 415 | } |
| 409 | 416 | set ::zlib_lib -lz |
| 410 | 417 | } |
| 411 | 418 |
| --- auto.def | |
| +++ auto.def | |
| @@ -104,18 +104,21 @@ | |
| 104 | msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." |
| 105 | define TCLSH false ;# force "make test" failure via /usr/bin/false |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | define EXTRA_CFLAGS "-Wall" |
| 110 | define EXTRA_LDFLAGS "" |
| 111 | define USE_SYSTEM_SQLITE 0 |
| 112 | define USE_LINENOISE 0 |
| 113 | define USE_MMAN_H 0 |
| 114 | define USE_SEE 0 |
| 115 | define SQLITE3_ORIGIN 0 |
| 116 | # SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided |
| 117 | |
| 118 | # Maintain the C89/C90-style order of variable declarations before statements. |
| 119 | # Check if the compiler supports the respective warning flag. |
| 120 | if {[cctest -cflags -Wdeclaration-after-statement]} { |
| 121 | define-append EXTRA_CFLAGS -Wdeclaration-after-statement |
| @@ -401,10 +404,14 @@ | |
| 401 | msg-result "Linked to zlib via default library path" |
| 402 | } else { |
| 403 | define-append EXTRA_LDFLAGS "$lcheck" |
| 404 | msg-result "Linked to zlib via $lcheck" |
| 405 | } |
| 406 | break |
| 407 | } |
| 408 | } |
| 409 | set ::zlib_lib -lz |
| 410 | } |
| 411 |
| --- auto.def | |
| +++ auto.def | |
| @@ -104,18 +104,21 @@ | |
| 104 | msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." |
| 105 | define TCLSH false ;# force "make test" failure via /usr/bin/false |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | define CFLAGS [get-env CFLAGS "-g -O2"] |
| 110 | define EXTRA_CFLAGS "-Wall" |
| 111 | define EXTRA_LDFLAGS "" |
| 112 | define USE_SYSTEM_SQLITE 0 |
| 113 | define USE_LINENOISE 0 |
| 114 | define USE_MMAN_H 0 |
| 115 | define USE_SEE 0 |
| 116 | define SQLITE3_ORIGIN 0 |
| 117 | # SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided |
| 118 | define SQLITE_OPTIONS_EXT "" |
| 119 | # SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c |
| 120 | |
| 121 | # Maintain the C89/C90-style order of variable declarations before statements. |
| 122 | # Check if the compiler supports the respective warning flag. |
| 123 | if {[cctest -cflags -Wdeclaration-after-statement]} { |
| 124 | define-append EXTRA_CFLAGS -Wdeclaration-after-statement |
| @@ -401,10 +404,14 @@ | |
| 404 | msg-result "Linked to zlib via default library path" |
| 405 | } else { |
| 406 | define-append EXTRA_LDFLAGS "$lcheck" |
| 407 | msg-result "Linked to zlib via $lcheck" |
| 408 | } |
| 409 | if {![check-function-in-lib compressBound z]} { |
| 410 | puts "Notice: disabling zlib compression in the SQL shell" |
| 411 | define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB} |
| 412 | } |
| 413 | break |
| 414 | } |
| 415 | } |
| 416 | set ::zlib_lib -lz |
| 417 | } |
| 418 |
+5
-3
| --- autosetup/autosetup | ||
| +++ autosetup/autosetup | ||
| @@ -564,11 +564,14 @@ | ||
| 564 | 564 | set max [max $max [string length $opt]] |
| 565 | 565 | } |
| 566 | 566 | set indent [string repeat " " [expr {$max+4}]] |
| 567 | 567 | set cols [getenv COLUMNS 80] |
| 568 | 568 | catch { |
| 569 | - lassign [exec stty size] rows cols | |
| 569 | + lassign [exec stty size] _ sttycols | |
| 570 | + if {[string is integer -strict $sttycols]} { | |
| 571 | + set cols $sttycols | |
| 572 | + } | |
| 570 | 573 | } |
| 571 | 574 | incr cols -1 |
| 572 | 575 | # Now output |
| 573 | 576 | foreach help $::autosetup(optionhelp) { |
| 574 | 577 | lassign $help opt module desc |
| @@ -908,12 +911,11 @@ | ||
| 908 | 911 | # @find-executable-path name |
| 909 | 912 | # |
| 910 | 913 | # Searches the path for an executable with the given name. |
| 911 | 914 | # Note that the name may include some parameters, e.g. 'cc -mbig-endian', |
| 912 | 915 | # in which case the parameters are ignored. |
| 913 | -# The full path to the executable if found, or "" if not found. | |
| 914 | -# Returns 1 if found, or 0 if not. | |
| 916 | +# Returns the full path to the executable if found, or "" if not found. | |
| 915 | 917 | # |
| 916 | 918 | proc find-executable-path {name} { |
| 917 | 919 | # Ignore any parameters |
| 918 | 920 | set name [lindex $name 0] |
| 919 | 921 | # The empty string is never a valid executable |
| 920 | 922 |
| --- autosetup/autosetup | |
| +++ autosetup/autosetup | |
| @@ -564,11 +564,14 @@ | |
| 564 | set max [max $max [string length $opt]] |
| 565 | } |
| 566 | set indent [string repeat " " [expr {$max+4}]] |
| 567 | set cols [getenv COLUMNS 80] |
| 568 | catch { |
| 569 | lassign [exec stty size] rows cols |
| 570 | } |
| 571 | incr cols -1 |
| 572 | # Now output |
| 573 | foreach help $::autosetup(optionhelp) { |
| 574 | lassign $help opt module desc |
| @@ -908,12 +911,11 @@ | |
| 908 | # @find-executable-path name |
| 909 | # |
| 910 | # Searches the path for an executable with the given name. |
| 911 | # Note that the name may include some parameters, e.g. 'cc -mbig-endian', |
| 912 | # in which case the parameters are ignored. |
| 913 | # The full path to the executable if found, or "" if not found. |
| 914 | # Returns 1 if found, or 0 if not. |
| 915 | # |
| 916 | proc find-executable-path {name} { |
| 917 | # Ignore any parameters |
| 918 | set name [lindex $name 0] |
| 919 | # The empty string is never a valid executable |
| 920 |
| --- autosetup/autosetup | |
| +++ autosetup/autosetup | |
| @@ -564,11 +564,14 @@ | |
| 564 | set max [max $max [string length $opt]] |
| 565 | } |
| 566 | set indent [string repeat " " [expr {$max+4}]] |
| 567 | set cols [getenv COLUMNS 80] |
| 568 | catch { |
| 569 | lassign [exec stty size] _ sttycols |
| 570 | if {[string is integer -strict $sttycols]} { |
| 571 | set cols $sttycols |
| 572 | } |
| 573 | } |
| 574 | incr cols -1 |
| 575 | # Now output |
| 576 | foreach help $::autosetup(optionhelp) { |
| 577 | lassign $help opt module desc |
| @@ -908,12 +911,11 @@ | |
| 911 | # @find-executable-path name |
| 912 | # |
| 913 | # Searches the path for an executable with the given name. |
| 914 | # Note that the name may include some parameters, e.g. 'cc -mbig-endian', |
| 915 | # in which case the parameters are ignored. |
| 916 | # Returns the full path to the executable if found, or "" if not found. |
| 917 | # |
| 918 | proc find-executable-path {name} { |
| 919 | # Ignore any parameters |
| 920 | set name [lindex $name 0] |
| 921 | # The empty string is never a valid executable |
| 922 |
+95
-28
| --- autosetup/autosetup-config.guess | ||
| +++ autosetup/autosetup-config.guess | ||
| @@ -1,16 +1,16 @@ | ||
| 1 | 1 | #! /bin/sh |
| 2 | 2 | # Attempt to guess a canonical system name. |
| 3 | -# Copyright 1992-2021 Free Software Foundation, Inc. | |
| 3 | +# Copyright 1992-2024 Free Software Foundation, Inc. | |
| 4 | 4 | |
| 5 | 5 | # shellcheck disable=SC2006,SC2268 # see below for rationale |
| 6 | 6 | |
| 7 | -timestamp='2021-06-03' | |
| 7 | +timestamp='2024-07-27' | |
| 8 | 8 | |
| 9 | 9 | # This file is free software; you can redistribute it and/or modify it |
| 10 | 10 | # under the terms of the GNU General Public License as published by |
| 11 | -# the Free Software Foundation; either version 3 of the License, or | |
| 11 | +# the Free Software Foundation, either version 3 of the License, or | |
| 12 | 12 | # (at your option) any later version. |
| 13 | 13 | # |
| 14 | 14 | # This program is distributed in the hope that it will be useful, but |
| 15 | 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| @@ -45,11 +45,11 @@ | ||
| 45 | 45 | me=`echo "$0" | sed -e 's,.*/,,'` |
| 46 | 46 | |
| 47 | 47 | usage="\ |
| 48 | 48 | Usage: $0 [OPTION] |
| 49 | 49 | |
| 50 | -Output the configuration name of the system \`$me' is run on. | |
| 50 | +Output the configuration name of the system '$me' is run on. | |
| 51 | 51 | |
| 52 | 52 | Options: |
| 53 | 53 | -h, --help print this help, then exit |
| 54 | 54 | -t, --time-stamp print date of last modification, then exit |
| 55 | 55 | -v, --version print version number, then exit |
| @@ -58,17 +58,17 @@ | ||
| 58 | 58 | |
| 59 | 59 | version="\ |
| 60 | 60 | GNU config.guess ($timestamp) |
| 61 | 61 | |
| 62 | 62 | Originally written by Per Bothner. |
| 63 | -Copyright 1992-2021 Free Software Foundation, Inc. | |
| 63 | +Copyright 1992-2024 Free Software Foundation, Inc. | |
| 64 | 64 | |
| 65 | 65 | This is free software; see the source for copying conditions. There is NO |
| 66 | 66 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." |
| 67 | 67 | |
| 68 | 68 | help=" |
| 69 | -Try \`$me --help' for more information." | |
| 69 | +Try '$me --help' for more information." | |
| 70 | 70 | |
| 71 | 71 | # Parse command line |
| 72 | 72 | while test $# -gt 0 ; do |
| 73 | 73 | case $1 in |
| 74 | 74 | --time-stamp | --time* | -t ) |
| @@ -100,12 +100,12 @@ | ||
| 100 | 100 | # CC_FOR_BUILD -- compiler used by this script. Note that the use of a |
| 101 | 101 | # compiler to aid in system detection is discouraged as it requires |
| 102 | 102 | # temporary files to be created and, as you can see below, it is a |
| 103 | 103 | # headache to deal with in a portable fashion. |
| 104 | 104 | |
| 105 | -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still | |
| 106 | -# use `HOST_CC' if defined, but it is deprecated. | |
| 105 | +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still | |
| 106 | +# use 'HOST_CC' if defined, but it is deprecated. | |
| 107 | 107 | |
| 108 | 108 | # Portable tmp directory creation inspired by the Autoconf team. |
| 109 | 109 | |
| 110 | 110 | tmp= |
| 111 | 111 | # shellcheck disable=SC2172 |
| @@ -121,11 +121,11 @@ | ||
| 121 | 121 | { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || |
| 122 | 122 | { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } |
| 123 | 123 | dummy=$tmp/dummy |
| 124 | 124 | case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in |
| 125 | 125 | ,,) echo "int x;" > "$dummy.c" |
| 126 | - for driver in cc gcc c89 c99 ; do | |
| 126 | + for driver in cc gcc c17 c99 c89 ; do | |
| 127 | 127 | if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then |
| 128 | 128 | CC_FOR_BUILD=$driver |
| 129 | 129 | break |
| 130 | 130 | fi |
| 131 | 131 | done |
| @@ -153,22 +153,28 @@ | ||
| 153 | 153 | Linux|GNU|GNU/*) |
| 154 | 154 | LIBC=unknown |
| 155 | 155 | |
| 156 | 156 | set_cc_for_build |
| 157 | 157 | cat <<-EOF > "$dummy.c" |
| 158 | + #if defined(__ANDROID__) | |
| 159 | + LIBC=android | |
| 160 | + #else | |
| 158 | 161 | #include <features.h> |
| 159 | 162 | #if defined(__UCLIBC__) |
| 160 | 163 | LIBC=uclibc |
| 161 | 164 | #elif defined(__dietlibc__) |
| 162 | 165 | LIBC=dietlibc |
| 163 | 166 | #elif defined(__GLIBC__) |
| 164 | 167 | LIBC=gnu |
| 168 | + #elif defined(__LLVM_LIBC__) | |
| 169 | + LIBC=llvm | |
| 165 | 170 | #else |
| 166 | 171 | #include <stdarg.h> |
| 167 | 172 | /* First heuristic to detect musl libc. */ |
| 168 | 173 | #ifdef __DEFINED_va_list |
| 169 | 174 | LIBC=musl |
| 175 | + #endif | |
| 170 | 176 | #endif |
| 171 | 177 | #endif |
| 172 | 178 | EOF |
| 173 | 179 | cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` |
| 174 | 180 | eval "$cc_set_libc" |
| @@ -435,11 +441,11 @@ | ||
| 435 | 441 | # If there is a compiler, see if it is configured for 64-bit objects. |
| 436 | 442 | # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. |
| 437 | 443 | # This test works for both compilers. |
| 438 | 444 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 439 | 445 | if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ |
| 440 | - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ | |
| 446 | + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ | |
| 441 | 447 | grep IS_64BIT_ARCH >/dev/null |
| 442 | 448 | then |
| 443 | 449 | SUN_ARCH=x86_64 |
| 444 | 450 | fi |
| 445 | 451 | fi |
| @@ -457,11 +463,11 @@ | ||
| 457 | 463 | case `/usr/bin/arch -k` in |
| 458 | 464 | Series*|S4*) |
| 459 | 465 | UNAME_RELEASE=`uname -v` |
| 460 | 466 | ;; |
| 461 | 467 | esac |
| 462 | - # Japanese Language versions have a version number like `4.1.3-JL'. | |
| 468 | + # Japanese Language versions have a version number like '4.1.3-JL'. | |
| 463 | 469 | SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` |
| 464 | 470 | GUESS=sparc-sun-sunos$SUN_REL |
| 465 | 471 | ;; |
| 466 | 472 | sun3*:SunOS:*:*) |
| 467 | 473 | GUESS=m68k-sun-sunos$UNAME_RELEASE |
| @@ -626,11 +632,12 @@ | ||
| 626 | 632 | if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then |
| 627 | 633 | set_cc_for_build |
| 628 | 634 | sed 's/^ //' << EOF > "$dummy.c" |
| 629 | 635 | #include <sys/systemcfg.h> |
| 630 | 636 | |
| 631 | - main() | |
| 637 | + int | |
| 638 | + main () | |
| 632 | 639 | { |
| 633 | 640 | if (!__power_pc()) |
| 634 | 641 | exit(1); |
| 635 | 642 | puts("powerpc-ibm-aix3.2.5"); |
| 636 | 643 | exit(0); |
| @@ -710,11 +717,12 @@ | ||
| 710 | 717 | |
| 711 | 718 | #define _HPUX_SOURCE |
| 712 | 719 | #include <stdlib.h> |
| 713 | 720 | #include <unistd.h> |
| 714 | 721 | |
| 715 | - int main () | |
| 722 | + int | |
| 723 | + main () | |
| 716 | 724 | { |
| 717 | 725 | #if defined(_SC_KERNEL_BITS) |
| 718 | 726 | long bits = sysconf(_SC_KERNEL_BITS); |
| 719 | 727 | #endif |
| 720 | 728 | long cpu = sysconf (_SC_CPU_VERSION); |
| @@ -902,11 +910,11 @@ | ||
| 902 | 910 | FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` |
| 903 | 911 | GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf |
| 904 | 912 | fi |
| 905 | 913 | ;; |
| 906 | 914 | *:FreeBSD:*:*) |
| 907 | - UNAME_PROCESSOR=`/usr/bin/uname -p` | |
| 915 | + UNAME_PROCESSOR=`uname -p` | |
| 908 | 916 | case $UNAME_PROCESSOR in |
| 909 | 917 | amd64) |
| 910 | 918 | UNAME_PROCESSOR=x86_64 ;; |
| 911 | 919 | i386) |
| 912 | 920 | UNAME_PROCESSOR=i586 ;; |
| @@ -927,10 +935,13 @@ | ||
| 927 | 935 | GUESS=$UNAME_MACHINE-pc-msys |
| 928 | 936 | ;; |
| 929 | 937 | i*:PW*:*) |
| 930 | 938 | GUESS=$UNAME_MACHINE-pc-pw32 |
| 931 | 939 | ;; |
| 940 | + *:SerenityOS:*:*) | |
| 941 | + GUESS=$UNAME_MACHINE-pc-serenity | |
| 942 | + ;; | |
| 932 | 943 | *:Interix*:*) |
| 933 | 944 | case $UNAME_MACHINE in |
| 934 | 945 | x86) |
| 935 | 946 | GUESS=i586-pc-interix$UNAME_RELEASE |
| 936 | 947 | ;; |
| @@ -961,15 +972,41 @@ | ||
| 961 | 972 | # other systems with GNU libc and userland |
| 962 | 973 | GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` |
| 963 | 974 | GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` |
| 964 | 975 | GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC |
| 965 | 976 | ;; |
| 977 | + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) | |
| 978 | + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" | |
| 979 | + ;; | |
| 980 | + *:[Mm]anagarm:*:*) | |
| 981 | + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" | |
| 982 | + ;; | |
| 966 | 983 | *:Minix:*:*) |
| 967 | 984 | GUESS=$UNAME_MACHINE-unknown-minix |
| 968 | 985 | ;; |
| 969 | 986 | aarch64:Linux:*:*) |
| 970 | - GUESS=$UNAME_MACHINE-unknown-linux-$LIBC | |
| 987 | + set_cc_for_build | |
| 988 | + CPU=$UNAME_MACHINE | |
| 989 | + LIBCABI=$LIBC | |
| 990 | + if test "$CC_FOR_BUILD" != no_compiler_found; then | |
| 991 | + ABI=64 | |
| 992 | + sed 's/^ //' << EOF > "$dummy.c" | |
| 993 | + #ifdef __ARM_EABI__ | |
| 994 | + #ifdef __ARM_PCS_VFP | |
| 995 | + ABI=eabihf | |
| 996 | + #else | |
| 997 | + ABI=eabi | |
| 998 | + #endif | |
| 999 | + #endif | |
| 1000 | +EOF | |
| 1001 | + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` | |
| 1002 | + eval "$cc_set_abi" | |
| 1003 | + case $ABI in | |
| 1004 | + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; | |
| 1005 | + esac | |
| 1006 | + fi | |
| 1007 | + GUESS=$CPU-unknown-linux-$LIBCABI | |
| 971 | 1008 | ;; |
| 972 | 1009 | aarch64_be:Linux:*:*) |
| 973 | 1010 | UNAME_MACHINE=aarch64_be |
| 974 | 1011 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 975 | 1012 | ;; |
| @@ -1031,11 +1068,20 @@ | ||
| 1031 | 1068 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1032 | 1069 | ;; |
| 1033 | 1070 | k1om:Linux:*:*) |
| 1034 | 1071 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1035 | 1072 | ;; |
| 1036 | - loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) | |
| 1073 | + kvx:Linux:*:*) | |
| 1074 | + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC | |
| 1075 | + ;; | |
| 1076 | + kvx:cos:*:*) | |
| 1077 | + GUESS=$UNAME_MACHINE-unknown-cos | |
| 1078 | + ;; | |
| 1079 | + kvx:mbr:*:*) | |
| 1080 | + GUESS=$UNAME_MACHINE-unknown-mbr | |
| 1081 | + ;; | |
| 1082 | + loongarch32:Linux:*:* | loongarch64:Linux:*:*) | |
| 1037 | 1083 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1038 | 1084 | ;; |
| 1039 | 1085 | m32r*:Linux:*:*) |
| 1040 | 1086 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1041 | 1087 | ;; |
| @@ -1146,20 +1192,31 @@ | ||
| 1146 | 1192 | vax:Linux:*:*) |
| 1147 | 1193 | GUESS=$UNAME_MACHINE-dec-linux-$LIBC |
| 1148 | 1194 | ;; |
| 1149 | 1195 | x86_64:Linux:*:*) |
| 1150 | 1196 | set_cc_for_build |
| 1197 | + CPU=$UNAME_MACHINE | |
| 1151 | 1198 | LIBCABI=$LIBC |
| 1152 | 1199 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 1153 | - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ | |
| 1154 | - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ | |
| 1155 | - grep IS_X32 >/dev/null | |
| 1156 | - then | |
| 1157 | - LIBCABI=${LIBC}x32 | |
| 1158 | - fi | |
| 1159 | - fi | |
| 1160 | - GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI | |
| 1200 | + ABI=64 | |
| 1201 | + sed 's/^ //' << EOF > "$dummy.c" | |
| 1202 | + #ifdef __i386__ | |
| 1203 | + ABI=x86 | |
| 1204 | + #else | |
| 1205 | + #ifdef __ILP32__ | |
| 1206 | + ABI=x32 | |
| 1207 | + #endif | |
| 1208 | + #endif | |
| 1209 | +EOF | |
| 1210 | + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` | |
| 1211 | + eval "$cc_set_abi" | |
| 1212 | + case $ABI in | |
| 1213 | + x86) CPU=i686 ;; | |
| 1214 | + x32) LIBCABI=${LIBC}x32 ;; | |
| 1215 | + esac | |
| 1216 | + fi | |
| 1217 | + GUESS=$CPU-pc-linux-$LIBCABI | |
| 1161 | 1218 | ;; |
| 1162 | 1219 | xtensa*:Linux:*:*) |
| 1163 | 1220 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1164 | 1221 | ;; |
| 1165 | 1222 | i*86:DYNIX/ptx:4*:*) |
| @@ -1175,11 +1232,11 @@ | ||
| 1175 | 1232 | # I just have to hope. -- rms. |
| 1176 | 1233 | # Use sysv4.2uw... so that sysv4* matches it. |
| 1177 | 1234 | GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION |
| 1178 | 1235 | ;; |
| 1179 | 1236 | i*86:OS/2:*:*) |
| 1180 | - # If we were able to find `uname', then EMX Unix compatibility | |
| 1237 | + # If we were able to find 'uname', then EMX Unix compatibility | |
| 1181 | 1238 | # is probably installed. |
| 1182 | 1239 | GUESS=$UNAME_MACHINE-pc-os2-emx |
| 1183 | 1240 | ;; |
| 1184 | 1241 | i*86:XTS-300:*:STOP) |
| 1185 | 1242 | GUESS=$UNAME_MACHINE-unknown-stop |
| @@ -1316,11 +1373,11 @@ | ||
| 1316 | 1373 | GUESS=$UNAME_MACHINE-sni-sysv4 |
| 1317 | 1374 | else |
| 1318 | 1375 | GUESS=ns32k-sni-sysv |
| 1319 | 1376 | fi |
| 1320 | 1377 | ;; |
| 1321 | - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort | |
| 1378 | + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort | |
| 1322 | 1379 | # says <[email protected]> |
| 1323 | 1380 | GUESS=i586-unisys-sysv4 |
| 1324 | 1381 | ;; |
| 1325 | 1382 | *:UNIX_System_V:4*:FTX*) |
| 1326 | 1383 | # From Gerald Hewes <[email protected]>. |
| @@ -1362,12 +1419,15 @@ | ||
| 1362 | 1419 | GUESS=i586-pc-beos |
| 1363 | 1420 | ;; |
| 1364 | 1421 | BePC:Haiku:*:*) # Haiku running on Intel PC compatible. |
| 1365 | 1422 | GUESS=i586-pc-haiku |
| 1366 | 1423 | ;; |
| 1367 | - x86_64:Haiku:*:*) | |
| 1368 | - GUESS=x86_64-unknown-haiku | |
| 1424 | + ppc:Haiku:*:*) # Haiku running on Apple PowerPC | |
| 1425 | + GUESS=powerpc-apple-haiku | |
| 1426 | + ;; | |
| 1427 | + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) | |
| 1428 | + GUESS=$UNAME_MACHINE-unknown-haiku | |
| 1369 | 1429 | ;; |
| 1370 | 1430 | SX-4:SUPER-UX:*:*) |
| 1371 | 1431 | GUESS=sx4-nec-superux$UNAME_RELEASE |
| 1372 | 1432 | ;; |
| 1373 | 1433 | SX-5:SUPER-UX:*:*) |
| @@ -1520,10 +1580,13 @@ | ||
| 1520 | 1580 | GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL |
| 1521 | 1581 | ;; |
| 1522 | 1582 | i*86:rdos:*:*) |
| 1523 | 1583 | GUESS=$UNAME_MACHINE-pc-rdos |
| 1524 | 1584 | ;; |
| 1585 | + i*86:Fiwix:*:*) | |
| 1586 | + GUESS=$UNAME_MACHINE-pc-fiwix | |
| 1587 | + ;; | |
| 1525 | 1588 | *:AROS:*:*) |
| 1526 | 1589 | GUESS=$UNAME_MACHINE-unknown-aros |
| 1527 | 1590 | ;; |
| 1528 | 1591 | x86_64:VMkernel:*:*) |
| 1529 | 1592 | GUESS=$UNAME_MACHINE-unknown-esx |
| @@ -1532,10 +1595,13 @@ | ||
| 1532 | 1595 | GUESS=x86_64-unknown-onefs |
| 1533 | 1596 | ;; |
| 1534 | 1597 | *:Unleashed:*:*) |
| 1535 | 1598 | GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE |
| 1536 | 1599 | ;; |
| 1600 | + *:Ironclad:*:*) | |
| 1601 | + GUESS=$UNAME_MACHINE-unknown-ironclad | |
| 1602 | + ;; | |
| 1537 | 1603 | esac |
| 1538 | 1604 | |
| 1539 | 1605 | # Do we have a guess based on uname results? |
| 1540 | 1606 | if test "x$GUESS" != x; then |
| 1541 | 1607 | echo "$GUESS" |
| @@ -1555,10 +1621,11 @@ | ||
| 1555 | 1621 | #if defined(_SIZE_T_) || defined(SIGLOST) |
| 1556 | 1622 | #include <sys/utsname.h> |
| 1557 | 1623 | #endif |
| 1558 | 1624 | #endif |
| 1559 | 1625 | #endif |
| 1626 | +int | |
| 1560 | 1627 | main () |
| 1561 | 1628 | { |
| 1562 | 1629 | #if defined (sony) |
| 1563 | 1630 | #if defined (MIPSEB) |
| 1564 | 1631 | /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, |
| 1565 | 1632 |
| --- autosetup/autosetup-config.guess | |
| +++ autosetup/autosetup-config.guess | |
| @@ -1,16 +1,16 @@ | |
| 1 | #! /bin/sh |
| 2 | # Attempt to guess a canonical system name. |
| 3 | # Copyright 1992-2021 Free Software Foundation, Inc. |
| 4 | |
| 5 | # shellcheck disable=SC2006,SC2268 # see below for rationale |
| 6 | |
| 7 | timestamp='2021-06-03' |
| 8 | |
| 9 | # This file is free software; you can redistribute it and/or modify it |
| 10 | # under the terms of the GNU General Public License as published by |
| 11 | # the Free Software Foundation; either version 3 of the License, or |
| 12 | # (at your option) any later version. |
| 13 | # |
| 14 | # This program is distributed in the hope that it will be useful, but |
| 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| @@ -45,11 +45,11 @@ | |
| 45 | me=`echo "$0" | sed -e 's,.*/,,'` |
| 46 | |
| 47 | usage="\ |
| 48 | Usage: $0 [OPTION] |
| 49 | |
| 50 | Output the configuration name of the system \`$me' is run on. |
| 51 | |
| 52 | Options: |
| 53 | -h, --help print this help, then exit |
| 54 | -t, --time-stamp print date of last modification, then exit |
| 55 | -v, --version print version number, then exit |
| @@ -58,17 +58,17 @@ | |
| 58 | |
| 59 | version="\ |
| 60 | GNU config.guess ($timestamp) |
| 61 | |
| 62 | Originally written by Per Bothner. |
| 63 | Copyright 1992-2021 Free Software Foundation, Inc. |
| 64 | |
| 65 | This is free software; see the source for copying conditions. There is NO |
| 66 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." |
| 67 | |
| 68 | help=" |
| 69 | Try \`$me --help' for more information." |
| 70 | |
| 71 | # Parse command line |
| 72 | while test $# -gt 0 ; do |
| 73 | case $1 in |
| 74 | --time-stamp | --time* | -t ) |
| @@ -100,12 +100,12 @@ | |
| 100 | # CC_FOR_BUILD -- compiler used by this script. Note that the use of a |
| 101 | # compiler to aid in system detection is discouraged as it requires |
| 102 | # temporary files to be created and, as you can see below, it is a |
| 103 | # headache to deal with in a portable fashion. |
| 104 | |
| 105 | # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still |
| 106 | # use `HOST_CC' if defined, but it is deprecated. |
| 107 | |
| 108 | # Portable tmp directory creation inspired by the Autoconf team. |
| 109 | |
| 110 | tmp= |
| 111 | # shellcheck disable=SC2172 |
| @@ -121,11 +121,11 @@ | |
| 121 | { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || |
| 122 | { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } |
| 123 | dummy=$tmp/dummy |
| 124 | case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in |
| 125 | ,,) echo "int x;" > "$dummy.c" |
| 126 | for driver in cc gcc c89 c99 ; do |
| 127 | if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then |
| 128 | CC_FOR_BUILD=$driver |
| 129 | break |
| 130 | fi |
| 131 | done |
| @@ -153,22 +153,28 @@ | |
| 153 | Linux|GNU|GNU/*) |
| 154 | LIBC=unknown |
| 155 | |
| 156 | set_cc_for_build |
| 157 | cat <<-EOF > "$dummy.c" |
| 158 | #include <features.h> |
| 159 | #if defined(__UCLIBC__) |
| 160 | LIBC=uclibc |
| 161 | #elif defined(__dietlibc__) |
| 162 | LIBC=dietlibc |
| 163 | #elif defined(__GLIBC__) |
| 164 | LIBC=gnu |
| 165 | #else |
| 166 | #include <stdarg.h> |
| 167 | /* First heuristic to detect musl libc. */ |
| 168 | #ifdef __DEFINED_va_list |
| 169 | LIBC=musl |
| 170 | #endif |
| 171 | #endif |
| 172 | EOF |
| 173 | cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` |
| 174 | eval "$cc_set_libc" |
| @@ -435,11 +441,11 @@ | |
| 435 | # If there is a compiler, see if it is configured for 64-bit objects. |
| 436 | # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. |
| 437 | # This test works for both compilers. |
| 438 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 439 | if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ |
| 440 | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ |
| 441 | grep IS_64BIT_ARCH >/dev/null |
| 442 | then |
| 443 | SUN_ARCH=x86_64 |
| 444 | fi |
| 445 | fi |
| @@ -457,11 +463,11 @@ | |
| 457 | case `/usr/bin/arch -k` in |
| 458 | Series*|S4*) |
| 459 | UNAME_RELEASE=`uname -v` |
| 460 | ;; |
| 461 | esac |
| 462 | # Japanese Language versions have a version number like `4.1.3-JL'. |
| 463 | SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` |
| 464 | GUESS=sparc-sun-sunos$SUN_REL |
| 465 | ;; |
| 466 | sun3*:SunOS:*:*) |
| 467 | GUESS=m68k-sun-sunos$UNAME_RELEASE |
| @@ -626,11 +632,12 @@ | |
| 626 | if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then |
| 627 | set_cc_for_build |
| 628 | sed 's/^ //' << EOF > "$dummy.c" |
| 629 | #include <sys/systemcfg.h> |
| 630 | |
| 631 | main() |
| 632 | { |
| 633 | if (!__power_pc()) |
| 634 | exit(1); |
| 635 | puts("powerpc-ibm-aix3.2.5"); |
| 636 | exit(0); |
| @@ -710,11 +717,12 @@ | |
| 710 | |
| 711 | #define _HPUX_SOURCE |
| 712 | #include <stdlib.h> |
| 713 | #include <unistd.h> |
| 714 | |
| 715 | int main () |
| 716 | { |
| 717 | #if defined(_SC_KERNEL_BITS) |
| 718 | long bits = sysconf(_SC_KERNEL_BITS); |
| 719 | #endif |
| 720 | long cpu = sysconf (_SC_CPU_VERSION); |
| @@ -902,11 +910,11 @@ | |
| 902 | FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` |
| 903 | GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf |
| 904 | fi |
| 905 | ;; |
| 906 | *:FreeBSD:*:*) |
| 907 | UNAME_PROCESSOR=`/usr/bin/uname -p` |
| 908 | case $UNAME_PROCESSOR in |
| 909 | amd64) |
| 910 | UNAME_PROCESSOR=x86_64 ;; |
| 911 | i386) |
| 912 | UNAME_PROCESSOR=i586 ;; |
| @@ -927,10 +935,13 @@ | |
| 927 | GUESS=$UNAME_MACHINE-pc-msys |
| 928 | ;; |
| 929 | i*:PW*:*) |
| 930 | GUESS=$UNAME_MACHINE-pc-pw32 |
| 931 | ;; |
| 932 | *:Interix*:*) |
| 933 | case $UNAME_MACHINE in |
| 934 | x86) |
| 935 | GUESS=i586-pc-interix$UNAME_RELEASE |
| 936 | ;; |
| @@ -961,15 +972,41 @@ | |
| 961 | # other systems with GNU libc and userland |
| 962 | GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` |
| 963 | GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` |
| 964 | GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC |
| 965 | ;; |
| 966 | *:Minix:*:*) |
| 967 | GUESS=$UNAME_MACHINE-unknown-minix |
| 968 | ;; |
| 969 | aarch64:Linux:*:*) |
| 970 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 971 | ;; |
| 972 | aarch64_be:Linux:*:*) |
| 973 | UNAME_MACHINE=aarch64_be |
| 974 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 975 | ;; |
| @@ -1031,11 +1068,20 @@ | |
| 1031 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1032 | ;; |
| 1033 | k1om:Linux:*:*) |
| 1034 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1035 | ;; |
| 1036 | loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) |
| 1037 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1038 | ;; |
| 1039 | m32r*:Linux:*:*) |
| 1040 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1041 | ;; |
| @@ -1146,20 +1192,31 @@ | |
| 1146 | vax:Linux:*:*) |
| 1147 | GUESS=$UNAME_MACHINE-dec-linux-$LIBC |
| 1148 | ;; |
| 1149 | x86_64:Linux:*:*) |
| 1150 | set_cc_for_build |
| 1151 | LIBCABI=$LIBC |
| 1152 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 1153 | if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ |
| 1154 | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ |
| 1155 | grep IS_X32 >/dev/null |
| 1156 | then |
| 1157 | LIBCABI=${LIBC}x32 |
| 1158 | fi |
| 1159 | fi |
| 1160 | GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI |
| 1161 | ;; |
| 1162 | xtensa*:Linux:*:*) |
| 1163 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1164 | ;; |
| 1165 | i*86:DYNIX/ptx:4*:*) |
| @@ -1175,11 +1232,11 @@ | |
| 1175 | # I just have to hope. -- rms. |
| 1176 | # Use sysv4.2uw... so that sysv4* matches it. |
| 1177 | GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION |
| 1178 | ;; |
| 1179 | i*86:OS/2:*:*) |
| 1180 | # If we were able to find `uname', then EMX Unix compatibility |
| 1181 | # is probably installed. |
| 1182 | GUESS=$UNAME_MACHINE-pc-os2-emx |
| 1183 | ;; |
| 1184 | i*86:XTS-300:*:STOP) |
| 1185 | GUESS=$UNAME_MACHINE-unknown-stop |
| @@ -1316,11 +1373,11 @@ | |
| 1316 | GUESS=$UNAME_MACHINE-sni-sysv4 |
| 1317 | else |
| 1318 | GUESS=ns32k-sni-sysv |
| 1319 | fi |
| 1320 | ;; |
| 1321 | PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort |
| 1322 | # says <[email protected]> |
| 1323 | GUESS=i586-unisys-sysv4 |
| 1324 | ;; |
| 1325 | *:UNIX_System_V:4*:FTX*) |
| 1326 | # From Gerald Hewes <[email protected]>. |
| @@ -1362,12 +1419,15 @@ | |
| 1362 | GUESS=i586-pc-beos |
| 1363 | ;; |
| 1364 | BePC:Haiku:*:*) # Haiku running on Intel PC compatible. |
| 1365 | GUESS=i586-pc-haiku |
| 1366 | ;; |
| 1367 | x86_64:Haiku:*:*) |
| 1368 | GUESS=x86_64-unknown-haiku |
| 1369 | ;; |
| 1370 | SX-4:SUPER-UX:*:*) |
| 1371 | GUESS=sx4-nec-superux$UNAME_RELEASE |
| 1372 | ;; |
| 1373 | SX-5:SUPER-UX:*:*) |
| @@ -1520,10 +1580,13 @@ | |
| 1520 | GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL |
| 1521 | ;; |
| 1522 | i*86:rdos:*:*) |
| 1523 | GUESS=$UNAME_MACHINE-pc-rdos |
| 1524 | ;; |
| 1525 | *:AROS:*:*) |
| 1526 | GUESS=$UNAME_MACHINE-unknown-aros |
| 1527 | ;; |
| 1528 | x86_64:VMkernel:*:*) |
| 1529 | GUESS=$UNAME_MACHINE-unknown-esx |
| @@ -1532,10 +1595,13 @@ | |
| 1532 | GUESS=x86_64-unknown-onefs |
| 1533 | ;; |
| 1534 | *:Unleashed:*:*) |
| 1535 | GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE |
| 1536 | ;; |
| 1537 | esac |
| 1538 | |
| 1539 | # Do we have a guess based on uname results? |
| 1540 | if test "x$GUESS" != x; then |
| 1541 | echo "$GUESS" |
| @@ -1555,10 +1621,11 @@ | |
| 1555 | #if defined(_SIZE_T_) || defined(SIGLOST) |
| 1556 | #include <sys/utsname.h> |
| 1557 | #endif |
| 1558 | #endif |
| 1559 | #endif |
| 1560 | main () |
| 1561 | { |
| 1562 | #if defined (sony) |
| 1563 | #if defined (MIPSEB) |
| 1564 | /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, |
| 1565 |
| --- autosetup/autosetup-config.guess | |
| +++ autosetup/autosetup-config.guess | |
| @@ -1,16 +1,16 @@ | |
| 1 | #! /bin/sh |
| 2 | # Attempt to guess a canonical system name. |
| 3 | # Copyright 1992-2024 Free Software Foundation, Inc. |
| 4 | |
| 5 | # shellcheck disable=SC2006,SC2268 # see below for rationale |
| 6 | |
| 7 | timestamp='2024-07-27' |
| 8 | |
| 9 | # This file is free software; you can redistribute it and/or modify it |
| 10 | # under the terms of the GNU General Public License as published by |
| 11 | # the Free Software Foundation, either version 3 of the License, or |
| 12 | # (at your option) any later version. |
| 13 | # |
| 14 | # This program is distributed in the hope that it will be useful, but |
| 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| @@ -45,11 +45,11 @@ | |
| 45 | me=`echo "$0" | sed -e 's,.*/,,'` |
| 46 | |
| 47 | usage="\ |
| 48 | Usage: $0 [OPTION] |
| 49 | |
| 50 | Output the configuration name of the system '$me' is run on. |
| 51 | |
| 52 | Options: |
| 53 | -h, --help print this help, then exit |
| 54 | -t, --time-stamp print date of last modification, then exit |
| 55 | -v, --version print version number, then exit |
| @@ -58,17 +58,17 @@ | |
| 58 | |
| 59 | version="\ |
| 60 | GNU config.guess ($timestamp) |
| 61 | |
| 62 | Originally written by Per Bothner. |
| 63 | Copyright 1992-2024 Free Software Foundation, Inc. |
| 64 | |
| 65 | This is free software; see the source for copying conditions. There is NO |
| 66 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." |
| 67 | |
| 68 | help=" |
| 69 | Try '$me --help' for more information." |
| 70 | |
| 71 | # Parse command line |
| 72 | while test $# -gt 0 ; do |
| 73 | case $1 in |
| 74 | --time-stamp | --time* | -t ) |
| @@ -100,12 +100,12 @@ | |
| 100 | # CC_FOR_BUILD -- compiler used by this script. Note that the use of a |
| 101 | # compiler to aid in system detection is discouraged as it requires |
| 102 | # temporary files to be created and, as you can see below, it is a |
| 103 | # headache to deal with in a portable fashion. |
| 104 | |
| 105 | # Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still |
| 106 | # use 'HOST_CC' if defined, but it is deprecated. |
| 107 | |
| 108 | # Portable tmp directory creation inspired by the Autoconf team. |
| 109 | |
| 110 | tmp= |
| 111 | # shellcheck disable=SC2172 |
| @@ -121,11 +121,11 @@ | |
| 121 | { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || |
| 122 | { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } |
| 123 | dummy=$tmp/dummy |
| 124 | case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in |
| 125 | ,,) echo "int x;" > "$dummy.c" |
| 126 | for driver in cc gcc c17 c99 c89 ; do |
| 127 | if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then |
| 128 | CC_FOR_BUILD=$driver |
| 129 | break |
| 130 | fi |
| 131 | done |
| @@ -153,22 +153,28 @@ | |
| 153 | Linux|GNU|GNU/*) |
| 154 | LIBC=unknown |
| 155 | |
| 156 | set_cc_for_build |
| 157 | cat <<-EOF > "$dummy.c" |
| 158 | #if defined(__ANDROID__) |
| 159 | LIBC=android |
| 160 | #else |
| 161 | #include <features.h> |
| 162 | #if defined(__UCLIBC__) |
| 163 | LIBC=uclibc |
| 164 | #elif defined(__dietlibc__) |
| 165 | LIBC=dietlibc |
| 166 | #elif defined(__GLIBC__) |
| 167 | LIBC=gnu |
| 168 | #elif defined(__LLVM_LIBC__) |
| 169 | LIBC=llvm |
| 170 | #else |
| 171 | #include <stdarg.h> |
| 172 | /* First heuristic to detect musl libc. */ |
| 173 | #ifdef __DEFINED_va_list |
| 174 | LIBC=musl |
| 175 | #endif |
| 176 | #endif |
| 177 | #endif |
| 178 | EOF |
| 179 | cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` |
| 180 | eval "$cc_set_libc" |
| @@ -435,11 +441,11 @@ | |
| 441 | # If there is a compiler, see if it is configured for 64-bit objects. |
| 442 | # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. |
| 443 | # This test works for both compilers. |
| 444 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 445 | if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ |
| 446 | (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ |
| 447 | grep IS_64BIT_ARCH >/dev/null |
| 448 | then |
| 449 | SUN_ARCH=x86_64 |
| 450 | fi |
| 451 | fi |
| @@ -457,11 +463,11 @@ | |
| 463 | case `/usr/bin/arch -k` in |
| 464 | Series*|S4*) |
| 465 | UNAME_RELEASE=`uname -v` |
| 466 | ;; |
| 467 | esac |
| 468 | # Japanese Language versions have a version number like '4.1.3-JL'. |
| 469 | SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` |
| 470 | GUESS=sparc-sun-sunos$SUN_REL |
| 471 | ;; |
| 472 | sun3*:SunOS:*:*) |
| 473 | GUESS=m68k-sun-sunos$UNAME_RELEASE |
| @@ -626,11 +632,12 @@ | |
| 632 | if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then |
| 633 | set_cc_for_build |
| 634 | sed 's/^ //' << EOF > "$dummy.c" |
| 635 | #include <sys/systemcfg.h> |
| 636 | |
| 637 | int |
| 638 | main () |
| 639 | { |
| 640 | if (!__power_pc()) |
| 641 | exit(1); |
| 642 | puts("powerpc-ibm-aix3.2.5"); |
| 643 | exit(0); |
| @@ -710,11 +717,12 @@ | |
| 717 | |
| 718 | #define _HPUX_SOURCE |
| 719 | #include <stdlib.h> |
| 720 | #include <unistd.h> |
| 721 | |
| 722 | int |
| 723 | main () |
| 724 | { |
| 725 | #if defined(_SC_KERNEL_BITS) |
| 726 | long bits = sysconf(_SC_KERNEL_BITS); |
| 727 | #endif |
| 728 | long cpu = sysconf (_SC_CPU_VERSION); |
| @@ -902,11 +910,11 @@ | |
| 910 | FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` |
| 911 | GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf |
| 912 | fi |
| 913 | ;; |
| 914 | *:FreeBSD:*:*) |
| 915 | UNAME_PROCESSOR=`uname -p` |
| 916 | case $UNAME_PROCESSOR in |
| 917 | amd64) |
| 918 | UNAME_PROCESSOR=x86_64 ;; |
| 919 | i386) |
| 920 | UNAME_PROCESSOR=i586 ;; |
| @@ -927,10 +935,13 @@ | |
| 935 | GUESS=$UNAME_MACHINE-pc-msys |
| 936 | ;; |
| 937 | i*:PW*:*) |
| 938 | GUESS=$UNAME_MACHINE-pc-pw32 |
| 939 | ;; |
| 940 | *:SerenityOS:*:*) |
| 941 | GUESS=$UNAME_MACHINE-pc-serenity |
| 942 | ;; |
| 943 | *:Interix*:*) |
| 944 | case $UNAME_MACHINE in |
| 945 | x86) |
| 946 | GUESS=i586-pc-interix$UNAME_RELEASE |
| 947 | ;; |
| @@ -961,15 +972,41 @@ | |
| 972 | # other systems with GNU libc and userland |
| 973 | GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` |
| 974 | GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` |
| 975 | GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC |
| 976 | ;; |
| 977 | x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) |
| 978 | GUESS="$UNAME_MACHINE-pc-managarm-mlibc" |
| 979 | ;; |
| 980 | *:[Mm]anagarm:*:*) |
| 981 | GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" |
| 982 | ;; |
| 983 | *:Minix:*:*) |
| 984 | GUESS=$UNAME_MACHINE-unknown-minix |
| 985 | ;; |
| 986 | aarch64:Linux:*:*) |
| 987 | set_cc_for_build |
| 988 | CPU=$UNAME_MACHINE |
| 989 | LIBCABI=$LIBC |
| 990 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 991 | ABI=64 |
| 992 | sed 's/^ //' << EOF > "$dummy.c" |
| 993 | #ifdef __ARM_EABI__ |
| 994 | #ifdef __ARM_PCS_VFP |
| 995 | ABI=eabihf |
| 996 | #else |
| 997 | ABI=eabi |
| 998 | #endif |
| 999 | #endif |
| 1000 | EOF |
| 1001 | cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` |
| 1002 | eval "$cc_set_abi" |
| 1003 | case $ABI in |
| 1004 | eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; |
| 1005 | esac |
| 1006 | fi |
| 1007 | GUESS=$CPU-unknown-linux-$LIBCABI |
| 1008 | ;; |
| 1009 | aarch64_be:Linux:*:*) |
| 1010 | UNAME_MACHINE=aarch64_be |
| 1011 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1012 | ;; |
| @@ -1031,11 +1068,20 @@ | |
| 1068 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1069 | ;; |
| 1070 | k1om:Linux:*:*) |
| 1071 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1072 | ;; |
| 1073 | kvx:Linux:*:*) |
| 1074 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1075 | ;; |
| 1076 | kvx:cos:*:*) |
| 1077 | GUESS=$UNAME_MACHINE-unknown-cos |
| 1078 | ;; |
| 1079 | kvx:mbr:*:*) |
| 1080 | GUESS=$UNAME_MACHINE-unknown-mbr |
| 1081 | ;; |
| 1082 | loongarch32:Linux:*:* | loongarch64:Linux:*:*) |
| 1083 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1084 | ;; |
| 1085 | m32r*:Linux:*:*) |
| 1086 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1087 | ;; |
| @@ -1146,20 +1192,31 @@ | |
| 1192 | vax:Linux:*:*) |
| 1193 | GUESS=$UNAME_MACHINE-dec-linux-$LIBC |
| 1194 | ;; |
| 1195 | x86_64:Linux:*:*) |
| 1196 | set_cc_for_build |
| 1197 | CPU=$UNAME_MACHINE |
| 1198 | LIBCABI=$LIBC |
| 1199 | if test "$CC_FOR_BUILD" != no_compiler_found; then |
| 1200 | ABI=64 |
| 1201 | sed 's/^ //' << EOF > "$dummy.c" |
| 1202 | #ifdef __i386__ |
| 1203 | ABI=x86 |
| 1204 | #else |
| 1205 | #ifdef __ILP32__ |
| 1206 | ABI=x32 |
| 1207 | #endif |
| 1208 | #endif |
| 1209 | EOF |
| 1210 | cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` |
| 1211 | eval "$cc_set_abi" |
| 1212 | case $ABI in |
| 1213 | x86) CPU=i686 ;; |
| 1214 | x32) LIBCABI=${LIBC}x32 ;; |
| 1215 | esac |
| 1216 | fi |
| 1217 | GUESS=$CPU-pc-linux-$LIBCABI |
| 1218 | ;; |
| 1219 | xtensa*:Linux:*:*) |
| 1220 | GUESS=$UNAME_MACHINE-unknown-linux-$LIBC |
| 1221 | ;; |
| 1222 | i*86:DYNIX/ptx:4*:*) |
| @@ -1175,11 +1232,11 @@ | |
| 1232 | # I just have to hope. -- rms. |
| 1233 | # Use sysv4.2uw... so that sysv4* matches it. |
| 1234 | GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION |
| 1235 | ;; |
| 1236 | i*86:OS/2:*:*) |
| 1237 | # If we were able to find 'uname', then EMX Unix compatibility |
| 1238 | # is probably installed. |
| 1239 | GUESS=$UNAME_MACHINE-pc-os2-emx |
| 1240 | ;; |
| 1241 | i*86:XTS-300:*:STOP) |
| 1242 | GUESS=$UNAME_MACHINE-unknown-stop |
| @@ -1316,11 +1373,11 @@ | |
| 1373 | GUESS=$UNAME_MACHINE-sni-sysv4 |
| 1374 | else |
| 1375 | GUESS=ns32k-sni-sysv |
| 1376 | fi |
| 1377 | ;; |
| 1378 | PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort |
| 1379 | # says <[email protected]> |
| 1380 | GUESS=i586-unisys-sysv4 |
| 1381 | ;; |
| 1382 | *:UNIX_System_V:4*:FTX*) |
| 1383 | # From Gerald Hewes <[email protected]>. |
| @@ -1362,12 +1419,15 @@ | |
| 1419 | GUESS=i586-pc-beos |
| 1420 | ;; |
| 1421 | BePC:Haiku:*:*) # Haiku running on Intel PC compatible. |
| 1422 | GUESS=i586-pc-haiku |
| 1423 | ;; |
| 1424 | ppc:Haiku:*:*) # Haiku running on Apple PowerPC |
| 1425 | GUESS=powerpc-apple-haiku |
| 1426 | ;; |
| 1427 | *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) |
| 1428 | GUESS=$UNAME_MACHINE-unknown-haiku |
| 1429 | ;; |
| 1430 | SX-4:SUPER-UX:*:*) |
| 1431 | GUESS=sx4-nec-superux$UNAME_RELEASE |
| 1432 | ;; |
| 1433 | SX-5:SUPER-UX:*:*) |
| @@ -1520,10 +1580,13 @@ | |
| 1580 | GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL |
| 1581 | ;; |
| 1582 | i*86:rdos:*:*) |
| 1583 | GUESS=$UNAME_MACHINE-pc-rdos |
| 1584 | ;; |
| 1585 | i*86:Fiwix:*:*) |
| 1586 | GUESS=$UNAME_MACHINE-pc-fiwix |
| 1587 | ;; |
| 1588 | *:AROS:*:*) |
| 1589 | GUESS=$UNAME_MACHINE-unknown-aros |
| 1590 | ;; |
| 1591 | x86_64:VMkernel:*:*) |
| 1592 | GUESS=$UNAME_MACHINE-unknown-esx |
| @@ -1532,10 +1595,13 @@ | |
| 1595 | GUESS=x86_64-unknown-onefs |
| 1596 | ;; |
| 1597 | *:Unleashed:*:*) |
| 1598 | GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE |
| 1599 | ;; |
| 1600 | *:Ironclad:*:*) |
| 1601 | GUESS=$UNAME_MACHINE-unknown-ironclad |
| 1602 | ;; |
| 1603 | esac |
| 1604 | |
| 1605 | # Do we have a guess based on uname results? |
| 1606 | if test "x$GUESS" != x; then |
| 1607 | echo "$GUESS" |
| @@ -1555,10 +1621,11 @@ | |
| 1621 | #if defined(_SIZE_T_) || defined(SIGLOST) |
| 1622 | #include <sys/utsname.h> |
| 1623 | #endif |
| 1624 | #endif |
| 1625 | #endif |
| 1626 | int |
| 1627 | main () |
| 1628 | { |
| 1629 | #if defined (sony) |
| 1630 | #if defined (MIPSEB) |
| 1631 | /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, |
| 1632 |
+722
-241
| --- autosetup/autosetup-config.sub | ||
| +++ autosetup/autosetup-config.sub | ||
| @@ -1,16 +1,16 @@ | ||
| 1 | 1 | #! /bin/sh |
| 2 | 2 | # Configuration validation subroutine script. |
| 3 | -# Copyright 1992-2021 Free Software Foundation, Inc. | |
| 3 | +# Copyright 1992-2024 Free Software Foundation, Inc. | |
| 4 | 4 | |
| 5 | -# shellcheck disable=SC2006,SC2268 # see below for rationale | |
| 5 | +# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale | |
| 6 | 6 | |
| 7 | -timestamp='2021-07-03' | |
| 7 | +timestamp='2024-05-27' | |
| 8 | 8 | |
| 9 | 9 | # This file is free software; you can redistribute it and/or modify it |
| 10 | 10 | # under the terms of the GNU General Public License as published by |
| 11 | -# the Free Software Foundation; either version 3 of the License, or | |
| 11 | +# the Free Software Foundation, either version 3 of the License, or | |
| 12 | 12 | # (at your option) any later version. |
| 13 | 13 | # |
| 14 | 14 | # This program is distributed in the hope that it will be useful, but |
| 15 | 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| @@ -74,17 +74,17 @@ | ||
| 74 | 74 | Report bugs and patches to <[email protected]>." |
| 75 | 75 | |
| 76 | 76 | version="\ |
| 77 | 77 | GNU config.sub ($timestamp) |
| 78 | 78 | |
| 79 | -Copyright 1992-2021 Free Software Foundation, Inc. | |
| 79 | +Copyright 1992-2024 Free Software Foundation, Inc. | |
| 80 | 80 | |
| 81 | 81 | This is free software; see the source for copying conditions. There is NO |
| 82 | 82 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." |
| 83 | 83 | |
| 84 | 84 | help=" |
| 85 | -Try \`$me --help' for more information." | |
| 85 | +Try '$me --help' for more information." | |
| 86 | 86 | |
| 87 | 87 | # Parse command line |
| 88 | 88 | while test $# -gt 0 ; do |
| 89 | 89 | case $1 in |
| 90 | 90 | --time-stamp | --time* | -t ) |
| @@ -118,19 +118,20 @@ | ||
| 118 | 118 | *) echo "$me: too many arguments$help" >&2 |
| 119 | 119 | exit 1;; |
| 120 | 120 | esac |
| 121 | 121 | |
| 122 | 122 | # Split fields of configuration type |
| 123 | -# shellcheck disable=SC2162 | |
| 123 | +saved_IFS=$IFS | |
| 124 | 124 | IFS="-" read field1 field2 field3 field4 <<EOF |
| 125 | 125 | $1 |
| 126 | 126 | EOF |
| 127 | +IFS=$saved_IFS | |
| 127 | 128 | |
| 128 | 129 | # Separate into logical components for further validation |
| 129 | 130 | case $1 in |
| 130 | 131 | *-*-*-*-*) |
| 131 | - echo Invalid configuration \`"$1"\': more than four components >&2 | |
| 132 | + echo "Invalid configuration '$1': more than four components" >&2 | |
| 132 | 133 | exit 1 |
| 133 | 134 | ;; |
| 134 | 135 | *-*-*-*) |
| 135 | 136 | basic_machine=$field1-$field2 |
| 136 | 137 | basic_os=$field3-$field4 |
| @@ -138,14 +139,25 @@ | ||
| 138 | 139 | *-*-*) |
| 139 | 140 | # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two |
| 140 | 141 | # parts |
| 141 | 142 | maybe_os=$field2-$field3 |
| 142 | 143 | case $maybe_os in |
| 143 | - nto-qnx* | linux-* | uclinux-uclibc* \ | |
| 144 | - | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | |
| 145 | - | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | |
| 146 | - | storm-chaos* | os2-emx* | rtmk-nova*) | |
| 144 | + cloudabi*-eabi* \ | |
| 145 | + | kfreebsd*-gnu* \ | |
| 146 | + | knetbsd*-gnu* \ | |
| 147 | + | kopensolaris*-gnu* \ | |
| 148 | + | linux-* \ | |
| 149 | + | managarm-* \ | |
| 150 | + | netbsd*-eabi* \ | |
| 151 | + | netbsd*-gnu* \ | |
| 152 | + | nto-qnx* \ | |
| 153 | + | os2-emx* \ | |
| 154 | + | rtmk-nova* \ | |
| 155 | + | storm-chaos* \ | |
| 156 | + | uclinux-gnu* \ | |
| 157 | + | uclinux-uclibc* \ | |
| 158 | + | windows-* ) | |
| 147 | 159 | basic_machine=$field1 |
| 148 | 160 | basic_os=$maybe_os |
| 149 | 161 | ;; |
| 150 | 162 | android-linux) |
| 151 | 163 | basic_machine=$field1-unknown |
| @@ -156,37 +168,105 @@ | ||
| 156 | 168 | basic_os=$field3 |
| 157 | 169 | ;; |
| 158 | 170 | esac |
| 159 | 171 | ;; |
| 160 | 172 | *-*) |
| 161 | - # A lone config we happen to match not fitting any pattern | |
| 162 | 173 | case $field1-$field2 in |
| 174 | + # Shorthands that happen to contain a single dash | |
| 175 | + convex-c[12] | convex-c3[248]) | |
| 176 | + basic_machine=$field2-convex | |
| 177 | + basic_os= | |
| 178 | + ;; | |
| 163 | 179 | decstation-3100) |
| 164 | 180 | basic_machine=mips-dec |
| 165 | 181 | basic_os= |
| 166 | 182 | ;; |
| 167 | 183 | *-*) |
| 168 | 184 | # Second component is usually, but not always the OS |
| 169 | 185 | case $field2 in |
| 170 | - # Prevent following clause from handling this valid os | |
| 186 | + # Do not treat sunos as a manufacturer | |
| 171 | 187 | sun*os*) |
| 172 | 188 | basic_machine=$field1 |
| 173 | 189 | basic_os=$field2 |
| 174 | 190 | ;; |
| 175 | 191 | # Manufacturers |
| 176 | - dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | |
| 177 | - | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | |
| 178 | - | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | |
| 179 | - | convergent* | ncr* | news | 32* | 3600* | 3100* \ | |
| 180 | - | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | |
| 181 | - | ultra | tti* | harris | dolphin | highlevel | gould \ | |
| 182 | - | cbm | ns | masscomp | apple | axis | knuth | cray \ | |
| 183 | - | microblaze* | sim | cisco \ | |
| 184 | - | oki | wec | wrs | winbond) | |
| 192 | + 3100* \ | |
| 193 | + | 32* \ | |
| 194 | + | 3300* \ | |
| 195 | + | 3600* \ | |
| 196 | + | 7300* \ | |
| 197 | + | acorn \ | |
| 198 | + | altos* \ | |
| 199 | + | apollo \ | |
| 200 | + | apple \ | |
| 201 | + | atari \ | |
| 202 | + | att* \ | |
| 203 | + | axis \ | |
| 204 | + | be \ | |
| 205 | + | bull \ | |
| 206 | + | cbm \ | |
| 207 | + | ccur \ | |
| 208 | + | cisco \ | |
| 209 | + | commodore \ | |
| 210 | + | convergent* \ | |
| 211 | + | convex* \ | |
| 212 | + | cray \ | |
| 213 | + | crds \ | |
| 214 | + | dec* \ | |
| 215 | + | delta* \ | |
| 216 | + | dg \ | |
| 217 | + | digital \ | |
| 218 | + | dolphin \ | |
| 219 | + | encore* \ | |
| 220 | + | gould \ | |
| 221 | + | harris \ | |
| 222 | + | highlevel \ | |
| 223 | + | hitachi* \ | |
| 224 | + | hp \ | |
| 225 | + | ibm* \ | |
| 226 | + | intergraph \ | |
| 227 | + | isi* \ | |
| 228 | + | knuth \ | |
| 229 | + | masscomp \ | |
| 230 | + | microblaze* \ | |
| 231 | + | mips* \ | |
| 232 | + | motorola* \ | |
| 233 | + | ncr* \ | |
| 234 | + | news \ | |
| 235 | + | next \ | |
| 236 | + | ns \ | |
| 237 | + | oki \ | |
| 238 | + | omron* \ | |
| 239 | + | pc533* \ | |
| 240 | + | rebel \ | |
| 241 | + | rom68k \ | |
| 242 | + | rombug \ | |
| 243 | + | semi \ | |
| 244 | + | sequent* \ | |
| 245 | + | siemens \ | |
| 246 | + | sgi* \ | |
| 247 | + | siemens \ | |
| 248 | + | sim \ | |
| 249 | + | sni \ | |
| 250 | + | sony* \ | |
| 251 | + | stratus \ | |
| 252 | + | sun \ | |
| 253 | + | sun[234]* \ | |
| 254 | + | tektronix \ | |
| 255 | + | tti* \ | |
| 256 | + | ultra \ | |
| 257 | + | unicom* \ | |
| 258 | + | wec \ | |
| 259 | + | winbond \ | |
| 260 | + | wrs) | |
| 185 | 261 | basic_machine=$field1-$field2 |
| 186 | 262 | basic_os= |
| 187 | 263 | ;; |
| 264 | + zephyr*) | |
| 265 | + basic_machine=$field1-unknown | |
| 266 | + basic_os=$field2 | |
| 267 | + ;; | |
| 188 | 268 | *) |
| 189 | 269 | basic_machine=$field1 |
| 190 | 270 | basic_os=$field2 |
| 191 | 271 | ;; |
| 192 | 272 | esac |
| @@ -263,30 +343,10 @@ | ||
| 263 | 343 | ;; |
| 264 | 344 | cegcc) |
| 265 | 345 | basic_machine=arm-unknown |
| 266 | 346 | basic_os=cegcc |
| 267 | 347 | ;; |
| 268 | - convex-c1) | |
| 269 | - basic_machine=c1-convex | |
| 270 | - basic_os=bsd | |
| 271 | - ;; | |
| 272 | - convex-c2) | |
| 273 | - basic_machine=c2-convex | |
| 274 | - basic_os=bsd | |
| 275 | - ;; | |
| 276 | - convex-c32) | |
| 277 | - basic_machine=c32-convex | |
| 278 | - basic_os=bsd | |
| 279 | - ;; | |
| 280 | - convex-c34) | |
| 281 | - basic_machine=c34-convex | |
| 282 | - basic_os=bsd | |
| 283 | - ;; | |
| 284 | - convex-c38) | |
| 285 | - basic_machine=c38-convex | |
| 286 | - basic_os=bsd | |
| 287 | - ;; | |
| 288 | 348 | cray) |
| 289 | 349 | basic_machine=j90-cray |
| 290 | 350 | basic_os=unicos |
| 291 | 351 | ;; |
| 292 | 352 | crds | unos) |
| @@ -705,13 +765,21 @@ | ||
| 705 | 765 | decsystem20* | dec20*) |
| 706 | 766 | cpu=pdp10 |
| 707 | 767 | vendor=dec |
| 708 | 768 | basic_os=tops20 |
| 709 | 769 | ;; |
| 710 | - delta | 3300 | motorola-3300 | motorola-delta \ | |
| 711 | - | 3300-motorola | delta-motorola) | |
| 770 | + delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) | |
| 712 | 771 | cpu=m68k |
| 713 | 772 | vendor=motorola |
| 714 | 773 | ;; |
| 715 | - dpx2*) | |
| 774 | + # This used to be dpx2*, but that gets the RS6000-based | |
| 775 | + # DPX/20 and the x86-based DPX/2-100 wrong. See | |
| 776 | + # https://oldskool.silicium.org/stations/bull_dpx20.htm | |
| 777 | + # https://www.feb-patrimoine.com/english/bull_dpx2.htm | |
| 778 | + # https://www.feb-patrimoine.com/english/unix_and_bull.htm | |
| 779 | + dpx2 | dpx2[23]00 | dpx2[23]xx) | |
| 716 | 780 | cpu=m68k |
| 717 | 781 | vendor=bull |
| 782 | + ;; | |
| 783 | + dpx2100 | dpx21xx) | |
| 784 | + cpu=i386 | |
| 785 | + vendor=bull | |
| @@ -718,6 +786,9 @@ | ||
| 718 | - basic_os=sysv3 | |
| 786 | + ;; | |
| 787 | + dpx20) | |
| 788 | + cpu=rs6000 | |
| 789 | + vendor=bull | |
| 719 | 790 | ;; |
| 720 | 791 | encore | umax | mmax) |
| 721 | 792 | cpu=ns32k |
| 722 | 793 | vendor=encore |
| 723 | 794 | ;; |
| @@ -828,22 +899,10 @@ | ||
| 828 | 899 | basic_os=newsos |
| 829 | 900 | ;; |
| 830 | 901 | next | m*-next) |
| 831 | 902 | cpu=m68k |
| 832 | 903 | vendor=next |
| 833 | - case $basic_os in | |
| 834 | - openstep*) | |
| 835 | - ;; | |
| 836 | - nextstep*) | |
| 837 | - ;; | |
| 838 | - ns2*) | |
| 839 | - basic_os=nextstep2 | |
| 840 | - ;; | |
| 841 | - *) | |
| 842 | - basic_os=nextstep3 | |
| 843 | - ;; | |
| 844 | - esac | |
| 845 | 904 | ;; |
| 846 | 905 | np1) |
| 847 | 906 | cpu=np1 |
| 848 | 907 | vendor=gould |
| 849 | 908 | ;; |
| @@ -928,16 +987,17 @@ | ||
| 928 | 987 | cpu=sparc |
| 929 | 988 | vendor=`echo "$basic_machine" | sed 's/-.*//'` |
| 930 | 989 | ;; |
| 931 | 990 | |
| 932 | 991 | *-*) |
| 933 | - # shellcheck disable=SC2162 | |
| 992 | + saved_IFS=$IFS | |
| 934 | 993 | IFS="-" read cpu vendor <<EOF |
| 935 | 994 | $basic_machine |
| 936 | 995 | EOF |
| 996 | + IFS=$saved_IFS | |
| 937 | 997 | ;; |
| 938 | - # We use `pc' rather than `unknown' | |
| 998 | + # We use 'pc' rather than 'unknown' | |
| 939 | 999 | # because (1) that's what they normally are, and |
| 940 | 1000 | # (2) the word "unknown" tends to confuse beginning users. |
| 941 | 1001 | i*86 | x86_64) |
| 942 | 1002 | cpu=$basic_machine |
| 943 | 1003 | vendor=pc |
| @@ -961,19 +1021,23 @@ | ||
| 961 | 1021 | |
| 962 | 1022 | unset -v basic_machine |
| 963 | 1023 | |
| 964 | 1024 | # Decode basic machines in the full and proper CPU-Company form. |
| 965 | 1025 | case $cpu-$vendor in |
| 966 | - # Here we handle the default manufacturer of certain CPU types in canonical form. It is in | |
| 967 | - # some cases the only manufacturer, in others, it is the most popular. | |
| 1026 | + # Here we handle the default manufacturer of certain CPU types in canonical form. | |
| 1027 | + # It is in some cases the only manufacturer, in others, it is the most popular. | |
| 1028 | + c[12]-convex | c[12]-unknown | c3[248]-convex | c3[248]-unknown) | |
| 1029 | + vendor=convex | |
| 1030 | + basic_os=${basic_os:-bsd} | |
| 1031 | + ;; | |
| 968 | 1032 | craynv-unknown) |
| 969 | 1033 | vendor=cray |
| 970 | 1034 | basic_os=${basic_os:-unicosmp} |
| 971 | 1035 | ;; |
| 972 | 1036 | c90-unknown | c90-cray) |
| 973 | 1037 | vendor=cray |
| 974 | - basic_os=${Basic_os:-unicos} | |
| 1038 | + basic_os=${basic_os:-unicos} | |
| 975 | 1039 | ;; |
| 976 | 1040 | fx80-unknown) |
| 977 | 1041 | vendor=alliant |
| 978 | 1042 | ;; |
| 979 | 1043 | romp-unknown) |
| @@ -1010,23 +1074,46 @@ | ||
| 1010 | 1074 | cpu=xps100 |
| 1011 | 1075 | vendor=honeywell |
| 1012 | 1076 | ;; |
| 1013 | 1077 | |
| 1014 | 1078 | # Here we normalize CPU types with a missing or matching vendor |
| 1015 | - dpx20-unknown | dpx20-bull) | |
| 1016 | - cpu=rs6000 | |
| 1017 | - vendor=bull | |
| 1079 | + armh-unknown | armh-alt) | |
| 1080 | + cpu=armv7l | |
| 1081 | + vendor=alt | |
| 1082 | + basic_os=${basic_os:-linux-gnueabihf} | |
| 1083 | + ;; | |
| 1084 | + | |
| 1085 | + # Normalized CPU+vendor pairs that imply an OS, if not otherwise specified | |
| 1086 | + m68k-isi) | |
| 1087 | + basic_os=${basic_os:-sysv} | |
| 1088 | + ;; | |
| 1089 | + m68k-sony) | |
| 1090 | + basic_os=${basic_os:-newsos} | |
| 1091 | + ;; | |
| 1092 | + m68k-tektronix) | |
| 1093 | + basic_os=${basic_os:-bsd} | |
| 1094 | + ;; | |
| 1095 | + m88k-harris) | |
| 1096 | + basic_os=${basic_os:-sysv3} | |
| 1097 | + ;; | |
| 1098 | + i386-bull | m68k-bull) | |
| 1099 | + basic_os=${basic_os:-sysv3} | |
| 1100 | + ;; | |
| 1101 | + rs6000-bull) | |
| 1018 | 1102 | basic_os=${basic_os:-bosx} |
| 1019 | 1103 | ;; |
| 1104 | + mips-sni) | |
| 1105 | + basic_os=${basic_os:-sysv4} | |
| 1106 | + ;; | |
| 1020 | 1107 | |
| 1021 | 1108 | # Here we normalize CPU types irrespective of the vendor |
| 1022 | 1109 | amd64-*) |
| 1023 | 1110 | cpu=x86_64 |
| 1024 | 1111 | ;; |
| 1025 | 1112 | blackfin-*) |
| 1026 | 1113 | cpu=bfin |
| 1027 | - basic_os=linux | |
| 1114 | + basic_os=${basic_os:-linux} | |
| 1028 | 1115 | ;; |
| 1029 | 1116 | c54x-*) |
| 1030 | 1117 | cpu=tic54x |
| 1031 | 1118 | ;; |
| 1032 | 1119 | c55x-*) |
| @@ -1045,37 +1132,34 @@ | ||
| 1045 | 1132 | ms1-*) |
| 1046 | 1133 | cpu=mt |
| 1047 | 1134 | ;; |
| 1048 | 1135 | m68knommu-*) |
| 1049 | 1136 | cpu=m68k |
| 1050 | - basic_os=linux | |
| 1137 | + basic_os=${basic_os:-linux} | |
| 1051 | 1138 | ;; |
| 1052 | 1139 | m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) |
| 1053 | 1140 | cpu=s12z |
| 1054 | 1141 | ;; |
| 1055 | 1142 | openrisc-*) |
| 1056 | 1143 | cpu=or32 |
| 1057 | 1144 | ;; |
| 1058 | 1145 | parisc-*) |
| 1059 | 1146 | cpu=hppa |
| 1060 | - basic_os=linux | |
| 1147 | + basic_os=${basic_os:-linux} | |
| 1061 | 1148 | ;; |
| 1062 | 1149 | pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) |
| 1063 | 1150 | cpu=i586 |
| 1064 | 1151 | ;; |
| 1065 | - pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*) | |
| 1152 | + pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*) | |
| 1066 | 1153 | cpu=i686 |
| 1067 | 1154 | ;; |
| 1068 | 1155 | pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) |
| 1069 | 1156 | cpu=i686 |
| 1070 | 1157 | ;; |
| 1071 | 1158 | pentium4-*) |
| 1072 | 1159 | cpu=i786 |
| 1073 | 1160 | ;; |
| 1074 | - pc98-*) | |
| 1075 | - cpu=i386 | |
| 1076 | - ;; | |
| 1077 | 1161 | ppc-* | ppcbe-*) |
| 1078 | 1162 | cpu=powerpc |
| 1079 | 1163 | ;; |
| 1080 | 1164 | ppcle-* | powerpclittle-*) |
| 1081 | 1165 | cpu=powerpcle |
| @@ -1105,17 +1189,14 @@ | ||
| 1105 | 1189 | cpu=mipstx39 |
| 1106 | 1190 | ;; |
| 1107 | 1191 | tx39el-*) |
| 1108 | 1192 | cpu=mipstx39el |
| 1109 | 1193 | ;; |
| 1110 | - x64-*) | |
| 1111 | - cpu=x86_64 | |
| 1112 | - ;; | |
| 1113 | 1194 | xscale-* | xscalee[bl]-*) |
| 1114 | 1195 | cpu=`echo "$cpu" | sed 's/^xscale/arm/'` |
| 1115 | 1196 | ;; |
| 1116 | - arm64-*) | |
| 1197 | + arm64-* | aarch64le-*) | |
| 1117 | 1198 | cpu=aarch64 |
| 1118 | 1199 | ;; |
| 1119 | 1200 | |
| 1120 | 1201 | # Recognize the canonical CPU Types that limit and/or modify the |
| 1121 | 1202 | # company names they are paired with. |
| @@ -1163,118 +1244,235 @@ | ||
| 1163 | 1244 | |
| 1164 | 1245 | *) |
| 1165 | 1246 | # Recognize the canonical CPU types that are allowed with any |
| 1166 | 1247 | # company name. |
| 1167 | 1248 | case $cpu in |
| 1168 | - 1750a | 580 \ | |
| 1249 | + 1750a \ | |
| 1250 | + | 580 \ | |
| 1251 | + | [cjt]90 \ | |
| 1169 | 1252 | | a29k \ |
| 1170 | - | aarch64 | aarch64_be \ | |
| 1253 | + | aarch64 \ | |
| 1254 | + | aarch64_be \ | |
| 1255 | + | aarch64c \ | |
| 1171 | 1256 | | abacus \ |
| 1172 | - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ | |
| 1173 | - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ | |
| 1174 | - | alphapca5[67] | alpha64pca5[67] \ | |
| 1257 | + | alpha \ | |
| 1258 | + | alpha64 \ | |
| 1259 | + | alpha64ev56 \ | |
| 1260 | + | alpha64ev6[78] \ | |
| 1261 | + | alpha64ev[4-8] \ | |
| 1262 | + | alpha64pca5[67] \ | |
| 1263 | + | alphaev56 \ | |
| 1264 | + | alphaev6[78] \ | |
| 1265 | + | alphaev[4-8] \ | |
| 1266 | + | alphapca5[67] \ | |
| 1175 | 1267 | | am33_2.0 \ |
| 1176 | 1268 | | amdgcn \ |
| 1177 | - | arc | arceb | arc32 | arc64 \ | |
| 1178 | - | arm | arm[lb]e | arme[lb] | armv* \ | |
| 1179 | - | avr | avr32 \ | |
| 1269 | + | arc \ | |
| 1270 | + | arc32 \ | |
| 1271 | + | arc64 \ | |
| 1272 | + | arceb \ | |
| 1273 | + | arm \ | |
| 1274 | + | arm64e \ | |
| 1275 | + | arm64ec \ | |
| 1276 | + | arm[lb]e \ | |
| 1277 | + | arme[lb] \ | |
| 1278 | + | armv* \ | |
| 1180 | 1279 | | asmjs \ |
| 1280 | + | avr \ | |
| 1281 | + | avr32 \ | |
| 1181 | 1282 | | ba \ |
| 1182 | - | be32 | be64 \ | |
| 1183 | - | bfin | bpf | bs2000 \ | |
| 1184 | - | c[123]* | c30 | [cjt]90 | c4x \ | |
| 1185 | - | c8051 | clipper | craynv | csky | cydra \ | |
| 1186 | - | d10v | d30v | dlx | dsp16xx \ | |
| 1187 | - | e2k | elxsi | epiphany \ | |
| 1188 | - | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ | |
| 1189 | - | h8300 | h8500 \ | |
| 1190 | - | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | |
| 1283 | + | be32 \ | |
| 1284 | + | be64 \ | |
| 1285 | + | bfin \ | |
| 1286 | + | bpf \ | |
| 1287 | + | bs2000 \ | |
| 1288 | + | c30 \ | |
| 1289 | + | c4x \ | |
| 1290 | + | c8051 \ | |
| 1291 | + | c[123]* \ | |
| 1292 | + | clipper \ | |
| 1293 | + | craynv \ | |
| 1294 | + | csky \ | |
| 1295 | + | cydra \ | |
| 1296 | + | d10v \ | |
| 1297 | + | d30v \ | |
| 1298 | + | dlx \ | |
| 1299 | + | dsp16xx \ | |
| 1300 | + | e2k \ | |
| 1301 | + | elxsi \ | |
| 1302 | + | epiphany \ | |
| 1303 | + | f30[01] \ | |
| 1304 | + | f700 \ | |
| 1305 | + | fido \ | |
| 1306 | + | fr30 \ | |
| 1307 | + | frv \ | |
| 1308 | + | ft32 \ | |
| 1309 | + | fx80 \ | |
| 1310 | + | h8300 \ | |
| 1311 | + | h8500 \ | |
| 1191 | 1312 | | hexagon \ |
| 1192 | - | i370 | i*86 | i860 | i960 | ia16 | ia64 \ | |
| 1193 | - | ip2k | iq2000 \ | |
| 1313 | + | hppa \ | |
| 1314 | + | hppa1.[01] \ | |
| 1315 | + | hppa2.0 \ | |
| 1316 | + | hppa2.0[nw] \ | |
| 1317 | + | hppa64 \ | |
| 1318 | + | i*86 \ | |
| 1319 | + | i370 \ | |
| 1320 | + | i860 \ | |
| 1321 | + | i960 \ | |
| 1322 | + | ia16 \ | |
| 1323 | + | ia64 \ | |
| 1324 | + | ip2k \ | |
| 1325 | + | iq2000 \ | |
| 1326 | + | javascript \ | |
| 1194 | 1327 | | k1om \ |
| 1195 | - | le32 | le64 \ | |
| 1328 | + | kvx \ | |
| 1329 | + | le32 \ | |
| 1330 | + | le64 \ | |
| 1196 | 1331 | | lm32 \ |
| 1197 | - | loongarch32 | loongarch64 | loongarchx32 \ | |
| 1198 | - | m32c | m32r | m32rle \ | |
| 1199 | - | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ | |
| 1200 | - | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ | |
| 1201 | - | m88110 | m88k | maxq | mb | mcore | mep | metag \ | |
| 1202 | - | microblaze | microblazeel \ | |
| 1203 | - | mips | mipsbe | mipseb | mipsel | mipsle \ | |
| 1204 | - | mips16 \ | |
| 1205 | - | mips64 | mips64eb | mips64el \ | |
| 1206 | - | mips64octeon | mips64octeonel \ | |
| 1207 | - | mips64orion | mips64orionel \ | |
| 1208 | - | mips64r5900 | mips64r5900el \ | |
| 1209 | - | mips64vr | mips64vrel \ | |
| 1210 | - | mips64vr4100 | mips64vr4100el \ | |
| 1211 | - | mips64vr4300 | mips64vr4300el \ | |
| 1212 | - | mips64vr5000 | mips64vr5000el \ | |
| 1213 | - | mips64vr5900 | mips64vr5900el \ | |
| 1214 | - | mipsisa32 | mipsisa32el \ | |
| 1215 | - | mipsisa32r2 | mipsisa32r2el \ | |
| 1216 | - | mipsisa32r3 | mipsisa32r3el \ | |
| 1217 | - | mipsisa32r5 | mipsisa32r5el \ | |
| 1218 | - | mipsisa32r6 | mipsisa32r6el \ | |
| 1219 | - | mipsisa64 | mipsisa64el \ | |
| 1220 | - | mipsisa64r2 | mipsisa64r2el \ | |
| 1221 | - | mipsisa64r3 | mipsisa64r3el \ | |
| 1222 | - | mipsisa64r5 | mipsisa64r5el \ | |
| 1223 | - | mipsisa64r6 | mipsisa64r6el \ | |
| 1224 | - | mipsisa64sb1 | mipsisa64sb1el \ | |
| 1225 | - | mipsisa64sr71k | mipsisa64sr71kel \ | |
| 1226 | - | mipsr5900 | mipsr5900el \ | |
| 1227 | - | mipstx39 | mipstx39el \ | |
| 1332 | + | loongarch32 \ | |
| 1333 | + | loongarch64 \ | |
| 1334 | + | m32c \ | |
| 1335 | + | m32r \ | |
| 1336 | + | m32rle \ | |
| 1337 | + | m5200 \ | |
| 1338 | + | m68000 \ | |
| 1339 | + | m680[012346]0 \ | |
| 1340 | + | m6811 \ | |
| 1341 | + | m6812 \ | |
| 1342 | + | m68360 \ | |
| 1343 | + | m683?2 \ | |
| 1344 | + | m68hc11 \ | |
| 1345 | + | m68hc12 \ | |
| 1346 | + | m68hcs12x \ | |
| 1347 | + | m68k \ | |
| 1348 | + | m88110 \ | |
| 1349 | + | m88k \ | |
| 1350 | + | maxq \ | |
| 1351 | + | mb \ | |
| 1352 | + | mcore \ | |
| 1353 | + | mep \ | |
| 1354 | + | metag \ | |
| 1355 | + | microblaze \ | |
| 1356 | + | microblazeel \ | |
| 1357 | + | mips* \ | |
| 1228 | 1358 | | mmix \ |
| 1229 | - | mn10200 | mn10300 \ | |
| 1359 | + | mn10200 \ | |
| 1360 | + | mn10300 \ | |
| 1230 | 1361 | | moxie \ |
| 1231 | - | mt \ | |
| 1232 | 1362 | | msp430 \ |
| 1233 | - | nds32 | nds32le | nds32be \ | |
| 1363 | + | mt \ | |
| 1364 | + | nanomips* \ | |
| 1365 | + | nds32 \ | |
| 1366 | + | nds32be \ | |
| 1367 | + | nds32le \ | |
| 1234 | 1368 | | nfp \ |
| 1235 | - | nios | nios2 | nios2eb | nios2el \ | |
| 1236 | - | none | np1 | ns16k | ns32k | nvptx \ | |
| 1369 | + | nios \ | |
| 1370 | + | nios2 \ | |
| 1371 | + | nios2eb \ | |
| 1372 | + | nios2el \ | |
| 1373 | + | none \ | |
| 1374 | + | np1 \ | |
| 1375 | + | ns16k \ | |
| 1376 | + | ns32k \ | |
| 1377 | + | nvptx \ | |
| 1237 | 1378 | | open8 \ |
| 1238 | 1379 | | or1k* \ |
| 1239 | 1380 | | or32 \ |
| 1240 | 1381 | | orion \ |
| 1382 | + | pdp10 \ | |
| 1383 | + | pdp11 \ | |
| 1241 | 1384 | | picochip \ |
| 1242 | - | pdp10 | pdp11 | pj | pjl | pn | power \ | |
| 1243 | - | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ | |
| 1385 | + | pj \ | |
| 1386 | + | pjl \ | |
| 1387 | + | pn \ | |
| 1388 | + | power \ | |
| 1389 | + | powerpc \ | |
| 1390 | + | powerpc64 \ | |
| 1391 | + | powerpc64le \ | |
| 1392 | + | powerpcle \ | |
| 1393 | + | powerpcspe \ | |
| 1244 | 1394 | | pru \ |
| 1245 | 1395 | | pyramid \ |
| 1246 | - | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ | |
| 1247 | - | rl78 | romp | rs6000 | rx \ | |
| 1248 | - | s390 | s390x \ | |
| 1396 | + | riscv \ | |
| 1397 | + | riscv32 \ | |
| 1398 | + | riscv32be \ | |
| 1399 | + | riscv64 \ | |
| 1400 | + | riscv64be \ | |
| 1401 | + | rl78 \ | |
| 1402 | + | romp \ | |
| 1403 | + | rs6000 \ | |
| 1404 | + | rx \ | |
| 1405 | + | s390 \ | |
| 1406 | + | s390x \ | |
| 1249 | 1407 | | score \ |
| 1250 | - | sh | shl \ | |
| 1251 | - | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ | |
| 1252 | - | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ | |
| 1253 | - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ | |
| 1408 | + | sh \ | |
| 1409 | + | sh64 \ | |
| 1410 | + | sh64le \ | |
| 1411 | + | sh[12345][lb]e \ | |
| 1412 | + | sh[1234] \ | |
| 1413 | + | sh[1234]e[lb] \ | |
| 1414 | + | sh[23]e \ | |
| 1415 | + | sh[23]ele \ | |
| 1416 | + | sh[24]a \ | |
| 1417 | + | sh[24]ae[lb] \ | |
| 1418 | + | sh[lb]e \ | |
| 1419 | + | she[lb] \ | |
| 1420 | + | shl \ | |
| 1421 | + | sparc \ | |
| 1422 | + | sparc64 \ | |
| 1423 | + | sparc64b \ | |
| 1424 | + | sparc64v \ | |
| 1425 | + | sparc86x \ | |
| 1426 | + | sparclet \ | |
| 1254 | 1427 | | sparclite \ |
| 1255 | - | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ | |
| 1428 | + | sparcv8 \ | |
| 1429 | + | sparcv9 \ | |
| 1430 | + | sparcv9b \ | |
| 1431 | + | sparcv9v \ | |
| 1256 | 1432 | | spu \ |
| 1433 | + | sv1 \ | |
| 1434 | + | sx* \ | |
| 1257 | 1435 | | tahoe \ |
| 1258 | 1436 | | thumbv7* \ |
| 1259 | - | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ | |
| 1437 | + | tic30 \ | |
| 1438 | + | tic4x \ | |
| 1439 | + | tic54x \ | |
| 1440 | + | tic55x \ | |
| 1441 | + | tic6x \ | |
| 1442 | + | tic80 \ | |
| 1260 | 1443 | | tron \ |
| 1261 | 1444 | | ubicom32 \ |
| 1262 | - | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ | |
| 1445 | + | v70 \ | |
| 1446 | + | v810 \ | |
| 1447 | + | v850 \ | |
| 1448 | + | v850e \ | |
| 1449 | + | v850e1 \ | |
| 1450 | + | v850e2 \ | |
| 1451 | + | v850e2v3 \ | |
| 1452 | + | v850es \ | |
| 1263 | 1453 | | vax \ |
| 1454 | + | vc4 \ | |
| 1264 | 1455 | | visium \ |
| 1265 | 1456 | | w65 \ |
| 1266 | - | wasm32 | wasm64 \ | |
| 1457 | + | wasm32 \ | |
| 1458 | + | wasm64 \ | |
| 1267 | 1459 | | we32k \ |
| 1268 | - | x86 | x86_64 | xc16x | xgate | xps100 \ | |
| 1269 | - | xstormy16 | xtensa* \ | |
| 1460 | + | x86 \ | |
| 1461 | + | x86_64 \ | |
| 1462 | + | xc16x \ | |
| 1463 | + | xgate \ | |
| 1464 | + | xps100 \ | |
| 1465 | + | xstormy16 \ | |
| 1466 | + | xtensa* \ | |
| 1270 | 1467 | | ymp \ |
| 1271 | - | z8k | z80) | |
| 1468 | + | z80 \ | |
| 1469 | + | z8k) | |
| 1272 | 1470 | ;; |
| 1273 | 1471 | |
| 1274 | 1472 | *) |
| 1275 | - echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 | |
| 1473 | + echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2 | |
| 1276 | 1474 | exit 1 |
| 1277 | 1475 | ;; |
| 1278 | 1476 | esac |
| 1279 | 1477 | ;; |
| 1280 | 1478 | esac |
| @@ -1291,15 +1489,16 @@ | ||
| 1291 | 1489 | ;; |
| 1292 | 1490 | esac |
| 1293 | 1491 | |
| 1294 | 1492 | # Decode manufacturer-specific aliases for certain operating systems. |
| 1295 | 1493 | |
| 1296 | -if test x$basic_os != x | |
| 1494 | +if test x"$basic_os" != x | |
| 1297 | 1495 | then |
| 1298 | 1496 | |
| 1299 | -# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just | |
| 1497 | +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just | |
| 1300 | 1498 | # set os. |
| 1499 | +obj= | |
| 1301 | 1500 | case $basic_os in |
| 1302 | 1501 | gnu/linux*) |
| 1303 | 1502 | kernel=linux |
| 1304 | 1503 | os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` |
| 1305 | 1504 | ;; |
| @@ -1310,14 +1509,15 @@ | ||
| 1310 | 1509 | nto-qnx*) |
| 1311 | 1510 | kernel=nto |
| 1312 | 1511 | os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` |
| 1313 | 1512 | ;; |
| 1314 | 1513 | *-*) |
| 1315 | - # shellcheck disable=SC2162 | |
| 1514 | + saved_IFS=$IFS | |
| 1316 | 1515 | IFS="-" read kernel os <<EOF |
| 1317 | 1516 | $basic_os |
| 1318 | 1517 | EOF |
| 1518 | + IFS=$saved_IFS | |
| 1319 | 1519 | ;; |
| 1320 | 1520 | # Default OS when just kernel was specified |
| 1321 | 1521 | nto*) |
| 1322 | 1522 | kernel=nto |
| 1323 | 1523 | os=`echo "$basic_os" | sed -e 's|nto|qnx|'` |
| @@ -1324,10 +1524,14 @@ | ||
| 1324 | 1524 | ;; |
| 1325 | 1525 | linux*) |
| 1326 | 1526 | kernel=linux |
| 1327 | 1527 | os=`echo "$basic_os" | sed -e 's|linux|gnu|'` |
| 1328 | 1528 | ;; |
| 1529 | + managarm*) | |
| 1530 | + kernel=managarm | |
| 1531 | + os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'` | |
| 1532 | + ;; | |
| 1329 | 1533 | *) |
| 1330 | 1534 | kernel= |
| 1331 | 1535 | os=$basic_os |
| 1332 | 1536 | ;; |
| 1333 | 1537 | esac |
| @@ -1350,10 +1554,27 @@ | ||
| 1350 | 1554 | solaris) |
| 1351 | 1555 | os=solaris2 |
| 1352 | 1556 | ;; |
| 1353 | 1557 | unixware*) |
| 1354 | 1558 | os=sysv4.2uw |
| 1559 | + ;; | |
| 1560 | + # The marketing names for NeXT's operating systems were | |
| 1561 | + # NeXTSTEP, NeXTSTEP 2, OpenSTEP 3, OpenSTEP 4. 'openstep' is | |
| 1562 | + # mapped to 'openstep3', but 'openstep1' and 'openstep2' are | |
| 1563 | + # mapped to 'nextstep' and 'nextstep2', consistent with the | |
| 1564 | + # treatment of SunOS/Solaris. | |
| 1565 | + ns | ns1 | nextstep | nextstep1 | openstep1) | |
| 1566 | + os=nextstep | |
| 1567 | + ;; | |
| 1568 | + ns2 | nextstep2 | openstep2) | |
| 1569 | + os=nextstep2 | |
| 1570 | + ;; | |
| 1571 | + ns3 | nextstep3 | openstep | openstep3) | |
| 1572 | + os=openstep3 | |
| 1573 | + ;; | |
| 1574 | + ns4 | nextstep4 | openstep4) | |
| 1575 | + os=openstep4 | |
| 1355 | 1576 | ;; |
| 1356 | 1577 | # es1800 is here to avoid being matched by es* (a different OS) |
| 1357 | 1578 | es1800*) |
| 1358 | 1579 | os=ose |
| 1359 | 1580 | ;; |
| @@ -1421,10 +1642,11 @@ | ||
| 1421 | 1642 | wince*) |
| 1422 | 1643 | os=wince |
| 1423 | 1644 | ;; |
| 1424 | 1645 | utek*) |
| 1425 | 1646 | os=bsd |
| 1647 | + vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'` | |
| 1426 | 1648 | ;; |
| 1427 | 1649 | dynix*) |
| 1428 | 1650 | os=bsd |
| 1429 | 1651 | ;; |
| 1430 | 1652 | acis*) |
| @@ -1437,25 +1659,29 @@ | ||
| 1437 | 1659 | os=syllable |
| 1438 | 1660 | ;; |
| 1439 | 1661 | 386bsd) |
| 1440 | 1662 | os=bsd |
| 1441 | 1663 | ;; |
| 1442 | - ctix* | uts*) | |
| 1664 | + ctix*) | |
| 1665 | + os=sysv | |
| 1666 | + vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'` | |
| 1667 | + ;; | |
| 1668 | + uts*) | |
| 1443 | 1669 | os=sysv |
| 1444 | 1670 | ;; |
| 1445 | 1671 | nova*) |
| 1446 | - os=rtmk-nova | |
| 1447 | - ;; | |
| 1448 | - ns2) | |
| 1449 | - os=nextstep2 | |
| 1672 | + kernel=rtmk | |
| 1673 | + os=nova | |
| 1450 | 1674 | ;; |
| 1451 | 1675 | # Preserve the version number of sinix5. |
| 1452 | 1676 | sinix5.*) |
| 1453 | 1677 | os=`echo "$os" | sed -e 's|sinix|sysv|'` |
| 1678 | + vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` | |
| 1454 | 1679 | ;; |
| 1455 | 1680 | sinix*) |
| 1456 | 1681 | os=sysv4 |
| 1682 | + vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` | |
| 1457 | 1683 | ;; |
| 1458 | 1684 | tpf*) |
| 1459 | 1685 | os=tpf |
| 1460 | 1686 | ;; |
| 1461 | 1687 | triton*) |
| @@ -1489,13 +1715,19 @@ | ||
| 1489 | 1715 | case $cpu in |
| 1490 | 1716 | arm*) |
| 1491 | 1717 | os=eabi |
| 1492 | 1718 | ;; |
| 1493 | 1719 | *) |
| 1494 | - os=elf | |
| 1720 | + os= | |
| 1721 | + obj=elf | |
| 1495 | 1722 | ;; |
| 1496 | 1723 | esac |
| 1724 | + ;; | |
| 1725 | + aout* | coff* | elf* | pe*) | |
| 1726 | + # These are machine code file formats, not OSes | |
| 1727 | + obj=$os | |
| 1728 | + os= | |
| 1497 | 1729 | ;; |
| 1498 | 1730 | *) |
| 1499 | 1731 | # No normalization, but not necessarily accepted, that comes below. |
| 1500 | 1732 | ;; |
| 1501 | 1733 | esac |
| @@ -1511,47 +1743,57 @@ | ||
| 1511 | 1743 | # that MANUFACTURER isn't an operating system. Otherwise, code above |
| 1512 | 1744 | # will signal an error saying that MANUFACTURER isn't an operating |
| 1513 | 1745 | # system, and we'll never get to this point. |
| 1514 | 1746 | |
| 1515 | 1747 | kernel= |
| 1748 | +obj= | |
| 1516 | 1749 | case $cpu-$vendor in |
| 1517 | 1750 | score-*) |
| 1518 | - os=elf | |
| 1751 | + os= | |
| 1752 | + obj=elf | |
| 1519 | 1753 | ;; |
| 1520 | 1754 | spu-*) |
| 1521 | - os=elf | |
| 1755 | + os= | |
| 1756 | + obj=elf | |
| 1522 | 1757 | ;; |
| 1523 | 1758 | *-acorn) |
| 1524 | 1759 | os=riscix1.2 |
| 1525 | 1760 | ;; |
| 1526 | 1761 | arm*-rebel) |
| 1527 | 1762 | kernel=linux |
| 1528 | 1763 | os=gnu |
| 1529 | 1764 | ;; |
| 1530 | 1765 | arm*-semi) |
| 1531 | - os=aout | |
| 1766 | + os= | |
| 1767 | + obj=aout | |
| 1532 | 1768 | ;; |
| 1533 | 1769 | c4x-* | tic4x-*) |
| 1534 | - os=coff | |
| 1770 | + os= | |
| 1771 | + obj=coff | |
| 1535 | 1772 | ;; |
| 1536 | 1773 | c8051-*) |
| 1537 | - os=elf | |
| 1774 | + os= | |
| 1775 | + obj=elf | |
| 1538 | 1776 | ;; |
| 1539 | 1777 | clipper-intergraph) |
| 1540 | 1778 | os=clix |
| 1541 | 1779 | ;; |
| 1542 | 1780 | hexagon-*) |
| 1543 | - os=elf | |
| 1781 | + os= | |
| 1782 | + obj=elf | |
| 1544 | 1783 | ;; |
| 1545 | 1784 | tic54x-*) |
| 1546 | - os=coff | |
| 1785 | + os= | |
| 1786 | + obj=coff | |
| 1547 | 1787 | ;; |
| 1548 | 1788 | tic55x-*) |
| 1549 | - os=coff | |
| 1789 | + os= | |
| 1790 | + obj=coff | |
| 1550 | 1791 | ;; |
| 1551 | 1792 | tic6x-*) |
| 1552 | - os=coff | |
| 1793 | + os= | |
| 1794 | + obj=coff | |
| 1553 | 1795 | ;; |
| 1554 | 1796 | # This must come before the *-dec entry. |
| 1555 | 1797 | pdp10-*) |
| 1556 | 1798 | os=tops20 |
| 1557 | 1799 | ;; |
| @@ -1569,32 +1811,47 @@ | ||
| 1569 | 1811 | ;; |
| 1570 | 1812 | m68000-sun) |
| 1571 | 1813 | os=sunos3 |
| 1572 | 1814 | ;; |
| 1573 | 1815 | m68*-cisco) |
| 1574 | - os=aout | |
| 1816 | + os= | |
| 1817 | + obj=aout | |
| 1575 | 1818 | ;; |
| 1576 | 1819 | mep-*) |
| 1577 | - os=elf | |
| 1820 | + os= | |
| 1821 | + obj=elf | |
| 1822 | + ;; | |
| 1823 | + # The -sgi and -siemens entries must be before the mips- entry | |
| 1824 | + # or we get the wrong os. | |
| 1825 | + *-sgi) | |
| 1826 | + os=irix | |
| 1827 | + ;; | |
| 1828 | + *-siemens) | |
| 1829 | + os=sysv4 | |
| 1578 | 1830 | ;; |
| 1579 | 1831 | mips*-cisco) |
| 1580 | - os=elf | |
| 1832 | + os= | |
| 1833 | + obj=elf | |
| 1581 | 1834 | ;; |
| 1582 | - mips*-*) | |
| 1583 | - os=elf | |
| 1835 | + mips*-*|nanomips*-*) | |
| 1836 | + os= | |
| 1837 | + obj=elf | |
| 1584 | 1838 | ;; |
| 1585 | 1839 | or32-*) |
| 1586 | - os=coff | |
| 1840 | + os= | |
| 1841 | + obj=coff | |
| 1587 | 1842 | ;; |
| 1588 | - *-tti) # must be before sparc entry or we get the wrong os. | |
| 1843 | + # This must be before the sparc-* entry or we get the wrong os. | |
| 1844 | + *-tti) | |
| 1589 | 1845 | os=sysv3 |
| 1590 | 1846 | ;; |
| 1591 | 1847 | sparc-* | *-sun) |
| 1592 | 1848 | os=sunos4.1.1 |
| 1593 | 1849 | ;; |
| 1594 | 1850 | pru-*) |
| 1595 | - os=elf | |
| 1851 | + os= | |
| 1852 | + obj=elf | |
| 1596 | 1853 | ;; |
| 1597 | 1854 | *-be) |
| 1598 | 1855 | os=beos |
| 1599 | 1856 | ;; |
| 1600 | 1857 | *-ibm) |
| @@ -1614,11 +1871,11 @@ | ||
| 1614 | 1871 | ;; |
| 1615 | 1872 | *-hp) |
| 1616 | 1873 | os=hpux |
| 1617 | 1874 | ;; |
| 1618 | 1875 | *-hitachi) |
| 1619 | - os=hiux | |
| 1876 | + os=hiuxwe2 | |
| 1620 | 1877 | ;; |
| 1621 | 1878 | i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) |
| 1622 | 1879 | os=sysv |
| 1623 | 1880 | ;; |
| 1624 | 1881 | *-cbm) |
| @@ -1658,27 +1915,23 @@ | ||
| 1658 | 1915 | os=bsd |
| 1659 | 1916 | ;; |
| 1660 | 1917 | *-encore) |
| 1661 | 1918 | os=bsd |
| 1662 | 1919 | ;; |
| 1663 | - *-sgi) | |
| 1664 | - os=irix | |
| 1665 | - ;; | |
| 1666 | - *-siemens) | |
| 1667 | - os=sysv4 | |
| 1668 | - ;; | |
| 1669 | 1920 | *-masscomp) |
| 1670 | 1921 | os=rtu |
| 1671 | 1922 | ;; |
| 1672 | 1923 | f30[01]-fujitsu | f700-fujitsu) |
| 1673 | 1924 | os=uxpv |
| 1674 | 1925 | ;; |
| 1675 | 1926 | *-rom68k) |
| 1676 | - os=coff | |
| 1927 | + os= | |
| 1928 | + obj=coff | |
| 1677 | 1929 | ;; |
| 1678 | 1930 | *-*bug) |
| 1679 | - os=coff | |
| 1931 | + os= | |
| 1932 | + obj=coff | |
| 1680 | 1933 | ;; |
| 1681 | 1934 | *-apple) |
| 1682 | 1935 | os=macos |
| 1683 | 1936 | ;; |
| 1684 | 1937 | *-atari*) |
| @@ -1692,96 +1945,324 @@ | ||
| 1692 | 1945 | ;; |
| 1693 | 1946 | esac |
| 1694 | 1947 | |
| 1695 | 1948 | fi |
| 1696 | 1949 | |
| 1697 | -# Now, validate our (potentially fixed-up) OS. | |
| 1950 | +# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). | |
| 1951 | + | |
| 1698 | 1952 | case $os in |
| 1699 | 1953 | # Sometimes we do "kernel-libc", so those need to count as OSes. |
| 1700 | - musl* | newlib* | uclibc*) | |
| 1954 | + llvm* | musl* | newlib* | relibc* | uclibc*) | |
| 1701 | 1955 | ;; |
| 1702 | 1956 | # Likewise for "kernel-abi" |
| 1703 | 1957 | eabi* | gnueabi*) |
| 1704 | 1958 | ;; |
| 1705 | 1959 | # VxWorks passes extra cpu info in the 4th filed. |
| 1706 | 1960 | simlinux | simwindows | spe) |
| 1961 | + ;; | |
| 1962 | + # See `case $cpu-$os` validation below | |
| 1963 | + ghcjs) | |
| 1707 | 1964 | ;; |
| 1708 | 1965 | # Now accept the basic system types. |
| 1709 | - # The portable systems comes first. | |
| 1710 | 1966 | # Each alternative MUST end in a * to match a version number. |
| 1711 | - gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ | |
| 1712 | - | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ | |
| 1713 | - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | |
| 1714 | - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | |
| 1715 | - | hiux* | abug | nacl* | netware* | windows* \ | |
| 1716 | - | os9* | macos* | osx* | ios* \ | |
| 1717 | - | mpw* | magic* | mmixware* | mon960* | lnews* \ | |
| 1718 | - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | |
| 1719 | - | aos* | aros* | cloudabi* | sortix* | twizzler* \ | |
| 1720 | - | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ | |
| 1721 | - | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ | |
| 1722 | - | mirbsd* | netbsd* | dicos* | openedition* | ose* \ | |
| 1723 | - | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ | |
| 1724 | - | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ | |
| 1725 | - | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ | |
| 1726 | - | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ | |
| 1727 | - | udi* | lites* | ieee* | go32* | aux* | hcos* \ | |
| 1728 | - | chorusrdb* | cegcc* | glidix* | serenity* \ | |
| 1729 | - | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ | |
| 1730 | - | midipix* | mingw32* | mingw64* | mint* \ | |
| 1731 | - | uxpv* | beos* | mpeix* | udk* | moxiebox* \ | |
| 1732 | - | interix* | uwin* | mks* | rhapsody* | darwin* \ | |
| 1733 | - | openstep* | oskit* | conix* | pw32* | nonstopux* \ | |
| 1734 | - | storm-chaos* | tops10* | tenex* | tops20* | its* \ | |
| 1735 | - | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ | |
| 1736 | - | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ | |
| 1737 | - | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ | |
| 1738 | - | skyos* | haiku* | rdos* | toppers* | drops* | es* \ | |
| 1739 | - | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | |
| 1740 | - | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | |
| 1741 | - | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*) | |
| 1967 | + abug \ | |
| 1968 | + | aix* \ | |
| 1969 | + | amdhsa* \ | |
| 1970 | + | amigados* \ | |
| 1971 | + | amigaos* \ | |
| 1972 | + | android* \ | |
| 1973 | + | aof* \ | |
| 1974 | + | aos* \ | |
| 1975 | + | aros* \ | |
| 1976 | + | atheos* \ | |
| 1977 | + | auroraux* \ | |
| 1978 | + | aux* \ | |
| 1979 | + | beos* \ | |
| 1980 | + | bitrig* \ | |
| 1981 | + | bme* \ | |
| 1982 | + | bosx* \ | |
| 1983 | + | bsd* \ | |
| 1984 | + | cegcc* \ | |
| 1985 | + | chorusos* \ | |
| 1986 | + | chorusrdb* \ | |
| 1987 | + | clix* \ | |
| 1988 | + | cloudabi* \ | |
| 1989 | + | cnk* \ | |
| 1990 | + | conix* \ | |
| 1991 | + | cos* \ | |
| 1992 | + | cxux* \ | |
| 1993 | + | cygwin* \ | |
| 1994 | + | darwin* \ | |
| 1995 | + | dgux* \ | |
| 1996 | + | dicos* \ | |
| 1997 | + | dnix* \ | |
| 1998 | + | domain* \ | |
| 1999 | + | dragonfly* \ | |
| 2000 | + | drops* \ | |
| 2001 | + | ebmon* \ | |
| 2002 | + | ecoff* \ | |
| 2003 | + | ekkobsd* \ | |
| 2004 | + | emscripten* \ | |
| 2005 | + | emx* \ | |
| 2006 | + | es* \ | |
| 2007 | + | fiwix* \ | |
| 2008 | + | freebsd* \ | |
| 2009 | + | fuchsia* \ | |
| 2010 | + | genix* \ | |
| 2011 | + | genode* \ | |
| 2012 | + | glidix* \ | |
| 2013 | + | gnu* \ | |
| 2014 | + | go32* \ | |
| 2015 | + | haiku* \ | |
| 2016 | + | hcos* \ | |
| 2017 | + | hiux* \ | |
| 2018 | + | hms* \ | |
| 2019 | + | hpux* \ | |
| 2020 | + | ieee* \ | |
| 2021 | + | interix* \ | |
| 2022 | + | ios* \ | |
| 2023 | + | iris* \ | |
| 2024 | + | irix* \ | |
| 2025 | + | ironclad* \ | |
| 2026 | + | isc* \ | |
| 2027 | + | its* \ | |
| 2028 | + | l4re* \ | |
| 2029 | + | libertybsd* \ | |
| 2030 | + | lites* \ | |
| 2031 | + | lnews* \ | |
| 2032 | + | luna* \ | |
| 2033 | + | lynxos* \ | |
| 2034 | + | mach* \ | |
| 2035 | + | macos* \ | |
| 2036 | + | magic* \ | |
| 2037 | + | mbr* \ | |
| 2038 | + | midipix* \ | |
| 2039 | + | midnightbsd* \ | |
| 2040 | + | mingw32* \ | |
| 2041 | + | mingw64* \ | |
| 2042 | + | minix* \ | |
| 2043 | + | mint* \ | |
| 2044 | + | mirbsd* \ | |
| 2045 | + | mks* \ | |
| 2046 | + | mlibc* \ | |
| 2047 | + | mmixware* \ | |
| 2048 | + | mon960* \ | |
| 2049 | + | morphos* \ | |
| 2050 | + | moss* \ | |
| 2051 | + | moxiebox* \ | |
| 2052 | + | mpeix* \ | |
| 2053 | + | mpw* \ | |
| 2054 | + | msdos* \ | |
| 2055 | + | msys* \ | |
| 2056 | + | mvs* \ | |
| 2057 | + | nacl* \ | |
| 2058 | + | netbsd* \ | |
| 2059 | + | netware* \ | |
| 2060 | + | newsos* \ | |
| 2061 | + | nextstep* \ | |
| 2062 | + | nindy* \ | |
| 2063 | + | nonstopux* \ | |
| 2064 | + | nova* \ | |
| 2065 | + | nsk* \ | |
| 2066 | + | nucleus* \ | |
| 2067 | + | nx6 \ | |
| 2068 | + | nx7 \ | |
| 2069 | + | oabi* \ | |
| 2070 | + | ohos* \ | |
| 2071 | + | onefs* \ | |
| 2072 | + | openbsd* \ | |
| 2073 | + | openedition* \ | |
| 2074 | + | openstep* \ | |
| 2075 | + | os108* \ | |
| 2076 | + | os2* \ | |
| 2077 | + | os400* \ | |
| 2078 | + | os68k* \ | |
| 2079 | + | os9* \ | |
| 2080 | + | ose* \ | |
| 2081 | + | osf* \ | |
| 2082 | + | oskit* \ | |
| 2083 | + | osx* \ | |
| 2084 | + | palmos* \ | |
| 2085 | + | phoenix* \ | |
| 2086 | + | plan9* \ | |
| 2087 | + | powermax* \ | |
| 2088 | + | powerunix* \ | |
| 2089 | + | proelf* \ | |
| 2090 | + | psos* \ | |
| 2091 | + | psp* \ | |
| 2092 | + | ptx* \ | |
| 2093 | + | pw32* \ | |
| 2094 | + | qnx* \ | |
| 2095 | + | rdos* \ | |
| 2096 | + | redox* \ | |
| 2097 | + | rhapsody* \ | |
| 2098 | + | riscix* \ | |
| 2099 | + | riscos* \ | |
| 2100 | + | rtems* \ | |
| 2101 | + | rtmk* \ | |
| 2102 | + | rtu* \ | |
| 2103 | + | scout* \ | |
| 2104 | + | secbsd* \ | |
| 2105 | + | sei* \ | |
| 2106 | + | serenity* \ | |
| 2107 | + | sim* \ | |
| 2108 | + | skyos* \ | |
| 2109 | + | solaris* \ | |
| 2110 | + | solidbsd* \ | |
| 2111 | + | sortix* \ | |
| 2112 | + | storm-chaos* \ | |
| 2113 | + | sunos \ | |
| 2114 | + | sunos[34]* \ | |
| 2115 | + | superux* \ | |
| 2116 | + | syllable* \ | |
| 2117 | + | sym* \ | |
| 2118 | + | sysv* \ | |
| 2119 | + | tenex* \ | |
| 2120 | + | tirtos* \ | |
| 2121 | + | toppers* \ | |
| 2122 | + | tops10* \ | |
| 2123 | + | tops20* \ | |
| 2124 | + | tpf* \ | |
| 2125 | + | tvos* \ | |
| 2126 | + | twizzler* \ | |
| 2127 | + | uclinux* \ | |
| 2128 | + | udi* \ | |
| 2129 | + | udk* \ | |
| 2130 | + | ultrix* \ | |
| 2131 | + | unicos* \ | |
| 2132 | + | uniplus* \ | |
| 2133 | + | unleashed* \ | |
| 2134 | + | unos* \ | |
| 2135 | + | uwin* \ | |
| 2136 | + | uxpv* \ | |
| 2137 | + | v88r* \ | |
| 2138 | + |*vms* \ | |
| 2139 | + | vos* \ | |
| 2140 | + | vsta* \ | |
| 2141 | + | vxsim* \ | |
| 2142 | + | vxworks* \ | |
| 2143 | + | wasi* \ | |
| 2144 | + | watchos* \ | |
| 2145 | + | wince* \ | |
| 2146 | + | windiss* \ | |
| 2147 | + | windows* \ | |
| 2148 | + | winnt* \ | |
| 2149 | + | xenix* \ | |
| 2150 | + | xray* \ | |
| 2151 | + | zephyr* \ | |
| 2152 | + | zvmoe* ) | |
| 1742 | 2153 | ;; |
| 1743 | 2154 | # This one is extra strict with allowed versions |
| 1744 | 2155 | sco3.2v2 | sco3.2v[4-9]* | sco5v6*) |
| 1745 | 2156 | # Don't forget version if it is 3.2v4 or newer. |
| 1746 | 2157 | ;; |
| 2158 | + # This refers to builds using the UEFI calling convention | |
| 2159 | + # (which depends on the architecture) and PE file format. | |
| 2160 | + # Note that this is both a different calling convention and | |
| 2161 | + # different file format than that of GNU-EFI | |
| 2162 | + # (x86_64-w64-mingw32). | |
| 2163 | + uefi) | |
| 2164 | + ;; | |
| 1747 | 2165 | none) |
| 1748 | 2166 | ;; |
| 2167 | + kernel* | msvc* ) | |
| 2168 | + # Restricted further below | |
| 2169 | + ;; | |
| 2170 | + '') | |
| 2171 | + if test x"$obj" = x | |
| 2172 | + then | |
| 2173 | + echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 | |
| 2174 | + fi | |
| 2175 | + ;; | |
| 2176 | + *) | |
| 2177 | + echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 | |
| 2178 | + exit 1 | |
| 2179 | + ;; | |
| 2180 | +esac | |
| 2181 | + | |
| 2182 | +case $obj in | |
| 2183 | + aout* | coff* | elf* | pe*) | |
| 2184 | + ;; | |
| 2185 | + '') | |
| 2186 | + # empty is fine | |
| 2187 | + ;; | |
| 1749 | 2188 | *) |
| 1750 | - echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 | |
| 2189 | + echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 | |
| 2190 | + exit 1 | |
| 2191 | + ;; | |
| 2192 | +esac | |
| 2193 | + | |
| 2194 | +# Here we handle the constraint that a (synthetic) cpu and os are | |
| 2195 | +# valid only in combination with each other and nowhere else. | |
| 2196 | +case $cpu-$os in | |
| 2197 | + # The "javascript-unknown-ghcjs" triple is used by GHC; we | |
| 2198 | + # accept it here in order to tolerate that, but reject any | |
| 2199 | + # variations. | |
| 2200 | + javascript-ghcjs) | |
| 2201 | + ;; | |
| 2202 | + javascript-* | *-ghcjs) | |
| 2203 | + echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 | |
| 1751 | 2204 | exit 1 |
| 1752 | 2205 | ;; |
| 1753 | 2206 | esac |
| 1754 | 2207 | |
| 1755 | 2208 | # As a final step for OS-related things, validate the OS-kernel combination |
| 1756 | 2209 | # (given a valid OS), if there is a kernel. |
| 1757 | -case $kernel-$os in | |
| 1758 | - linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) | |
| 2210 | +case $kernel-$os-$obj in | |
| 2211 | + linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ | |
| 2212 | + | linux-mlibc*- | linux-musl*- | linux-newlib*- \ | |
| 2213 | + | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) | |
| 2214 | + ;; | |
| 2215 | + uclinux-uclibc*- | uclinux-gnu*- ) | |
| 2216 | + ;; | |
| 2217 | + managarm-mlibc*- | managarm-kernel*- ) | |
| 1759 | 2218 | ;; |
| 1760 | - uclinux-uclibc* ) | |
| 2219 | + windows*-msvc*-) | |
| 1761 | 2220 | ;; |
| 1762 | - -dietlibc* | -newlib* | -musl* | -uclibc* ) | |
| 2221 | + -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ | |
| 2222 | + | -uclibc*- ) | |
| 1763 | 2223 | # These are just libc implementations, not actual OSes, and thus |
| 1764 | 2224 | # require a kernel. |
| 1765 | - echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 | |
| 2225 | + echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 | |
| 2226 | + exit 1 | |
| 2227 | + ;; | |
| 2228 | + -kernel*- ) | |
| 2229 | + echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 | |
| 2230 | + exit 1 | |
| 2231 | + ;; | |
| 2232 | + *-kernel*- ) | |
| 2233 | + echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 | |
| 2234 | + exit 1 | |
| 2235 | + ;; | |
| 2236 | + *-msvc*- ) | |
| 2237 | + echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 | |
| 1766 | 2238 | exit 1 |
| 1767 | 2239 | ;; |
| 1768 | - kfreebsd*-gnu* | kopensolaris*-gnu*) | |
| 1769 | - ;; | |
| 1770 | - vxworks-simlinux | vxworks-simwindows | vxworks-spe) | |
| 1771 | - ;; | |
| 1772 | - nto-qnx*) | |
| 1773 | - ;; | |
| 1774 | - os2-emx) | |
| 1775 | - ;; | |
| 1776 | - *-eabi* | *-gnueabi*) | |
| 1777 | - ;; | |
| 1778 | - -*) | |
| 2240 | + kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) | |
| 2241 | + ;; | |
| 2242 | + vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) | |
| 2243 | + ;; | |
| 2244 | + nto-qnx*-) | |
| 2245 | + ;; | |
| 2246 | + os2-emx-) | |
| 2247 | + ;; | |
| 2248 | + rtmk-nova-) | |
| 2249 | + ;; | |
| 2250 | + *-eabi*- | *-gnueabi*-) | |
| 2251 | + ;; | |
| 2252 | + none--*) | |
| 2253 | + # None (no kernel, i.e. freestanding / bare metal), | |
| 2254 | + # can be paired with an machine code file format | |
| 2255 | + ;; | |
| 2256 | + -*-) | |
| 1779 | 2257 | # Blank kernel with real OS is always fine. |
| 1780 | 2258 | ;; |
| 1781 | - *-*) | |
| 1782 | - echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 | |
| 2259 | + --*) | |
| 2260 | + # Blank kernel and OS with real machine code file format is always fine. | |
| 2261 | + ;; | |
| 2262 | + *-*-*) | |
| 2263 | + echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 | |
| 1783 | 2264 | exit 1 |
| 1784 | 2265 | ;; |
| 1785 | 2266 | esac |
| 1786 | 2267 | |
| 1787 | 2268 | # Here we handle the case where we know the os, and the CPU type, but not the |
| @@ -1790,11 +2271,11 @@ | ||
| 1790 | 2271 | unknown) |
| 1791 | 2272 | case $cpu-$os in |
| 1792 | 2273 | *-riscix*) |
| 1793 | 2274 | vendor=acorn |
| 1794 | 2275 | ;; |
| 1795 | - *-sunos*) | |
| 2276 | + *-sunos* | *-solaris*) | |
| 1796 | 2277 | vendor=sun |
| 1797 | 2278 | ;; |
| 1798 | 2279 | *-cnk* | *-aix*) |
| 1799 | 2280 | vendor=ibm |
| 1800 | 2281 | ;; |
| @@ -1860,14 +2341,14 @@ | ||
| 1860 | 2341 | ;; |
| 1861 | 2342 | esac |
| 1862 | 2343 | ;; |
| 1863 | 2344 | esac |
| 1864 | 2345 | |
| 1865 | -echo "$cpu-$vendor-${kernel:+$kernel-}$os" | |
| 2346 | +echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" | |
| 1866 | 2347 | exit |
| 1867 | 2348 | |
| 1868 | 2349 | # Local variables: |
| 1869 | 2350 | # eval: (add-hook 'before-save-hook 'time-stamp) |
| 1870 | 2351 | # time-stamp-start: "timestamp='" |
| 1871 | 2352 | # time-stamp-format: "%:y-%02m-%02d" |
| 1872 | 2353 | # time-stamp-end: "'" |
| 1873 | 2354 | # End: |
| 1874 | 2355 |
| --- autosetup/autosetup-config.sub | |
| +++ autosetup/autosetup-config.sub | |
| @@ -1,16 +1,16 @@ | |
| 1 | #! /bin/sh |
| 2 | # Configuration validation subroutine script. |
| 3 | # Copyright 1992-2021 Free Software Foundation, Inc. |
| 4 | |
| 5 | # shellcheck disable=SC2006,SC2268 # see below for rationale |
| 6 | |
| 7 | timestamp='2021-07-03' |
| 8 | |
| 9 | # This file is free software; you can redistribute it and/or modify it |
| 10 | # under the terms of the GNU General Public License as published by |
| 11 | # the Free Software Foundation; either version 3 of the License, or |
| 12 | # (at your option) any later version. |
| 13 | # |
| 14 | # This program is distributed in the hope that it will be useful, but |
| 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| @@ -74,17 +74,17 @@ | |
| 74 | Report bugs and patches to <[email protected]>." |
| 75 | |
| 76 | version="\ |
| 77 | GNU config.sub ($timestamp) |
| 78 | |
| 79 | Copyright 1992-2021 Free Software Foundation, Inc. |
| 80 | |
| 81 | This is free software; see the source for copying conditions. There is NO |
| 82 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." |
| 83 | |
| 84 | help=" |
| 85 | Try \`$me --help' for more information." |
| 86 | |
| 87 | # Parse command line |
| 88 | while test $# -gt 0 ; do |
| 89 | case $1 in |
| 90 | --time-stamp | --time* | -t ) |
| @@ -118,19 +118,20 @@ | |
| 118 | *) echo "$me: too many arguments$help" >&2 |
| 119 | exit 1;; |
| 120 | esac |
| 121 | |
| 122 | # Split fields of configuration type |
| 123 | # shellcheck disable=SC2162 |
| 124 | IFS="-" read field1 field2 field3 field4 <<EOF |
| 125 | $1 |
| 126 | EOF |
| 127 | |
| 128 | # Separate into logical components for further validation |
| 129 | case $1 in |
| 130 | *-*-*-*-*) |
| 131 | echo Invalid configuration \`"$1"\': more than four components >&2 |
| 132 | exit 1 |
| 133 | ;; |
| 134 | *-*-*-*) |
| 135 | basic_machine=$field1-$field2 |
| 136 | basic_os=$field3-$field4 |
| @@ -138,14 +139,25 @@ | |
| 138 | *-*-*) |
| 139 | # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two |
| 140 | # parts |
| 141 | maybe_os=$field2-$field3 |
| 142 | case $maybe_os in |
| 143 | nto-qnx* | linux-* | uclinux-uclibc* \ |
| 144 | | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ |
| 145 | | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ |
| 146 | | storm-chaos* | os2-emx* | rtmk-nova*) |
| 147 | basic_machine=$field1 |
| 148 | basic_os=$maybe_os |
| 149 | ;; |
| 150 | android-linux) |
| 151 | basic_machine=$field1-unknown |
| @@ -156,37 +168,105 @@ | |
| 156 | basic_os=$field3 |
| 157 | ;; |
| 158 | esac |
| 159 | ;; |
| 160 | *-*) |
| 161 | # A lone config we happen to match not fitting any pattern |
| 162 | case $field1-$field2 in |
| 163 | decstation-3100) |
| 164 | basic_machine=mips-dec |
| 165 | basic_os= |
| 166 | ;; |
| 167 | *-*) |
| 168 | # Second component is usually, but not always the OS |
| 169 | case $field2 in |
| 170 | # Prevent following clause from handling this valid os |
| 171 | sun*os*) |
| 172 | basic_machine=$field1 |
| 173 | basic_os=$field2 |
| 174 | ;; |
| 175 | # Manufacturers |
| 176 | dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ |
| 177 | | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ |
| 178 | | unicom* | ibm* | next | hp | isi* | apollo | altos* \ |
| 179 | | convergent* | ncr* | news | 32* | 3600* | 3100* \ |
| 180 | | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ |
| 181 | | ultra | tti* | harris | dolphin | highlevel | gould \ |
| 182 | | cbm | ns | masscomp | apple | axis | knuth | cray \ |
| 183 | | microblaze* | sim | cisco \ |
| 184 | | oki | wec | wrs | winbond) |
| 185 | basic_machine=$field1-$field2 |
| 186 | basic_os= |
| 187 | ;; |
| 188 | *) |
| 189 | basic_machine=$field1 |
| 190 | basic_os=$field2 |
| 191 | ;; |
| 192 | esac |
| @@ -263,30 +343,10 @@ | |
| 263 | ;; |
| 264 | cegcc) |
| 265 | basic_machine=arm-unknown |
| 266 | basic_os=cegcc |
| 267 | ;; |
| 268 | convex-c1) |
| 269 | basic_machine=c1-convex |
| 270 | basic_os=bsd |
| 271 | ;; |
| 272 | convex-c2) |
| 273 | basic_machine=c2-convex |
| 274 | basic_os=bsd |
| 275 | ;; |
| 276 | convex-c32) |
| 277 | basic_machine=c32-convex |
| 278 | basic_os=bsd |
| 279 | ;; |
| 280 | convex-c34) |
| 281 | basic_machine=c34-convex |
| 282 | basic_os=bsd |
| 283 | ;; |
| 284 | convex-c38) |
| 285 | basic_machine=c38-convex |
| 286 | basic_os=bsd |
| 287 | ;; |
| 288 | cray) |
| 289 | basic_machine=j90-cray |
| 290 | basic_os=unicos |
| 291 | ;; |
| 292 | crds | unos) |
| @@ -705,13 +765,21 @@ | |
| 705 | decsystem20* | dec20*) |
| 706 | cpu=pdp10 |
| 707 | vendor=dec |
| 708 | basic_os=tops20 |
| 709 | ;; |
| 710 | delta | 3300 | motorola-3300 | motorola-delta \ |
| 711 | | 3300-motorola | delta-motorola) |
| 712 | cpu=m68k |
| 713 | vendor=motorola |
| 714 | ;; |
| 715 | dpx2*) |
| 716 | cpu=m68k |
| 717 | vendor=bull |
| @@ -718,6 +786,9 @@ | |
| 718 | basic_os=sysv3 |
| 719 | ;; |
| 720 | encore | umax | mmax) |
| 721 | cpu=ns32k |
| 722 | vendor=encore |
| 723 | ;; |
| @@ -828,22 +899,10 @@ | |
| 828 | basic_os=newsos |
| 829 | ;; |
| 830 | next | m*-next) |
| 831 | cpu=m68k |
| 832 | vendor=next |
| 833 | case $basic_os in |
| 834 | openstep*) |
| 835 | ;; |
| 836 | nextstep*) |
| 837 | ;; |
| 838 | ns2*) |
| 839 | basic_os=nextstep2 |
| 840 | ;; |
| 841 | *) |
| 842 | basic_os=nextstep3 |
| 843 | ;; |
| 844 | esac |
| 845 | ;; |
| 846 | np1) |
| 847 | cpu=np1 |
| 848 | vendor=gould |
| 849 | ;; |
| @@ -928,16 +987,17 @@ | |
| 928 | cpu=sparc |
| 929 | vendor=`echo "$basic_machine" | sed 's/-.*//'` |
| 930 | ;; |
| 931 | |
| 932 | *-*) |
| 933 | # shellcheck disable=SC2162 |
| 934 | IFS="-" read cpu vendor <<EOF |
| 935 | $basic_machine |
| 936 | EOF |
| 937 | ;; |
| 938 | # We use `pc' rather than `unknown' |
| 939 | # because (1) that's what they normally are, and |
| 940 | # (2) the word "unknown" tends to confuse beginning users. |
| 941 | i*86 | x86_64) |
| 942 | cpu=$basic_machine |
| 943 | vendor=pc |
| @@ -961,19 +1021,23 @@ | |
| 961 | |
| 962 | unset -v basic_machine |
| 963 | |
| 964 | # Decode basic machines in the full and proper CPU-Company form. |
| 965 | case $cpu-$vendor in |
| 966 | # Here we handle the default manufacturer of certain CPU types in canonical form. It is in |
| 967 | # some cases the only manufacturer, in others, it is the most popular. |
| 968 | craynv-unknown) |
| 969 | vendor=cray |
| 970 | basic_os=${basic_os:-unicosmp} |
| 971 | ;; |
| 972 | c90-unknown | c90-cray) |
| 973 | vendor=cray |
| 974 | basic_os=${Basic_os:-unicos} |
| 975 | ;; |
| 976 | fx80-unknown) |
| 977 | vendor=alliant |
| 978 | ;; |
| 979 | romp-unknown) |
| @@ -1010,23 +1074,46 @@ | |
| 1010 | cpu=xps100 |
| 1011 | vendor=honeywell |
| 1012 | ;; |
| 1013 | |
| 1014 | # Here we normalize CPU types with a missing or matching vendor |
| 1015 | dpx20-unknown | dpx20-bull) |
| 1016 | cpu=rs6000 |
| 1017 | vendor=bull |
| 1018 | basic_os=${basic_os:-bosx} |
| 1019 | ;; |
| 1020 | |
| 1021 | # Here we normalize CPU types irrespective of the vendor |
| 1022 | amd64-*) |
| 1023 | cpu=x86_64 |
| 1024 | ;; |
| 1025 | blackfin-*) |
| 1026 | cpu=bfin |
| 1027 | basic_os=linux |
| 1028 | ;; |
| 1029 | c54x-*) |
| 1030 | cpu=tic54x |
| 1031 | ;; |
| 1032 | c55x-*) |
| @@ -1045,37 +1132,34 @@ | |
| 1045 | ms1-*) |
| 1046 | cpu=mt |
| 1047 | ;; |
| 1048 | m68knommu-*) |
| 1049 | cpu=m68k |
| 1050 | basic_os=linux |
| 1051 | ;; |
| 1052 | m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) |
| 1053 | cpu=s12z |
| 1054 | ;; |
| 1055 | openrisc-*) |
| 1056 | cpu=or32 |
| 1057 | ;; |
| 1058 | parisc-*) |
| 1059 | cpu=hppa |
| 1060 | basic_os=linux |
| 1061 | ;; |
| 1062 | pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) |
| 1063 | cpu=i586 |
| 1064 | ;; |
| 1065 | pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*) |
| 1066 | cpu=i686 |
| 1067 | ;; |
| 1068 | pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) |
| 1069 | cpu=i686 |
| 1070 | ;; |
| 1071 | pentium4-*) |
| 1072 | cpu=i786 |
| 1073 | ;; |
| 1074 | pc98-*) |
| 1075 | cpu=i386 |
| 1076 | ;; |
| 1077 | ppc-* | ppcbe-*) |
| 1078 | cpu=powerpc |
| 1079 | ;; |
| 1080 | ppcle-* | powerpclittle-*) |
| 1081 | cpu=powerpcle |
| @@ -1105,17 +1189,14 @@ | |
| 1105 | cpu=mipstx39 |
| 1106 | ;; |
| 1107 | tx39el-*) |
| 1108 | cpu=mipstx39el |
| 1109 | ;; |
| 1110 | x64-*) |
| 1111 | cpu=x86_64 |
| 1112 | ;; |
| 1113 | xscale-* | xscalee[bl]-*) |
| 1114 | cpu=`echo "$cpu" | sed 's/^xscale/arm/'` |
| 1115 | ;; |
| 1116 | arm64-*) |
| 1117 | cpu=aarch64 |
| 1118 | ;; |
| 1119 | |
| 1120 | # Recognize the canonical CPU Types that limit and/or modify the |
| 1121 | # company names they are paired with. |
| @@ -1163,118 +1244,235 @@ | |
| 1163 | |
| 1164 | *) |
| 1165 | # Recognize the canonical CPU types that are allowed with any |
| 1166 | # company name. |
| 1167 | case $cpu in |
| 1168 | 1750a | 580 \ |
| 1169 | | a29k \ |
| 1170 | | aarch64 | aarch64_be \ |
| 1171 | | abacus \ |
| 1172 | | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ |
| 1173 | | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ |
| 1174 | | alphapca5[67] | alpha64pca5[67] \ |
| 1175 | | am33_2.0 \ |
| 1176 | | amdgcn \ |
| 1177 | | arc | arceb | arc32 | arc64 \ |
| 1178 | | arm | arm[lb]e | arme[lb] | armv* \ |
| 1179 | | avr | avr32 \ |
| 1180 | | asmjs \ |
| 1181 | | ba \ |
| 1182 | | be32 | be64 \ |
| 1183 | | bfin | bpf | bs2000 \ |
| 1184 | | c[123]* | c30 | [cjt]90 | c4x \ |
| 1185 | | c8051 | clipper | craynv | csky | cydra \ |
| 1186 | | d10v | d30v | dlx | dsp16xx \ |
| 1187 | | e2k | elxsi | epiphany \ |
| 1188 | | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ |
| 1189 | | h8300 | h8500 \ |
| 1190 | | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ |
| 1191 | | hexagon \ |
| 1192 | | i370 | i*86 | i860 | i960 | ia16 | ia64 \ |
| 1193 | | ip2k | iq2000 \ |
| 1194 | | k1om \ |
| 1195 | | le32 | le64 \ |
| 1196 | | lm32 \ |
| 1197 | | loongarch32 | loongarch64 | loongarchx32 \ |
| 1198 | | m32c | m32r | m32rle \ |
| 1199 | | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ |
| 1200 | | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ |
| 1201 | | m88110 | m88k | maxq | mb | mcore | mep | metag \ |
| 1202 | | microblaze | microblazeel \ |
| 1203 | | mips | mipsbe | mipseb | mipsel | mipsle \ |
| 1204 | | mips16 \ |
| 1205 | | mips64 | mips64eb | mips64el \ |
| 1206 | | mips64octeon | mips64octeonel \ |
| 1207 | | mips64orion | mips64orionel \ |
| 1208 | | mips64r5900 | mips64r5900el \ |
| 1209 | | mips64vr | mips64vrel \ |
| 1210 | | mips64vr4100 | mips64vr4100el \ |
| 1211 | | mips64vr4300 | mips64vr4300el \ |
| 1212 | | mips64vr5000 | mips64vr5000el \ |
| 1213 | | mips64vr5900 | mips64vr5900el \ |
| 1214 | | mipsisa32 | mipsisa32el \ |
| 1215 | | mipsisa32r2 | mipsisa32r2el \ |
| 1216 | | mipsisa32r3 | mipsisa32r3el \ |
| 1217 | | mipsisa32r5 | mipsisa32r5el \ |
| 1218 | | mipsisa32r6 | mipsisa32r6el \ |
| 1219 | | mipsisa64 | mipsisa64el \ |
| 1220 | | mipsisa64r2 | mipsisa64r2el \ |
| 1221 | | mipsisa64r3 | mipsisa64r3el \ |
| 1222 | | mipsisa64r5 | mipsisa64r5el \ |
| 1223 | | mipsisa64r6 | mipsisa64r6el \ |
| 1224 | | mipsisa64sb1 | mipsisa64sb1el \ |
| 1225 | | mipsisa64sr71k | mipsisa64sr71kel \ |
| 1226 | | mipsr5900 | mipsr5900el \ |
| 1227 | | mipstx39 | mipstx39el \ |
| 1228 | | mmix \ |
| 1229 | | mn10200 | mn10300 \ |
| 1230 | | moxie \ |
| 1231 | | mt \ |
| 1232 | | msp430 \ |
| 1233 | | nds32 | nds32le | nds32be \ |
| 1234 | | nfp \ |
| 1235 | | nios | nios2 | nios2eb | nios2el \ |
| 1236 | | none | np1 | ns16k | ns32k | nvptx \ |
| 1237 | | open8 \ |
| 1238 | | or1k* \ |
| 1239 | | or32 \ |
| 1240 | | orion \ |
| 1241 | | picochip \ |
| 1242 | | pdp10 | pdp11 | pj | pjl | pn | power \ |
| 1243 | | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ |
| 1244 | | pru \ |
| 1245 | | pyramid \ |
| 1246 | | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ |
| 1247 | | rl78 | romp | rs6000 | rx \ |
| 1248 | | s390 | s390x \ |
| 1249 | | score \ |
| 1250 | | sh | shl \ |
| 1251 | | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ |
| 1252 | | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ |
| 1253 | | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ |
| 1254 | | sparclite \ |
| 1255 | | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ |
| 1256 | | spu \ |
| 1257 | | tahoe \ |
| 1258 | | thumbv7* \ |
| 1259 | | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ |
| 1260 | | tron \ |
| 1261 | | ubicom32 \ |
| 1262 | | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ |
| 1263 | | vax \ |
| 1264 | | visium \ |
| 1265 | | w65 \ |
| 1266 | | wasm32 | wasm64 \ |
| 1267 | | we32k \ |
| 1268 | | x86 | x86_64 | xc16x | xgate | xps100 \ |
| 1269 | | xstormy16 | xtensa* \ |
| 1270 | | ymp \ |
| 1271 | | z8k | z80) |
| 1272 | ;; |
| 1273 | |
| 1274 | *) |
| 1275 | echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 |
| 1276 | exit 1 |
| 1277 | ;; |
| 1278 | esac |
| 1279 | ;; |
| 1280 | esac |
| @@ -1291,15 +1489,16 @@ | |
| 1291 | ;; |
| 1292 | esac |
| 1293 | |
| 1294 | # Decode manufacturer-specific aliases for certain operating systems. |
| 1295 | |
| 1296 | if test x$basic_os != x |
| 1297 | then |
| 1298 | |
| 1299 | # First recognize some ad-hoc caes, or perhaps split kernel-os, or else just |
| 1300 | # set os. |
| 1301 | case $basic_os in |
| 1302 | gnu/linux*) |
| 1303 | kernel=linux |
| 1304 | os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` |
| 1305 | ;; |
| @@ -1310,14 +1509,15 @@ | |
| 1310 | nto-qnx*) |
| 1311 | kernel=nto |
| 1312 | os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` |
| 1313 | ;; |
| 1314 | *-*) |
| 1315 | # shellcheck disable=SC2162 |
| 1316 | IFS="-" read kernel os <<EOF |
| 1317 | $basic_os |
| 1318 | EOF |
| 1319 | ;; |
| 1320 | # Default OS when just kernel was specified |
| 1321 | nto*) |
| 1322 | kernel=nto |
| 1323 | os=`echo "$basic_os" | sed -e 's|nto|qnx|'` |
| @@ -1324,10 +1524,14 @@ | |
| 1324 | ;; |
| 1325 | linux*) |
| 1326 | kernel=linux |
| 1327 | os=`echo "$basic_os" | sed -e 's|linux|gnu|'` |
| 1328 | ;; |
| 1329 | *) |
| 1330 | kernel= |
| 1331 | os=$basic_os |
| 1332 | ;; |
| 1333 | esac |
| @@ -1350,10 +1554,27 @@ | |
| 1350 | solaris) |
| 1351 | os=solaris2 |
| 1352 | ;; |
| 1353 | unixware*) |
| 1354 | os=sysv4.2uw |
| 1355 | ;; |
| 1356 | # es1800 is here to avoid being matched by es* (a different OS) |
| 1357 | es1800*) |
| 1358 | os=ose |
| 1359 | ;; |
| @@ -1421,10 +1642,11 @@ | |
| 1421 | wince*) |
| 1422 | os=wince |
| 1423 | ;; |
| 1424 | utek*) |
| 1425 | os=bsd |
| 1426 | ;; |
| 1427 | dynix*) |
| 1428 | os=bsd |
| 1429 | ;; |
| 1430 | acis*) |
| @@ -1437,25 +1659,29 @@ | |
| 1437 | os=syllable |
| 1438 | ;; |
| 1439 | 386bsd) |
| 1440 | os=bsd |
| 1441 | ;; |
| 1442 | ctix* | uts*) |
| 1443 | os=sysv |
| 1444 | ;; |
| 1445 | nova*) |
| 1446 | os=rtmk-nova |
| 1447 | ;; |
| 1448 | ns2) |
| 1449 | os=nextstep2 |
| 1450 | ;; |
| 1451 | # Preserve the version number of sinix5. |
| 1452 | sinix5.*) |
| 1453 | os=`echo "$os" | sed -e 's|sinix|sysv|'` |
| 1454 | ;; |
| 1455 | sinix*) |
| 1456 | os=sysv4 |
| 1457 | ;; |
| 1458 | tpf*) |
| 1459 | os=tpf |
| 1460 | ;; |
| 1461 | triton*) |
| @@ -1489,13 +1715,19 @@ | |
| 1489 | case $cpu in |
| 1490 | arm*) |
| 1491 | os=eabi |
| 1492 | ;; |
| 1493 | *) |
| 1494 | os=elf |
| 1495 | ;; |
| 1496 | esac |
| 1497 | ;; |
| 1498 | *) |
| 1499 | # No normalization, but not necessarily accepted, that comes below. |
| 1500 | ;; |
| 1501 | esac |
| @@ -1511,47 +1743,57 @@ | |
| 1511 | # that MANUFACTURER isn't an operating system. Otherwise, code above |
| 1512 | # will signal an error saying that MANUFACTURER isn't an operating |
| 1513 | # system, and we'll never get to this point. |
| 1514 | |
| 1515 | kernel= |
| 1516 | case $cpu-$vendor in |
| 1517 | score-*) |
| 1518 | os=elf |
| 1519 | ;; |
| 1520 | spu-*) |
| 1521 | os=elf |
| 1522 | ;; |
| 1523 | *-acorn) |
| 1524 | os=riscix1.2 |
| 1525 | ;; |
| 1526 | arm*-rebel) |
| 1527 | kernel=linux |
| 1528 | os=gnu |
| 1529 | ;; |
| 1530 | arm*-semi) |
| 1531 | os=aout |
| 1532 | ;; |
| 1533 | c4x-* | tic4x-*) |
| 1534 | os=coff |
| 1535 | ;; |
| 1536 | c8051-*) |
| 1537 | os=elf |
| 1538 | ;; |
| 1539 | clipper-intergraph) |
| 1540 | os=clix |
| 1541 | ;; |
| 1542 | hexagon-*) |
| 1543 | os=elf |
| 1544 | ;; |
| 1545 | tic54x-*) |
| 1546 | os=coff |
| 1547 | ;; |
| 1548 | tic55x-*) |
| 1549 | os=coff |
| 1550 | ;; |
| 1551 | tic6x-*) |
| 1552 | os=coff |
| 1553 | ;; |
| 1554 | # This must come before the *-dec entry. |
| 1555 | pdp10-*) |
| 1556 | os=tops20 |
| 1557 | ;; |
| @@ -1569,32 +1811,47 @@ | |
| 1569 | ;; |
| 1570 | m68000-sun) |
| 1571 | os=sunos3 |
| 1572 | ;; |
| 1573 | m68*-cisco) |
| 1574 | os=aout |
| 1575 | ;; |
| 1576 | mep-*) |
| 1577 | os=elf |
| 1578 | ;; |
| 1579 | mips*-cisco) |
| 1580 | os=elf |
| 1581 | ;; |
| 1582 | mips*-*) |
| 1583 | os=elf |
| 1584 | ;; |
| 1585 | or32-*) |
| 1586 | os=coff |
| 1587 | ;; |
| 1588 | *-tti) # must be before sparc entry or we get the wrong os. |
| 1589 | os=sysv3 |
| 1590 | ;; |
| 1591 | sparc-* | *-sun) |
| 1592 | os=sunos4.1.1 |
| 1593 | ;; |
| 1594 | pru-*) |
| 1595 | os=elf |
| 1596 | ;; |
| 1597 | *-be) |
| 1598 | os=beos |
| 1599 | ;; |
| 1600 | *-ibm) |
| @@ -1614,11 +1871,11 @@ | |
| 1614 | ;; |
| 1615 | *-hp) |
| 1616 | os=hpux |
| 1617 | ;; |
| 1618 | *-hitachi) |
| 1619 | os=hiux |
| 1620 | ;; |
| 1621 | i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) |
| 1622 | os=sysv |
| 1623 | ;; |
| 1624 | *-cbm) |
| @@ -1658,27 +1915,23 @@ | |
| 1658 | os=bsd |
| 1659 | ;; |
| 1660 | *-encore) |
| 1661 | os=bsd |
| 1662 | ;; |
| 1663 | *-sgi) |
| 1664 | os=irix |
| 1665 | ;; |
| 1666 | *-siemens) |
| 1667 | os=sysv4 |
| 1668 | ;; |
| 1669 | *-masscomp) |
| 1670 | os=rtu |
| 1671 | ;; |
| 1672 | f30[01]-fujitsu | f700-fujitsu) |
| 1673 | os=uxpv |
| 1674 | ;; |
| 1675 | *-rom68k) |
| 1676 | os=coff |
| 1677 | ;; |
| 1678 | *-*bug) |
| 1679 | os=coff |
| 1680 | ;; |
| 1681 | *-apple) |
| 1682 | os=macos |
| 1683 | ;; |
| 1684 | *-atari*) |
| @@ -1692,96 +1945,324 @@ | |
| 1692 | ;; |
| 1693 | esac |
| 1694 | |
| 1695 | fi |
| 1696 | |
| 1697 | # Now, validate our (potentially fixed-up) OS. |
| 1698 | case $os in |
| 1699 | # Sometimes we do "kernel-libc", so those need to count as OSes. |
| 1700 | musl* | newlib* | uclibc*) |
| 1701 | ;; |
| 1702 | # Likewise for "kernel-abi" |
| 1703 | eabi* | gnueabi*) |
| 1704 | ;; |
| 1705 | # VxWorks passes extra cpu info in the 4th filed. |
| 1706 | simlinux | simwindows | spe) |
| 1707 | ;; |
| 1708 | # Now accept the basic system types. |
| 1709 | # The portable systems comes first. |
| 1710 | # Each alternative MUST end in a * to match a version number. |
| 1711 | gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ |
| 1712 | | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ |
| 1713 | | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ |
| 1714 | | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ |
| 1715 | | hiux* | abug | nacl* | netware* | windows* \ |
| 1716 | | os9* | macos* | osx* | ios* \ |
| 1717 | | mpw* | magic* | mmixware* | mon960* | lnews* \ |
| 1718 | | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ |
| 1719 | | aos* | aros* | cloudabi* | sortix* | twizzler* \ |
| 1720 | | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ |
| 1721 | | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ |
| 1722 | | mirbsd* | netbsd* | dicos* | openedition* | ose* \ |
| 1723 | | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ |
| 1724 | | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ |
| 1725 | | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ |
| 1726 | | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ |
| 1727 | | udi* | lites* | ieee* | go32* | aux* | hcos* \ |
| 1728 | | chorusrdb* | cegcc* | glidix* | serenity* \ |
| 1729 | | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ |
| 1730 | | midipix* | mingw32* | mingw64* | mint* \ |
| 1731 | | uxpv* | beos* | mpeix* | udk* | moxiebox* \ |
| 1732 | | interix* | uwin* | mks* | rhapsody* | darwin* \ |
| 1733 | | openstep* | oskit* | conix* | pw32* | nonstopux* \ |
| 1734 | | storm-chaos* | tops10* | tenex* | tops20* | its* \ |
| 1735 | | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ |
| 1736 | | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ |
| 1737 | | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ |
| 1738 | | skyos* | haiku* | rdos* | toppers* | drops* | es* \ |
| 1739 | | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ |
| 1740 | | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ |
| 1741 | | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*) |
| 1742 | ;; |
| 1743 | # This one is extra strict with allowed versions |
| 1744 | sco3.2v2 | sco3.2v[4-9]* | sco5v6*) |
| 1745 | # Don't forget version if it is 3.2v4 or newer. |
| 1746 | ;; |
| 1747 | none) |
| 1748 | ;; |
| 1749 | *) |
| 1750 | echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 |
| 1751 | exit 1 |
| 1752 | ;; |
| 1753 | esac |
| 1754 | |
| 1755 | # As a final step for OS-related things, validate the OS-kernel combination |
| 1756 | # (given a valid OS), if there is a kernel. |
| 1757 | case $kernel-$os in |
| 1758 | linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) |
| 1759 | ;; |
| 1760 | uclinux-uclibc* ) |
| 1761 | ;; |
| 1762 | -dietlibc* | -newlib* | -musl* | -uclibc* ) |
| 1763 | # These are just libc implementations, not actual OSes, and thus |
| 1764 | # require a kernel. |
| 1765 | echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 |
| 1766 | exit 1 |
| 1767 | ;; |
| 1768 | kfreebsd*-gnu* | kopensolaris*-gnu*) |
| 1769 | ;; |
| 1770 | vxworks-simlinux | vxworks-simwindows | vxworks-spe) |
| 1771 | ;; |
| 1772 | nto-qnx*) |
| 1773 | ;; |
| 1774 | os2-emx) |
| 1775 | ;; |
| 1776 | *-eabi* | *-gnueabi*) |
| 1777 | ;; |
| 1778 | -*) |
| 1779 | # Blank kernel with real OS is always fine. |
| 1780 | ;; |
| 1781 | *-*) |
| 1782 | echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 |
| 1783 | exit 1 |
| 1784 | ;; |
| 1785 | esac |
| 1786 | |
| 1787 | # Here we handle the case where we know the os, and the CPU type, but not the |
| @@ -1790,11 +2271,11 @@ | |
| 1790 | unknown) |
| 1791 | case $cpu-$os in |
| 1792 | *-riscix*) |
| 1793 | vendor=acorn |
| 1794 | ;; |
| 1795 | *-sunos*) |
| 1796 | vendor=sun |
| 1797 | ;; |
| 1798 | *-cnk* | *-aix*) |
| 1799 | vendor=ibm |
| 1800 | ;; |
| @@ -1860,14 +2341,14 @@ | |
| 1860 | ;; |
| 1861 | esac |
| 1862 | ;; |
| 1863 | esac |
| 1864 | |
| 1865 | echo "$cpu-$vendor-${kernel:+$kernel-}$os" |
| 1866 | exit |
| 1867 | |
| 1868 | # Local variables: |
| 1869 | # eval: (add-hook 'before-save-hook 'time-stamp) |
| 1870 | # time-stamp-start: "timestamp='" |
| 1871 | # time-stamp-format: "%:y-%02m-%02d" |
| 1872 | # time-stamp-end: "'" |
| 1873 | # End: |
| 1874 |
| --- autosetup/autosetup-config.sub | |
| +++ autosetup/autosetup-config.sub | |
| @@ -1,16 +1,16 @@ | |
| 1 | #! /bin/sh |
| 2 | # Configuration validation subroutine script. |
| 3 | # Copyright 1992-2024 Free Software Foundation, Inc. |
| 4 | |
| 5 | # shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale |
| 6 | |
| 7 | timestamp='2024-05-27' |
| 8 | |
| 9 | # This file is free software; you can redistribute it and/or modify it |
| 10 | # under the terms of the GNU General Public License as published by |
| 11 | # the Free Software Foundation, either version 3 of the License, or |
| 12 | # (at your option) any later version. |
| 13 | # |
| 14 | # This program is distributed in the hope that it will be useful, but |
| 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| @@ -74,17 +74,17 @@ | |
| 74 | Report bugs and patches to <[email protected]>." |
| 75 | |
| 76 | version="\ |
| 77 | GNU config.sub ($timestamp) |
| 78 | |
| 79 | Copyright 1992-2024 Free Software Foundation, Inc. |
| 80 | |
| 81 | This is free software; see the source for copying conditions. There is NO |
| 82 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." |
| 83 | |
| 84 | help=" |
| 85 | Try '$me --help' for more information." |
| 86 | |
| 87 | # Parse command line |
| 88 | while test $# -gt 0 ; do |
| 89 | case $1 in |
| 90 | --time-stamp | --time* | -t ) |
| @@ -118,19 +118,20 @@ | |
| 118 | *) echo "$me: too many arguments$help" >&2 |
| 119 | exit 1;; |
| 120 | esac |
| 121 | |
| 122 | # Split fields of configuration type |
| 123 | saved_IFS=$IFS |
| 124 | IFS="-" read field1 field2 field3 field4 <<EOF |
| 125 | $1 |
| 126 | EOF |
| 127 | IFS=$saved_IFS |
| 128 | |
| 129 | # Separate into logical components for further validation |
| 130 | case $1 in |
| 131 | *-*-*-*-*) |
| 132 | echo "Invalid configuration '$1': more than four components" >&2 |
| 133 | exit 1 |
| 134 | ;; |
| 135 | *-*-*-*) |
| 136 | basic_machine=$field1-$field2 |
| 137 | basic_os=$field3-$field4 |
| @@ -138,14 +139,25 @@ | |
| 139 | *-*-*) |
| 140 | # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two |
| 141 | # parts |
| 142 | maybe_os=$field2-$field3 |
| 143 | case $maybe_os in |
| 144 | cloudabi*-eabi* \ |
| 145 | | kfreebsd*-gnu* \ |
| 146 | | knetbsd*-gnu* \ |
| 147 | | kopensolaris*-gnu* \ |
| 148 | | linux-* \ |
| 149 | | managarm-* \ |
| 150 | | netbsd*-eabi* \ |
| 151 | | netbsd*-gnu* \ |
| 152 | | nto-qnx* \ |
| 153 | | os2-emx* \ |
| 154 | | rtmk-nova* \ |
| 155 | | storm-chaos* \ |
| 156 | | uclinux-gnu* \ |
| 157 | | uclinux-uclibc* \ |
| 158 | | windows-* ) |
| 159 | basic_machine=$field1 |
| 160 | basic_os=$maybe_os |
| 161 | ;; |
| 162 | android-linux) |
| 163 | basic_machine=$field1-unknown |
| @@ -156,37 +168,105 @@ | |
| 168 | basic_os=$field3 |
| 169 | ;; |
| 170 | esac |
| 171 | ;; |
| 172 | *-*) |
| 173 | case $field1-$field2 in |
| 174 | # Shorthands that happen to contain a single dash |
| 175 | convex-c[12] | convex-c3[248]) |
| 176 | basic_machine=$field2-convex |
| 177 | basic_os= |
| 178 | ;; |
| 179 | decstation-3100) |
| 180 | basic_machine=mips-dec |
| 181 | basic_os= |
| 182 | ;; |
| 183 | *-*) |
| 184 | # Second component is usually, but not always the OS |
| 185 | case $field2 in |
| 186 | # Do not treat sunos as a manufacturer |
| 187 | sun*os*) |
| 188 | basic_machine=$field1 |
| 189 | basic_os=$field2 |
| 190 | ;; |
| 191 | # Manufacturers |
| 192 | 3100* \ |
| 193 | | 32* \ |
| 194 | | 3300* \ |
| 195 | | 3600* \ |
| 196 | | 7300* \ |
| 197 | | acorn \ |
| 198 | | altos* \ |
| 199 | | apollo \ |
| 200 | | apple \ |
| 201 | | atari \ |
| 202 | | att* \ |
| 203 | | axis \ |
| 204 | | be \ |
| 205 | | bull \ |
| 206 | | cbm \ |
| 207 | | ccur \ |
| 208 | | cisco \ |
| 209 | | commodore \ |
| 210 | | convergent* \ |
| 211 | | convex* \ |
| 212 | | cray \ |
| 213 | | crds \ |
| 214 | | dec* \ |
| 215 | | delta* \ |
| 216 | | dg \ |
| 217 | | digital \ |
| 218 | | dolphin \ |
| 219 | | encore* \ |
| 220 | | gould \ |
| 221 | | harris \ |
| 222 | | highlevel \ |
| 223 | | hitachi* \ |
| 224 | | hp \ |
| 225 | | ibm* \ |
| 226 | | intergraph \ |
| 227 | | isi* \ |
| 228 | | knuth \ |
| 229 | | masscomp \ |
| 230 | | microblaze* \ |
| 231 | | mips* \ |
| 232 | | motorola* \ |
| 233 | | ncr* \ |
| 234 | | news \ |
| 235 | | next \ |
| 236 | | ns \ |
| 237 | | oki \ |
| 238 | | omron* \ |
| 239 | | pc533* \ |
| 240 | | rebel \ |
| 241 | | rom68k \ |
| 242 | | rombug \ |
| 243 | | semi \ |
| 244 | | sequent* \ |
| 245 | | siemens \ |
| 246 | | sgi* \ |
| 247 | | siemens \ |
| 248 | | sim \ |
| 249 | | sni \ |
| 250 | | sony* \ |
| 251 | | stratus \ |
| 252 | | sun \ |
| 253 | | sun[234]* \ |
| 254 | | tektronix \ |
| 255 | | tti* \ |
| 256 | | ultra \ |
| 257 | | unicom* \ |
| 258 | | wec \ |
| 259 | | winbond \ |
| 260 | | wrs) |
| 261 | basic_machine=$field1-$field2 |
| 262 | basic_os= |
| 263 | ;; |
| 264 | zephyr*) |
| 265 | basic_machine=$field1-unknown |
| 266 | basic_os=$field2 |
| 267 | ;; |
| 268 | *) |
| 269 | basic_machine=$field1 |
| 270 | basic_os=$field2 |
| 271 | ;; |
| 272 | esac |
| @@ -263,30 +343,10 @@ | |
| 343 | ;; |
| 344 | cegcc) |
| 345 | basic_machine=arm-unknown |
| 346 | basic_os=cegcc |
| 347 | ;; |
| 348 | cray) |
| 349 | basic_machine=j90-cray |
| 350 | basic_os=unicos |
| 351 | ;; |
| 352 | crds | unos) |
| @@ -705,13 +765,21 @@ | |
| 765 | decsystem20* | dec20*) |
| 766 | cpu=pdp10 |
| 767 | vendor=dec |
| 768 | basic_os=tops20 |
| 769 | ;; |
| 770 | delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) |
| 771 | cpu=m68k |
| 772 | vendor=motorola |
| 773 | ;; |
| 774 | # This used to be dpx2*, but that gets the RS6000-based |
| 775 | # DPX/20 and the x86-based DPX/2-100 wrong. See |
| 776 | # https://oldskool.silicium.org/stations/bull_dpx20.htm |
| 777 | # https://www.feb-patrimoine.com/english/bull_dpx2.htm |
| 778 | # https://www.feb-patrimoine.com/english/unix_and_bull.htm |
| 779 | dpx2 | dpx2[23]00 | dpx2[23]xx) |
| 780 | cpu=m68k |
| 781 | vendor=bull |
| 782 | ;; |
| 783 | dpx2100 | dpx21xx) |
| 784 | cpu=i386 |
| 785 | vendor=bull |
| @@ -718,6 +786,9 @@ | |
| 786 | ;; |
| 787 | dpx20) |
| 788 | cpu=rs6000 |
| 789 | vendor=bull |
| 790 | ;; |
| 791 | encore | umax | mmax) |
| 792 | cpu=ns32k |
| 793 | vendor=encore |
| 794 | ;; |
| @@ -828,22 +899,10 @@ | |
| 899 | basic_os=newsos |
| 900 | ;; |
| 901 | next | m*-next) |
| 902 | cpu=m68k |
| 903 | vendor=next |
| 904 | ;; |
| 905 | np1) |
| 906 | cpu=np1 |
| 907 | vendor=gould |
| 908 | ;; |
| @@ -928,16 +987,17 @@ | |
| 987 | cpu=sparc |
| 988 | vendor=`echo "$basic_machine" | sed 's/-.*//'` |
| 989 | ;; |
| 990 | |
| 991 | *-*) |
| 992 | saved_IFS=$IFS |
| 993 | IFS="-" read cpu vendor <<EOF |
| 994 | $basic_machine |
| 995 | EOF |
| 996 | IFS=$saved_IFS |
| 997 | ;; |
| 998 | # We use 'pc' rather than 'unknown' |
| 999 | # because (1) that's what they normally are, and |
| 1000 | # (2) the word "unknown" tends to confuse beginning users. |
| 1001 | i*86 | x86_64) |
| 1002 | cpu=$basic_machine |
| 1003 | vendor=pc |
| @@ -961,19 +1021,23 @@ | |
| 1021 | |
| 1022 | unset -v basic_machine |
| 1023 | |
| 1024 | # Decode basic machines in the full and proper CPU-Company form. |
| 1025 | case $cpu-$vendor in |
| 1026 | # Here we handle the default manufacturer of certain CPU types in canonical form. |
| 1027 | # It is in some cases the only manufacturer, in others, it is the most popular. |
| 1028 | c[12]-convex | c[12]-unknown | c3[248]-convex | c3[248]-unknown) |
| 1029 | vendor=convex |
| 1030 | basic_os=${basic_os:-bsd} |
| 1031 | ;; |
| 1032 | craynv-unknown) |
| 1033 | vendor=cray |
| 1034 | basic_os=${basic_os:-unicosmp} |
| 1035 | ;; |
| 1036 | c90-unknown | c90-cray) |
| 1037 | vendor=cray |
| 1038 | basic_os=${basic_os:-unicos} |
| 1039 | ;; |
| 1040 | fx80-unknown) |
| 1041 | vendor=alliant |
| 1042 | ;; |
| 1043 | romp-unknown) |
| @@ -1010,23 +1074,46 @@ | |
| 1074 | cpu=xps100 |
| 1075 | vendor=honeywell |
| 1076 | ;; |
| 1077 | |
| 1078 | # Here we normalize CPU types with a missing or matching vendor |
| 1079 | armh-unknown | armh-alt) |
| 1080 | cpu=armv7l |
| 1081 | vendor=alt |
| 1082 | basic_os=${basic_os:-linux-gnueabihf} |
| 1083 | ;; |
| 1084 | |
| 1085 | # Normalized CPU+vendor pairs that imply an OS, if not otherwise specified |
| 1086 | m68k-isi) |
| 1087 | basic_os=${basic_os:-sysv} |
| 1088 | ;; |
| 1089 | m68k-sony) |
| 1090 | basic_os=${basic_os:-newsos} |
| 1091 | ;; |
| 1092 | m68k-tektronix) |
| 1093 | basic_os=${basic_os:-bsd} |
| 1094 | ;; |
| 1095 | m88k-harris) |
| 1096 | basic_os=${basic_os:-sysv3} |
| 1097 | ;; |
| 1098 | i386-bull | m68k-bull) |
| 1099 | basic_os=${basic_os:-sysv3} |
| 1100 | ;; |
| 1101 | rs6000-bull) |
| 1102 | basic_os=${basic_os:-bosx} |
| 1103 | ;; |
| 1104 | mips-sni) |
| 1105 | basic_os=${basic_os:-sysv4} |
| 1106 | ;; |
| 1107 | |
| 1108 | # Here we normalize CPU types irrespective of the vendor |
| 1109 | amd64-*) |
| 1110 | cpu=x86_64 |
| 1111 | ;; |
| 1112 | blackfin-*) |
| 1113 | cpu=bfin |
| 1114 | basic_os=${basic_os:-linux} |
| 1115 | ;; |
| 1116 | c54x-*) |
| 1117 | cpu=tic54x |
| 1118 | ;; |
| 1119 | c55x-*) |
| @@ -1045,37 +1132,34 @@ | |
| 1132 | ms1-*) |
| 1133 | cpu=mt |
| 1134 | ;; |
| 1135 | m68knommu-*) |
| 1136 | cpu=m68k |
| 1137 | basic_os=${basic_os:-linux} |
| 1138 | ;; |
| 1139 | m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) |
| 1140 | cpu=s12z |
| 1141 | ;; |
| 1142 | openrisc-*) |
| 1143 | cpu=or32 |
| 1144 | ;; |
| 1145 | parisc-*) |
| 1146 | cpu=hppa |
| 1147 | basic_os=${basic_os:-linux} |
| 1148 | ;; |
| 1149 | pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) |
| 1150 | cpu=i586 |
| 1151 | ;; |
| 1152 | pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*) |
| 1153 | cpu=i686 |
| 1154 | ;; |
| 1155 | pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) |
| 1156 | cpu=i686 |
| 1157 | ;; |
| 1158 | pentium4-*) |
| 1159 | cpu=i786 |
| 1160 | ;; |
| 1161 | ppc-* | ppcbe-*) |
| 1162 | cpu=powerpc |
| 1163 | ;; |
| 1164 | ppcle-* | powerpclittle-*) |
| 1165 | cpu=powerpcle |
| @@ -1105,17 +1189,14 @@ | |
| 1189 | cpu=mipstx39 |
| 1190 | ;; |
| 1191 | tx39el-*) |
| 1192 | cpu=mipstx39el |
| 1193 | ;; |
| 1194 | xscale-* | xscalee[bl]-*) |
| 1195 | cpu=`echo "$cpu" | sed 's/^xscale/arm/'` |
| 1196 | ;; |
| 1197 | arm64-* | aarch64le-*) |
| 1198 | cpu=aarch64 |
| 1199 | ;; |
| 1200 | |
| 1201 | # Recognize the canonical CPU Types that limit and/or modify the |
| 1202 | # company names they are paired with. |
| @@ -1163,118 +1244,235 @@ | |
| 1244 | |
| 1245 | *) |
| 1246 | # Recognize the canonical CPU types that are allowed with any |
| 1247 | # company name. |
| 1248 | case $cpu in |
| 1249 | 1750a \ |
| 1250 | | 580 \ |
| 1251 | | [cjt]90 \ |
| 1252 | | a29k \ |
| 1253 | | aarch64 \ |
| 1254 | | aarch64_be \ |
| 1255 | | aarch64c \ |
| 1256 | | abacus \ |
| 1257 | | alpha \ |
| 1258 | | alpha64 \ |
| 1259 | | alpha64ev56 \ |
| 1260 | | alpha64ev6[78] \ |
| 1261 | | alpha64ev[4-8] \ |
| 1262 | | alpha64pca5[67] \ |
| 1263 | | alphaev56 \ |
| 1264 | | alphaev6[78] \ |
| 1265 | | alphaev[4-8] \ |
| 1266 | | alphapca5[67] \ |
| 1267 | | am33_2.0 \ |
| 1268 | | amdgcn \ |
| 1269 | | arc \ |
| 1270 | | arc32 \ |
| 1271 | | arc64 \ |
| 1272 | | arceb \ |
| 1273 | | arm \ |
| 1274 | | arm64e \ |
| 1275 | | arm64ec \ |
| 1276 | | arm[lb]e \ |
| 1277 | | arme[lb] \ |
| 1278 | | armv* \ |
| 1279 | | asmjs \ |
| 1280 | | avr \ |
| 1281 | | avr32 \ |
| 1282 | | ba \ |
| 1283 | | be32 \ |
| 1284 | | be64 \ |
| 1285 | | bfin \ |
| 1286 | | bpf \ |
| 1287 | | bs2000 \ |
| 1288 | | c30 \ |
| 1289 | | c4x \ |
| 1290 | | c8051 \ |
| 1291 | | c[123]* \ |
| 1292 | | clipper \ |
| 1293 | | craynv \ |
| 1294 | | csky \ |
| 1295 | | cydra \ |
| 1296 | | d10v \ |
| 1297 | | d30v \ |
| 1298 | | dlx \ |
| 1299 | | dsp16xx \ |
| 1300 | | e2k \ |
| 1301 | | elxsi \ |
| 1302 | | epiphany \ |
| 1303 | | f30[01] \ |
| 1304 | | f700 \ |
| 1305 | | fido \ |
| 1306 | | fr30 \ |
| 1307 | | frv \ |
| 1308 | | ft32 \ |
| 1309 | | fx80 \ |
| 1310 | | h8300 \ |
| 1311 | | h8500 \ |
| 1312 | | hexagon \ |
| 1313 | | hppa \ |
| 1314 | | hppa1.[01] \ |
| 1315 | | hppa2.0 \ |
| 1316 | | hppa2.0[nw] \ |
| 1317 | | hppa64 \ |
| 1318 | | i*86 \ |
| 1319 | | i370 \ |
| 1320 | | i860 \ |
| 1321 | | i960 \ |
| 1322 | | ia16 \ |
| 1323 | | ia64 \ |
| 1324 | | ip2k \ |
| 1325 | | iq2000 \ |
| 1326 | | javascript \ |
| 1327 | | k1om \ |
| 1328 | | kvx \ |
| 1329 | | le32 \ |
| 1330 | | le64 \ |
| 1331 | | lm32 \ |
| 1332 | | loongarch32 \ |
| 1333 | | loongarch64 \ |
| 1334 | | m32c \ |
| 1335 | | m32r \ |
| 1336 | | m32rle \ |
| 1337 | | m5200 \ |
| 1338 | | m68000 \ |
| 1339 | | m680[012346]0 \ |
| 1340 | | m6811 \ |
| 1341 | | m6812 \ |
| 1342 | | m68360 \ |
| 1343 | | m683?2 \ |
| 1344 | | m68hc11 \ |
| 1345 | | m68hc12 \ |
| 1346 | | m68hcs12x \ |
| 1347 | | m68k \ |
| 1348 | | m88110 \ |
| 1349 | | m88k \ |
| 1350 | | maxq \ |
| 1351 | | mb \ |
| 1352 | | mcore \ |
| 1353 | | mep \ |
| 1354 | | metag \ |
| 1355 | | microblaze \ |
| 1356 | | microblazeel \ |
| 1357 | | mips* \ |
| 1358 | | mmix \ |
| 1359 | | mn10200 \ |
| 1360 | | mn10300 \ |
| 1361 | | moxie \ |
| 1362 | | msp430 \ |
| 1363 | | mt \ |
| 1364 | | nanomips* \ |
| 1365 | | nds32 \ |
| 1366 | | nds32be \ |
| 1367 | | nds32le \ |
| 1368 | | nfp \ |
| 1369 | | nios \ |
| 1370 | | nios2 \ |
| 1371 | | nios2eb \ |
| 1372 | | nios2el \ |
| 1373 | | none \ |
| 1374 | | np1 \ |
| 1375 | | ns16k \ |
| 1376 | | ns32k \ |
| 1377 | | nvptx \ |
| 1378 | | open8 \ |
| 1379 | | or1k* \ |
| 1380 | | or32 \ |
| 1381 | | orion \ |
| 1382 | | pdp10 \ |
| 1383 | | pdp11 \ |
| 1384 | | picochip \ |
| 1385 | | pj \ |
| 1386 | | pjl \ |
| 1387 | | pn \ |
| 1388 | | power \ |
| 1389 | | powerpc \ |
| 1390 | | powerpc64 \ |
| 1391 | | powerpc64le \ |
| 1392 | | powerpcle \ |
| 1393 | | powerpcspe \ |
| 1394 | | pru \ |
| 1395 | | pyramid \ |
| 1396 | | riscv \ |
| 1397 | | riscv32 \ |
| 1398 | | riscv32be \ |
| 1399 | | riscv64 \ |
| 1400 | | riscv64be \ |
| 1401 | | rl78 \ |
| 1402 | | romp \ |
| 1403 | | rs6000 \ |
| 1404 | | rx \ |
| 1405 | | s390 \ |
| 1406 | | s390x \ |
| 1407 | | score \ |
| 1408 | | sh \ |
| 1409 | | sh64 \ |
| 1410 | | sh64le \ |
| 1411 | | sh[12345][lb]e \ |
| 1412 | | sh[1234] \ |
| 1413 | | sh[1234]e[lb] \ |
| 1414 | | sh[23]e \ |
| 1415 | | sh[23]ele \ |
| 1416 | | sh[24]a \ |
| 1417 | | sh[24]ae[lb] \ |
| 1418 | | sh[lb]e \ |
| 1419 | | she[lb] \ |
| 1420 | | shl \ |
| 1421 | | sparc \ |
| 1422 | | sparc64 \ |
| 1423 | | sparc64b \ |
| 1424 | | sparc64v \ |
| 1425 | | sparc86x \ |
| 1426 | | sparclet \ |
| 1427 | | sparclite \ |
| 1428 | | sparcv8 \ |
| 1429 | | sparcv9 \ |
| 1430 | | sparcv9b \ |
| 1431 | | sparcv9v \ |
| 1432 | | spu \ |
| 1433 | | sv1 \ |
| 1434 | | sx* \ |
| 1435 | | tahoe \ |
| 1436 | | thumbv7* \ |
| 1437 | | tic30 \ |
| 1438 | | tic4x \ |
| 1439 | | tic54x \ |
| 1440 | | tic55x \ |
| 1441 | | tic6x \ |
| 1442 | | tic80 \ |
| 1443 | | tron \ |
| 1444 | | ubicom32 \ |
| 1445 | | v70 \ |
| 1446 | | v810 \ |
| 1447 | | v850 \ |
| 1448 | | v850e \ |
| 1449 | | v850e1 \ |
| 1450 | | v850e2 \ |
| 1451 | | v850e2v3 \ |
| 1452 | | v850es \ |
| 1453 | | vax \ |
| 1454 | | vc4 \ |
| 1455 | | visium \ |
| 1456 | | w65 \ |
| 1457 | | wasm32 \ |
| 1458 | | wasm64 \ |
| 1459 | | we32k \ |
| 1460 | | x86 \ |
| 1461 | | x86_64 \ |
| 1462 | | xc16x \ |
| 1463 | | xgate \ |
| 1464 | | xps100 \ |
| 1465 | | xstormy16 \ |
| 1466 | | xtensa* \ |
| 1467 | | ymp \ |
| 1468 | | z80 \ |
| 1469 | | z8k) |
| 1470 | ;; |
| 1471 | |
| 1472 | *) |
| 1473 | echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2 |
| 1474 | exit 1 |
| 1475 | ;; |
| 1476 | esac |
| 1477 | ;; |
| 1478 | esac |
| @@ -1291,15 +1489,16 @@ | |
| 1489 | ;; |
| 1490 | esac |
| 1491 | |
| 1492 | # Decode manufacturer-specific aliases for certain operating systems. |
| 1493 | |
| 1494 | if test x"$basic_os" != x |
| 1495 | then |
| 1496 | |
| 1497 | # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just |
| 1498 | # set os. |
| 1499 | obj= |
| 1500 | case $basic_os in |
| 1501 | gnu/linux*) |
| 1502 | kernel=linux |
| 1503 | os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` |
| 1504 | ;; |
| @@ -1310,14 +1509,15 @@ | |
| 1509 | nto-qnx*) |
| 1510 | kernel=nto |
| 1511 | os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` |
| 1512 | ;; |
| 1513 | *-*) |
| 1514 | saved_IFS=$IFS |
| 1515 | IFS="-" read kernel os <<EOF |
| 1516 | $basic_os |
| 1517 | EOF |
| 1518 | IFS=$saved_IFS |
| 1519 | ;; |
| 1520 | # Default OS when just kernel was specified |
| 1521 | nto*) |
| 1522 | kernel=nto |
| 1523 | os=`echo "$basic_os" | sed -e 's|nto|qnx|'` |
| @@ -1324,10 +1524,14 @@ | |
| 1524 | ;; |
| 1525 | linux*) |
| 1526 | kernel=linux |
| 1527 | os=`echo "$basic_os" | sed -e 's|linux|gnu|'` |
| 1528 | ;; |
| 1529 | managarm*) |
| 1530 | kernel=managarm |
| 1531 | os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'` |
| 1532 | ;; |
| 1533 | *) |
| 1534 | kernel= |
| 1535 | os=$basic_os |
| 1536 | ;; |
| 1537 | esac |
| @@ -1350,10 +1554,27 @@ | |
| 1554 | solaris) |
| 1555 | os=solaris2 |
| 1556 | ;; |
| 1557 | unixware*) |
| 1558 | os=sysv4.2uw |
| 1559 | ;; |
| 1560 | # The marketing names for NeXT's operating systems were |
| 1561 | # NeXTSTEP, NeXTSTEP 2, OpenSTEP 3, OpenSTEP 4. 'openstep' is |
| 1562 | # mapped to 'openstep3', but 'openstep1' and 'openstep2' are |
| 1563 | # mapped to 'nextstep' and 'nextstep2', consistent with the |
| 1564 | # treatment of SunOS/Solaris. |
| 1565 | ns | ns1 | nextstep | nextstep1 | openstep1) |
| 1566 | os=nextstep |
| 1567 | ;; |
| 1568 | ns2 | nextstep2 | openstep2) |
| 1569 | os=nextstep2 |
| 1570 | ;; |
| 1571 | ns3 | nextstep3 | openstep | openstep3) |
| 1572 | os=openstep3 |
| 1573 | ;; |
| 1574 | ns4 | nextstep4 | openstep4) |
| 1575 | os=openstep4 |
| 1576 | ;; |
| 1577 | # es1800 is here to avoid being matched by es* (a different OS) |
| 1578 | es1800*) |
| 1579 | os=ose |
| 1580 | ;; |
| @@ -1421,10 +1642,11 @@ | |
| 1642 | wince*) |
| 1643 | os=wince |
| 1644 | ;; |
| 1645 | utek*) |
| 1646 | os=bsd |
| 1647 | vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'` |
| 1648 | ;; |
| 1649 | dynix*) |
| 1650 | os=bsd |
| 1651 | ;; |
| 1652 | acis*) |
| @@ -1437,25 +1659,29 @@ | |
| 1659 | os=syllable |
| 1660 | ;; |
| 1661 | 386bsd) |
| 1662 | os=bsd |
| 1663 | ;; |
| 1664 | ctix*) |
| 1665 | os=sysv |
| 1666 | vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'` |
| 1667 | ;; |
| 1668 | uts*) |
| 1669 | os=sysv |
| 1670 | ;; |
| 1671 | nova*) |
| 1672 | kernel=rtmk |
| 1673 | os=nova |
| 1674 | ;; |
| 1675 | # Preserve the version number of sinix5. |
| 1676 | sinix5.*) |
| 1677 | os=`echo "$os" | sed -e 's|sinix|sysv|'` |
| 1678 | vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` |
| 1679 | ;; |
| 1680 | sinix*) |
| 1681 | os=sysv4 |
| 1682 | vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` |
| 1683 | ;; |
| 1684 | tpf*) |
| 1685 | os=tpf |
| 1686 | ;; |
| 1687 | triton*) |
| @@ -1489,13 +1715,19 @@ | |
| 1715 | case $cpu in |
| 1716 | arm*) |
| 1717 | os=eabi |
| 1718 | ;; |
| 1719 | *) |
| 1720 | os= |
| 1721 | obj=elf |
| 1722 | ;; |
| 1723 | esac |
| 1724 | ;; |
| 1725 | aout* | coff* | elf* | pe*) |
| 1726 | # These are machine code file formats, not OSes |
| 1727 | obj=$os |
| 1728 | os= |
| 1729 | ;; |
| 1730 | *) |
| 1731 | # No normalization, but not necessarily accepted, that comes below. |
| 1732 | ;; |
| 1733 | esac |
| @@ -1511,47 +1743,57 @@ | |
| 1743 | # that MANUFACTURER isn't an operating system. Otherwise, code above |
| 1744 | # will signal an error saying that MANUFACTURER isn't an operating |
| 1745 | # system, and we'll never get to this point. |
| 1746 | |
| 1747 | kernel= |
| 1748 | obj= |
| 1749 | case $cpu-$vendor in |
| 1750 | score-*) |
| 1751 | os= |
| 1752 | obj=elf |
| 1753 | ;; |
| 1754 | spu-*) |
| 1755 | os= |
| 1756 | obj=elf |
| 1757 | ;; |
| 1758 | *-acorn) |
| 1759 | os=riscix1.2 |
| 1760 | ;; |
| 1761 | arm*-rebel) |
| 1762 | kernel=linux |
| 1763 | os=gnu |
| 1764 | ;; |
| 1765 | arm*-semi) |
| 1766 | os= |
| 1767 | obj=aout |
| 1768 | ;; |
| 1769 | c4x-* | tic4x-*) |
| 1770 | os= |
| 1771 | obj=coff |
| 1772 | ;; |
| 1773 | c8051-*) |
| 1774 | os= |
| 1775 | obj=elf |
| 1776 | ;; |
| 1777 | clipper-intergraph) |
| 1778 | os=clix |
| 1779 | ;; |
| 1780 | hexagon-*) |
| 1781 | os= |
| 1782 | obj=elf |
| 1783 | ;; |
| 1784 | tic54x-*) |
| 1785 | os= |
| 1786 | obj=coff |
| 1787 | ;; |
| 1788 | tic55x-*) |
| 1789 | os= |
| 1790 | obj=coff |
| 1791 | ;; |
| 1792 | tic6x-*) |
| 1793 | os= |
| 1794 | obj=coff |
| 1795 | ;; |
| 1796 | # This must come before the *-dec entry. |
| 1797 | pdp10-*) |
| 1798 | os=tops20 |
| 1799 | ;; |
| @@ -1569,32 +1811,47 @@ | |
| 1811 | ;; |
| 1812 | m68000-sun) |
| 1813 | os=sunos3 |
| 1814 | ;; |
| 1815 | m68*-cisco) |
| 1816 | os= |
| 1817 | obj=aout |
| 1818 | ;; |
| 1819 | mep-*) |
| 1820 | os= |
| 1821 | obj=elf |
| 1822 | ;; |
| 1823 | # The -sgi and -siemens entries must be before the mips- entry |
| 1824 | # or we get the wrong os. |
| 1825 | *-sgi) |
| 1826 | os=irix |
| 1827 | ;; |
| 1828 | *-siemens) |
| 1829 | os=sysv4 |
| 1830 | ;; |
| 1831 | mips*-cisco) |
| 1832 | os= |
| 1833 | obj=elf |
| 1834 | ;; |
| 1835 | mips*-*|nanomips*-*) |
| 1836 | os= |
| 1837 | obj=elf |
| 1838 | ;; |
| 1839 | or32-*) |
| 1840 | os= |
| 1841 | obj=coff |
| 1842 | ;; |
| 1843 | # This must be before the sparc-* entry or we get the wrong os. |
| 1844 | *-tti) |
| 1845 | os=sysv3 |
| 1846 | ;; |
| 1847 | sparc-* | *-sun) |
| 1848 | os=sunos4.1.1 |
| 1849 | ;; |
| 1850 | pru-*) |
| 1851 | os= |
| 1852 | obj=elf |
| 1853 | ;; |
| 1854 | *-be) |
| 1855 | os=beos |
| 1856 | ;; |
| 1857 | *-ibm) |
| @@ -1614,11 +1871,11 @@ | |
| 1871 | ;; |
| 1872 | *-hp) |
| 1873 | os=hpux |
| 1874 | ;; |
| 1875 | *-hitachi) |
| 1876 | os=hiuxwe2 |
| 1877 | ;; |
| 1878 | i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) |
| 1879 | os=sysv |
| 1880 | ;; |
| 1881 | *-cbm) |
| @@ -1658,27 +1915,23 @@ | |
| 1915 | os=bsd |
| 1916 | ;; |
| 1917 | *-encore) |
| 1918 | os=bsd |
| 1919 | ;; |
| 1920 | *-masscomp) |
| 1921 | os=rtu |
| 1922 | ;; |
| 1923 | f30[01]-fujitsu | f700-fujitsu) |
| 1924 | os=uxpv |
| 1925 | ;; |
| 1926 | *-rom68k) |
| 1927 | os= |
| 1928 | obj=coff |
| 1929 | ;; |
| 1930 | *-*bug) |
| 1931 | os= |
| 1932 | obj=coff |
| 1933 | ;; |
| 1934 | *-apple) |
| 1935 | os=macos |
| 1936 | ;; |
| 1937 | *-atari*) |
| @@ -1692,96 +1945,324 @@ | |
| 1945 | ;; |
| 1946 | esac |
| 1947 | |
| 1948 | fi |
| 1949 | |
| 1950 | # Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). |
| 1951 | |
| 1952 | case $os in |
| 1953 | # Sometimes we do "kernel-libc", so those need to count as OSes. |
| 1954 | llvm* | musl* | newlib* | relibc* | uclibc*) |
| 1955 | ;; |
| 1956 | # Likewise for "kernel-abi" |
| 1957 | eabi* | gnueabi*) |
| 1958 | ;; |
| 1959 | # VxWorks passes extra cpu info in the 4th filed. |
| 1960 | simlinux | simwindows | spe) |
| 1961 | ;; |
| 1962 | # See `case $cpu-$os` validation below |
| 1963 | ghcjs) |
| 1964 | ;; |
| 1965 | # Now accept the basic system types. |
| 1966 | # Each alternative MUST end in a * to match a version number. |
| 1967 | abug \ |
| 1968 | | aix* \ |
| 1969 | | amdhsa* \ |
| 1970 | | amigados* \ |
| 1971 | | amigaos* \ |
| 1972 | | android* \ |
| 1973 | | aof* \ |
| 1974 | | aos* \ |
| 1975 | | aros* \ |
| 1976 | | atheos* \ |
| 1977 | | auroraux* \ |
| 1978 | | aux* \ |
| 1979 | | beos* \ |
| 1980 | | bitrig* \ |
| 1981 | | bme* \ |
| 1982 | | bosx* \ |
| 1983 | | bsd* \ |
| 1984 | | cegcc* \ |
| 1985 | | chorusos* \ |
| 1986 | | chorusrdb* \ |
| 1987 | | clix* \ |
| 1988 | | cloudabi* \ |
| 1989 | | cnk* \ |
| 1990 | | conix* \ |
| 1991 | | cos* \ |
| 1992 | | cxux* \ |
| 1993 | | cygwin* \ |
| 1994 | | darwin* \ |
| 1995 | | dgux* \ |
| 1996 | | dicos* \ |
| 1997 | | dnix* \ |
| 1998 | | domain* \ |
| 1999 | | dragonfly* \ |
| 2000 | | drops* \ |
| 2001 | | ebmon* \ |
| 2002 | | ecoff* \ |
| 2003 | | ekkobsd* \ |
| 2004 | | emscripten* \ |
| 2005 | | emx* \ |
| 2006 | | es* \ |
| 2007 | | fiwix* \ |
| 2008 | | freebsd* \ |
| 2009 | | fuchsia* \ |
| 2010 | | genix* \ |
| 2011 | | genode* \ |
| 2012 | | glidix* \ |
| 2013 | | gnu* \ |
| 2014 | | go32* \ |
| 2015 | | haiku* \ |
| 2016 | | hcos* \ |
| 2017 | | hiux* \ |
| 2018 | | hms* \ |
| 2019 | | hpux* \ |
| 2020 | | ieee* \ |
| 2021 | | interix* \ |
| 2022 | | ios* \ |
| 2023 | | iris* \ |
| 2024 | | irix* \ |
| 2025 | | ironclad* \ |
| 2026 | | isc* \ |
| 2027 | | its* \ |
| 2028 | | l4re* \ |
| 2029 | | libertybsd* \ |
| 2030 | | lites* \ |
| 2031 | | lnews* \ |
| 2032 | | luna* \ |
| 2033 | | lynxos* \ |
| 2034 | | mach* \ |
| 2035 | | macos* \ |
| 2036 | | magic* \ |
| 2037 | | mbr* \ |
| 2038 | | midipix* \ |
| 2039 | | midnightbsd* \ |
| 2040 | | mingw32* \ |
| 2041 | | mingw64* \ |
| 2042 | | minix* \ |
| 2043 | | mint* \ |
| 2044 | | mirbsd* \ |
| 2045 | | mks* \ |
| 2046 | | mlibc* \ |
| 2047 | | mmixware* \ |
| 2048 | | mon960* \ |
| 2049 | | morphos* \ |
| 2050 | | moss* \ |
| 2051 | | moxiebox* \ |
| 2052 | | mpeix* \ |
| 2053 | | mpw* \ |
| 2054 | | msdos* \ |
| 2055 | | msys* \ |
| 2056 | | mvs* \ |
| 2057 | | nacl* \ |
| 2058 | | netbsd* \ |
| 2059 | | netware* \ |
| 2060 | | newsos* \ |
| 2061 | | nextstep* \ |
| 2062 | | nindy* \ |
| 2063 | | nonstopux* \ |
| 2064 | | nova* \ |
| 2065 | | nsk* \ |
| 2066 | | nucleus* \ |
| 2067 | | nx6 \ |
| 2068 | | nx7 \ |
| 2069 | | oabi* \ |
| 2070 | | ohos* \ |
| 2071 | | onefs* \ |
| 2072 | | openbsd* \ |
| 2073 | | openedition* \ |
| 2074 | | openstep* \ |
| 2075 | | os108* \ |
| 2076 | | os2* \ |
| 2077 | | os400* \ |
| 2078 | | os68k* \ |
| 2079 | | os9* \ |
| 2080 | | ose* \ |
| 2081 | | osf* \ |
| 2082 | | oskit* \ |
| 2083 | | osx* \ |
| 2084 | | palmos* \ |
| 2085 | | phoenix* \ |
| 2086 | | plan9* \ |
| 2087 | | powermax* \ |
| 2088 | | powerunix* \ |
| 2089 | | proelf* \ |
| 2090 | | psos* \ |
| 2091 | | psp* \ |
| 2092 | | ptx* \ |
| 2093 | | pw32* \ |
| 2094 | | qnx* \ |
| 2095 | | rdos* \ |
| 2096 | | redox* \ |
| 2097 | | rhapsody* \ |
| 2098 | | riscix* \ |
| 2099 | | riscos* \ |
| 2100 | | rtems* \ |
| 2101 | | rtmk* \ |
| 2102 | | rtu* \ |
| 2103 | | scout* \ |
| 2104 | | secbsd* \ |
| 2105 | | sei* \ |
| 2106 | | serenity* \ |
| 2107 | | sim* \ |
| 2108 | | skyos* \ |
| 2109 | | solaris* \ |
| 2110 | | solidbsd* \ |
| 2111 | | sortix* \ |
| 2112 | | storm-chaos* \ |
| 2113 | | sunos \ |
| 2114 | | sunos[34]* \ |
| 2115 | | superux* \ |
| 2116 | | syllable* \ |
| 2117 | | sym* \ |
| 2118 | | sysv* \ |
| 2119 | | tenex* \ |
| 2120 | | tirtos* \ |
| 2121 | | toppers* \ |
| 2122 | | tops10* \ |
| 2123 | | tops20* \ |
| 2124 | | tpf* \ |
| 2125 | | tvos* \ |
| 2126 | | twizzler* \ |
| 2127 | | uclinux* \ |
| 2128 | | udi* \ |
| 2129 | | udk* \ |
| 2130 | | ultrix* \ |
| 2131 | | unicos* \ |
| 2132 | | uniplus* \ |
| 2133 | | unleashed* \ |
| 2134 | | unos* \ |
| 2135 | | uwin* \ |
| 2136 | | uxpv* \ |
| 2137 | | v88r* \ |
| 2138 | |*vms* \ |
| 2139 | | vos* \ |
| 2140 | | vsta* \ |
| 2141 | | vxsim* \ |
| 2142 | | vxworks* \ |
| 2143 | | wasi* \ |
| 2144 | | watchos* \ |
| 2145 | | wince* \ |
| 2146 | | windiss* \ |
| 2147 | | windows* \ |
| 2148 | | winnt* \ |
| 2149 | | xenix* \ |
| 2150 | | xray* \ |
| 2151 | | zephyr* \ |
| 2152 | | zvmoe* ) |
| 2153 | ;; |
| 2154 | # This one is extra strict with allowed versions |
| 2155 | sco3.2v2 | sco3.2v[4-9]* | sco5v6*) |
| 2156 | # Don't forget version if it is 3.2v4 or newer. |
| 2157 | ;; |
| 2158 | # This refers to builds using the UEFI calling convention |
| 2159 | # (which depends on the architecture) and PE file format. |
| 2160 | # Note that this is both a different calling convention and |
| 2161 | # different file format than that of GNU-EFI |
| 2162 | # (x86_64-w64-mingw32). |
| 2163 | uefi) |
| 2164 | ;; |
| 2165 | none) |
| 2166 | ;; |
| 2167 | kernel* | msvc* ) |
| 2168 | # Restricted further below |
| 2169 | ;; |
| 2170 | '') |
| 2171 | if test x"$obj" = x |
| 2172 | then |
| 2173 | echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 |
| 2174 | fi |
| 2175 | ;; |
| 2176 | *) |
| 2177 | echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 |
| 2178 | exit 1 |
| 2179 | ;; |
| 2180 | esac |
| 2181 | |
| 2182 | case $obj in |
| 2183 | aout* | coff* | elf* | pe*) |
| 2184 | ;; |
| 2185 | '') |
| 2186 | # empty is fine |
| 2187 | ;; |
| 2188 | *) |
| 2189 | echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 |
| 2190 | exit 1 |
| 2191 | ;; |
| 2192 | esac |
| 2193 | |
| 2194 | # Here we handle the constraint that a (synthetic) cpu and os are |
| 2195 | # valid only in combination with each other and nowhere else. |
| 2196 | case $cpu-$os in |
| 2197 | # The "javascript-unknown-ghcjs" triple is used by GHC; we |
| 2198 | # accept it here in order to tolerate that, but reject any |
| 2199 | # variations. |
| 2200 | javascript-ghcjs) |
| 2201 | ;; |
| 2202 | javascript-* | *-ghcjs) |
| 2203 | echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 |
| 2204 | exit 1 |
| 2205 | ;; |
| 2206 | esac |
| 2207 | |
| 2208 | # As a final step for OS-related things, validate the OS-kernel combination |
| 2209 | # (given a valid OS), if there is a kernel. |
| 2210 | case $kernel-$os-$obj in |
| 2211 | linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ |
| 2212 | | linux-mlibc*- | linux-musl*- | linux-newlib*- \ |
| 2213 | | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) |
| 2214 | ;; |
| 2215 | uclinux-uclibc*- | uclinux-gnu*- ) |
| 2216 | ;; |
| 2217 | managarm-mlibc*- | managarm-kernel*- ) |
| 2218 | ;; |
| 2219 | windows*-msvc*-) |
| 2220 | ;; |
| 2221 | -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ |
| 2222 | | -uclibc*- ) |
| 2223 | # These are just libc implementations, not actual OSes, and thus |
| 2224 | # require a kernel. |
| 2225 | echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 |
| 2226 | exit 1 |
| 2227 | ;; |
| 2228 | -kernel*- ) |
| 2229 | echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 |
| 2230 | exit 1 |
| 2231 | ;; |
| 2232 | *-kernel*- ) |
| 2233 | echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 |
| 2234 | exit 1 |
| 2235 | ;; |
| 2236 | *-msvc*- ) |
| 2237 | echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 |
| 2238 | exit 1 |
| 2239 | ;; |
| 2240 | kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) |
| 2241 | ;; |
| 2242 | vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) |
| 2243 | ;; |
| 2244 | nto-qnx*-) |
| 2245 | ;; |
| 2246 | os2-emx-) |
| 2247 | ;; |
| 2248 | rtmk-nova-) |
| 2249 | ;; |
| 2250 | *-eabi*- | *-gnueabi*-) |
| 2251 | ;; |
| 2252 | none--*) |
| 2253 | # None (no kernel, i.e. freestanding / bare metal), |
| 2254 | # can be paired with an machine code file format |
| 2255 | ;; |
| 2256 | -*-) |
| 2257 | # Blank kernel with real OS is always fine. |
| 2258 | ;; |
| 2259 | --*) |
| 2260 | # Blank kernel and OS with real machine code file format is always fine. |
| 2261 | ;; |
| 2262 | *-*-*) |
| 2263 | echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 |
| 2264 | exit 1 |
| 2265 | ;; |
| 2266 | esac |
| 2267 | |
| 2268 | # Here we handle the case where we know the os, and the CPU type, but not the |
| @@ -1790,11 +2271,11 @@ | |
| 2271 | unknown) |
| 2272 | case $cpu-$os in |
| 2273 | *-riscix*) |
| 2274 | vendor=acorn |
| 2275 | ;; |
| 2276 | *-sunos* | *-solaris*) |
| 2277 | vendor=sun |
| 2278 | ;; |
| 2279 | *-cnk* | *-aix*) |
| 2280 | vendor=ibm |
| 2281 | ;; |
| @@ -1860,14 +2341,14 @@ | |
| 2341 | ;; |
| 2342 | esac |
| 2343 | ;; |
| 2344 | esac |
| 2345 | |
| 2346 | echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" |
| 2347 | exit |
| 2348 | |
| 2349 | # Local variables: |
| 2350 | # eval: (add-hook 'before-save-hook 'time-stamp) |
| 2351 | # time-stamp-start: "timestamp='" |
| 2352 | # time-stamp-format: "%:y-%02m-%02d" |
| 2353 | # time-stamp-end: "'" |
| 2354 | # End: |
| 2355 |
+3
-3
| --- autosetup/autosetup-find-tclsh | ||
| +++ autosetup/autosetup-find-tclsh | ||
| @@ -3,14 +3,14 @@ | ||
| 3 | 3 | # If not found, builds a bootstrap jimsh in current dir from source |
| 4 | 4 | # Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works) |
| 5 | 5 | # If an argument is given, use that as the test instead of autosetup-test-tclsh |
| 6 | 6 | d="`dirname "$0"`" |
| 7 | 7 | for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do |
| 8 | - { $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0 | |
| 8 | + { $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0 | |
| 9 | 9 | done |
| 10 | 10 | echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" |
| 11 | 11 | for cc in ${CC_FOR_BUILD:-cc} gcc; do |
| 12 | - { $cc -o jimsh0 "$d/jimsh0.c"; } 2>&1 >/dev/null || continue | |
| 13 | - ./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0 | |
| 12 | + { $cc -o jimsh0 "$d/jimsh0.c"; } >/dev/null 2>&1 || continue | |
| 13 | + ./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0 | |
| 14 | 14 | done |
| 15 | 15 | echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." |
| 16 | 16 | echo false |
| 17 | 17 |
| --- autosetup/autosetup-find-tclsh | |
| +++ autosetup/autosetup-find-tclsh | |
| @@ -3,14 +3,14 @@ | |
| 3 | # If not found, builds a bootstrap jimsh in current dir from source |
| 4 | # Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works) |
| 5 | # If an argument is given, use that as the test instead of autosetup-test-tclsh |
| 6 | d="`dirname "$0"`" |
| 7 | for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do |
| 8 | { $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0 |
| 9 | done |
| 10 | echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" |
| 11 | for cc in ${CC_FOR_BUILD:-cc} gcc; do |
| 12 | { $cc -o jimsh0 "$d/jimsh0.c"; } 2>&1 >/dev/null || continue |
| 13 | ./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0 |
| 14 | done |
| 15 | echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." |
| 16 | echo false |
| 17 |
| --- autosetup/autosetup-find-tclsh | |
| +++ autosetup/autosetup-find-tclsh | |
| @@ -3,14 +3,14 @@ | |
| 3 | # If not found, builds a bootstrap jimsh in current dir from source |
| 4 | # Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works) |
| 5 | # If an argument is given, use that as the test instead of autosetup-test-tclsh |
| 6 | d="`dirname "$0"`" |
| 7 | for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do |
| 8 | { $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0 |
| 9 | done |
| 10 | echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" |
| 11 | for cc in ${CC_FOR_BUILD:-cc} gcc; do |
| 12 | { $cc -o jimsh0 "$d/jimsh0.c"; } >/dev/null 2>&1 || continue |
| 13 | ./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0 |
| 14 | done |
| 15 | echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." |
| 16 | echo false |
| 17 |
+1
-1
| --- autosetup/cc.tcl | ||
| +++ autosetup/cc.tcl | ||
| @@ -3,11 +3,11 @@ | ||
| 3 | 3 | |
| 4 | 4 | # @synopsis: |
| 5 | 5 | # |
| 6 | 6 | # The 'cc' module supports checking various 'features' of the C or C++ |
| 7 | 7 | # compiler/linker environment. Common commands are 'cc-check-includes', |
| 8 | -# 'cc-check-types', 'cc-check-functions', 'cc-with', 'make-config-header' and 'make-template'. | |
| 8 | +# 'cc-check-types', 'cc-check-functions', 'cc-with' and 'make-config-header' | |
| 9 | 9 | # |
| 10 | 10 | # The following environment variables are used if set: |
| 11 | 11 | # |
| 12 | 12 | ## CC - C compiler |
| 13 | 13 | ## CXX - C++ compiler |
| 14 | 14 |
| --- autosetup/cc.tcl | |
| +++ autosetup/cc.tcl | |
| @@ -3,11 +3,11 @@ | |
| 3 | |
| 4 | # @synopsis: |
| 5 | # |
| 6 | # The 'cc' module supports checking various 'features' of the C or C++ |
| 7 | # compiler/linker environment. Common commands are 'cc-check-includes', |
| 8 | # 'cc-check-types', 'cc-check-functions', 'cc-with', 'make-config-header' and 'make-template'. |
| 9 | # |
| 10 | # The following environment variables are used if set: |
| 11 | # |
| 12 | ## CC - C compiler |
| 13 | ## CXX - C++ compiler |
| 14 |
| --- autosetup/cc.tcl | |
| +++ autosetup/cc.tcl | |
| @@ -3,11 +3,11 @@ | |
| 3 | |
| 4 | # @synopsis: |
| 5 | # |
| 6 | # The 'cc' module supports checking various 'features' of the C or C++ |
| 7 | # compiler/linker environment. Common commands are 'cc-check-includes', |
| 8 | # 'cc-check-types', 'cc-check-functions', 'cc-with' and 'make-config-header' |
| 9 | # |
| 10 | # The following environment variables are used if set: |
| 11 | # |
| 12 | ## CC - C compiler |
| 13 | ## CXX - C++ compiler |
| 14 |
+3370
-1991
| --- extsrc/shell.c | ||
| +++ extsrc/shell.c | ||
| @@ -120,13 +120,10 @@ | ||
| 120 | 120 | #include <math.h> |
| 121 | 121 | #include "sqlite3.h" |
| 122 | 122 | typedef sqlite3_int64 i64; |
| 123 | 123 | typedef sqlite3_uint64 u64; |
| 124 | 124 | typedef unsigned char u8; |
| 125 | -#if SQLITE_USER_AUTHENTICATION | |
| 126 | -# include "sqlite3userauth.h" | |
| 127 | -#endif | |
| 128 | 125 | #include <ctype.h> |
| 129 | 126 | #include <stdarg.h> |
| 130 | 127 | |
| 131 | 128 | #if !defined(_WIN32) && !defined(WIN32) |
| 132 | 129 | # include <signal.h> |
| @@ -208,12 +205,10 @@ | ||
| 208 | 205 | # define unlink _unlink |
| 209 | 206 | # endif |
| 210 | 207 | # ifndef strdup |
| 211 | 208 | # define strdup _strdup |
| 212 | 209 | # endif |
| 213 | -# undef popen | |
| 214 | -# define popen _popen | |
| 215 | 210 | # undef pclose |
| 216 | 211 | # define pclose _pclose |
| 217 | 212 | # endif |
| 218 | 213 | #else |
| 219 | 214 | /* Make sure isatty() has a prototype. */ |
| @@ -253,10 +248,370 @@ | ||
| 253 | 248 | /* string conversion routines only needed on Win32 */ |
| 254 | 249 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 255 | 250 | extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); |
| 256 | 251 | #endif |
| 257 | 252 | |
| 253 | +/************************* Begin ../ext/misc/sqlite3_stdio.h ******************/ | |
| 254 | +/* | |
| 255 | +** 2024-09-24 | |
| 256 | +** | |
| 257 | +** The author disclaims copyright to this source code. In place of | |
| 258 | +** a legal notice, here is a blessing: | |
| 259 | +** | |
| 260 | +** May you do good and not evil. | |
| 261 | +** May you find forgiveness for yourself and forgive others. | |
| 262 | +** May you share freely, never taking more than you give. | |
| 263 | +** | |
| 264 | +************************************************************************* | |
| 265 | +** | |
| 266 | +** This header file contains definitions of interfaces that provide | |
| 267 | +** cross-platform I/O for UTF-8 content. | |
| 268 | +** | |
| 269 | +** On most platforms, the interfaces definitions in this file are | |
| 270 | +** just #defines. For example sqlite3_fopen() is a macro that resolves | |
| 271 | +** to the standard fopen() in the C-library. | |
| 272 | +** | |
| 273 | +** But Windows does not have a standard C-library, at least not one that | |
| 274 | +** can handle UTF-8. So for windows build, the interfaces resolve to new | |
| 275 | +** C-language routines contained in the separate sqlite3_stdio.c source file. | |
| 276 | +** | |
| 277 | +** So on all non-Windows platforms, simply #include this header file and | |
| 278 | +** use the interfaces defined herein. Then to run your application on Windows, | |
| 279 | +** also link in the accompanying sqlite3_stdio.c source file when compiling | |
| 280 | +** to get compatible interfaces. | |
| 281 | +*/ | |
| 282 | +#ifndef _SQLITE3_STDIO_H_ | |
| 283 | +#define _SQLITE3_STDIO_H_ 1 | |
| 284 | +#ifdef _WIN32 | |
| 285 | +/**** Definitions For Windows ****/ | |
| 286 | +#include <stdio.h> | |
| 287 | +#include <windows.h> | |
| 288 | + | |
| 289 | +FILE *sqlite3_fopen(const char *zFilename, const char *zMode); | |
| 290 | +FILE *sqlite3_popen(const char *zCommand, const char *type); | |
| 291 | +char *sqlite3_fgets(char *s, int size, FILE *stream); | |
| 292 | +int sqlite3_fputs(const char *s, FILE *stream); | |
| 293 | +int sqlite3_fprintf(FILE *stream, const char *format, ...); | |
| 294 | +void sqlite3_fsetmode(FILE *stream, int mode); | |
| 295 | + | |
| 296 | + | |
| 297 | +#else | |
| 298 | +/**** Definitions For All Other Platforms ****/ | |
| 299 | +#include <stdio.h> | |
| 300 | +#define sqlite3_fopen fopen | |
| 301 | +#define sqlite3_popen popen | |
| 302 | +#define sqlite3_fgets fgets | |
| 303 | +#define sqlite3_fputs fputs | |
| 304 | +#define sqlite3_fprintf fprintf | |
| 305 | +#define sqlite3_fsetmode(F,X) /*no-op*/ | |
| 306 | + | |
| 307 | +#endif | |
| 308 | +#endif /* _SQLITE3_STDIO_H_ */ | |
| 309 | + | |
| 310 | +/************************* End ../ext/misc/sqlite3_stdio.h ********************/ | |
| 311 | +/************************* Begin ../ext/misc/sqlite3_stdio.c ******************/ | |
| 312 | +/* | |
| 313 | +** 2024-09-24 | |
| 314 | +** | |
| 315 | +** The author disclaims copyright to this source code. In place of | |
| 316 | +** a legal notice, here is a blessing: | |
| 317 | +** | |
| 318 | +** May you do good and not evil. | |
| 319 | +** May you find forgiveness for yourself and forgive others. | |
| 320 | +** May you share freely, never taking more than you give. | |
| 321 | +** | |
| 322 | +************************************************************************* | |
| 323 | +** | |
| 324 | +** Implementation of standard I/O interfaces for UTF-8 that are missing | |
| 325 | +** on Windows. | |
| 326 | +*/ | |
| 327 | +#ifdef _WIN32 /* This file is a no-op on all platforms except Windows */ | |
| 328 | +#ifndef _SQLITE3_STDIO_H_ | |
| 329 | +/* #include "sqlite3_stdio.h" */ | |
| 330 | +#endif | |
| 331 | +#undef WIN32_LEAN_AND_MEAN | |
| 332 | +#define WIN32_LEAN_AND_MEAN | |
| 333 | +#include <windows.h> | |
| 334 | +#include <stdlib.h> | |
| 335 | +#include <string.h> | |
| 336 | +#include <stdio.h> | |
| 337 | +#include <assert.h> | |
| 338 | +/* #include "sqlite3.h" */ | |
| 339 | +#include <ctype.h> | |
| 340 | +#include <stdarg.h> | |
| 341 | +#include <io.h> | |
| 342 | +#include <fcntl.h> | |
| 343 | + | |
| 344 | +/* | |
| 345 | +** If the SQLITE_U8TEXT_ONLY option is defined, then use O_U8TEXT | |
| 346 | +** when appropriate on all output. (Sometimes use O_BINARY when | |
| 347 | +** rendering ASCII text in cases where NL-to-CRLF expansion would | |
| 348 | +** not be correct.) | |
| 349 | +** | |
| 350 | +** If the SQLITE_U8TEXT_STDIO option is defined, then use O_U8TEXT | |
| 351 | +** when appropriate when writing to stdout or stderr. Use O_BINARY | |
| 352 | +** or O_TEXT (depending on things like the .mode and the .crlf setting | |
| 353 | +** in the CLI, or other context clues in other applications) for all | |
| 354 | +** other output channels. | |
| 355 | +** | |
| 356 | +** The default behavior, if neither of the above is defined is to | |
| 357 | +** use O_U8TEXT when writing to the Windows console (or anything | |
| 358 | +** else for which _isatty() returns true) and to use O_BINARY or O_TEXT | |
| 359 | +** for all other output channels. | |
| 360 | +*/ | |
| 361 | +#if defined(SQLITE_U8TEXT_ONLY) | |
| 362 | +# define UseWtextForOutput(fd) 1 | |
| 363 | +# define UseWtextForInput(fd) 1 | |
| 364 | +# define IsConsole(fd) _isatty(_fileno(fd)) | |
| 365 | +#elif defined(SQLITE_U8TEXT_STDIO) | |
| 366 | +# define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr) | |
| 367 | +# define UseWtextForInput(fd) ((fd)==stdin) | |
| 368 | +# define IsConsole(fd) _isatty(_fileno(fd)) | |
| 369 | +#else | |
| 370 | +# define UseWtextForOutput(fd) _isatty(_fileno(fd)) | |
| 371 | +# define UseWtextForInput(fd) _isatty(_fileno(fd)) | |
| 372 | +# define IsConsole(fd) 1 | |
| 373 | +#endif | |
| 374 | + | |
| 375 | +/* | |
| 376 | +** Global variables determine if simulated O_BINARY mode is to be | |
| 377 | +** used for stdout or other, respectively. Simulated O_BINARY mode | |
| 378 | +** means the mode is usually O_BINARY, but switches to O_U8TEXT for | |
| 379 | +** unicode characters U+0080 or greater (any character that has a | |
| 380 | +** multi-byte representation in UTF-8). This is the only way we | |
| 381 | +** have found to render Unicode characters on a Windows console while | |
| 382 | +** at the same time avoiding undesirable \n to \r\n translation. | |
| 383 | +*/ | |
| 384 | +static int simBinaryStdout = 0; | |
| 385 | +static int simBinaryOther = 0; | |
| 386 | + | |
| 387 | + | |
| 388 | +/* | |
| 389 | +** Determine if simulated binary mode should be used for output to fd | |
| 390 | +*/ | |
| 391 | +static int UseBinaryWText(FILE *fd){ | |
| 392 | + if( fd==stdout || fd==stderr ){ | |
| 393 | + return simBinaryStdout; | |
| 394 | + }else{ | |
| 395 | + return simBinaryOther; | |
| 396 | + } | |
| 397 | +} | |
| 398 | + | |
| 399 | + | |
| 400 | +/* | |
| 401 | +** Work-alike for the fopen() routine from the standard C library. | |
| 402 | +*/ | |
| 403 | +FILE *sqlite3_fopen(const char *zFilename, const char *zMode){ | |
| 404 | + FILE *fp = 0; | |
| 405 | + wchar_t *b1, *b2; | |
| 406 | + int sz1, sz2; | |
| 407 | + | |
| 408 | + sz1 = (int)strlen(zFilename); | |
| 409 | + sz2 = (int)strlen(zMode); | |
| 410 | + b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) ); | |
| 411 | + b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) ); | |
| 412 | + if( b1 && b2 ){ | |
| 413 | + sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1); | |
| 414 | + b1[sz1] = 0; | |
| 415 | + sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2); | |
| 416 | + b2[sz2] = 0; | |
| 417 | + fp = _wfopen(b1, b2); | |
| 418 | + } | |
| 419 | + sqlite3_free(b1); | |
| 420 | + sqlite3_free(b2); | |
| 421 | + simBinaryOther = 0; | |
| 422 | + return fp; | |
| 423 | +} | |
| 424 | + | |
| 425 | + | |
| 426 | +/* | |
| 427 | +** Work-alike for the popen() routine from the standard C library. | |
| 428 | +*/ | |
| 429 | +FILE *sqlite3_popen(const char *zCommand, const char *zMode){ | |
| 430 | + FILE *fp = 0; | |
| 431 | + wchar_t *b1, *b2; | |
| 432 | + int sz1, sz2; | |
| 433 | + | |
| 434 | + sz1 = (int)strlen(zCommand); | |
| 435 | + sz2 = (int)strlen(zMode); | |
| 436 | + b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) ); | |
| 437 | + b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) ); | |
| 438 | + if( b1 && b2 ){ | |
| 439 | + sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1); | |
| 440 | + b1[sz1] = 0; | |
| 441 | + sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2); | |
| 442 | + b2[sz2] = 0; | |
| 443 | + fp = _wpopen(b1, b2); | |
| 444 | + } | |
| 445 | + sqlite3_free(b1); | |
| 446 | + sqlite3_free(b2); | |
| 447 | + return fp; | |
| 448 | +} | |
| 449 | + | |
| 450 | +/* | |
| 451 | +** Work-alike for fgets() from the standard C library. | |
| 452 | +*/ | |
| 453 | +char *sqlite3_fgets(char *buf, int sz, FILE *in){ | |
| 454 | + if( UseWtextForInput(in) ){ | |
| 455 | + /* When reading from the command-prompt in Windows, it is necessary | |
| 456 | + ** to use _O_WTEXT input mode to read UTF-16 characters, then translate | |
| 457 | + ** that into UTF-8. Otherwise, non-ASCII characters all get translated | |
| 458 | + ** into '?'. | |
| 459 | + */ | |
| 460 | + wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) ); | |
| 461 | + if( b1==0 ) return 0; | |
| 462 | +#ifndef SQLITE_USE_STDIO_FOR_CONSOLE | |
| 463 | + DWORD nRead = 0; | |
| 464 | + if( IsConsole(in) | |
| 465 | + && ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0) | |
| 466 | + ){ | |
| 467 | + b1[nRead] = 0; | |
| 468 | + }else | |
| 469 | +#endif | |
| 470 | + { | |
| 471 | + _setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT); | |
| 472 | + if( fgetws(b1, sz/4, in)==0 ){ | |
| 473 | + sqlite3_free(b1); | |
| 474 | + return 0; | |
| 475 | + } | |
| 476 | + } | |
| 477 | + WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0); | |
| 478 | + sqlite3_free(b1); | |
| 479 | + return buf; | |
| 480 | + }else{ | |
| 481 | + /* Reading from a file or other input source, just read bytes without | |
| 482 | + ** any translation. */ | |
| 483 | + return fgets(buf, sz, in); | |
| 484 | + } | |
| 485 | +} | |
| 486 | + | |
| 487 | +/* | |
| 488 | +** Send ASCII text as O_BINARY. But for Unicode characters U+0080 and | |
| 489 | +** greater, switch to O_U8TEXT. | |
| 490 | +*/ | |
| 491 | +static void piecemealOutput(wchar_t *b1, int sz, FILE *out){ | |
| 492 | + int i; | |
| 493 | + wchar_t c; | |
| 494 | + while( sz>0 ){ | |
| 495 | + for(i=0; i<sz && b1[i]>=0x80; i++){} | |
| 496 | + if( i>0 ){ | |
| 497 | + c = b1[i]; | |
| 498 | + b1[i] = 0; | |
| 499 | + fflush(out); | |
| 500 | + _setmode(_fileno(out), _O_U8TEXT); | |
| 501 | + fputws(b1, out); | |
| 502 | + fflush(out); | |
| 503 | + b1 += i; | |
| 504 | + b1[0] = c; | |
| 505 | + sz -= i; | |
| 506 | + }else{ | |
| 507 | + fflush(out); | |
| 508 | + _setmode(_fileno(out), _O_TEXT); | |
| 509 | + _setmode(_fileno(out), _O_BINARY); | |
| 510 | + fwrite(&b1[0], 1, 1, out); | |
| 511 | + for(i=1; i<sz && b1[i]<0x80; i++){ | |
| 512 | + fwrite(&b1[i], 1, 1, out); | |
| 513 | + } | |
| 514 | + fflush(out); | |
| 515 | + _setmode(_fileno(out), _O_U8TEXT); | |
| 516 | + b1 += i; | |
| 517 | + sz -= i; | |
| 518 | + } | |
| 519 | + } | |
| 520 | +} | |
| 521 | + | |
| 522 | +/* | |
| 523 | +** Work-alike for fputs() from the standard C library. | |
| 524 | +*/ | |
| 525 | +int sqlite3_fputs(const char *z, FILE *out){ | |
| 526 | + if( !UseWtextForOutput(out) ){ | |
| 527 | + /* Writing to a file or other destination, just write bytes without | |
| 528 | + ** any translation. */ | |
| 529 | + return fputs(z, out); | |
| 530 | + }else{ | |
| 531 | + /* One must use UTF16 in order to get unicode support when writing | |
| 532 | + ** to the console on Windows. | |
| 533 | + */ | |
| 534 | + int sz = (int)strlen(z); | |
| 535 | + wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) ); | |
| 536 | + if( b1==0 ) return 0; | |
| 537 | + sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz); | |
| 538 | + b1[sz] = 0; | |
| 539 | + | |
| 540 | +#ifndef SQLITE_STDIO_FOR_CONSOLE | |
| 541 | + DWORD nWr = 0; | |
| 542 | + if( IsConsole(out) | |
| 543 | + && WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0) | |
| 544 | + ){ | |
| 545 | + /* If writing to the console, then the WriteConsoleW() is all we | |
| 546 | + ** need to do. */ | |
| 547 | + }else | |
| 548 | +#endif | |
| 549 | + { | |
| 550 | + /* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined | |
| 551 | + ** then write using the standard library. */ | |
| 552 | + _setmode(_fileno(out), _O_U8TEXT); | |
| 553 | + if( UseBinaryWText(out) ){ | |
| 554 | + piecemealOutput(b1, sz, out); | |
| 555 | + }else{ | |
| 556 | + fputws(b1, out); | |
| 557 | + } | |
| 558 | + } | |
| 559 | + sqlite3_free(b1); | |
| 560 | + return 0; | |
| 561 | + } | |
| 562 | +} | |
| 563 | + | |
| 564 | + | |
| 565 | +/* | |
| 566 | +** Work-alike for fprintf() from the standard C library. | |
| 567 | +*/ | |
| 568 | +int sqlite3_fprintf(FILE *out, const char *zFormat, ...){ | |
| 569 | + int rc; | |
| 570 | + if( UseWtextForOutput(out) ){ | |
| 571 | + /* When writing to the command-prompt in Windows, it is necessary | |
| 572 | + ** to use _O_WTEXT input mode and write UTF-16 characters. | |
| 573 | + */ | |
| 574 | + char *z; | |
| 575 | + va_list ap; | |
| 576 | + | |
| 577 | + va_start(ap, zFormat); | |
| 578 | + z = sqlite3_vmprintf(zFormat, ap); | |
| 579 | + va_end(ap); | |
| 580 | + sqlite3_fputs(z, out); | |
| 581 | + rc = (int)strlen(z); | |
| 582 | + sqlite3_free(z); | |
| 583 | + }else{ | |
| 584 | + /* Writing to a file or other destination, just write bytes without | |
| 585 | + ** any translation. */ | |
| 586 | + va_list ap; | |
| 587 | + va_start(ap, zFormat); | |
| 588 | + rc = vfprintf(out, zFormat, ap); | |
| 589 | + va_end(ap); | |
| 590 | + } | |
| 591 | + return rc; | |
| 592 | +} | |
| 593 | + | |
| 594 | +/* | |
| 595 | +** Set the mode for an output stream. mode argument is typically _O_BINARY or | |
| 596 | +** _O_TEXT. | |
| 597 | +*/ | |
| 598 | +void sqlite3_fsetmode(FILE *fp, int mode){ | |
| 599 | + if( !UseWtextForOutput(fp) ){ | |
| 600 | + fflush(fp); | |
| 601 | + _setmode(_fileno(fp), mode); | |
| 602 | + }else if( fp==stdout || fp==stderr ){ | |
| 603 | + simBinaryStdout = (mode==_O_BINARY); | |
| 604 | + }else{ | |
| 605 | + simBinaryOther = (mode==_O_BINARY); | |
| 606 | + } | |
| 607 | +} | |
| 608 | + | |
| 609 | +#endif /* defined(_WIN32) */ | |
| 610 | + | |
| 611 | +/************************* End ../ext/misc/sqlite3_stdio.c ********************/ | |
| 612 | + | |
| 258 | 613 | /* Use console I/O package as a direct INCLUDE. */ |
| 259 | 614 | #define SQLITE_INTERNAL_LINKAGE static |
| 260 | 615 | |
| 261 | 616 | #ifdef SQLITE_SHELL_FIDDLE |
| 262 | 617 | /* Deselect most features from the console I/O package for Fiddle. */ |
| @@ -264,1118 +619,13 @@ | ||
| 264 | 619 | # define SQLITE_CIO_NO_CLASSIFY |
| 265 | 620 | # define SQLITE_CIO_NO_TRANSLATE |
| 266 | 621 | # define SQLITE_CIO_NO_SETMODE |
| 267 | 622 | # define SQLITE_CIO_NO_FLUSH |
| 268 | 623 | #endif |
| 269 | -/************************* Begin ../ext/consio/console_io.h ******************/ | |
| 270 | -/* | |
| 271 | -** 2023 November 1 | |
| 272 | -** | |
| 273 | -** The author disclaims copyright to this source code. In place of | |
| 274 | -** a legal notice, here is a blessing: | |
| 275 | -** | |
| 276 | -** May you do good and not evil. | |
| 277 | -** May you find forgiveness for yourself and forgive others. | |
| 278 | -** May you share freely, never taking more than you give. | |
| 279 | -** | |
| 280 | -******************************************************************************** | |
| 281 | -** This file exposes various interfaces used for console and other I/O | |
| 282 | -** by the SQLite project command-line tools. These interfaces are used | |
| 283 | -** at either source conglomeration time, compilation time, or run time. | |
| 284 | -** This source provides for either inclusion into conglomerated, | |
| 285 | -** "single-source" forms or separate compilation then linking. | |
| 286 | -** | |
| 287 | -** Platform dependencies are "hidden" here by various stratagems so | |
| 288 | -** that, provided certain conditions are met, the programs using this | |
| 289 | -** source or object code compiled from it need no explicit conditional | |
| 290 | -** compilation in their source for their console and stream I/O. | |
| 291 | -** | |
| 292 | -** The symbols and functionality exposed here are not a public API. | |
| 293 | -** This code may change in tandem with other project code as needed. | |
| 294 | -** | |
| 295 | -** When this .h file and its companion .c are directly incorporated into | |
| 296 | -** a source conglomeration (such as shell.c), the preprocessor symbol | |
| 297 | -** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O | |
| 298 | -** translation for Windows is effected for the build. | |
| 299 | -*/ | |
| 300 | -#define HAVE_CONSOLE_IO_H 1 | |
| 301 | -#ifndef SQLITE_INTERNAL_LINKAGE | |
| 302 | -# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ | |
| 303 | -# include <stdio.h> | |
| 304 | -#else | |
| 305 | -# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ | |
| 306 | -#endif | |
| 307 | - | |
| 308 | -#ifndef SQLITE3_H | |
| 309 | -/* # include "sqlite3.h" */ | |
| 310 | -#endif | |
| 311 | - | |
| 312 | -#ifndef SQLITE_CIO_NO_CLASSIFY | |
| 313 | - | |
| 314 | -/* Define enum for use with following function. */ | |
| 315 | -typedef enum StreamsAreConsole { | |
| 316 | - SAC_NoConsole = 0, | |
| 317 | - SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4, | |
| 318 | - SAC_AnyConsole = 0x7 | |
| 319 | -} StreamsAreConsole; | |
| 320 | - | |
| 321 | -/* | |
| 322 | -** Classify the three standard I/O streams according to whether | |
| 323 | -** they are connected to a console attached to the process. | |
| 324 | -** | |
| 325 | -** Returns the bit-wise OR of SAC_{In,Out,Err}Console values, | |
| 326 | -** or SAC_NoConsole if none of the streams reaches a console. | |
| 327 | -** | |
| 328 | -** This function should be called before any I/O is done with | |
| 329 | -** the given streams. As a side-effect, the given inputs are | |
| 330 | -** recorded so that later I/O operations on them may be done | |
| 331 | -** differently than the C library FILE* I/O would be done, | |
| 332 | -** iff the stream is used for the I/O functions that follow, | |
| 333 | -** and to support the ones that use an implicit stream. | |
| 334 | -** | |
| 335 | -** On some platforms, stream or console mode alteration (aka | |
| 336 | -** "Setup") may be made which is undone by consoleRestore(). | |
| 337 | -*/ | |
| 338 | -SQLITE_INTERNAL_LINKAGE StreamsAreConsole | |
| 339 | -consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); | |
| 340 | -/* A usual call for convenience: */ | |
| 341 | -#define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr) | |
| 342 | - | |
| 343 | -/* | |
| 344 | -** After an initial call to consoleClassifySetup(...), renew | |
| 345 | -** the same setup it effected. (A call not after is an error.) | |
| 346 | -** This will restore state altered by consoleRestore(); | |
| 347 | -** | |
| 348 | -** Applications which run an inferior (child) process which | |
| 349 | -** inherits the same I/O streams may call this function after | |
| 350 | -** such a process exits to guard against console mode changes. | |
| 351 | -*/ | |
| 352 | -SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void); | |
| 353 | - | |
| 354 | -/* | |
| 355 | -** Undo any side-effects left by consoleClassifySetup(...). | |
| 356 | -** | |
| 357 | -** This should be called after consoleClassifySetup() and | |
| 358 | -** before the process terminates normally. It is suitable | |
| 359 | -** for use with the atexit() C library procedure. After | |
| 360 | -** this call, no console I/O should be done until one of | |
| 361 | -** console{Classify or Renew}Setup(...) is called again. | |
| 362 | -** | |
| 363 | -** Applications which run an inferior (child) process that | |
| 364 | -** inherits the same I/O streams might call this procedure | |
| 365 | -** before so that said process will have a console setup | |
| 366 | -** however users have configured it or come to expect. | |
| 367 | -*/ | |
| 368 | -SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); | |
| 369 | - | |
| 370 | -#else /* defined(SQLITE_CIO_NO_CLASSIFY) */ | |
| 371 | -# define consoleClassifySetup(i,o,e) | |
| 372 | -# define consoleRenewSetup() | |
| 373 | -# define consoleRestore() | |
| 374 | -#endif /* defined(SQLITE_CIO_NO_CLASSIFY) */ | |
| 375 | - | |
| 376 | -#ifndef SQLITE_CIO_NO_REDIRECT | |
| 377 | -/* | |
| 378 | -** Set stream to be used for the functions below which write | |
| 379 | -** to "the designated X stream", where X is Output or Error. | |
| 380 | -** Returns the previous value. | |
| 381 | -** | |
| 382 | -** Alternatively, pass the special value, invalidFileStream, | |
| 383 | -** to get the designated stream value without setting it. | |
| 384 | -** | |
| 385 | -** Before the designated streams are set, they default to | |
| 386 | -** those passed to consoleClassifySetup(...), and before | |
| 387 | -** that is called they default to stdout and stderr. | |
| 388 | -** | |
| 389 | -** It is error to close a stream so designated, then, without | |
| 390 | -** designating another, use the corresponding {o,e}Emit(...). | |
| 391 | -*/ | |
| 392 | -SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream; | |
| 393 | -SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf); | |
| 394 | -# ifdef CONSIO_SET_ERROR_STREAM | |
| 395 | -SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); | |
| 396 | -# endif | |
| 397 | -#else | |
| 398 | -# define setOutputStream(pf) | |
| 399 | -# define setErrorStream(pf) | |
| 400 | -#endif /* !defined(SQLITE_CIO_NO_REDIRECT) */ | |
| 401 | - | |
| 402 | -#ifndef SQLITE_CIO_NO_TRANSLATE | |
| 403 | -/* | |
| 404 | -** Emit output like fprintf(). If the output is going to the | |
| 405 | -** console and translation from UTF-8 is necessary, perform | |
| 406 | -** the needed translation. Otherwise, write formatted output | |
| 407 | -** to the provided stream almost as-is, possibly with newline | |
| 408 | -** translation as specified by set{Binary,Text}Mode(). | |
| 409 | -*/ | |
| 410 | -SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...); | |
| 411 | -/* Like fPrintfUtf8 except stream is always the designated output. */ | |
| 412 | -SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...); | |
| 413 | -/* Like fPrintfUtf8 except stream is always the designated error. */ | |
| 414 | -SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...); | |
| 415 | - | |
| 416 | -/* | |
| 417 | -** Emit output like fputs(). If the output is going to the | |
| 418 | -** console and translation from UTF-8 is necessary, perform | |
| 419 | -** the needed translation. Otherwise, write given text to the | |
| 420 | -** provided stream almost as-is, possibly with newline | |
| 421 | -** translation as specified by set{Binary,Text}Mode(). | |
| 422 | -*/ | |
| 423 | -SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO); | |
| 424 | -/* Like fPutsUtf8 except stream is always the designated output. */ | |
| 425 | -SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z); | |
| 426 | -/* Like fPutsUtf8 except stream is always the designated error. */ | |
| 427 | -SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); | |
| 428 | - | |
| 429 | -/* | |
| 430 | -** Emit output like fPutsUtf8(), except that the length of the | |
| 431 | -** accepted char or character sequence is limited by nAccept. | |
| 432 | -** | |
| 433 | -** Returns the number of accepted char values. | |
| 434 | -*/ | |
| 435 | -#ifdef CONSIO_SPUTB | |
| 436 | -SQLITE_INTERNAL_LINKAGE int | |
| 437 | -fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); | |
| 438 | -/* Like fPutbUtf8 except stream is always the designated output. */ | |
| 439 | -#endif | |
| 440 | -SQLITE_INTERNAL_LINKAGE int | |
| 441 | -oPutbUtf8(const char *cBuf, int nAccept); | |
| 442 | -/* Like fPutbUtf8 except stream is always the designated error. */ | |
| 443 | -#ifdef CONSIO_EPUTB | |
| 444 | -SQLITE_INTERNAL_LINKAGE int | |
| 445 | -ePutbUtf8(const char *cBuf, int nAccept); | |
| 446 | -#endif | |
| 447 | - | |
| 448 | -/* | |
| 449 | -** Flush the given output stream. Return non-zero for success, else 0. | |
| 450 | -*/ | |
| 451 | -#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE) | |
| 452 | -SQLITE_INTERNAL_LINKAGE int | |
| 453 | -fFlushBuffer(FILE *pfOut); | |
| 454 | -#endif | |
| 455 | - | |
| 456 | -/* | |
| 457 | -** Collect input like fgets(...) with special provisions for input | |
| 458 | -** from the console on such platforms as require same. Newline | |
| 459 | -** translation may be done as set by set{Binary,Text}Mode(). | |
| 460 | -** As a convenience, pfIn==NULL is treated as stdin. | |
| 461 | -*/ | |
| 462 | -SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn); | |
| 463 | -/* Like fGetsUtf8 except stream is always the designated input. */ | |
| 464 | -/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ | |
| 465 | - | |
| 466 | -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ | |
| 467 | - | |
| 468 | -#ifndef SQLITE_CIO_NO_SETMODE | |
| 469 | -/* | |
| 470 | -** Set given stream for binary mode, where newline translation is | |
| 471 | -** not done, or for text mode where, for some platforms, newlines | |
| 472 | -** are translated to the platform's conventional char sequence. | |
| 473 | -** If bFlush true, flush the stream. | |
| 474 | -** | |
| 475 | -** An additional side-effect is that if the stream is one passed | |
| 476 | -** to consoleClassifySetup() as an output, it is flushed first. | |
| 477 | -** | |
| 478 | -** Note that binary/text mode has no effect on console I/O | |
| 479 | -** translation. On all platforms, newline to the console starts | |
| 480 | -** a new line and CR,LF chars from the console become a newline. | |
| 481 | -*/ | |
| 482 | -SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush); | |
| 483 | -SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush); | |
| 484 | -#endif | |
| 485 | - | |
| 486 | -#ifdef SQLITE_CIO_PROMPTED_IN | |
| 487 | -typedef struct Prompts { | |
| 488 | - int numPrompts; | |
| 489 | - const char **azPrompts; | |
| 490 | -} Prompts; | |
| 491 | - | |
| 492 | -/* | |
| 493 | -** Macros for use of a line editor. | |
| 494 | -** | |
| 495 | -** The following macros define operations involving use of a | |
| 496 | -** line-editing library or simple console interaction. | |
| 497 | -** A "T" argument is a text (char *) buffer or filename. | |
| 498 | -** A "N" argument is an integer. | |
| 499 | -** | |
| 500 | -** SHELL_ADD_HISTORY(T) // Record text as line(s) of history. | |
| 501 | -** SHELL_READ_HISTORY(T) // Read history from file named by T. | |
| 502 | -** SHELL_WRITE_HISTORY(T) // Write history to file named by T. | |
| 503 | -** SHELL_STIFLE_HISTORY(N) // Limit history to N entries. | |
| 504 | -** | |
| 505 | -** A console program which does interactive console input is | |
| 506 | -** expected to call: | |
| 507 | -** SHELL_READ_HISTORY(T) before collecting such input; | |
| 508 | -** SHELL_ADD_HISTORY(T) as record-worthy input is taken; | |
| 509 | -** SHELL_STIFLE_HISTORY(N) after console input ceases; then | |
| 510 | -** SHELL_WRITE_HISTORY(T) before the program exits. | |
| 511 | -*/ | |
| 512 | - | |
| 513 | -/* | |
| 514 | -** Retrieve a single line of input text from an input stream. | |
| 515 | -** | |
| 516 | -** If pfIn is the input stream passed to consoleClassifySetup(), | |
| 517 | -** and azPrompt is not NULL, then a prompt is issued before the | |
| 518 | -** line is collected, as selected by the isContinuation flag. | |
| 519 | -** Array azPrompt[{0,1}] holds the {main,continuation} prompt. | |
| 520 | -** | |
| 521 | -** If zBufPrior is not NULL then it is a buffer from a prior | |
| 522 | -** call to this routine that can be reused, or will be freed. | |
| 523 | -** | |
| 524 | -** The result is stored in space obtained from malloc() and | |
| 525 | -** must either be freed by the caller or else passed back to | |
| 526 | -** this function as zBufPrior for reuse. | |
| 527 | -** | |
| 528 | -** This function may call upon services of a line-editing | |
| 529 | -** library to interactively collect line edited input. | |
| 530 | -*/ | |
| 531 | -SQLITE_INTERNAL_LINKAGE char * | |
| 532 | -shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, | |
| 533 | - short isContinuation, Prompts azPrompt); | |
| 534 | -#endif /* defined(SQLITE_CIO_PROMPTED_IN) */ | |
| 535 | -/* | |
| 536 | -** TBD: Define an interface for application(s) to generate | |
| 537 | -** completion candidates for use by the line-editor. | |
| 538 | -** | |
| 539 | -** This may be premature; the CLI is the only application | |
| 540 | -** that does this. Yet, getting line-editing melded into | |
| 541 | -** console I/O is desirable because a line-editing library | |
| 542 | -** may have to establish console operating mode, possibly | |
| 543 | -** in a way that interferes with the above functionality. | |
| 544 | -*/ | |
| 545 | - | |
| 546 | -#if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE)) | |
| 547 | -/* Skip over as much z[] input char sequence as is valid UTF-8, | |
| 548 | -** limited per nAccept char's or whole characters and containing | |
| 549 | -** no char cn such that ((1<<cn) & ccm)!=0. On return, the | |
| 550 | -** sequence z:return (inclusive:exclusive) is validated UTF-8. | |
| 551 | -** Limit: nAccept>=0 => char count, nAccept<0 => character | |
| 552 | - */ | |
| 553 | -SQLITE_INTERNAL_LINKAGE const char* | |
| 554 | -zSkipValidUtf8(const char *z, int nAccept, long ccm); | |
| 555 | - | |
| 556 | -#endif | |
| 557 | - | |
| 558 | -/************************* End ../ext/consio/console_io.h ********************/ | |
| 559 | -/************************* Begin ../ext/consio/console_io.c ******************/ | |
| 560 | -/* | |
| 561 | -** 2023 November 4 | |
| 562 | -** | |
| 563 | -** The author disclaims copyright to this source code. In place of | |
| 564 | -** a legal notice, here is a blessing: | |
| 565 | -** | |
| 566 | -** May you do good and not evil. | |
| 567 | -** May you find forgiveness for yourself and forgive others. | |
| 568 | -** May you share freely, never taking more than you give. | |
| 569 | -** | |
| 570 | -******************************************************************************** | |
| 571 | -** This file implements various interfaces used for console and stream I/O | |
| 572 | -** by the SQLite project command-line tools, as explained in console_io.h . | |
| 573 | -** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there. | |
| 574 | -*/ | |
| 575 | - | |
| 576 | -#ifndef SQLITE_CDECL | |
| 577 | -# define SQLITE_CDECL | |
| 578 | -#endif | |
| 579 | - | |
| 580 | -#ifndef SHELL_NO_SYSINC | |
| 581 | -# include <stdarg.h> | |
| 582 | -# include <string.h> | |
| 583 | -# include <stdlib.h> | |
| 584 | -# include <limits.h> | |
| 585 | -# include <assert.h> | |
| 586 | -/* # include "sqlite3.h" */ | |
| 587 | -#endif | |
| 588 | -#ifndef HAVE_CONSOLE_IO_H | |
| 589 | -# include "console_io.h" | |
| 590 | -#endif | |
| 591 | -#if defined(_MSC_VER) | |
| 592 | -# pragma warning(disable : 4204) | |
| 593 | -#endif | |
| 594 | - | |
| 595 | -#ifndef SQLITE_CIO_NO_TRANSLATE | |
| 596 | -# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT | |
| 597 | -# ifndef SHELL_NO_SYSINC | |
| 598 | -# include <io.h> | |
| 599 | -# include <fcntl.h> | |
| 600 | -# undef WIN32_LEAN_AND_MEAN | |
| 601 | -# define WIN32_LEAN_AND_MEAN | |
| 602 | -# include <windows.h> | |
| 603 | -# endif | |
| 604 | -# define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */ | |
| 605 | -# else | |
| 606 | -# ifndef SHELL_NO_SYSINC | |
| 607 | -# include <unistd.h> | |
| 608 | -# endif | |
| 609 | -# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ | |
| 610 | -# endif | |
| 611 | -#else | |
| 612 | -# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ | |
| 613 | -#endif | |
| 614 | - | |
| 615 | -#if CIO_WIN_WC_XLATE | |
| 616 | -static HANDLE handleOfFile(FILE *pf){ | |
| 617 | - int fileDesc = _fileno(pf); | |
| 618 | - union { intptr_t osfh; HANDLE fh; } fid = { | |
| 619 | - (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE | |
| 620 | - }; | |
| 621 | - return fid.fh; | |
| 622 | -} | |
| 623 | -#endif | |
| 624 | - | |
| 625 | -#ifndef SQLITE_CIO_NO_TRANSLATE | |
| 626 | -typedef struct PerStreamTags { | |
| 627 | -# if CIO_WIN_WC_XLATE | |
| 628 | - HANDLE hx; | |
| 629 | - DWORD consMode; | |
| 630 | - char acIncomplete[4]; | |
| 631 | -# else | |
| 632 | - short reachesConsole; | |
| 633 | -# endif | |
| 634 | - FILE *pf; | |
| 635 | -} PerStreamTags; | |
| 636 | - | |
| 637 | -/* Define NULL-like value for things which can validly be 0. */ | |
| 638 | -# define SHELL_INVALID_FILE_PTR ((FILE *)~0) | |
| 639 | -# if CIO_WIN_WC_XLATE | |
| 640 | -# define SHELL_INVALID_CONS_MODE 0xFFFF0000 | |
| 641 | -# endif | |
| 642 | - | |
| 643 | -# if CIO_WIN_WC_XLATE | |
| 644 | -# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ | |
| 645 | - {0,0,0,0}, SHELL_INVALID_FILE_PTR } | |
| 646 | -# else | |
| 647 | -# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } | |
| 648 | -# endif | |
| 649 | - | |
| 650 | -/* Quickly say whether a known output is going to the console. */ | |
| 651 | -# if CIO_WIN_WC_XLATE | |
| 652 | -static short pstReachesConsole(PerStreamTags *ppst){ | |
| 653 | - return (ppst->hx != INVALID_HANDLE_VALUE); | |
| 654 | -} | |
| 655 | -# else | |
| 656 | -# define pstReachesConsole(ppst) 0 | |
| 657 | -# endif | |
| 658 | - | |
| 659 | -# if CIO_WIN_WC_XLATE | |
| 660 | -static void restoreConsoleArb(PerStreamTags *ppst){ | |
| 661 | - if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode); | |
| 662 | -} | |
| 663 | -# else | |
| 664 | -# define restoreConsoleArb(ppst) | |
| 665 | -# endif | |
| 666 | - | |
| 667 | -/* Say whether FILE* appears to be a console, collect associated info. */ | |
| 668 | -static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){ | |
| 669 | -# if CIO_WIN_WC_XLATE | |
| 670 | - short rv = 0; | |
| 671 | - DWORD dwCM = SHELL_INVALID_CONS_MODE; | |
| 672 | - HANDLE fh = handleOfFile(pf); | |
| 673 | - ppst->pf = pf; | |
| 674 | - if( INVALID_HANDLE_VALUE != fh ){ | |
| 675 | - rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM)); | |
| 676 | - } | |
| 677 | - ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE; | |
| 678 | - ppst->consMode = dwCM; | |
| 679 | - return rv; | |
| 680 | -# else | |
| 681 | - ppst->pf = pf; | |
| 682 | - ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); | |
| 683 | - return ppst->reachesConsole; | |
| 684 | -# endif | |
| 685 | -} | |
| 686 | - | |
| 687 | -# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING | |
| 688 | -# define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4) | |
| 689 | -# endif | |
| 690 | - | |
| 691 | -# if CIO_WIN_WC_XLATE | |
| 692 | -/* Define console modes for use with the Windows Console API. */ | |
| 693 | -# define SHELL_CONI_MODE \ | |
| 694 | - (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ | |
| 695 | - | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) | |
| 696 | -# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ | |
| 697 | - | ENABLE_VIRTUAL_TERMINAL_PROCESSING) | |
| 698 | -# endif | |
| 699 | - | |
| 700 | -typedef struct ConsoleInfo { | |
| 701 | - PerStreamTags pstSetup[3]; | |
| 702 | - PerStreamTags pstDesignated[3]; | |
| 703 | - StreamsAreConsole sacSetup; | |
| 704 | -} ConsoleInfo; | |
| 705 | - | |
| 706 | -static short isValidStreamInfo(PerStreamTags *ppst){ | |
| 707 | - return (ppst->pf != SHELL_INVALID_FILE_PTR); | |
| 708 | -} | |
| 709 | - | |
| 710 | -static ConsoleInfo consoleInfo = { | |
| 711 | - { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, | |
| 712 | - { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, | |
| 713 | - SAC_NoConsole /* sacSetup */ | |
| 714 | -}; | |
| 715 | - | |
| 716 | -SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; | |
| 717 | - | |
| 718 | -# if CIO_WIN_WC_XLATE | |
| 719 | -static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ | |
| 720 | - if( pstReachesConsole(ppst) ){ | |
| 721 | - DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; | |
| 722 | - SetConsoleMode(ppst->hx, cm); | |
| 723 | - } | |
| 724 | -} | |
| 725 | -# else | |
| 726 | -# define maybeSetupAsConsole(ppst,odir) | |
| 727 | -# endif | |
| 728 | - | |
| 729 | -SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ | |
| 730 | -# if CIO_WIN_WC_XLATE | |
| 731 | - int ix = 0; | |
| 732 | - while( ix < 6 ){ | |
| 733 | - PerStreamTags *ppst = (ix<3)? | |
| 734 | - &consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3]; | |
| 735 | - maybeSetupAsConsole(ppst, (ix % 3)>0); | |
| 736 | - ++ix; | |
| 737 | - } | |
| 738 | -# endif | |
| 739 | -} | |
| 740 | - | |
| 741 | -SQLITE_INTERNAL_LINKAGE StreamsAreConsole | |
| 742 | -consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ | |
| 743 | - StreamsAreConsole rv = SAC_NoConsole; | |
| 744 | - FILE* apf[3] = { pfIn, pfOut, pfErr }; | |
| 745 | - int ix; | |
| 746 | - for( ix = 2; ix >= 0; --ix ){ | |
| 747 | - PerStreamTags *ppst = &consoleInfo.pstSetup[ix]; | |
| 748 | - if( streamOfConsole(apf[ix], ppst) ){ | |
| 749 | - rv |= (SAC_InConsole<<ix); | |
| 750 | - } | |
| 751 | - consoleInfo.pstDesignated[ix] = *ppst; | |
| 752 | - if( ix > 0 ) fflush(apf[ix]); | |
| 753 | - } | |
| 754 | - consoleInfo.sacSetup = rv; | |
| 755 | - consoleRenewSetup(); | |
| 756 | - return rv; | |
| 757 | -} | |
| 758 | - | |
| 759 | -SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ | |
| 760 | -# if CIO_WIN_WC_XLATE | |
| 761 | - static ConsoleInfo *pci = &consoleInfo; | |
| 762 | - if( pci->sacSetup ){ | |
| 763 | - int ix; | |
| 764 | - for( ix=0; ix<3; ++ix ){ | |
| 765 | - if( pci->sacSetup & (SAC_InConsole<<ix) ){ | |
| 766 | - PerStreamTags *ppst = &pci->pstSetup[ix]; | |
| 767 | - SetConsoleMode(ppst->hx, ppst->consMode); | |
| 768 | - } | |
| 769 | - } | |
| 770 | - } | |
| 771 | -# endif | |
| 772 | -} | |
| 773 | -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ | |
| 774 | - | |
| 775 | -#ifdef SQLITE_CIO_INPUT_REDIR | |
| 776 | -/* Say whether given FILE* is among those known, via either | |
| 777 | -** consoleClassifySetup() or set{Output,Error}Stream, as | |
| 778 | -** readable, and return an associated PerStreamTags pointer | |
| 779 | -** if so. Otherwise, return 0. | |
| 780 | -*/ | |
| 781 | -static PerStreamTags * isKnownReadable(FILE *pf){ | |
| 782 | - static PerStreamTags *apst[] = { | |
| 783 | - &consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0 | |
| 784 | - }; | |
| 785 | - int ix = 0; | |
| 786 | - do { | |
| 787 | - if( apst[ix]->pf == pf ) break; | |
| 788 | - } while( apst[++ix] != 0 ); | |
| 789 | - return apst[ix]; | |
| 790 | -} | |
| 791 | -#endif | |
| 792 | - | |
| 793 | -#ifndef SQLITE_CIO_NO_TRANSLATE | |
| 794 | -/* Say whether given FILE* is among those known, via either | |
| 795 | -** consoleClassifySetup() or set{Output,Error}Stream, as | |
| 796 | -** writable, and return an associated PerStreamTags pointer | |
| 797 | -** if so. Otherwise, return 0. | |
| 798 | -*/ | |
| 799 | -static PerStreamTags * isKnownWritable(FILE *pf){ | |
| 800 | - static PerStreamTags *apst[] = { | |
| 801 | - &consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2], | |
| 802 | - &consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0 | |
| 803 | - }; | |
| 804 | - int ix = 0; | |
| 805 | - do { | |
| 806 | - if( apst[ix]->pf == pf ) break; | |
| 807 | - } while( apst[++ix] != 0 ); | |
| 808 | - return apst[ix]; | |
| 809 | -} | |
| 810 | - | |
| 811 | -static FILE *designateEmitStream(FILE *pf, unsigned chix){ | |
| 812 | - FILE *rv = consoleInfo.pstDesignated[chix].pf; | |
| 813 | - if( pf == invalidFileStream ) return rv; | |
| 814 | - else{ | |
| 815 | - /* Setting a possibly new output stream. */ | |
| 816 | - PerStreamTags *ppst = isKnownWritable(pf); | |
| 817 | - if( ppst != 0 ){ | |
| 818 | - PerStreamTags pst = *ppst; | |
| 819 | - consoleInfo.pstDesignated[chix] = pst; | |
| 820 | - }else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]); | |
| 821 | - } | |
| 822 | - return rv; | |
| 823 | -} | |
| 824 | - | |
| 825 | -SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){ | |
| 826 | - return designateEmitStream(pf, 1); | |
| 827 | -} | |
| 828 | -# ifdef CONSIO_SET_ERROR_STREAM | |
| 829 | -SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ | |
| 830 | - return designateEmitStream(pf, 2); | |
| 831 | -} | |
| 832 | -# endif | |
| 833 | -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ | |
| 834 | - | |
| 835 | -#ifndef SQLITE_CIO_NO_SETMODE | |
| 836 | -# if CIO_WIN_WC_XLATE | |
| 837 | -static void setModeFlushQ(FILE *pf, short bFlush, int mode){ | |
| 838 | - if( bFlush ) fflush(pf); | |
| 839 | - _setmode(_fileno(pf), mode); | |
| 840 | -} | |
| 841 | -# else | |
| 842 | -# define setModeFlushQ(f, b, m) if(b) fflush(f) | |
| 843 | -# endif | |
| 844 | - | |
| 845 | -SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ | |
| 846 | - setModeFlushQ(pf, bFlush, _O_BINARY); | |
| 847 | -} | |
| 848 | -SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ | |
| 849 | - setModeFlushQ(pf, bFlush, _O_TEXT); | |
| 850 | -} | |
| 851 | -# undef setModeFlushQ | |
| 852 | - | |
| 853 | -#else /* defined(SQLITE_CIO_NO_SETMODE) */ | |
| 854 | -# define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) | |
| 855 | -# define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) | |
| 856 | -#endif /* defined(SQLITE_CIO_NO_SETMODE) */ | |
| 857 | - | |
| 858 | -#ifndef SQLITE_CIO_NO_TRANSLATE | |
| 859 | -# if CIO_WIN_WC_XLATE | |
| 860 | -/* Write buffer cBuf as output to stream known to reach console, | |
| 861 | -** limited to ncTake char's. Return ncTake on success, else 0. */ | |
| 862 | -static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ | |
| 863 | - int rv = 0; | |
| 864 | - if( z!=NULL ){ | |
| 865 | - int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0); | |
| 866 | - if( nwc > 0 ){ | |
| 867 | - WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); | |
| 868 | - if( zw!=NULL ){ | |
| 869 | - nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc); | |
| 870 | - if( nwc > 0 ){ | |
| 871 | - /* Translation from UTF-8 to UTF-16, then WCHARs out. */ | |
| 872 | - if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ | |
| 873 | - rv = ncTake; | |
| 874 | - } | |
| 875 | - } | |
| 876 | - sqlite3_free(zw); | |
| 877 | - } | |
| 878 | - } | |
| 879 | - } | |
| 880 | - return rv; | |
| 881 | -} | |
| 882 | - | |
| 883 | -/* For {f,o,e}PrintfUtf8() when stream is known to reach console. */ | |
| 884 | -static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ | |
| 885 | - char *z = sqlite3_vmprintf(zFormat, ap); | |
| 886 | - if( z ){ | |
| 887 | - int rv = conZstrEmit(ppst, z, (int)strlen(z)); | |
| 888 | - sqlite3_free(z); | |
| 889 | - return rv; | |
| 890 | - }else return 0; | |
| 891 | -} | |
| 892 | -# endif /* CIO_WIN_WC_XLATE */ | |
| 893 | - | |
| 894 | -# ifdef CONSIO_GET_EMIT_STREAM | |
| 895 | -static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, | |
| 896 | - PerStreamTags *ppst){ | |
| 897 | - PerStreamTags *rv = isKnownWritable(pf); | |
| 898 | - short isValid = (rv!=0)? isValidStreamInfo(rv) : 0; | |
| 899 | - if( rv != 0 && isValid ) return rv; | |
| 900 | - streamOfConsole(pf, ppst); | |
| 901 | - return ppst; | |
| 902 | -} | |
| 903 | -# endif | |
| 904 | - | |
| 905 | -/* Get stream info, either for designated output or error stream when | |
| 906 | -** chix equals 1 or 2, or for an arbitrary stream when chix == 0. | |
| 907 | -** In either case, ppst references a caller-owned PerStreamTags | |
| 908 | -** struct which may be filled in if none of the known writable | |
| 909 | -** streams is being held by consoleInfo. The ppf parameter is a | |
| 910 | -** byref output when chix!=0 and a byref input when chix==0. | |
| 911 | - */ | |
| 912 | -static PerStreamTags * | |
| 913 | -getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, | |
| 914 | - /* in/out */ FILE **ppf){ | |
| 915 | - PerStreamTags *ppstTry; | |
| 916 | - FILE *pfEmit; | |
| 917 | - if( chix > 0 ){ | |
| 918 | - ppstTry = &consoleInfo.pstDesignated[chix]; | |
| 919 | - if( !isValidStreamInfo(ppstTry) ){ | |
| 920 | - ppstTry = &consoleInfo.pstSetup[chix]; | |
| 921 | - pfEmit = ppst->pf; | |
| 922 | - }else pfEmit = ppstTry->pf; | |
| 923 | - if( !isValidStreamInfo(ppstTry) ){ | |
| 924 | - pfEmit = (chix > 1)? stderr : stdout; | |
| 925 | - ppstTry = ppst; | |
| 926 | - streamOfConsole(pfEmit, ppstTry); | |
| 927 | - } | |
| 928 | - *ppf = pfEmit; | |
| 929 | - }else{ | |
| 930 | - ppstTry = isKnownWritable(*ppf); | |
| 931 | - if( ppstTry != 0 ) return ppstTry; | |
| 932 | - streamOfConsole(*ppf, ppst); | |
| 933 | - return ppst; | |
| 934 | - } | |
| 935 | - return ppstTry; | |
| 936 | -} | |
| 937 | - | |
| 938 | -SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){ | |
| 939 | - va_list ap; | |
| 940 | - int rv; | |
| 941 | - FILE *pfOut; | |
| 942 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 943 | -# if CIO_WIN_WC_XLATE | |
| 944 | - PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); | |
| 945 | -# else | |
| 946 | - getEmitStreamInfo(1, &pst, &pfOut); | |
| 947 | -# endif | |
| 948 | - assert(zFormat!=0); | |
| 949 | - va_start(ap, zFormat); | |
| 950 | -# if CIO_WIN_WC_XLATE | |
| 951 | - if( pstReachesConsole(ppst) ){ | |
| 952 | - rv = conioVmPrintf(ppst, zFormat, ap); | |
| 953 | - }else{ | |
| 954 | -# endif | |
| 955 | - rv = vfprintf(pfOut, zFormat, ap); | |
| 956 | -# if CIO_WIN_WC_XLATE | |
| 957 | - } | |
| 958 | -# endif | |
| 959 | - va_end(ap); | |
| 960 | - return rv; | |
| 961 | -} | |
| 962 | - | |
| 963 | -SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ | |
| 964 | - va_list ap; | |
| 965 | - int rv; | |
| 966 | - FILE *pfErr; | |
| 967 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 968 | -# if CIO_WIN_WC_XLATE | |
| 969 | - PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); | |
| 970 | -# else | |
| 971 | - getEmitStreamInfo(2, &pst, &pfErr); | |
| 972 | -# endif | |
| 973 | - assert(zFormat!=0); | |
| 974 | - va_start(ap, zFormat); | |
| 975 | -# if CIO_WIN_WC_XLATE | |
| 976 | - if( pstReachesConsole(ppst) ){ | |
| 977 | - rv = conioVmPrintf(ppst, zFormat, ap); | |
| 978 | - }else{ | |
| 979 | -# endif | |
| 980 | - rv = vfprintf(pfErr, zFormat, ap); | |
| 981 | -# if CIO_WIN_WC_XLATE | |
| 982 | - } | |
| 983 | -# endif | |
| 984 | - va_end(ap); | |
| 985 | - return rv; | |
| 986 | -} | |
| 987 | - | |
| 988 | -SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ | |
| 989 | - va_list ap; | |
| 990 | - int rv; | |
| 991 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 992 | -# if CIO_WIN_WC_XLATE | |
| 993 | - PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); | |
| 994 | -# else | |
| 995 | - getEmitStreamInfo(0, &pst, &pfO); | |
| 996 | -# endif | |
| 997 | - assert(zFormat!=0); | |
| 998 | - va_start(ap, zFormat); | |
| 999 | -# if CIO_WIN_WC_XLATE | |
| 1000 | - if( pstReachesConsole(ppst) ){ | |
| 1001 | - maybeSetupAsConsole(ppst, 1); | |
| 1002 | - rv = conioVmPrintf(ppst, zFormat, ap); | |
| 1003 | - if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); | |
| 1004 | - }else{ | |
| 1005 | -# endif | |
| 1006 | - rv = vfprintf(pfO, zFormat, ap); | |
| 1007 | -# if CIO_WIN_WC_XLATE | |
| 1008 | - } | |
| 1009 | -# endif | |
| 1010 | - va_end(ap); | |
| 1011 | - return rv; | |
| 1012 | -} | |
| 1013 | - | |
| 1014 | -SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ | |
| 1015 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 1016 | -# if CIO_WIN_WC_XLATE | |
| 1017 | - PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); | |
| 1018 | -# else | |
| 1019 | - getEmitStreamInfo(0, &pst, &pfO); | |
| 1020 | -# endif | |
| 1021 | - assert(z!=0); | |
| 1022 | -# if CIO_WIN_WC_XLATE | |
| 1023 | - if( pstReachesConsole(ppst) ){ | |
| 1024 | - int rv; | |
| 1025 | - maybeSetupAsConsole(ppst, 1); | |
| 1026 | - rv = conZstrEmit(ppst, z, (int)strlen(z)); | |
| 1027 | - if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); | |
| 1028 | - return rv; | |
| 1029 | - }else { | |
| 1030 | -# endif | |
| 1031 | - return (fputs(z, pfO)<0)? 0 : (int)strlen(z); | |
| 1032 | -# if CIO_WIN_WC_XLATE | |
| 1033 | - } | |
| 1034 | -# endif | |
| 1035 | -} | |
| 1036 | - | |
| 1037 | -SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ | |
| 1038 | - FILE *pfErr; | |
| 1039 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 1040 | -# if CIO_WIN_WC_XLATE | |
| 1041 | - PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); | |
| 1042 | -# else | |
| 1043 | - getEmitStreamInfo(2, &pst, &pfErr); | |
| 1044 | -# endif | |
| 1045 | - assert(z!=0); | |
| 1046 | -# if CIO_WIN_WC_XLATE | |
| 1047 | - if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); | |
| 1048 | - else { | |
| 1049 | -# endif | |
| 1050 | - return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); | |
| 1051 | -# if CIO_WIN_WC_XLATE | |
| 1052 | - } | |
| 1053 | -# endif | |
| 1054 | -} | |
| 1055 | - | |
| 1056 | -SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ | |
| 1057 | - FILE *pfOut; | |
| 1058 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 1059 | -# if CIO_WIN_WC_XLATE | |
| 1060 | - PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); | |
| 1061 | -# else | |
| 1062 | - getEmitStreamInfo(1, &pst, &pfOut); | |
| 1063 | -# endif | |
| 1064 | - assert(z!=0); | |
| 1065 | -# if CIO_WIN_WC_XLATE | |
| 1066 | - if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); | |
| 1067 | - else { | |
| 1068 | -# endif | |
| 1069 | - return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); | |
| 1070 | -# if CIO_WIN_WC_XLATE | |
| 1071 | - } | |
| 1072 | -# endif | |
| 1073 | -} | |
| 1074 | - | |
| 1075 | -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ | |
| 1076 | - | |
| 1077 | -#if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE)) | |
| 1078 | -/* Skip over as much z[] input char sequence as is valid UTF-8, | |
| 1079 | -** limited per nAccept char's or whole characters and containing | |
| 1080 | -** no char cn such that ((1<<cn) & ccm)!=0. On return, the | |
| 1081 | -** sequence z:return (inclusive:exclusive) is validated UTF-8. | |
| 1082 | -** Limit: nAccept>=0 => char count, nAccept<0 => character | |
| 1083 | - */ | |
| 1084 | -SQLITE_INTERNAL_LINKAGE const char* | |
| 1085 | -zSkipValidUtf8(const char *z, int nAccept, long ccm){ | |
| 1086 | - int ng = (nAccept<0)? -nAccept : 0; | |
| 1087 | - const char *pcLimit = (nAccept>=0)? z+nAccept : 0; | |
| 1088 | - assert(z!=0); | |
| 1089 | - while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){ | |
| 1090 | - char c = *z; | |
| 1091 | - if( (c & 0x80) == 0 ){ | |
| 1092 | - if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z; | |
| 1093 | - ++z; /* ASCII */ | |
| 1094 | - }else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */ | |
| 1095 | - else{ | |
| 1096 | - const char *zt = z+1; /* Got lead byte, look at trail bytes.*/ | |
| 1097 | - do{ | |
| 1098 | - if( pcLimit && zt >= pcLimit ) return z; | |
| 1099 | - else{ | |
| 1100 | - char ct = *zt++; | |
| 1101 | - if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ | |
| 1102 | - /* Trailing bytes are too few, too many, or invalid. */ | |
| 1103 | - return z; | |
| 1104 | - } | |
| 1105 | - } | |
| 1106 | - } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ | |
| 1107 | - z = zt; | |
| 1108 | - } | |
| 1109 | - } | |
| 1110 | - return z; | |
| 1111 | -} | |
| 1112 | -#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ | |
| 1113 | - | |
| 1114 | -#ifndef SQLITE_CIO_NO_TRANSLATE | |
| 1115 | -# ifdef CONSIO_SPUTB | |
| 1116 | -SQLITE_INTERNAL_LINKAGE int | |
| 1117 | -fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ | |
| 1118 | - assert(pfO!=0); | |
| 1119 | -# if CIO_WIN_WC_XLATE | |
| 1120 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 1121 | - PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); | |
| 1122 | - if( pstReachesConsole(ppst) ){ | |
| 1123 | - int rv; | |
| 1124 | - maybeSetupAsConsole(ppst, 1); | |
| 1125 | - rv = conZstrEmit(ppst, cBuf, nAccept); | |
| 1126 | - if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); | |
| 1127 | - return rv; | |
| 1128 | - }else { | |
| 1129 | -# endif | |
| 1130 | - return (int)fwrite(cBuf, 1, nAccept, pfO); | |
| 1131 | -# if CIO_WIN_WC_XLATE | |
| 1132 | - } | |
| 1133 | -# endif | |
| 1134 | -} | |
| 1135 | -# endif | |
| 1136 | - | |
| 1137 | -SQLITE_INTERNAL_LINKAGE int | |
| 1138 | -oPutbUtf8(const char *cBuf, int nAccept){ | |
| 1139 | - FILE *pfOut; | |
| 1140 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 1141 | -# if CIO_WIN_WC_XLATE | |
| 1142 | - PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); | |
| 1143 | -# else | |
| 1144 | - getEmitStreamInfo(1, &pst, &pfOut); | |
| 1145 | -# endif | |
| 1146 | -# if CIO_WIN_WC_XLATE | |
| 1147 | - if( pstReachesConsole(ppst) ){ | |
| 1148 | - return conZstrEmit(ppst, cBuf, nAccept); | |
| 1149 | - }else { | |
| 1150 | -# endif | |
| 1151 | - return (int)fwrite(cBuf, 1, nAccept, pfOut); | |
| 1152 | -# if CIO_WIN_WC_XLATE | |
| 1153 | - } | |
| 1154 | -# endif | |
| 1155 | -} | |
| 1156 | - | |
| 1157 | -/* | |
| 1158 | -** Flush the given output stream. Return non-zero for success, else 0. | |
| 1159 | -*/ | |
| 1160 | -#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE) | |
| 1161 | -SQLITE_INTERNAL_LINKAGE int | |
| 1162 | -fFlushBuffer(FILE *pfOut){ | |
| 1163 | -# if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE) | |
| 1164 | - return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0; | |
| 1165 | -# else | |
| 1166 | - return fflush(pfOut); | |
| 1167 | -# endif | |
| 1168 | -} | |
| 1169 | -#endif | |
| 1170 | - | |
| 1171 | -#if CIO_WIN_WC_XLATE \ | |
| 1172 | - && !defined(SHELL_OMIT_FIO_DUPE) \ | |
| 1173 | - && defined(SQLITE_USE_ONLY_WIN32) | |
| 1174 | -static struct FileAltIds { | |
| 1175 | - int fd; | |
| 1176 | - HANDLE fh; | |
| 1177 | -} altIdsOfFile(FILE *pf){ | |
| 1178 | - struct FileAltIds rv = { _fileno(pf) }; | |
| 1179 | - union { intptr_t osfh; HANDLE fh; } fid = { | |
| 1180 | - (rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE | |
| 1181 | - }; | |
| 1182 | - rv.fh = fid.fh; | |
| 1183 | - return rv; | |
| 1184 | -} | |
| 1185 | - | |
| 1186 | -SQLITE_INTERNAL_LINKAGE size_t | |
| 1187 | -cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){ | |
| 1188 | - size_t rv = 0; | |
| 1189 | - struct FileAltIds fai = altIdsOfFile(pf); | |
| 1190 | - int fmode = _setmode(fai.fd, _O_BINARY); | |
| 1191 | - _setmode(fai.fd, fmode); | |
| 1192 | - while( rv < ocnt ){ | |
| 1193 | - size_t nbo = osz; | |
| 1194 | - while( nbo > 0 ){ | |
| 1195 | - DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo; | |
| 1196 | - BOOL wrc = TRUE; | |
| 1197 | - BOOL genCR = (fmode & _O_TEXT)!=0; | |
| 1198 | - if( genCR ){ | |
| 1199 | - const char *pnl = (const char*)memchr(buf, '\n', nbo); | |
| 1200 | - if( pnl ) nbo = pnl - (const char*)buf; | |
| 1201 | - else genCR = 0; | |
| 1202 | - } | |
| 1203 | - if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0); | |
| 1204 | - if( genCR && wrc ){ | |
| 1205 | - wrc = WriteFile(fai.fh, "\r\n", 2, 0,0); | |
| 1206 | - ++dwno; /* Skip over the LF */ | |
| 1207 | - } | |
| 1208 | - if( !wrc ) return rv; | |
| 1209 | - buf = (const char*)buf + dwno; | |
| 1210 | - nbo += dwno; | |
| 1211 | - } | |
| 1212 | - ++rv; | |
| 1213 | - } | |
| 1214 | - return rv; | |
| 1215 | -} | |
| 1216 | - | |
| 1217 | -SQLITE_INTERNAL_LINKAGE char * | |
| 1218 | -cfGets(char *cBuf, int n, FILE *pf){ | |
| 1219 | - int nci = 0; | |
| 1220 | - struct FileAltIds fai = altIdsOfFile(pf); | |
| 1221 | - int fmode = _setmode(fai.fd, _O_BINARY); | |
| 1222 | - BOOL eatCR = (fmode & _O_TEXT)!=0; | |
| 1223 | - _setmode(fai.fd, fmode); | |
| 1224 | - while( nci < n-1 ){ | |
| 1225 | - DWORD nr; | |
| 1226 | - if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break; | |
| 1227 | - if( nr>0 && (!eatCR || cBuf[nci]!='\r') ) nci += nr; | |
| 1228 | - } | |
| 1229 | - if( nci < n ) cBuf[nci] = 0; | |
| 1230 | - return (nci>0)? cBuf : 0; | |
| 1231 | -} | |
| 1232 | -# else | |
| 1233 | -# define cfWrite(b,os,no,f) fwrite(b,os,no,f) | |
| 1234 | -# define cfGets(b,n,f) fgets(b,n,f) | |
| 1235 | -# endif | |
| 1236 | - | |
| 1237 | -# ifdef CONSIO_EPUTB | |
| 1238 | -SQLITE_INTERNAL_LINKAGE int | |
| 1239 | -ePutbUtf8(const char *cBuf, int nAccept){ | |
| 1240 | - FILE *pfErr; | |
| 1241 | - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ | |
| 1242 | - PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); | |
| 1243 | -# if CIO_WIN_WC_XLATE | |
| 1244 | - if( pstReachesConsole(ppst) ){ | |
| 1245 | - return conZstrEmit(ppst, cBuf, nAccept); | |
| 1246 | - }else { | |
| 1247 | -# endif | |
| 1248 | - return (int)cfWrite(cBuf, 1, nAccept, pfErr); | |
| 1249 | -# if CIO_WIN_WC_XLATE | |
| 1250 | - } | |
| 1251 | -# endif | |
| 1252 | -} | |
| 1253 | -# endif /* defined(CONSIO_EPUTB) */ | |
| 1254 | - | |
| 1255 | -SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ | |
| 1256 | - if( pfIn==0 ) pfIn = stdin; | |
| 1257 | -# if CIO_WIN_WC_XLATE | |
| 1258 | - if( pfIn == consoleInfo.pstSetup[0].pf | |
| 1259 | - && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ | |
| 1260 | -# if CIO_WIN_WC_XLATE==1 | |
| 1261 | -# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ | |
| 1262 | - WCHAR wcBuf[SHELL_GULP+1]; | |
| 1263 | - int lend = 0, noc = 0; | |
| 1264 | - if( ncMax > 0 ) cBuf[0] = 0; | |
| 1265 | - while( noc < ncMax-8-1 && !lend ){ | |
| 1266 | - /* There is room for at least 2 more characters and a 0-terminator. */ | |
| 1267 | - int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; | |
| 1268 | -# undef SHELL_GULP | |
| 1269 | - DWORD nbr = 0; | |
| 1270 | - BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0); | |
| 1271 | - if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ | |
| 1272 | - /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */ | |
| 1273 | - DWORD nbrx; | |
| 1274 | - bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0); | |
| 1275 | - if( bRC ) nbr += nbrx; | |
| 1276 | - } | |
| 1277 | - if( !bRC || (noc==0 && nbr==0) ) return 0; | |
| 1278 | - if( nbr > 0 ){ | |
| 1279 | - int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); | |
| 1280 | - if( nmb != 0 && noc+nmb <= ncMax ){ | |
| 1281 | - int iseg = noc; | |
| 1282 | - nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0); | |
| 1283 | - noc += nmb; | |
| 1284 | - /* Fixup line-ends as coded by Windows for CR (or "Enter".) | |
| 1285 | - ** This is done without regard for any setMode{Text,Binary}() | |
| 1286 | - ** call that might have been done on the interactive input. | |
| 1287 | - */ | |
| 1288 | - if( noc > 0 ){ | |
| 1289 | - if( cBuf[noc-1]=='\n' ){ | |
| 1290 | - lend = 1; | |
| 1291 | - if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n'; | |
| 1292 | - } | |
| 1293 | - } | |
| 1294 | - /* Check for ^Z (anywhere in line) too, to act as EOF. */ | |
| 1295 | - while( iseg < noc ){ | |
| 1296 | - if( cBuf[iseg]=='\x1a' ){ | |
| 1297 | - noc = iseg; /* Chop ^Z and anything following. */ | |
| 1298 | - lend = 1; /* Counts as end of line too. */ | |
| 1299 | - break; | |
| 1300 | - } | |
| 1301 | - ++iseg; | |
| 1302 | - } | |
| 1303 | - }else break; /* Drop apparent garbage in. (Could assert.) */ | |
| 1304 | - }else break; | |
| 1305 | - } | |
| 1306 | - /* If got nothing, (after ^Z chop), must be at end-of-file. */ | |
| 1307 | - if( noc > 0 ){ | |
| 1308 | - cBuf[noc] = 0; | |
| 1309 | - return cBuf; | |
| 1310 | - }else return 0; | |
| 1311 | -# endif | |
| 1312 | - }else{ | |
| 1313 | -# endif | |
| 1314 | - return cfGets(cBuf, ncMax, pfIn); | |
| 1315 | -# if CIO_WIN_WC_XLATE | |
| 1316 | - } | |
| 1317 | -# endif | |
| 1318 | -} | |
| 1319 | -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ | |
| 1320 | - | |
| 1321 | -#if defined(_MSC_VER) | |
| 1322 | -# pragma warning(default : 4204) | |
| 1323 | -#endif | |
| 1324 | - | |
| 1325 | -#undef SHELL_INVALID_FILE_PTR | |
| 1326 | - | |
| 1327 | -/************************* End ../ext/consio/console_io.c ********************/ | |
| 1328 | - | |
| 1329 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 1330 | - | |
| 1331 | -/* From here onward, fgets() is redirected to the console_io library. */ | |
| 1332 | -# define fgets(b,n,f) fGetsUtf8(b,n,f) | |
| 1333 | -/* | |
| 1334 | - * Define macros for emitting output text in various ways: | |
| 1335 | - * sputz(s, z) => emit 0-terminated string z to given stream s | |
| 1336 | - * sputf(s, f, ...) => emit varargs per format f to given stream s | |
| 1337 | - * oputz(z) => emit 0-terminated string z to default stream | |
| 1338 | - * oputf(f, ...) => emit varargs per format f to default stream | |
| 1339 | - * eputz(z) => emit 0-terminated string z to error stream | |
| 1340 | - * eputf(f, ...) => emit varargs per format f to error stream | |
| 1341 | - * oputb(b, n) => emit char buffer b[0..n-1] to default stream | |
| 1342 | - * | |
| 1343 | - * Note that the default stream is whatever has been last set via: | |
| 1344 | - * setOutputStream(FILE *pf) | |
| 1345 | - * This is normally the stream that CLI normal output goes to. | |
| 1346 | - * For the stand-alone CLI, it is stdout with no .output redirect. | |
| 1347 | - * | |
| 1348 | - * The ?putz(z) forms are required for the Fiddle builds for string literal | |
| 1349 | - * output, in aid of enforcing format string to argument correspondence. | |
| 1350 | - */ | |
| 1351 | -# define sputz(s,z) fPutsUtf8(z,s) | |
| 1352 | -# define sputf fPrintfUtf8 | |
| 1353 | -# define oputz(z) oPutsUtf8(z) | |
| 1354 | -# define oputf oPrintfUtf8 | |
| 1355 | -# define eputz(z) ePutsUtf8(z) | |
| 1356 | -# define eputf ePrintfUtf8 | |
| 1357 | -# define oputb(buf,na) oPutbUtf8(buf,na) | |
| 1358 | -# define fflush(s) fFlushBuffer(s); | |
| 1359 | - | |
| 1360 | -#else | |
| 1361 | -/* For Fiddle, all console handling and emit redirection is omitted. */ | |
| 1362 | -/* These next 3 macros are for emitting formatted output. When complaints | |
| 1363 | - * from the WASM build are issued for non-formatted output, when a mere | |
| 1364 | - * string literal is to be emitted, the ?putz(z) forms should be used. | |
| 1365 | - * (This permits compile-time checking of format string / argument mismatch.) | |
| 1366 | - */ | |
| 1367 | -# define oputf(fmt, ...) printf(fmt,__VA_ARGS__) | |
| 1368 | -# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) | |
| 1369 | -# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) | |
| 1370 | -/* These next 3 macros are for emitting simple string literals. */ | |
| 1371 | -# define oputz(z) fputs(z,stdout) | |
| 1372 | -# define eputz(z) fputs(z,stderr) | |
| 1373 | -# define sputz(fp,z) fputs(z,fp) | |
| 1374 | -# define oputb(buf,na) fwrite(buf,1,na,stdout) | |
| 1375 | -# undef fflush | |
| 1376 | -#endif | |
| 624 | + | |
| 625 | +#define eputz(z) sqlite3_fputs(z,stderr) | |
| 626 | +#define sputz(fp,z) sqlite3_fputs(z,fp) | |
| 1377 | 627 | |
| 1378 | 628 | /* True if the timer is enabled */ |
| 1379 | 629 | static int enableTimer = 0; |
| 1380 | 630 | |
| 1381 | 631 | /* A version of strcmp() that works with NULL values */ |
| @@ -1416,10 +666,11 @@ | ||
| 1416 | 666 | struct timeval ru_utime; /* user CPU time used */ |
| 1417 | 667 | struct timeval ru_stime; /* system CPU time used */ |
| 1418 | 668 | }; |
| 1419 | 669 | #define getrusage(A,B) memset(B,0,sizeof(*B)) |
| 1420 | 670 | #endif |
| 671 | + | |
| 1421 | 672 | |
| 1422 | 673 | /* Saved resource information for the beginning of an operation */ |
| 1423 | 674 | static struct rusage sBegin; /* CPU time at start */ |
| 1424 | 675 | static sqlite3_int64 iBegin; /* Wall-clock time at start */ |
| 1425 | 676 | |
| @@ -1440,24 +691,24 @@ | ||
| 1440 | 691 | } |
| 1441 | 692 | |
| 1442 | 693 | /* |
| 1443 | 694 | ** Print the timing results. |
| 1444 | 695 | */ |
| 1445 | -static void endTimer(void){ | |
| 696 | +static void endTimer(FILE *out){ | |
| 1446 | 697 | if( enableTimer ){ |
| 1447 | 698 | sqlite3_int64 iEnd = timeOfDay(); |
| 1448 | 699 | struct rusage sEnd; |
| 1449 | 700 | getrusage(RUSAGE_SELF, &sEnd); |
| 1450 | - sputf(stdout, "Run Time: real %.3f user %f sys %f\n", | |
| 701 | + sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", | |
| 1451 | 702 | (iEnd - iBegin)*0.001, |
| 1452 | 703 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 1453 | 704 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 1454 | 705 | } |
| 1455 | 706 | } |
| 1456 | 707 | |
| 1457 | 708 | #define BEGIN_TIMER beginTimer() |
| 1458 | -#define END_TIMER endTimer() | |
| 709 | +#define END_TIMER(X) endTimer(X) | |
| 1459 | 710 | #define HAS_TIMER 1 |
| 1460 | 711 | |
| 1461 | 712 | #elif (defined(_WIN32) || defined(WIN32)) |
| 1462 | 713 | |
| 1463 | 714 | /* Saved resource information for the beginning of an operation */ |
| @@ -1519,29 +770,29 @@ | ||
| 1519 | 770 | } |
| 1520 | 771 | |
| 1521 | 772 | /* |
| 1522 | 773 | ** Print the timing results. |
| 1523 | 774 | */ |
| 1524 | -static void endTimer(void){ | |
| 775 | +static void endTimer(FILE *out){ | |
| 1525 | 776 | if( enableTimer && getProcessTimesAddr){ |
| 1526 | 777 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 1527 | 778 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 1528 | 779 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
| 1529 | - sputf(stdout, "Run Time: real %.3f user %f sys %f\n", | |
| 780 | + sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", | |
| 1530 | 781 | (ftWallEnd - ftWallBegin)*0.001, |
| 1531 | 782 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 1532 | 783 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 1533 | 784 | } |
| 1534 | 785 | } |
| 1535 | 786 | |
| 1536 | 787 | #define BEGIN_TIMER beginTimer() |
| 1537 | -#define END_TIMER endTimer() | |
| 788 | +#define END_TIMER(X) endTimer(X) | |
| 1538 | 789 | #define HAS_TIMER hasTimer() |
| 1539 | 790 | |
| 1540 | 791 | #else |
| 1541 | 792 | #define BEGIN_TIMER |
| 1542 | -#define END_TIMER | |
| 793 | +#define END_TIMER(X) /*no-op*/ | |
| 1543 | 794 | #define HAS_TIMER 0 |
| 1544 | 795 | #endif |
| 1545 | 796 | |
| 1546 | 797 | /* |
| 1547 | 798 | ** Used to prevent warnings about unused parameters |
| @@ -1738,41 +989,216 @@ | ||
| 1738 | 989 | char *z; |
| 1739 | 990 | if( iotrace==0 ) return; |
| 1740 | 991 | va_start(ap, zFormat); |
| 1741 | 992 | z = sqlite3_vmprintf(zFormat, ap); |
| 1742 | 993 | va_end(ap); |
| 1743 | - sputf(iotrace, "%s", z); | |
| 994 | + sqlite3_fprintf(iotrace, "%s", z); | |
| 1744 | 995 | sqlite3_free(z); |
| 1745 | 996 | } |
| 1746 | 997 | #endif |
| 1747 | 998 | |
| 999 | +/* Lookup table to estimate the number of columns consumed by a Unicode | |
| 1000 | +** character. | |
| 1001 | +*/ | |
| 1002 | +static const struct { | |
| 1003 | + unsigned char w; /* Width of the character in columns */ | |
| 1004 | + int iFirst; /* First character in a span having this width */ | |
| 1005 | +} aUWidth[] = { | |
| 1006 | + /* {1, 0x00000}, */ | |
| 1007 | + {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, | |
| 1008 | + {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, | |
| 1009 | + {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, | |
| 1010 | + {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, | |
| 1011 | + {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, | |
| 1012 | + {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, | |
| 1013 | + {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, | |
| 1014 | + {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, | |
| 1015 | + {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, | |
| 1016 | + {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, | |
| 1017 | + {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, | |
| 1018 | + {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, | |
| 1019 | + {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, | |
| 1020 | + {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, | |
| 1021 | + {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, | |
| 1022 | + {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, | |
| 1023 | + {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, | |
| 1024 | + {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, | |
| 1025 | + {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, | |
| 1026 | + {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, | |
| 1027 | + {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, | |
| 1028 | + {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, | |
| 1029 | + {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, | |
| 1030 | + {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, | |
| 1031 | + {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, | |
| 1032 | + {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, | |
| 1033 | + {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, | |
| 1034 | + {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, | |
| 1035 | + {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, | |
| 1036 | + {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, | |
| 1037 | + {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, | |
| 1038 | + {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, | |
| 1039 | + {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, | |
| 1040 | + {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, | |
| 1041 | + {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, | |
| 1042 | + {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, | |
| 1043 | + {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, | |
| 1044 | + {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, | |
| 1045 | + {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, | |
| 1046 | + {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, | |
| 1047 | + {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, | |
| 1048 | + {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, | |
| 1049 | + {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, | |
| 1050 | + {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, | |
| 1051 | + {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, | |
| 1052 | + {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, | |
| 1053 | + {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, | |
| 1054 | + {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, | |
| 1055 | + {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, | |
| 1056 | + {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, | |
| 1057 | + {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, | |
| 1058 | + {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, | |
| 1059 | + {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, | |
| 1060 | + {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, | |
| 1061 | + {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, | |
| 1062 | + {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, | |
| 1063 | + {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, | |
| 1064 | + {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, | |
| 1065 | + {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, | |
| 1066 | + {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, | |
| 1067 | + {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} | |
| 1068 | +}; | |
| 1069 | + | |
| 1748 | 1070 | /* |
| 1749 | -** Output string zUtf to Out stream as w characters. If w is negative, | |
| 1071 | +** Return an estimate of the width, in columns, for the single Unicode | |
| 1072 | +** character c. For normal characters, the answer is always 1. But the | |
| 1073 | +** estimate might be 0 or 2 for zero-width and double-width characters. | |
| 1074 | +** | |
| 1075 | +** Different display devices display unicode using different widths. So | |
| 1076 | +** it is impossible to know that true display width with 100% accuracy. | |
| 1077 | +** Inaccuracies in the width estimates might cause columns to be misaligned. | |
| 1078 | +** Unfortunately, there is nothing we can do about that. | |
| 1079 | +*/ | |
| 1080 | +int cli_wcwidth(int c){ | |
| 1081 | + int iFirst, iLast; | |
| 1082 | + | |
| 1083 | + /* Fast path for common characters */ | |
| 1084 | + if( c<=0x300 ) return 1; | |
| 1085 | + | |
| 1086 | + /* The general case */ | |
| 1087 | + iFirst = 0; | |
| 1088 | + iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; | |
| 1089 | + while( iFirst<iLast-1 ){ | |
| 1090 | + int iMid = (iFirst+iLast)/2; | |
| 1091 | + int cMid = aUWidth[iMid].iFirst; | |
| 1092 | + if( cMid < c ){ | |
| 1093 | + iFirst = iMid; | |
| 1094 | + }else if( cMid > c ){ | |
| 1095 | + iLast = iMid - 1; | |
| 1096 | + }else{ | |
| 1097 | + return aUWidth[iMid].w; | |
| 1098 | + } | |
| 1099 | + } | |
| 1100 | + if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; | |
| 1101 | + return aUWidth[iLast].w; | |
| 1102 | +} | |
| 1103 | + | |
| 1104 | +/* | |
| 1105 | +** Compute the value and length of a multi-byte UTF-8 character that | |
| 1106 | +** begins at z[0]. Return the length. Write the Unicode value into *pU. | |
| 1107 | +** | |
| 1108 | +** This routine only works for *multi-byte* UTF-8 characters. | |
| 1109 | +*/ | |
| 1110 | +static int decodeUtf8(const unsigned char *z, int *pU){ | |
| 1111 | + if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ | |
| 1112 | + *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); | |
| 1113 | + return 2; | |
| 1114 | + } | |
| 1115 | + if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ | |
| 1116 | + *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); | |
| 1117 | + return 3; | |
| 1118 | + } | |
| 1119 | + if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 | |
| 1120 | + && (z[3] & 0xc0)==0x80 | |
| 1121 | + ){ | |
| 1122 | + *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 | |
| 1123 | + | (z[4] & 0x3f); | |
| 1124 | + return 4; | |
| 1125 | + } | |
| 1126 | + *pU = 0; | |
| 1127 | + return 1; | |
| 1128 | +} | |
| 1129 | + | |
| 1130 | + | |
| 1131 | +#if 0 /* NOT USED */ | |
| 1132 | +/* | |
| 1133 | +** Return the width, in display columns, of a UTF-8 string. | |
| 1134 | +** | |
| 1135 | +** Each normal character counts as 1. Zero-width characters count | |
| 1136 | +** as zero, and double-width characters count as 2. | |
| 1137 | +*/ | |
| 1138 | +int cli_wcswidth(const char *z){ | |
| 1139 | + const unsigned char *a = (const unsigned char*)z; | |
| 1140 | + int n = 0; | |
| 1141 | + int i = 0; | |
| 1142 | + unsigned char c; | |
| 1143 | + while( (c = a[i])!=0 ){ | |
| 1144 | + if( c>=0xc0 ){ | |
| 1145 | + int u; | |
| 1146 | + int len = decodeUtf8(&a[i], &u); | |
| 1147 | + i += len; | |
| 1148 | + n += cli_wcwidth(u); | |
| 1149 | + }else if( c>=' ' ){ | |
| 1150 | + n++; | |
| 1151 | + i++; | |
| 1152 | + }else{ | |
| 1153 | + i++; | |
| 1154 | + } | |
| 1155 | + } | |
| 1156 | + return n; | |
| 1157 | +} | |
| 1158 | +#endif | |
| 1159 | + | |
| 1160 | +/* | |
| 1161 | +** Output string zUtf to stdout as w characters. If w is negative, | |
| 1750 | 1162 | ** then right-justify the text. W is the width in UTF-8 characters, not |
| 1751 | 1163 | ** in bytes. This is different from the %*.*s specification in printf |
| 1752 | 1164 | ** since with %*.*s the width is measured in bytes, not characters. |
| 1165 | +** | |
| 1166 | +** Take into account zero-width and double-width Unicode characters. | |
| 1167 | +** In other words, a zero-width character does not count toward the | |
| 1168 | +** the w limit. A double-width character counts as two. | |
| 1753 | 1169 | */ |
| 1754 | -static void utf8_width_print(int w, const char *zUtf){ | |
| 1755 | - int i; | |
| 1756 | - int n; | |
| 1170 | +static void utf8_width_print(FILE *out, int w, const char *zUtf){ | |
| 1171 | + const unsigned char *a = (const unsigned char*)zUtf; | |
| 1172 | + unsigned char c; | |
| 1173 | + int i = 0; | |
| 1174 | + int n = 0; | |
| 1757 | 1175 | int aw = w<0 ? -w : w; |
| 1758 | 1176 | if( zUtf==0 ) zUtf = ""; |
| 1759 | - for(i=n=0; zUtf[i]; i++){ | |
| 1760 | - if( (zUtf[i]&0xc0)!=0x80 ){ | |
| 1177 | + while( (c = a[i])!=0 ){ | |
| 1178 | + if( (c&0xc0)==0xc0 ){ | |
| 1179 | + int u; | |
| 1180 | + int len = decodeUtf8(a+i, &u); | |
| 1181 | + int x = cli_wcwidth(u); | |
| 1182 | + if( x+n>aw ){ | |
| 1183 | + break; | |
| 1184 | + } | |
| 1185 | + i += len; | |
| 1186 | + n += x; | |
| 1187 | + }else if( n>=aw ){ | |
| 1188 | + break; | |
| 1189 | + }else{ | |
| 1761 | 1190 | n++; |
| 1762 | - if( n==aw ){ | |
| 1763 | - do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); | |
| 1764 | - break; | |
| 1765 | - } | |
| 1191 | + i++; | |
| 1766 | 1192 | } |
| 1767 | 1193 | } |
| 1768 | 1194 | if( n>=aw ){ |
| 1769 | - oputf("%.*s", i, zUtf); | |
| 1195 | + sqlite3_fprintf(out, "%.*s", i, zUtf); | |
| 1770 | 1196 | }else if( w<0 ){ |
| 1771 | - oputf("%*s%s", aw-n, "", zUtf); | |
| 1197 | + sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf); | |
| 1772 | 1198 | }else{ |
| 1773 | - oputf("%s%*s", zUtf, aw-n, ""); | |
| 1199 | + sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, ""); | |
| 1774 | 1200 | } |
| 1775 | 1201 | } |
| 1776 | 1202 | |
| 1777 | 1203 | |
| 1778 | 1204 | /* |
| @@ -1834,11 +1260,11 @@ | ||
| 1834 | 1260 | struct __stat64 x = {0}; |
| 1835 | 1261 | # define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) |
| 1836 | 1262 | /* On Windows, open first, then check the stream nature. This order |
| 1837 | 1263 | ** is necessary because _stat() and sibs, when checking a named pipe, |
| 1838 | 1264 | ** effectively break the pipe as its supplier sees it. */ |
| 1839 | - FILE *rv = fopen(zFile, "rb"); | |
| 1265 | + FILE *rv = sqlite3_fopen(zFile, "rb"); | |
| 1840 | 1266 | if( rv==0 ) return 0; |
| 1841 | 1267 | if( _fstat64(_fileno(rv), &x) != 0 |
| 1842 | 1268 | || !STAT_CHR_SRC(x.st_mode)){ |
| 1843 | 1269 | fclose(rv); |
| 1844 | 1270 | rv = 0; |
| @@ -1848,11 +1274,11 @@ | ||
| 1848 | 1274 | struct stat x = {0}; |
| 1849 | 1275 | int rc = stat(zFile, &x); |
| 1850 | 1276 | # define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) |
| 1851 | 1277 | if( rc!=0 ) return 0; |
| 1852 | 1278 | if( STAT_CHR_SRC(x.st_mode) ){ |
| 1853 | - return fopen(zFile, "rb"); | |
| 1279 | + return sqlite3_fopen(zFile, "rb"); | |
| 1854 | 1280 | }else{ |
| 1855 | 1281 | return 0; |
| 1856 | 1282 | } |
| 1857 | 1283 | #endif |
| 1858 | 1284 | #undef STAT_CHR_SRC |
| @@ -1875,11 +1301,11 @@ | ||
| 1875 | 1301 | if( n+100>nLine ){ |
| 1876 | 1302 | nLine = nLine*2 + 100; |
| 1877 | 1303 | zLine = realloc(zLine, nLine); |
| 1878 | 1304 | shell_check_oom(zLine); |
| 1879 | 1305 | } |
| 1880 | - if( fgets(&zLine[n], nLine - n, in)==0 ){ | |
| 1306 | + if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){ | |
| 1881 | 1307 | if( n==0 ){ |
| 1882 | 1308 | free(zLine); |
| 1883 | 1309 | return 0; |
| 1884 | 1310 | } |
| 1885 | 1311 | zLine[n] = 0; |
| @@ -2941,11 +2367,11 @@ | ||
| 2941 | 2367 | ** |
| 2942 | 2368 | ****************************************************************************** |
| 2943 | 2369 | ** |
| 2944 | 2370 | ** This SQLite extension implements functions that compute SHA3 hashes |
| 2945 | 2371 | ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. |
| 2946 | -** Two SQL functions are implemented: | |
| 2372 | +** Three SQL functions are implemented: | |
| 2947 | 2373 | ** |
| 2948 | 2374 | ** sha3(X,SIZE) |
| 2949 | 2375 | ** sha3_agg(Y,SIZE) |
| 2950 | 2376 | ** sha3_query(Z,SIZE) |
| 2951 | 2377 | ** |
| @@ -3783,10 +3209,422 @@ | ||
| 3783 | 3209 | } |
| 3784 | 3210 | return rc; |
| 3785 | 3211 | } |
| 3786 | 3212 | |
| 3787 | 3213 | /************************* End ../ext/misc/shathree.c ********************/ |
| 3214 | +/************************* Begin ../ext/misc/sha1.c ******************/ | |
| 3215 | +/* | |
| 3216 | +** 2017-01-27 | |
| 3217 | +** | |
| 3218 | +** The author disclaims copyright to this source code. In place of | |
| 3219 | +** a legal notice, here is a blessing: | |
| 3220 | +** | |
| 3221 | +** May you do good and not evil. | |
| 3222 | +** May you find forgiveness for yourself and forgive others. | |
| 3223 | +** May you share freely, never taking more than you give. | |
| 3224 | +** | |
| 3225 | +****************************************************************************** | |
| 3226 | +** | |
| 3227 | +** This SQLite extension implements functions that compute SHA1 hashes. | |
| 3228 | +** Two SQL functions are implemented: | |
| 3229 | +** | |
| 3230 | +** sha1(X) | |
| 3231 | +** sha1_query(Y) | |
| 3232 | +** | |
| 3233 | +** The sha1(X) function computes the SHA1 hash of the input X, or NULL if | |
| 3234 | +** X is NULL. | |
| 3235 | +** | |
| 3236 | +** The sha1_query(Y) function evalutes all queries in the SQL statements of Y | |
| 3237 | +** and returns a hash of their results. | |
| 3238 | +*/ | |
| 3239 | +/* #include "sqlite3ext.h" */ | |
| 3240 | +SQLITE_EXTENSION_INIT1 | |
| 3241 | +#include <assert.h> | |
| 3242 | +#include <string.h> | |
| 3243 | +#include <stdarg.h> | |
| 3244 | + | |
| 3245 | +/****************************************************************************** | |
| 3246 | +** The Hash Engine | |
| 3247 | +*/ | |
| 3248 | +/* Context for the SHA1 hash */ | |
| 3249 | +typedef struct SHA1Context SHA1Context; | |
| 3250 | +struct SHA1Context { | |
| 3251 | + unsigned int state[5]; | |
| 3252 | + unsigned int count[2]; | |
| 3253 | + unsigned char buffer[64]; | |
| 3254 | +}; | |
| 3255 | + | |
| 3256 | +#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) | |
| 3257 | +#define rol(x,k) SHA_ROT(x,k,32-(k)) | |
| 3258 | +#define ror(x,k) SHA_ROT(x,32-(k),k) | |
| 3259 | + | |
| 3260 | +#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ | |
| 3261 | + |(rol(block[i],8)&0x00FF00FF)) | |
| 3262 | +#define blk0be(i) block[i] | |
| 3263 | +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ | |
| 3264 | + ^block[(i+2)&15]^block[i&15],1)) | |
| 3265 | + | |
| 3266 | +/* | |
| 3267 | + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 | |
| 3268 | + * | |
| 3269 | + * Rl0() for little-endian and Rb0() for big-endian. Endianness is | |
| 3270 | + * determined at run-time. | |
| 3271 | + */ | |
| 3272 | +#define Rl0(v,w,x,y,z,i) \ | |
| 3273 | + z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); | |
| 3274 | +#define Rb0(v,w,x,y,z,i) \ | |
| 3275 | + z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); | |
| 3276 | +#define R1(v,w,x,y,z,i) \ | |
| 3277 | + z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); | |
| 3278 | +#define R2(v,w,x,y,z,i) \ | |
| 3279 | + z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); | |
| 3280 | +#define R3(v,w,x,y,z,i) \ | |
| 3281 | + z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); | |
| 3282 | +#define R4(v,w,x,y,z,i) \ | |
| 3283 | + z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); | |
| 3284 | + | |
| 3285 | +/* | |
| 3286 | + * Hash a single 512-bit block. This is the core of the algorithm. | |
| 3287 | + */ | |
| 3288 | +static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){ | |
| 3289 | + unsigned int qq[5]; /* a, b, c, d, e; */ | |
| 3290 | + static int one = 1; | |
| 3291 | + unsigned int block[16]; | |
| 3292 | + memcpy(block, buffer, 64); | |
| 3293 | + memcpy(qq,state,5*sizeof(unsigned int)); | |
| 3294 | + | |
| 3295 | +#define a qq[0] | |
| 3296 | +#define b qq[1] | |
| 3297 | +#define c qq[2] | |
| 3298 | +#define d qq[3] | |
| 3299 | +#define e qq[4] | |
| 3300 | + | |
| 3301 | + /* Copy p->state[] to working vars */ | |
| 3302 | + /* | |
| 3303 | + a = state[0]; | |
| 3304 | + b = state[1]; | |
| 3305 | + c = state[2]; | |
| 3306 | + d = state[3]; | |
| 3307 | + e = state[4]; | |
| 3308 | + */ | |
| 3309 | + | |
| 3310 | + /* 4 rounds of 20 operations each. Loop unrolled. */ | |
| 3311 | + if( 1 == *(unsigned char*)&one ){ | |
| 3312 | + Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); | |
| 3313 | + Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); | |
| 3314 | + Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); | |
| 3315 | + Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); | |
| 3316 | + }else{ | |
| 3317 | + Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); | |
| 3318 | + Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); | |
| 3319 | + Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); | |
| 3320 | + Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); | |
| 3321 | + } | |
| 3322 | + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); | |
| 3323 | + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); | |
| 3324 | + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); | |
| 3325 | + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); | |
| 3326 | + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); | |
| 3327 | + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); | |
| 3328 | + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); | |
| 3329 | + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); | |
| 3330 | + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); | |
| 3331 | + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); | |
| 3332 | + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); | |
| 3333 | + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); | |
| 3334 | + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); | |
| 3335 | + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); | |
| 3336 | + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); | |
| 3337 | + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); | |
| 3338 | + | |
| 3339 | + /* Add the working vars back into context.state[] */ | |
| 3340 | + state[0] += a; | |
| 3341 | + state[1] += b; | |
| 3342 | + state[2] += c; | |
| 3343 | + state[3] += d; | |
| 3344 | + state[4] += e; | |
| 3345 | + | |
| 3346 | +#undef a | |
| 3347 | +#undef b | |
| 3348 | +#undef c | |
| 3349 | +#undef d | |
| 3350 | +#undef e | |
| 3351 | +} | |
| 3352 | + | |
| 3353 | + | |
| 3354 | +/* Initialize a SHA1 context */ | |
| 3355 | +static void hash_init(SHA1Context *p){ | |
| 3356 | + /* SHA1 initialization constants */ | |
| 3357 | + p->state[0] = 0x67452301; | |
| 3358 | + p->state[1] = 0xEFCDAB89; | |
| 3359 | + p->state[2] = 0x98BADCFE; | |
| 3360 | + p->state[3] = 0x10325476; | |
| 3361 | + p->state[4] = 0xC3D2E1F0; | |
| 3362 | + p->count[0] = p->count[1] = 0; | |
| 3363 | +} | |
| 3364 | + | |
| 3365 | +/* Add new content to the SHA1 hash */ | |
| 3366 | +static void hash_step( | |
| 3367 | + SHA1Context *p, /* Add content to this context */ | |
| 3368 | + const unsigned char *data, /* Data to be added */ | |
| 3369 | + unsigned int len /* Number of bytes in data */ | |
| 3370 | +){ | |
| 3371 | + unsigned int i, j; | |
| 3372 | + | |
| 3373 | + j = p->count[0]; | |
| 3374 | + if( (p->count[0] += len << 3) < j ){ | |
| 3375 | + p->count[1] += (len>>29)+1; | |
| 3376 | + } | |
| 3377 | + j = (j >> 3) & 63; | |
| 3378 | + if( (j + len) > 63 ){ | |
| 3379 | + (void)memcpy(&p->buffer[j], data, (i = 64-j)); | |
| 3380 | + SHA1Transform(p->state, p->buffer); | |
| 3381 | + for(; i + 63 < len; i += 64){ | |
| 3382 | + SHA1Transform(p->state, &data[i]); | |
| 3383 | + } | |
| 3384 | + j = 0; | |
| 3385 | + }else{ | |
| 3386 | + i = 0; | |
| 3387 | + } | |
| 3388 | + (void)memcpy(&p->buffer[j], &data[i], len - i); | |
| 3389 | +} | |
| 3390 | + | |
| 3391 | +/* Compute a string using sqlite3_vsnprintf() and hash it */ | |
| 3392 | +static void hash_step_vformat( | |
| 3393 | + SHA1Context *p, /* Add content to this context */ | |
| 3394 | + const char *zFormat, | |
| 3395 | + ... | |
| 3396 | +){ | |
| 3397 | + va_list ap; | |
| 3398 | + int n; | |
| 3399 | + char zBuf[50]; | |
| 3400 | + va_start(ap, zFormat); | |
| 3401 | + sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); | |
| 3402 | + va_end(ap); | |
| 3403 | + n = (int)strlen(zBuf); | |
| 3404 | + hash_step(p, (unsigned char*)zBuf, n); | |
| 3405 | +} | |
| 3406 | + | |
| 3407 | + | |
| 3408 | +/* Add padding and compute the message digest. Render the | |
| 3409 | +** message digest as lower-case hexadecimal and put it into | |
| 3410 | +** zOut[]. zOut[] must be at least 41 bytes long. */ | |
| 3411 | +static void hash_finish( | |
| 3412 | + SHA1Context *p, /* The SHA1 context to finish and render */ | |
| 3413 | + char *zOut, /* Store hex or binary hash here */ | |
| 3414 | + int bAsBinary /* 1 for binary hash, 0 for hex hash */ | |
| 3415 | +){ | |
| 3416 | + unsigned int i; | |
| 3417 | + unsigned char finalcount[8]; | |
| 3418 | + unsigned char digest[20]; | |
| 3419 | + static const char zEncode[] = "0123456789abcdef"; | |
| 3420 | + | |
| 3421 | + for (i = 0; i < 8; i++){ | |
| 3422 | + finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)] | |
| 3423 | + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ | |
| 3424 | + } | |
| 3425 | + hash_step(p, (const unsigned char *)"\200", 1); | |
| 3426 | + while ((p->count[0] & 504) != 448){ | |
| 3427 | + hash_step(p, (const unsigned char *)"\0", 1); | |
| 3428 | + } | |
| 3429 | + hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */ | |
| 3430 | + for (i = 0; i < 20; i++){ | |
| 3431 | + digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); | |
| 3432 | + } | |
| 3433 | + if( bAsBinary ){ | |
| 3434 | + memcpy(zOut, digest, 20); | |
| 3435 | + }else{ | |
| 3436 | + for(i=0; i<20; i++){ | |
| 3437 | + zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; | |
| 3438 | + zOut[i*2+1] = zEncode[digest[i] & 0xf]; | |
| 3439 | + } | |
| 3440 | + zOut[i*2]= 0; | |
| 3441 | + } | |
| 3442 | +} | |
| 3443 | +/* End of the hashing logic | |
| 3444 | +*****************************************************************************/ | |
| 3445 | + | |
| 3446 | +/* | |
| 3447 | +** Implementation of the sha1(X) function. | |
| 3448 | +** | |
| 3449 | +** Return a lower-case hexadecimal rendering of the SHA1 hash of the | |
| 3450 | +** argument X. If X is a BLOB, it is hashed as is. For all other | |
| 3451 | +** types of input, X is converted into a UTF-8 string and the string | |
| 3452 | +** is hash without the trailing 0x00 terminator. The hash of a NULL | |
| 3453 | +** value is NULL. | |
| 3454 | +*/ | |
| 3455 | +static void sha1Func( | |
| 3456 | + sqlite3_context *context, | |
| 3457 | + int argc, | |
| 3458 | + sqlite3_value **argv | |
| 3459 | +){ | |
| 3460 | + SHA1Context cx; | |
| 3461 | + int eType = sqlite3_value_type(argv[0]); | |
| 3462 | + int nByte = sqlite3_value_bytes(argv[0]); | |
| 3463 | + char zOut[44]; | |
| 3464 | + | |
| 3465 | + assert( argc==1 ); | |
| 3466 | + if( eType==SQLITE_NULL ) return; | |
| 3467 | + hash_init(&cx); | |
| 3468 | + if( eType==SQLITE_BLOB ){ | |
| 3469 | + hash_step(&cx, sqlite3_value_blob(argv[0]), nByte); | |
| 3470 | + }else{ | |
| 3471 | + hash_step(&cx, sqlite3_value_text(argv[0]), nByte); | |
| 3472 | + } | |
| 3473 | + if( sqlite3_user_data(context)!=0 ){ | |
| 3474 | + hash_finish(&cx, zOut, 1); | |
| 3475 | + sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT); | |
| 3476 | + }else{ | |
| 3477 | + hash_finish(&cx, zOut, 0); | |
| 3478 | + sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT); | |
| 3479 | + } | |
| 3480 | +} | |
| 3481 | + | |
| 3482 | +/* | |
| 3483 | +** Implementation of the sha1_query(SQL) function. | |
| 3484 | +** | |
| 3485 | +** This function compiles and runs the SQL statement(s) given in the | |
| 3486 | +** argument. The results are hashed using SHA1 and that hash is returned. | |
| 3487 | +** | |
| 3488 | +** The original SQL text is included as part of the hash. | |
| 3489 | +** | |
| 3490 | +** The hash is not just a concatenation of the outputs. Each query | |
| 3491 | +** is delimited and each row and value within the query is delimited, | |
| 3492 | +** with all values being marked with their datatypes. | |
| 3493 | +*/ | |
| 3494 | +static void sha1QueryFunc( | |
| 3495 | + sqlite3_context *context, | |
| 3496 | + int argc, | |
| 3497 | + sqlite3_value **argv | |
| 3498 | +){ | |
| 3499 | + sqlite3 *db = sqlite3_context_db_handle(context); | |
| 3500 | + const char *zSql = (const char*)sqlite3_value_text(argv[0]); | |
| 3501 | + sqlite3_stmt *pStmt = 0; | |
| 3502 | + int nCol; /* Number of columns in the result set */ | |
| 3503 | + int i; /* Loop counter */ | |
| 3504 | + int rc; | |
| 3505 | + int n; | |
| 3506 | + const char *z; | |
| 3507 | + SHA1Context cx; | |
| 3508 | + char zOut[44]; | |
| 3509 | + | |
| 3510 | + assert( argc==1 ); | |
| 3511 | + if( zSql==0 ) return; | |
| 3512 | + hash_init(&cx); | |
| 3513 | + while( zSql[0] ){ | |
| 3514 | + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); | |
| 3515 | + if( rc ){ | |
| 3516 | + char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", | |
| 3517 | + zSql, sqlite3_errmsg(db)); | |
| 3518 | + sqlite3_finalize(pStmt); | |
| 3519 | + sqlite3_result_error(context, zMsg, -1); | |
| 3520 | + sqlite3_free(zMsg); | |
| 3521 | + return; | |
| 3522 | + } | |
| 3523 | + if( !sqlite3_stmt_readonly(pStmt) ){ | |
| 3524 | + char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); | |
| 3525 | + sqlite3_finalize(pStmt); | |
| 3526 | + sqlite3_result_error(context, zMsg, -1); | |
| 3527 | + sqlite3_free(zMsg); | |
| 3528 | + return; | |
| 3529 | + } | |
| 3530 | + nCol = sqlite3_column_count(pStmt); | |
| 3531 | + z = sqlite3_sql(pStmt); | |
| 3532 | + n = (int)strlen(z); | |
| 3533 | + hash_step_vformat(&cx,"S%d:",n); | |
| 3534 | + hash_step(&cx,(unsigned char*)z,n); | |
| 3535 | + | |
| 3536 | + /* Compute a hash over the result of the query */ | |
| 3537 | + while( SQLITE_ROW==sqlite3_step(pStmt) ){ | |
| 3538 | + hash_step(&cx,(const unsigned char*)"R",1); | |
| 3539 | + for(i=0; i<nCol; i++){ | |
| 3540 | + switch( sqlite3_column_type(pStmt,i) ){ | |
| 3541 | + case SQLITE_NULL: { | |
| 3542 | + hash_step(&cx, (const unsigned char*)"N",1); | |
| 3543 | + break; | |
| 3544 | + } | |
| 3545 | + case SQLITE_INTEGER: { | |
| 3546 | + sqlite3_uint64 u; | |
| 3547 | + int j; | |
| 3548 | + unsigned char x[9]; | |
| 3549 | + sqlite3_int64 v = sqlite3_column_int64(pStmt,i); | |
| 3550 | + memcpy(&u, &v, 8); | |
| 3551 | + for(j=8; j>=1; j--){ | |
| 3552 | + x[j] = u & 0xff; | |
| 3553 | + u >>= 8; | |
| 3554 | + } | |
| 3555 | + x[0] = 'I'; | |
| 3556 | + hash_step(&cx, x, 9); | |
| 3557 | + break; | |
| 3558 | + } | |
| 3559 | + case SQLITE_FLOAT: { | |
| 3560 | + sqlite3_uint64 u; | |
| 3561 | + int j; | |
| 3562 | + unsigned char x[9]; | |
| 3563 | + double r = sqlite3_column_double(pStmt,i); | |
| 3564 | + memcpy(&u, &r, 8); | |
| 3565 | + for(j=8; j>=1; j--){ | |
| 3566 | + x[j] = u & 0xff; | |
| 3567 | + u >>= 8; | |
| 3568 | + } | |
| 3569 | + x[0] = 'F'; | |
| 3570 | + hash_step(&cx,x,9); | |
| 3571 | + break; | |
| 3572 | + } | |
| 3573 | + case SQLITE_TEXT: { | |
| 3574 | + int n2 = sqlite3_column_bytes(pStmt, i); | |
| 3575 | + const unsigned char *z2 = sqlite3_column_text(pStmt, i); | |
| 3576 | + hash_step_vformat(&cx,"T%d:",n2); | |
| 3577 | + hash_step(&cx, z2, n2); | |
| 3578 | + break; | |
| 3579 | + } | |
| 3580 | + case SQLITE_BLOB: { | |
| 3581 | + int n2 = sqlite3_column_bytes(pStmt, i); | |
| 3582 | + const unsigned char *z2 = sqlite3_column_blob(pStmt, i); | |
| 3583 | + hash_step_vformat(&cx,"B%d:",n2); | |
| 3584 | + hash_step(&cx, z2, n2); | |
| 3585 | + break; | |
| 3586 | + } | |
| 3587 | + } | |
| 3588 | + } | |
| 3589 | + } | |
| 3590 | + sqlite3_finalize(pStmt); | |
| 3591 | + } | |
| 3592 | + hash_finish(&cx, zOut, 0); | |
| 3593 | + sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); | |
| 3594 | +} | |
| 3595 | + | |
| 3596 | + | |
| 3597 | +#ifdef _WIN32 | |
| 3598 | + | |
| 3599 | +#endif | |
| 3600 | +int sqlite3_sha_init( | |
| 3601 | + sqlite3 *db, | |
| 3602 | + char **pzErrMsg, | |
| 3603 | + const sqlite3_api_routines *pApi | |
| 3604 | +){ | |
| 3605 | + int rc = SQLITE_OK; | |
| 3606 | + static int one = 1; | |
| 3607 | + SQLITE_EXTENSION_INIT2(pApi); | |
| 3608 | + (void)pzErrMsg; /* Unused parameter */ | |
| 3609 | + rc = sqlite3_create_function(db, "sha1", 1, | |
| 3610 | + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, | |
| 3611 | + 0, sha1Func, 0, 0); | |
| 3612 | + if( rc==SQLITE_OK ){ | |
| 3613 | + rc = sqlite3_create_function(db, "sha1b", 1, | |
| 3614 | + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, | |
| 3615 | + (void*)&one, sha1Func, 0, 0); | |
| 3616 | + } | |
| 3617 | + if( rc==SQLITE_OK ){ | |
| 3618 | + rc = sqlite3_create_function(db, "sha1_query", 1, | |
| 3619 | + SQLITE_UTF8|SQLITE_DIRECTONLY, 0, | |
| 3620 | + sha1QueryFunc, 0, 0); | |
| 3621 | + } | |
| 3622 | + return rc; | |
| 3623 | +} | |
| 3624 | + | |
| 3625 | +/************************* End ../ext/misc/sha1.c ********************/ | |
| 3788 | 3626 | /************************* Begin ../ext/misc/uint.c ******************/ |
| 3789 | 3627 | /* |
| 3790 | 3628 | ** 2020-04-14 |
| 3791 | 3629 | ** |
| 3792 | 3630 | ** The author disclaims copyright to this source code. In place of |
| @@ -5075,11 +4913,11 @@ | ||
| 5075 | 4913 | }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){ |
| 5076 | 4914 | p->a[p->nUsed++] = y; |
| 5077 | 4915 | }else if( p->bKeepSorted ){ |
| 5078 | 4916 | int i; |
| 5079 | 4917 | i = percentBinarySearch(p, y, 0); |
| 5080 | - if( i<p->nUsed ){ | |
| 4918 | + if( i<(int)p->nUsed ){ | |
| 5081 | 4919 | memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0])); |
| 5082 | 4920 | } |
| 5083 | 4921 | p->a[i] = y; |
| 5084 | 4922 | p->nUsed++; |
| 5085 | 4923 | }else{ |
| @@ -5191,11 +5029,11 @@ | ||
| 5191 | 5029 | |
| 5192 | 5030 | /* Find and remove the row */ |
| 5193 | 5031 | i = percentBinarySearch(p, y, 1); |
| 5194 | 5032 | if( i>=0 ){ |
| 5195 | 5033 | p->nUsed--; |
| 5196 | - if( i<p->nUsed ){ | |
| 5034 | + if( i<(int)p->nUsed ){ | |
| 5197 | 5035 | memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0])); |
| 5198 | 5036 | } |
| 5199 | 5037 | } |
| 5200 | 5038 | } |
| 5201 | 5039 | |
| @@ -5251,15 +5089,15 @@ | ||
| 5251 | 5089 | sqlite3 *db, |
| 5252 | 5090 | char **pzErrMsg, |
| 5253 | 5091 | const sqlite3_api_routines *pApi |
| 5254 | 5092 | ){ |
| 5255 | 5093 | int rc = SQLITE_OK; |
| 5256 | - int i; | |
| 5257 | -#if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE) | |
| 5258 | - (void)pApi; /* Unused parameter */ | |
| 5259 | -#else | |
| 5094 | + unsigned int i; | |
| 5095 | +#ifdef SQLITE3EXT_H | |
| 5260 | 5096 | SQLITE_EXTENSION_INIT2(pApi); |
| 5097 | +#else | |
| 5098 | + (void)pApi; /* Unused parameter */ | |
| 5261 | 5099 | #endif |
| 5262 | 5100 | (void)pzErrMsg; /* Unused parameter */ |
| 5263 | 5101 | for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){ |
| 5264 | 5102 | rc = sqlite3_create_window_function(db, |
| 5265 | 5103 | aPercentFunc[i].zName, |
| @@ -7012,50 +6850,58 @@ | ||
| 7012 | 6850 | aIdx[4] = i; |
| 7013 | 6851 | idxNum |= 0x40; |
| 7014 | 6852 | } |
| 7015 | 6853 | continue; |
| 7016 | 6854 | } |
| 7017 | - if( pConstraint->iColumn==SERIES_COLUMN_VALUE ){ | |
| 7018 | - switch( op ){ | |
| 7019 | - case SQLITE_INDEX_CONSTRAINT_EQ: | |
| 7020 | - case SQLITE_INDEX_CONSTRAINT_IS: { | |
| 7021 | - idxNum |= 0x0080; | |
| 7022 | - idxNum &= ~0x3300; | |
| 7023 | - aIdx[5] = i; | |
| 7024 | - aIdx[6] = -1; | |
| 7025 | - bStartSeen = 1; | |
| 7026 | - break; | |
| 7027 | - } | |
| 7028 | - case SQLITE_INDEX_CONSTRAINT_GE: { | |
| 7029 | - if( idxNum & 0x0080 ) break; | |
| 7030 | - idxNum |= 0x0100; | |
| 7031 | - idxNum &= ~0x0200; | |
| 7032 | - aIdx[5] = i; | |
| 7033 | - bStartSeen = 1; | |
| 7034 | - break; | |
| 7035 | - } | |
| 7036 | - case SQLITE_INDEX_CONSTRAINT_GT: { | |
| 7037 | - if( idxNum & 0x0080 ) break; | |
| 7038 | - idxNum |= 0x0200; | |
| 7039 | - idxNum &= ~0x0100; | |
| 7040 | - aIdx[5] = i; | |
| 7041 | - bStartSeen = 1; | |
| 7042 | - break; | |
| 7043 | - } | |
| 7044 | - case SQLITE_INDEX_CONSTRAINT_LE: { | |
| 7045 | - if( idxNum & 0x0080 ) break; | |
| 7046 | - idxNum |= 0x1000; | |
| 7047 | - idxNum &= ~0x2000; | |
| 7048 | - aIdx[6] = i; | |
| 7049 | - break; | |
| 7050 | - } | |
| 7051 | - case SQLITE_INDEX_CONSTRAINT_LT: { | |
| 7052 | - if( idxNum & 0x0080 ) break; | |
| 7053 | - idxNum |= 0x2000; | |
| 7054 | - idxNum &= ~0x1000; | |
| 7055 | - aIdx[6] = i; | |
| 7056 | - break; | |
| 6855 | + if( pConstraint->iColumn<SERIES_COLUMN_START ){ | |
| 6856 | + if( pConstraint->iColumn==SERIES_COLUMN_VALUE && pConstraint->usable ){ | |
| 6857 | + switch( op ){ | |
| 6858 | + case SQLITE_INDEX_CONSTRAINT_EQ: | |
| 6859 | + case SQLITE_INDEX_CONSTRAINT_IS: { | |
| 6860 | + idxNum |= 0x0080; | |
| 6861 | + idxNum &= ~0x3300; | |
| 6862 | + aIdx[5] = i; | |
| 6863 | + aIdx[6] = -1; | |
| 6864 | +#ifndef ZERO_ARGUMENT_GENERATE_SERIES | |
| 6865 | + bStartSeen = 1; | |
| 6866 | +#endif | |
| 6867 | + break; | |
| 6868 | + } | |
| 6869 | + case SQLITE_INDEX_CONSTRAINT_GE: { | |
| 6870 | + if( idxNum & 0x0080 ) break; | |
| 6871 | + idxNum |= 0x0100; | |
| 6872 | + idxNum &= ~0x0200; | |
| 6873 | + aIdx[5] = i; | |
| 6874 | +#ifndef ZERO_ARGUMENT_GENERATE_SERIES | |
| 6875 | + bStartSeen = 1; | |
| 6876 | +#endif | |
| 6877 | + break; | |
| 6878 | + } | |
| 6879 | + case SQLITE_INDEX_CONSTRAINT_GT: { | |
| 6880 | + if( idxNum & 0x0080 ) break; | |
| 6881 | + idxNum |= 0x0200; | |
| 6882 | + idxNum &= ~0x0100; | |
| 6883 | + aIdx[5] = i; | |
| 6884 | +#ifndef ZERO_ARGUMENT_GENERATE_SERIES | |
| 6885 | + bStartSeen = 1; | |
| 6886 | +#endif | |
| 6887 | + break; | |
| 6888 | + } | |
| 6889 | + case SQLITE_INDEX_CONSTRAINT_LE: { | |
| 6890 | + if( idxNum & 0x0080 ) break; | |
| 6891 | + idxNum |= 0x1000; | |
| 6892 | + idxNum &= ~0x2000; | |
| 6893 | + aIdx[6] = i; | |
| 6894 | + break; | |
| 6895 | + } | |
| 6896 | + case SQLITE_INDEX_CONSTRAINT_LT: { | |
| 6897 | + if( idxNum & 0x0080 ) break; | |
| 6898 | + idxNum |= 0x2000; | |
| 6899 | + idxNum &= ~0x1000; | |
| 6900 | + aIdx[6] = i; | |
| 6901 | + break; | |
| 6902 | + } | |
| 7057 | 6903 | } |
| 7058 | 6904 | } |
| 7059 | 6905 | continue; |
| 7060 | 6906 | } |
| 7061 | 6907 | iCol = pConstraint->iColumn - SERIES_COLUMN_START; |
| @@ -8115,11 +7961,11 @@ | ||
| 8115 | 7961 | ** If the optional MTIME argument is present, then it is interpreted |
| 8116 | 7962 | ** as an integer - the number of seconds since the unix epoch. The |
| 8117 | 7963 | ** modification-time of the target file is set to this value before |
| 8118 | 7964 | ** returning. |
| 8119 | 7965 | ** |
| 8120 | -** If three or more arguments are passed to this function and an | |
| 7966 | +** If five or more arguments are passed to this function and an | |
| 8121 | 7967 | ** error is encountered, an exception is raised. |
| 8122 | 7968 | ** |
| 8123 | 7969 | ** READFILE(FILE): |
| 8124 | 7970 | ** |
| 8125 | 7971 | ** Read and return the contents of file FILE (type blob) from disk. |
| @@ -8185,10 +8031,17 @@ | ||
| 8185 | 8031 | # define lstat(path,buf) stat(path,buf) |
| 8186 | 8032 | #endif |
| 8187 | 8033 | #include <time.h> |
| 8188 | 8034 | #include <errno.h> |
| 8189 | 8035 | |
| 8036 | +/* When used as part of the CLI, the sqlite3_stdio.h module will have | |
| 8037 | +** been included before this one. In that case use the sqlite3_stdio.h | |
| 8038 | +** #defines. If not, create our own for fopen(). | |
| 8039 | +*/ | |
| 8040 | +#ifndef _SQLITE3_STDIO_H_ | |
| 8041 | +# define sqlite3_fopen fopen | |
| 8042 | +#endif | |
| 8190 | 8043 | |
| 8191 | 8044 | /* |
| 8192 | 8045 | ** Structure of the fsdir() table-valued function |
| 8193 | 8046 | */ |
| 8194 | 8047 | /* 0 1 2 3 4 5 */ |
| @@ -8217,11 +8070,11 @@ | ||
| 8217 | 8070 | sqlite3_int64 nIn; |
| 8218 | 8071 | void *pBuf; |
| 8219 | 8072 | sqlite3 *db; |
| 8220 | 8073 | int mxBlob; |
| 8221 | 8074 | |
| 8222 | - in = fopen(zName, "rb"); | |
| 8075 | + in = sqlite3_fopen(zName, "rb"); | |
| 8223 | 8076 | if( in==0 ){ |
| 8224 | 8077 | /* File does not exist or is unreadable. Leave the result set to NULL. */ |
| 8225 | 8078 | return; |
| 8226 | 8079 | } |
| 8227 | 8080 | fseek(in, 0, SEEK_END); |
| @@ -8472,11 +8325,11 @@ | ||
| 8472 | 8325 | } |
| 8473 | 8326 | }else{ |
| 8474 | 8327 | sqlite3_int64 nWrite = 0; |
| 8475 | 8328 | const char *z; |
| 8476 | 8329 | int rc = 0; |
| 8477 | - FILE *out = fopen(zFile, "wb"); | |
| 8330 | + FILE *out = sqlite3_fopen(zFile, "wb"); | |
| 8478 | 8331 | if( out==0 ) return 1; |
| 8479 | 8332 | z = (const char*)sqlite3_value_blob(pData); |
| 8480 | 8333 | if( z ){ |
| 8481 | 8334 | sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out); |
| 8482 | 8335 | nWrite = sqlite3_value_bytes(pData); |
| @@ -10333,10 +10186,18 @@ | ||
| 10333 | 10186 | #ifndef SQLITE_NO_STDINT |
| 10334 | 10187 | # include <stdint.h> |
| 10335 | 10188 | #endif |
| 10336 | 10189 | |
| 10337 | 10190 | #include <zlib.h> |
| 10191 | + | |
| 10192 | +/* When used as part of the CLI, the sqlite3_stdio.h module will have | |
| 10193 | +** been included before this one. In that case use the sqlite3_stdio.h | |
| 10194 | +** #defines. If not, create our own for fopen(). | |
| 10195 | +*/ | |
| 10196 | +#ifndef _SQLITE3_STDIO_H_ | |
| 10197 | +# define sqlite3_fopen fopen | |
| 10198 | +#endif | |
| 10338 | 10199 | |
| 10339 | 10200 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 10340 | 10201 | |
| 10341 | 10202 | #ifndef SQLITE_AMALGAMATION |
| 10342 | 10203 | |
| @@ -11590,11 +11451,11 @@ | ||
| 11590 | 11451 | }else{ |
| 11591 | 11452 | zFile = (const char*)sqlite3_value_text(argv[0]); |
| 11592 | 11453 | } |
| 11593 | 11454 | |
| 11594 | 11455 | if( 0==pTab->pWriteFd && 0==bInMemory ){ |
| 11595 | - pCsr->pFile = zFile ? fopen(zFile, "rb") : 0; | |
| 11456 | + pCsr->pFile = zFile ? sqlite3_fopen(zFile, "rb") : 0; | |
| 11596 | 11457 | if( pCsr->pFile==0 ){ |
| 11597 | 11458 | zipfileCursorErr(pCsr, "cannot open file: %s", zFile); |
| 11598 | 11459 | rc = SQLITE_ERROR; |
| 11599 | 11460 | }else{ |
| 11600 | 11461 | rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd); |
| @@ -11780,11 +11641,11 @@ | ||
| 11780 | 11641 | |
| 11781 | 11642 | /* Open a write fd on the file. Also load the entire central directory |
| 11782 | 11643 | ** structure into memory. During the transaction any new file data is |
| 11783 | 11644 | ** appended to the archive file, but the central directory is accumulated |
| 11784 | 11645 | ** in main-memory until the transaction is committed. */ |
| 11785 | - pTab->pWriteFd = fopen(pTab->zFile, "ab+"); | |
| 11646 | + pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+"); | |
| 11786 | 11647 | if( pTab->pWriteFd==0 ){ |
| 11787 | 11648 | pTab->base.zErrMsg = sqlite3_mprintf( |
| 11788 | 11649 | "zipfile: failed to open file %s for writing", pTab->zFile |
| 11789 | 11650 | ); |
| 11790 | 11651 | rc = SQLITE_ERROR; |
| @@ -14233,10 +14094,70 @@ | ||
| 14233 | 14094 | } |
| 14234 | 14095 | |
| 14235 | 14096 | return rc; |
| 14236 | 14097 | } |
| 14237 | 14098 | |
| 14099 | +/* | |
| 14100 | +** This function tests if the schema of the main database of database handle | |
| 14101 | +** db contains an object named zTab. Assuming no error occurs, output parameter | |
| 14102 | +** (*pbContains) is set to true if zTab exists, or false if it does not. | |
| 14103 | +** | |
| 14104 | +** Or, if an error occurs, an SQLite error code is returned. The final value | |
| 14105 | +** of (*pbContains) is undefined in this case. | |
| 14106 | +*/ | |
| 14107 | +static int expertDbContainsObject( | |
| 14108 | + sqlite3 *db, | |
| 14109 | + const char *zTab, | |
| 14110 | + int *pbContains /* OUT: True if object exists */ | |
| 14111 | +){ | |
| 14112 | + const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?"; | |
| 14113 | + sqlite3_stmt *pSql = 0; | |
| 14114 | + int rc = SQLITE_OK; | |
| 14115 | + int ret = 0; | |
| 14116 | + | |
| 14117 | + rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); | |
| 14118 | + if( rc==SQLITE_OK ){ | |
| 14119 | + sqlite3_bind_text(pSql, 1, zTab, -1, SQLITE_STATIC); | |
| 14120 | + if( SQLITE_ROW==sqlite3_step(pSql) ){ | |
| 14121 | + ret = 1; | |
| 14122 | + } | |
| 14123 | + rc = sqlite3_finalize(pSql); | |
| 14124 | + } | |
| 14125 | + | |
| 14126 | + *pbContains = ret; | |
| 14127 | + return rc; | |
| 14128 | +} | |
| 14129 | + | |
| 14130 | +/* | |
| 14131 | +** Execute SQL command zSql using database handle db. If no error occurs, | |
| 14132 | +** set (*pzErr) to NULL and return SQLITE_OK. | |
| 14133 | +** | |
| 14134 | +** If an error does occur, return an SQLite error code and set (*pzErr) to | |
| 14135 | +** point to a buffer containing an English language error message. Except, | |
| 14136 | +** if the error message begins with "no such module:", then ignore the | |
| 14137 | +** error and return as if the SQL statement had succeeded. | |
| 14138 | +** | |
| 14139 | +** This is used to copy as much of the database schema as possible while | |
| 14140 | +** ignoring any errors related to missing virtual table modules. | |
| 14141 | +*/ | |
| 14142 | +static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){ | |
| 14143 | + int rc = SQLITE_OK; | |
| 14144 | + char *zErr = 0; | |
| 14145 | + | |
| 14146 | + rc = sqlite3_exec(db, zSql, 0, 0, &zErr); | |
| 14147 | + if( rc!=SQLITE_OK && zErr ){ | |
| 14148 | + int nErr = STRLEN(zErr); | |
| 14149 | + if( nErr>=15 && memcmp(zErr, "no such module:", 15)==0 ){ | |
| 14150 | + sqlite3_free(zErr); | |
| 14151 | + rc = SQLITE_OK; | |
| 14152 | + zErr = 0; | |
| 14153 | + } | |
| 14154 | + } | |
| 14155 | + | |
| 14156 | + *pzErr = zErr; | |
| 14157 | + return rc; | |
| 14158 | +} | |
| 14238 | 14159 | |
| 14239 | 14160 | static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){ |
| 14240 | 14161 | int rc = idxRegisterVtab(p); |
| 14241 | 14162 | sqlite3_stmt *pSchema = 0; |
| 14242 | 14163 | |
| @@ -14244,30 +14165,39 @@ | ||
| 14244 | 14165 | ** |
| 14245 | 14166 | ** 1) Add an entry to the p->pTable list, and |
| 14246 | 14167 | ** 2) Create the equivalent virtual table in dbv. |
| 14247 | 14168 | */ |
| 14248 | 14169 | rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg, |
| 14249 | - "SELECT type, name, sql, 1 FROM sqlite_schema " | |
| 14250 | - "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' " | |
| 14170 | + "SELECT type, name, sql, 1, " | |
| 14171 | + " substr(sql,1,14)=='create virtual' COLLATE nocase " | |
| 14172 | + "FROM sqlite_schema " | |
| 14173 | + "WHERE type IN ('table','view') AND " | |
| 14174 | + " substr(name,1,7)!='sqlite_' COLLATE nocase " | |
| 14251 | 14175 | " UNION ALL " |
| 14252 | - "SELECT type, name, sql, 2 FROM sqlite_schema " | |
| 14176 | + "SELECT type, name, sql, 2, 0 FROM sqlite_schema " | |
| 14253 | 14177 | "WHERE type = 'trigger'" |
| 14254 | 14178 | " AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') " |
| 14255 | - "ORDER BY 4, 1" | |
| 14179 | + "ORDER BY 4, 5 DESC, 1" | |
| 14256 | 14180 | ); |
| 14257 | 14181 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){ |
| 14258 | 14182 | const char *zType = (const char*)sqlite3_column_text(pSchema, 0); |
| 14259 | 14183 | const char *zName = (const char*)sqlite3_column_text(pSchema, 1); |
| 14260 | 14184 | const char *zSql = (const char*)sqlite3_column_text(pSchema, 2); |
| 14185 | + int bVirtual = sqlite3_column_int(pSchema, 4); | |
| 14186 | + int bExists = 0; | |
| 14261 | 14187 | |
| 14262 | 14188 | if( zType==0 || zName==0 ) continue; |
| 14263 | - if( zType[0]=='v' || zType[1]=='r' ){ | |
| 14264 | - if( zSql ) rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg); | |
| 14189 | + rc = expertDbContainsObject(p->dbv, zName, &bExists); | |
| 14190 | + if( rc || bExists ) continue; | |
| 14191 | + | |
| 14192 | + if( zType[0]=='v' || zType[1]=='r' || bVirtual ){ | |
| 14193 | + /* A view. Or a trigger on a view. */ | |
| 14194 | + if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg); | |
| 14265 | 14195 | }else{ |
| 14266 | 14196 | IdxTable *pTab; |
| 14267 | 14197 | rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg); |
| 14268 | - if( rc==SQLITE_OK ){ | |
| 14198 | + if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){ | |
| 14269 | 14199 | int i; |
| 14270 | 14200 | char *zInner = 0; |
| 14271 | 14201 | char *zOuter = 0; |
| 14272 | 14202 | pTab->pNext = p->pTable; |
| 14273 | 14203 | p->pTable = pTab; |
| @@ -14464,10 +14394,16 @@ | ||
| 14464 | 14394 | sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC); |
| 14465 | 14395 | while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){ |
| 14466 | 14396 | const char *zComma = zCols==0 ? "" : ", "; |
| 14467 | 14397 | const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0); |
| 14468 | 14398 | const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1); |
| 14399 | + if( zName==0 ){ | |
| 14400 | + /* This index contains an expression. Ignore it. */ | |
| 14401 | + sqlite3_free(zCols); | |
| 14402 | + sqlite3_free(zOrder); | |
| 14403 | + return sqlite3_reset(pIndexXInfo); | |
| 14404 | + } | |
| 14469 | 14405 | zCols = idxAppendText(&rc, zCols, |
| 14470 | 14406 | "%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s", |
| 14471 | 14407 | zComma, zName, nCol, zName, zColl |
| 14472 | 14408 | ); |
| 14473 | 14409 | zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol); |
| @@ -14792,16 +14728,22 @@ | ||
| 14792 | 14728 | |
| 14793 | 14729 | /* Copy the entire schema of database [db] into [dbm]. */ |
| 14794 | 14730 | if( rc==SQLITE_OK ){ |
| 14795 | 14731 | sqlite3_stmt *pSql = 0; |
| 14796 | 14732 | rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, |
| 14797 | - "SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'" | |
| 14798 | - " AND sql NOT LIKE 'CREATE VIRTUAL %%' ORDER BY rowid" | |
| 14733 | + "SELECT sql, name, substr(sql,1,14)=='create virtual' COLLATE nocase" | |
| 14734 | + " FROM sqlite_schema WHERE substr(name,1,7)!='sqlite_' COLLATE nocase" | |
| 14735 | + " ORDER BY 3 DESC, rowid" | |
| 14799 | 14736 | ); |
| 14800 | 14737 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 14801 | 14738 | const char *zSql = (const char*)sqlite3_column_text(pSql, 0); |
| 14802 | - if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg); | |
| 14739 | + const char *zName = (const char*)sqlite3_column_text(pSql, 1); | |
| 14740 | + int bExists = 0; | |
| 14741 | + rc = expertDbContainsObject(pNew->dbm, zName, &bExists); | |
| 14742 | + if( rc==SQLITE_OK && zSql && bExists==0 ){ | |
| 14743 | + rc = expertSchemaSql(pNew->dbm, zSql, pzErrmsg); | |
| 14744 | + } | |
| 14803 | 14745 | } |
| 14804 | 14746 | idxFinalize(&rc, pSql); |
| 14805 | 14747 | } |
| 14806 | 14748 | |
| 14807 | 14749 | /* Create the vtab schema */ |
| @@ -16211,10 +16153,1197 @@ | ||
| 16211 | 16153 | } |
| 16212 | 16154 | return rc; |
| 16213 | 16155 | } |
| 16214 | 16156 | |
| 16215 | 16157 | /************************* End ../ext/misc/stmtrand.c ********************/ |
| 16158 | +/************************* Begin ../ext/misc/vfstrace.c ******************/ | |
| 16159 | +/* | |
| 16160 | +** 2011 March 16 | |
| 16161 | +** | |
| 16162 | +** The author disclaims copyright to this source code. In place of | |
| 16163 | +** a legal notice, here is a blessing: | |
| 16164 | +** | |
| 16165 | +** May you do good and not evil. | |
| 16166 | +** May you find forgiveness for yourself and forgive others. | |
| 16167 | +** May you share freely, never taking more than you give. | |
| 16168 | +** | |
| 16169 | +****************************************************************************** | |
| 16170 | +** | |
| 16171 | +** This file contains code implements a VFS shim that writes diagnostic | |
| 16172 | +** output for each VFS call, similar to "strace". | |
| 16173 | +** | |
| 16174 | +** USAGE: | |
| 16175 | +** | |
| 16176 | +** This source file exports a single symbol which is the name of a | |
| 16177 | +** function: | |
| 16178 | +** | |
| 16179 | +** int vfstrace_register( | |
| 16180 | +** const char *zTraceName, // Name of the newly constructed VFS | |
| 16181 | +** const char *zOldVfsName, // Name of the underlying VFS | |
| 16182 | +** int (*xOut)(const char*,void*), // Output routine. ex: fputs | |
| 16183 | +** void *pOutArg, // 2nd argument to xOut. ex: stderr | |
| 16184 | +** int makeDefault // Make the new VFS the default | |
| 16185 | +** ); | |
| 16186 | +** | |
| 16187 | +** Applications that want to trace their VFS usage must provide a callback | |
| 16188 | +** function with this prototype: | |
| 16189 | +** | |
| 16190 | +** int traceOutput(const char *zMessage, void *pAppData); | |
| 16191 | +** | |
| 16192 | +** This function will "output" the trace messages, where "output" can | |
| 16193 | +** mean different things to different applications. The traceOutput function | |
| 16194 | +** for the command-line shell (see shell.c) is "fputs" from the standard | |
| 16195 | +** library, which means that all trace output is written on the stream | |
| 16196 | +** specified by the second argument. In the case of the command-line shell | |
| 16197 | +** the second argument is stderr. Other applications might choose to output | |
| 16198 | +** trace information to a file, over a socket, or write it into a buffer. | |
| 16199 | +** | |
| 16200 | +** The vfstrace_register() function creates a new "shim" VFS named by | |
| 16201 | +** the zTraceName parameter. A "shim" VFS is an SQLite backend that does | |
| 16202 | +** not really perform the duties of a true backend, but simply filters or | |
| 16203 | +** interprets VFS calls before passing them off to another VFS which does | |
| 16204 | +** the actual work. In this case the other VFS - the one that does the | |
| 16205 | +** real work - is identified by the second parameter, zOldVfsName. If | |
| 16206 | +** the 2nd parameter is NULL then the default VFS is used. The common | |
| 16207 | +** case is for the 2nd parameter to be NULL. | |
| 16208 | +** | |
| 16209 | +** The third and fourth parameters are the pointer to the output function | |
| 16210 | +** and the second argument to the output function. For the SQLite | |
| 16211 | +** command-line shell, when the -vfstrace option is used, these parameters | |
| 16212 | +** are fputs and stderr, respectively. | |
| 16213 | +** | |
| 16214 | +** The fifth argument is true (non-zero) to cause the newly created VFS | |
| 16215 | +** to become the default VFS. The common case is for the fifth parameter | |
| 16216 | +** to be true. | |
| 16217 | +** | |
| 16218 | +** The call to vfstrace_register() simply creates the shim VFS that does | |
| 16219 | +** tracing. The application must also arrange to use the new VFS for | |
| 16220 | +** all database connections that are created and for which tracing is | |
| 16221 | +** desired. This can be done by specifying the trace VFS using URI filename | |
| 16222 | +** notation, or by specifying the trace VFS as the 4th parameter to | |
| 16223 | +** sqlite3_open_v2() or by making the trace VFS be the default (by setting | |
| 16224 | +** the 5th parameter of vfstrace_register() to 1). | |
| 16225 | +** | |
| 16226 | +** | |
| 16227 | +** ENABLING VFSTRACE IN A COMMAND-LINE SHELL | |
| 16228 | +** | |
| 16229 | +** The SQLite command line shell implemented by the shell.c source file | |
| 16230 | +** can be used with this module. To compile in -vfstrace support, first | |
| 16231 | +** gather this file (test_vfstrace.c), the shell source file (shell.c), | |
| 16232 | +** and the SQLite amalgamation source files (sqlite3.c, sqlite3.h) into | |
| 16233 | +** the working directory. Then compile using a command like the following: | |
| 16234 | +** | |
| 16235 | +** gcc -o sqlite3 -Os -I. -DSQLITE_ENABLE_VFSTRACE \ | |
| 16236 | +** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE \ | |
| 16237 | +** -DHAVE_READLINE -DHAVE_USLEEP=1 \ | |
| 16238 | +** shell.c test_vfstrace.c sqlite3.c -ldl -lreadline -lncurses | |
| 16239 | +** | |
| 16240 | +** The gcc command above works on Linux and provides (in addition to the | |
| 16241 | +** -vfstrace option) support for FTS3 and FTS4, RTREE, and command-line | |
| 16242 | +** editing using the readline library. The command-line shell does not | |
| 16243 | +** use threads so we added -DSQLITE_THREADSAFE=0 just to make the code | |
| 16244 | +** run a little faster. For compiling on a Mac, you'll probably need | |
| 16245 | +** to omit the -DHAVE_READLINE, the -lreadline, and the -lncurses options. | |
| 16246 | +** The compilation could be simplified to just this: | |
| 16247 | +** | |
| 16248 | +** gcc -DSQLITE_ENABLE_VFSTRACE \ | |
| 16249 | +** shell.c test_vfstrace.c sqlite3.c -ldl -lpthread | |
| 16250 | +** | |
| 16251 | +** In this second example, all unnecessary options have been removed | |
| 16252 | +** Note that since the code is now threadsafe, we had to add the -lpthread | |
| 16253 | +** option to pull in the pthreads library. | |
| 16254 | +** | |
| 16255 | +** To cross-compile for windows using MinGW, a command like this might | |
| 16256 | +** work: | |
| 16257 | +** | |
| 16258 | +** /opt/mingw/bin/i386-mingw32msvc-gcc -o sqlite3.exe -Os -I \ | |
| 16259 | +** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_VFSTRACE \ | |
| 16260 | +** shell.c test_vfstrace.c sqlite3.c | |
| 16261 | +** | |
| 16262 | +** Similar compiler commands will work on different systems. The key | |
| 16263 | +** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that | |
| 16264 | +** the shell.c source file will know to include the -vfstrace command-line | |
| 16265 | +** option and (2) you must compile and link the three source files | |
| 16266 | +** shell,c, test_vfstrace.c, and sqlite3.c. | |
| 16267 | +** | |
| 16268 | +** RUNTIME CONTROL OF VFSTRACE OUTPUT | |
| 16269 | +** | |
| 16270 | +** The application can use the "vfstrace" pragma to control which VFS | |
| 16271 | +** APIs are traced. To disable all output: | |
| 16272 | +** | |
| 16273 | +** PRAGMA vfstrace('-all'); | |
| 16274 | +** | |
| 16275 | +** To enable all output (which is the default setting): | |
| 16276 | +** | |
| 16277 | +** PRAGMA vfstrace('+all'); | |
| 16278 | +** | |
| 16279 | +** Individual APIs can be enabled or disabled by name, with or without | |
| 16280 | +** the initial "x" character. For example, to set up for tracing lock | |
| 16281 | +** primatives only: | |
| 16282 | +** | |
| 16283 | +** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock'); | |
| 16284 | +** | |
| 16285 | +** The argument to the vfstrace pragma ignores capitalization and any | |
| 16286 | +** characters other than alphabetics, '+', and '-'. | |
| 16287 | +*/ | |
| 16288 | +#include <stdlib.h> | |
| 16289 | +#include <string.h> | |
| 16290 | +/* #include "sqlite3.h" */ | |
| 16291 | + | |
| 16292 | +/* | |
| 16293 | +** An instance of this structure is attached to the each trace VFS to | |
| 16294 | +** provide auxiliary information. | |
| 16295 | +*/ | |
| 16296 | +typedef struct vfstrace_info vfstrace_info; | |
| 16297 | +struct vfstrace_info { | |
| 16298 | + sqlite3_vfs *pRootVfs; /* The underlying real VFS */ | |
| 16299 | + int (*xOut)(const char*, void*); /* Send output here */ | |
| 16300 | + unsigned int mTrace; /* Mask of interfaces to trace */ | |
| 16301 | + u8 bOn; /* Tracing on/off */ | |
| 16302 | + void *pOutArg; /* First argument to xOut */ | |
| 16303 | + const char *zVfsName; /* Name of this trace-VFS */ | |
| 16304 | + sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */ | |
| 16305 | +}; | |
| 16306 | + | |
| 16307 | +/* | |
| 16308 | +** The sqlite3_file object for the trace VFS | |
| 16309 | +*/ | |
| 16310 | +typedef struct vfstrace_file vfstrace_file; | |
| 16311 | +struct vfstrace_file { | |
| 16312 | + sqlite3_file base; /* Base class. Must be first */ | |
| 16313 | + vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */ | |
| 16314 | + const char *zFName; /* Base name of the file */ | |
| 16315 | + sqlite3_file *pReal; /* The real underlying file */ | |
| 16316 | +}; | |
| 16317 | + | |
| 16318 | +/* | |
| 16319 | +** Bit values for vfstrace_info.mTrace. | |
| 16320 | +*/ | |
| 16321 | +#define VTR_CLOSE 0x00000001 | |
| 16322 | +#define VTR_READ 0x00000002 | |
| 16323 | +#define VTR_WRITE 0x00000004 | |
| 16324 | +#define VTR_TRUNC 0x00000008 | |
| 16325 | +#define VTR_SYNC 0x00000010 | |
| 16326 | +#define VTR_FSIZE 0x00000020 | |
| 16327 | +#define VTR_LOCK 0x00000040 | |
| 16328 | +#define VTR_UNLOCK 0x00000080 | |
| 16329 | +#define VTR_CRL 0x00000100 | |
| 16330 | +#define VTR_FCTRL 0x00000200 | |
| 16331 | +#define VTR_SECSZ 0x00000400 | |
| 16332 | +#define VTR_DEVCHAR 0x00000800 | |
| 16333 | +#define VTR_SHMLOCK 0x00001000 | |
| 16334 | +#define VTR_SHMMAP 0x00002000 | |
| 16335 | +#define VTR_SHMBAR 0x00004000 | |
| 16336 | +#define VTR_SHMUNMAP 0x00008000 | |
| 16337 | +#define VTR_OPEN 0x00010000 | |
| 16338 | +#define VTR_DELETE 0x00020000 | |
| 16339 | +#define VTR_ACCESS 0x00040000 | |
| 16340 | +#define VTR_FULLPATH 0x00080000 | |
| 16341 | +#define VTR_DLOPEN 0x00100000 | |
| 16342 | +#define VTR_DLERR 0x00200000 | |
| 16343 | +#define VTR_DLSYM 0x00400000 | |
| 16344 | +#define VTR_DLCLOSE 0x00800000 | |
| 16345 | +#define VTR_RAND 0x01000000 | |
| 16346 | +#define VTR_SLEEP 0x02000000 | |
| 16347 | +#define VTR_CURTIME 0x04000000 | |
| 16348 | +#define VTR_LASTERR 0x08000000 | |
| 16349 | + | |
| 16350 | +/* | |
| 16351 | +** Method declarations for vfstrace_file. | |
| 16352 | +*/ | |
| 16353 | +static int vfstraceClose(sqlite3_file*); | |
| 16354 | +static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); | |
| 16355 | +static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); | |
| 16356 | +static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size); | |
| 16357 | +static int vfstraceSync(sqlite3_file*, int flags); | |
| 16358 | +static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize); | |
| 16359 | +static int vfstraceLock(sqlite3_file*, int); | |
| 16360 | +static int vfstraceUnlock(sqlite3_file*, int); | |
| 16361 | +static int vfstraceCheckReservedLock(sqlite3_file*, int *); | |
| 16362 | +static int vfstraceFileControl(sqlite3_file*, int op, void *pArg); | |
| 16363 | +static int vfstraceSectorSize(sqlite3_file*); | |
| 16364 | +static int vfstraceDeviceCharacteristics(sqlite3_file*); | |
| 16365 | +static int vfstraceShmLock(sqlite3_file*,int,int,int); | |
| 16366 | +static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **); | |
| 16367 | +static void vfstraceShmBarrier(sqlite3_file*); | |
| 16368 | +static int vfstraceShmUnmap(sqlite3_file*,int); | |
| 16369 | + | |
| 16370 | +/* | |
| 16371 | +** Method declarations for vfstrace_vfs. | |
| 16372 | +*/ | |
| 16373 | +static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); | |
| 16374 | +static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir); | |
| 16375 | +static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *); | |
| 16376 | +static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *); | |
| 16377 | +static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename); | |
| 16378 | +static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg); | |
| 16379 | +static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); | |
| 16380 | +static void vfstraceDlClose(sqlite3_vfs*, void*); | |
| 16381 | +static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut); | |
| 16382 | +static int vfstraceSleep(sqlite3_vfs*, int microseconds); | |
| 16383 | +static int vfstraceCurrentTime(sqlite3_vfs*, double*); | |
| 16384 | +static int vfstraceGetLastError(sqlite3_vfs*, int, char*); | |
| 16385 | +static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); | |
| 16386 | +static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr); | |
| 16387 | +static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *); | |
| 16388 | +static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName); | |
| 16389 | + | |
| 16390 | +/* | |
| 16391 | +** Return a pointer to the tail of the pathname. Examples: | |
| 16392 | +** | |
| 16393 | +** /home/drh/xyzzy.txt -> xyzzy.txt | |
| 16394 | +** xyzzy.txt -> xyzzy.txt | |
| 16395 | +*/ | |
| 16396 | +static const char *fileTail(const char *z){ | |
| 16397 | + size_t i; | |
| 16398 | + if( z==0 ) return 0; | |
| 16399 | + i = strlen(z)-1; | |
| 16400 | + while( i>0 && z[i-1]!='/' ){ i--; } | |
| 16401 | + return &z[i]; | |
| 16402 | +} | |
| 16403 | + | |
| 16404 | +/* | |
| 16405 | +** Send trace output defined by zFormat and subsequent arguments. | |
| 16406 | +*/ | |
| 16407 | +static void vfstrace_printf( | |
| 16408 | + vfstrace_info *pInfo, | |
| 16409 | + const char *zFormat, | |
| 16410 | + ... | |
| 16411 | +){ | |
| 16412 | + va_list ap; | |
| 16413 | + char *zMsg; | |
| 16414 | + if( pInfo->bOn ){ | |
| 16415 | + va_start(ap, zFormat); | |
| 16416 | + zMsg = sqlite3_vmprintf(zFormat, ap); | |
| 16417 | + va_end(ap); | |
| 16418 | + pInfo->xOut(zMsg, pInfo->pOutArg); | |
| 16419 | + sqlite3_free(zMsg); | |
| 16420 | + } | |
| 16421 | +} | |
| 16422 | + | |
| 16423 | +/* | |
| 16424 | +** Try to convert an error code into a symbolic name for that error code. | |
| 16425 | +*/ | |
| 16426 | +static const char *vfstrace_errcode_name(int rc ){ | |
| 16427 | + const char *zVal = 0; | |
| 16428 | + switch( rc ){ | |
| 16429 | + case SQLITE_OK: zVal = "SQLITE_OK"; break; | |
| 16430 | + case SQLITE_INTERNAL: zVal = "SQLITE_INTERNAL"; break; | |
| 16431 | + case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break; | |
| 16432 | + case SQLITE_PERM: zVal = "SQLITE_PERM"; break; | |
| 16433 | + case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break; | |
| 16434 | + case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break; | |
| 16435 | + case SQLITE_LOCKED: zVal = "SQLITE_LOCKED"; break; | |
| 16436 | + case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break; | |
| 16437 | + case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break; | |
| 16438 | + case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break; | |
| 16439 | + case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break; | |
| 16440 | + case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break; | |
| 16441 | + case SQLITE_NOTFOUND: zVal = "SQLITE_NOTFOUND"; break; | |
| 16442 | + case SQLITE_FULL: zVal = "SQLITE_FULL"; break; | |
| 16443 | + case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break; | |
| 16444 | + case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break; | |
| 16445 | + case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break; | |
| 16446 | + case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break; | |
| 16447 | + case SQLITE_TOOBIG: zVal = "SQLITE_TOOBIG"; break; | |
| 16448 | + case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break; | |
| 16449 | + case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break; | |
| 16450 | + case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break; | |
| 16451 | + case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break; | |
| 16452 | + case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break; | |
| 16453 | + case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break; | |
| 16454 | + case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break; | |
| 16455 | + case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break; | |
| 16456 | + case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break; | |
| 16457 | + case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break; | |
| 16458 | + case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break; | |
| 16459 | + case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break; | |
| 16460 | + case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break; | |
| 16461 | + case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break; | |
| 16462 | + case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break; | |
| 16463 | + case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break; | |
| 16464 | + case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break; | |
| 16465 | + case SQLITE_IOERR_CHECKRESERVEDLOCK: | |
| 16466 | + zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; | |
| 16467 | + case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break; | |
| 16468 | + case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break; | |
| 16469 | + case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break; | |
| 16470 | + case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break; | |
| 16471 | + case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break; | |
| 16472 | + case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break; | |
| 16473 | + case SQLITE_IOERR_SHMMAP: zVal = "SQLITE_IOERR_SHMMAP"; break; | |
| 16474 | + case SQLITE_IOERR_SEEK: zVal = "SQLITE_IOERR_SEEK"; break; | |
| 16475 | + case SQLITE_IOERR_GETTEMPPATH: zVal = "SQLITE_IOERR_GETTEMPPATH"; break; | |
| 16476 | + case SQLITE_IOERR_CONVPATH: zVal = "SQLITE_IOERR_CONVPATH"; break; | |
| 16477 | + case SQLITE_READONLY_DBMOVED: zVal = "SQLITE_READONLY_DBMOVED"; break; | |
| 16478 | + case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break; | |
| 16479 | + case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break; | |
| 16480 | + case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break; | |
| 16481 | + } | |
| 16482 | + return zVal; | |
| 16483 | +} | |
| 16484 | + | |
| 16485 | +/* | |
| 16486 | +** Convert value rc into a string and print it using zFormat. zFormat | |
| 16487 | +** should have exactly one %s | |
| 16488 | +*/ | |
| 16489 | +static void vfstrace_print_errcode( | |
| 16490 | + vfstrace_info *pInfo, | |
| 16491 | + const char *zFormat, | |
| 16492 | + int rc | |
| 16493 | +){ | |
| 16494 | + const char *zVal; | |
| 16495 | + char zBuf[50]; | |
| 16496 | + zVal = vfstrace_errcode_name(rc); | |
| 16497 | + if( zVal==0 ){ | |
| 16498 | + zVal = vfstrace_errcode_name(rc&0xff); | |
| 16499 | + if( zVal ){ | |
| 16500 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%s | 0x%x", zVal, rc&0xffff00); | |
| 16501 | + }else{ | |
| 16502 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d (0x%x)", rc, rc); | |
| 16503 | + } | |
| 16504 | + zVal = zBuf; | |
| 16505 | + } | |
| 16506 | + vfstrace_printf(pInfo, zFormat, zVal); | |
| 16507 | +} | |
| 16508 | + | |
| 16509 | +/* | |
| 16510 | +** Append to a buffer. | |
| 16511 | +*/ | |
| 16512 | +static void strappend(char *z, int *pI, const char *zAppend){ | |
| 16513 | + int i = *pI; | |
| 16514 | + while( zAppend[0] ){ z[i++] = *(zAppend++); } | |
| 16515 | + z[i] = 0; | |
| 16516 | + *pI = i; | |
| 16517 | +} | |
| 16518 | + | |
| 16519 | +/* | |
| 16520 | +** Turn tracing output on or off according to mMask. | |
| 16521 | +*/ | |
| 16522 | +static void vfstraceOnOff(vfstrace_info *pInfo, unsigned int mMask){ | |
| 16523 | + pInfo->bOn = (pInfo->mTrace & mMask)!=0; | |
| 16524 | +} | |
| 16525 | + | |
| 16526 | +/* | |
| 16527 | +** Close an vfstrace-file. | |
| 16528 | +*/ | |
| 16529 | +static int vfstraceClose(sqlite3_file *pFile){ | |
| 16530 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16531 | + vfstrace_info *pInfo = p->pInfo; | |
| 16532 | + int rc; | |
| 16533 | + vfstraceOnOff(pInfo, VTR_CLOSE); | |
| 16534 | + vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName); | |
| 16535 | + rc = p->pReal->pMethods->xClose(p->pReal); | |
| 16536 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16537 | + if( rc==SQLITE_OK ){ | |
| 16538 | + sqlite3_free((void*)p->base.pMethods); | |
| 16539 | + p->base.pMethods = 0; | |
| 16540 | + } | |
| 16541 | + return rc; | |
| 16542 | +} | |
| 16543 | + | |
| 16544 | +/* | |
| 16545 | +** Read data from an vfstrace-file. | |
| 16546 | +*/ | |
| 16547 | +static int vfstraceRead( | |
| 16548 | + sqlite3_file *pFile, | |
| 16549 | + void *zBuf, | |
| 16550 | + int iAmt, | |
| 16551 | + sqlite_int64 iOfst | |
| 16552 | +){ | |
| 16553 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16554 | + vfstrace_info *pInfo = p->pInfo; | |
| 16555 | + int rc; | |
| 16556 | + vfstraceOnOff(pInfo, VTR_READ); | |
| 16557 | + vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)", | |
| 16558 | + pInfo->zVfsName, p->zFName, iAmt, iOfst); | |
| 16559 | + rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); | |
| 16560 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16561 | + return rc; | |
| 16562 | +} | |
| 16563 | + | |
| 16564 | +/* | |
| 16565 | +** Write data to an vfstrace-file. | |
| 16566 | +*/ | |
| 16567 | +static int vfstraceWrite( | |
| 16568 | + sqlite3_file *pFile, | |
| 16569 | + const void *zBuf, | |
| 16570 | + int iAmt, | |
| 16571 | + sqlite_int64 iOfst | |
| 16572 | +){ | |
| 16573 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16574 | + vfstrace_info *pInfo = p->pInfo; | |
| 16575 | + int rc; | |
| 16576 | + vfstraceOnOff(pInfo, VTR_WRITE); | |
| 16577 | + vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)", | |
| 16578 | + pInfo->zVfsName, p->zFName, iAmt, iOfst); | |
| 16579 | + rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); | |
| 16580 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16581 | + return rc; | |
| 16582 | +} | |
| 16583 | + | |
| 16584 | +/* | |
| 16585 | +** Truncate an vfstrace-file. | |
| 16586 | +*/ | |
| 16587 | +static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){ | |
| 16588 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16589 | + vfstrace_info *pInfo = p->pInfo; | |
| 16590 | + int rc; | |
| 16591 | + vfstraceOnOff(pInfo, VTR_TRUNC); | |
| 16592 | + vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName, | |
| 16593 | + size); | |
| 16594 | + rc = p->pReal->pMethods->xTruncate(p->pReal, size); | |
| 16595 | + vfstrace_printf(pInfo, " -> %d\n", rc); | |
| 16596 | + return rc; | |
| 16597 | +} | |
| 16598 | + | |
| 16599 | +/* | |
| 16600 | +** Sync an vfstrace-file. | |
| 16601 | +*/ | |
| 16602 | +static int vfstraceSync(sqlite3_file *pFile, int flags){ | |
| 16603 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16604 | + vfstrace_info *pInfo = p->pInfo; | |
| 16605 | + int rc; | |
| 16606 | + int i; | |
| 16607 | + char zBuf[100]; | |
| 16608 | + memcpy(zBuf, "|0", 3); | |
| 16609 | + i = 0; | |
| 16610 | + if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL"); | |
| 16611 | + else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL"); | |
| 16612 | + if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY"); | |
| 16613 | + if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){ | |
| 16614 | + sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags); | |
| 16615 | + } | |
| 16616 | + vfstraceOnOff(pInfo, VTR_SYNC); | |
| 16617 | + vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName, | |
| 16618 | + &zBuf[1]); | |
| 16619 | + rc = p->pReal->pMethods->xSync(p->pReal, flags); | |
| 16620 | + vfstrace_printf(pInfo, " -> %d\n", rc); | |
| 16621 | + return rc; | |
| 16622 | +} | |
| 16623 | + | |
| 16624 | +/* | |
| 16625 | +** Return the current file-size of an vfstrace-file. | |
| 16626 | +*/ | |
| 16627 | +static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ | |
| 16628 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16629 | + vfstrace_info *pInfo = p->pInfo; | |
| 16630 | + int rc; | |
| 16631 | + vfstraceOnOff(pInfo, VTR_FSIZE); | |
| 16632 | + vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName); | |
| 16633 | + rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); | |
| 16634 | + vfstrace_print_errcode(pInfo, " -> %s,", rc); | |
| 16635 | + vfstrace_printf(pInfo, " size=%lld\n", *pSize); | |
| 16636 | + return rc; | |
| 16637 | +} | |
| 16638 | + | |
| 16639 | +/* | |
| 16640 | +** Return the name of a lock. | |
| 16641 | +*/ | |
| 16642 | +static const char *lockName(int eLock){ | |
| 16643 | + const char *azLockNames[] = { | |
| 16644 | + "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE" | |
| 16645 | + }; | |
| 16646 | + if( eLock<0 || eLock>=(int)(sizeof(azLockNames)/sizeof(azLockNames[0])) ){ | |
| 16647 | + return "???"; | |
| 16648 | + }else{ | |
| 16649 | + return azLockNames[eLock]; | |
| 16650 | + } | |
| 16651 | +} | |
| 16652 | + | |
| 16653 | +/* | |
| 16654 | +** Lock an vfstrace-file. | |
| 16655 | +*/ | |
| 16656 | +static int vfstraceLock(sqlite3_file *pFile, int eLock){ | |
| 16657 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16658 | + vfstrace_info *pInfo = p->pInfo; | |
| 16659 | + int rc; | |
| 16660 | + vfstraceOnOff(pInfo, VTR_LOCK); | |
| 16661 | + vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName, | |
| 16662 | + lockName(eLock)); | |
| 16663 | + rc = p->pReal->pMethods->xLock(p->pReal, eLock); | |
| 16664 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16665 | + return rc; | |
| 16666 | +} | |
| 16667 | + | |
| 16668 | +/* | |
| 16669 | +** Unlock an vfstrace-file. | |
| 16670 | +*/ | |
| 16671 | +static int vfstraceUnlock(sqlite3_file *pFile, int eLock){ | |
| 16672 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16673 | + vfstrace_info *pInfo = p->pInfo; | |
| 16674 | + int rc; | |
| 16675 | + vfstraceOnOff(pInfo, VTR_UNLOCK); | |
| 16676 | + vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName, | |
| 16677 | + lockName(eLock)); | |
| 16678 | + rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); | |
| 16679 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16680 | + return rc; | |
| 16681 | +} | |
| 16682 | + | |
| 16683 | +/* | |
| 16684 | +** Check if another file-handle holds a RESERVED lock on an vfstrace-file. | |
| 16685 | +*/ | |
| 16686 | +static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){ | |
| 16687 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16688 | + vfstrace_info *pInfo = p->pInfo; | |
| 16689 | + int rc; | |
| 16690 | + vfstraceOnOff(pInfo, VTR_CRL); | |
| 16691 | + vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)", | |
| 16692 | + pInfo->zVfsName, p->zFName); | |
| 16693 | + rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); | |
| 16694 | + vfstrace_print_errcode(pInfo, " -> %s", rc); | |
| 16695 | + vfstrace_printf(pInfo, ", out=%d\n", *pResOut); | |
| 16696 | + return rc; | |
| 16697 | +} | |
| 16698 | + | |
| 16699 | +/* | |
| 16700 | +** File control method. For custom operations on an vfstrace-file. | |
| 16701 | +*/ | |
| 16702 | +static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){ | |
| 16703 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16704 | + vfstrace_info *pInfo = p->pInfo; | |
| 16705 | + int rc; | |
| 16706 | + char zBuf[100]; | |
| 16707 | + char zBuf2[100]; | |
| 16708 | + char *zOp; | |
| 16709 | + char *zRVal = 0; | |
| 16710 | + vfstraceOnOff(pInfo, VTR_FCTRL); | |
| 16711 | + switch( op ){ | |
| 16712 | + case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break; | |
| 16713 | + case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break; | |
| 16714 | + case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break; | |
| 16715 | + case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break; | |
| 16716 | + case SQLITE_FCNTL_SIZE_HINT: { | |
| 16717 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld", | |
| 16718 | + *(sqlite3_int64*)pArg); | |
| 16719 | + zOp = zBuf; | |
| 16720 | + break; | |
| 16721 | + } | |
| 16722 | + case SQLITE_FCNTL_CHUNK_SIZE: { | |
| 16723 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg); | |
| 16724 | + zOp = zBuf; | |
| 16725 | + break; | |
| 16726 | + } | |
| 16727 | + case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break; | |
| 16728 | + case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break; | |
| 16729 | + case SQLITE_FCNTL_PERSIST_WAL: { | |
| 16730 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "PERSIST_WAL,%d", *(int*)pArg); | |
| 16731 | + zOp = zBuf; | |
| 16732 | + break; | |
| 16733 | + } | |
| 16734 | + case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break; | |
| 16735 | + case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break; | |
| 16736 | + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break; | |
| 16737 | + case SQLITE_FCNTL_PRAGMA: { | |
| 16738 | + const char *const* a = (const char*const*)pArg; | |
| 16739 | + if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){ | |
| 16740 | + const u8 *zArg = (const u8*)a[2]; | |
| 16741 | + if( zArg[0]>='0' && zArg[0]<=9 ){ | |
| 16742 | + pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0); | |
| 16743 | + }else{ | |
| 16744 | + static const struct { | |
| 16745 | + const char *z; | |
| 16746 | + unsigned int m; | |
| 16747 | + } aKw[] = { | |
| 16748 | + { "all", 0xffffffff }, | |
| 16749 | + { "close", VTR_CLOSE }, | |
| 16750 | + { "read", VTR_READ }, | |
| 16751 | + { "write", VTR_WRITE }, | |
| 16752 | + { "truncate", VTR_TRUNC }, | |
| 16753 | + { "sync", VTR_SYNC }, | |
| 16754 | + { "filesize", VTR_FSIZE }, | |
| 16755 | + { "lock", VTR_LOCK }, | |
| 16756 | + { "unlock", VTR_UNLOCK }, | |
| 16757 | + { "checkreservedlock", VTR_CRL }, | |
| 16758 | + { "filecontrol", VTR_FCTRL }, | |
| 16759 | + { "sectorsize", VTR_SECSZ }, | |
| 16760 | + { "devicecharacteristics", VTR_DEVCHAR }, | |
| 16761 | + { "shmlock", VTR_SHMLOCK }, | |
| 16762 | + { "shmmap", VTR_SHMMAP }, | |
| 16763 | + { "shmummap", VTR_SHMUNMAP }, | |
| 16764 | + { "shmbarrier", VTR_SHMBAR }, | |
| 16765 | + { "open", VTR_OPEN }, | |
| 16766 | + { "delete", VTR_DELETE }, | |
| 16767 | + { "access", VTR_ACCESS }, | |
| 16768 | + { "fullpathname", VTR_FULLPATH }, | |
| 16769 | + { "dlopen", VTR_DLOPEN }, | |
| 16770 | + { "dlerror", VTR_DLERR }, | |
| 16771 | + { "dlsym", VTR_DLSYM }, | |
| 16772 | + { "dlclose", VTR_DLCLOSE }, | |
| 16773 | + { "randomness", VTR_RAND }, | |
| 16774 | + { "sleep", VTR_SLEEP }, | |
| 16775 | + { "currenttime", VTR_CURTIME }, | |
| 16776 | + { "currenttimeint64", VTR_CURTIME }, | |
| 16777 | + { "getlasterror", VTR_LASTERR }, | |
| 16778 | + }; | |
| 16779 | + int onOff = 1; | |
| 16780 | + while( zArg[0] ){ | |
| 16781 | + int jj, n; | |
| 16782 | + while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+' | |
| 16783 | + && !isalpha(zArg[0]) ) zArg++; | |
| 16784 | + if( zArg[0]==0 ) break; | |
| 16785 | + if( zArg[0]=='-' ){ | |
| 16786 | + onOff = 0; | |
| 16787 | + zArg++; | |
| 16788 | + }else if( zArg[0]=='+' ){ | |
| 16789 | + onOff = 1; | |
| 16790 | + zArg++; | |
| 16791 | + } | |
| 16792 | + while( !isalpha(zArg[0]) ){ | |
| 16793 | + if( zArg[0]==0 ) break; | |
| 16794 | + zArg++; | |
| 16795 | + } | |
| 16796 | + if( zArg[0]=='x' && isalpha(zArg[1]) ) zArg++; | |
| 16797 | + for(n=0; isalpha(zArg[n]); n++){} | |
| 16798 | + for(jj=0; jj<(int)(sizeof(aKw)/sizeof(aKw[0])); jj++){ | |
| 16799 | + if( sqlite3_strnicmp(aKw[jj].z,(const char*)zArg,n)==0 ){ | |
| 16800 | + if( onOff ){ | |
| 16801 | + pInfo->mTrace |= aKw[jj].m; | |
| 16802 | + }else{ | |
| 16803 | + pInfo->mTrace &= ~aKw[jj].m; | |
| 16804 | + } | |
| 16805 | + break; | |
| 16806 | + } | |
| 16807 | + } | |
| 16808 | + zArg += n; | |
| 16809 | + } | |
| 16810 | + } | |
| 16811 | + } | |
| 16812 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]); | |
| 16813 | + zOp = zBuf; | |
| 16814 | + break; | |
| 16815 | + } | |
| 16816 | + case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break; | |
| 16817 | + case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break; | |
| 16818 | + case SQLITE_FCNTL_MMAP_SIZE: { | |
| 16819 | + sqlite3_int64 iMMap = *(sqlite3_int64*)pArg; | |
| 16820 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "MMAP_SIZE,%lld",iMMap); | |
| 16821 | + zOp = zBuf; | |
| 16822 | + break; | |
| 16823 | + } | |
| 16824 | + case SQLITE_FCNTL_TRACE: zOp = "TRACE"; break; | |
| 16825 | + case SQLITE_FCNTL_HAS_MOVED: zOp = "HAS_MOVED"; break; | |
| 16826 | + case SQLITE_FCNTL_SYNC: zOp = "SYNC"; break; | |
| 16827 | + case SQLITE_FCNTL_COMMIT_PHASETWO: zOp = "COMMIT_PHASETWO"; break; | |
| 16828 | + case SQLITE_FCNTL_WIN32_SET_HANDLE: zOp = "WIN32_SET_HANDLE"; break; | |
| 16829 | + case SQLITE_FCNTL_WAL_BLOCK: zOp = "WAL_BLOCK"; break; | |
| 16830 | + case SQLITE_FCNTL_ZIPVFS: zOp = "ZIPVFS"; break; | |
| 16831 | + case SQLITE_FCNTL_RBU: zOp = "RBU"; break; | |
| 16832 | + case SQLITE_FCNTL_VFS_POINTER: zOp = "VFS_POINTER"; break; | |
| 16833 | + case SQLITE_FCNTL_JOURNAL_POINTER: zOp = "JOURNAL_POINTER"; break; | |
| 16834 | + case SQLITE_FCNTL_WIN32_GET_HANDLE: zOp = "WIN32_GET_HANDLE"; break; | |
| 16835 | + case SQLITE_FCNTL_PDB: zOp = "PDB"; break; | |
| 16836 | + case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: zOp = "BEGIN_ATOMIC_WRITE"; break; | |
| 16837 | + case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: zOp = "COMMIT_ATOMIC_WRITE"; break; | |
| 16838 | + case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { | |
| 16839 | + zOp = "ROLLBACK_ATOMIC_WRITE"; | |
| 16840 | + break; | |
| 16841 | + } | |
| 16842 | + case SQLITE_FCNTL_LOCK_TIMEOUT: { | |
| 16843 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "LOCK_TIMEOUT,%d", *(int*)pArg); | |
| 16844 | + zOp = zBuf; | |
| 16845 | + break; | |
| 16846 | + } | |
| 16847 | + case SQLITE_FCNTL_DATA_VERSION: zOp = "DATA_VERSION"; break; | |
| 16848 | + case SQLITE_FCNTL_SIZE_LIMIT: zOp = "SIZE_LIMIT"; break; | |
| 16849 | + case SQLITE_FCNTL_CKPT_DONE: zOp = "CKPT_DONE"; break; | |
| 16850 | + case SQLITE_FCNTL_RESERVE_BYTES: zOp = "RESERVED_BYTES"; break; | |
| 16851 | + case SQLITE_FCNTL_CKPT_START: zOp = "CKPT_START"; break; | |
| 16852 | + case SQLITE_FCNTL_EXTERNAL_READER: zOp = "EXTERNAL_READER"; break; | |
| 16853 | + case SQLITE_FCNTL_CKSM_FILE: zOp = "CKSM_FILE"; break; | |
| 16854 | + case SQLITE_FCNTL_RESET_CACHE: zOp = "RESET_CACHE"; break; | |
| 16855 | + case 0xca093fa0: zOp = "DB_UNCHANGED"; break; | |
| 16856 | + default: { | |
| 16857 | + sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op); | |
| 16858 | + zOp = zBuf; | |
| 16859 | + break; | |
| 16860 | + } | |
| 16861 | + } | |
| 16862 | + vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)", | |
| 16863 | + pInfo->zVfsName, p->zFName, zOp); | |
| 16864 | + rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); | |
| 16865 | + if( rc==SQLITE_OK ){ | |
| 16866 | + switch( op ){ | |
| 16867 | + case SQLITE_FCNTL_VFSNAME: { | |
| 16868 | + *(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z", | |
| 16869 | + pInfo->zVfsName, *(char**)pArg); | |
| 16870 | + zRVal = *(char**)pArg; | |
| 16871 | + break; | |
| 16872 | + } | |
| 16873 | + case SQLITE_FCNTL_MMAP_SIZE: { | |
| 16874 | + sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%lld", *(sqlite3_int64*)pArg); | |
| 16875 | + zRVal = zBuf2; | |
| 16876 | + break; | |
| 16877 | + } | |
| 16878 | + case SQLITE_FCNTL_HAS_MOVED: | |
| 16879 | + case SQLITE_FCNTL_PERSIST_WAL: { | |
| 16880 | + sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%d", *(int*)pArg); | |
| 16881 | + zRVal = zBuf2; | |
| 16882 | + break; | |
| 16883 | + } | |
| 16884 | + case SQLITE_FCNTL_PRAGMA: | |
| 16885 | + case SQLITE_FCNTL_TEMPFILENAME: { | |
| 16886 | + zRVal = *(char**)pArg; | |
| 16887 | + break; | |
| 16888 | + } | |
| 16889 | + } | |
| 16890 | + } | |
| 16891 | + if( zRVal ){ | |
| 16892 | + vfstrace_print_errcode(pInfo, " -> %s", rc); | |
| 16893 | + vfstrace_printf(pInfo, ", %s\n", zRVal); | |
| 16894 | + }else{ | |
| 16895 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16896 | + } | |
| 16897 | + return rc; | |
| 16898 | +} | |
| 16899 | + | |
| 16900 | +/* | |
| 16901 | +** Return the sector-size in bytes for an vfstrace-file. | |
| 16902 | +*/ | |
| 16903 | +static int vfstraceSectorSize(sqlite3_file *pFile){ | |
| 16904 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16905 | + vfstrace_info *pInfo = p->pInfo; | |
| 16906 | + int rc; | |
| 16907 | + vfstraceOnOff(pInfo, VTR_SECSZ); | |
| 16908 | + vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName); | |
| 16909 | + rc = p->pReal->pMethods->xSectorSize(p->pReal); | |
| 16910 | + vfstrace_printf(pInfo, " -> %d\n", rc); | |
| 16911 | + return rc; | |
| 16912 | +} | |
| 16913 | + | |
| 16914 | +/* | |
| 16915 | +** Return the device characteristic flags supported by an vfstrace-file. | |
| 16916 | +*/ | |
| 16917 | +static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){ | |
| 16918 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16919 | + vfstrace_info *pInfo = p->pInfo; | |
| 16920 | + int rc; | |
| 16921 | + vfstraceOnOff(pInfo, VTR_DEVCHAR); | |
| 16922 | + vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)", | |
| 16923 | + pInfo->zVfsName, p->zFName); | |
| 16924 | + rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); | |
| 16925 | + vfstrace_printf(pInfo, " -> 0x%08x\n", rc); | |
| 16926 | + return rc; | |
| 16927 | +} | |
| 16928 | + | |
| 16929 | +/* | |
| 16930 | +** Shared-memory operations. | |
| 16931 | +*/ | |
| 16932 | +static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ | |
| 16933 | + static const char *azLockName[] = { | |
| 16934 | + "WRITE", | |
| 16935 | + "CKPT", | |
| 16936 | + "RECOVER", | |
| 16937 | + "READ0", | |
| 16938 | + "READ1", | |
| 16939 | + "READ2", | |
| 16940 | + "READ3", | |
| 16941 | + "READ4", | |
| 16942 | + }; | |
| 16943 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16944 | + vfstrace_info *pInfo = p->pInfo; | |
| 16945 | + int rc; | |
| 16946 | + char zLck[100]; | |
| 16947 | + int i = 0; | |
| 16948 | + vfstraceOnOff(pInfo, VTR_SHMLOCK); | |
| 16949 | + memcpy(zLck, "|0", 3); | |
| 16950 | + if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK"); | |
| 16951 | + if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK"); | |
| 16952 | + if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED"); | |
| 16953 | + if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE"); | |
| 16954 | + if( flags & ~(0xf) ){ | |
| 16955 | + sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags); | |
| 16956 | + } | |
| 16957 | + if( ofst>=0 && ofst<(int)(sizeof(azLockName)/sizeof(azLockName[0])) ){ | |
| 16958 | + vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)", | |
| 16959 | + pInfo->zVfsName, p->zFName, ofst, azLockName[ofst], | |
| 16960 | + n, &zLck[1]); | |
| 16961 | + }else{ | |
| 16962 | + vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)", | |
| 16963 | + pInfo->zVfsName, p->zFName, ofst, | |
| 16964 | + n, &zLck[1]); | |
| 16965 | + } | |
| 16966 | + rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); | |
| 16967 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16968 | + return rc; | |
| 16969 | +} | |
| 16970 | +static int vfstraceShmMap( | |
| 16971 | + sqlite3_file *pFile, | |
| 16972 | + int iRegion, | |
| 16973 | + int szRegion, | |
| 16974 | + int isWrite, | |
| 16975 | + void volatile **pp | |
| 16976 | +){ | |
| 16977 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16978 | + vfstrace_info *pInfo = p->pInfo; | |
| 16979 | + int rc; | |
| 16980 | + vfstraceOnOff(pInfo, VTR_SHMMAP); | |
| 16981 | + vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)", | |
| 16982 | + pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite); | |
| 16983 | + rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); | |
| 16984 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 16985 | + return rc; | |
| 16986 | +} | |
| 16987 | +static void vfstraceShmBarrier(sqlite3_file *pFile){ | |
| 16988 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16989 | + vfstrace_info *pInfo = p->pInfo; | |
| 16990 | + vfstraceOnOff(pInfo, VTR_SHMBAR); | |
| 16991 | + vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName); | |
| 16992 | + p->pReal->pMethods->xShmBarrier(p->pReal); | |
| 16993 | +} | |
| 16994 | +static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){ | |
| 16995 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 16996 | + vfstrace_info *pInfo = p->pInfo; | |
| 16997 | + int rc; | |
| 16998 | + vfstraceOnOff(pInfo, VTR_SHMUNMAP); | |
| 16999 | + vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)", | |
| 17000 | + pInfo->zVfsName, p->zFName, delFlag); | |
| 17001 | + rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag); | |
| 17002 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 17003 | + return rc; | |
| 17004 | +} | |
| 17005 | + | |
| 17006 | + | |
| 17007 | + | |
| 17008 | +/* | |
| 17009 | +** Open an vfstrace file handle. | |
| 17010 | +*/ | |
| 17011 | +static int vfstraceOpen( | |
| 17012 | + sqlite3_vfs *pVfs, | |
| 17013 | + const char *zName, | |
| 17014 | + sqlite3_file *pFile, | |
| 17015 | + int flags, | |
| 17016 | + int *pOutFlags | |
| 17017 | +){ | |
| 17018 | + int rc; | |
| 17019 | + vfstrace_file *p = (vfstrace_file *)pFile; | |
| 17020 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17021 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17022 | + p->pInfo = pInfo; | |
| 17023 | + p->zFName = zName ? fileTail(zName) : "<temp>"; | |
| 17024 | + p->pReal = (sqlite3_file *)&p[1]; | |
| 17025 | + rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags); | |
| 17026 | + vfstraceOnOff(pInfo, VTR_OPEN); | |
| 17027 | + vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)", | |
| 17028 | + pInfo->zVfsName, p->zFName, flags); | |
| 17029 | + if( p->pReal->pMethods ){ | |
| 17030 | + sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) ); | |
| 17031 | + const sqlite3_io_methods *pSub = p->pReal->pMethods; | |
| 17032 | + memset(pNew, 0, sizeof(*pNew)); | |
| 17033 | + pNew->iVersion = pSub->iVersion; | |
| 17034 | + pNew->xClose = vfstraceClose; | |
| 17035 | + pNew->xRead = vfstraceRead; | |
| 17036 | + pNew->xWrite = vfstraceWrite; | |
| 17037 | + pNew->xTruncate = vfstraceTruncate; | |
| 17038 | + pNew->xSync = vfstraceSync; | |
| 17039 | + pNew->xFileSize = vfstraceFileSize; | |
| 17040 | + pNew->xLock = vfstraceLock; | |
| 17041 | + pNew->xUnlock = vfstraceUnlock; | |
| 17042 | + pNew->xCheckReservedLock = vfstraceCheckReservedLock; | |
| 17043 | + pNew->xFileControl = vfstraceFileControl; | |
| 17044 | + pNew->xSectorSize = vfstraceSectorSize; | |
| 17045 | + pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics; | |
| 17046 | + if( pNew->iVersion>=2 ){ | |
| 17047 | + pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0; | |
| 17048 | + pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0; | |
| 17049 | + pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0; | |
| 17050 | + pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0; | |
| 17051 | + } | |
| 17052 | + pFile->pMethods = pNew; | |
| 17053 | + } | |
| 17054 | + vfstrace_print_errcode(pInfo, " -> %s", rc); | |
| 17055 | + if( pOutFlags ){ | |
| 17056 | + vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags); | |
| 17057 | + }else{ | |
| 17058 | + vfstrace_printf(pInfo, "\n"); | |
| 17059 | + } | |
| 17060 | + return rc; | |
| 17061 | +} | |
| 17062 | + | |
| 17063 | +/* | |
| 17064 | +** Delete the file located at zPath. If the dirSync argument is true, | |
| 17065 | +** ensure the file-system modifications are synced to disk before | |
| 17066 | +** returning. | |
| 17067 | +*/ | |
| 17068 | +static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ | |
| 17069 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17070 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17071 | + int rc; | |
| 17072 | + vfstraceOnOff(pInfo, VTR_DELETE); | |
| 17073 | + vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", | |
| 17074 | + pInfo->zVfsName, zPath, dirSync); | |
| 17075 | + rc = pRoot->xDelete(pRoot, zPath, dirSync); | |
| 17076 | + vfstrace_print_errcode(pInfo, " -> %s\n", rc); | |
| 17077 | + return rc; | |
| 17078 | +} | |
| 17079 | + | |
| 17080 | +/* | |
| 17081 | +** Test for access permissions. Return true if the requested permission | |
| 17082 | +** is available, or false otherwise. | |
| 17083 | +*/ | |
| 17084 | +static int vfstraceAccess( | |
| 17085 | + sqlite3_vfs *pVfs, | |
| 17086 | + const char *zPath, | |
| 17087 | + int flags, | |
| 17088 | + int *pResOut | |
| 17089 | +){ | |
| 17090 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17091 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17092 | + int rc; | |
| 17093 | + vfstraceOnOff(pInfo, VTR_ACCESS); | |
| 17094 | + vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)", | |
| 17095 | + pInfo->zVfsName, zPath, flags); | |
| 17096 | + rc = pRoot->xAccess(pRoot, zPath, flags, pResOut); | |
| 17097 | + vfstrace_print_errcode(pInfo, " -> %s", rc); | |
| 17098 | + vfstrace_printf(pInfo, ", out=%d\n", *pResOut); | |
| 17099 | + return rc; | |
| 17100 | +} | |
| 17101 | + | |
| 17102 | +/* | |
| 17103 | +** Populate buffer zOut with the full canonical pathname corresponding | |
| 17104 | +** to the pathname in zPath. zOut is guaranteed to point to a buffer | |
| 17105 | +** of at least (DEVSYM_MAX_PATHNAME+1) bytes. | |
| 17106 | +*/ | |
| 17107 | +static int vfstraceFullPathname( | |
| 17108 | + sqlite3_vfs *pVfs, | |
| 17109 | + const char *zPath, | |
| 17110 | + int nOut, | |
| 17111 | + char *zOut | |
| 17112 | +){ | |
| 17113 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17114 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17115 | + int rc; | |
| 17116 | + vfstraceOnOff(pInfo, VTR_FULLPATH); | |
| 17117 | + vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")", | |
| 17118 | + pInfo->zVfsName, zPath); | |
| 17119 | + rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut); | |
| 17120 | + vfstrace_print_errcode(pInfo, " -> %s", rc); | |
| 17121 | + vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut); | |
| 17122 | + return rc; | |
| 17123 | +} | |
| 17124 | + | |
| 17125 | +/* | |
| 17126 | +** Open the dynamic library located at zPath and return a handle. | |
| 17127 | +*/ | |
| 17128 | +static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){ | |
| 17129 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17130 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17131 | + vfstraceOnOff(pInfo, VTR_DLOPEN); | |
| 17132 | + vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath); | |
| 17133 | + return pRoot->xDlOpen(pRoot, zPath); | |
| 17134 | +} | |
| 17135 | + | |
| 17136 | +/* | |
| 17137 | +** Populate the buffer zErrMsg (size nByte bytes) with a human readable | |
| 17138 | +** utf-8 string describing the most recent error encountered associated | |
| 17139 | +** with dynamic libraries. | |
| 17140 | +*/ | |
| 17141 | +static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ | |
| 17142 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17143 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17144 | + vfstraceOnOff(pInfo, VTR_DLERR); | |
| 17145 | + vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte); | |
| 17146 | + pRoot->xDlError(pRoot, nByte, zErrMsg); | |
| 17147 | + vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg); | |
| 17148 | +} | |
| 17149 | + | |
| 17150 | +/* | |
| 17151 | +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. | |
| 17152 | +*/ | |
| 17153 | +static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){ | |
| 17154 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17155 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17156 | + vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym); | |
| 17157 | + return pRoot->xDlSym(pRoot, p, zSym); | |
| 17158 | +} | |
| 17159 | + | |
| 17160 | +/* | |
| 17161 | +** Close the dynamic library handle pHandle. | |
| 17162 | +*/ | |
| 17163 | +static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){ | |
| 17164 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17165 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17166 | + vfstraceOnOff(pInfo, VTR_DLCLOSE); | |
| 17167 | + vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName); | |
| 17168 | + pRoot->xDlClose(pRoot, pHandle); | |
| 17169 | +} | |
| 17170 | + | |
| 17171 | +/* | |
| 17172 | +** Populate the buffer pointed to by zBufOut with nByte bytes of | |
| 17173 | +** random data. | |
| 17174 | +*/ | |
| 17175 | +static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ | |
| 17176 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17177 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17178 | + vfstraceOnOff(pInfo, VTR_RAND); | |
| 17179 | + vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte); | |
| 17180 | + return pRoot->xRandomness(pRoot, nByte, zBufOut); | |
| 17181 | +} | |
| 17182 | + | |
| 17183 | +/* | |
| 17184 | +** Sleep for nMicro microseconds. Return the number of microseconds | |
| 17185 | +** actually slept. | |
| 17186 | +*/ | |
| 17187 | +static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){ | |
| 17188 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17189 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17190 | + vfstraceOnOff(pInfo, VTR_SLEEP); | |
| 17191 | + vfstrace_printf(pInfo, "%s.xSleep(%d)\n", pInfo->zVfsName, nMicro); | |
| 17192 | + return pRoot->xSleep(pRoot, nMicro); | |
| 17193 | +} | |
| 17194 | + | |
| 17195 | +/* | |
| 17196 | +** Return the current time as a Julian Day number in *pTimeOut. | |
| 17197 | +*/ | |
| 17198 | +static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ | |
| 17199 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17200 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17201 | + int rc; | |
| 17202 | + vfstraceOnOff(pInfo, VTR_CURTIME); | |
| 17203 | + vfstrace_printf(pInfo, "%s.xCurrentTime()", pInfo->zVfsName); | |
| 17204 | + rc = pRoot->xCurrentTime(pRoot, pTimeOut); | |
| 17205 | + vfstrace_printf(pInfo, " -> %.17g\n", *pTimeOut); | |
| 17206 | + return rc; | |
| 17207 | +} | |
| 17208 | +static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ | |
| 17209 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17210 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17211 | + int rc; | |
| 17212 | + vfstraceOnOff(pInfo, VTR_CURTIME); | |
| 17213 | + vfstrace_printf(pInfo, "%s.xCurrentTimeInt64()", pInfo->zVfsName); | |
| 17214 | + rc = pRoot->xCurrentTimeInt64(pRoot, pTimeOut); | |
| 17215 | + vfstrace_printf(pInfo, " -> %lld\n", *pTimeOut); | |
| 17216 | + return rc; | |
| 17217 | +} | |
| 17218 | + | |
| 17219 | +/* | |
| 17220 | +** Return the most recent error code and message | |
| 17221 | +*/ | |
| 17222 | +static int vfstraceGetLastError(sqlite3_vfs *pVfs, int nErr, char *zErr){ | |
| 17223 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17224 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17225 | + int rc; | |
| 17226 | + vfstraceOnOff(pInfo, VTR_LASTERR); | |
| 17227 | + vfstrace_printf(pInfo, "%s.xGetLastError(%d,zBuf)", pInfo->zVfsName, nErr); | |
| 17228 | + if( nErr ) zErr[0] = 0; | |
| 17229 | + rc = pRoot->xGetLastError(pRoot, nErr, zErr); | |
| 17230 | + vfstrace_printf(pInfo, " -> zBuf[] = \"%s\", rc = %d\n", nErr?zErr:"", rc); | |
| 17231 | + return rc; | |
| 17232 | +} | |
| 17233 | + | |
| 17234 | +/* | |
| 17235 | +** Override system calls. | |
| 17236 | +*/ | |
| 17237 | +static int vfstraceSetSystemCall( | |
| 17238 | + sqlite3_vfs *pVfs, | |
| 17239 | + const char *zName, | |
| 17240 | + sqlite3_syscall_ptr pFunc | |
| 17241 | +){ | |
| 17242 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17243 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17244 | + return pRoot->xSetSystemCall(pRoot, zName, pFunc); | |
| 17245 | +} | |
| 17246 | +static sqlite3_syscall_ptr vfstraceGetSystemCall( | |
| 17247 | + sqlite3_vfs *pVfs, | |
| 17248 | + const char *zName | |
| 17249 | +){ | |
| 17250 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17251 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17252 | + return pRoot->xGetSystemCall(pRoot, zName); | |
| 17253 | +} | |
| 17254 | +static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ | |
| 17255 | + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; | |
| 17256 | + sqlite3_vfs *pRoot = pInfo->pRootVfs; | |
| 17257 | + return pRoot->xNextSystemCall(pRoot, zName); | |
| 17258 | +} | |
| 17259 | + | |
| 17260 | + | |
| 17261 | +/* | |
| 17262 | +** Clients invoke this routine to construct a new trace-vfs shim. | |
| 17263 | +** | |
| 17264 | +** Return SQLITE_OK on success. | |
| 17265 | +** | |
| 17266 | +** SQLITE_NOMEM is returned in the case of a memory allocation error. | |
| 17267 | +** SQLITE_NOTFOUND is returned if zOldVfsName does not exist. | |
| 17268 | +*/ | |
| 17269 | +int vfstrace_register( | |
| 17270 | + const char *zTraceName, /* Name of the newly constructed VFS */ | |
| 17271 | + const char *zOldVfsName, /* Name of the underlying VFS */ | |
| 17272 | + int (*xOut)(const char*,void*), /* Output routine. ex: fputs */ | |
| 17273 | + void *pOutArg, /* 2nd argument to xOut. ex: stderr */ | |
| 17274 | + int makeDefault /* True to make the new VFS the default */ | |
| 17275 | +){ | |
| 17276 | + sqlite3_vfs *pNew; | |
| 17277 | + sqlite3_vfs *pRoot; | |
| 17278 | + vfstrace_info *pInfo; | |
| 17279 | + size_t nName; | |
| 17280 | + size_t nByte; | |
| 17281 | + | |
| 17282 | + pRoot = sqlite3_vfs_find(zOldVfsName); | |
| 17283 | + if( pRoot==0 ) return SQLITE_NOTFOUND; | |
| 17284 | + nName = strlen(zTraceName); | |
| 17285 | + nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1; | |
| 17286 | + pNew = sqlite3_malloc64( nByte ); | |
| 17287 | + if( pNew==0 ) return SQLITE_NOMEM; | |
| 17288 | + memset(pNew, 0, nByte); | |
| 17289 | + pInfo = (vfstrace_info*)&pNew[1]; | |
| 17290 | + pNew->iVersion = pRoot->iVersion; | |
| 17291 | + pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file); | |
| 17292 | + pNew->mxPathname = pRoot->mxPathname; | |
| 17293 | + pNew->zName = (char*)&pInfo[1]; | |
| 17294 | + memcpy((char*)&pInfo[1], zTraceName, nName+1); | |
| 17295 | + pNew->pAppData = pInfo; | |
| 17296 | + pNew->xOpen = vfstraceOpen; | |
| 17297 | + pNew->xDelete = vfstraceDelete; | |
| 17298 | + pNew->xAccess = vfstraceAccess; | |
| 17299 | + pNew->xFullPathname = vfstraceFullPathname; | |
| 17300 | + pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen; | |
| 17301 | + pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError; | |
| 17302 | + pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym; | |
| 17303 | + pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose; | |
| 17304 | + pNew->xRandomness = vfstraceRandomness; | |
| 17305 | + pNew->xSleep = vfstraceSleep; | |
| 17306 | + pNew->xCurrentTime = vfstraceCurrentTime; | |
| 17307 | + pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError; | |
| 17308 | + if( pNew->iVersion>=2 ){ | |
| 17309 | + pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 : | |
| 17310 | + vfstraceCurrentTimeInt64; | |
| 17311 | + if( pNew->iVersion>=3 ){ | |
| 17312 | + pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 : | |
| 17313 | + vfstraceSetSystemCall; | |
| 17314 | + pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 : | |
| 17315 | + vfstraceGetSystemCall; | |
| 17316 | + pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 : | |
| 17317 | + vfstraceNextSystemCall; | |
| 17318 | + } | |
| 17319 | + } | |
| 17320 | + pInfo->pRootVfs = pRoot; | |
| 17321 | + pInfo->xOut = xOut; | |
| 17322 | + pInfo->pOutArg = pOutArg; | |
| 17323 | + pInfo->zVfsName = pNew->zName; | |
| 17324 | + pInfo->pTraceVfs = pNew; | |
| 17325 | + pInfo->mTrace = 0xffffffff; | |
| 17326 | + pInfo->bOn = 1; | |
| 17327 | + vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n", | |
| 17328 | + pInfo->zVfsName, pRoot->zName); | |
| 17329 | + return sqlite3_vfs_register(pNew, makeDefault); | |
| 17330 | +} | |
| 17331 | + | |
| 17332 | +/* | |
| 17333 | +** Look for the named VFS. If it is a TRACEVFS, then unregister it | |
| 17334 | +** and delete it. | |
| 17335 | +*/ | |
| 17336 | +void vfstrace_unregister(const char *zTraceName){ | |
| 17337 | + sqlite3_vfs *pVfs = sqlite3_vfs_find(zTraceName); | |
| 17338 | + if( pVfs==0 ) return; | |
| 17339 | + if( pVfs->xOpen!=vfstraceOpen ) return; | |
| 17340 | + sqlite3_vfs_unregister(pVfs); | |
| 17341 | + sqlite3_free(pVfs); | |
| 17342 | +} | |
| 17343 | + | |
| 17344 | +/************************* End ../ext/misc/vfstrace.c ********************/ | |
| 16216 | 17345 | |
| 16217 | 17346 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 16218 | 17347 | #define SQLITE_SHELL_HAVE_RECOVER 1 |
| 16219 | 17348 | #else |
| 16220 | 17349 | #define SQLITE_SHELL_HAVE_RECOVER 0 |
| @@ -18241,11 +19370,11 @@ | ||
| 18241 | 19370 | }while( strstr(z,zBuf)!=0 ); |
| 18242 | 19371 | return zBuf; |
| 18243 | 19372 | } |
| 18244 | 19373 | |
| 18245 | 19374 | /* |
| 18246 | -** Implementation of scalar SQL function "escape_crnl". The argument passed to | |
| 19375 | +** Implementation of scalar SQL function "escape_crlf". The argument passed to | |
| 18247 | 19376 | ** this function is the output of built-in function quote(). If the first |
| 18248 | 19377 | ** character of the input is "'", indicating that the value passed to quote() |
| 18249 | 19378 | ** was a text value, then this function searches the input for "\n" and "\r" |
| 18250 | 19379 | ** characters and adds a wrapper similar to the following: |
| 18251 | 19380 | ** |
| @@ -18252,11 +19381,11 @@ | ||
| 18252 | 19381 | ** replace(replace(<input>, '\n', char(10), '\r', char(13)); |
| 18253 | 19382 | ** |
| 18254 | 19383 | ** Or, if the first character of the input is not "'", then a copy of the input |
| 18255 | 19384 | ** is returned. |
| 18256 | 19385 | */ |
| 18257 | -static void recoverEscapeCrnl( | |
| 19386 | +static void recoverEscapeCrlf( | |
| 18258 | 19387 | sqlite3_context *context, |
| 18259 | 19388 | int argc, |
| 18260 | 19389 | sqlite3_value **argv |
| 18261 | 19390 | ){ |
| 18262 | 19391 | const char *zText = (const char*)sqlite3_value_text(argv[0]); |
| @@ -18467,11 +19596,11 @@ | ||
| 18467 | 19596 | void (*xFunc)(sqlite3_context*,int,sqlite3_value **); |
| 18468 | 19597 | } aFunc[] = { |
| 18469 | 19598 | { "getpage", 1, recoverGetPage }, |
| 18470 | 19599 | { "page_is_used", 1, recoverPageIsUsed }, |
| 18471 | 19600 | { "read_i32", 2, recoverReadI32 }, |
| 18472 | - { "escape_crnl", 1, recoverEscapeCrnl }, | |
| 19601 | + { "escape_crlf", 1, recoverEscapeCrlf }, | |
| 18473 | 19602 | }; |
| 18474 | 19603 | |
| 18475 | 19604 | const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; |
| 18476 | 19605 | sqlite3 *db = 0; /* New database handle */ |
| 18477 | 19606 | int ii; /* For iterating through aFunc[] */ |
| @@ -18820,11 +19949,11 @@ | ||
| 18820 | 19949 | assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); |
| 18821 | 19950 | zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); |
| 18822 | 19951 | |
| 18823 | 19952 | if( bSql ){ |
| 18824 | 19953 | zBind = recoverMPrintf(p, |
| 18825 | - "%z%sescape_crnl(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind | |
| 19954 | + "%z%sescape_crlf(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind | |
| 18826 | 19955 | ); |
| 18827 | 19956 | zSqlSep = "||', '||"; |
| 18828 | 19957 | }else{ |
| 18829 | 19958 | zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); |
| 18830 | 19959 | } |
| @@ -19322,10 +20451,12 @@ | ||
| 19322 | 20451 | apVal[iField] = sqlite3_value_dup( pVal ); |
| 19323 | 20452 | if( apVal[iField]==0 ){ |
| 19324 | 20453 | recoverError(p, SQLITE_NOMEM, 0); |
| 19325 | 20454 | } |
| 19326 | 20455 | p1->nVal = iField+1; |
| 20456 | + }else if( pTab->nCol==0 ){ | |
| 20457 | + p1->nVal = pTab->nCol; | |
| 19327 | 20458 | } |
| 19328 | 20459 | p1->iPrevCell = iCell; |
| 19329 | 20460 | p1->iPrevPage = iPage; |
| 19330 | 20461 | } |
| 19331 | 20462 | }else{ |
| @@ -20437,10 +21568,11 @@ | ||
| 20437 | 21568 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 20438 | 21569 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 20439 | 21570 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 20440 | 21571 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 20441 | 21572 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 21573 | + u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ | |
| 20442 | 21574 | ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 20443 | 21575 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 20444 | 21576 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 20445 | 21577 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 20446 | 21578 | int outCount; /* Revert to stdout when reaching zero */ |
| @@ -20582,10 +21714,11 @@ | ||
| 20582 | 21714 | #define MODE_Table 15 /* MySQL-style table formatting */ |
| 20583 | 21715 | #define MODE_Box 16 /* Unicode box-drawing characters */ |
| 20584 | 21716 | #define MODE_Count 17 /* Output only a count of the rows of output */ |
| 20585 | 21717 | #define MODE_Off 18 /* No query output shown */ |
| 20586 | 21718 | #define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */ |
| 21719 | +#define MODE_Www 20 /* Full web-page output */ | |
| 20587 | 21720 | |
| 20588 | 21721 | static const char *modeDescr[] = { |
| 20589 | 21722 | "line", |
| 20590 | 21723 | "column", |
| 20591 | 21724 | "list", |
| @@ -20602,11 +21735,13 @@ | ||
| 20602 | 21735 | "json", |
| 20603 | 21736 | "markdown", |
| 20604 | 21737 | "table", |
| 20605 | 21738 | "box", |
| 20606 | 21739 | "count", |
| 20607 | - "off" | |
| 21740 | + "off", | |
| 21741 | + "scanexp", | |
| 21742 | + "www", | |
| 20608 | 21743 | }; |
| 20609 | 21744 | |
| 20610 | 21745 | /* |
| 20611 | 21746 | ** These are the column/row/line separators used by the various |
| 20612 | 21747 | ** import/export modes. |
| @@ -20630,11 +21765,11 @@ | ||
| 20630 | 21765 | ** A callback for the sqlite3_log() interface. |
| 20631 | 21766 | */ |
| 20632 | 21767 | static void shellLog(void *pArg, int iErrCode, const char *zMsg){ |
| 20633 | 21768 | ShellState *p = (ShellState*)pArg; |
| 20634 | 21769 | if( p->pLog==0 ) return; |
| 20635 | - sputf(p->pLog, "(%d) %s\n", iErrCode, zMsg); | |
| 21770 | + sqlite3_fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); | |
| 20636 | 21771 | fflush(p->pLog); |
| 20637 | 21772 | } |
| 20638 | 21773 | |
| 20639 | 21774 | /* |
| 20640 | 21775 | ** SQL function: shell_putsnl(X) |
| @@ -20645,13 +21780,13 @@ | ||
| 20645 | 21780 | static void shellPutsFunc( |
| 20646 | 21781 | sqlite3_context *pCtx, |
| 20647 | 21782 | int nVal, |
| 20648 | 21783 | sqlite3_value **apVal |
| 20649 | 21784 | ){ |
| 20650 | - /* Unused: (ShellState*)sqlite3_user_data(pCtx); */ | |
| 21785 | + ShellState *p = (ShellState*)sqlite3_user_data(pCtx); | |
| 20651 | 21786 | (void)nVal; |
| 20652 | - oputf("%s\n", sqlite3_value_text(apVal[0])); | |
| 21787 | + sqlite3_fprintf(p->out, "%s\n", sqlite3_value_text(apVal[0])); | |
| 20653 | 21788 | sqlite3_result_value(pCtx, apVal[0]); |
| 20654 | 21789 | } |
| 20655 | 21790 | |
| 20656 | 21791 | /* |
| 20657 | 21792 | ** If in safe mode, print an error message described by the arguments |
| @@ -20666,11 +21801,11 @@ | ||
| 20666 | 21801 | va_list ap; |
| 20667 | 21802 | char *zMsg; |
| 20668 | 21803 | va_start(ap, zErrMsg); |
| 20669 | 21804 | zMsg = sqlite3_vmprintf(zErrMsg, ap); |
| 20670 | 21805 | va_end(ap); |
| 20671 | - eputf("line %d: %s\n", p->lineno, zMsg); | |
| 21806 | + sqlite3_fprintf(stderr, "line %d: %s\n", p->lineno, zMsg); | |
| 20672 | 21807 | exit(1); |
| 20673 | 21808 | } |
| 20674 | 21809 | } |
| 20675 | 21810 | |
| 20676 | 21811 | /* |
| @@ -20699,11 +21834,11 @@ | ||
| 20699 | 21834 | char *zTempFile = 0; |
| 20700 | 21835 | sqlite3 *db; |
| 20701 | 21836 | char *zCmd = 0; |
| 20702 | 21837 | int bBin; |
| 20703 | 21838 | int rc; |
| 20704 | - int hasCRNL = 0; | |
| 21839 | + int hasCRLF = 0; | |
| 20705 | 21840 | FILE *f = 0; |
| 20706 | 21841 | sqlite3_int64 sz; |
| 20707 | 21842 | sqlite3_int64 x; |
| 20708 | 21843 | unsigned char *p = 0; |
| 20709 | 21844 | |
| @@ -20733,11 +21868,11 @@ | ||
| 20733 | 21868 | } |
| 20734 | 21869 | } |
| 20735 | 21870 | bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; |
| 20736 | 21871 | /* When writing the file to be edited, do \n to \r\n conversions on systems |
| 20737 | 21872 | ** that want \r\n line endings */ |
| 20738 | - f = fopen(zTempFile, bBin ? "wb" : "w"); | |
| 21873 | + f = sqlite3_fopen(zTempFile, bBin ? "wb" : "w"); | |
| 20739 | 21874 | if( f==0 ){ |
| 20740 | 21875 | sqlite3_result_error(context, "edit() cannot open temp file", -1); |
| 20741 | 21876 | goto edit_func_end; |
| 20742 | 21877 | } |
| 20743 | 21878 | sz = sqlite3_value_bytes(argv[0]); |
| @@ -20744,11 +21879,11 @@ | ||
| 20744 | 21879 | if( bBin ){ |
| 20745 | 21880 | x = fwrite(sqlite3_value_blob(argv[0]), 1, (size_t)sz, f); |
| 20746 | 21881 | }else{ |
| 20747 | 21882 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 20748 | 21883 | /* Remember whether or not the value originally contained \r\n */ |
| 20749 | - if( z && strstr(z,"\r\n")!=0 ) hasCRNL = 1; | |
| 21884 | + if( z && strstr(z,"\r\n")!=0 ) hasCRLF = 1; | |
| 20750 | 21885 | x = fwrite(sqlite3_value_text(argv[0]), 1, (size_t)sz, f); |
| 20751 | 21886 | } |
| 20752 | 21887 | fclose(f); |
| 20753 | 21888 | f = 0; |
| 20754 | 21889 | if( x!=sz ){ |
| @@ -20764,11 +21899,11 @@ | ||
| 20764 | 21899 | sqlite3_free(zCmd); |
| 20765 | 21900 | if( rc ){ |
| 20766 | 21901 | sqlite3_result_error(context, "EDITOR returned non-zero", -1); |
| 20767 | 21902 | goto edit_func_end; |
| 20768 | 21903 | } |
| 20769 | - f = fopen(zTempFile, "rb"); | |
| 21904 | + f = sqlite3_fopen(zTempFile, "rb"); | |
| 20770 | 21905 | if( f==0 ){ |
| 20771 | 21906 | sqlite3_result_error(context, |
| 20772 | 21907 | "edit() cannot reopen temp file after edit", -1); |
| 20773 | 21908 | goto edit_func_end; |
| 20774 | 21909 | } |
| @@ -20789,11 +21924,11 @@ | ||
| 20789 | 21924 | } |
| 20790 | 21925 | if( bBin ){ |
| 20791 | 21926 | sqlite3_result_blob64(context, p, sz, sqlite3_free); |
| 20792 | 21927 | }else{ |
| 20793 | 21928 | sqlite3_int64 i, j; |
| 20794 | - if( hasCRNL ){ | |
| 21929 | + if( hasCRLF ){ | |
| 20795 | 21930 | /* If the original contains \r\n then do no conversions back to \n */ |
| 20796 | 21931 | }else{ |
| 20797 | 21932 | /* If the file did not originally contain \r\n then convert any new |
| 20798 | 21933 | ** \r\n back into \n */ |
| 20799 | 21934 | p[sz] = 0; |
| @@ -20830,15 +21965,30 @@ | ||
| 20830 | 21965 | p->mode = p->modePrior; |
| 20831 | 21966 | p->shellFlgs = p->priorShFlgs; |
| 20832 | 21967 | memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 20833 | 21968 | memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 20834 | 21969 | } |
| 21970 | + | |
| 21971 | +/* | |
| 21972 | +** Set output mode to text or binary for Windows. | |
| 21973 | +*/ | |
| 21974 | +static void setCrlfMode(ShellState *p){ | |
| 21975 | +#ifdef _WIN32 | |
| 21976 | + if( p->crlfMode ){ | |
| 21977 | + sqlite3_fsetmode(p->out, _O_TEXT); | |
| 21978 | + }else{ | |
| 21979 | + sqlite3_fsetmode(p->out, _O_BINARY); | |
| 21980 | + } | |
| 21981 | +#else | |
| 21982 | + UNUSED_PARAMETER(p); | |
| 21983 | +#endif | |
| 21984 | +} | |
| 20835 | 21985 | |
| 20836 | 21986 | /* |
| 20837 | 21987 | ** Output the given string as a hex-encoded blob (eg. X'1234' ) |
| 20838 | 21988 | */ |
| 20839 | -static void output_hex_blob(const void *pBlob, int nBlob){ | |
| 21989 | +static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ | |
| 20840 | 21990 | int i; |
| 20841 | 21991 | unsigned char *aBlob = (unsigned char*)pBlob; |
| 20842 | 21992 | |
| 20843 | 21993 | char *zStr = sqlite3_malloc(nBlob*2 + 1); |
| 20844 | 21994 | shell_check_oom(zStr); |
| @@ -20851,11 +22001,11 @@ | ||
| 20851 | 22001 | zStr[i*2] = aHex[ (aBlob[i] >> 4) ]; |
| 20852 | 22002 | zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ]; |
| 20853 | 22003 | } |
| 20854 | 22004 | zStr[i*2] = '\0'; |
| 20855 | 22005 | |
| 20856 | - oputf("X'%s'", zStr); | |
| 22006 | + sqlite3_fprintf(out, "X'%s'", zStr); | |
| 20857 | 22007 | sqlite3_free(zStr); |
| 20858 | 22008 | } |
| 20859 | 22009 | |
| 20860 | 22010 | /* |
| 20861 | 22011 | ** Find a string that is not found anywhere in z[]. Return a pointer |
| @@ -20881,46 +22031,40 @@ | ||
| 20881 | 22031 | /* |
| 20882 | 22032 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 20883 | 22033 | ** |
| 20884 | 22034 | ** See also: output_quoted_escaped_string() |
| 20885 | 22035 | */ |
| 20886 | -static void output_quoted_string(const char *z){ | |
| 22036 | +static void output_quoted_string(ShellState *p, const char *z){ | |
| 20887 | 22037 | int i; |
| 20888 | 22038 | char c; |
| 20889 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 20890 | - FILE *pfO = setOutputStream(invalidFileStream); | |
| 20891 | - setBinaryMode(pfO, 1); | |
| 20892 | -#endif | |
| 22039 | + FILE *out = p->out; | |
| 22040 | + sqlite3_fsetmode(out, _O_BINARY); | |
| 20893 | 22041 | if( z==0 ) return; |
| 20894 | 22042 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 20895 | 22043 | if( c==0 ){ |
| 20896 | - oputf("'%s'",z); | |
| 22044 | + sqlite3_fprintf(out, "'%s'",z); | |
| 20897 | 22045 | }else{ |
| 20898 | - oputz("'"); | |
| 22046 | + sqlite3_fputs("'", out); | |
| 20899 | 22047 | while( *z ){ |
| 20900 | 22048 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 20901 | 22049 | if( c=='\'' ) i++; |
| 20902 | 22050 | if( i ){ |
| 20903 | - oputf("%.*s", i, z); | |
| 22051 | + sqlite3_fprintf(out, "%.*s", i, z); | |
| 20904 | 22052 | z += i; |
| 20905 | 22053 | } |
| 20906 | 22054 | if( c=='\'' ){ |
| 20907 | - oputz("'"); | |
| 22055 | + sqlite3_fputs("'", out); | |
| 20908 | 22056 | continue; |
| 20909 | 22057 | } |
| 20910 | 22058 | if( c==0 ){ |
| 20911 | 22059 | break; |
| 20912 | 22060 | } |
| 20913 | 22061 | z++; |
| 20914 | 22062 | } |
| 20915 | - oputz("'"); | |
| 22063 | + sqlite3_fputs("'", out); | |
| 20916 | 22064 | } |
| 20917 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 20918 | - setTextMode(pfO, 1); | |
| 20919 | -#else | |
| 20920 | - setTextMode(stdout, 1); | |
| 20921 | -#endif | |
| 22065 | + setCrlfMode(p); | |
| 20922 | 22066 | } |
| 20923 | 22067 | |
| 20924 | 22068 | /* |
| 20925 | 22069 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 20926 | 22070 | ** Additionallly , escape the "\n" and "\r" characters so that they do not |
| @@ -20928,20 +22072,18 @@ | ||
| 20928 | 22072 | ** systems. |
| 20929 | 22073 | ** |
| 20930 | 22074 | ** This is like output_quoted_string() but with the addition of the \r\n |
| 20931 | 22075 | ** escape mechanism. |
| 20932 | 22076 | */ |
| 20933 | -static void output_quoted_escaped_string(const char *z){ | |
| 22077 | +static void output_quoted_escaped_string(ShellState *p, const char *z){ | |
| 20934 | 22078 | int i; |
| 20935 | 22079 | char c; |
| 20936 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 20937 | - FILE *pfO = setOutputStream(invalidFileStream); | |
| 20938 | - setBinaryMode(pfO, 1); | |
| 20939 | -#endif | |
| 22080 | + FILE *out = p->out; | |
| 22081 | + sqlite3_fsetmode(out, _O_BINARY); | |
| 20940 | 22082 | for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} |
| 20941 | 22083 | if( c==0 ){ |
| 20942 | - oputf("'%s'",z); | |
| 22084 | + sqlite3_fprintf(out, "'%s'",z); | |
| 20943 | 22085 | }else{ |
| 20944 | 22086 | const char *zNL = 0; |
| 20945 | 22087 | const char *zCR = 0; |
| 20946 | 22088 | int nNL = 0; |
| 20947 | 22089 | int nCR = 0; |
| @@ -20949,52 +22091,48 @@ | ||
| 20949 | 22091 | for(i=0; z[i]; i++){ |
| 20950 | 22092 | if( z[i]=='\n' ) nNL++; |
| 20951 | 22093 | if( z[i]=='\r' ) nCR++; |
| 20952 | 22094 | } |
| 20953 | 22095 | if( nNL ){ |
| 20954 | - oputz("replace("); | |
| 22096 | + sqlite3_fputs("replace(", out); | |
| 20955 | 22097 | zNL = unused_string(z, "\\n", "\\012", zBuf1); |
| 20956 | 22098 | } |
| 20957 | 22099 | if( nCR ){ |
| 20958 | - oputz("replace("); | |
| 22100 | + sqlite3_fputs("replace(", out); | |
| 20959 | 22101 | zCR = unused_string(z, "\\r", "\\015", zBuf2); |
| 20960 | 22102 | } |
| 20961 | - oputz("'"); | |
| 22103 | + sqlite3_fputs("'", out); | |
| 20962 | 22104 | while( *z ){ |
| 20963 | 22105 | for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} |
| 20964 | 22106 | if( c=='\'' ) i++; |
| 20965 | 22107 | if( i ){ |
| 20966 | - oputf("%.*s", i, z); | |
| 22108 | + sqlite3_fprintf(out, "%.*s", i, z); | |
| 20967 | 22109 | z += i; |
| 20968 | 22110 | } |
| 20969 | 22111 | if( c=='\'' ){ |
| 20970 | - oputz("'"); | |
| 22112 | + sqlite3_fputs("'", out); | |
| 20971 | 22113 | continue; |
| 20972 | 22114 | } |
| 20973 | 22115 | if( c==0 ){ |
| 20974 | 22116 | break; |
| 20975 | 22117 | } |
| 20976 | 22118 | z++; |
| 20977 | 22119 | if( c=='\n' ){ |
| 20978 | - oputz(zNL); | |
| 22120 | + sqlite3_fputs(zNL, out); | |
| 20979 | 22121 | continue; |
| 20980 | 22122 | } |
| 20981 | - oputz(zCR); | |
| 22123 | + sqlite3_fputs(zCR, out); | |
| 20982 | 22124 | } |
| 20983 | - oputz("'"); | |
| 22125 | + sqlite3_fputs("'", out); | |
| 20984 | 22126 | if( nCR ){ |
| 20985 | - oputf(",'%s',char(13))", zCR); | |
| 22127 | + sqlite3_fprintf(out, ",'%s',char(13))", zCR); | |
| 20986 | 22128 | } |
| 20987 | 22129 | if( nNL ){ |
| 20988 | - oputf(",'%s',char(10))", zNL); | |
| 22130 | + sqlite3_fprintf(out, ",'%s',char(10))", zNL); | |
| 20989 | 22131 | } |
| 20990 | 22132 | } |
| 20991 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 20992 | - setTextMode(pfO, 1); | |
| 20993 | -#else | |
| 20994 | - setTextMode(stdout, 1); | |
| 20995 | -#endif | |
| 22133 | + setCrlfMode(p); | |
| 20996 | 22134 | } |
| 20997 | 22135 | |
| 20998 | 22136 | /* |
| 20999 | 22137 | ** Find earliest of chars within s specified in zAny. |
| 21000 | 22138 | ** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated. |
| @@ -21010,26 +22148,64 @@ | ||
| 21010 | 22148 | } |
| 21011 | 22149 | ++zAny; |
| 21012 | 22150 | } |
| 21013 | 22151 | return pcFirst; |
| 21014 | 22152 | } |
| 22153 | + | |
| 22154 | +/* Skip over as much z[] input char sequence as is valid UTF-8, | |
| 22155 | +** limited per nAccept char's or whole characters and containing | |
| 22156 | +** no char cn such that ((1<<cn) & ccm)!=0. On return, the | |
| 22157 | +** sequence z:return (inclusive:exclusive) is validated UTF-8. | |
| 22158 | +** Limit: nAccept>=0 => char count, nAccept<0 => character | |
| 22159 | + */ | |
| 22160 | +const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){ | |
| 22161 | + int ng = (nAccept<0)? -nAccept : 0; | |
| 22162 | + const char *pcLimit = (nAccept>=0)? z+nAccept : 0; | |
| 22163 | + assert(z!=0); | |
| 22164 | + while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){ | |
| 22165 | + unsigned char c = *(u8*)z; | |
| 22166 | + if( c<0x7f ){ | |
| 22167 | + if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z; | |
| 22168 | + ++z; /* ASCII */ | |
| 22169 | + }else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */ | |
| 22170 | + else{ | |
| 22171 | + const char *zt = z+1; /* Got lead byte, look at trail bytes.*/ | |
| 22172 | + do{ | |
| 22173 | + if( pcLimit && zt >= pcLimit ) return z; | |
| 22174 | + else{ | |
| 22175 | + char ct = *zt++; | |
| 22176 | + if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ | |
| 22177 | + /* Trailing bytes are too few, too many, or invalid. */ | |
| 22178 | + return z; | |
| 22179 | + } | |
| 22180 | + } | |
| 22181 | + } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ | |
| 22182 | + z = zt; | |
| 22183 | + } | |
| 22184 | + } | |
| 22185 | + return z; | |
| 22186 | +} | |
| 22187 | + | |
| 22188 | + | |
| 21015 | 22189 | /* |
| 21016 | 22190 | ** Output the given string as a quoted according to C or TCL quoting rules. |
| 21017 | 22191 | */ |
| 21018 | -static void output_c_string(const char *z){ | |
| 22192 | +static void output_c_string(FILE *out, const char *z){ | |
| 21019 | 22193 | char c; |
| 21020 | 22194 | static const char *zq = "\""; |
| 21021 | 22195 | static long ctrlMask = ~0L; |
| 21022 | 22196 | static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ |
| 21023 | 22197 | char ace[3] = "\\?"; |
| 21024 | 22198 | char cbsSay; |
| 21025 | - oputz(zq); | |
| 22199 | + sqlite3_fputs(zq, out); | |
| 21026 | 22200 | while( *z!=0 ){ |
| 21027 | 22201 | const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); |
| 21028 | 22202 | const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); |
| 21029 | 22203 | const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; |
| 21030 | - if( pcEnd > z ) oputb(z, (int)(pcEnd-z)); | |
| 22204 | + if( pcEnd > z ){ | |
| 22205 | + sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); | |
| 22206 | + } | |
| 21031 | 22207 | if( (c = *pcEnd)==0 ) break; |
| 21032 | 22208 | ++pcEnd; |
| 21033 | 22209 | switch( c ){ |
| 21034 | 22210 | case '\\': case '"': |
| 21035 | 22211 | cbsSay = (char)c; |
| @@ -21040,47 +22216,47 @@ | ||
| 21040 | 22216 | case '\f': cbsSay = 'f'; break; |
| 21041 | 22217 | default: cbsSay = 0; break; |
| 21042 | 22218 | } |
| 21043 | 22219 | if( cbsSay ){ |
| 21044 | 22220 | ace[1] = cbsSay; |
| 21045 | - oputz(ace); | |
| 22221 | + sqlite3_fputs(ace, out); | |
| 21046 | 22222 | }else if( !isprint(c&0xff) ){ |
| 21047 | - oputf("\\%03o", c&0xff); | |
| 22223 | + sqlite3_fprintf(out, "\\%03o", c&0xff); | |
| 21048 | 22224 | }else{ |
| 21049 | 22225 | ace[1] = (char)c; |
| 21050 | - oputz(ace+1); | |
| 22226 | + sqlite3_fputs(ace+1, out); | |
| 21051 | 22227 | } |
| 21052 | 22228 | z = pcEnd; |
| 21053 | 22229 | } |
| 21054 | - oputz(zq); | |
| 22230 | + sqlite3_fputs(zq, out); | |
| 21055 | 22231 | } |
| 21056 | 22232 | |
| 21057 | 22233 | /* |
| 21058 | -** Output the given string as a quoted according to JSON quoting rules. | |
| 22234 | +** Output the given string as quoted according to JSON quoting rules. | |
| 21059 | 22235 | */ |
| 21060 | -static void output_json_string(const char *z, i64 n){ | |
| 21061 | - char c; | |
| 22236 | +static void output_json_string(FILE *out, const char *z, i64 n){ | |
| 22237 | + unsigned char c; | |
| 21062 | 22238 | static const char *zq = "\""; |
| 21063 | 22239 | static long ctrlMask = ~0L; |
| 21064 | 22240 | static const char *zDQBS = "\"\\"; |
| 21065 | 22241 | const char *pcLimit; |
| 21066 | 22242 | char ace[3] = "\\?"; |
| 21067 | 22243 | char cbsSay; |
| 21068 | 22244 | |
| 21069 | 22245 | if( z==0 ) z = ""; |
| 21070 | 22246 | pcLimit = z + ((n<0)? strlen(z) : (size_t)n); |
| 21071 | - oputz(zq); | |
| 22247 | + sqlite3_fputs(zq, out); | |
| 21072 | 22248 | while( z < pcLimit ){ |
| 21073 | 22249 | const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); |
| 21074 | 22250 | const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); |
| 21075 | 22251 | const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; |
| 21076 | 22252 | if( pcEnd > z ){ |
| 21077 | - oputb(z, (int)(pcEnd-z)); | |
| 22253 | + sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); | |
| 21078 | 22254 | z = pcEnd; |
| 21079 | 22255 | } |
| 21080 | 22256 | if( z >= pcLimit ) break; |
| 21081 | - c = *(z++); | |
| 22257 | + c = (unsigned char)*(z++); | |
| 21082 | 22258 | switch( c ){ |
| 21083 | 22259 | case '"': case '\\': |
| 21084 | 22260 | cbsSay = (char)c; |
| 21085 | 22261 | break; |
| 21086 | 22262 | case '\b': cbsSay = 'b'; break; |
| @@ -21090,26 +22266,26 @@ | ||
| 21090 | 22266 | case '\t': cbsSay = 't'; break; |
| 21091 | 22267 | default: cbsSay = 0; break; |
| 21092 | 22268 | } |
| 21093 | 22269 | if( cbsSay ){ |
| 21094 | 22270 | ace[1] = cbsSay; |
| 21095 | - oputz(ace); | |
| 21096 | - }else if( c<=0x1f ){ | |
| 21097 | - oputf("u%04x", c); | |
| 22271 | + sqlite3_fputs(ace, out); | |
| 22272 | + }else if( c<=0x1f || c>=0x7f ){ | |
| 22273 | + sqlite3_fprintf(out, "\\u%04x", c); | |
| 21098 | 22274 | }else{ |
| 21099 | 22275 | ace[1] = (char)c; |
| 21100 | - oputz(ace+1); | |
| 22276 | + sqlite3_fputs(ace+1, out); | |
| 21101 | 22277 | } |
| 21102 | 22278 | } |
| 21103 | - oputz(zq); | |
| 22279 | + sqlite3_fputs(zq, out); | |
| 21104 | 22280 | } |
| 21105 | 22281 | |
| 21106 | 22282 | /* |
| 21107 | 22283 | ** Output the given string with characters that are special to |
| 21108 | 22284 | ** HTML escaped. |
| 21109 | 22285 | */ |
| 21110 | -static void output_html_string(const char *z){ | |
| 22286 | +static void output_html_string(FILE *out, const char *z){ | |
| 21111 | 22287 | int i; |
| 21112 | 22288 | if( z==0 ) z = ""; |
| 21113 | 22289 | while( *z ){ |
| 21114 | 22290 | for(i=0; z[i] |
| 21115 | 22291 | && z[i]!='<' |
| @@ -21117,22 +22293,22 @@ | ||
| 21117 | 22293 | && z[i]!='>' |
| 21118 | 22294 | && z[i]!='\"' |
| 21119 | 22295 | && z[i]!='\''; |
| 21120 | 22296 | i++){} |
| 21121 | 22297 | if( i>0 ){ |
| 21122 | - oputf("%.*s",i,z); | |
| 22298 | + sqlite3_fprintf(out, "%.*s",i,z); | |
| 21123 | 22299 | } |
| 21124 | 22300 | if( z[i]=='<' ){ |
| 21125 | - oputz("<"); | |
| 22301 | + sqlite3_fputs("<", out); | |
| 21126 | 22302 | }else if( z[i]=='&' ){ |
| 21127 | - oputz("&"); | |
| 22303 | + sqlite3_fputs("&", out); | |
| 21128 | 22304 | }else if( z[i]=='>' ){ |
| 21129 | - oputz(">"); | |
| 22305 | + sqlite3_fputs(">", out); | |
| 21130 | 22306 | }else if( z[i]=='\"' ){ |
| 21131 | - oputz("""); | |
| 22307 | + sqlite3_fputs(""", out); | |
| 21132 | 22308 | }else if( z[i]=='\'' ){ |
| 21133 | - oputz("'"); | |
| 22309 | + sqlite3_fputs("'", out); | |
| 21134 | 22310 | }else{ |
| 21135 | 22311 | break; |
| 21136 | 22312 | } |
| 21137 | 22313 | z += i + 1; |
| 21138 | 22314 | } |
| @@ -21167,11 +22343,11 @@ | ||
| 21167 | 22343 | ** the null value. Strings are quoted if necessary. The separator |
| 21168 | 22344 | ** is only issued if bSep is true. |
| 21169 | 22345 | */ |
| 21170 | 22346 | static void output_csv(ShellState *p, const char *z, int bSep){ |
| 21171 | 22347 | if( z==0 ){ |
| 21172 | - oputf("%s",p->nullValue); | |
| 22348 | + sqlite3_fprintf(p->out, "%s",p->nullValue); | |
| 21173 | 22349 | }else{ |
| 21174 | 22350 | unsigned i; |
| 21175 | 22351 | for(i=0; z[i]; i++){ |
| 21176 | 22352 | if( needCsvQuote[((unsigned char*)z)[i]] ){ |
| 21177 | 22353 | i = 0; |
| @@ -21179,18 +22355,18 @@ | ||
| 21179 | 22355 | } |
| 21180 | 22356 | } |
| 21181 | 22357 | if( i==0 || strstr(z, p->colSeparator)!=0 ){ |
| 21182 | 22358 | char *zQuoted = sqlite3_mprintf("\"%w\"", z); |
| 21183 | 22359 | shell_check_oom(zQuoted); |
| 21184 | - oputz(zQuoted); | |
| 22360 | + sqlite3_fputs(zQuoted, p->out); | |
| 21185 | 22361 | sqlite3_free(zQuoted); |
| 21186 | 22362 | }else{ |
| 21187 | - oputz(z); | |
| 22363 | + sqlite3_fputs(z, p->out); | |
| 21188 | 22364 | } |
| 21189 | 22365 | } |
| 21190 | 22366 | if( bSep ){ |
| 21191 | - oputz(p->colSeparator); | |
| 22367 | + sqlite3_fputs(p->colSeparator, p->out); | |
| 21192 | 22368 | } |
| 21193 | 22369 | } |
| 21194 | 22370 | |
| 21195 | 22371 | /* |
| 21196 | 22372 | ** This routine runs when the user presses Ctrl-C |
| @@ -21294,20 +22470,20 @@ | ||
| 21294 | 22470 | const char *az[4]; |
| 21295 | 22471 | az[0] = zA1; |
| 21296 | 22472 | az[1] = zA2; |
| 21297 | 22473 | az[2] = zA3; |
| 21298 | 22474 | az[3] = zA4; |
| 21299 | - oputf("authorizer: %s", azAction[op]); | |
| 22475 | + sqlite3_fprintf(p->out, "authorizer: %s", azAction[op]); | |
| 21300 | 22476 | for(i=0; i<4; i++){ |
| 21301 | - oputz(" "); | |
| 22477 | + sqlite3_fputs(" ", p->out); | |
| 21302 | 22478 | if( az[i] ){ |
| 21303 | - output_c_string(az[i]); | |
| 22479 | + output_c_string(p->out, az[i]); | |
| 21304 | 22480 | }else{ |
| 21305 | - oputz("NULL"); | |
| 22481 | + sqlite3_fputs("NULL", p->out); | |
| 21306 | 22482 | } |
| 21307 | 22483 | } |
| 21308 | - oputz("\n"); | |
| 22484 | + sqlite3_fputs("\n", p->out); | |
| 21309 | 22485 | if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); |
| 21310 | 22486 | return SQLITE_OK; |
| 21311 | 22487 | } |
| 21312 | 22488 | #endif |
| 21313 | 22489 | |
| @@ -21319,11 +22495,11 @@ | ||
| 21319 | 22495 | ** |
| 21320 | 22496 | ** If the schema statement in z[] contains a start-of-comment and if |
| 21321 | 22497 | ** sqlite3_complete() returns false, try to terminate the comment before |
| 21322 | 22498 | ** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c |
| 21323 | 22499 | */ |
| 21324 | -static void printSchemaLine(const char *z, const char *zTail){ | |
| 22500 | +static void printSchemaLine(FILE *out, const char *z, const char *zTail){ | |
| 21325 | 22501 | char *zToFree = 0; |
| 21326 | 22502 | if( z==0 ) return; |
| 21327 | 22503 | if( zTail==0 ) return; |
| 21328 | 22504 | if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){ |
| 21329 | 22505 | const char *zOrig = z; |
| @@ -21341,20 +22517,20 @@ | ||
| 21341 | 22517 | } |
| 21342 | 22518 | sqlite3_free(zNew); |
| 21343 | 22519 | } |
| 21344 | 22520 | } |
| 21345 | 22521 | if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ |
| 21346 | - oputf("CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); | |
| 22522 | + sqlite3_fprintf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); | |
| 21347 | 22523 | }else{ |
| 21348 | - oputf("%s%s", z, zTail); | |
| 22524 | + sqlite3_fprintf(out, "%s%s", z, zTail); | |
| 21349 | 22525 | } |
| 21350 | 22526 | sqlite3_free(zToFree); |
| 21351 | 22527 | } |
| 21352 | -static void printSchemaLineN(char *z, int n, const char *zTail){ | |
| 22528 | +static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ | |
| 21353 | 22529 | char c = z[n]; |
| 21354 | 22530 | z[n] = 0; |
| 21355 | - printSchemaLine(z, zTail); | |
| 22531 | + printSchemaLine(out, z, zTail); | |
| 21356 | 22532 | z[n] = c; |
| 21357 | 22533 | } |
| 21358 | 22534 | |
| 21359 | 22535 | /* |
| 21360 | 22536 | ** Return true if string z[] has nothing but whitespace and comments to the |
| @@ -21378,11 +22554,11 @@ | ||
| 21378 | 22554 | EQPGraphRow *pNew; |
| 21379 | 22555 | i64 nText; |
| 21380 | 22556 | if( zText==0 ) return; |
| 21381 | 22557 | nText = strlen(zText); |
| 21382 | 22558 | if( p->autoEQPtest ){ |
| 21383 | - oputf("%d,%d,%s\n", iEqpId, p2, zText); | |
| 22559 | + sqlite3_fprintf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); | |
| 21384 | 22560 | } |
| 21385 | 22561 | pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); |
| 21386 | 22562 | shell_check_oom(pNew); |
| 21387 | 22563 | pNew->iEqpId = iEqpId; |
| 21388 | 22564 | pNew->iParentId = p2; |
| @@ -21426,11 +22602,12 @@ | ||
| 21426 | 22602 | i64 n = strlen(p->sGraph.zPrefix); |
| 21427 | 22603 | char *z; |
| 21428 | 22604 | for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ |
| 21429 | 22605 | pNext = eqp_next_row(p, iEqpId, pRow); |
| 21430 | 22606 | z = pRow->zText; |
| 21431 | - oputf("%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); | |
| 22607 | + sqlite3_fprintf(p->out, "%s%s%s\n", p->sGraph.zPrefix, | |
| 22608 | + pNext ? "|--" : "`--", z); | |
| 21432 | 22609 | if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ |
| 21433 | 22610 | memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); |
| 21434 | 22611 | eqp_render_level(p, pRow->iEqpId); |
| 21435 | 22612 | p->sGraph.zPrefix[n] = 0; |
| 21436 | 22613 | } |
| @@ -21446,17 +22623,17 @@ | ||
| 21446 | 22623 | if( pRow->zText[0]=='-' ){ |
| 21447 | 22624 | if( pRow->pNext==0 ){ |
| 21448 | 22625 | eqp_reset(p); |
| 21449 | 22626 | return; |
| 21450 | 22627 | } |
| 21451 | - oputf("%s\n", pRow->zText+3); | |
| 22628 | + sqlite3_fprintf(p->out, "%s\n", pRow->zText+3); | |
| 21452 | 22629 | p->sGraph.pRow = pRow->pNext; |
| 21453 | 22630 | sqlite3_free(pRow); |
| 21454 | 22631 | }else if( nCycle>0 ){ |
| 21455 | - oputf("QUERY PLAN (cycles=%lld [100%%])\n", nCycle); | |
| 22632 | + sqlite3_fprintf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); | |
| 21456 | 22633 | }else{ |
| 21457 | - oputz("QUERY PLAN\n"); | |
| 22634 | + sqlite3_fputs("QUERY PLAN\n", p->out); | |
| 21458 | 22635 | } |
| 21459 | 22636 | p->sGraph.zPrefix[0] = 0; |
| 21460 | 22637 | eqp_render_level(p, 0); |
| 21461 | 22638 | eqp_reset(p); |
| 21462 | 22639 | } |
| @@ -21468,33 +22645,33 @@ | ||
| 21468 | 22645 | */ |
| 21469 | 22646 | static int progress_handler(void *pClientData) { |
| 21470 | 22647 | ShellState *p = (ShellState*)pClientData; |
| 21471 | 22648 | p->nProgress++; |
| 21472 | 22649 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 21473 | - oputf("Progress limit reached (%u)\n", p->nProgress); | |
| 22650 | + sqlite3_fprintf(p->out, "Progress limit reached (%u)\n", p->nProgress); | |
| 21474 | 22651 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 21475 | 22652 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 21476 | 22653 | return 1; |
| 21477 | 22654 | } |
| 21478 | 22655 | if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ |
| 21479 | - oputf("Progress %u\n", p->nProgress); | |
| 22656 | + sqlite3_fprintf(p->out, "Progress %u\n", p->nProgress); | |
| 21480 | 22657 | } |
| 21481 | 22658 | return 0; |
| 21482 | 22659 | } |
| 21483 | 22660 | #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
| 21484 | 22661 | |
| 21485 | 22662 | /* |
| 21486 | 22663 | ** Print N dashes |
| 21487 | 22664 | */ |
| 21488 | -static void print_dashes(int N){ | |
| 22665 | +static void print_dashes(FILE *out, int N){ | |
| 21489 | 22666 | const char zDash[] = "--------------------------------------------------"; |
| 21490 | 22667 | const int nDash = sizeof(zDash) - 1; |
| 21491 | 22668 | while( N>nDash ){ |
| 21492 | - oputz(zDash); | |
| 22669 | + sqlite3_fputs(zDash, out); | |
| 21493 | 22670 | N -= nDash; |
| 21494 | 22671 | } |
| 21495 | - oputf("%.*s", N, zDash); | |
| 22672 | + sqlite3_fprintf(out, "%.*s", N, zDash); | |
| 21496 | 22673 | } |
| 21497 | 22674 | |
| 21498 | 22675 | /* |
| 21499 | 22676 | ** Print a markdown or table-style row separator using ascii-art |
| 21500 | 22677 | */ |
| @@ -21503,19 +22680,19 @@ | ||
| 21503 | 22680 | int nArg, |
| 21504 | 22681 | const char *zSep |
| 21505 | 22682 | ){ |
| 21506 | 22683 | int i; |
| 21507 | 22684 | if( nArg>0 ){ |
| 21508 | - oputz(zSep); | |
| 21509 | - print_dashes(p->actualWidth[0]+2); | |
| 22685 | + sqlite3_fputs(zSep, p->out); | |
| 22686 | + print_dashes(p->out, p->actualWidth[0]+2); | |
| 21510 | 22687 | for(i=1; i<nArg; i++){ |
| 21511 | - oputz(zSep); | |
| 21512 | - print_dashes(p->actualWidth[i]+2); | |
| 22688 | + sqlite3_fputs(zSep, p->out); | |
| 22689 | + print_dashes(p->out, p->actualWidth[i]+2); | |
| 21513 | 22690 | } |
| 21514 | - oputz(zSep); | |
| 22691 | + sqlite3_fputs(zSep, p->out); | |
| 21515 | 22692 | } |
| 21516 | - oputz("\n"); | |
| 22693 | + sqlite3_fputs("\n", p->out); | |
| 21517 | 22694 | } |
| 21518 | 22695 | |
| 21519 | 22696 | /* |
| 21520 | 22697 | ** This is the callback routine that the shell |
| 21521 | 22698 | ** invokes for each row of a query result. |
| @@ -21541,13 +22718,13 @@ | ||
| 21541 | 22718 | if( azArg==0 ) break; |
| 21542 | 22719 | for(i=0; i<nArg; i++){ |
| 21543 | 22720 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 21544 | 22721 | if( len>w ) w = len; |
| 21545 | 22722 | } |
| 21546 | - if( p->cnt++>0 ) oputz(p->rowSeparator); | |
| 22723 | + if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); | |
| 21547 | 22724 | for(i=0; i<nArg; i++){ |
| 21548 | - oputf("%*s = %s%s", w, azCol[i], | |
| 22725 | + sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], | |
| 21549 | 22726 | azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
| 21550 | 22727 | } |
| 21551 | 22728 | break; |
| 21552 | 22729 | } |
| 21553 | 22730 | case MODE_ScanExp: |
| @@ -21571,16 +22748,16 @@ | ||
| 21571 | 22748 | if( nArg>nWidth ) nArg = nWidth; |
| 21572 | 22749 | |
| 21573 | 22750 | /* If this is the first row seen, print out the headers */ |
| 21574 | 22751 | if( p->cnt++==0 ){ |
| 21575 | 22752 | for(i=0; i<nArg; i++){ |
| 21576 | - utf8_width_print(aWidth[i], azCol[ aMap[i] ]); | |
| 21577 | - oputz(i==nArg-1 ? "\n" : " "); | |
| 22753 | + utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]); | |
| 22754 | + sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); | |
| 21578 | 22755 | } |
| 21579 | 22756 | for(i=0; i<nArg; i++){ |
| 21580 | - print_dashes(aWidth[i]); | |
| 21581 | - oputz(i==nArg-1 ? "\n" : " "); | |
| 22757 | + print_dashes(p->out, aWidth[i]); | |
| 22758 | + sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); | |
| 21582 | 22759 | } |
| 21583 | 22760 | } |
| 21584 | 22761 | |
| 21585 | 22762 | /* If there is no data, exit early. */ |
| 21586 | 22763 | if( azArg==0 ) break; |
| @@ -21594,21 +22771,21 @@ | ||
| 21594 | 22771 | w = strlenChar(zVal); |
| 21595 | 22772 | zSep = " "; |
| 21596 | 22773 | } |
| 21597 | 22774 | if( i==iIndent && p->aiIndent && p->pStmt ){ |
| 21598 | 22775 | if( p->iIndent<p->nIndent ){ |
| 21599 | - oputf("%*.s", p->aiIndent[p->iIndent], ""); | |
| 22776 | + sqlite3_fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); | |
| 21600 | 22777 | } |
| 21601 | 22778 | p->iIndent++; |
| 21602 | 22779 | } |
| 21603 | - utf8_width_print(w, zVal ? zVal : p->nullValue); | |
| 21604 | - oputz(i==nArg-1 ? "\n" : zSep); | |
| 22780 | + utf8_width_print(p->out, w, zVal ? zVal : p->nullValue); | |
| 22781 | + sqlite3_fputs(i==nArg-1 ? "\n" : zSep, p->out); | |
| 21605 | 22782 | } |
| 21606 | 22783 | break; |
| 21607 | 22784 | } |
| 21608 | 22785 | case MODE_Semi: { /* .schema and .fullschema output */ |
| 21609 | - printSchemaLine(azArg[0], ";\n"); | |
| 22786 | + printSchemaLine(p->out, azArg[0], ";\n"); | |
| 21610 | 22787 | break; |
| 21611 | 22788 | } |
| 21612 | 22789 | case MODE_Pretty: { /* .schema and .fullschema with --indent */ |
| 21613 | 22790 | char *z; |
| 21614 | 22791 | int j; |
| @@ -21619,11 +22796,11 @@ | ||
| 21619 | 22796 | assert( nArg==1 ); |
| 21620 | 22797 | if( azArg[0]==0 ) break; |
| 21621 | 22798 | if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 21622 | 22799 | || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 21623 | 22800 | ){ |
| 21624 | - oputf("%s;\n", azArg[0]); | |
| 22801 | + sqlite3_fprintf(p->out, "%s;\n", azArg[0]); | |
| 21625 | 22802 | break; |
| 21626 | 22803 | } |
| 21627 | 22804 | z = sqlite3_mprintf("%s", azArg[0]); |
| 21628 | 22805 | shell_check_oom(z); |
| 21629 | 22806 | j = 0; |
| @@ -21652,255 +22829,265 @@ | ||
| 21652 | 22829 | }else if( c=='(' ){ |
| 21653 | 22830 | nParen++; |
| 21654 | 22831 | }else if( c==')' ){ |
| 21655 | 22832 | nParen--; |
| 21656 | 22833 | if( nLine>0 && nParen==0 && j>0 ){ |
| 21657 | - printSchemaLineN(z, j, "\n"); | |
| 22834 | + printSchemaLineN(p->out, z, j, "\n"); | |
| 21658 | 22835 | j = 0; |
| 21659 | 22836 | } |
| 21660 | 22837 | } |
| 21661 | 22838 | z[j++] = c; |
| 21662 | 22839 | if( nParen==1 && cEnd==0 |
| 21663 | 22840 | && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 21664 | 22841 | ){ |
| 21665 | 22842 | if( c=='\n' ) j--; |
| 21666 | - printSchemaLineN(z, j, "\n "); | |
| 22843 | + printSchemaLineN(p->out, z, j, "\n "); | |
| 21667 | 22844 | j = 0; |
| 21668 | 22845 | nLine++; |
| 21669 | 22846 | while( IsSpace(z[i+1]) ){ i++; } |
| 21670 | 22847 | } |
| 21671 | 22848 | } |
| 21672 | 22849 | z[j] = 0; |
| 21673 | 22850 | } |
| 21674 | - printSchemaLine(z, ";\n"); | |
| 22851 | + printSchemaLine(p->out, z, ";\n"); | |
| 21675 | 22852 | sqlite3_free(z); |
| 21676 | 22853 | break; |
| 21677 | 22854 | } |
| 21678 | 22855 | case MODE_List: { |
| 21679 | 22856 | if( p->cnt++==0 && p->showHeader ){ |
| 21680 | 22857 | for(i=0; i<nArg; i++){ |
| 21681 | - oputf("%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); | |
| 22858 | + sqlite3_fprintf(p->out, "%s%s", azCol[i], | |
| 22859 | + i==nArg-1 ? p->rowSeparator : p->colSeparator); | |
| 21682 | 22860 | } |
| 21683 | 22861 | } |
| 21684 | 22862 | if( azArg==0 ) break; |
| 21685 | 22863 | for(i=0; i<nArg; i++){ |
| 21686 | 22864 | char *z = azArg[i]; |
| 21687 | 22865 | if( z==0 ) z = p->nullValue; |
| 21688 | - oputz(z); | |
| 21689 | - oputz((i<nArg-1)? p->colSeparator : p->rowSeparator); | |
| 22866 | + sqlite3_fputs(z, p->out); | |
| 22867 | + sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); | |
| 21690 | 22868 | } |
| 21691 | 22869 | break; |
| 21692 | 22870 | } |
| 22871 | + case MODE_Www: | |
| 21693 | 22872 | case MODE_Html: { |
| 21694 | - if( p->cnt++==0 && p->showHeader ){ | |
| 21695 | - oputz("<TR>"); | |
| 22873 | + if( p->cnt==0 && p->cMode==MODE_Www ){ | |
| 22874 | + sqlite3_fputs( | |
| 22875 | + "</PRE>\n" | |
| 22876 | + "<TABLE border='1' cellspacing='0' cellpadding='2'>\n" | |
| 22877 | + ,p->out | |
| 22878 | + ); | |
| 22879 | + } | |
| 22880 | + if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){ | |
| 22881 | + sqlite3_fputs("<TR>", p->out); | |
| 21696 | 22882 | for(i=0; i<nArg; i++){ |
| 21697 | - oputz("<TH>"); | |
| 21698 | - output_html_string(azCol[i]); | |
| 21699 | - oputz("</TH>\n"); | |
| 22883 | + sqlite3_fputs("<TH>", p->out); | |
| 22884 | + output_html_string(p->out, azCol[i]); | |
| 22885 | + sqlite3_fputs("</TH>\n", p->out); | |
| 21700 | 22886 | } |
| 21701 | - oputz("</TR>\n"); | |
| 22887 | + sqlite3_fputs("</TR>\n", p->out); | |
| 21702 | 22888 | } |
| 22889 | + p->cnt++; | |
| 21703 | 22890 | if( azArg==0 ) break; |
| 21704 | - oputz("<TR>"); | |
| 22891 | + sqlite3_fputs("<TR>", p->out); | |
| 21705 | 22892 | for(i=0; i<nArg; i++){ |
| 21706 | - oputz("<TD>"); | |
| 21707 | - output_html_string(azArg[i] ? azArg[i] : p->nullValue); | |
| 21708 | - oputz("</TD>\n"); | |
| 22893 | + sqlite3_fputs("<TD>", p->out); | |
| 22894 | + output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); | |
| 22895 | + sqlite3_fputs("</TD>\n", p->out); | |
| 21709 | 22896 | } |
| 21710 | - oputz("</TR>\n"); | |
| 22897 | + sqlite3_fputs("</TR>\n", p->out); | |
| 21711 | 22898 | break; |
| 21712 | 22899 | } |
| 21713 | 22900 | case MODE_Tcl: { |
| 21714 | 22901 | if( p->cnt++==0 && p->showHeader ){ |
| 21715 | 22902 | for(i=0; i<nArg; i++){ |
| 21716 | - output_c_string(azCol[i] ? azCol[i] : ""); | |
| 21717 | - if(i<nArg-1) oputz(p->colSeparator); | |
| 22903 | + output_c_string(p->out, azCol[i] ? azCol[i] : ""); | |
| 22904 | + if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); | |
| 21718 | 22905 | } |
| 21719 | - oputz(p->rowSeparator); | |
| 22906 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21720 | 22907 | } |
| 21721 | 22908 | if( azArg==0 ) break; |
| 21722 | 22909 | for(i=0; i<nArg; i++){ |
| 21723 | - output_c_string(azArg[i] ? azArg[i] : p->nullValue); | |
| 21724 | - if(i<nArg-1) oputz(p->colSeparator); | |
| 22910 | + output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); | |
| 22911 | + if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); | |
| 21725 | 22912 | } |
| 21726 | - oputz(p->rowSeparator); | |
| 22913 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21727 | 22914 | break; |
| 21728 | 22915 | } |
| 21729 | 22916 | case MODE_Csv: { |
| 21730 | - setBinaryMode(p->out, 1); | |
| 22917 | + sqlite3_fsetmode(p->out, _O_BINARY); | |
| 21731 | 22918 | if( p->cnt++==0 && p->showHeader ){ |
| 21732 | 22919 | for(i=0; i<nArg; i++){ |
| 21733 | 22920 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 21734 | 22921 | } |
| 21735 | - oputz(p->rowSeparator); | |
| 22922 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21736 | 22923 | } |
| 21737 | 22924 | if( nArg>0 ){ |
| 21738 | 22925 | for(i=0; i<nArg; i++){ |
| 21739 | 22926 | output_csv(p, azArg[i], i<nArg-1); |
| 21740 | 22927 | } |
| 21741 | - oputz(p->rowSeparator); | |
| 22928 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21742 | 22929 | } |
| 21743 | - setTextMode(p->out, 1); | |
| 22930 | + setCrlfMode(p); | |
| 21744 | 22931 | break; |
| 21745 | 22932 | } |
| 21746 | 22933 | case MODE_Insert: { |
| 21747 | 22934 | if( azArg==0 ) break; |
| 21748 | - oputf("INSERT INTO %s",p->zDestTable); | |
| 22935 | + sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable); | |
| 21749 | 22936 | if( p->showHeader ){ |
| 21750 | - oputz("("); | |
| 22937 | + sqlite3_fputs("(", p->out); | |
| 21751 | 22938 | for(i=0; i<nArg; i++){ |
| 21752 | - if( i>0 ) oputz(","); | |
| 22939 | + if( i>0 ) sqlite3_fputs(",", p->out); | |
| 21753 | 22940 | if( quoteChar(azCol[i]) ){ |
| 21754 | 22941 | char *z = sqlite3_mprintf("\"%w\"", azCol[i]); |
| 21755 | 22942 | shell_check_oom(z); |
| 21756 | - oputz(z); | |
| 22943 | + sqlite3_fputs(z, p->out); | |
| 21757 | 22944 | sqlite3_free(z); |
| 21758 | 22945 | }else{ |
| 21759 | - oputf("%s", azCol[i]); | |
| 22946 | + sqlite3_fprintf(p->out, "%s", azCol[i]); | |
| 21760 | 22947 | } |
| 21761 | 22948 | } |
| 21762 | - oputz(")"); | |
| 22949 | + sqlite3_fputs(")", p->out); | |
| 21763 | 22950 | } |
| 21764 | 22951 | p->cnt++; |
| 21765 | 22952 | for(i=0; i<nArg; i++){ |
| 21766 | - oputz(i>0 ? "," : " VALUES("); | |
| 22953 | + sqlite3_fputs(i>0 ? "," : " VALUES(", p->out); | |
| 21767 | 22954 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 21768 | - oputz("NULL"); | |
| 22955 | + sqlite3_fputs("NULL", p->out); | |
| 21769 | 22956 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 21770 | 22957 | if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 21771 | - output_quoted_string(azArg[i]); | |
| 22958 | + output_quoted_string(p, azArg[i]); | |
| 21772 | 22959 | }else{ |
| 21773 | - output_quoted_escaped_string(azArg[i]); | |
| 22960 | + output_quoted_escaped_string(p, azArg[i]); | |
| 21774 | 22961 | } |
| 21775 | 22962 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 21776 | - oputz(azArg[i]); | |
| 22963 | + sqlite3_fputs(azArg[i], p->out); | |
| 21777 | 22964 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 21778 | 22965 | char z[50]; |
| 21779 | 22966 | double r = sqlite3_column_double(p->pStmt, i); |
| 21780 | 22967 | sqlite3_uint64 ur; |
| 21781 | 22968 | memcpy(&ur,&r,sizeof(r)); |
| 21782 | 22969 | if( ur==0x7ff0000000000000LL ){ |
| 21783 | - oputz("9.0e+999"); | |
| 22970 | + sqlite3_fputs("9.0e+999", p->out); | |
| 21784 | 22971 | }else if( ur==0xfff0000000000000LL ){ |
| 21785 | - oputz("-9.0e+999"); | |
| 22972 | + sqlite3_fputs("-9.0e+999", p->out); | |
| 21786 | 22973 | }else{ |
| 21787 | 22974 | sqlite3_int64 ir = (sqlite3_int64)r; |
| 21788 | 22975 | if( r==(double)ir ){ |
| 21789 | 22976 | sqlite3_snprintf(50,z,"%lld.0", ir); |
| 21790 | 22977 | }else{ |
| 21791 | 22978 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 21792 | 22979 | } |
| 21793 | - oputz(z); | |
| 22980 | + sqlite3_fputs(z, p->out); | |
| 21794 | 22981 | } |
| 21795 | 22982 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 21796 | 22983 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 21797 | 22984 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 21798 | - output_hex_blob(pBlob, nBlob); | |
| 22985 | + output_hex_blob(p->out, pBlob, nBlob); | |
| 21799 | 22986 | }else if( isNumber(azArg[i], 0) ){ |
| 21800 | - oputz(azArg[i]); | |
| 22987 | + sqlite3_fputs(azArg[i], p->out); | |
| 21801 | 22988 | }else if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 21802 | - output_quoted_string(azArg[i]); | |
| 22989 | + output_quoted_string(p, azArg[i]); | |
| 21803 | 22990 | }else{ |
| 21804 | - output_quoted_escaped_string(azArg[i]); | |
| 22991 | + output_quoted_escaped_string(p, azArg[i]); | |
| 21805 | 22992 | } |
| 21806 | 22993 | } |
| 21807 | - oputz(");\n"); | |
| 22994 | + sqlite3_fputs(");\n", p->out); | |
| 21808 | 22995 | break; |
| 21809 | 22996 | } |
| 21810 | 22997 | case MODE_Json: { |
| 21811 | 22998 | if( azArg==0 ) break; |
| 21812 | 22999 | if( p->cnt==0 ){ |
| 21813 | - fputs("[{", p->out); | |
| 23000 | + sqlite3_fputs("[{", p->out); | |
| 21814 | 23001 | }else{ |
| 21815 | - fputs(",\n{", p->out); | |
| 23002 | + sqlite3_fputs(",\n{", p->out); | |
| 21816 | 23003 | } |
| 21817 | 23004 | p->cnt++; |
| 21818 | 23005 | for(i=0; i<nArg; i++){ |
| 21819 | - output_json_string(azCol[i], -1); | |
| 21820 | - oputz(":"); | |
| 23006 | + output_json_string(p->out, azCol[i], -1); | |
| 23007 | + sqlite3_fputs(":", p->out); | |
| 21821 | 23008 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 21822 | - oputz("null"); | |
| 23009 | + sqlite3_fputs("null", p->out); | |
| 21823 | 23010 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 21824 | 23011 | char z[50]; |
| 21825 | 23012 | double r = sqlite3_column_double(p->pStmt, i); |
| 21826 | 23013 | sqlite3_uint64 ur; |
| 21827 | 23014 | memcpy(&ur,&r,sizeof(r)); |
| 21828 | 23015 | if( ur==0x7ff0000000000000LL ){ |
| 21829 | - oputz("9.0e+999"); | |
| 23016 | + sqlite3_fputs("9.0e+999", p->out); | |
| 21830 | 23017 | }else if( ur==0xfff0000000000000LL ){ |
| 21831 | - oputz("-9.0e+999"); | |
| 23018 | + sqlite3_fputs("-9.0e+999", p->out); | |
| 21832 | 23019 | }else{ |
| 21833 | 23020 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 21834 | - oputz(z); | |
| 23021 | + sqlite3_fputs(z, p->out); | |
| 21835 | 23022 | } |
| 21836 | 23023 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 21837 | 23024 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 21838 | 23025 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 21839 | - output_json_string(pBlob, nBlob); | |
| 23026 | + output_json_string(p->out, pBlob, nBlob); | |
| 21840 | 23027 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 21841 | - output_json_string(azArg[i], -1); | |
| 23028 | + output_json_string(p->out, azArg[i], -1); | |
| 21842 | 23029 | }else{ |
| 21843 | - oputz(azArg[i]); | |
| 23030 | + sqlite3_fputs(azArg[i], p->out); | |
| 21844 | 23031 | } |
| 21845 | 23032 | if( i<nArg-1 ){ |
| 21846 | - oputz(","); | |
| 23033 | + sqlite3_fputs(",", p->out); | |
| 21847 | 23034 | } |
| 21848 | 23035 | } |
| 21849 | - oputz("}"); | |
| 23036 | + sqlite3_fputs("}", p->out); | |
| 21850 | 23037 | break; |
| 21851 | 23038 | } |
| 21852 | 23039 | case MODE_Quote: { |
| 21853 | 23040 | if( azArg==0 ) break; |
| 21854 | 23041 | if( p->cnt==0 && p->showHeader ){ |
| 21855 | 23042 | for(i=0; i<nArg; i++){ |
| 21856 | - if( i>0 ) fputs(p->colSeparator, p->out); | |
| 21857 | - output_quoted_string(azCol[i]); | |
| 23043 | + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); | |
| 23044 | + output_quoted_string(p, azCol[i]); | |
| 21858 | 23045 | } |
| 21859 | - fputs(p->rowSeparator, p->out); | |
| 23046 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21860 | 23047 | } |
| 21861 | 23048 | p->cnt++; |
| 21862 | 23049 | for(i=0; i<nArg; i++){ |
| 21863 | - if( i>0 ) fputs(p->colSeparator, p->out); | |
| 23050 | + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); | |
| 21864 | 23051 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 21865 | - oputz("NULL"); | |
| 23052 | + sqlite3_fputs("NULL", p->out); | |
| 21866 | 23053 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 21867 | - output_quoted_string(azArg[i]); | |
| 23054 | + output_quoted_string(p, azArg[i]); | |
| 21868 | 23055 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 21869 | - oputz(azArg[i]); | |
| 23056 | + sqlite3_fputs(azArg[i], p->out); | |
| 21870 | 23057 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 21871 | 23058 | char z[50]; |
| 21872 | 23059 | double r = sqlite3_column_double(p->pStmt, i); |
| 21873 | 23060 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 21874 | - oputz(z); | |
| 23061 | + sqlite3_fputs(z, p->out); | |
| 21875 | 23062 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 21876 | 23063 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 21877 | 23064 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 21878 | - output_hex_blob(pBlob, nBlob); | |
| 23065 | + output_hex_blob(p->out, pBlob, nBlob); | |
| 21879 | 23066 | }else if( isNumber(azArg[i], 0) ){ |
| 21880 | - oputz(azArg[i]); | |
| 23067 | + sqlite3_fputs(azArg[i], p->out); | |
| 21881 | 23068 | }else{ |
| 21882 | - output_quoted_string(azArg[i]); | |
| 23069 | + output_quoted_string(p, azArg[i]); | |
| 21883 | 23070 | } |
| 21884 | 23071 | } |
| 21885 | - fputs(p->rowSeparator, p->out); | |
| 23072 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21886 | 23073 | break; |
| 21887 | 23074 | } |
| 21888 | 23075 | case MODE_Ascii: { |
| 21889 | 23076 | if( p->cnt++==0 && p->showHeader ){ |
| 21890 | 23077 | for(i=0; i<nArg; i++){ |
| 21891 | - if( i>0 ) oputz(p->colSeparator); | |
| 21892 | - oputz(azCol[i] ? azCol[i] : ""); | |
| 23078 | + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); | |
| 23079 | + sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out); | |
| 21893 | 23080 | } |
| 21894 | - oputz(p->rowSeparator); | |
| 23081 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21895 | 23082 | } |
| 21896 | 23083 | if( azArg==0 ) break; |
| 21897 | 23084 | for(i=0; i<nArg; i++){ |
| 21898 | - if( i>0 ) oputz(p->colSeparator); | |
| 21899 | - oputz(azArg[i] ? azArg[i] : p->nullValue); | |
| 23085 | + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); | |
| 23086 | + sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out); | |
| 21900 | 23087 | } |
| 21901 | - oputz(p->rowSeparator); | |
| 23088 | + sqlite3_fputs(p->rowSeparator, p->out); | |
| 21902 | 23089 | break; |
| 21903 | 23090 | } |
| 21904 | 23091 | case MODE_EQP: { |
| 21905 | 23092 | eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); |
| 21906 | 23093 | break; |
| @@ -21975,11 +23162,11 @@ | ||
| 21975 | 23162 | "INSERT INTO selftest(tno,op,cmd,ans)" |
| 21976 | 23163 | " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" |
| 21977 | 23164 | "DROP TABLE [_shell$self];" |
| 21978 | 23165 | ,0,0,&zErrMsg); |
| 21979 | 23166 | if( zErrMsg ){ |
| 21980 | - eputf("SELFTEST initialization failure: %s\n", zErrMsg); | |
| 23167 | + sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); | |
| 21981 | 23168 | sqlite3_free(zErrMsg); |
| 21982 | 23169 | } |
| 21983 | 23170 | sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); |
| 21984 | 23171 | } |
| 21985 | 23172 | |
| @@ -22078,36 +23265,37 @@ | ||
| 22078 | 23265 | int i; |
| 22079 | 23266 | const char *z; |
| 22080 | 23267 | rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); |
| 22081 | 23268 | if( rc!=SQLITE_OK || !pSelect ){ |
| 22082 | 23269 | char *zContext = shell_error_context(zSelect, p->db); |
| 22083 | - oputf("/**** ERROR: (%d) %s *****/\n%s", | |
| 23270 | + sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s", | |
| 22084 | 23271 | rc, sqlite3_errmsg(p->db), zContext); |
| 22085 | 23272 | sqlite3_free(zContext); |
| 22086 | 23273 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 22087 | 23274 | return rc; |
| 22088 | 23275 | } |
| 22089 | 23276 | rc = sqlite3_step(pSelect); |
| 22090 | 23277 | nResult = sqlite3_column_count(pSelect); |
| 22091 | 23278 | while( rc==SQLITE_ROW ){ |
| 22092 | 23279 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 22093 | - oputf("%s", z); | |
| 23280 | + sqlite3_fprintf(p->out, "%s", z); | |
| 22094 | 23281 | for(i=1; i<nResult; i++){ |
| 22095 | - oputf(",%s", sqlite3_column_text(pSelect, i)); | |
| 23282 | + sqlite3_fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i)); | |
| 22096 | 23283 | } |
| 22097 | 23284 | if( z==0 ) z = ""; |
| 22098 | 23285 | while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; |
| 22099 | 23286 | if( z[0] ){ |
| 22100 | - oputz("\n;\n"); | |
| 23287 | + sqlite3_fputs("\n;\n", p->out); | |
| 22101 | 23288 | }else{ |
| 22102 | - oputz(";\n"); | |
| 23289 | + sqlite3_fputs(";\n", p->out); | |
| 22103 | 23290 | } |
| 22104 | 23291 | rc = sqlite3_step(pSelect); |
| 22105 | 23292 | } |
| 22106 | 23293 | rc = sqlite3_finalize(pSelect); |
| 22107 | 23294 | if( rc!=SQLITE_OK ){ |
| 22108 | - oputf("/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); | |
| 23295 | + sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", | |
| 23296 | + rc, sqlite3_errmsg(p->db)); | |
| 22109 | 23297 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 22110 | 23298 | } |
| 22111 | 23299 | return rc; |
| 22112 | 23300 | } |
| 22113 | 23301 | |
| @@ -22139,17 +23327,17 @@ | ||
| 22139 | 23327 | |
| 22140 | 23328 | #ifdef __linux__ |
| 22141 | 23329 | /* |
| 22142 | 23330 | ** Attempt to display I/O stats on Linux using /proc/PID/io |
| 22143 | 23331 | */ |
| 22144 | -static void displayLinuxIoStats(void){ | |
| 23332 | +static void displayLinuxIoStats(FILE *out){ | |
| 22145 | 23333 | FILE *in; |
| 22146 | 23334 | char z[200]; |
| 22147 | 23335 | sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); |
| 22148 | - in = fopen(z, "rb"); | |
| 23336 | + in = sqlite3_fopen(z, "rb"); | |
| 22149 | 23337 | if( in==0 ) return; |
| 22150 | - while( fgets(z, sizeof(z), in)!=0 ){ | |
| 23338 | + while( sqlite3_fgets(z, sizeof(z), in)!=0 ){ | |
| 22151 | 23339 | static const struct { |
| 22152 | 23340 | const char *zPattern; |
| 22153 | 23341 | const char *zDesc; |
| 22154 | 23342 | } aTrans[] = { |
| 22155 | 23343 | { "rchar: ", "Bytes received by read():" }, |
| @@ -22162,11 +23350,11 @@ | ||
| 22162 | 23350 | }; |
| 22163 | 23351 | int i; |
| 22164 | 23352 | for(i=0; i<ArraySize(aTrans); i++){ |
| 22165 | 23353 | int n = strlen30(aTrans[i].zPattern); |
| 22166 | 23354 | if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){ |
| 22167 | - oputf("%-36s %s", aTrans[i].zDesc, &z[n]); | |
| 23355 | + sqlite3_fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); | |
| 22168 | 23356 | break; |
| 22169 | 23357 | } |
| 22170 | 23358 | } |
| 22171 | 23359 | } |
| 22172 | 23360 | fclose(in); |
| @@ -22175,10 +23363,11 @@ | ||
| 22175 | 23363 | |
| 22176 | 23364 | /* |
| 22177 | 23365 | ** Display a single line of status using 64-bit values. |
| 22178 | 23366 | */ |
| 22179 | 23367 | static void displayStatLine( |
| 23368 | + FILE *out, /* Write to this channel */ | |
| 22180 | 23369 | char *zLabel, /* Label for this one line */ |
| 22181 | 23370 | char *zFormat, /* Format for the result */ |
| 22182 | 23371 | int iStatusCtrl, /* Which status to display */ |
| 22183 | 23372 | int bReset /* True to reset the stats */ |
| 22184 | 23373 | ){ |
| @@ -22193,11 +23382,11 @@ | ||
| 22193 | 23382 | if( nPercent>1 ){ |
| 22194 | 23383 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); |
| 22195 | 23384 | }else{ |
| 22196 | 23385 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); |
| 22197 | 23386 | } |
| 22198 | - oputf("%-36s %s\n", zLabel, zLine); | |
| 23387 | + sqlite3_fprintf(out, "%-36s %s\n", zLabel, zLine); | |
| 22199 | 23388 | } |
| 22200 | 23389 | |
| 22201 | 23390 | /* |
| 22202 | 23391 | ** Display memory stats. |
| 22203 | 23392 | */ |
| @@ -22206,130 +23395,152 @@ | ||
| 22206 | 23395 | ShellState *pArg, /* Pointer to ShellState */ |
| 22207 | 23396 | int bReset /* True to reset the stats */ |
| 22208 | 23397 | ){ |
| 22209 | 23398 | int iCur; |
| 22210 | 23399 | int iHiwtr; |
| 23400 | + FILE *out; | |
| 22211 | 23401 | if( pArg==0 || pArg->out==0 ) return 0; |
| 23402 | + out = pArg->out; | |
| 22212 | 23403 | |
| 22213 | 23404 | if( pArg->pStmt && pArg->statsOn==2 ){ |
| 22214 | 23405 | int nCol, i, x; |
| 22215 | 23406 | sqlite3_stmt *pStmt = pArg->pStmt; |
| 22216 | 23407 | char z[100]; |
| 22217 | 23408 | nCol = sqlite3_column_count(pStmt); |
| 22218 | - oputf("%-36s %d\n", "Number of output columns:", nCol); | |
| 23409 | + sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol); | |
| 22219 | 23410 | for(i=0; i<nCol; i++){ |
| 22220 | 23411 | sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); |
| 22221 | - oputf("%-36s %s\n", z, sqlite3_column_name(pStmt,i)); | |
| 23412 | + sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); | |
| 22222 | 23413 | #ifndef SQLITE_OMIT_DECLTYPE |
| 22223 | 23414 | sqlite3_snprintf(30, z+x, "declared type:"); |
| 22224 | - oputf("%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); | |
| 23415 | + sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); | |
| 22225 | 23416 | #endif |
| 22226 | 23417 | #ifdef SQLITE_ENABLE_COLUMN_METADATA |
| 22227 | 23418 | sqlite3_snprintf(30, z+x, "database name:"); |
| 22228 | - oputf("%-36s %s\n", z, sqlite3_column_database_name(pStmt,i)); | |
| 23419 | + sqlite3_fprintf(out, "%-36s %s\n", z, | |
| 23420 | + sqlite3_column_database_name(pStmt,i)); | |
| 22229 | 23421 | sqlite3_snprintf(30, z+x, "table name:"); |
| 22230 | - oputf("%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); | |
| 23422 | + sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); | |
| 22231 | 23423 | sqlite3_snprintf(30, z+x, "origin name:"); |
| 22232 | - oputf("%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i)); | |
| 23424 | + sqlite3_fprintf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i)); | |
| 22233 | 23425 | #endif |
| 22234 | 23426 | } |
| 22235 | 23427 | } |
| 22236 | 23428 | |
| 22237 | 23429 | if( pArg->statsOn==3 ){ |
| 22238 | 23430 | if( pArg->pStmt ){ |
| 22239 | 23431 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); |
| 22240 | - oputf("VM-steps: %d\n", iCur); | |
| 23432 | + sqlite3_fprintf(out, "VM-steps: %d\n", iCur); | |
| 22241 | 23433 | } |
| 22242 | 23434 | return 0; |
| 22243 | 23435 | } |
| 22244 | 23436 | |
| 22245 | - displayStatLine("Memory Used:", | |
| 23437 | + displayStatLine(out, "Memory Used:", | |
| 22246 | 23438 | "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); |
| 22247 | - displayStatLine("Number of Outstanding Allocations:", | |
| 23439 | + displayStatLine(out, "Number of Outstanding Allocations:", | |
| 22248 | 23440 | "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); |
| 22249 | 23441 | if( pArg->shellFlgs & SHFLG_Pagecache ){ |
| 22250 | - displayStatLine("Number of Pcache Pages Used:", | |
| 23442 | + displayStatLine(out, "Number of Pcache Pages Used:", | |
| 22251 | 23443 | "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); |
| 22252 | 23444 | } |
| 22253 | - displayStatLine("Number of Pcache Overflow Bytes:", | |
| 23445 | + displayStatLine(out, "Number of Pcache Overflow Bytes:", | |
| 22254 | 23446 | "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); |
| 22255 | - displayStatLine("Largest Allocation:", | |
| 23447 | + displayStatLine(out, "Largest Allocation:", | |
| 22256 | 23448 | "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); |
| 22257 | - displayStatLine("Largest Pcache Allocation:", | |
| 23449 | + displayStatLine(out, "Largest Pcache Allocation:", | |
| 22258 | 23450 | "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); |
| 22259 | 23451 | #ifdef YYTRACKMAXSTACKDEPTH |
| 22260 | - displayStatLine("Deepest Parser Stack:", | |
| 23452 | + displayStatLine(out, "Deepest Parser Stack:", | |
| 22261 | 23453 | "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); |
| 22262 | 23454 | #endif |
| 22263 | 23455 | |
| 22264 | 23456 | if( db ){ |
| 22265 | 23457 | if( pArg->shellFlgs & SHFLG_Lookaside ){ |
| 22266 | 23458 | iHiwtr = iCur = -1; |
| 22267 | 23459 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, |
| 22268 | 23460 | &iCur, &iHiwtr, bReset); |
| 22269 | - oputf("Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); | |
| 23461 | + sqlite3_fprintf(out, | |
| 23462 | + "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); | |
| 22270 | 23463 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, |
| 22271 | 23464 | &iCur, &iHiwtr, bReset); |
| 22272 | - oputf("Successful lookaside attempts: %d\n", iHiwtr); | |
| 23465 | + sqlite3_fprintf(out, | |
| 23466 | + "Successful lookaside attempts: %d\n", iHiwtr); | |
| 22273 | 23467 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, |
| 22274 | 23468 | &iCur, &iHiwtr, bReset); |
| 22275 | - oputf("Lookaside failures due to size: %d\n", iHiwtr); | |
| 23469 | + sqlite3_fprintf(out, | |
| 23470 | + "Lookaside failures due to size: %d\n", iHiwtr); | |
| 22276 | 23471 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, |
| 22277 | 23472 | &iCur, &iHiwtr, bReset); |
| 22278 | - oputf("Lookaside failures due to OOM: %d\n", iHiwtr); | |
| 23473 | + sqlite3_fprintf(out, | |
| 23474 | + "Lookaside failures due to OOM: %d\n", iHiwtr); | |
| 22279 | 23475 | } |
| 22280 | 23476 | iHiwtr = iCur = -1; |
| 22281 | 23477 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); |
| 22282 | - oputf("Pager Heap Usage: %d bytes\n", iCur); | |
| 23478 | + sqlite3_fprintf(out, | |
| 23479 | + "Pager Heap Usage: %d bytes\n", iCur); | |
| 22283 | 23480 | iHiwtr = iCur = -1; |
| 22284 | 23481 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); |
| 22285 | - oputf("Page cache hits: %d\n", iCur); | |
| 23482 | + sqlite3_fprintf(out, | |
| 23483 | + "Page cache hits: %d\n", iCur); | |
| 22286 | 23484 | iHiwtr = iCur = -1; |
| 22287 | 23485 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); |
| 22288 | - oputf("Page cache misses: %d\n", iCur); | |
| 23486 | + sqlite3_fprintf(out, | |
| 23487 | + "Page cache misses: %d\n", iCur); | |
| 22289 | 23488 | iHiwtr = iCur = -1; |
| 22290 | 23489 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); |
| 22291 | - oputf("Page cache writes: %d\n", iCur); | |
| 23490 | + sqlite3_fprintf(out, | |
| 23491 | + "Page cache writes: %d\n", iCur); | |
| 22292 | 23492 | iHiwtr = iCur = -1; |
| 22293 | 23493 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); |
| 22294 | - oputf("Page cache spills: %d\n", iCur); | |
| 23494 | + sqlite3_fprintf(out, | |
| 23495 | + "Page cache spills: %d\n", iCur); | |
| 22295 | 23496 | iHiwtr = iCur = -1; |
| 22296 | 23497 | sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); |
| 22297 | - oputf("Schema Heap Usage: %d bytes\n", iCur); | |
| 23498 | + sqlite3_fprintf(out, | |
| 23499 | + "Schema Heap Usage: %d bytes\n", iCur); | |
| 22298 | 23500 | iHiwtr = iCur = -1; |
| 22299 | 23501 | sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); |
| 22300 | - oputf("Statement Heap/Lookaside Usage: %d bytes\n", iCur); | |
| 23502 | + sqlite3_fprintf(out, | |
| 23503 | + "Statement Heap/Lookaside Usage: %d bytes\n", iCur); | |
| 22301 | 23504 | } |
| 22302 | 23505 | |
| 22303 | 23506 | if( pArg->pStmt ){ |
| 22304 | 23507 | int iHit, iMiss; |
| 22305 | 23508 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, |
| 22306 | 23509 | bReset); |
| 22307 | - oputf("Fullscan Steps: %d\n", iCur); | |
| 23510 | + sqlite3_fprintf(out, | |
| 23511 | + "Fullscan Steps: %d\n", iCur); | |
| 22308 | 23512 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 22309 | - oputf("Sort Operations: %d\n", iCur); | |
| 23513 | + sqlite3_fprintf(out, | |
| 23514 | + "Sort Operations: %d\n", iCur); | |
| 22310 | 23515 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); |
| 22311 | - oputf("Autoindex Inserts: %d\n", iCur); | |
| 23516 | + sqlite3_fprintf(out, | |
| 23517 | + "Autoindex Inserts: %d\n", iCur); | |
| 22312 | 23518 | iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, |
| 22313 | 23519 | bReset); |
| 22314 | 23520 | iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, |
| 22315 | 23521 | bReset); |
| 22316 | 23522 | if( iHit || iMiss ){ |
| 22317 | - oputf("Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); | |
| 23523 | + sqlite3_fprintf(out, | |
| 23524 | + "Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); | |
| 22318 | 23525 | } |
| 22319 | 23526 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 22320 | - oputf("Virtual Machine Steps: %d\n", iCur); | |
| 23527 | + sqlite3_fprintf(out, | |
| 23528 | + "Virtual Machine Steps: %d\n", iCur); | |
| 22321 | 23529 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); |
| 22322 | - oputf("Reprepare operations: %d\n", iCur); | |
| 23530 | + sqlite3_fprintf(out, | |
| 23531 | + "Reprepare operations: %d\n", iCur); | |
| 22323 | 23532 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); |
| 22324 | - oputf("Number of times run: %d\n", iCur); | |
| 23533 | + sqlite3_fprintf(out, | |
| 23534 | + "Number of times run: %d\n", iCur); | |
| 22325 | 23535 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); |
| 22326 | - oputf("Memory used by prepared stmt: %d\n", iCur); | |
| 23536 | + sqlite3_fprintf(out, | |
| 23537 | + "Memory used by prepared stmt: %d\n", iCur); | |
| 22327 | 23538 | } |
| 22328 | 23539 | |
| 22329 | 23540 | #ifdef __linux__ |
| 22330 | - displayLinuxIoStats(); | |
| 23541 | + displayLinuxIoStats(pArg->out); | |
| 22331 | 23542 | #endif |
| 22332 | 23543 | |
| 22333 | 23544 | /* Do not remove this machine readable comment: extra-stats-output-here */ |
| 22334 | 23545 | |
| 22335 | 23546 | return 0; |
| @@ -22721,21 +23932,21 @@ | ||
| 22721 | 23932 | #define BOX_1234 "\342\224\274" /* U+253c -|- */ |
| 22722 | 23933 | |
| 22723 | 23934 | /* Draw horizontal line N characters long using unicode box |
| 22724 | 23935 | ** characters |
| 22725 | 23936 | */ |
| 22726 | -static void print_box_line(int N){ | |
| 23937 | +static void print_box_line(FILE *out, int N){ | |
| 22727 | 23938 | const char zDash[] = |
| 22728 | 23939 | BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 |
| 22729 | 23940 | BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; |
| 22730 | 23941 | const int nDash = sizeof(zDash) - 1; |
| 22731 | 23942 | N *= 3; |
| 22732 | 23943 | while( N>nDash ){ |
| 22733 | - oputz(zDash); | |
| 23944 | + sqlite3_fputs(zDash, out); | |
| 22734 | 23945 | N -= nDash; |
| 22735 | 23946 | } |
| 22736 | - oputf("%.*s", N, zDash); | |
| 23947 | + sqlite3_fprintf(out, "%.*s", N, zDash); | |
| 22737 | 23948 | } |
| 22738 | 23949 | |
| 22739 | 23950 | /* |
| 22740 | 23951 | ** Draw a horizontal separator for a MODE_Box table. |
| 22741 | 23952 | */ |
| @@ -22746,19 +23957,19 @@ | ||
| 22746 | 23957 | const char *zSep2, |
| 22747 | 23958 | const char *zSep3 |
| 22748 | 23959 | ){ |
| 22749 | 23960 | int i; |
| 22750 | 23961 | if( nArg>0 ){ |
| 22751 | - oputz(zSep1); | |
| 22752 | - print_box_line(p->actualWidth[0]+2); | |
| 23962 | + sqlite3_fputs(zSep1, p->out); | |
| 23963 | + print_box_line(p->out, p->actualWidth[0]+2); | |
| 22753 | 23964 | for(i=1; i<nArg; i++){ |
| 22754 | - oputz(zSep2); | |
| 22755 | - print_box_line(p->actualWidth[i]+2); | |
| 23965 | + sqlite3_fputs(zSep2, p->out); | |
| 23966 | + print_box_line(p->out, p->actualWidth[i]+2); | |
| 22756 | 23967 | } |
| 22757 | - oputz(zSep3); | |
| 23968 | + sqlite3_fputs(zSep3, p->out); | |
| 22758 | 23969 | } |
| 22759 | - oputz("\n"); | |
| 23970 | + sqlite3_fputs("\n", p->out); | |
| 22760 | 23971 | } |
| 22761 | 23972 | |
| 22762 | 23973 | /* |
| 22763 | 23974 | ** z[] is a line of text that is to be displayed the .mode box or table or |
| 22764 | 23975 | ** similar tabular formats. z[] might contain control characters such |
| @@ -22788,16 +23999,26 @@ | ||
| 22788 | 23999 | } |
| 22789 | 24000 | if( mxWidth<0 ) mxWidth = -mxWidth; |
| 22790 | 24001 | if( mxWidth==0 ) mxWidth = 1000000; |
| 22791 | 24002 | i = j = n = 0; |
| 22792 | 24003 | while( n<mxWidth ){ |
| 22793 | - if( z[i]>=' ' ){ | |
| 24004 | + unsigned char c = z[i]; | |
| 24005 | + if( c>=0xc0 ){ | |
| 24006 | + int u; | |
| 24007 | + int len = decodeUtf8(&z[i], &u); | |
| 24008 | + i += len; | |
| 24009 | + j += len; | |
| 24010 | + n += cli_wcwidth(u); | |
| 24011 | + continue; | |
| 24012 | + } | |
| 24013 | + if( c>=' ' ){ | |
| 22794 | 24014 | n++; |
| 22795 | - do{ i++; j++; }while( (z[i]&0xc0)==0x80 ); | |
| 24015 | + i++; | |
| 24016 | + j++; | |
| 22796 | 24017 | continue; |
| 22797 | 24018 | } |
| 22798 | - if( z[i]=='\t' ){ | |
| 24019 | + if( c=='\t' ){ | |
| 22799 | 24020 | do{ |
| 22800 | 24021 | n++; |
| 22801 | 24022 | j++; |
| 22802 | 24023 | }while( (n&7)!=0 && n<mxWidth ); |
| 22803 | 24024 | i++; |
| @@ -22835,13 +24056,21 @@ | ||
| 22835 | 24056 | } |
| 22836 | 24057 | zOut = malloc( j+1 ); |
| 22837 | 24058 | shell_check_oom(zOut); |
| 22838 | 24059 | i = j = n = 0; |
| 22839 | 24060 | while( i<k ){ |
| 22840 | - if( z[i]>=' ' ){ | |
| 24061 | + unsigned char c = z[i]; | |
| 24062 | + if( c>=0xc0 ){ | |
| 24063 | + int u; | |
| 24064 | + int len = decodeUtf8(&z[i], &u); | |
| 24065 | + do{ zOut[j++] = z[i++]; }while( (--len)>0 ); | |
| 24066 | + n += cli_wcwidth(u); | |
| 24067 | + continue; | |
| 24068 | + } | |
| 24069 | + if( c>=' ' ){ | |
| 22841 | 24070 | n++; |
| 22842 | - do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 ); | |
| 24071 | + zOut[j++] = z[i++]; | |
| 22843 | 24072 | continue; |
| 22844 | 24073 | } |
| 22845 | 24074 | if( z[i]=='\t' ){ |
| 22846 | 24075 | do{ |
| 22847 | 24076 | n++; |
| @@ -23017,97 +24246,99 @@ | ||
| 23017 | 24246 | rowSep = "\n"; |
| 23018 | 24247 | if( p->showHeader ){ |
| 23019 | 24248 | for(i=0; i<nColumn; i++){ |
| 23020 | 24249 | w = p->actualWidth[i]; |
| 23021 | 24250 | if( p->colWidth[i]<0 ) w = -w; |
| 23022 | - utf8_width_print(w, azData[i]); | |
| 23023 | - fputs(i==nColumn-1?"\n":" ", p->out); | |
| 24251 | + utf8_width_print(p->out, w, azData[i]); | |
| 24252 | + sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); | |
| 23024 | 24253 | } |
| 23025 | 24254 | for(i=0; i<nColumn; i++){ |
| 23026 | - print_dashes(p->actualWidth[i]); | |
| 23027 | - fputs(i==nColumn-1?"\n":" ", p->out); | |
| 24255 | + print_dashes(p->out, p->actualWidth[i]); | |
| 24256 | + sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); | |
| 23028 | 24257 | } |
| 23029 | 24258 | } |
| 23030 | 24259 | break; |
| 23031 | 24260 | } |
| 23032 | 24261 | case MODE_Table: { |
| 23033 | 24262 | colSep = " | "; |
| 23034 | 24263 | rowSep = " |\n"; |
| 23035 | 24264 | print_row_separator(p, nColumn, "+"); |
| 23036 | - fputs("| ", p->out); | |
| 24265 | + sqlite3_fputs("| ", p->out); | |
| 23037 | 24266 | for(i=0; i<nColumn; i++){ |
| 23038 | 24267 | w = p->actualWidth[i]; |
| 23039 | 24268 | n = strlenChar(azData[i]); |
| 23040 | - oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); | |
| 23041 | - oputz(i==nColumn-1?" |\n":" | "); | |
| 24269 | + sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", | |
| 24270 | + azData[i], (w-n+1)/2, ""); | |
| 24271 | + sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); | |
| 23042 | 24272 | } |
| 23043 | 24273 | print_row_separator(p, nColumn, "+"); |
| 23044 | 24274 | break; |
| 23045 | 24275 | } |
| 23046 | 24276 | case MODE_Markdown: { |
| 23047 | 24277 | colSep = " | "; |
| 23048 | 24278 | rowSep = " |\n"; |
| 23049 | - fputs("| ", p->out); | |
| 24279 | + sqlite3_fputs("| ", p->out); | |
| 23050 | 24280 | for(i=0; i<nColumn; i++){ |
| 23051 | 24281 | w = p->actualWidth[i]; |
| 23052 | 24282 | n = strlenChar(azData[i]); |
| 23053 | - oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); | |
| 23054 | - oputz(i==nColumn-1?" |\n":" | "); | |
| 24283 | + sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", | |
| 24284 | + azData[i], (w-n+1)/2, ""); | |
| 24285 | + sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); | |
| 23055 | 24286 | } |
| 23056 | 24287 | print_row_separator(p, nColumn, "|"); |
| 23057 | 24288 | break; |
| 23058 | 24289 | } |
| 23059 | 24290 | case MODE_Box: { |
| 23060 | 24291 | colSep = " " BOX_13 " "; |
| 23061 | 24292 | rowSep = " " BOX_13 "\n"; |
| 23062 | 24293 | print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); |
| 23063 | - oputz(BOX_13 " "); | |
| 24294 | + sqlite3_fputs(BOX_13 " ", p->out); | |
| 23064 | 24295 | for(i=0; i<nColumn; i++){ |
| 23065 | 24296 | w = p->actualWidth[i]; |
| 23066 | 24297 | n = strlenChar(azData[i]); |
| 23067 | - oputf("%*s%s%*s%s", | |
| 24298 | + sqlite3_fprintf(p->out, "%*s%s%*s%s", | |
| 23068 | 24299 | (w-n)/2, "", azData[i], (w-n+1)/2, "", |
| 23069 | 24300 | i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); |
| 23070 | 24301 | } |
| 23071 | 24302 | print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 23072 | 24303 | break; |
| 23073 | 24304 | } |
| 23074 | 24305 | } |
| 23075 | 24306 | for(i=nColumn, j=0; i<nTotal; i++, j++){ |
| 23076 | 24307 | if( j==0 && p->cMode!=MODE_Column ){ |
| 23077 | - oputz(p->cMode==MODE_Box?BOX_13" ":"| "); | |
| 24308 | + sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out); | |
| 23078 | 24309 | } |
| 23079 | 24310 | z = azData[i]; |
| 23080 | 24311 | if( z==0 ) z = p->nullValue; |
| 23081 | 24312 | w = p->actualWidth[j]; |
| 23082 | 24313 | if( p->colWidth[j]<0 ) w = -w; |
| 23083 | - utf8_width_print(w, z); | |
| 24314 | + utf8_width_print(p->out, w, z); | |
| 23084 | 24315 | if( j==nColumn-1 ){ |
| 23085 | - oputz(rowSep); | |
| 24316 | + sqlite3_fputs(rowSep, p->out); | |
| 23086 | 24317 | if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){ |
| 23087 | 24318 | if( p->cMode==MODE_Table ){ |
| 23088 | 24319 | print_row_separator(p, nColumn, "+"); |
| 23089 | 24320 | }else if( p->cMode==MODE_Box ){ |
| 23090 | 24321 | print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 23091 | 24322 | }else if( p->cMode==MODE_Column ){ |
| 23092 | - oputz("\n"); | |
| 24323 | + sqlite3_fputs("\n", p->out); | |
| 23093 | 24324 | } |
| 23094 | 24325 | } |
| 23095 | 24326 | j = -1; |
| 23096 | 24327 | if( seenInterrupt ) goto columnar_end; |
| 23097 | 24328 | }else{ |
| 23098 | - oputz(colSep); | |
| 24329 | + sqlite3_fputs(colSep, p->out); | |
| 23099 | 24330 | } |
| 23100 | 24331 | } |
| 23101 | 24332 | if( p->cMode==MODE_Table ){ |
| 23102 | 24333 | print_row_separator(p, nColumn, "+"); |
| 23103 | 24334 | }else if( p->cMode==MODE_Box ){ |
| 23104 | 24335 | print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); |
| 23105 | 24336 | } |
| 23106 | 24337 | columnar_end: |
| 23107 | 24338 | if( seenInterrupt ){ |
| 23108 | - oputz("Interrupt\n"); | |
| 24339 | + sqlite3_fputs("Interrupt\n", p->out); | |
| 23109 | 24340 | } |
| 23110 | 24341 | nData = (nRow+1)*nColumn; |
| 23111 | 24342 | for(i=0; i<nData; i++){ |
| 23112 | 24343 | z = azData[i]; |
| 23113 | 24344 | if( z!=zEmpty && z!=zShowNull ) free(azData[i]); |
| @@ -23190,11 +24421,13 @@ | ||
| 23190 | 24421 | } |
| 23191 | 24422 | } |
| 23192 | 24423 | } while( SQLITE_ROW == rc ); |
| 23193 | 24424 | sqlite3_free(pData); |
| 23194 | 24425 | if( pArg->cMode==MODE_Json ){ |
| 23195 | - fputs("]\n", pArg->out); | |
| 24426 | + sqlite3_fputs("]\n", pArg->out); | |
| 24427 | + }else if( pArg->cMode==MODE_Www ){ | |
| 24428 | + sqlite3_fputs("</TABLE>\n<PRE>\n", pArg->out); | |
| 23196 | 24429 | }else if( pArg->cMode==MODE_Count ){ |
| 23197 | 24430 | char zBuf[200]; |
| 23198 | 24431 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n", |
| 23199 | 24432 | nRow, nRow!=1 ? "s" : ""); |
| 23200 | 24433 | printf("%s", zBuf); |
| @@ -23239,10 +24472,11 @@ | ||
| 23239 | 24472 | int bCancel, |
| 23240 | 24473 | char **pzErr |
| 23241 | 24474 | ){ |
| 23242 | 24475 | int rc = SQLITE_OK; |
| 23243 | 24476 | sqlite3expert *p = pState->expert.pExpert; |
| 24477 | + FILE *out = pState->out; | |
| 23244 | 24478 | assert( p ); |
| 23245 | 24479 | assert( bCancel || pzErr==0 || *pzErr==0 ); |
| 23246 | 24480 | if( bCancel==0 ){ |
| 23247 | 24481 | int bVerbose = pState->expert.bVerbose; |
| 23248 | 24482 | |
| @@ -23251,24 +24485,25 @@ | ||
| 23251 | 24485 | int nQuery = sqlite3_expert_count(p); |
| 23252 | 24486 | int i; |
| 23253 | 24487 | |
| 23254 | 24488 | if( bVerbose ){ |
| 23255 | 24489 | const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); |
| 23256 | - oputz("-- Candidates -----------------------------\n"); | |
| 23257 | - oputf("%s\n", zCand); | |
| 24490 | + sqlite3_fputs("-- Candidates -----------------------------\n", out); | |
| 24491 | + sqlite3_fprintf(out, "%s\n", zCand); | |
| 23258 | 24492 | } |
| 23259 | 24493 | for(i=0; i<nQuery; i++){ |
| 23260 | 24494 | const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL); |
| 23261 | 24495 | const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES); |
| 23262 | 24496 | const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN); |
| 23263 | 24497 | if( zIdx==0 ) zIdx = "(no new indexes)\n"; |
| 23264 | 24498 | if( bVerbose ){ |
| 23265 | - oputf("-- Query %d --------------------------------\n",i+1); | |
| 23266 | - oputf("%s\n\n", zSql); | |
| 24499 | + sqlite3_fprintf(out, | |
| 24500 | + "-- Query %d --------------------------------\n" | |
| 24501 | + "%s\n\n" | |
| 24502 | + ,i+1, zSql); | |
| 23267 | 24503 | } |
| 23268 | - oputf("%s\n", zIdx); | |
| 23269 | - oputf("%s\n", zEQP); | |
| 24504 | + sqlite3_fprintf(out, "%s\n%s\n", zIdx, zEQP); | |
| 23270 | 24505 | } |
| 23271 | 24506 | } |
| 23272 | 24507 | } |
| 23273 | 24508 | sqlite3_expert_destroy(p); |
| 23274 | 24509 | pState->expert.pExpert = 0; |
| @@ -23299,30 +24534,31 @@ | ||
| 23299 | 24534 | if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){ |
| 23300 | 24535 | pState->expert.bVerbose = 1; |
| 23301 | 24536 | } |
| 23302 | 24537 | else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){ |
| 23303 | 24538 | if( i==(nArg-1) ){ |
| 23304 | - eputf("option requires an argument: %s\n", z); | |
| 24539 | + sqlite3_fprintf(stderr, "option requires an argument: %s\n", z); | |
| 23305 | 24540 | rc = SQLITE_ERROR; |
| 23306 | 24541 | }else{ |
| 23307 | 24542 | iSample = (int)integerValue(azArg[++i]); |
| 23308 | 24543 | if( iSample<0 || iSample>100 ){ |
| 23309 | - eputf("value out of range: %s\n", azArg[i]); | |
| 24544 | + sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]); | |
| 23310 | 24545 | rc = SQLITE_ERROR; |
| 23311 | 24546 | } |
| 23312 | 24547 | } |
| 23313 | 24548 | } |
| 23314 | 24549 | else{ |
| 23315 | - eputf("unknown option: %s\n", z); | |
| 24550 | + sqlite3_fprintf(stderr,"unknown option: %s\n", z); | |
| 23316 | 24551 | rc = SQLITE_ERROR; |
| 23317 | 24552 | } |
| 23318 | 24553 | } |
| 23319 | 24554 | |
| 23320 | 24555 | if( rc==SQLITE_OK ){ |
| 23321 | 24556 | pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); |
| 23322 | 24557 | if( pState->expert.pExpert==0 ){ |
| 23323 | - eputf("sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); | |
| 24558 | + sqlite3_fprintf(stderr, | |
| 24559 | + "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); | |
| 23324 | 24560 | rc = SQLITE_ERROR; |
| 23325 | 24561 | }else{ |
| 23326 | 24562 | sqlite3_expert_config( |
| 23327 | 24563 | pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample |
| 23328 | 24564 | ); |
| @@ -23647,33 +24883,33 @@ | ||
| 23647 | 24883 | if( zType==0 ) return 0; |
| 23648 | 24884 | dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0; |
| 23649 | 24885 | noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; |
| 23650 | 24886 | |
| 23651 | 24887 | if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ |
| 23652 | - if( !dataOnly ) oputz("DELETE FROM sqlite_sequence;\n"); | |
| 24888 | + /* no-op */ | |
| 23653 | 24889 | }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ |
| 23654 | - if( !dataOnly ) oputz("ANALYZE sqlite_schema;\n"); | |
| 24890 | + if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); | |
| 23655 | 24891 | }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ |
| 23656 | 24892 | return 0; |
| 23657 | 24893 | }else if( dataOnly ){ |
| 23658 | 24894 | /* no-op */ |
| 23659 | 24895 | }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ |
| 23660 | 24896 | char *zIns; |
| 23661 | 24897 | if( !p->writableSchema ){ |
| 23662 | - oputz("PRAGMA writable_schema=ON;\n"); | |
| 24898 | + sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out); | |
| 23663 | 24899 | p->writableSchema = 1; |
| 23664 | 24900 | } |
| 23665 | 24901 | zIns = sqlite3_mprintf( |
| 23666 | 24902 | "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" |
| 23667 | 24903 | "VALUES('table','%q','%q',0,'%q');", |
| 23668 | 24904 | zTable, zTable, zSql); |
| 23669 | 24905 | shell_check_oom(zIns); |
| 23670 | - oputf("%s\n", zIns); | |
| 24906 | + sqlite3_fprintf(p->out, "%s\n", zIns); | |
| 23671 | 24907 | sqlite3_free(zIns); |
| 23672 | 24908 | return 0; |
| 23673 | 24909 | }else{ |
| 23674 | - printSchemaLine(zSql, ";\n"); | |
| 24910 | + printSchemaLine(p->out, zSql, ";\n"); | |
| 23675 | 24911 | } |
| 23676 | 24912 | |
| 23677 | 24913 | if( cli_strcmp(zType, "table")==0 ){ |
| 23678 | 24914 | ShellText sSelect; |
| 23679 | 24915 | ShellText sTable; |
| @@ -23727,11 +24963,11 @@ | ||
| 23727 | 24963 | savedMode = p->mode; |
| 23728 | 24964 | p->zDestTable = sTable.z; |
| 23729 | 24965 | p->mode = p->cMode = MODE_Insert; |
| 23730 | 24966 | rc = shell_exec(p, sSelect.z, 0); |
| 23731 | 24967 | if( (rc&0xff)==SQLITE_CORRUPT ){ |
| 23732 | - oputz("/****** CORRUPTION ERROR *******/\n"); | |
| 24968 | + sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); | |
| 23733 | 24969 | toggleSelectOrder(p->db); |
| 23734 | 24970 | shell_exec(p, sSelect.z, 0); |
| 23735 | 24971 | toggleSelectOrder(p->db); |
| 23736 | 24972 | } |
| 23737 | 24973 | p->zDestTable = savedDestTable; |
| @@ -23758,28 +24994,28 @@ | ||
| 23758 | 24994 | char *zErr = 0; |
| 23759 | 24995 | rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); |
| 23760 | 24996 | if( rc==SQLITE_CORRUPT ){ |
| 23761 | 24997 | char *zQ2; |
| 23762 | 24998 | int len = strlen30(zQuery); |
| 23763 | - oputz("/****** CORRUPTION ERROR *******/\n"); | |
| 24999 | + sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); | |
| 23764 | 25000 | if( zErr ){ |
| 23765 | - oputf("/****** %s ******/\n", zErr); | |
| 25001 | + sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr); | |
| 23766 | 25002 | sqlite3_free(zErr); |
| 23767 | 25003 | zErr = 0; |
| 23768 | 25004 | } |
| 23769 | 25005 | zQ2 = malloc( len+100 ); |
| 23770 | 25006 | if( zQ2==0 ) return rc; |
| 23771 | 25007 | sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); |
| 23772 | 25008 | rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); |
| 23773 | 25009 | if( rc ){ |
| 23774 | - oputf("/****** ERROR: %s ******/\n", zErr); | |
| 25010 | + sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); | |
| 23775 | 25011 | }else{ |
| 23776 | 25012 | rc = SQLITE_CORRUPT; |
| 23777 | 25013 | } |
| 23778 | - sqlite3_free(zErr); | |
| 23779 | 25014 | free(zQ2); |
| 23780 | 25015 | } |
| 25016 | + sqlite3_free(zErr); | |
| 23781 | 25017 | return rc; |
| 23782 | 25018 | } |
| 23783 | 25019 | |
| 23784 | 25020 | /* |
| 23785 | 25021 | ** Text of help messages. |
| @@ -23832,18 +25068,17 @@ | ||
| 23832 | 25068 | #ifndef SQLITE_SHELL_FIDDLE |
| 23833 | 25069 | ".check GLOB Fail if output since .testcase does not match", |
| 23834 | 25070 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 23835 | 25071 | #endif |
| 23836 | 25072 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 23837 | -#if defined(_WIN32) || defined(WIN32) | |
| 23838 | - ".crnl on|off Translate \\n to \\r\\n. Default ON", | |
| 23839 | -#endif | |
| 25073 | + ".crlf ?on|off? Whether or not to use \\r\\n line endings", | |
| 23840 | 25074 | ".databases List names and files of attached databases", |
| 23841 | 25075 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 23842 | 25076 | #if SQLITE_SHELL_HAVE_RECOVER |
| 23843 | 25077 | ".dbinfo ?DB? Show status information about the database", |
| 23844 | 25078 | #endif |
| 25079 | + ".dbtotxt Hex dump of the database file", | |
| 23845 | 25080 | ".dump ?OBJECTS? Render database content as SQL", |
| 23846 | 25081 | " Options:", |
| 23847 | 25082 | " --data-only Output only INSERT statements", |
| 23848 | 25083 | " --newlines Allow unescaped newline characters in output", |
| 23849 | 25084 | " --nosys Omit system tables (ex: \"sqlite_stat1\")", |
| @@ -23940,13 +25175,15 @@ | ||
| 23940 | 25175 | #endif |
| 23941 | 25176 | ".nullvalue STRING Use STRING in place of NULL values", |
| 23942 | 25177 | #ifndef SQLITE_SHELL_FIDDLE |
| 23943 | 25178 | ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", |
| 23944 | 25179 | " If FILE begins with '|' then open as a pipe", |
| 23945 | - " --bom Put a UTF8 byte-order mark at the beginning", | |
| 23946 | - " -e Send output to the system text editor", | |
| 23947 | - " -x Send output as CSV to a spreadsheet (same as \".excel\")", | |
| 25180 | + " --bom Put a UTF8 byte-order mark at the beginning", | |
| 25181 | + " -e Send output to the system text editor", | |
| 25182 | + " --plain Use text/plain output instead of HTML for -w option", | |
| 25183 | + " -w Send output as HTML to a web browser (same as \".www\")", | |
| 25184 | + " -x Send output as CSV to a spreadsheet (same as \".excel\")", | |
| 23948 | 25185 | /* Note that .open is (partially) available in WASM builds but is |
| 23949 | 25186 | ** currently only intended to be used by the fiddle tool, not |
| 23950 | 25187 | ** end users, so is "undocumented." */ |
| 23951 | 25188 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 23952 | 25189 | " Options:", |
| @@ -23965,10 +25202,12 @@ | ||
| 23965 | 25202 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 23966 | 25203 | " If FILE begins with '|' then open it as a pipe.", |
| 23967 | 25204 | " Options:", |
| 23968 | 25205 | " --bom Prefix output with a UTF8 byte-order mark", |
| 23969 | 25206 | " -e Send output to the system text editor", |
| 25207 | + " --plain Use text/plain for -w option", | |
| 25208 | + " -w Send output to a web browser", | |
| 23970 | 25209 | " -x Send output as CSV to a spreadsheet", |
| 23971 | 25210 | #endif |
| 23972 | 25211 | ".parameter CMD ... Manage SQL parameter bindings", |
| 23973 | 25212 | " clear Erase all bindings", |
| 23974 | 25213 | " init Initialize the TEMP table that holds bindings", |
| @@ -24078,10 +25317,14 @@ | ||
| 24078 | 25317 | ".vfsinfo ?AUX? Information about the top-level VFS", |
| 24079 | 25318 | ".vfslist List all available VFSes", |
| 24080 | 25319 | ".vfsname ?AUX? Print the name of the VFS stack", |
| 24081 | 25320 | ".width NUM1 NUM2 ... Set minimum column widths for columnar output", |
| 24082 | 25321 | " Negative values right-justify", |
| 25322 | +#ifndef SQLITE_SHELL_FIDDLE | |
| 25323 | + ".www Display output of the next command in web browser", | |
| 25324 | + " --plain Show results as text/plain, not as HTML", | |
| 25325 | +#endif | |
| 24083 | 25326 | }; |
| 24084 | 25327 | |
| 24085 | 25328 | /* |
| 24086 | 25329 | ** Output help text. |
| 24087 | 25330 | ** |
| @@ -24126,24 +25369,24 @@ | ||
| 24126 | 25369 | hh &= ~HH_Summary; |
| 24127 | 25370 | break; |
| 24128 | 25371 | } |
| 24129 | 25372 | if( ((hw^hh)&HH_Undoc)==0 ){ |
| 24130 | 25373 | if( (hh&HH_Summary)!=0 ){ |
| 24131 | - sputf(out, ".%s\n", azHelp[i]+1); | |
| 25374 | + sqlite3_fprintf(out, ".%s\n", azHelp[i]+1); | |
| 24132 | 25375 | ++n; |
| 24133 | 25376 | }else if( (hw&HW_SummaryOnly)==0 ){ |
| 24134 | - sputf(out, "%s\n", azHelp[i]); | |
| 25377 | + sqlite3_fprintf(out, "%s\n", azHelp[i]); | |
| 24135 | 25378 | } |
| 24136 | 25379 | } |
| 24137 | 25380 | } |
| 24138 | 25381 | }else{ |
| 24139 | 25382 | /* Seek documented commands for which zPattern is an exact prefix */ |
| 24140 | 25383 | zPat = sqlite3_mprintf(".%s*", zPattern); |
| 24141 | 25384 | shell_check_oom(zPat); |
| 24142 | 25385 | for(i=0; i<ArraySize(azHelp); i++){ |
| 24143 | 25386 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 24144 | - sputf(out, "%s\n", azHelp[i]); | |
| 25387 | + sqlite3_fprintf(out, "%s\n", azHelp[i]); | |
| 24145 | 25388 | j = i+1; |
| 24146 | 25389 | n++; |
| 24147 | 25390 | } |
| 24148 | 25391 | } |
| 24149 | 25392 | sqlite3_free(zPat); |
| @@ -24150,11 +25393,11 @@ | ||
| 24150 | 25393 | if( n ){ |
| 24151 | 25394 | if( n==1 ){ |
| 24152 | 25395 | /* when zPattern is a prefix of exactly one command, then include |
| 24153 | 25396 | ** the details of that command, which should begin at offset j */ |
| 24154 | 25397 | while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
| 24155 | - sputf(out, "%s\n", azHelp[j]); | |
| 25398 | + sqlite3_fprintf(out, "%s\n", azHelp[j]); | |
| 24156 | 25399 | j++; |
| 24157 | 25400 | } |
| 24158 | 25401 | } |
| 24159 | 25402 | return n; |
| 24160 | 25403 | } |
| @@ -24167,14 +25410,14 @@ | ||
| 24167 | 25410 | while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; |
| 24168 | 25411 | continue; |
| 24169 | 25412 | } |
| 24170 | 25413 | if( azHelp[i][0]=='.' ) j = i; |
| 24171 | 25414 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 24172 | - sputf(out, "%s\n", azHelp[j]); | |
| 25415 | + sqlite3_fprintf(out, "%s\n", azHelp[j]); | |
| 24173 | 25416 | while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ |
| 24174 | 25417 | j++; |
| 24175 | - sputf(out, "%s\n", azHelp[j]); | |
| 25418 | + sqlite3_fprintf(out, "%s\n", azHelp[j]); | |
| 24176 | 25419 | } |
| 24177 | 25420 | i = j; |
| 24178 | 25421 | n++; |
| 24179 | 25422 | } |
| 24180 | 25423 | } |
| @@ -24200,35 +25443,35 @@ | ||
| 24200 | 25443 | ** |
| 24201 | 25444 | ** NULL is returned if any error is encountered. The final value of *pnByte |
| 24202 | 25445 | ** is undefined in this case. |
| 24203 | 25446 | */ |
| 24204 | 25447 | static char *readFile(const char *zName, int *pnByte){ |
| 24205 | - FILE *in = fopen(zName, "rb"); | |
| 25448 | + FILE *in = sqlite3_fopen(zName, "rb"); | |
| 24206 | 25449 | long nIn; |
| 24207 | 25450 | size_t nRead; |
| 24208 | 25451 | char *pBuf; |
| 24209 | 25452 | int rc; |
| 24210 | 25453 | if( in==0 ) return 0; |
| 24211 | 25454 | rc = fseek(in, 0, SEEK_END); |
| 24212 | 25455 | if( rc!=0 ){ |
| 24213 | - eputf("Error: '%s' not seekable\n", zName); | |
| 25456 | + sqlite3_fprintf(stderr,"Error: '%s' not seekable\n", zName); | |
| 24214 | 25457 | fclose(in); |
| 24215 | 25458 | return 0; |
| 24216 | 25459 | } |
| 24217 | 25460 | nIn = ftell(in); |
| 24218 | 25461 | rewind(in); |
| 24219 | 25462 | pBuf = sqlite3_malloc64( nIn+1 ); |
| 24220 | 25463 | if( pBuf==0 ){ |
| 24221 | - eputz("Error: out of memory\n"); | |
| 25464 | + sqlite3_fputs("Error: out of memory\n", stderr); | |
| 24222 | 25465 | fclose(in); |
| 24223 | 25466 | return 0; |
| 24224 | 25467 | } |
| 24225 | 25468 | nRead = fread(pBuf, nIn, 1, in); |
| 24226 | 25469 | fclose(in); |
| 24227 | 25470 | if( nRead!=1 ){ |
| 24228 | 25471 | sqlite3_free(pBuf); |
| 24229 | - eputf("Error: cannot read '%s'\n", zName); | |
| 25472 | + sqlite3_fprintf(stderr,"Error: cannot read '%s'\n", zName); | |
| 24230 | 25473 | return 0; |
| 24231 | 25474 | } |
| 24232 | 25475 | pBuf[nIn] = 0; |
| 24233 | 25476 | if( pnByte ) *pnByte = nIn; |
| 24234 | 25477 | return pBuf; |
| @@ -24290,11 +25533,11 @@ | ||
| 24290 | 25533 | ** archive and the dfltZip flag is true, then assume it is a ZIP archive. |
| 24291 | 25534 | ** Otherwise, assume an ordinary database regardless of the filename if |
| 24292 | 25535 | ** the type cannot be determined from content. |
| 24293 | 25536 | */ |
| 24294 | 25537 | int deduceDatabaseType(const char *zName, int dfltZip){ |
| 24295 | - FILE *f = fopen(zName, "rb"); | |
| 25538 | + FILE *f = sqlite3_fopen(zName, "rb"); | |
| 24296 | 25539 | size_t n; |
| 24297 | 25540 | int rc = SHELL_OPEN_UNSPEC; |
| 24298 | 25541 | char zBuf[100]; |
| 24299 | 25542 | if( f==0 ){ |
| 24300 | 25543 | if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ |
| @@ -24343,13 +25586,13 @@ | ||
| 24343 | 25586 | FILE *in; |
| 24344 | 25587 | const char *zDbFilename = p->pAuxDb->zDbFilename; |
| 24345 | 25588 | unsigned int x[16]; |
| 24346 | 25589 | char zLine[1000]; |
| 24347 | 25590 | if( zDbFilename ){ |
| 24348 | - in = fopen(zDbFilename, "r"); | |
| 25591 | + in = sqlite3_fopen(zDbFilename, "r"); | |
| 24349 | 25592 | if( in==0 ){ |
| 24350 | - eputf("cannot open \"%s\" for reading\n", zDbFilename); | |
| 25593 | + sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename); | |
| 24351 | 25594 | return 0; |
| 24352 | 25595 | } |
| 24353 | 25596 | nLine = 0; |
| 24354 | 25597 | }else{ |
| 24355 | 25598 | in = p->in; |
| @@ -24356,24 +25599,24 @@ | ||
| 24356 | 25599 | nLine = p->lineno; |
| 24357 | 25600 | if( in==0 ) in = stdin; |
| 24358 | 25601 | } |
| 24359 | 25602 | *pnData = 0; |
| 24360 | 25603 | nLine++; |
| 24361 | - if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; | |
| 25604 | + if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; | |
| 24362 | 25605 | rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); |
| 24363 | 25606 | if( rc!=2 ) goto readHexDb_error; |
| 24364 | 25607 | if( n<0 ) goto readHexDb_error; |
| 24365 | 25608 | if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error; |
| 24366 | 25609 | n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */ |
| 24367 | 25610 | a = sqlite3_malloc( n ? n : 1 ); |
| 24368 | 25611 | shell_check_oom(a); |
| 24369 | 25612 | memset(a, 0, n); |
| 24370 | 25613 | if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ |
| 24371 | - eputz("invalid pagesize\n"); | |
| 25614 | + sqlite3_fputs("invalid pagesize\n", stderr); | |
| 24372 | 25615 | goto readHexDb_error; |
| 24373 | 25616 | } |
| 24374 | - for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ | |
| 25617 | + for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){ | |
| 24375 | 25618 | rc = sscanf(zLine, "| page %d offset %d", &j, &k); |
| 24376 | 25619 | if( rc==2 ){ |
| 24377 | 25620 | iOffset = k; |
| 24378 | 25621 | continue; |
| 24379 | 25622 | } |
| @@ -24401,18 +25644,18 @@ | ||
| 24401 | 25644 | |
| 24402 | 25645 | readHexDb_error: |
| 24403 | 25646 | if( in!=p->in ){ |
| 24404 | 25647 | fclose(in); |
| 24405 | 25648 | }else{ |
| 24406 | - while( fgets(zLine, sizeof(zLine), p->in)!=0 ){ | |
| 25649 | + while( sqlite3_fgets(zLine, sizeof(zLine), p->in)!=0 ){ | |
| 24407 | 25650 | nLine++; |
| 24408 | 25651 | if(cli_strncmp(zLine, "| end ", 6)==0 ) break; |
| 24409 | 25652 | } |
| 24410 | 25653 | p->lineno = nLine; |
| 24411 | 25654 | } |
| 24412 | 25655 | sqlite3_free(a); |
| 24413 | - eputf("Error on line %d of --hexdb input\n", nLine); | |
| 25656 | + sqlite3_fprintf(stderr,"Error on line %d of --hexdb input\n", nLine); | |
| 24414 | 25657 | return 0; |
| 24415 | 25658 | } |
| 24416 | 25659 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 24417 | 25660 | |
| 24418 | 25661 | /* |
| @@ -24483,22 +25726,24 @@ | ||
| 24483 | 25726 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); |
| 24484 | 25727 | break; |
| 24485 | 25728 | } |
| 24486 | 25729 | } |
| 24487 | 25730 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 24488 | - eputf("Error: unable to open database \"%s\": %s\n", | |
| 25731 | + sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n", | |
| 24489 | 25732 | zDbFilename, sqlite3_errmsg(p->db)); |
| 24490 | 25733 | if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ |
| 24491 | 25734 | exit(1); |
| 24492 | 25735 | } |
| 24493 | 25736 | sqlite3_close(p->db); |
| 24494 | 25737 | sqlite3_open(":memory:", &p->db); |
| 24495 | 25738 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 24496 | - eputz("Also: unable to open substitute in-memory database.\n"); | |
| 25739 | + sqlite3_fputs("Also: unable to open substitute in-memory database.\n", | |
| 25740 | + stderr); | |
| 24497 | 25741 | exit(1); |
| 24498 | 25742 | }else{ |
| 24499 | - eputf("Notice: using substitute in-memory database instead of \"%s\"\n", | |
| 25743 | + sqlite3_fprintf(stderr, | |
| 25744 | + "Notice: using substitute in-memory database instead of \"%s\"\n", | |
| 24500 | 25745 | zDbFilename); |
| 24501 | 25746 | } |
| 24502 | 25747 | } |
| 24503 | 25748 | globalDb = p->db; |
| 24504 | 25749 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); |
| @@ -24511,10 +25756,11 @@ | ||
| 24511 | 25756 | } |
| 24512 | 25757 | |
| 24513 | 25758 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 24514 | 25759 | sqlite3_enable_load_extension(p->db, 1); |
| 24515 | 25760 | #endif |
| 25761 | + sqlite3_sha_init(p->db, 0, 0); | |
| 24516 | 25762 | sqlite3_shathree_init(p->db, 0, 0); |
| 24517 | 25763 | sqlite3_uint_init(p->db, 0, 0); |
| 24518 | 25764 | sqlite3_stmtrand_init(p->db, 0, 0); |
| 24519 | 25765 | sqlite3_decimal_init(p->db, 0, 0); |
| 24520 | 25766 | sqlite3_percentile_init(p->db, 0, 0); |
| @@ -24605,11 +25851,11 @@ | ||
| 24605 | 25851 | } |
| 24606 | 25852 | rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, |
| 24607 | 25853 | SQLITE_DESERIALIZE_RESIZEABLE | |
| 24608 | 25854 | SQLITE_DESERIALIZE_FREEONCLOSE); |
| 24609 | 25855 | if( rc ){ |
| 24610 | - eputf("Error: sqlite3_deserialize() returns %d\n", rc); | |
| 25856 | + sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc); | |
| 24611 | 25857 | } |
| 24612 | 25858 | if( p->szMax>0 ){ |
| 24613 | 25859 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); |
| 24614 | 25860 | } |
| 24615 | 25861 | } |
| @@ -24629,15 +25875,17 @@ | ||
| 24629 | 25875 | ** Attempt to close the database connection. Report errors. |
| 24630 | 25876 | */ |
| 24631 | 25877 | void close_db(sqlite3 *db){ |
| 24632 | 25878 | int rc = sqlite3_close(db); |
| 24633 | 25879 | if( rc ){ |
| 24634 | - eputf("Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); | |
| 25880 | + sqlite3_fprintf(stderr, | |
| 25881 | + "Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); | |
| 24635 | 25882 | } |
| 24636 | 25883 | } |
| 24637 | 25884 | |
| 24638 | -#if HAVE_READLINE || HAVE_EDITLINE | |
| 25885 | +#if (HAVE_READLINE || HAVE_EDITLINE) \ | |
| 25886 | + && !defined(SQLITE_OMIT_READLINE_COMPLETION) | |
| 24639 | 25887 | /* |
| 24640 | 25888 | ** Readline completion callbacks |
| 24641 | 25889 | */ |
| 24642 | 25890 | static char *readline_completion_generator(const char *text, int state){ |
| 24643 | 25891 | static sqlite3_stmt *pStmt = 0; |
| @@ -24671,19 +25919,26 @@ | ||
| 24671 | 25919 | #elif HAVE_LINENOISE |
| 24672 | 25920 | /* |
| 24673 | 25921 | ** Linenoise completion callback. Note that the 3rd argument is from |
| 24674 | 25922 | ** the "msteveb" version of linenoise, not the "antirez" version. |
| 24675 | 25923 | */ |
| 24676 | -static void linenoise_completion(const char *zLine, linenoiseCompletions *lc, | |
| 24677 | - void *pUserData){ | |
| 25924 | +static void linenoise_completion( | |
| 25925 | + const char *zLine, | |
| 25926 | + linenoiseCompletions *lc | |
| 25927 | +#if HAVE_LINENOISE==2 | |
| 25928 | + ,void *pUserData | |
| 25929 | +#endif | |
| 25930 | +){ | |
| 24678 | 25931 | i64 nLine = strlen(zLine); |
| 24679 | 25932 | i64 i, iStart; |
| 24680 | 25933 | sqlite3_stmt *pStmt = 0; |
| 24681 | 25934 | char *zSql; |
| 24682 | 25935 | char zBuf[1000]; |
| 24683 | 25936 | |
| 25937 | +#if HAVE_LINENOISE==2 | |
| 24684 | 25938 | UNUSED_PARAMETER(pUserData); |
| 25939 | +#endif | |
| 24685 | 25940 | if( nLine>(i64)sizeof(zBuf)-30 ) return; |
| 24686 | 25941 | if( zLine[0]=='.' || zLine[0]=='#') return; |
| 24687 | 25942 | for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} |
| 24688 | 25943 | if( i==nLine-1 ) return; |
| 24689 | 25944 | iStart = i+1; |
| @@ -24793,11 +26048,12 @@ | ||
| 24793 | 26048 | return 1; |
| 24794 | 26049 | } |
| 24795 | 26050 | if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ |
| 24796 | 26051 | return 0; |
| 24797 | 26052 | } |
| 24798 | - eputf("ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); | |
| 26053 | + sqlite3_fprintf(stderr, | |
| 26054 | + "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); | |
| 24799 | 26055 | return 0; |
| 24800 | 26056 | } |
| 24801 | 26057 | |
| 24802 | 26058 | /* |
| 24803 | 26059 | ** Set or clear a shell flag according to a boolean value. |
| @@ -24820,22 +26076,22 @@ | ||
| 24820 | 26076 | /* |
| 24821 | 26077 | ** Try to open an output file. The names "stdout" and "stderr" are |
| 24822 | 26078 | ** recognized and do the right thing. NULL is returned if the output |
| 24823 | 26079 | ** filename is "off". |
| 24824 | 26080 | */ |
| 24825 | -static FILE *output_file_open(const char *zFile, int bTextMode){ | |
| 26081 | +static FILE *output_file_open(const char *zFile){ | |
| 24826 | 26082 | FILE *f; |
| 24827 | 26083 | if( cli_strcmp(zFile,"stdout")==0 ){ |
| 24828 | 26084 | f = stdout; |
| 24829 | 26085 | }else if( cli_strcmp(zFile, "stderr")==0 ){ |
| 24830 | 26086 | f = stderr; |
| 24831 | 26087 | }else if( cli_strcmp(zFile, "off")==0 ){ |
| 24832 | 26088 | f = 0; |
| 24833 | 26089 | }else{ |
| 24834 | - f = fopen(zFile, bTextMode ? "w" : "wb"); | |
| 26090 | + f = sqlite3_fopen(zFile, "w"); | |
| 24835 | 26091 | if( f==0 ){ |
| 24836 | - eputf("Error: cannot open \"%s\"\n", zFile); | |
| 26092 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); | |
| 24837 | 26093 | } |
| 24838 | 26094 | } |
| 24839 | 26095 | return f; |
| 24840 | 26096 | } |
| 24841 | 26097 | |
| @@ -24884,16 +26140,17 @@ | ||
| 24884 | 26140 | if( nSql>1000000000 ) nSql = 1000000000; |
| 24885 | 26141 | while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; } |
| 24886 | 26142 | switch( mType ){ |
| 24887 | 26143 | case SQLITE_TRACE_ROW: |
| 24888 | 26144 | case SQLITE_TRACE_STMT: { |
| 24889 | - sputf(p->traceOut, "%.*s;\n", (int)nSql, zSql); | |
| 26145 | + sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql); | |
| 24890 | 26146 | break; |
| 24891 | 26147 | } |
| 24892 | 26148 | case SQLITE_TRACE_PROFILE: { |
| 24893 | 26149 | sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; |
| 24894 | - sputf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); | |
| 26150 | + sqlite3_fprintf(p->traceOut, | |
| 26151 | + "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); | |
| 24895 | 26152 | break; |
| 24896 | 26153 | } |
| 24897 | 26154 | } |
| 24898 | 26155 | return 0; |
| 24899 | 26156 | } |
| @@ -24996,14 +26253,15 @@ | ||
| 24996 | 26253 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 24997 | 26254 | p->cTerm = c; |
| 24998 | 26255 | break; |
| 24999 | 26256 | } |
| 25000 | 26257 | if( pc==cQuote && c!='\r' ){ |
| 25001 | - eputf("%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote); | |
| 26258 | + sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n", | |
| 26259 | + p->zFile, p->nLine, cQuote); | |
| 25002 | 26260 | } |
| 25003 | 26261 | if( c==EOF ){ |
| 25004 | - eputf("%s:%d: unterminated %c-quoted field\n", | |
| 26262 | + sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n", | |
| 25005 | 26263 | p->zFile, startLine, cQuote); |
| 25006 | 26264 | p->cTerm = c; |
| 25007 | 26265 | break; |
| 25008 | 26266 | } |
| 25009 | 26267 | import_append_char(p, c); |
| @@ -25098,11 +26356,11 @@ | ||
| 25098 | 26356 | |
| 25099 | 26357 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); |
| 25100 | 26358 | shell_check_oom(zQuery); |
| 25101 | 26359 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25102 | 26360 | if( rc ){ |
| 25103 | - eputf("Error %d: %s on [%s]\n", | |
| 26361 | + sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", | |
| 25104 | 26362 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 25105 | 26363 | goto end_data_xfer; |
| 25106 | 26364 | } |
| 25107 | 26365 | n = sqlite3_column_count(pQuery); |
| 25108 | 26366 | zInsert = sqlite3_malloc64(200 + nTable + n*3); |
| @@ -25115,11 +26373,11 @@ | ||
| 25115 | 26373 | i += 2; |
| 25116 | 26374 | } |
| 25117 | 26375 | memcpy(zInsert+i, ");", 3); |
| 25118 | 26376 | rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); |
| 25119 | 26377 | if( rc ){ |
| 25120 | - eputf("Error %d: %s on [%s]\n", | |
| 26378 | + sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", | |
| 25121 | 26379 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); |
| 25122 | 26380 | goto end_data_xfer; |
| 25123 | 26381 | } |
| 25124 | 26382 | for(k=0; k<2; k++){ |
| 25125 | 26383 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| @@ -25151,11 +26409,11 @@ | ||
| 25151 | 26409 | } |
| 25152 | 26410 | } |
| 25153 | 26411 | } /* End for */ |
| 25154 | 26412 | rc = sqlite3_step(pInsert); |
| 25155 | 26413 | if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 25156 | - eputf("Error %d: %s\n", | |
| 26414 | + sqlite3_fprintf(stderr,"Error %d: %s\n", | |
| 25157 | 26415 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); |
| 25158 | 26416 | } |
| 25159 | 26417 | sqlite3_reset(pInsert); |
| 25160 | 26418 | cnt++; |
| 25161 | 26419 | if( (cnt%spinRate)==0 ){ |
| @@ -25169,11 +26427,11 @@ | ||
| 25169 | 26427 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", |
| 25170 | 26428 | zTable); |
| 25171 | 26429 | shell_check_oom(zQuery); |
| 25172 | 26430 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25173 | 26431 | if( rc ){ |
| 25174 | - eputf("Warning: cannot step \"%s\" backwards", zTable); | |
| 26432 | + sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable); | |
| 25175 | 26433 | break; |
| 25176 | 26434 | } |
| 25177 | 26435 | } /* End for(k=0...) */ |
| 25178 | 26436 | |
| 25179 | 26437 | end_data_xfer: |
| @@ -25206,23 +26464,24 @@ | ||
| 25206 | 26464 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 25207 | 26465 | " WHERE %s ORDER BY rowid ASC", zWhere); |
| 25208 | 26466 | shell_check_oom(zQuery); |
| 25209 | 26467 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25210 | 26468 | if( rc ){ |
| 25211 | - eputf("Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), | |
| 26469 | + sqlite3_fprintf(stderr, | |
| 26470 | + "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), | |
| 25212 | 26471 | sqlite3_errmsg(p->db), zQuery); |
| 25213 | 26472 | goto end_schema_xfer; |
| 25214 | 26473 | } |
| 25215 | 26474 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 25216 | 26475 | zName = sqlite3_column_text(pQuery, 0); |
| 25217 | 26476 | zSql = sqlite3_column_text(pQuery, 1); |
| 25218 | 26477 | if( zName==0 || zSql==0 ) continue; |
| 25219 | 26478 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ |
| 25220 | - sputf(stdout, "%s... ", zName); fflush(stdout); | |
| 26479 | + sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); | |
| 25221 | 26480 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 25222 | 26481 | if( zErrMsg ){ |
| 25223 | - eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); | |
| 26482 | + sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); | |
| 25224 | 26483 | sqlite3_free(zErrMsg); |
| 25225 | 26484 | zErrMsg = 0; |
| 25226 | 26485 | } |
| 25227 | 26486 | } |
| 25228 | 26487 | if( xForEach ){ |
| @@ -25236,23 +26495,23 @@ | ||
| 25236 | 26495 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 25237 | 26496 | " WHERE %s ORDER BY rowid DESC", zWhere); |
| 25238 | 26497 | shell_check_oom(zQuery); |
| 25239 | 26498 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25240 | 26499 | if( rc ){ |
| 25241 | - eputf("Error: (%d) %s on [%s]\n", | |
| 26500 | + sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n", | |
| 25242 | 26501 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 25243 | 26502 | goto end_schema_xfer; |
| 25244 | 26503 | } |
| 25245 | 26504 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 25246 | 26505 | zName = sqlite3_column_text(pQuery, 0); |
| 25247 | 26506 | zSql = sqlite3_column_text(pQuery, 1); |
| 25248 | 26507 | if( zName==0 || zSql==0 ) continue; |
| 25249 | 26508 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; |
| 25250 | - sputf(stdout, "%s... ", zName); fflush(stdout); | |
| 26509 | + sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); | |
| 25251 | 26510 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 25252 | 26511 | if( zErrMsg ){ |
| 25253 | - eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); | |
| 26512 | + sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); | |
| 25254 | 26513 | sqlite3_free(zErrMsg); |
| 25255 | 26514 | zErrMsg = 0; |
| 25256 | 26515 | } |
| 25257 | 26516 | if( xForEach ){ |
| 25258 | 26517 | xForEach(p, newDb, (const char*)zName); |
| @@ -25272,16 +26531,17 @@ | ||
| 25272 | 26531 | */ |
| 25273 | 26532 | static void tryToClone(ShellState *p, const char *zNewDb){ |
| 25274 | 26533 | int rc; |
| 25275 | 26534 | sqlite3 *newDb = 0; |
| 25276 | 26535 | if( access(zNewDb,0)==0 ){ |
| 25277 | - eputf("File \"%s\" already exists.\n", zNewDb); | |
| 26536 | + sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb); | |
| 25278 | 26537 | return; |
| 25279 | 26538 | } |
| 25280 | 26539 | rc = sqlite3_open(zNewDb, &newDb); |
| 25281 | 26540 | if( rc ){ |
| 25282 | - eputf("Cannot create output database: %s\n", sqlite3_errmsg(newDb)); | |
| 26541 | + sqlite3_fprintf(stderr, | |
| 26542 | + "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); | |
| 25283 | 26543 | }else{ |
| 25284 | 26544 | sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); |
| 25285 | 26545 | sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); |
| 25286 | 26546 | tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); |
| 25287 | 26547 | tryToCloneSchema(p, newDb, "type!='table'", 0); |
| @@ -25294,14 +26554,22 @@ | ||
| 25294 | 26554 | #ifndef SQLITE_SHELL_FIDDLE |
| 25295 | 26555 | /* |
| 25296 | 26556 | ** Change the output stream (file or pipe or console) to something else. |
| 25297 | 26557 | */ |
| 25298 | 26558 | static void output_redir(ShellState *p, FILE *pfNew){ |
| 25299 | - if( p->out != stdout ) eputz("Output already redirected.\n"); | |
| 25300 | - else{ | |
| 26559 | + if( p->out != stdout ){ | |
| 26560 | + sqlite3_fputs("Output already redirected.\n", stderr); | |
| 26561 | + }else{ | |
| 25301 | 26562 | p->out = pfNew; |
| 25302 | - setOutputStream(pfNew); | |
| 26563 | + setCrlfMode(p); | |
| 26564 | + if( p->mode==MODE_Www ){ | |
| 26565 | + sqlite3_fputs( | |
| 26566 | + "<!DOCTYPE html>\n" | |
| 26567 | + "<HTML><BODY><PRE>\n", | |
| 26568 | + p->out | |
| 26569 | + ); | |
| 26570 | + } | |
| 25303 | 26571 | } |
| 25304 | 26572 | } |
| 25305 | 26573 | |
| 25306 | 26574 | /* |
| 25307 | 26575 | ** Change the output file back to stdout. |
| @@ -25314,10 +26582,13 @@ | ||
| 25314 | 26582 | if( p->outfile[0]=='|' ){ |
| 25315 | 26583 | #ifndef SQLITE_OMIT_POPEN |
| 25316 | 26584 | pclose(p->out); |
| 25317 | 26585 | #endif |
| 25318 | 26586 | }else{ |
| 26587 | + if( p->mode==MODE_Www ){ | |
| 26588 | + sqlite3_fputs("</PRE></BODY></HTML>\n", p->out); | |
| 26589 | + } | |
| 25319 | 26590 | output_file_close(p->out); |
| 25320 | 26591 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 25321 | 26592 | if( p->doXdgOpen ){ |
| 25322 | 26593 | const char *zXdgOpenCmd = |
| 25323 | 26594 | #if defined(_WIN32) |
| @@ -25328,11 +26599,11 @@ | ||
| 25328 | 26599 | "xdg-open"; |
| 25329 | 26600 | #endif |
| 25330 | 26601 | char *zCmd; |
| 25331 | 26602 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 25332 | 26603 | if( system(zCmd) ){ |
| 25333 | - eputf("Failed: [%s]\n", zCmd); | |
| 26604 | + sqlite3_fprintf(stderr,"Failed: [%s]\n", zCmd); | |
| 25334 | 26605 | }else{ |
| 25335 | 26606 | /* Give the start/open/xdg-open command some time to get |
| 25336 | 26607 | ** going before we continue, and potential delete the |
| 25337 | 26608 | ** p->zTempFile data file out from under it */ |
| 25338 | 26609 | sqlite3_sleep(2000); |
| @@ -25343,28 +26614,34 @@ | ||
| 25343 | 26614 | } |
| 25344 | 26615 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 25345 | 26616 | } |
| 25346 | 26617 | p->outfile[0] = 0; |
| 25347 | 26618 | p->out = stdout; |
| 25348 | - setOutputStream(stdout); | |
| 26619 | + setCrlfMode(p); | |
| 25349 | 26620 | } |
| 25350 | 26621 | #else |
| 25351 | 26622 | # define output_redir(SS,pfO) |
| 25352 | 26623 | # define output_reset(SS) |
| 25353 | 26624 | #endif |
| 25354 | 26625 | |
| 25355 | 26626 | /* |
| 25356 | 26627 | ** Run an SQL command and return the single integer result. |
| 25357 | 26628 | */ |
| 25358 | -static int db_int(sqlite3 *db, const char *zSql){ | |
| 26629 | +static int db_int(sqlite3 *db, const char *zSql, ...){ | |
| 25359 | 26630 | sqlite3_stmt *pStmt; |
| 25360 | 26631 | int res = 0; |
| 25361 | - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); | |
| 26632 | + char *z; | |
| 26633 | + va_list ap; | |
| 26634 | + va_start(ap, zSql); | |
| 26635 | + z = sqlite3_vmprintf(zSql, ap); | |
| 26636 | + va_end(ap); | |
| 26637 | + sqlite3_prepare_v2(db, z, -1, &pStmt, 0); | |
| 25362 | 26638 | if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 25363 | 26639 | res = sqlite3_column_int(pStmt,0); |
| 25364 | 26640 | } |
| 25365 | 26641 | sqlite3_finalize(pStmt); |
| 26642 | + sqlite3_free(z); | |
| 25366 | 26643 | return res; |
| 25367 | 26644 | } |
| 25368 | 26645 | |
| 25369 | 26646 | #if SQLITE_SHELL_HAVE_RECOVER |
| 25370 | 26647 | /* |
| @@ -25419,11 +26696,11 @@ | ||
| 25419 | 26696 | if( p->db==0 ) return 1; |
| 25420 | 26697 | rc = sqlite3_prepare_v2(p->db, |
| 25421 | 26698 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 25422 | 26699 | -1, &pStmt, 0); |
| 25423 | 26700 | if( rc ){ |
| 25424 | - eputf("error: %s\n", sqlite3_errmsg(p->db)); | |
| 26701 | + sqlite3_fprintf(stderr,"error: %s\n", sqlite3_errmsg(p->db)); | |
| 25425 | 26702 | sqlite3_finalize(pStmt); |
| 25426 | 26703 | return 1; |
| 25427 | 26704 | } |
| 25428 | 26705 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 25429 | 26706 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| @@ -25432,58 +26709,149 @@ | ||
| 25432 | 26709 | const u8 *pb = sqlite3_column_blob(pStmt,0); |
| 25433 | 26710 | shell_check_oom(pb); |
| 25434 | 26711 | memcpy(aHdr, pb, 100); |
| 25435 | 26712 | sqlite3_finalize(pStmt); |
| 25436 | 26713 | }else{ |
| 25437 | - eputz("unable to read database header\n"); | |
| 26714 | + sqlite3_fputs("unable to read database header\n", stderr); | |
| 25438 | 26715 | sqlite3_finalize(pStmt); |
| 25439 | 26716 | return 1; |
| 25440 | 26717 | } |
| 25441 | 26718 | i = get2byteInt(aHdr+16); |
| 25442 | 26719 | if( i==1 ) i = 65536; |
| 25443 | - oputf("%-20s %d\n", "database page size:", i); | |
| 25444 | - oputf("%-20s %d\n", "write format:", aHdr[18]); | |
| 25445 | - oputf("%-20s %d\n", "read format:", aHdr[19]); | |
| 25446 | - oputf("%-20s %d\n", "reserved bytes:", aHdr[20]); | |
| 26720 | + sqlite3_fprintf(p->out, "%-20s %d\n", "database page size:", i); | |
| 26721 | + sqlite3_fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]); | |
| 26722 | + sqlite3_fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]); | |
| 26723 | + sqlite3_fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); | |
| 25447 | 26724 | for(i=0; i<ArraySize(aField); i++){ |
| 25448 | 26725 | int ofst = aField[i].ofst; |
| 25449 | 26726 | unsigned int val = get4byteInt(aHdr + ofst); |
| 25450 | - oputf("%-20s %u", aField[i].zName, val); | |
| 26727 | + sqlite3_fprintf(p->out, "%-20s %u", aField[i].zName, val); | |
| 25451 | 26728 | switch( ofst ){ |
| 25452 | 26729 | case 56: { |
| 25453 | - if( val==1 ) oputz(" (utf8)"); | |
| 25454 | - if( val==2 ) oputz(" (utf16le)"); | |
| 25455 | - if( val==3 ) oputz(" (utf16be)"); | |
| 26730 | + if( val==1 ) sqlite3_fputs(" (utf8)", p->out); | |
| 26731 | + if( val==2 ) sqlite3_fputs(" (utf16le)", p->out); | |
| 26732 | + if( val==3 ) sqlite3_fputs(" (utf16be)", p->out); | |
| 25456 | 26733 | } |
| 25457 | 26734 | } |
| 25458 | - oputz("\n"); | |
| 26735 | + sqlite3_fputs("\n", p->out); | |
| 25459 | 26736 | } |
| 25460 | 26737 | if( zDb==0 ){ |
| 25461 | 26738 | zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); |
| 25462 | 26739 | }else if( cli_strcmp(zDb,"temp")==0 ){ |
| 25463 | 26740 | zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema"); |
| 25464 | 26741 | }else{ |
| 25465 | 26742 | zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); |
| 25466 | 26743 | } |
| 25467 | 26744 | for(i=0; i<ArraySize(aQuery); i++){ |
| 25468 | - char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); | |
| 25469 | - int val = db_int(p->db, zSql); | |
| 25470 | - sqlite3_free(zSql); | |
| 25471 | - oputf("%-20s %d\n", aQuery[i].zName, val); | |
| 26745 | + int val = db_int(p->db, aQuery[i].zSql, zSchemaTab); | |
| 26746 | + sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val); | |
| 25472 | 26747 | } |
| 25473 | 26748 | sqlite3_free(zSchemaTab); |
| 25474 | 26749 | sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); |
| 25475 | - oputf("%-20s %u\n", "data version", iDataVersion); | |
| 26750 | + sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion); | |
| 25476 | 26751 | return 0; |
| 25477 | 26752 | } |
| 25478 | 26753 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 26754 | + | |
| 26755 | +/* | |
| 26756 | +** Implementation of the ".dbtotxt" command. | |
| 26757 | +** | |
| 26758 | +** Return 1 on error, 2 to exit, and 0 otherwise. | |
| 26759 | +*/ | |
| 26760 | +static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){ | |
| 26761 | + sqlite3_stmt *pStmt = 0; | |
| 26762 | + sqlite3_int64 nPage = 0; | |
| 26763 | + int pgSz = 0; | |
| 26764 | + const char *zTail; | |
| 26765 | + char *zName = 0; | |
| 26766 | + int rc, i, j; | |
| 26767 | + unsigned char bShow[256]; /* Characters ok to display */ | |
| 26768 | + | |
| 26769 | + UNUSED_PARAMETER(nArg); | |
| 26770 | + UNUSED_PARAMETER(azArg); | |
| 26771 | + memset(bShow, '.', sizeof(bShow)); | |
| 26772 | + for(i=' '; i<='~'; i++){ | |
| 26773 | + if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i; | |
| 26774 | + } | |
| 26775 | + rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0); | |
| 26776 | + if( rc ) goto dbtotxt_error; | |
| 26777 | + rc = 0; | |
| 26778 | + if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; | |
| 26779 | + pgSz = sqlite3_column_int(pStmt, 0); | |
| 26780 | + sqlite3_finalize(pStmt); | |
| 26781 | + pStmt = 0; | |
| 26782 | + if( pgSz<512 || pgSz>65536 || (pgSz&(pgSz-1))!=0 ) goto dbtotxt_error; | |
| 26783 | + rc = sqlite3_prepare_v2(p->db, "PRAGMA page_count", -1, &pStmt, 0); | |
| 26784 | + if( rc ) goto dbtotxt_error; | |
| 26785 | + rc = 0; | |
| 26786 | + if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; | |
| 26787 | + nPage = sqlite3_column_int64(pStmt, 0); | |
| 26788 | + sqlite3_finalize(pStmt); | |
| 26789 | + pStmt = 0; | |
| 26790 | + if( nPage<1 ) goto dbtotxt_error; | |
| 26791 | + rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0); | |
| 26792 | + if( rc ) goto dbtotxt_error; | |
| 26793 | + if( sqlite3_step(pStmt)!=SQLITE_ROW ){ | |
| 26794 | + zTail = "unk.db"; | |
| 26795 | + }else{ | |
| 26796 | + const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2); | |
| 26797 | + if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db"; | |
| 26798 | + zTail = strrchr(zFilename, '/'); | |
| 26799 | +#if defined(_WIN32) | |
| 26800 | + if( zTail==0 ) zTail = strrchr(zFilename, '\\'); | |
| 26801 | +#endif | |
| 26802 | + } | |
| 26803 | + zName = strdup(zTail); | |
| 26804 | + shell_check_oom(zName); | |
| 26805 | + sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n", | |
| 26806 | + nPage*pgSz, pgSz, zName); | |
| 26807 | + sqlite3_finalize(pStmt); | |
| 26808 | + pStmt = 0; | |
| 26809 | + rc = sqlite3_prepare_v2(p->db, | |
| 26810 | + "SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0); | |
| 26811 | + if( rc ) goto dbtotxt_error; | |
| 26812 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){ | |
| 26813 | + sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0); | |
| 26814 | + const u8 *aData = sqlite3_column_blob(pStmt, 1); | |
| 26815 | + int seenPageLabel = 0; | |
| 26816 | + for(i=0; i<pgSz; i+=16){ | |
| 26817 | + const u8 *aLine = aData+i; | |
| 26818 | + for(j=0; j<16 && aLine[j]==0; j++){} | |
| 26819 | + if( j==16 ) continue; | |
| 26820 | + if( !seenPageLabel ){ | |
| 26821 | + sqlite3_fprintf(p->out, "| page %lld offset %lld\n", pgno, pgno*pgSz); | |
| 26822 | + seenPageLabel = 1; | |
| 26823 | + } | |
| 26824 | + sqlite3_fprintf(p->out, "| %5d:", i); | |
| 26825 | + for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]); | |
| 26826 | + sqlite3_fprintf(p->out, " "); | |
| 26827 | + for(j=0; j<16; j++){ | |
| 26828 | + unsigned char c = (unsigned char)aLine[j]; | |
| 26829 | + sqlite3_fprintf(p->out, "%c", bShow[c]); | |
| 26830 | + } | |
| 26831 | + sqlite3_fprintf(p->out, "\n"); | |
| 26832 | + } | |
| 26833 | + } | |
| 26834 | + sqlite3_finalize(pStmt); | |
| 26835 | + sqlite3_fprintf(p->out, "| end %s\n", zName); | |
| 26836 | + free(zName); | |
| 26837 | + return 0; | |
| 26838 | + | |
| 26839 | +dbtotxt_error: | |
| 26840 | + if( rc ){ | |
| 26841 | + sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db)); | |
| 26842 | + } | |
| 26843 | + sqlite3_finalize(pStmt); | |
| 26844 | + free(zName); | |
| 26845 | + return 1; | |
| 26846 | +} | |
| 25479 | 26847 | |
| 25480 | 26848 | /* |
| 25481 | 26849 | ** Print the given string as an error message. |
| 25482 | 26850 | */ |
| 25483 | 26851 | static void shellEmitError(const char *zErr){ |
| 25484 | - eputf("Error: %s\n", zErr); | |
| 26852 | + sqlite3_fprintf(stderr,"Error: %s\n", zErr); | |
| 25485 | 26853 | } |
| 25486 | 26854 | /* |
| 25487 | 26855 | ** Print the current sqlite3_errmsg() value to stderr and return 1. |
| 25488 | 26856 | */ |
| 25489 | 26857 | static int shellDatabaseError(sqlite3 *db){ |
| @@ -25726,10 +27094,11 @@ | ||
| 25726 | 27094 | int bGroupByParent = 0; /* If -groupbyparent is present */ |
| 25727 | 27095 | int i; /* To iterate through azArg[] */ |
| 25728 | 27096 | const char *zIndent = ""; /* How much to indent CREATE INDEX by */ |
| 25729 | 27097 | int rc; /* Return code */ |
| 25730 | 27098 | sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ |
| 27099 | + FILE *out = pState->out; /* Send output here */ | |
| 25731 | 27100 | |
| 25732 | 27101 | /* |
| 25733 | 27102 | ** This SELECT statement returns one row for each foreign key constraint |
| 25734 | 27103 | ** in the schema of the main database. The column values are: |
| 25735 | 27104 | ** |
| @@ -25801,11 +27170,12 @@ | ||
| 25801 | 27170 | else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ |
| 25802 | 27171 | bGroupByParent = 1; |
| 25803 | 27172 | zIndent = " "; |
| 25804 | 27173 | } |
| 25805 | 27174 | else{ |
| 25806 | - eputf("Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); | |
| 27175 | + sqlite3_fprintf(stderr, | |
| 27176 | + "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); | |
| 25807 | 27177 | return SQLITE_ERROR; |
| 25808 | 27178 | } |
| 25809 | 27179 | } |
| 25810 | 27180 | |
| 25811 | 27181 | /* Register the fkey_collate_clause() SQL function */ |
| @@ -25845,44 +27215,45 @@ | ||
| 25845 | 27215 | } |
| 25846 | 27216 | rc = sqlite3_finalize(pExplain); |
| 25847 | 27217 | if( rc!=SQLITE_OK ) break; |
| 25848 | 27218 | |
| 25849 | 27219 | if( res<0 ){ |
| 25850 | - eputz("Error: internal error"); | |
| 27220 | + sqlite3_fputs("Error: internal error", stderr); | |
| 25851 | 27221 | break; |
| 25852 | 27222 | }else{ |
| 25853 | 27223 | if( bGroupByParent |
| 25854 | 27224 | && (bVerbose || res==0) |
| 25855 | 27225 | && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) |
| 25856 | 27226 | ){ |
| 25857 | - oputf("-- Parent table %s\n", zParent); | |
| 27227 | + sqlite3_fprintf(out, "-- Parent table %s\n", zParent); | |
| 25858 | 27228 | sqlite3_free(zPrev); |
| 25859 | 27229 | zPrev = sqlite3_mprintf("%s", zParent); |
| 25860 | 27230 | } |
| 25861 | 27231 | |
| 25862 | 27232 | if( res==0 ){ |
| 25863 | - oputf("%s%s --> %s\n", zIndent, zCI, zTarget); | |
| 27233 | + sqlite3_fprintf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); | |
| 25864 | 27234 | }else if( bVerbose ){ |
| 25865 | - oputf("%s/* no extra indexes required for %s -> %s */\n", | |
| 27235 | + sqlite3_fprintf(out, | |
| 27236 | + "%s/* no extra indexes required for %s -> %s */\n", | |
| 25866 | 27237 | zIndent, zFrom, zTarget |
| 25867 | 27238 | ); |
| 25868 | 27239 | } |
| 25869 | 27240 | } |
| 25870 | 27241 | } |
| 25871 | 27242 | sqlite3_free(zPrev); |
| 25872 | 27243 | |
| 25873 | 27244 | if( rc!=SQLITE_OK ){ |
| 25874 | - eputf("%s\n", sqlite3_errmsg(db)); | |
| 27245 | + sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); | |
| 25875 | 27246 | } |
| 25876 | 27247 | |
| 25877 | 27248 | rc2 = sqlite3_finalize(pSql); |
| 25878 | 27249 | if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ |
| 25879 | 27250 | rc = rc2; |
| 25880 | - eputf("%s\n", sqlite3_errmsg(db)); | |
| 27251 | + sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); | |
| 25881 | 27252 | } |
| 25882 | 27253 | }else{ |
| 25883 | - eputf("%s\n", sqlite3_errmsg(db)); | |
| 27254 | + sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); | |
| 25884 | 27255 | } |
| 25885 | 27256 | |
| 25886 | 27257 | return rc; |
| 25887 | 27258 | } |
| 25888 | 27259 | |
| @@ -25898,13 +27269,13 @@ | ||
| 25898 | 27269 | n = (nArg>=2 ? strlen30(azArg[1]) : 0); |
| 25899 | 27270 | if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; |
| 25900 | 27271 | return lintFkeyIndexes(pState, azArg, nArg); |
| 25901 | 27272 | |
| 25902 | 27273 | usage: |
| 25903 | - eputf("Usage %s sub-command ?switches...?\n", azArg[0]); | |
| 25904 | - eputz("Where sub-commands are:\n"); | |
| 25905 | - eputz(" fkey-indexes\n"); | |
| 27274 | + sqlite3_fprintf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]); | |
| 27275 | + sqlite3_fprintf(stderr, "Where sub-commands are:\n"); | |
| 27276 | + sqlite3_fprintf(stderr, " fkey-indexes\n"); | |
| 25906 | 27277 | return SQLITE_ERROR; |
| 25907 | 27278 | } |
| 25908 | 27279 | |
| 25909 | 27280 | static void shellPrepare( |
| 25910 | 27281 | sqlite3 *db, |
| @@ -25914,11 +27285,12 @@ | ||
| 25914 | 27285 | ){ |
| 25915 | 27286 | *ppStmt = 0; |
| 25916 | 27287 | if( *pRc==SQLITE_OK ){ |
| 25917 | 27288 | int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); |
| 25918 | 27289 | if( rc!=SQLITE_OK ){ |
| 25919 | - eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); | |
| 27290 | + sqlite3_fprintf(stderr, | |
| 27291 | + "sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); | |
| 25920 | 27292 | *pRc = rc; |
| 25921 | 27293 | } |
| 25922 | 27294 | } |
| 25923 | 27295 | } |
| 25924 | 27296 | |
| @@ -25958,11 +27330,11 @@ | ||
| 25958 | 27330 | if( pStmt ){ |
| 25959 | 27331 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 25960 | 27332 | int rc = sqlite3_finalize(pStmt); |
| 25961 | 27333 | if( *pRc==SQLITE_OK ){ |
| 25962 | 27334 | if( rc!=SQLITE_OK ){ |
| 25963 | - eputf("SQL error: %s\n", sqlite3_errmsg(db)); | |
| 27335 | + sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); | |
| 25964 | 27336 | } |
| 25965 | 27337 | *pRc = rc; |
| 25966 | 27338 | } |
| 25967 | 27339 | } |
| 25968 | 27340 | } |
| @@ -25980,11 +27352,11 @@ | ||
| 25980 | 27352 | ){ |
| 25981 | 27353 | int rc = sqlite3_reset(pStmt); |
| 25982 | 27354 | if( *pRc==SQLITE_OK ){ |
| 25983 | 27355 | if( rc!=SQLITE_OK ){ |
| 25984 | 27356 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 25985 | - eputf("SQL error: %s\n", sqlite3_errmsg(db)); | |
| 27357 | + sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); | |
| 25986 | 27358 | } |
| 25987 | 27359 | *pRc = rc; |
| 25988 | 27360 | } |
| 25989 | 27361 | } |
| 25990 | 27362 | #endif /* !defined SQLITE_OMIT_VIRTUALTABLE */ |
| @@ -26009,10 +27381,11 @@ | ||
| 26009 | 27381 | char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ |
| 26010 | 27382 | const char *zFile; /* --file argument, or NULL */ |
| 26011 | 27383 | const char *zDir; /* --directory argument, or NULL */ |
| 26012 | 27384 | char **azArg; /* Array of command arguments */ |
| 26013 | 27385 | ShellState *p; /* Shell state */ |
| 27386 | + FILE *out; /* Output to this stream */ | |
| 26014 | 27387 | sqlite3 *db; /* Database containing the archive */ |
| 26015 | 27388 | }; |
| 26016 | 27389 | |
| 26017 | 27390 | /* |
| 26018 | 27391 | ** Print a usage message for the .ar command to stderr and return SQLITE_ERROR. |
| @@ -26032,13 +27405,13 @@ | ||
| 26032 | 27405 | va_start(ap, zFmt); |
| 26033 | 27406 | z = sqlite3_vmprintf(zFmt, ap); |
| 26034 | 27407 | va_end(ap); |
| 26035 | 27408 | shellEmitError(z); |
| 26036 | 27409 | if( pAr->fromCmdLine ){ |
| 26037 | - eputz("Use \"-A\" for more help\n"); | |
| 27410 | + sqlite3_fputs("Use \"-A\" for more help\n", stderr); | |
| 26038 | 27411 | }else{ |
| 26039 | - eputz("Use \".archive --help\" for more help\n"); | |
| 27412 | + sqlite3_fputs("Use \".archive --help\" for more help\n", stderr); | |
| 26040 | 27413 | } |
| 26041 | 27414 | sqlite3_free(z); |
| 26042 | 27415 | return SQLITE_ERROR; |
| 26043 | 27416 | } |
| 26044 | 27417 | |
| @@ -26134,11 +27507,11 @@ | ||
| 26134 | 27507 | }; |
| 26135 | 27508 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); |
| 26136 | 27509 | struct ArSwitch *pEnd = &aSwitch[nSwitch]; |
| 26137 | 27510 | |
| 26138 | 27511 | if( nArg<=1 ){ |
| 26139 | - eputz("Wrong number of arguments. Usage:\n"); | |
| 27512 | + sqlite3_fprintf(stderr, "Wrong number of arguments. Usage:\n"); | |
| 26140 | 27513 | return arUsage(stderr); |
| 26141 | 27514 | }else{ |
| 26142 | 27515 | char *z = azArg[1]; |
| 26143 | 27516 | if( z[0]!='-' ){ |
| 26144 | 27517 | /* Traditional style [tar] invocation */ |
| @@ -26240,11 +27613,11 @@ | ||
| 26240 | 27613 | } |
| 26241 | 27614 | } |
| 26242 | 27615 | } |
| 26243 | 27616 | } |
| 26244 | 27617 | if( pAr->eCmd==0 ){ |
| 26245 | - eputz("Required argument missing. Usage:\n"); | |
| 27618 | + sqlite3_fprintf(stderr, "Required argument missing. Usage:\n"); | |
| 26246 | 27619 | return arUsage(stderr); |
| 26247 | 27620 | } |
| 26248 | 27621 | return SQLITE_OK; |
| 26249 | 27622 | } |
| 26250 | 27623 | |
| @@ -26283,11 +27656,11 @@ | ||
| 26283 | 27656 | if( SQLITE_ROW==sqlite3_step(pTest) ){ |
| 26284 | 27657 | bOk = 1; |
| 26285 | 27658 | } |
| 26286 | 27659 | shellReset(&rc, pTest); |
| 26287 | 27660 | if( rc==SQLITE_OK && bOk==0 ){ |
| 26288 | - eputf("not found in archive: %s\n", z); | |
| 27661 | + sqlite3_fprintf(stderr,"not found in archive: %s\n", z); | |
| 26289 | 27662 | rc = SQLITE_ERROR; |
| 26290 | 27663 | } |
| 26291 | 27664 | } |
| 26292 | 27665 | shellFinalize(&rc, pTest); |
| 26293 | 27666 | } |
| @@ -26350,19 +27723,19 @@ | ||
| 26350 | 27723 | arWhereClause(&rc, pAr, &zWhere); |
| 26351 | 27724 | |
| 26352 | 27725 | shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], |
| 26353 | 27726 | pAr->zSrcTable, zWhere); |
| 26354 | 27727 | if( pAr->bDryRun ){ |
| 26355 | - oputf("%s\n", sqlite3_sql(pSql)); | |
| 27728 | + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); | |
| 26356 | 27729 | }else{ |
| 26357 | 27730 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 26358 | 27731 | if( pAr->bVerbose ){ |
| 26359 | - oputf("%s % 10d %s %s\n", | |
| 27732 | + sqlite3_fprintf(pAr->out, "%s % 10d %s %s\n", | |
| 26360 | 27733 | sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), |
| 26361 | 27734 | sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); |
| 26362 | 27735 | }else{ |
| 26363 | - oputf("%s\n", sqlite3_column_text(pSql, 0)); | |
| 27736 | + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); | |
| 26364 | 27737 | } |
| 26365 | 27738 | } |
| 26366 | 27739 | } |
| 26367 | 27740 | shellFinalize(&rc, pSql); |
| 26368 | 27741 | sqlite3_free(zWhere); |
| @@ -26385,11 +27758,11 @@ | ||
| 26385 | 27758 | } |
| 26386 | 27759 | if( rc==SQLITE_OK ){ |
| 26387 | 27760 | zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", |
| 26388 | 27761 | pAr->zSrcTable, zWhere); |
| 26389 | 27762 | if( pAr->bDryRun ){ |
| 26390 | - oputf("%s\n", zSql); | |
| 27763 | + sqlite3_fprintf(pAr->out, "%s\n", zSql); | |
| 26391 | 27764 | }else{ |
| 26392 | 27765 | char *zErr = 0; |
| 26393 | 27766 | rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); |
| 26394 | 27767 | if( rc==SQLITE_OK ){ |
| 26395 | 27768 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| @@ -26398,11 +27771,11 @@ | ||
| 26398 | 27771 | }else{ |
| 26399 | 27772 | rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0); |
| 26400 | 27773 | } |
| 26401 | 27774 | } |
| 26402 | 27775 | if( zErr ){ |
| 26403 | - sputf(stdout, "ERROR: %s\n", zErr); /* stdout? */ | |
| 27776 | + sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); /* stdout? */ | |
| 26404 | 27777 | sqlite3_free(zErr); |
| 26405 | 27778 | } |
| 26406 | 27779 | } |
| 26407 | 27780 | } |
| 26408 | 27781 | sqlite3_free(zWhere); |
| @@ -26462,15 +27835,15 @@ | ||
| 26462 | 27835 | ** populating them changes the timestamp). */ |
| 26463 | 27836 | for(i=0; i<2; i++){ |
| 26464 | 27837 | j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); |
| 26465 | 27838 | sqlite3_bind_int(pSql, j, i); |
| 26466 | 27839 | if( pAr->bDryRun ){ |
| 26467 | - oputf("%s\n", sqlite3_sql(pSql)); | |
| 27840 | + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); | |
| 26468 | 27841 | }else{ |
| 26469 | 27842 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 26470 | 27843 | if( i==0 && pAr->bVerbose ){ |
| 26471 | - oputf("%s\n", sqlite3_column_text(pSql, 0)); | |
| 27844 | + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); | |
| 26472 | 27845 | } |
| 26473 | 27846 | } |
| 26474 | 27847 | } |
| 26475 | 27848 | shellReset(&rc, pSql); |
| 26476 | 27849 | } |
| @@ -26486,17 +27859,17 @@ | ||
| 26486 | 27859 | ** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out. |
| 26487 | 27860 | */ |
| 26488 | 27861 | static int arExecSql(ArCommand *pAr, const char *zSql){ |
| 26489 | 27862 | int rc; |
| 26490 | 27863 | if( pAr->bDryRun ){ |
| 26491 | - oputf("%s\n", zSql); | |
| 27864 | + sqlite3_fprintf(pAr->out, "%s\n", zSql); | |
| 26492 | 27865 | rc = SQLITE_OK; |
| 26493 | 27866 | }else{ |
| 26494 | 27867 | char *zErr = 0; |
| 26495 | 27868 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| 26496 | 27869 | if( zErr ){ |
| 26497 | - sputf(stdout, "ERROR: %s\n", zErr); | |
| 27870 | + sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); | |
| 26498 | 27871 | sqlite3_free(zErr); |
| 26499 | 27872 | } |
| 26500 | 27873 | } |
| 26501 | 27874 | return rc; |
| 26502 | 27875 | } |
| @@ -26641,10 +28014,11 @@ | ||
| 26641 | 28014 | cmd.fromCmdLine = fromCmdLine; |
| 26642 | 28015 | rc = arParseCommand(azArg, nArg, &cmd); |
| 26643 | 28016 | if( rc==SQLITE_OK ){ |
| 26644 | 28017 | int eDbType = SHELL_OPEN_UNSPEC; |
| 26645 | 28018 | cmd.p = pState; |
| 28019 | + cmd.out = pState->out; | |
| 26646 | 28020 | cmd.db = pState->db; |
| 26647 | 28021 | if( cmd.zFile ){ |
| 26648 | 28022 | eDbType = deduceDatabaseType(cmd.zFile, 1); |
| 26649 | 28023 | }else{ |
| 26650 | 28024 | eDbType = pState->openMode; |
| @@ -26667,17 +28041,18 @@ | ||
| 26667 | 28041 | }else{ |
| 26668 | 28042 | flags = SQLITE_OPEN_READONLY; |
| 26669 | 28043 | } |
| 26670 | 28044 | cmd.db = 0; |
| 26671 | 28045 | if( cmd.bDryRun ){ |
| 26672 | - oputf("-- open database '%s'%s\n", cmd.zFile, | |
| 28046 | + sqlite3_fprintf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, | |
| 26673 | 28047 | eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); |
| 26674 | 28048 | } |
| 26675 | 28049 | rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, |
| 26676 | 28050 | eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); |
| 26677 | 28051 | if( rc!=SQLITE_OK ){ |
| 26678 | - eputf("cannot open file: %s (%s)\n", cmd.zFile, sqlite3_errmsg(cmd.db)); | |
| 28052 | + sqlite3_fprintf(stderr, "cannot open file: %s (%s)\n", | |
| 28053 | + cmd.zFile, sqlite3_errmsg(cmd.db)); | |
| 26679 | 28054 | goto end_ar_command; |
| 26680 | 28055 | } |
| 26681 | 28056 | sqlite3_fileio_init(cmd.db, 0, 0); |
| 26682 | 28057 | sqlite3_sqlar_init(cmd.db, 0, 0); |
| 26683 | 28058 | sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, |
| @@ -26686,11 +28061,11 @@ | ||
| 26686 | 28061 | } |
| 26687 | 28062 | if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ |
| 26688 | 28063 | if( cmd.eCmd!=AR_CMD_CREATE |
| 26689 | 28064 | && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) |
| 26690 | 28065 | ){ |
| 26691 | - eputz("database does not contain an 'sqlar' table\n"); | |
| 28066 | + sqlite3_fprintf(stderr, "database does not contain an 'sqlar' table\n"); | |
| 26692 | 28067 | rc = SQLITE_ERROR; |
| 26693 | 28068 | goto end_ar_command; |
| 26694 | 28069 | } |
| 26695 | 28070 | cmd.zSrcTable = sqlite3_mprintf("sqlar"); |
| 26696 | 28071 | } |
| @@ -26744,11 +28119,11 @@ | ||
| 26744 | 28119 | ** This function is used as a callback by the recover extension. Simply |
| 26745 | 28120 | ** print the supplied SQL statement to stdout. |
| 26746 | 28121 | */ |
| 26747 | 28122 | static int recoverSqlCb(void *pCtx, const char *zSql){ |
| 26748 | 28123 | ShellState *pState = (ShellState*)pCtx; |
| 26749 | - sputf(pState->out, "%s;\n", zSql); | |
| 28124 | + sqlite3_fprintf(pState->out, "%s;\n", zSql); | |
| 26750 | 28125 | return SQLITE_OK; |
| 26751 | 28126 | } |
| 26752 | 28127 | |
| 26753 | 28128 | /* |
| 26754 | 28129 | ** This function is called to recover data from the database. A script |
| @@ -26787,11 +28162,11 @@ | ||
| 26787 | 28162 | }else |
| 26788 | 28163 | if( n<=10 && memcmp("-no-rowids", z, n)==0 ){ |
| 26789 | 28164 | bRowids = 0; |
| 26790 | 28165 | } |
| 26791 | 28166 | else{ |
| 26792 | - eputf("unexpected option: %s\n", azArg[i]); | |
| 28167 | + sqlite3_fprintf(stderr,"unexpected option: %s\n", azArg[i]); | |
| 26793 | 28168 | showHelp(pState->out, azArg[0]); |
| 26794 | 28169 | return 1; |
| 26795 | 28170 | } |
| 26796 | 28171 | } |
| 26797 | 28172 | |
| @@ -26806,11 +28181,11 @@ | ||
| 26806 | 28181 | |
| 26807 | 28182 | sqlite3_recover_run(p); |
| 26808 | 28183 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 26809 | 28184 | const char *zErr = sqlite3_recover_errmsg(p); |
| 26810 | 28185 | int errCode = sqlite3_recover_errcode(p); |
| 26811 | - eputf("sql error: %s (%d)\n", zErr, errCode); | |
| 28186 | + sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); | |
| 26812 | 28187 | } |
| 26813 | 28188 | rc = sqlite3_recover_finish(p); |
| 26814 | 28189 | return rc; |
| 26815 | 28190 | } |
| 26816 | 28191 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| @@ -26828,25 +28203,25 @@ | ||
| 26828 | 28203 | i64 nError = 0; |
| 26829 | 28204 | const char *zErr = 0; |
| 26830 | 28205 | while( SQLITE_OK==sqlite3_intck_step(p) ){ |
| 26831 | 28206 | const char *zMsg = sqlite3_intck_message(p); |
| 26832 | 28207 | if( zMsg ){ |
| 26833 | - oputf("%s\n", zMsg); | |
| 28208 | + sqlite3_fprintf(pState->out, "%s\n", zMsg); | |
| 26834 | 28209 | nError++; |
| 26835 | 28210 | } |
| 26836 | 28211 | nStep++; |
| 26837 | 28212 | if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){ |
| 26838 | 28213 | sqlite3_intck_unlock(p); |
| 26839 | 28214 | } |
| 26840 | 28215 | } |
| 26841 | 28216 | rc = sqlite3_intck_error(p, &zErr); |
| 26842 | 28217 | if( zErr ){ |
| 26843 | - eputf("%s\n", zErr); | |
| 28218 | + sqlite3_fprintf(stderr,"%s\n", zErr); | |
| 26844 | 28219 | } |
| 26845 | 28220 | sqlite3_intck_close(p); |
| 26846 | 28221 | |
| 26847 | - oputf("%lld steps, %lld errors\n", nStep, nError); | |
| 28222 | + sqlite3_fprintf(pState->out, "%lld steps, %lld errors\n", nStep, nError); | |
| 26848 | 28223 | } |
| 26849 | 28224 | |
| 26850 | 28225 | return rc; |
| 26851 | 28226 | } |
| 26852 | 28227 | |
| @@ -26865,11 +28240,11 @@ | ||
| 26865 | 28240 | */ |
| 26866 | 28241 | #ifdef SHELL_DEBUG |
| 26867 | 28242 | #define rc_err_oom_die(rc) \ |
| 26868 | 28243 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ |
| 26869 | 28244 | else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ |
| 26870 | - eputf("E:%d\n",rc), assert(0) | |
| 28245 | + sqlite3_fprintf(stderr,"E:%d\n",rc), assert(0) | |
| 26871 | 28246 | #else |
| 26872 | 28247 | static void rc_err_oom_die(int rc){ |
| 26873 | 28248 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); |
| 26874 | 28249 | assert(rc==SQLITE_OK||rc==SQLITE_DONE); |
| 26875 | 28250 | } |
| @@ -27023,12 +28398,12 @@ | ||
| 27023 | 28398 | }else if( *pDb==0 ){ |
| 27024 | 28399 | return 0; |
| 27025 | 28400 | }else{ |
| 27026 | 28401 | /* Formulate the columns spec, close the DB, zero *pDb. */ |
| 27027 | 28402 | char *zColsSpec = 0; |
| 27028 | - int hasDupes = db_int(*pDb, zHasDupes); | |
| 27029 | - int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0; | |
| 28403 | + int hasDupes = db_int(*pDb, "%s", zHasDupes); | |
| 28404 | + int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0; | |
| 27030 | 28405 | if( hasDupes ){ |
| 27031 | 28406 | #ifdef SHELL_COLUMN_RENAME_CLEAN |
| 27032 | 28407 | rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0); |
| 27033 | 28408 | rc_err_oom_die(rc); |
| 27034 | 28409 | #endif |
| @@ -27039,11 +28414,11 @@ | ||
| 27039 | 28414 | sqlite3_bind_int(pStmt, 1, nDigits); |
| 27040 | 28415 | rc = sqlite3_step(pStmt); |
| 27041 | 28416 | sqlite3_finalize(pStmt); |
| 27042 | 28417 | if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); |
| 27043 | 28418 | } |
| 27044 | - assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ | |
| 28419 | + assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */ | |
| 27045 | 28420 | rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 27046 | 28421 | rc_err_oom_die(rc); |
| 27047 | 28422 | rc = sqlite3_step(pStmt); |
| 27048 | 28423 | if( rc==SQLITE_ROW ){ |
| 27049 | 28424 | zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); |
| @@ -27082,12 +28457,13 @@ | ||
| 27082 | 28457 | shellPreparePrintf(p->db, &rc, &pStmt, |
| 27083 | 28458 | "SELECT 1 FROM sqlite_schema o WHERE " |
| 27084 | 28459 | "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true" |
| 27085 | 28460 | ); |
| 27086 | 28461 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 27087 | - oputz("/* WARNING: " | |
| 27088 | - "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n" | |
| 28462 | + sqlite3_fputs("/* WARNING: " | |
| 28463 | + "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n", | |
| 28464 | + p->out | |
| 27089 | 28465 | ); |
| 27090 | 28466 | } |
| 27091 | 28467 | shellFinalize(&rc, pStmt); |
| 27092 | 28468 | return rc; |
| 27093 | 28469 | } |
| @@ -27114,16 +28490,18 @@ | ||
| 27114 | 28490 | return SQLITE_OK; |
| 27115 | 28491 | } |
| 27116 | 28492 | if( faultsim_state.iCnt ){ |
| 27117 | 28493 | if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; |
| 27118 | 28494 | if( faultsim_state.eVerbose>=2 ){ |
| 27119 | - oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); | |
| 28495 | + sqlite3_fprintf(stdout, | |
| 28496 | + "FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); | |
| 27120 | 28497 | } |
| 27121 | 28498 | return SQLITE_OK; |
| 27122 | 28499 | } |
| 27123 | 28500 | if( faultsim_state.eVerbose>=1 ){ |
| 27124 | - oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); | |
| 28501 | + sqlite3_fprintf(stdout, | |
| 28502 | + "FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); | |
| 27125 | 28503 | } |
| 27126 | 28504 | faultsim_state.iCnt = faultsim_state.iInterval; |
| 27127 | 28505 | faultsim_state.nHit++; |
| 27128 | 28506 | if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){ |
| 27129 | 28507 | faultsim_state.iCnt = -1; |
| @@ -27182,11 +28560,11 @@ | ||
| 27182 | 28560 | clearTempFile(p); |
| 27183 | 28561 | |
| 27184 | 28562 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 27185 | 28563 | if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ |
| 27186 | 28564 | if( nArg!=2 ){ |
| 27187 | - eputz("Usage: .auth ON|OFF\n"); | |
| 28565 | + sqlite3_fprintf(stderr, "Usage: .auth ON|OFF\n"); | |
| 27188 | 28566 | rc = 1; |
| 27189 | 28567 | goto meta_command_exit; |
| 27190 | 28568 | } |
| 27191 | 28569 | open_db(p, 0); |
| 27192 | 28570 | if( booleanValue(azArg[1]) ){ |
| @@ -27229,32 +28607,32 @@ | ||
| 27229 | 28607 | }else |
| 27230 | 28608 | if( cli_strcmp(z, "-async")==0 ){ |
| 27231 | 28609 | bAsync = 1; |
| 27232 | 28610 | }else |
| 27233 | 28611 | { |
| 27234 | - eputf("unknown option: %s\n", azArg[j]); | |
| 28612 | + sqlite3_fprintf(stderr,"unknown option: %s\n", azArg[j]); | |
| 27235 | 28613 | return 1; |
| 27236 | 28614 | } |
| 27237 | 28615 | }else if( zDestFile==0 ){ |
| 27238 | 28616 | zDestFile = azArg[j]; |
| 27239 | 28617 | }else if( zDb==0 ){ |
| 27240 | 28618 | zDb = zDestFile; |
| 27241 | 28619 | zDestFile = azArg[j]; |
| 27242 | 28620 | }else{ |
| 27243 | - eputz("Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); | |
| 28621 | + sqlite3_fprintf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); | |
| 27244 | 28622 | return 1; |
| 27245 | 28623 | } |
| 27246 | 28624 | } |
| 27247 | 28625 | if( zDestFile==0 ){ |
| 27248 | - eputz("missing FILENAME argument on .backup\n"); | |
| 28626 | + sqlite3_fprintf(stderr, "missing FILENAME argument on .backup\n"); | |
| 27249 | 28627 | return 1; |
| 27250 | 28628 | } |
| 27251 | 28629 | if( zDb==0 ) zDb = "main"; |
| 27252 | 28630 | rc = sqlite3_open_v2(zDestFile, &pDest, |
| 27253 | 28631 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); |
| 27254 | 28632 | if( rc!=SQLITE_OK ){ |
| 27255 | - eputf("Error: cannot open \"%s\"\n", zDestFile); | |
| 28633 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zDestFile); | |
| 27256 | 28634 | close_db(pDest); |
| 27257 | 28635 | return 1; |
| 27258 | 28636 | } |
| 27259 | 28637 | if( bAsync ){ |
| 27260 | 28638 | sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;", |
| @@ -27286,23 +28664,14 @@ | ||
| 27286 | 28664 | eputz("Usage: .bail on|off\n"); |
| 27287 | 28665 | rc = 1; |
| 27288 | 28666 | } |
| 27289 | 28667 | }else |
| 27290 | 28668 | |
| 27291 | - /* Undocumented. Legacy only. See "crnl" below */ | |
| 28669 | + /* Undocumented. Legacy only. See "crlf" below */ | |
| 27292 | 28670 | if( c=='b' && n>=3 && cli_strncmp(azArg[0], "binary", n)==0 ){ |
| 27293 | - if( nArg==2 ){ | |
| 27294 | - if( booleanValue(azArg[1]) ){ | |
| 27295 | - setBinaryMode(p->out, 1); | |
| 27296 | - }else{ | |
| 27297 | - setTextMode(p->out, 1); | |
| 27298 | - } | |
| 27299 | - }else{ | |
| 27300 | - eputz("The \".binary\" command is deprecated. Use \".crnl\" instead.\n" | |
| 27301 | - "Usage: .binary on|off\n"); | |
| 27302 | - rc = 1; | |
| 27303 | - } | |
| 28671 | + eputz("The \".binary\" command is deprecated.\n"); | |
| 28672 | + rc = 1; | |
| 27304 | 28673 | }else |
| 27305 | 28674 | |
| 27306 | 28675 | /* The undocumented ".breakpoint" command causes a call to the no-op |
| 27307 | 28676 | ** routine named test_breakpoint(). |
| 27308 | 28677 | */ |
| @@ -27320,11 +28689,11 @@ | ||
| 27320 | 28689 | sqlite3_free(z); |
| 27321 | 28690 | #else |
| 27322 | 28691 | rc = chdir(azArg[1]); |
| 27323 | 28692 | #endif |
| 27324 | 28693 | if( rc ){ |
| 27325 | - eputf("Cannot change to directory \"%s\"\n", azArg[1]); | |
| 28694 | + sqlite3_fprintf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]); | |
| 27326 | 28695 | rc = 1; |
| 27327 | 28696 | } |
| 27328 | 28697 | }else{ |
| 27329 | 28698 | eputz("Usage: .cd DIRECTORY\n"); |
| 27330 | 28699 | rc = 1; |
| @@ -27353,15 +28722,16 @@ | ||
| 27353 | 28722 | eputz("Usage: .check GLOB-PATTERN\n"); |
| 27354 | 28723 | rc = 2; |
| 27355 | 28724 | }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ |
| 27356 | 28725 | rc = 2; |
| 27357 | 28726 | }else if( testcase_glob(azArg[1],zRes)==0 ){ |
| 27358 | - eputf("testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", | |
| 28727 | + sqlite3_fprintf(stderr, | |
| 28728 | + "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", | |
| 27359 | 28729 | p->zTestcase, azArg[1], zRes); |
| 27360 | 28730 | rc = 1; |
| 27361 | 28731 | }else{ |
| 27362 | - oputf("testcase-%s ok\n", p->zTestcase); | |
| 28732 | + sqlite3_fprintf(p->out, "testcase-%s ok\n", p->zTestcase); | |
| 27363 | 28733 | p->nCheck++; |
| 27364 | 28734 | } |
| 27365 | 28735 | sqlite3_free(zRes); |
| 27366 | 28736 | }else |
| 27367 | 28737 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| @@ -27390,13 +28760,13 @@ | ||
| 27390 | 28760 | zFile = "(memory)"; |
| 27391 | 28761 | }else if( zFile[0]==0 ){ |
| 27392 | 28762 | zFile = "(temporary-file)"; |
| 27393 | 28763 | } |
| 27394 | 28764 | if( p->pAuxDb == &p->aAuxDb[i] ){ |
| 27395 | - sputf(stdout, "ACTIVE %d: %s\n", i, zFile); | |
| 28765 | + sqlite3_fprintf(stdout, "ACTIVE %d: %s\n", i, zFile); | |
| 27396 | 28766 | }else if( p->aAuxDb[i].db!=0 ){ |
| 27397 | - sputf(stdout, " %d: %s\n", i, zFile); | |
| 28767 | + sqlite3_fprintf(stdout, " %d: %s\n", i, zFile); | |
| 27398 | 28768 | } |
| 27399 | 28769 | } |
| 27400 | 28770 | }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ |
| 27401 | 28771 | int i = azArg[1][0] - '0'; |
| 27402 | 28772 | if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){ |
| @@ -27422,24 +28792,22 @@ | ||
| 27422 | 28792 | eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n"); |
| 27423 | 28793 | rc = 1; |
| 27424 | 28794 | } |
| 27425 | 28795 | }else |
| 27426 | 28796 | |
| 27427 | - if( c=='c' && n==4 && cli_strncmp(azArg[0], "crnl", n)==0 ){ | |
| 28797 | + if( c=='c' && n==4 | |
| 28798 | + && (cli_strncmp(azArg[0], "crlf", n)==0 | |
| 28799 | + || cli_strncmp(azArg[0], "crnl",n)==0) | |
| 28800 | + ){ | |
| 27428 | 28801 | if( nArg==2 ){ |
| 27429 | - if( booleanValue(azArg[1]) ){ | |
| 27430 | - setTextMode(p->out, 1); | |
| 27431 | - }else{ | |
| 27432 | - setBinaryMode(p->out, 1); | |
| 27433 | - } | |
| 27434 | - }else{ | |
| 27435 | -#if !defined(_WIN32) && !defined(WIN32) | |
| 27436 | - eputz("The \".crnl\" is a no-op on non-Windows machines.\n"); | |
| 28802 | +#ifdef _WIN32 | |
| 28803 | + p->crlfMode = booleanValue(azArg[1]); | |
| 28804 | +#else | |
| 28805 | + p->crlfMode = 0; | |
| 27437 | 28806 | #endif |
| 27438 | - eputz("Usage: .crnl on|off\n"); | |
| 27439 | - rc = 1; | |
| 27440 | 28807 | } |
| 28808 | + sqlite3_fprintf(stderr, "crlf is %s\n", p->crlfMode ? "ON" : "OFF"); | |
| 27441 | 28809 | }else |
| 27442 | 28810 | |
| 27443 | 28811 | if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){ |
| 27444 | 28812 | char **azName = 0; |
| 27445 | 28813 | int nName = 0; |
| @@ -27465,11 +28833,11 @@ | ||
| 27465 | 28833 | sqlite3_finalize(pStmt); |
| 27466 | 28834 | for(i=0; i<nName; i++){ |
| 27467 | 28835 | int eTxn = sqlite3_txn_state(p->db, azName[i*2]); |
| 27468 | 28836 | int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); |
| 27469 | 28837 | const char *z = azName[i*2+1]; |
| 27470 | - oputf("%s: %s %s%s\n", | |
| 28838 | + sqlite3_fprintf(p->out, "%s: %s %s%s\n", | |
| 27471 | 28839 | azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", |
| 27472 | 28840 | eTxn==SQLITE_TXN_NONE ? "" : |
| 27473 | 28841 | eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); |
| 27474 | 28842 | free(azName[i*2]); |
| 27475 | 28843 | free(azName[i*2+1]); |
| @@ -27507,15 +28875,16 @@ | ||
| 27507 | 28875 | if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; |
| 27508 | 28876 | if( nArg>=3 ){ |
| 27509 | 28877 | sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); |
| 27510 | 28878 | } |
| 27511 | 28879 | sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); |
| 27512 | - oputf("%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); | |
| 28880 | + sqlite3_fprintf(p->out, "%19s %s\n", | |
| 28881 | + aDbConfig[ii].zName, v ? "on" : "off"); | |
| 27513 | 28882 | if( nArg>1 ) break; |
| 27514 | 28883 | } |
| 27515 | 28884 | if( nArg>1 && ii==ArraySize(aDbConfig) ){ |
| 27516 | - eputf("Error: unknown dbconfig \"%s\"\n", azArg[1]); | |
| 28885 | + sqlite3_fprintf(stderr,"Error: unknown dbconfig \"%s\"\n", azArg[1]); | |
| 27517 | 28886 | eputz("Enter \".dbconfig\" with no arguments for a list\n"); |
| 27518 | 28887 | } |
| 27519 | 28888 | }else |
| 27520 | 28889 | |
| 27521 | 28890 | #if SQLITE_SHELL_HAVE_RECOVER |
| @@ -27561,11 +28930,12 @@ | ||
| 27561 | 28930 | }else |
| 27562 | 28931 | if( cli_strcmp(z,"nosys")==0 ){ |
| 27563 | 28932 | ShellSetFlag(p, SHFLG_DumpNoSys); |
| 27564 | 28933 | }else |
| 27565 | 28934 | { |
| 27566 | - eputf("Unknown option \"%s\" on \".dump\"\n", azArg[i]); | |
| 28935 | + sqlite3_fprintf(stderr, | |
| 28936 | + "Unknown option \"%s\" on \".dump\"\n", azArg[i]); | |
| 27567 | 28937 | rc = 1; |
| 27568 | 28938 | sqlite3_free(zLike); |
| 27569 | 28939 | goto meta_command_exit; |
| 27570 | 28940 | } |
| 27571 | 28941 | }else{ |
| @@ -27596,12 +28966,12 @@ | ||
| 27596 | 28966 | outputDumpWarning(p, zLike); |
| 27597 | 28967 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 27598 | 28968 | /* When playing back a "dump", the content might appear in an order |
| 27599 | 28969 | ** which causes immediate foreign key constraints to be violated. |
| 27600 | 28970 | ** So disable foreign-key constraint enforcement to prevent problems. */ |
| 27601 | - oputz("PRAGMA foreign_keys=OFF;\n"); | |
| 27602 | - oputz("BEGIN TRANSACTION;\n"); | |
| 28971 | + sqlite3_fputs("PRAGMA foreign_keys=OFF;\n", p->out); | |
| 28972 | + sqlite3_fputs("BEGIN TRANSACTION;\n", p->out); | |
| 27603 | 28973 | } |
| 27604 | 28974 | p->writableSchema = 0; |
| 27605 | 28975 | p->showHeader = 0; |
| 27606 | 28976 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 27607 | 28977 | ** as much of the schema as it can even if the sqlite_schema table is |
| @@ -27629,17 +28999,17 @@ | ||
| 27629 | 28999 | run_table_dump_query(p, zSql); |
| 27630 | 29000 | sqlite3_free(zSql); |
| 27631 | 29001 | } |
| 27632 | 29002 | sqlite3_free(zLike); |
| 27633 | 29003 | if( p->writableSchema ){ |
| 27634 | - oputz("PRAGMA writable_schema=OFF;\n"); | |
| 29004 | + sqlite3_fputs("PRAGMA writable_schema=OFF;\n", p->out); | |
| 27635 | 29005 | p->writableSchema = 0; |
| 27636 | 29006 | } |
| 27637 | 29007 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| 27638 | 29008 | sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); |
| 27639 | 29009 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 27640 | - oputz(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); | |
| 29010 | + sqlite3_fputs(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out); | |
| 27641 | 29011 | } |
| 27642 | 29012 | p->showHeader = savedShowHeader; |
| 27643 | 29013 | p->shellFlgs = savedShellFlags; |
| 27644 | 29014 | }else |
| 27645 | 29015 | |
| @@ -27649,10 +29019,14 @@ | ||
| 27649 | 29019 | }else{ |
| 27650 | 29020 | eputz("Usage: .echo on|off\n"); |
| 27651 | 29021 | rc = 1; |
| 27652 | 29022 | } |
| 27653 | 29023 | }else |
| 29024 | + | |
| 29025 | + if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){ | |
| 29026 | + rc = shell_dbtotxt_command(p, nArg, azArg); | |
| 29027 | + }else | |
| 27654 | 29028 | |
| 27655 | 29029 | if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){ |
| 27656 | 29030 | if( nArg==2 ){ |
| 27657 | 29031 | p->autoEQPtest = 0; |
| 27658 | 29032 | if( p->autoEQPtrace ){ |
| @@ -27715,11 +29089,12 @@ | ||
| 27715 | 29089 | }else |
| 27716 | 29090 | |
| 27717 | 29091 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 27718 | 29092 | if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ |
| 27719 | 29093 | if( p->bSafeMode ){ |
| 27720 | - eputf("Cannot run experimental commands such as \"%s\" in safe mode\n", | |
| 29094 | + sqlite3_fprintf(stderr, | |
| 29095 | + "Cannot run experimental commands such as \"%s\" in safe mode\n", | |
| 27721 | 29096 | azArg[0]); |
| 27722 | 29097 | rc = 1; |
| 27723 | 29098 | }else{ |
| 27724 | 29099 | open_db(p, 0); |
| 27725 | 29100 | expertDotCommand(p, azArg, nArg); |
| @@ -27772,13 +29147,14 @@ | ||
| 27772 | 29147 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 27773 | 29148 | } |
| 27774 | 29149 | |
| 27775 | 29150 | /* --help lists all file-controls */ |
| 27776 | 29151 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 27777 | - oputz("Available file-controls:\n"); | |
| 29152 | + sqlite3_fputs("Available file-controls:\n", p->out); | |
| 27778 | 29153 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 27779 | - oputf(" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); | |
| 29154 | + sqlite3_fprintf(p->out, | |
| 29155 | + " .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); | |
| 27780 | 29156 | } |
| 27781 | 29157 | rc = 1; |
| 27782 | 29158 | goto meta_command_exit; |
| 27783 | 29159 | } |
| 27784 | 29160 | |
| @@ -27789,19 +29165,19 @@ | ||
| 27789 | 29165 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 27790 | 29166 | if( filectrl<0 ){ |
| 27791 | 29167 | filectrl = aCtrl[i].ctrlCode; |
| 27792 | 29168 | iCtrl = i; |
| 27793 | 29169 | }else{ |
| 27794 | - eputf("Error: ambiguous file-control: \"%s\"\n" | |
| 29170 | + sqlite3_fprintf(stderr,"Error: ambiguous file-control: \"%s\"\n" | |
| 27795 | 29171 | "Use \".filectrl --help\" for help\n", zCmd); |
| 27796 | 29172 | rc = 1; |
| 27797 | 29173 | goto meta_command_exit; |
| 27798 | 29174 | } |
| 27799 | 29175 | } |
| 27800 | 29176 | } |
| 27801 | 29177 | if( filectrl<0 ){ |
| 27802 | - eputf("Error: unknown file-control: %s\n" | |
| 29178 | + sqlite3_fprintf(stderr,"Error: unknown file-control: %s\n" | |
| 27803 | 29179 | "Use \".filectrl --help\" for help\n", zCmd); |
| 27804 | 29180 | }else{ |
| 27805 | 29181 | switch(filectrl){ |
| 27806 | 29182 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 27807 | 29183 | if( nArg!=2 && nArg!=3 ) break; |
| @@ -27841,11 +29217,11 @@ | ||
| 27841 | 29217 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 27842 | 29218 | char *z = 0; |
| 27843 | 29219 | if( nArg!=2 ) break; |
| 27844 | 29220 | sqlite3_file_control(p->db, zSchema, filectrl, &z); |
| 27845 | 29221 | if( z ){ |
| 27846 | - oputf("%s\n", z); | |
| 29222 | + sqlite3_fprintf(p->out, "%s\n", z); | |
| 27847 | 29223 | sqlite3_free(z); |
| 27848 | 29224 | } |
| 27849 | 29225 | isOk = 2; |
| 27850 | 29226 | break; |
| 27851 | 29227 | } |
| @@ -27855,23 +29231,24 @@ | ||
| 27855 | 29231 | x = atoi(azArg[2]); |
| 27856 | 29232 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 27857 | 29233 | } |
| 27858 | 29234 | x = -1; |
| 27859 | 29235 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 27860 | - oputf("%d\n", x); | |
| 29236 | + sqlite3_fprintf(p->out, "%d\n", x); | |
| 27861 | 29237 | isOk = 2; |
| 27862 | 29238 | break; |
| 27863 | 29239 | } |
| 27864 | 29240 | } |
| 27865 | 29241 | } |
| 27866 | 29242 | if( isOk==0 && iCtrl>=0 ){ |
| 27867 | - oputf("Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); | |
| 29243 | + sqlite3_fprintf(p->out, "Usage: .filectrl %s %s\n", | |
| 29244 | + zCmd, aCtrl[iCtrl].zUsage); | |
| 27868 | 29245 | rc = 1; |
| 27869 | 29246 | }else if( isOk==1 ){ |
| 27870 | 29247 | char zBuf[100]; |
| 27871 | 29248 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); |
| 27872 | - oputf("%s\n", zBuf); | |
| 29249 | + sqlite3_fprintf(p->out, "%s\n", zBuf); | |
| 27873 | 29250 | } |
| 27874 | 29251 | }else |
| 27875 | 29252 | |
| 27876 | 29253 | if( c=='f' && cli_strncmp(azArg[0], "fullschema", n)==0 ){ |
| 27877 | 29254 | ShellState data; |
| @@ -27908,19 +29285,19 @@ | ||
| 27908 | 29285 | doStats = sqlite3_step(pStmt)==SQLITE_ROW; |
| 27909 | 29286 | sqlite3_finalize(pStmt); |
| 27910 | 29287 | } |
| 27911 | 29288 | } |
| 27912 | 29289 | if( doStats==0 ){ |
| 27913 | - oputz("/* No STAT tables available */\n"); | |
| 29290 | + sqlite3_fputs("/* No STAT tables available */\n", p->out); | |
| 27914 | 29291 | }else{ |
| 27915 | - oputz("ANALYZE sqlite_schema;\n"); | |
| 29292 | + sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); | |
| 27916 | 29293 | data.cMode = data.mode = MODE_Insert; |
| 27917 | 29294 | data.zDestTable = "sqlite_stat1"; |
| 27918 | 29295 | shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); |
| 27919 | 29296 | data.zDestTable = "sqlite_stat4"; |
| 27920 | 29297 | shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); |
| 27921 | - oputz("ANALYZE sqlite_schema;\n"); | |
| 29298 | + sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); | |
| 27922 | 29299 | } |
| 27923 | 29300 | }else |
| 27924 | 29301 | |
| 27925 | 29302 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 27926 | 29303 | if( nArg==2 ){ |
| @@ -27934,11 +29311,11 @@ | ||
| 27934 | 29311 | |
| 27935 | 29312 | if( c=='h' && cli_strncmp(azArg[0], "help", n)==0 ){ |
| 27936 | 29313 | if( nArg>=2 ){ |
| 27937 | 29314 | n = showHelp(p->out, azArg[1]); |
| 27938 | 29315 | if( n==0 ){ |
| 27939 | - oputf("Nothing matches '%s'\n", azArg[1]); | |
| 29316 | + sqlite3_fprintf(p->out, "Nothing matches '%s'\n", azArg[1]); | |
| 27940 | 29317 | } |
| 27941 | 29318 | }else{ |
| 27942 | 29319 | showHelp(p->out, 0); |
| 27943 | 29320 | } |
| 27944 | 29321 | }else |
| @@ -27977,11 +29354,11 @@ | ||
| 27977 | 29354 | if( zFile==0 ){ |
| 27978 | 29355 | zFile = z; |
| 27979 | 29356 | }else if( zTable==0 ){ |
| 27980 | 29357 | zTable = z; |
| 27981 | 29358 | }else{ |
| 27982 | - oputf("ERROR: extra argument: \"%s\". Usage:\n", z); | |
| 29359 | + sqlite3_fprintf(p->out, "ERROR: extra argument: \"%s\". Usage:\n",z); | |
| 27983 | 29360 | showHelp(p->out, "import"); |
| 27984 | 29361 | goto meta_command_exit; |
| 27985 | 29362 | } |
| 27986 | 29363 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 27987 | 29364 | eVerbose++; |
| @@ -27998,17 +29375,17 @@ | ||
| 27998 | 29375 | sCtx.cColSep = ','; |
| 27999 | 29376 | sCtx.cRowSep = '\n'; |
| 28000 | 29377 | xRead = csv_read_one_field; |
| 28001 | 29378 | useOutputMode = 0; |
| 28002 | 29379 | }else{ |
| 28003 | - oputf("ERROR: unknown option: \"%s\". Usage:\n", z); | |
| 29380 | + sqlite3_fprintf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); | |
| 28004 | 29381 | showHelp(p->out, "import"); |
| 28005 | 29382 | goto meta_command_exit; |
| 28006 | 29383 | } |
| 28007 | 29384 | } |
| 28008 | 29385 | if( zTable==0 ){ |
| 28009 | - oputf("ERROR: missing %s argument. Usage:\n", | |
| 29386 | + sqlite3_fprintf(p->out, "ERROR: missing %s argument. Usage:\n", | |
| 28010 | 29387 | zFile==0 ? "FILE" : "TABLE"); |
| 28011 | 29388 | showHelp(p->out, "import"); |
| 28012 | 29389 | goto meta_command_exit; |
| 28013 | 29390 | } |
| 28014 | 29391 | seenInterrupt = 0; |
| @@ -28054,32 +29431,32 @@ | ||
| 28054 | 29431 | if( sCtx.zFile[0]=='|' ){ |
| 28055 | 29432 | #ifdef SQLITE_OMIT_POPEN |
| 28056 | 29433 | eputz("Error: pipes are not supported in this OS\n"); |
| 28057 | 29434 | goto meta_command_exit; |
| 28058 | 29435 | #else |
| 28059 | - sCtx.in = popen(sCtx.zFile+1, "r"); | |
| 29436 | + sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); | |
| 28060 | 29437 | sCtx.zFile = "<pipe>"; |
| 28061 | 29438 | sCtx.xCloser = pclose; |
| 28062 | 29439 | #endif |
| 28063 | 29440 | }else{ |
| 28064 | - sCtx.in = fopen(sCtx.zFile, "rb"); | |
| 29441 | + sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); | |
| 28065 | 29442 | sCtx.xCloser = fclose; |
| 28066 | 29443 | } |
| 28067 | 29444 | if( sCtx.in==0 ){ |
| 28068 | - eputf("Error: cannot open \"%s\"\n", zFile); | |
| 29445 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); | |
| 28069 | 29446 | goto meta_command_exit; |
| 28070 | 29447 | } |
| 28071 | 29448 | if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 28072 | 29449 | char zSep[2]; |
| 28073 | 29450 | zSep[1] = 0; |
| 28074 | 29451 | zSep[0] = sCtx.cColSep; |
| 28075 | - oputz("Column separator "); | |
| 28076 | - output_c_string(zSep); | |
| 28077 | - oputz(", row separator "); | |
| 29452 | + sqlite3_fputs("Column separator ", p->out); | |
| 29453 | + output_c_string(p->out, zSep); | |
| 29454 | + sqlite3_fputs(", row separator ", p->out); | |
| 28078 | 29455 | zSep[0] = sCtx.cRowSep; |
| 28079 | - output_c_string(zSep); | |
| 28080 | - oputz("\n"); | |
| 29456 | + output_c_string(p->out, zSep); | |
| 29457 | + sqlite3_fputs("\n", p->out); | |
| 28081 | 29458 | } |
| 28082 | 29459 | sCtx.z = sqlite3_malloc64(120); |
| 28083 | 29460 | if( sCtx.z==0 ){ |
| 28084 | 29461 | import_cleanup(&sCtx); |
| 28085 | 29462 | shell_out_of_memory(); |
| @@ -28087,11 +29464,15 @@ | ||
| 28087 | 29464 | /* Below, resources must be freed before exit. */ |
| 28088 | 29465 | while( (nSkip--)>0 ){ |
| 28089 | 29466 | while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} |
| 28090 | 29467 | } |
| 28091 | 29468 | import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 28092 | - if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){ | |
| 29469 | + if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) | |
| 29470 | + && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema" | |
| 29471 | + " WHERE name=%Q AND type='view'", | |
| 29472 | + zSchema ? zSchema : "main", zTable) | |
| 29473 | + ){ | |
| 28093 | 29474 | /* Table does not exist. Create it. */ |
| 28094 | 29475 | sqlite3 *dbCols = 0; |
| 28095 | 29476 | char *zRenames = 0; |
| 28096 | 29477 | char *zColDefs; |
| 28097 | 29478 | zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
| @@ -28100,18 +29481,18 @@ | ||
| 28100 | 29481 | zAutoColumn(sCtx.z, &dbCols, 0); |
| 28101 | 29482 | if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 28102 | 29483 | } |
| 28103 | 29484 | zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
| 28104 | 29485 | if( zRenames!=0 ){ |
| 28105 | - sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr, | |
| 29486 | + sqlite3_fprintf((stdin_is_interactive && p->in==stdin)? p->out : stderr, | |
| 28106 | 29487 | "Columns renamed during .import %s due to duplicates:\n" |
| 28107 | 29488 | "%s\n", sCtx.zFile, zRenames); |
| 28108 | 29489 | sqlite3_free(zRenames); |
| 28109 | 29490 | } |
| 28110 | 29491 | assert(dbCols==0); |
| 28111 | 29492 | if( zColDefs==0 ){ |
| 28112 | - eputf("%s: empty file\n", sCtx.zFile); | |
| 29493 | + sqlite3_fprintf(stderr,"%s: empty file\n", sCtx.zFile); | |
| 28113 | 29494 | import_cleanup(&sCtx); |
| 28114 | 29495 | rc = 1; |
| 28115 | 29496 | sqlite3_free(zCreate); |
| 28116 | 29497 | goto meta_command_exit; |
| 28117 | 29498 | } |
| @@ -28119,17 +29500,20 @@ | ||
| 28119 | 29500 | if( zCreate==0 ){ |
| 28120 | 29501 | import_cleanup(&sCtx); |
| 28121 | 29502 | shell_out_of_memory(); |
| 28122 | 29503 | } |
| 28123 | 29504 | if( eVerbose>=1 ){ |
| 28124 | - oputf("%s\n", zCreate); | |
| 29505 | + sqlite3_fprintf(p->out, "%s\n", zCreate); | |
| 28125 | 29506 | } |
| 28126 | 29507 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 29508 | + if( rc ){ | |
| 29509 | + sqlite3_fprintf(stderr, | |
| 29510 | + "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); | |
| 29511 | + } | |
| 28127 | 29512 | sqlite3_free(zCreate); |
| 28128 | 29513 | zCreate = 0; |
| 28129 | 29514 | if( rc ){ |
| 28130 | - eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); | |
| 28131 | 29515 | import_cleanup(&sCtx); |
| 28132 | 29516 | rc = 1; |
| 28133 | 29517 | goto meta_command_exit; |
| 28134 | 29518 | } |
| 28135 | 29519 | } |
| @@ -28180,11 +29564,11 @@ | ||
| 28180 | 29564 | } |
| 28181 | 29565 | zSql[j++] = ')'; |
| 28182 | 29566 | zSql[j] = 0; |
| 28183 | 29567 | assert( j<nByte ); |
| 28184 | 29568 | if( eVerbose>=2 ){ |
| 28185 | - oputf("Insert using: %s\n", zSql); | |
| 29569 | + sqlite3_fprintf(p->out, "Insert using: %s\n", zSql); | |
| 28186 | 29570 | } |
| 28187 | 29571 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 28188 | 29572 | sqlite3_free(zSql); |
| 28189 | 29573 | zSql = 0; |
| 28190 | 29574 | if( rc ){ |
| @@ -28219,11 +29603,11 @@ | ||
| 28219 | 29603 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 28220 | 29604 | z = ""; |
| 28221 | 29605 | } |
| 28222 | 29606 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 28223 | 29607 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 28224 | - eputf("%s:%d: expected %d columns but found %d" | |
| 29608 | + sqlite3_fprintf(stderr,"%s:%d: expected %d columns but found %d" | |
| 28225 | 29609 | " - filling the rest with NULL\n", |
| 28226 | 29610 | sCtx.zFile, startLine, nCol, i+1); |
| 28227 | 29611 | i += 2; |
| 28228 | 29612 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 28229 | 29613 | } |
| @@ -28231,18 +29615,19 @@ | ||
| 28231 | 29615 | if( sCtx.cTerm==sCtx.cColSep ){ |
| 28232 | 29616 | do{ |
| 28233 | 29617 | xRead(&sCtx); |
| 28234 | 29618 | i++; |
| 28235 | 29619 | }while( sCtx.cTerm==sCtx.cColSep ); |
| 28236 | - eputf("%s:%d: expected %d columns but found %d - extras ignored\n", | |
| 29620 | + sqlite3_fprintf(stderr, | |
| 29621 | + "%s:%d: expected %d columns but found %d - extras ignored\n", | |
| 28237 | 29622 | sCtx.zFile, startLine, nCol, i); |
| 28238 | 29623 | } |
| 28239 | 29624 | if( i>=nCol ){ |
| 28240 | 29625 | sqlite3_step(pStmt); |
| 28241 | 29626 | rc = sqlite3_reset(pStmt); |
| 28242 | 29627 | if( rc!=SQLITE_OK ){ |
| 28243 | - eputf("%s:%d: INSERT failed: %s\n", | |
| 29628 | + sqlite3_fprintf(stderr,"%s:%d: INSERT failed: %s\n", | |
| 28244 | 29629 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 28245 | 29630 | sCtx.nErr++; |
| 28246 | 29631 | }else{ |
| 28247 | 29632 | sCtx.nRow++; |
| 28248 | 29633 | } |
| @@ -28251,11 +29636,12 @@ | ||
| 28251 | 29636 | |
| 28252 | 29637 | import_cleanup(&sCtx); |
| 28253 | 29638 | sqlite3_finalize(pStmt); |
| 28254 | 29639 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 28255 | 29640 | if( eVerbose>0 ){ |
| 28256 | - oputf("Added %d rows with %d errors using %d lines of input\n", | |
| 29641 | + sqlite3_fprintf(p->out, | |
| 29642 | + "Added %d rows with %d errors using %d lines of input\n", | |
| 28257 | 29643 | sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 28258 | 29644 | } |
| 28259 | 29645 | }else |
| 28260 | 29646 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 28261 | 29647 | |
| @@ -28267,11 +29653,11 @@ | ||
| 28267 | 29653 | int tnum = 0; |
| 28268 | 29654 | int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ |
| 28269 | 29655 | int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ |
| 28270 | 29656 | int i; |
| 28271 | 29657 | if( !ShellHasFlag(p,SHFLG_TestingMode) ){ |
| 28272 | - eputf(".%s unavailable without --unsafe-testing\n", | |
| 29658 | + sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n", | |
| 28273 | 29659 | "imposter"); |
| 28274 | 29660 | rc = 1; |
| 28275 | 29661 | goto meta_command_exit; |
| 28276 | 29662 | } |
| 28277 | 29663 | if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ |
| @@ -28333,11 +29719,11 @@ | ||
| 28333 | 29719 | zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); |
| 28334 | 29720 | } |
| 28335 | 29721 | } |
| 28336 | 29722 | sqlite3_finalize(pStmt); |
| 28337 | 29723 | if( i==0 || tnum==0 ){ |
| 28338 | - eputf("no such index: \"%s\"\n", azArg[1]); | |
| 29724 | + sqlite3_fprintf(stderr,"no such index: \"%s\"\n", azArg[1]); | |
| 28339 | 29725 | rc = 1; |
| 28340 | 29726 | sqlite3_free(zCollist); |
| 28341 | 29727 | goto meta_command_exit; |
| 28342 | 29728 | } |
| 28343 | 29729 | if( lenPK==0 ) lenPK = 100000; |
| @@ -28348,18 +29734,20 @@ | ||
| 28348 | 29734 | rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); |
| 28349 | 29735 | if( rc==SQLITE_OK ){ |
| 28350 | 29736 | rc = sqlite3_exec(p->db, zSql, 0, 0, 0); |
| 28351 | 29737 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); |
| 28352 | 29738 | if( rc ){ |
| 28353 | - eputf("Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); | |
| 29739 | + sqlite3_fprintf(stderr, | |
| 29740 | + "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); | |
| 28354 | 29741 | }else{ |
| 28355 | - sputf(stdout, "%s;\n", zSql); | |
| 28356 | - sputf(stdout, "WARNING: writing to an imposter table will corrupt" | |
| 29742 | + sqlite3_fprintf(stdout, "%s;\n", zSql); | |
| 29743 | + sqlite3_fprintf(stdout, | |
| 29744 | + "WARNING: writing to an imposter table will corrupt" | |
| 28357 | 29745 | " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index"); |
| 28358 | 29746 | } |
| 28359 | 29747 | }else{ |
| 28360 | - eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); | |
| 29748 | + sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); | |
| 28361 | 29749 | rc = 1; |
| 28362 | 29750 | } |
| 28363 | 29751 | sqlite3_free(zSql); |
| 28364 | 29752 | }else |
| 28365 | 29753 | #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ |
| @@ -28369,11 +29757,11 @@ | ||
| 28369 | 29757 | if( nArg==2 ){ |
| 28370 | 29758 | iArg = integerValue(azArg[1]); |
| 28371 | 29759 | if( iArg==0 ) iArg = -1; |
| 28372 | 29760 | } |
| 28373 | 29761 | if( (nArg!=1 && nArg!=2) || iArg<0 ){ |
| 28374 | - eputf("%s","Usage: .intck STEPS_PER_UNLOCK\n"); | |
| 29762 | + sqlite3_fprintf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); | |
| 28375 | 29763 | rc = 1; |
| 28376 | 29764 | goto meta_command_exit; |
| 28377 | 29765 | } |
| 28378 | 29766 | open_db(p, 0); |
| 28379 | 29767 | rc = intckDatabaseCmd(p, iArg); |
| @@ -28388,13 +29776,13 @@ | ||
| 28388 | 29776 | sqlite3IoTrace = 0; |
| 28389 | 29777 | }else if( cli_strcmp(azArg[1], "-")==0 ){ |
| 28390 | 29778 | sqlite3IoTrace = iotracePrintf; |
| 28391 | 29779 | iotrace = stdout; |
| 28392 | 29780 | }else{ |
| 28393 | - iotrace = fopen(azArg[1], "w"); | |
| 29781 | + iotrace = sqlite3_fopen(azArg[1], "w"); | |
| 28394 | 29782 | if( iotrace==0 ){ |
| 28395 | - eputf("Error: cannot open \"%s\"\n", azArg[1]); | |
| 29783 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); | |
| 28396 | 29784 | sqlite3IoTrace = 0; |
| 28397 | 29785 | rc = 1; |
| 28398 | 29786 | }else{ |
| 28399 | 29787 | sqlite3IoTrace = iotracePrintf; |
| 28400 | 29788 | } |
| @@ -28422,11 +29810,11 @@ | ||
| 28422 | 29810 | }; |
| 28423 | 29811 | int i, n2; |
| 28424 | 29812 | open_db(p, 0); |
| 28425 | 29813 | if( nArg==1 ){ |
| 28426 | 29814 | for(i=0; i<ArraySize(aLimit); i++){ |
| 28427 | - sputf(stdout, "%20s %d\n", aLimit[i].zLimitName, | |
| 29815 | + sqlite3_fprintf(stdout, "%20s %d\n", aLimit[i].zLimitName, | |
| 28428 | 29816 | sqlite3_limit(p->db, aLimit[i].limitCode, -1)); |
| 28429 | 29817 | } |
| 28430 | 29818 | }else if( nArg>3 ){ |
| 28431 | 29819 | eputz("Usage: .limit NAME ?NEW-VALUE?\n"); |
| 28432 | 29820 | rc = 1; |
| @@ -28437,28 +29825,28 @@ | ||
| 28437 | 29825 | for(i=0; i<ArraySize(aLimit); i++){ |
| 28438 | 29826 | if( sqlite3_strnicmp(aLimit[i].zLimitName, azArg[1], n2)==0 ){ |
| 28439 | 29827 | if( iLimit<0 ){ |
| 28440 | 29828 | iLimit = i; |
| 28441 | 29829 | }else{ |
| 28442 | - eputf("ambiguous limit: \"%s\"\n", azArg[1]); | |
| 29830 | + sqlite3_fprintf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]); | |
| 28443 | 29831 | rc = 1; |
| 28444 | 29832 | goto meta_command_exit; |
| 28445 | 29833 | } |
| 28446 | 29834 | } |
| 28447 | 29835 | } |
| 28448 | 29836 | if( iLimit<0 ){ |
| 28449 | - eputf("unknown limit: \"%s\"\n" | |
| 29837 | + sqlite3_fprintf(stderr,"unknown limit: \"%s\"\n" | |
| 28450 | 29838 | "enter \".limits\" with no arguments for a list.\n", |
| 28451 | 29839 | azArg[1]); |
| 28452 | 29840 | rc = 1; |
| 28453 | 29841 | goto meta_command_exit; |
| 28454 | 29842 | } |
| 28455 | 29843 | if( nArg==3 ){ |
| 28456 | 29844 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 28457 | 29845 | (int)integerValue(azArg[2])); |
| 28458 | 29846 | } |
| 28459 | - sputf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, | |
| 29847 | + sqlite3_fprintf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, | |
| 28460 | 29848 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 28461 | 29849 | } |
| 28462 | 29850 | }else |
| 28463 | 29851 | |
| 28464 | 29852 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| @@ -28503,11 +29891,11 @@ | ||
| 28503 | 29891 | " than \"on\" or \"off\"\n"); |
| 28504 | 29892 | zFile = "off"; |
| 28505 | 29893 | } |
| 28506 | 29894 | output_file_close(p->pLog); |
| 28507 | 29895 | if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout"; |
| 28508 | - p->pLog = output_file_open(zFile, 0); | |
| 29896 | + p->pLog = output_file_open(zFile); | |
| 28509 | 29897 | } |
| 28510 | 29898 | }else |
| 28511 | 29899 | |
| 28512 | 29900 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 28513 | 29901 | const char *zMode = 0; |
| @@ -28537,35 +29925,37 @@ | ||
| 28537 | 29925 | cmOpts = cmo; |
| 28538 | 29926 | } |
| 28539 | 29927 | }else if( zTabname==0 ){ |
| 28540 | 29928 | zTabname = z; |
| 28541 | 29929 | }else if( z[0]=='-' ){ |
| 28542 | - eputf("unknown option: %s\n", z); | |
| 29930 | + sqlite3_fprintf(stderr,"unknown option: %s\n", z); | |
| 28543 | 29931 | eputz("options:\n" |
| 28544 | 29932 | " --noquote\n" |
| 28545 | 29933 | " --quote\n" |
| 28546 | 29934 | " --wordwrap on/off\n" |
| 28547 | 29935 | " --wrap N\n" |
| 28548 | 29936 | " --ww\n"); |
| 28549 | 29937 | rc = 1; |
| 28550 | 29938 | goto meta_command_exit; |
| 28551 | 29939 | }else{ |
| 28552 | - eputf("extra argument: \"%s\"\n", z); | |
| 29940 | + sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); | |
| 28553 | 29941 | rc = 1; |
| 28554 | 29942 | goto meta_command_exit; |
| 28555 | 29943 | } |
| 28556 | 29944 | } |
| 28557 | 29945 | if( zMode==0 ){ |
| 28558 | 29946 | if( p->mode==MODE_Column |
| 28559 | 29947 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 28560 | 29948 | ){ |
| 28561 | - oputf("current output mode: %s --wrap %d --wordwrap %s --%squote\n", | |
| 29949 | + sqlite3_fprintf(p->out, | |
| 29950 | + "current output mode: %s --wrap %d --wordwrap %s --%squote\n", | |
| 28562 | 29951 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 28563 | 29952 | p->cmOpts.bWordWrap ? "on" : "off", |
| 28564 | 29953 | p->cmOpts.bQuote ? "" : "no"); |
| 28565 | 29954 | }else{ |
| 28566 | - oputf("current output mode: %s\n", modeDescr[p->mode]); | |
| 29955 | + sqlite3_fprintf(p->out, | |
| 29956 | + "current output mode: %s\n", modeDescr[p->mode]); | |
| 28567 | 29957 | } |
| 28568 | 29958 | zMode = modeDescr[p->mode]; |
| 28569 | 29959 | } |
| 28570 | 29960 | n2 = strlen30(zMode); |
| 28571 | 29961 | if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| @@ -28634,11 +30024,11 @@ | ||
| 28634 | 30024 | if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ |
| 28635 | 30025 | if( nArg!=2 ){ |
| 28636 | 30026 | eputz("Usage: .nonce NONCE\n"); |
| 28637 | 30027 | rc = 1; |
| 28638 | 30028 | }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ |
| 28639 | - eputf("line %d: incorrect nonce: \"%s\"\n", | |
| 30029 | + sqlite3_fprintf(stderr,"line %d: incorrect nonce: \"%s\"\n", | |
| 28640 | 30030 | p->lineno, azArg[1]); |
| 28641 | 30031 | exit(1); |
| 28642 | 30032 | }else{ |
| 28643 | 30033 | p->bSafeMode = 0; |
| 28644 | 30034 | return 0; /* Return immediately to bypass the safe mode reset |
| @@ -28689,15 +30079,15 @@ | ||
| 28689 | 30079 | p->szMax = integerValue(azArg[++iName]); |
| 28690 | 30080 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 28691 | 30081 | }else |
| 28692 | 30082 | #endif /* !SQLITE_SHELL_FIDDLE */ |
| 28693 | 30083 | if( z[0]=='-' ){ |
| 28694 | - eputf("unknown option: %s\n", z); | |
| 30084 | + sqlite3_fprintf(stderr,"unknown option: %s\n", z); | |
| 28695 | 30085 | rc = 1; |
| 28696 | 30086 | goto meta_command_exit; |
| 28697 | 30087 | }else if( zFN ){ |
| 28698 | - eputf("extra argument: \"%s\"\n", z); | |
| 30088 | + sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); | |
| 28699 | 30089 | rc = 1; |
| 28700 | 30090 | goto meta_command_exit; |
| 28701 | 30091 | }else{ |
| 28702 | 30092 | zFN = z; |
| 28703 | 30093 | } |
| @@ -28735,11 +30125,11 @@ | ||
| 28735 | 30125 | zNewFilename = 0; |
| 28736 | 30126 | } |
| 28737 | 30127 | p->pAuxDb->zDbFilename = zNewFilename; |
| 28738 | 30128 | open_db(p, OPEN_DB_KEEPALIVE); |
| 28739 | 30129 | if( p->db==0 ){ |
| 28740 | - eputf("Error: cannot open '%s'\n", zNewFilename); | |
| 30130 | + sqlite3_fprintf(stderr,"Error: cannot open '%s'\n", zNewFilename); | |
| 28741 | 30131 | sqlite3_free(zNewFilename); |
| 28742 | 30132 | }else{ |
| 28743 | 30133 | p->pAuxDb->zFreeOnClose = zNewFilename; |
| 28744 | 30134 | } |
| 28745 | 30135 | } |
| @@ -28753,22 +30143,26 @@ | ||
| 28753 | 30143 | #ifndef SQLITE_SHELL_FIDDLE |
| 28754 | 30144 | if( (c=='o' |
| 28755 | 30145 | && (cli_strncmp(azArg[0], "output", n)==0 |
| 28756 | 30146 | || cli_strncmp(azArg[0], "once", n)==0)) |
| 28757 | 30147 | || (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0) |
| 30148 | + || (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0) | |
| 28758 | 30149 | ){ |
| 28759 | 30150 | char *zFile = 0; |
| 28760 | - int bTxtMode = 0; | |
| 28761 | 30151 | int i; |
| 28762 | 30152 | int eMode = 0; |
| 28763 | - int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ | |
| 28764 | - static const char *zBomUtf8 = "\xef\xbb\xbf"; | |
| 30153 | + int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ | |
| 30154 | + int bPlain = 0; /* --plain option */ | |
| 30155 | + static const char *zBomUtf8 = "\357\273\277"; | |
| 28765 | 30156 | const char *zBom = 0; |
| 28766 | 30157 | |
| 28767 | 30158 | failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); |
| 28768 | 30159 | if( c=='e' ){ |
| 28769 | 30160 | eMode = 'x'; |
| 30161 | + bOnce = 2; | |
| 30162 | + }else if( c=='w' ){ | |
| 30163 | + eMode = 'w'; | |
| 28770 | 30164 | bOnce = 2; |
| 28771 | 30165 | }else if( cli_strncmp(azArg[0],"once",n)==0 ){ |
| 28772 | 30166 | bOnce = 1; |
| 28773 | 30167 | } |
| 28774 | 30168 | for(i=1; i<nArg; i++){ |
| @@ -28775,28 +30169,34 @@ | ||
| 28775 | 30169 | char *z = azArg[i]; |
| 28776 | 30170 | if( z[0]=='-' ){ |
| 28777 | 30171 | if( z[1]=='-' ) z++; |
| 28778 | 30172 | if( cli_strcmp(z,"-bom")==0 ){ |
| 28779 | 30173 | zBom = zBomUtf8; |
| 28780 | - }else if( c!='e' && cli_strcmp(z,"-x")==0 ){ | |
| 30174 | + }else if( cli_strcmp(z,"-plain")==0 ){ | |
| 30175 | + bPlain = 1; | |
| 30176 | + }else if( c=='o' && cli_strcmp(z,"-x")==0 ){ | |
| 28781 | 30177 | eMode = 'x'; /* spreadsheet */ |
| 28782 | - }else if( c!='e' && cli_strcmp(z,"-e")==0 ){ | |
| 30178 | + }else if( c=='o' && cli_strcmp(z,"-e")==0 ){ | |
| 28783 | 30179 | eMode = 'e'; /* text editor */ |
| 30180 | + }else if( c=='o' && cli_strcmp(z,"-w")==0 ){ | |
| 30181 | + eMode = 'w'; /* Web browser */ | |
| 28784 | 30182 | }else{ |
| 28785 | - oputf("ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); | |
| 30183 | + sqlite3_fprintf(p->out, | |
| 30184 | + "ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); | |
| 28786 | 30185 | showHelp(p->out, azArg[0]); |
| 28787 | 30186 | rc = 1; |
| 28788 | 30187 | goto meta_command_exit; |
| 28789 | 30188 | } |
| 28790 | - }else if( zFile==0 && eMode!='e' && eMode!='x' ){ | |
| 30189 | + }else if( zFile==0 && eMode==0 ){ | |
| 28791 | 30190 | zFile = sqlite3_mprintf("%s", z); |
| 28792 | 30191 | if( zFile && zFile[0]=='|' ){ |
| 28793 | 30192 | while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); |
| 28794 | 30193 | break; |
| 28795 | 30194 | } |
| 28796 | 30195 | }else{ |
| 28797 | - oputf("ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); | |
| 30196 | + sqlite3_fprintf(p->out, | |
| 30197 | + "ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); | |
| 28798 | 30198 | showHelp(p->out, azArg[0]); |
| 28799 | 30199 | rc = 1; |
| 28800 | 30200 | sqlite3_free(zFile); |
| 28801 | 30201 | goto meta_command_exit; |
| 28802 | 30202 | } |
| @@ -28809,24 +30209,31 @@ | ||
| 28809 | 30209 | }else{ |
| 28810 | 30210 | p->outCount = 0; |
| 28811 | 30211 | } |
| 28812 | 30212 | output_reset(p); |
| 28813 | 30213 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 28814 | - if( eMode=='e' || eMode=='x' ){ | |
| 30214 | + if( eMode=='e' || eMode=='x' || eMode=='w' ){ | |
| 28815 | 30215 | p->doXdgOpen = 1; |
| 28816 | 30216 | outputModePush(p); |
| 28817 | 30217 | if( eMode=='x' ){ |
| 28818 | 30218 | /* spreadsheet mode. Output as CSV. */ |
| 28819 | 30219 | newTempFile(p, "csv"); |
| 28820 | 30220 | ShellClearFlag(p, SHFLG_Echo); |
| 28821 | 30221 | p->mode = MODE_Csv; |
| 28822 | 30222 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 28823 | 30223 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 30224 | +#ifdef _WIN32 | |
| 30225 | + zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does | |
| 30226 | + ** not work without it. */ | |
| 30227 | +#endif | |
| 30228 | + }else if( eMode=='w' ){ | |
| 30229 | + /* web-browser mode. */ | |
| 30230 | + newTempFile(p, "html"); | |
| 30231 | + if( !bPlain ) p->mode = MODE_Www; | |
| 28824 | 30232 | }else{ |
| 28825 | 30233 | /* text editor mode */ |
| 28826 | 30234 | newTempFile(p, "txt"); |
| 28827 | - bTxtMode = 1; | |
| 28828 | 30235 | } |
| 28829 | 30236 | sqlite3_free(zFile); |
| 28830 | 30237 | zFile = sqlite3_mprintf("%s", p->zTempFile); |
| 28831 | 30238 | } |
| 28832 | 30239 | #endif /* SQLITE_NOHAVE_SYSTEM */ |
| @@ -28835,30 +30242,36 @@ | ||
| 28835 | 30242 | #ifdef SQLITE_OMIT_POPEN |
| 28836 | 30243 | eputz("Error: pipes are not supported in this OS\n"); |
| 28837 | 30244 | rc = 1; |
| 28838 | 30245 | output_redir(p, stdout); |
| 28839 | 30246 | #else |
| 28840 | - FILE *pfPipe = popen(zFile + 1, "w"); | |
| 30247 | + FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); | |
| 28841 | 30248 | if( pfPipe==0 ){ |
| 28842 | - eputf("Error: cannot open pipe \"%s\"\n", zFile + 1); | |
| 30249 | + sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); | |
| 28843 | 30250 | rc = 1; |
| 28844 | 30251 | }else{ |
| 28845 | 30252 | output_redir(p, pfPipe); |
| 28846 | - if( zBom ) oputz(zBom); | |
| 30253 | + if( zBom ) sqlite3_fputs(zBom, pfPipe); | |
| 28847 | 30254 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 28848 | 30255 | } |
| 28849 | 30256 | #endif |
| 28850 | 30257 | }else{ |
| 28851 | - FILE *pfFile = output_file_open(zFile, bTxtMode); | |
| 30258 | + FILE *pfFile = output_file_open(zFile); | |
| 28852 | 30259 | if( pfFile==0 ){ |
| 28853 | 30260 | if( cli_strcmp(zFile,"off")!=0 ){ |
| 28854 | - eputf("Error: cannot write to \"%s\"\n", zFile); | |
| 30261 | + sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); | |
| 28855 | 30262 | } |
| 28856 | 30263 | rc = 1; |
| 28857 | 30264 | } else { |
| 28858 | 30265 | output_redir(p, pfFile); |
| 28859 | - if( zBom ) oputz(zBom); | |
| 30266 | + if( zBom ) sqlite3_fputs(zBom, pfFile); | |
| 30267 | + if( bPlain && eMode=='w' ){ | |
| 30268 | + sqlite3_fputs( | |
| 30269 | + "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n", | |
| 30270 | + pfFile | |
| 30271 | + ); | |
| 30272 | + } | |
| 28860 | 30273 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 28861 | 30274 | } |
| 28862 | 30275 | } |
| 28863 | 30276 | sqlite3_free(zFile); |
| 28864 | 30277 | }else |
| @@ -28895,11 +30308,12 @@ | ||
| 28895 | 30308 | if( len ){ |
| 28896 | 30309 | rx = sqlite3_prepare_v2(p->db, |
| 28897 | 30310 | "SELECT key, quote(value) " |
| 28898 | 30311 | "FROM temp.sqlite_parameters;", -1, &pStmt, 0); |
| 28899 | 30312 | while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 28900 | - oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), | |
| 30313 | + sqlite3_fprintf(p->out, | |
| 30314 | + "%-*s %s\n", len, sqlite3_column_text(pStmt,0), | |
| 28901 | 30315 | sqlite3_column_text(pStmt,1)); |
| 28902 | 30316 | } |
| 28903 | 30317 | sqlite3_finalize(pStmt); |
| 28904 | 30318 | } |
| 28905 | 30319 | }else |
| @@ -28940,11 +30354,11 @@ | ||
| 28940 | 30354 | "VALUES(%Q,%Q);", zKey, zValue); |
| 28941 | 30355 | shell_check_oom(zSql); |
| 28942 | 30356 | rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 28943 | 30357 | sqlite3_free(zSql); |
| 28944 | 30358 | if( rx!=SQLITE_OK ){ |
| 28945 | - oputf("Error: %s\n", sqlite3_errmsg(p->db)); | |
| 30359 | + sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); | |
| 28946 | 30360 | sqlite3_finalize(pStmt); |
| 28947 | 30361 | pStmt = 0; |
| 28948 | 30362 | rc = 1; |
| 28949 | 30363 | } |
| 28950 | 30364 | } |
| @@ -28969,14 +30383,14 @@ | ||
| 28969 | 30383 | }else |
| 28970 | 30384 | |
| 28971 | 30385 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ |
| 28972 | 30386 | int i; |
| 28973 | 30387 | for(i=1; i<nArg; i++){ |
| 28974 | - if( i>1 ) oputz(" "); | |
| 28975 | - oputz(azArg[i]); | |
| 30388 | + if( i>1 ) sqlite3_fputs(" ", p->out); | |
| 30389 | + sqlite3_fputs(azArg[i], p->out); | |
| 28976 | 30390 | } |
| 28977 | - oputz("\n"); | |
| 30391 | + sqlite3_fputs("\n", p->out); | |
| 28978 | 30392 | }else |
| 28979 | 30393 | |
| 28980 | 30394 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 28981 | 30395 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "progress", n)==0 ){ |
| 28982 | 30396 | int i; |
| @@ -29009,11 +30423,11 @@ | ||
| 29009 | 30423 | }else{ |
| 29010 | 30424 | p->mxProgress = (int)integerValue(azArg[++i]); |
| 29011 | 30425 | } |
| 29012 | 30426 | continue; |
| 29013 | 30427 | } |
| 29014 | - eputf("Error: unknown option: \"%s\"\n", azArg[i]); | |
| 30428 | + sqlite3_fprintf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]); | |
| 29015 | 30429 | rc = 1; |
| 29016 | 30430 | goto meta_command_exit; |
| 29017 | 30431 | }else{ |
| 29018 | 30432 | nn = (int)integerValue(z); |
| 29019 | 30433 | } |
| @@ -29050,23 +30464,22 @@ | ||
| 29050 | 30464 | } |
| 29051 | 30465 | if( azArg[1][0]=='|' ){ |
| 29052 | 30466 | #ifdef SQLITE_OMIT_POPEN |
| 29053 | 30467 | eputz("Error: pipes are not supported in this OS\n"); |
| 29054 | 30468 | rc = 1; |
| 29055 | - p->out = stdout; | |
| 29056 | 30469 | #else |
| 29057 | - p->in = popen(azArg[1]+1, "r"); | |
| 30470 | + p->in = sqlite3_popen(azArg[1]+1, "r"); | |
| 29058 | 30471 | if( p->in==0 ){ |
| 29059 | - eputf("Error: cannot open \"%s\"\n", azArg[1]); | |
| 30472 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); | |
| 29060 | 30473 | rc = 1; |
| 29061 | 30474 | }else{ |
| 29062 | 30475 | rc = process_input(p); |
| 29063 | 30476 | pclose(p->in); |
| 29064 | 30477 | } |
| 29065 | 30478 | #endif |
| 29066 | 30479 | }else if( (p->in = openChrSource(azArg[1]))==0 ){ |
| 29067 | - eputf("Error: cannot open \"%s\"\n", azArg[1]); | |
| 30480 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); | |
| 29068 | 30481 | rc = 1; |
| 29069 | 30482 | }else{ |
| 29070 | 30483 | rc = process_input(p); |
| 29071 | 30484 | fclose(p->in); |
| 29072 | 30485 | } |
| @@ -29095,11 +30508,11 @@ | ||
| 29095 | 30508 | rc = 1; |
| 29096 | 30509 | goto meta_command_exit; |
| 29097 | 30510 | } |
| 29098 | 30511 | rc = sqlite3_open(zSrcFile, &pSrc); |
| 29099 | 30512 | if( rc!=SQLITE_OK ){ |
| 29100 | - eputf("Error: cannot open \"%s\"\n", zSrcFile); | |
| 30513 | + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zSrcFile); | |
| 29101 | 30514 | close_db(pSrc); |
| 29102 | 30515 | return 1; |
| 29103 | 30516 | } |
| 29104 | 30517 | open_db(p, 0); |
| 29105 | 30518 | pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); |
| @@ -29127,11 +30540,14 @@ | ||
| 29127 | 30540 | } |
| 29128 | 30541 | close_db(pSrc); |
| 29129 | 30542 | }else |
| 29130 | 30543 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 29131 | 30544 | |
| 29132 | - if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){ | |
| 30545 | + if( c=='s' && | |
| 30546 | + (cli_strncmp(azArg[0], "scanstats", n)==0 || | |
| 30547 | + cli_strncmp(azArg[0], "scanstatus", n)==0) | |
| 30548 | + ){ | |
| 29133 | 30549 | if( nArg==2 ){ |
| 29134 | 30550 | if( cli_strcmp(azArg[1], "vm")==0 ){ |
| 29135 | 30551 | p->scanstatsOn = 3; |
| 29136 | 30552 | }else |
| 29137 | 30553 | if( cli_strcmp(azArg[1], "est")==0 ){ |
| @@ -29178,11 +30594,11 @@ | ||
| 29178 | 30594 | }else if( optionMatch(azArg[ii],"debug") ){ |
| 29179 | 30595 | bDebug = 1; |
| 29180 | 30596 | }else if( optionMatch(azArg[ii],"nosys") ){ |
| 29181 | 30597 | bNoSystemTabs = 1; |
| 29182 | 30598 | }else if( azArg[ii][0]=='-' ){ |
| 29183 | - eputf("Unknown option: \"%s\"\n", azArg[ii]); | |
| 30599 | + sqlite3_fprintf(stderr,"Unknown option: \"%s\"\n", azArg[ii]); | |
| 29184 | 30600 | rc = 1; |
| 29185 | 30601 | goto meta_command_exit; |
| 29186 | 30602 | }else if( zName==0 ){ |
| 29187 | 30603 | zName = azArg[ii]; |
| 29188 | 30604 | }else{ |
| @@ -29279,11 +30695,11 @@ | ||
| 29279 | 30695 | appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0); |
| 29280 | 30696 | } |
| 29281 | 30697 | appendText(&sSelect, "sql IS NOT NULL" |
| 29282 | 30698 | " ORDER BY snum, rowid", 0); |
| 29283 | 30699 | if( bDebug ){ |
| 29284 | - oputf("SQL: %s;\n", sSelect.z); | |
| 30700 | + sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z); | |
| 29285 | 30701 | }else{ |
| 29286 | 30702 | rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); |
| 29287 | 30703 | } |
| 29288 | 30704 | freeText(&sSelect); |
| 29289 | 30705 | } |
| @@ -29340,11 +30756,12 @@ | ||
| 29340 | 30756 | session_not_open: |
| 29341 | 30757 | eputz("ERROR: No sessions are open\n"); |
| 29342 | 30758 | }else{ |
| 29343 | 30759 | rc = sqlite3session_attach(pSession->p, azCmd[1]); |
| 29344 | 30760 | if( rc ){ |
| 29345 | - eputf("ERROR: sqlite3session_attach() returns %d\n",rc); | |
| 30761 | + sqlite3_fprintf(stderr, | |
| 30762 | + "ERROR: sqlite3session_attach() returns %d\n",rc); | |
| 29346 | 30763 | rc = 0; |
| 29347 | 30764 | } |
| 29348 | 30765 | } |
| 29349 | 30766 | }else |
| 29350 | 30767 | |
| @@ -29357,13 +30774,13 @@ | ||
| 29357 | 30774 | ){ |
| 29358 | 30775 | FILE *out = 0; |
| 29359 | 30776 | failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]); |
| 29360 | 30777 | if( nCmd!=2 ) goto session_syntax_error; |
| 29361 | 30778 | if( pSession->p==0 ) goto session_not_open; |
| 29362 | - out = fopen(azCmd[1], "wb"); | |
| 30779 | + out = sqlite3_fopen(azCmd[1], "wb"); | |
| 29363 | 30780 | if( out==0 ){ |
| 29364 | - eputf("ERROR: cannot open \"%s\" for writing\n", | |
| 30781 | + sqlite3_fprintf(stderr,"ERROR: cannot open \"%s\" for writing\n", | |
| 29365 | 30782 | azCmd[1]); |
| 29366 | 30783 | }else{ |
| 29367 | 30784 | int szChng; |
| 29368 | 30785 | void *pChng; |
| 29369 | 30786 | if( azCmd[0][0]=='c' ){ |
| @@ -29370,16 +30787,17 @@ | ||
| 29370 | 30787 | rc = sqlite3session_changeset(pSession->p, &szChng, &pChng); |
| 29371 | 30788 | }else{ |
| 29372 | 30789 | rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); |
| 29373 | 30790 | } |
| 29374 | 30791 | if( rc ){ |
| 29375 | - sputf(stdout, "Error: error code %d\n", rc); | |
| 30792 | + sqlite3_fprintf(stdout, "Error: error code %d\n", rc); | |
| 29376 | 30793 | rc = 0; |
| 29377 | 30794 | } |
| 29378 | 30795 | if( pChng |
| 29379 | 30796 | && fwrite(pChng, szChng, 1, out)!=1 ){ |
| 29380 | - eputf("ERROR: Failed to write entire %d-byte output\n", szChng); | |
| 30797 | + sqlite3_fprintf(stderr, | |
| 30798 | + "ERROR: Failed to write entire %d-byte output\n", szChng); | |
| 29381 | 30799 | } |
| 29382 | 30800 | sqlite3_free(pChng); |
| 29383 | 30801 | fclose(out); |
| 29384 | 30802 | } |
| 29385 | 30803 | }else |
| @@ -29402,11 +30820,12 @@ | ||
| 29402 | 30820 | int ii; |
| 29403 | 30821 | if( nCmd>2 ) goto session_syntax_error; |
| 29404 | 30822 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 29405 | 30823 | if( pAuxDb->nSession ){ |
| 29406 | 30824 | ii = sqlite3session_enable(pSession->p, ii); |
| 29407 | - oputf("session %s enable flag = %d\n", pSession->zName, ii); | |
| 30825 | + sqlite3_fprintf(p->out, | |
| 30826 | + "session %s enable flag = %d\n", pSession->zName, ii); | |
| 29408 | 30827 | } |
| 29409 | 30828 | }else |
| 29410 | 30829 | |
| 29411 | 30830 | /* .session filter GLOB .... |
| 29412 | 30831 | ** Set a list of GLOB patterns of table names to be excluded. |
| @@ -29437,11 +30856,12 @@ | ||
| 29437 | 30856 | int ii; |
| 29438 | 30857 | if( nCmd>2 ) goto session_syntax_error; |
| 29439 | 30858 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 29440 | 30859 | if( pAuxDb->nSession ){ |
| 29441 | 30860 | ii = sqlite3session_indirect(pSession->p, ii); |
| 29442 | - oputf("session %s indirect flag = %d\n", pSession->zName, ii); | |
| 30861 | + sqlite3_fprintf(p->out, | |
| 30862 | + "session %s indirect flag = %d\n", pSession->zName, ii); | |
| 29443 | 30863 | } |
| 29444 | 30864 | }else |
| 29445 | 30865 | |
| 29446 | 30866 | /* .session isempty |
| 29447 | 30867 | ** Determine if the session is empty |
| @@ -29449,20 +30869,21 @@ | ||
| 29449 | 30869 | if( cli_strcmp(azCmd[0], "isempty")==0 ){ |
| 29450 | 30870 | int ii; |
| 29451 | 30871 | if( nCmd!=1 ) goto session_syntax_error; |
| 29452 | 30872 | if( pAuxDb->nSession ){ |
| 29453 | 30873 | ii = sqlite3session_isempty(pSession->p); |
| 29454 | - oputf("session %s isempty flag = %d\n", pSession->zName, ii); | |
| 30874 | + sqlite3_fprintf(p->out, | |
| 30875 | + "session %s isempty flag = %d\n", pSession->zName, ii); | |
| 29455 | 30876 | } |
| 29456 | 30877 | }else |
| 29457 | 30878 | |
| 29458 | 30879 | /* .session list |
| 29459 | 30880 | ** List all currently open sessions |
| 29460 | 30881 | */ |
| 29461 | 30882 | if( cli_strcmp(azCmd[0],"list")==0 ){ |
| 29462 | 30883 | for(i=0; i<pAuxDb->nSession; i++){ |
| 29463 | - oputf("%d %s\n", i, pAuxDb->aSession[i].zName); | |
| 30884 | + sqlite3_fprintf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); | |
| 29464 | 30885 | } |
| 29465 | 30886 | }else |
| 29466 | 30887 | |
| 29467 | 30888 | /* .session open DB NAME |
| 29468 | 30889 | ** Open a new session called NAME on the attached database DB. |
| @@ -29473,22 +30894,23 @@ | ||
| 29473 | 30894 | if( nCmd!=3 ) goto session_syntax_error; |
| 29474 | 30895 | zName = azCmd[2]; |
| 29475 | 30896 | if( zName[0]==0 ) goto session_syntax_error; |
| 29476 | 30897 | for(i=0; i<pAuxDb->nSession; i++){ |
| 29477 | 30898 | if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){ |
| 29478 | - eputf("Session \"%s\" already exists\n", zName); | |
| 30899 | + sqlite3_fprintf(stderr,"Session \"%s\" already exists\n", zName); | |
| 29479 | 30900 | goto meta_command_exit; |
| 29480 | 30901 | } |
| 29481 | 30902 | } |
| 29482 | 30903 | if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){ |
| 29483 | - eputf("Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); | |
| 30904 | + sqlite3_fprintf(stderr, | |
| 30905 | + "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); | |
| 29484 | 30906 | goto meta_command_exit; |
| 29485 | 30907 | } |
| 29486 | 30908 | pSession = &pAuxDb->aSession[pAuxDb->nSession]; |
| 29487 | 30909 | rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); |
| 29488 | 30910 | if( rc ){ |
| 29489 | - eputf("Cannot open session: error code=%d\n", rc); | |
| 30911 | + sqlite3_fprintf(stderr,"Cannot open session: error code=%d\n", rc); | |
| 29490 | 30912 | rc = 0; |
| 29491 | 30913 | goto meta_command_exit; |
| 29492 | 30914 | } |
| 29493 | 30915 | pSession->nFilter = 0; |
| 29494 | 30916 | sqlite3session_table_filter(pSession->p, session_filter, pSession); |
| @@ -29508,20 +30930,20 @@ | ||
| 29508 | 30930 | if( c=='s' && n>=10 && cli_strncmp(azArg[0], "selftest-", 9)==0 ){ |
| 29509 | 30931 | if( cli_strncmp(azArg[0]+9, "boolean", n-9)==0 ){ |
| 29510 | 30932 | int i, v; |
| 29511 | 30933 | for(i=1; i<nArg; i++){ |
| 29512 | 30934 | v = booleanValue(azArg[i]); |
| 29513 | - oputf("%s: %d 0x%x\n", azArg[i], v, v); | |
| 30935 | + sqlite3_fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); | |
| 29514 | 30936 | } |
| 29515 | 30937 | } |
| 29516 | 30938 | if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ |
| 29517 | 30939 | int i; sqlite3_int64 v; |
| 29518 | 30940 | for(i=1; i<nArg; i++){ |
| 29519 | 30941 | char zBuf[200]; |
| 29520 | 30942 | v = integerValue(azArg[i]); |
| 29521 | 30943 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); |
| 29522 | - oputz(zBuf); | |
| 30944 | + sqlite3_fputs(zBuf, p->out); | |
| 29523 | 30945 | } |
| 29524 | 30946 | } |
| 29525 | 30947 | }else |
| 29526 | 30948 | #endif |
| 29527 | 30949 | |
| @@ -29544,12 +30966,13 @@ | ||
| 29544 | 30966 | }else |
| 29545 | 30967 | if( cli_strcmp(z,"-v")==0 ){ |
| 29546 | 30968 | bVerbose++; |
| 29547 | 30969 | }else |
| 29548 | 30970 | { |
| 29549 | - eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); | |
| 29550 | - eputz("Should be one of: --init -v\n"); | |
| 30971 | + sqlite3_fprintf(stderr, | |
| 30972 | + "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); | |
| 30973 | + sqlite3_fputs("Should be one of: --init -v\n", stderr); | |
| 29551 | 30974 | rc = 1; |
| 29552 | 30975 | goto meta_command_exit; |
| 29553 | 30976 | } |
| 29554 | 30977 | } |
| 29555 | 30978 | if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0) |
| @@ -29590,46 +31013,47 @@ | ||
| 29590 | 31013 | if( zOp==0 ) continue; |
| 29591 | 31014 | if( zSql==0 ) continue; |
| 29592 | 31015 | if( zAns==0 ) continue; |
| 29593 | 31016 | k = 0; |
| 29594 | 31017 | if( bVerbose>0 ){ |
| 29595 | - sputf(stdout, "%d: %s %s\n", tno, zOp, zSql); | |
| 31018 | + sqlite3_fprintf(stdout, "%d: %s %s\n", tno, zOp, zSql); | |
| 29596 | 31019 | } |
| 29597 | 31020 | if( cli_strcmp(zOp,"memo")==0 ){ |
| 29598 | - oputf("%s\n", zSql); | |
| 31021 | + sqlite3_fprintf(p->out, "%s\n", zSql); | |
| 29599 | 31022 | }else |
| 29600 | 31023 | if( cli_strcmp(zOp,"run")==0 ){ |
| 29601 | 31024 | char *zErrMsg = 0; |
| 29602 | 31025 | str.n = 0; |
| 29603 | 31026 | str.z[0] = 0; |
| 29604 | 31027 | rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); |
| 29605 | 31028 | nTest++; |
| 29606 | 31029 | if( bVerbose ){ |
| 29607 | - oputf("Result: %s\n", str.z); | |
| 31030 | + sqlite3_fprintf(p->out, "Result: %s\n", str.z); | |
| 29608 | 31031 | } |
| 29609 | 31032 | if( rc || zErrMsg ){ |
| 29610 | 31033 | nErr++; |
| 29611 | 31034 | rc = 1; |
| 29612 | - oputf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); | |
| 31035 | + sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg); | |
| 29613 | 31036 | sqlite3_free(zErrMsg); |
| 29614 | 31037 | }else if( cli_strcmp(zAns,str.z)!=0 ){ |
| 29615 | 31038 | nErr++; |
| 29616 | 31039 | rc = 1; |
| 29617 | - oputf("%d: Expected: [%s]\n", tno, zAns); | |
| 29618 | - oputf("%d: Got: [%s]\n", tno, str.z); | |
| 31040 | + sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns); | |
| 31041 | + sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z); | |
| 29619 | 31042 | } |
| 29620 | 31043 | } |
| 29621 | 31044 | else{ |
| 29622 | - eputf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno); | |
| 31045 | + sqlite3_fprintf(stderr, | |
| 31046 | + "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); | |
| 29623 | 31047 | rc = 1; |
| 29624 | 31048 | break; |
| 29625 | 31049 | } |
| 29626 | 31050 | } /* End loop over rows of content from SELFTEST */ |
| 29627 | 31051 | sqlite3_finalize(pStmt); |
| 29628 | 31052 | } /* End loop over k */ |
| 29629 | 31053 | freeText(&str); |
| 29630 | - oputf("%d errors out of %d tests\n", nErr, nTest); | |
| 31054 | + sqlite3_fprintf(p->out, "%d errors out of %d tests\n", nErr, nTest); | |
| 29631 | 31055 | }else |
| 29632 | 31056 | |
| 29633 | 31057 | if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ |
| 29634 | 31058 | if( nArg<2 || nArg>3 ){ |
| 29635 | 31059 | eputz("Usage: .separator COL ?ROW?\n"); |
| @@ -29673,11 +31097,12 @@ | ||
| 29673 | 31097 | }else |
| 29674 | 31098 | if( cli_strcmp(z,"debug")==0 ){ |
| 29675 | 31099 | bDebug = 1; |
| 29676 | 31100 | }else |
| 29677 | 31101 | { |
| 29678 | - eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); | |
| 31102 | + sqlite3_fprintf(stderr, | |
| 31103 | + "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); | |
| 29679 | 31104 | showHelp(p->out, azArg[0]); |
| 29680 | 31105 | rc = 1; |
| 29681 | 31106 | goto meta_command_exit; |
| 29682 | 31107 | } |
| 29683 | 31108 | }else if( zLike ){ |
| @@ -29751,11 +31176,11 @@ | ||
| 29751 | 31176 | } |
| 29752 | 31177 | shell_check_oom(zSql); |
| 29753 | 31178 | freeText(&sQuery); |
| 29754 | 31179 | freeText(&sSql); |
| 29755 | 31180 | if( bDebug ){ |
| 29756 | - oputf("%s\n", zSql); | |
| 31181 | + sqlite3_fprintf(p->out, "%s\n", zSql); | |
| 29757 | 31182 | }else{ |
| 29758 | 31183 | shell_exec(p, zSql, 0); |
| 29759 | 31184 | } |
| 29760 | 31185 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 29761 | 31186 | { |
| @@ -29781,11 +31206,11 @@ | ||
| 29781 | 31206 | "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n" |
| 29782 | 31207 | "|| ' AND typeof('||cname||')=''text'' ',\n" |
| 29783 | 31208 | "' OR ') as query, tname from tabcols group by tname)" |
| 29784 | 31209 | , zRevText); |
| 29785 | 31210 | shell_check_oom(zRevText); |
| 29786 | - if( bDebug ) oputf("%s\n", zRevText); | |
| 31211 | + if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zRevText); | |
| 29787 | 31212 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 29788 | 31213 | if( lrc!=SQLITE_OK ){ |
| 29789 | 31214 | /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the |
| 29790 | 31215 | ** user does cruel and unnatural things like ".limit expr_depth 0". */ |
| 29791 | 31216 | rc = 1; |
| @@ -29794,19 +31219,20 @@ | ||
| 29794 | 31219 | lrc = SQLITE_ROW==sqlite3_step(pStmt); |
| 29795 | 31220 | if( lrc ){ |
| 29796 | 31221 | const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); |
| 29797 | 31222 | sqlite3_stmt *pCheckStmt; |
| 29798 | 31223 | lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); |
| 29799 | - if( bDebug ) oputf("%s\n", zGenQuery); | |
| 31224 | + if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zGenQuery); | |
| 29800 | 31225 | if( lrc!=SQLITE_OK ){ |
| 29801 | 31226 | rc = 1; |
| 29802 | 31227 | }else{ |
| 29803 | 31228 | if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ |
| 29804 | 31229 | double countIrreversible = sqlite3_column_double(pCheckStmt, 0); |
| 29805 | 31230 | if( countIrreversible>0 ){ |
| 29806 | 31231 | int sz = (int)(countIrreversible + 0.5); |
| 29807 | - eputf("Digest includes %d invalidly encoded text field%s.\n", | |
| 31232 | + sqlite3_fprintf(stderr, | |
| 31233 | + "Digest includes %d invalidly encoded text field%s.\n", | |
| 29808 | 31234 | sz, (sz>1)? "s": ""); |
| 29809 | 31235 | } |
| 29810 | 31236 | } |
| 29811 | 31237 | sqlite3_finalize(pCheckStmt); |
| 29812 | 31238 | } |
| @@ -29836,15 +31262,15 @@ | ||
| 29836 | 31262 | zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); |
| 29837 | 31263 | for(i=2; i<nArg && zCmd!=0; i++){ |
| 29838 | 31264 | zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", |
| 29839 | 31265 | zCmd, azArg[i]); |
| 29840 | 31266 | } |
| 29841 | - consoleRestore(); | |
| 31267 | + /*consoleRestore();*/ | |
| 29842 | 31268 | x = zCmd!=0 ? system(zCmd) : 1; |
| 29843 | - consoleRenewSetup(); | |
| 31269 | + /*consoleRenewSetup();*/ | |
| 29844 | 31270 | sqlite3_free(zCmd); |
| 29845 | - if( x ) eputf("System command returns %d\n", x); | |
| 31271 | + if( x ) sqlite3_fprintf(stderr,"System command returns %d\n", x); | |
| 29846 | 31272 | }else |
| 29847 | 31273 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ |
| 29848 | 31274 | |
| 29849 | 31275 | if( c=='s' && cli_strncmp(azArg[0], "show", n)==0 ){ |
| 29850 | 31276 | static const char *azBool[] = { "off", "on", "trigger", "full"}; |
| @@ -29853,50 +31279,52 @@ | ||
| 29853 | 31279 | if( nArg!=1 ){ |
| 29854 | 31280 | eputz("Usage: .show\n"); |
| 29855 | 31281 | rc = 1; |
| 29856 | 31282 | goto meta_command_exit; |
| 29857 | 31283 | } |
| 29858 | - oputf("%12.12s: %s\n","echo", | |
| 31284 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","echo", | |
| 29859 | 31285 | azBool[ShellHasFlag(p, SHFLG_Echo)]); |
| 29860 | - oputf("%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); | |
| 29861 | - oputf("%12.12s: %s\n","explain", | |
| 31286 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); | |
| 31287 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","explain", | |
| 29862 | 31288 | p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); |
| 29863 | - oputf("%12.12s: %s\n","headers", azBool[p->showHeader!=0]); | |
| 31289 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","headers", | |
| 31290 | + azBool[p->showHeader!=0]); | |
| 29864 | 31291 | if( p->mode==MODE_Column |
| 29865 | 31292 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 29866 | 31293 | ){ |
| 29867 | - oputf("%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", | |
| 31294 | + sqlite3_fprintf(p->out, | |
| 31295 | + "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", | |
| 29868 | 31296 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 29869 | 31297 | p->cmOpts.bWordWrap ? "on" : "off", |
| 29870 | 31298 | p->cmOpts.bQuote ? "" : "no"); |
| 29871 | 31299 | }else{ |
| 29872 | - oputf("%12.12s: %s\n","mode", modeDescr[p->mode]); | |
| 31300 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); | |
| 29873 | 31301 | } |
| 29874 | - oputf("%12.12s: ", "nullvalue"); | |
| 29875 | - output_c_string(p->nullValue); | |
| 29876 | - oputz("\n"); | |
| 29877 | - oputf("%12.12s: %s\n","output", | |
| 31302 | + sqlite3_fprintf(p->out, "%12.12s: ", "nullvalue"); | |
| 31303 | + output_c_string(p->out, p->nullValue); | |
| 31304 | + sqlite3_fputs("\n", p->out); | |
| 31305 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","output", | |
| 29878 | 31306 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 29879 | - oputf("%12.12s: ", "colseparator"); | |
| 29880 | - output_c_string(p->colSeparator); | |
| 29881 | - oputz("\n"); | |
| 29882 | - oputf("%12.12s: ", "rowseparator"); | |
| 29883 | - output_c_string(p->rowSeparator); | |
| 29884 | - oputz("\n"); | |
| 31307 | + sqlite3_fprintf(p->out, "%12.12s: ", "colseparator"); | |
| 31308 | + output_c_string(p->out, p->colSeparator); | |
| 31309 | + sqlite3_fputs("\n", p->out); | |
| 31310 | + sqlite3_fprintf(p->out, "%12.12s: ", "rowseparator"); | |
| 31311 | + output_c_string(p->out, p->rowSeparator); | |
| 31312 | + sqlite3_fputs("\n", p->out); | |
| 29885 | 31313 | switch( p->statsOn ){ |
| 29886 | 31314 | case 0: zOut = "off"; break; |
| 29887 | 31315 | default: zOut = "on"; break; |
| 29888 | 31316 | case 2: zOut = "stmt"; break; |
| 29889 | 31317 | case 3: zOut = "vmstep"; break; |
| 29890 | 31318 | } |
| 29891 | - oputf("%12.12s: %s\n","stats", zOut); | |
| 29892 | - oputf("%12.12s: ", "width"); | |
| 31319 | + sqlite3_fprintf(p->out, "%12.12s: %s\n","stats", zOut); | |
| 31320 | + sqlite3_fprintf(p->out, "%12.12s: ", "width"); | |
| 29893 | 31321 | for (i=0;i<p->nWidth;i++) { |
| 29894 | - oputf("%d ", p->colWidth[i]); | |
| 31322 | + sqlite3_fprintf(p->out, "%d ", p->colWidth[i]); | |
| 29895 | 31323 | } |
| 29896 | - oputz("\n"); | |
| 29897 | - oputf("%12.12s: %s\n", "filename", | |
| 31324 | + sqlite3_fputs("\n", p->out); | |
| 31325 | + sqlite3_fprintf(p->out, "%12.12s: %s\n", "filename", | |
| 29898 | 31326 | p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); |
| 29899 | 31327 | }else |
| 29900 | 31328 | |
| 29901 | 31329 | if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){ |
| 29902 | 31330 | if( nArg==2 ){ |
| @@ -30010,13 +31438,14 @@ | ||
| 30010 | 31438 | if( nPrintCol<1 ) nPrintCol = 1; |
| 30011 | 31439 | nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; |
| 30012 | 31440 | for(i=0; i<nPrintRow; i++){ |
| 30013 | 31441 | for(j=i; j<nRow; j+=nPrintRow){ |
| 30014 | 31442 | char *zSp = j<nPrintRow ? "" : " "; |
| 30015 | - oputf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); | |
| 31443 | + sqlite3_fprintf(p->out, | |
| 31444 | + "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); | |
| 30016 | 31445 | } |
| 30017 | - oputz("\n"); | |
| 31446 | + sqlite3_fputs("\n", p->out); | |
| 30018 | 31447 | } |
| 30019 | 31448 | } |
| 30020 | 31449 | |
| 30021 | 31450 | for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); |
| 30022 | 31451 | sqlite3_free(azResult); |
| @@ -30024,11 +31453,11 @@ | ||
| 30024 | 31453 | |
| 30025 | 31454 | #ifndef SQLITE_SHELL_FIDDLE |
| 30026 | 31455 | /* Begin redirecting output to the file "testcase-out.txt" */ |
| 30027 | 31456 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 30028 | 31457 | output_reset(p); |
| 30029 | - p->out = output_file_open("testcase-out.txt", 0); | |
| 31458 | + p->out = output_file_open("testcase-out.txt"); | |
| 30030 | 31459 | if( p->out==0 ){ |
| 30031 | 31460 | eputz("Error: cannot open 'testcase-out.txt'\n"); |
| 30032 | 31461 | } |
| 30033 | 31462 | if( nArg>=2 ){ |
| 30034 | 31463 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| @@ -30068,11 +31497,10 @@ | ||
| 30068 | 31497 | {"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, |
| 30069 | 31498 | {"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, |
| 30070 | 31499 | {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, |
| 30071 | 31500 | {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, |
| 30072 | 31501 | {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, |
| 30073 | - {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, | |
| 30074 | 31502 | }; |
| 30075 | 31503 | int testctrl = -1; |
| 30076 | 31504 | int iCtrl = -1; |
| 30077 | 31505 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 30078 | 31506 | int isOk = 0; |
| @@ -30088,14 +31516,14 @@ | ||
| 30088 | 31516 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 30089 | 31517 | } |
| 30090 | 31518 | |
| 30091 | 31519 | /* --help lists all test-controls */ |
| 30092 | 31520 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 30093 | - oputz("Available test-controls:\n"); | |
| 31521 | + sqlite3_fputs("Available test-controls:\n", p->out); | |
| 30094 | 31522 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 30095 | 31523 | if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue; |
| 30096 | - oputf(" .testctrl %s %s\n", | |
| 31524 | + sqlite3_fprintf(p->out, " .testctrl %s %s\n", | |
| 30097 | 31525 | aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 30098 | 31526 | } |
| 30099 | 31527 | rc = 1; |
| 30100 | 31528 | goto meta_command_exit; |
| 30101 | 31529 | } |
| @@ -30108,19 +31536,19 @@ | ||
| 30108 | 31536 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 30109 | 31537 | if( testctrl<0 ){ |
| 30110 | 31538 | testctrl = aCtrl[i].ctrlCode; |
| 30111 | 31539 | iCtrl = i; |
| 30112 | 31540 | }else{ |
| 30113 | - eputf("Error: ambiguous test-control: \"%s\"\n" | |
| 31541 | + sqlite3_fprintf(stderr,"Error: ambiguous test-control: \"%s\"\n" | |
| 30114 | 31542 | "Use \".testctrl --help\" for help\n", zCmd); |
| 30115 | 31543 | rc = 1; |
| 30116 | 31544 | goto meta_command_exit; |
| 30117 | 31545 | } |
| 30118 | 31546 | } |
| 30119 | 31547 | } |
| 30120 | 31548 | if( testctrl<0 ){ |
| 30121 | - eputf("Error: unknown test-control: %s\n" | |
| 31549 | + sqlite3_fprintf(stderr,"Error: unknown test-control: %s\n" | |
| 30122 | 31550 | "Use \".testctrl --help\" for help\n", zCmd); |
| 30123 | 31551 | }else{ |
| 30124 | 31552 | switch(testctrl){ |
| 30125 | 31553 | |
| 30126 | 31554 | /* Special processing for .testctrl opt MASK ... |
| @@ -30170,11 +31598,13 @@ | ||
| 30170 | 31598 | { 0x10000000, 1, "OrderBySubq" }, |
| 30171 | 31599 | { 0xffffffff, 0, "All" }, |
| 30172 | 31600 | }; |
| 30173 | 31601 | unsigned int curOpt; |
| 30174 | 31602 | unsigned int newOpt; |
| 31603 | + unsigned int m; | |
| 30175 | 31604 | int ii; |
| 31605 | + int nOff; | |
| 30176 | 31606 | sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt); |
| 30177 | 31607 | newOpt = curOpt; |
| 30178 | 31608 | for(ii=2; ii<nArg; ii++){ |
| 30179 | 31609 | const char *z = azArg[ii]; |
| 30180 | 31610 | int useLabel = 0; |
| @@ -30192,16 +31622,17 @@ | ||
| 30192 | 31622 | int jj; |
| 30193 | 31623 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 30194 | 31624 | if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break; |
| 30195 | 31625 | } |
| 30196 | 31626 | if( jj>=ArraySize(aLabel) ){ |
| 30197 | - eputf("Error: no such optimization: \"%s\"\n", zLabel); | |
| 30198 | - eputz("Should be one of:"); | |
| 31627 | + sqlite3_fprintf(stderr, | |
| 31628 | + "Error: no such optimization: \"%s\"\n", zLabel); | |
| 31629 | + sqlite3_fputs("Should be one of:", stderr); | |
| 30199 | 31630 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 30200 | - eputf(" %s", aLabel[jj].zLabel); | |
| 31631 | + sqlite3_fprintf(stderr," %s", aLabel[jj].zLabel); | |
| 30201 | 31632 | } |
| 30202 | - eputz("\n"); | |
| 31633 | + sqlite3_fputs("\n", stderr); | |
| 30203 | 31634 | rc = 1; |
| 30204 | 31635 | goto meta_command_exit; |
| 30205 | 31636 | } |
| 30206 | 31637 | if( useLabel=='+' ){ |
| 30207 | 31638 | newOpt &= ~aLabel[jj].mask; |
| @@ -30210,28 +31641,32 @@ | ||
| 30210 | 31641 | } |
| 30211 | 31642 | } |
| 30212 | 31643 | } |
| 30213 | 31644 | if( curOpt!=newOpt ){ |
| 30214 | 31645 | sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt); |
| 30215 | - }else if( nArg<3 ){ | |
| 30216 | - curOpt = ~newOpt; | |
| 31646 | + } | |
| 31647 | + for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){ | |
| 31648 | + if( m & newOpt ) nOff++; | |
| 30217 | 31649 | } |
| 30218 | - if( newOpt==0 ){ | |
| 30219 | - oputz("+All\n"); | |
| 30220 | - }else if( newOpt==0xffffffff ){ | |
| 30221 | - oputz("-All\n"); | |
| 31650 | + if( nOff<12 ){ | |
| 31651 | + sqlite3_fputs("+All", p->out); | |
| 31652 | + for(ii=0; ii<ArraySize(aLabel); ii++){ | |
| 31653 | + if( !aLabel[ii].bDsply ) continue; | |
| 31654 | + if( (newOpt & aLabel[ii].mask)!=0 ){ | |
| 31655 | + sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel); | |
| 31656 | + } | |
| 31657 | + } | |
| 30222 | 31658 | }else{ |
| 30223 | - int jj; | |
| 30224 | - for(jj=0; jj<ArraySize(aLabel); jj++){ | |
| 30225 | - unsigned int m = aLabel[jj].mask; | |
| 30226 | - if( !aLabel[jj].bDsply ) continue; | |
| 30227 | - if( (curOpt&m)!=(newOpt&m) ){ | |
| 30228 | - oputf("%c%s\n", (newOpt & m)==0 ? '+' : '-', | |
| 30229 | - aLabel[jj].zLabel); | |
| 31659 | + sqlite3_fputs("-All", p->out); | |
| 31660 | + for(ii=0; ii<ArraySize(aLabel); ii++){ | |
| 31661 | + if( !aLabel[ii].bDsply ) continue; | |
| 31662 | + if( (newOpt & aLabel[ii].mask)==0 ){ | |
| 31663 | + sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel); | |
| 30230 | 31664 | } |
| 30231 | 31665 | } |
| 30232 | 31666 | } |
| 31667 | + sqlite3_fputs("\n", p->out); | |
| 30233 | 31668 | rc2 = isOk = 3; |
| 30234 | 31669 | break; |
| 30235 | 31670 | } |
| 30236 | 31671 | |
| 30237 | 31672 | /* sqlite3_test_control(int, db, int) */ |
| @@ -30267,11 +31702,11 @@ | ||
| 30267 | 31702 | if( nArg==3 || nArg==4 ){ |
| 30268 | 31703 | int ii = (int)integerValue(azArg[2]); |
| 30269 | 31704 | sqlite3 *db; |
| 30270 | 31705 | if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){ |
| 30271 | 31706 | sqlite3_randomness(sizeof(ii),&ii); |
| 30272 | - sputf(stdout, "-- random seed: %d\n", ii); | |
| 31707 | + sqlite3_fprintf(stdout, "-- random seed: %d\n", ii); | |
| 30273 | 31708 | } |
| 30274 | 31709 | if( nArg==3 ){ |
| 30275 | 31710 | db = 0; |
| 30276 | 31711 | }else{ |
| 30277 | 31712 | db = p->db; |
| @@ -30301,25 +31736,10 @@ | ||
| 30301 | 31736 | rc2 = sqlite3_test_control(testctrl, opt); |
| 30302 | 31737 | isOk = 3; |
| 30303 | 31738 | } |
| 30304 | 31739 | break; |
| 30305 | 31740 | |
| 30306 | - /* sqlite3_test_control(int, int) */ | |
| 30307 | - case SQLITE_TESTCTRL_USELONGDOUBLE: { | |
| 30308 | - int opt = -1; | |
| 30309 | - if( nArg==3 ){ | |
| 30310 | - if( cli_strcmp(azArg[2],"default")==0 ){ | |
| 30311 | - opt = 2; | |
| 30312 | - }else{ | |
| 30313 | - opt = booleanValue(azArg[2]); | |
| 30314 | - } | |
| 30315 | - } | |
| 30316 | - rc2 = sqlite3_test_control(testctrl, opt); | |
| 30317 | - isOk = 1; | |
| 30318 | - break; | |
| 30319 | - } | |
| 30320 | - | |
| 30321 | 31741 | /* sqlite3_test_control(sqlite3*) */ |
| 30322 | 31742 | case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: |
| 30323 | 31743 | rc2 = sqlite3_test_control(testctrl, p->db); |
| 30324 | 31744 | isOk = 3; |
| 30325 | 31745 | break; |
| @@ -30335,11 +31755,11 @@ | ||
| 30335 | 31755 | break; |
| 30336 | 31756 | |
| 30337 | 31757 | case SQLITE_TESTCTRL_SEEK_COUNT: { |
| 30338 | 31758 | u64 x = 0; |
| 30339 | 31759 | rc2 = sqlite3_test_control(testctrl, p->db, &x); |
| 30340 | - oputf("%llu\n", x); | |
| 31760 | + sqlite3_fprintf(p->out, "%llu\n", x); | |
| 30341 | 31761 | isOk = 3; |
| 30342 | 31762 | break; |
| 30343 | 31763 | } |
| 30344 | 31764 | |
| 30345 | 31765 | #ifdef YYCOVERAGE |
| @@ -30366,15 +31786,15 @@ | ||
| 30366 | 31786 | int id = 1; |
| 30367 | 31787 | while(1){ |
| 30368 | 31788 | int val = 0; |
| 30369 | 31789 | rc2 = sqlite3_test_control(testctrl, -id, &val); |
| 30370 | 31790 | if( rc2!=SQLITE_OK ) break; |
| 30371 | - if( id>1 ) oputz(" "); | |
| 30372 | - oputf("%d: %d", id, val); | |
| 31791 | + if( id>1 ) sqlite3_fputs(" ", p->out); | |
| 31792 | + sqlite3_fprintf(p->out, "%d: %d", id, val); | |
| 30373 | 31793 | id++; |
| 30374 | 31794 | } |
| 30375 | - if( id>1 ) oputz("\n"); | |
| 31795 | + if( id>1 ) sqlite3_fputs("\n", p->out); | |
| 30376 | 31796 | isOk = 3; |
| 30377 | 31797 | } |
| 30378 | 31798 | break; |
| 30379 | 31799 | } |
| 30380 | 31800 | #endif |
| @@ -30412,18 +31832,26 @@ | ||
| 30412 | 31832 | }else if( cli_strcmp(z,"reset")==0 ){ |
| 30413 | 31833 | faultsim_state.iCnt = faultsim_state.nSkip; |
| 30414 | 31834 | faultsim_state.nHit = 0; |
| 30415 | 31835 | sqlite3_test_control(testctrl, faultsim_callback); |
| 30416 | 31836 | }else if( cli_strcmp(z,"status")==0 ){ |
| 30417 | - oputf("faultsim.iId: %d\n", faultsim_state.iId); | |
| 30418 | - oputf("faultsim.iErr: %d\n", faultsim_state.iErr); | |
| 30419 | - oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt); | |
| 30420 | - oputf("faultsim.nHit: %d\n", faultsim_state.nHit); | |
| 30421 | - oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval); | |
| 30422 | - oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose); | |
| 30423 | - oputf("faultsim.nRepeat: %d\n", faultsim_state.nRepeat); | |
| 30424 | - oputf("faultsim.nSkip: %d\n", faultsim_state.nSkip); | |
| 31837 | + sqlite3_fprintf(p->out, "faultsim.iId: %d\n", | |
| 31838 | + faultsim_state.iId); | |
| 31839 | + sqlite3_fprintf(p->out, "faultsim.iErr: %d\n", | |
| 31840 | + faultsim_state.iErr); | |
| 31841 | + sqlite3_fprintf(p->out, "faultsim.iCnt: %d\n", | |
| 31842 | + faultsim_state.iCnt); | |
| 31843 | + sqlite3_fprintf(p->out, "faultsim.nHit: %d\n", | |
| 31844 | + faultsim_state.nHit); | |
| 31845 | + sqlite3_fprintf(p->out, "faultsim.iInterval: %d\n", | |
| 31846 | + faultsim_state.iInterval); | |
| 31847 | + sqlite3_fprintf(p->out, "faultsim.eVerbose: %d\n", | |
| 31848 | + faultsim_state.eVerbose); | |
| 31849 | + sqlite3_fprintf(p->out, "faultsim.nRepeat: %d\n", | |
| 31850 | + faultsim_state.nRepeat); | |
| 31851 | + sqlite3_fprintf(p->out, "faultsim.nSkip: %d\n", | |
| 31852 | + faultsim_state.nSkip); | |
| 30425 | 31853 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 30426 | 31854 | if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; |
| 30427 | 31855 | }else if( cli_strcmp(z,"-q")==0 ){ |
| 30428 | 31856 | if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--; |
| 30429 | 31857 | }else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){ |
| @@ -30437,19 +31865,20 @@ | ||
| 30437 | 31865 | }else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){ |
| 30438 | 31866 | faultsim_state.nSkip = atoi(azArg[++kk]); |
| 30439 | 31867 | }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ |
| 30440 | 31868 | bShowHelp = 1; |
| 30441 | 31869 | }else{ |
| 30442 | - eputf("Unrecognized fault_install argument: \"%s\"\n", | |
| 31870 | + sqlite3_fprintf(stderr, | |
| 31871 | + "Unrecognized fault_install argument: \"%s\"\n", | |
| 30443 | 31872 | azArg[kk]); |
| 30444 | 31873 | rc = 1; |
| 30445 | 31874 | bShowHelp = 1; |
| 30446 | 31875 | break; |
| 30447 | 31876 | } |
| 30448 | 31877 | } |
| 30449 | 31878 | if( bShowHelp ){ |
| 30450 | - oputz( | |
| 31879 | + sqlite3_fputs( | |
| 30451 | 31880 | "Usage: .testctrl fault_install ARGS\n" |
| 30452 | 31881 | "Possible arguments:\n" |
| 30453 | 31882 | " off Disable faultsim\n" |
| 30454 | 31883 | " on Activate faultsim\n" |
| 30455 | 31884 | " reset Reset the trigger counter\n" |
| @@ -30459,23 +31888,25 @@ | ||
| 30459 | 31888 | " --errcode N When triggered, return N as error code\n" |
| 30460 | 31889 | " --id ID Trigger only for the ID specified\n" |
| 30461 | 31890 | " --interval N Trigger only after every N-th call\n" |
| 30462 | 31891 | " --repeat N Turn off after N hits. 0 means never\n" |
| 30463 | 31892 | " --skip N Skip the first N encounters\n" |
| 31893 | + ,p->out | |
| 30464 | 31894 | ); |
| 30465 | 31895 | } |
| 30466 | 31896 | break; |
| 30467 | 31897 | } |
| 30468 | 31898 | } |
| 30469 | 31899 | } |
| 30470 | 31900 | if( isOk==0 && iCtrl>=0 ){ |
| 30471 | - oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); | |
| 31901 | + sqlite3_fprintf(p->out, | |
| 31902 | + "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); | |
| 30472 | 31903 | rc = 1; |
| 30473 | 31904 | }else if( isOk==1 ){ |
| 30474 | - oputf("%d\n", rc2); | |
| 31905 | + sqlite3_fprintf(p->out, "%d\n", rc2); | |
| 30475 | 31906 | }else if( isOk==2 ){ |
| 30476 | - oputf("0x%08x\n", rc2); | |
| 31907 | + sqlite3_fprintf(p->out, "0x%08x\n", rc2); | |
| 30477 | 31908 | } |
| 30478 | 31909 | }else |
| 30479 | 31910 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 30480 | 31911 | |
| 30481 | 31912 | if( c=='t' && n>4 && cli_strncmp(azArg[0], "timeout", n)==0 ){ |
| @@ -30526,17 +31957,17 @@ | ||
| 30526 | 31957 | } |
| 30527 | 31958 | else if( optionMatch(z, "close") ){ |
| 30528 | 31959 | mType |= SQLITE_TRACE_CLOSE; |
| 30529 | 31960 | } |
| 30530 | 31961 | else { |
| 30531 | - eputf("Unknown option \"%s\" on \".trace\"\n", z); | |
| 31962 | + sqlite3_fprintf(stderr,"Unknown option \"%s\" on \".trace\"\n", z); | |
| 30532 | 31963 | rc = 1; |
| 30533 | 31964 | goto meta_command_exit; |
| 30534 | 31965 | } |
| 30535 | 31966 | }else{ |
| 30536 | 31967 | output_file_close(p->traceOut); |
| 30537 | - p->traceOut = output_file_open(z, 0); | |
| 31968 | + p->traceOut = output_file_open(z); | |
| 30538 | 31969 | } |
| 30539 | 31970 | } |
| 30540 | 31971 | if( p->traceOut==0 ){ |
| 30541 | 31972 | sqlite3_trace_v2(p->db, 0, 0, 0); |
| 30542 | 31973 | }else{ |
| @@ -30569,103 +32000,40 @@ | ||
| 30569 | 32000 | } |
| 30570 | 32001 | } |
| 30571 | 32002 | }else |
| 30572 | 32003 | #endif |
| 30573 | 32004 | |
| 30574 | -#if SQLITE_USER_AUTHENTICATION | |
| 30575 | - if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){ | |
| 30576 | - if( nArg<2 ){ | |
| 30577 | - eputz("Usage: .user SUBCOMMAND ...\n"); | |
| 30578 | - rc = 1; | |
| 30579 | - goto meta_command_exit; | |
| 30580 | - } | |
| 30581 | - open_db(p, 0); | |
| 30582 | - if( cli_strcmp(azArg[1],"login")==0 ){ | |
| 30583 | - if( nArg!=4 ){ | |
| 30584 | - eputz("Usage: .user login USER PASSWORD\n"); | |
| 30585 | - rc = 1; | |
| 30586 | - goto meta_command_exit; | |
| 30587 | - } | |
| 30588 | - rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], | |
| 30589 | - strlen30(azArg[3])); | |
| 30590 | - if( rc ){ | |
| 30591 | - eputf("Authentication failed for user %s\n", azArg[2]); | |
| 30592 | - rc = 1; | |
| 30593 | - } | |
| 30594 | - }else if( cli_strcmp(azArg[1],"add")==0 ){ | |
| 30595 | - if( nArg!=5 ){ | |
| 30596 | - eputz("Usage: .user add USER PASSWORD ISADMIN\n"); | |
| 30597 | - rc = 1; | |
| 30598 | - goto meta_command_exit; | |
| 30599 | - } | |
| 30600 | - rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), | |
| 30601 | - booleanValue(azArg[4])); | |
| 30602 | - if( rc ){ | |
| 30603 | - eputf("User-Add failed: %d\n", rc); | |
| 30604 | - rc = 1; | |
| 30605 | - } | |
| 30606 | - }else if( cli_strcmp(azArg[1],"edit")==0 ){ | |
| 30607 | - if( nArg!=5 ){ | |
| 30608 | - eputz("Usage: .user edit USER PASSWORD ISADMIN\n"); | |
| 30609 | - rc = 1; | |
| 30610 | - goto meta_command_exit; | |
| 30611 | - } | |
| 30612 | - rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), | |
| 30613 | - booleanValue(azArg[4])); | |
| 30614 | - if( rc ){ | |
| 30615 | - eputf("User-Edit failed: %d\n", rc); | |
| 30616 | - rc = 1; | |
| 30617 | - } | |
| 30618 | - }else if( cli_strcmp(azArg[1],"delete")==0 ){ | |
| 30619 | - if( nArg!=3 ){ | |
| 30620 | - eputz("Usage: .user delete USER\n"); | |
| 30621 | - rc = 1; | |
| 30622 | - goto meta_command_exit; | |
| 30623 | - } | |
| 30624 | - rc = sqlite3_user_delete(p->db, azArg[2]); | |
| 30625 | - if( rc ){ | |
| 30626 | - eputf("User-Delete failed: %d\n", rc); | |
| 30627 | - rc = 1; | |
| 30628 | - } | |
| 30629 | - }else{ | |
| 30630 | - eputz("Usage: .user login|add|edit|delete ...\n"); | |
| 30631 | - rc = 1; | |
| 30632 | - goto meta_command_exit; | |
| 30633 | - } | |
| 30634 | - }else | |
| 30635 | -#endif /* SQLITE_USER_AUTHENTICATION */ | |
| 30636 | - | |
| 30637 | 32005 | if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ |
| 30638 | 32006 | char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; |
| 30639 | - oputf("SQLite %s %s\n" /*extra-version-info*/, | |
| 32007 | + sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/, | |
| 30640 | 32008 | sqlite3_libversion(), sqlite3_sourceid()); |
| 30641 | 32009 | #if SQLITE_HAVE_ZLIB |
| 30642 | - oputf("zlib version %s\n", zlibVersion()); | |
| 32010 | + sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion()); | |
| 30643 | 32011 | #endif |
| 30644 | 32012 | #define CTIMEOPT_VAL_(opt) #opt |
| 30645 | 32013 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 30646 | 32014 | #if defined(__clang__) && defined(__clang_major__) |
| 30647 | - oputf("clang-" CTIMEOPT_VAL(__clang_major__) "." | |
| 32015 | + sqlite3_fprintf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." | |
| 30648 | 32016 | CTIMEOPT_VAL(__clang_minor__) "." |
| 30649 | 32017 | CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); |
| 30650 | 32018 | #elif defined(_MSC_VER) |
| 30651 | - oputf("msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); | |
| 32019 | + sqlite3_fprintf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); | |
| 30652 | 32020 | #elif defined(__GNUC__) && defined(__VERSION__) |
| 30653 | - oputf("gcc-" __VERSION__ " (%s)\n", zPtrSz); | |
| 32021 | + sqlite3_fprintf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); | |
| 30654 | 32022 | #endif |
| 30655 | 32023 | }else |
| 30656 | 32024 | |
| 30657 | 32025 | if( c=='v' && cli_strncmp(azArg[0], "vfsinfo", n)==0 ){ |
| 30658 | 32026 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 30659 | 32027 | sqlite3_vfs *pVfs = 0; |
| 30660 | 32028 | if( p->db ){ |
| 30661 | 32029 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); |
| 30662 | 32030 | if( pVfs ){ |
| 30663 | - oputf("vfs.zName = \"%s\"\n", pVfs->zName); | |
| 30664 | - oputf("vfs.iVersion = %d\n", pVfs->iVersion); | |
| 30665 | - oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); | |
| 30666 | - oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); | |
| 32031 | + sqlite3_fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); | |
| 32032 | + sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); | |
| 32033 | + sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); | |
| 32034 | + sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); | |
| 30667 | 32035 | } |
| 30668 | 32036 | } |
| 30669 | 32037 | }else |
| 30670 | 32038 | |
| 30671 | 32039 | if( c=='v' && cli_strncmp(azArg[0], "vfslist", n)==0 ){ |
| @@ -30673,17 +32041,17 @@ | ||
| 30673 | 32041 | sqlite3_vfs *pCurrent = 0; |
| 30674 | 32042 | if( p->db ){ |
| 30675 | 32043 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); |
| 30676 | 32044 | } |
| 30677 | 32045 | for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ |
| 30678 | - oputf("vfs.zName = \"%s\"%s\n", pVfs->zName, | |
| 32046 | + sqlite3_fprintf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, | |
| 30679 | 32047 | pVfs==pCurrent ? " <--- CURRENT" : ""); |
| 30680 | - oputf("vfs.iVersion = %d\n", pVfs->iVersion); | |
| 30681 | - oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); | |
| 30682 | - oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); | |
| 32048 | + sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); | |
| 32049 | + sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); | |
| 32050 | + sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); | |
| 30683 | 32051 | if( pVfs->pNext ){ |
| 30684 | - oputz("-----------------------------------\n"); | |
| 32052 | + sqlite3_fputs("-----------------------------------\n", p->out); | |
| 30685 | 32053 | } |
| 30686 | 32054 | } |
| 30687 | 32055 | }else |
| 30688 | 32056 | |
| 30689 | 32057 | if( c=='v' && cli_strncmp(azArg[0], "vfsname", n)==0 ){ |
| @@ -30690,11 +32058,11 @@ | ||
| 30690 | 32058 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 30691 | 32059 | char *zVfsName = 0; |
| 30692 | 32060 | if( p->db ){ |
| 30693 | 32061 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); |
| 30694 | 32062 | if( zVfsName ){ |
| 30695 | - oputf("%s\n", zVfsName); | |
| 32063 | + sqlite3_fprintf(p->out, "%s\n", zVfsName); | |
| 30696 | 32064 | sqlite3_free(zVfsName); |
| 30697 | 32065 | } |
| 30698 | 32066 | } |
| 30699 | 32067 | }else |
| 30700 | 32068 | |
| @@ -30714,11 +32082,11 @@ | ||
| 30714 | 32082 | p->colWidth[j-1] = (int)integerValue(azArg[j]); |
| 30715 | 32083 | } |
| 30716 | 32084 | }else |
| 30717 | 32085 | |
| 30718 | 32086 | { |
| 30719 | - eputf("Error: unknown command or invalid arguments: " | |
| 32087 | + sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: " | |
| 30720 | 32088 | " \"%s\". Enter \".help\" for help\n", azArg[0]); |
| 30721 | 32089 | rc = 1; |
| 30722 | 32090 | } |
| 30723 | 32091 | |
| 30724 | 32092 | meta_command_exit: |
| @@ -30755,11 +32123,10 @@ | ||
| 30755 | 32123 | SCAN_TRACKER_REFTYPE pst){ |
| 30756 | 32124 | char cin; |
| 30757 | 32125 | char cWait = (char)qss; /* intentional narrowing loss */ |
| 30758 | 32126 | if( cWait==0 ){ |
| 30759 | 32127 | PlainScan: |
| 30760 | - assert( cWait==0 ); | |
| 30761 | 32128 | while( (cin = *zLine++)!=0 ){ |
| 30762 | 32129 | if( IsSpace(cin) ) |
| 30763 | 32130 | continue; |
| 30764 | 32131 | switch (cin){ |
| 30765 | 32132 | case '-': |
| @@ -30807,11 +32174,10 @@ | ||
| 30807 | 32174 | switch( cWait ){ |
| 30808 | 32175 | case '*': |
| 30809 | 32176 | if( *zLine != '/' ) |
| 30810 | 32177 | continue; |
| 30811 | 32178 | ++zLine; |
| 30812 | - cWait = 0; | |
| 30813 | 32179 | CONTINUE_PROMPT_AWAITC(pst, 0); |
| 30814 | 32180 | qss = QSS_SETV(qss, 0); |
| 30815 | 32181 | goto PlainScan; |
| 30816 | 32182 | case '`': case '\'': case '"': |
| 30817 | 32183 | if(*zLine==cWait){ |
| @@ -30819,11 +32185,10 @@ | ||
| 30819 | 32185 | ++zLine; |
| 30820 | 32186 | continue; |
| 30821 | 32187 | } |
| 30822 | 32188 | deliberate_fall_through; |
| 30823 | 32189 | case ']': |
| 30824 | - cWait = 0; | |
| 30825 | 32190 | CONTINUE_PROMPT_AWAITC(pst, 0); |
| 30826 | 32191 | qss = QSS_SETV(qss, 0); |
| 30827 | 32192 | goto PlainScan; |
| 30828 | 32193 | default: assert(0); |
| 30829 | 32194 | } |
| @@ -30966,11 +32331,11 @@ | ||
| 30966 | 32331 | open_db(p, 0); |
| 30967 | 32332 | if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
| 30968 | 32333 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 30969 | 32334 | BEGIN_TIMER; |
| 30970 | 32335 | rc = shell_exec(p, zSql, &zErrMsg); |
| 30971 | - END_TIMER; | |
| 32336 | + END_TIMER(p->out); | |
| 30972 | 32337 | if( rc || zErrMsg ){ |
| 30973 | 32338 | char zPrefix[100]; |
| 30974 | 32339 | const char *zErrorTail; |
| 30975 | 32340 | const char *zErrorType; |
| 30976 | 32341 | if( zErrMsg==0 ){ |
| @@ -30990,28 +32355,31 @@ | ||
| 30990 | 32355 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
| 30991 | 32356 | "%s near line %d:", zErrorType, startline); |
| 30992 | 32357 | }else{ |
| 30993 | 32358 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType); |
| 30994 | 32359 | } |
| 30995 | - eputf("%s %s\n", zPrefix, zErrorTail); | |
| 32360 | + sqlite3_fprintf(stderr,"%s %s\n", zPrefix, zErrorTail); | |
| 30996 | 32361 | sqlite3_free(zErrMsg); |
| 30997 | 32362 | zErrMsg = 0; |
| 30998 | 32363 | return 1; |
| 30999 | 32364 | }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ |
| 31000 | 32365 | char zLineBuf[2000]; |
| 31001 | 32366 | sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, |
| 31002 | 32367 | "changes: %lld total_changes: %lld", |
| 31003 | 32368 | sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); |
| 31004 | - oputf("%s\n", zLineBuf); | |
| 32369 | + sqlite3_fprintf(p->out, "%s\n", zLineBuf); | |
| 31005 | 32370 | } |
| 31006 | 32371 | |
| 31007 | 32372 | if( doAutoDetectRestore(p, zSql) ) return 1; |
| 31008 | 32373 | return 0; |
| 31009 | 32374 | } |
| 31010 | 32375 | |
| 31011 | 32376 | static void echo_group_input(ShellState *p, const char *zDo){ |
| 31012 | - if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); | |
| 32377 | + if( ShellHasFlag(p, SHFLG_Echo) ){ | |
| 32378 | + sqlite3_fprintf(p->out, "%s\n", zDo); | |
| 32379 | + fflush(p->out); | |
| 32380 | + } | |
| 31013 | 32381 | } |
| 31014 | 32382 | |
| 31015 | 32383 | #ifdef SQLITE_SHELL_FIDDLE |
| 31016 | 32384 | /* |
| 31017 | 32385 | ** Alternate one_input_line() impl for wasm mode. This is not in the primary |
| @@ -31065,11 +32433,11 @@ | ||
| 31065 | 32433 | i64 startline = 0; /* Line number for start of current input */ |
| 31066 | 32434 | QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ |
| 31067 | 32435 | |
| 31068 | 32436 | if( p->inputNesting==MAX_INPUT_NESTING ){ |
| 31069 | 32437 | /* This will be more informative in a later version. */ |
| 31070 | - eputf("Input nesting limit (%d) reached at line %d." | |
| 32438 | + sqlite3_fprintf(stderr,"Input nesting limit (%d) reached at line %d." | |
| 31071 | 32439 | " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); |
| 31072 | 32440 | return 1; |
| 31073 | 32441 | } |
| 31074 | 32442 | ++p->inputNesting; |
| 31075 | 32443 | p->lineno = 0; |
| @@ -31077,11 +32445,11 @@ | ||
| 31077 | 32445 | while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ |
| 31078 | 32446 | fflush(p->out); |
| 31079 | 32447 | zLine = one_input_line(p->in, zLine, nSql>0); |
| 31080 | 32448 | if( zLine==0 ){ |
| 31081 | 32449 | /* End of input */ |
| 31082 | - if( p->in==0 && stdin_is_interactive ) oputz("\n"); | |
| 32450 | + if( p->in==0 && stdin_is_interactive ) sqlite3_fputs("\n", p->out); | |
| 31083 | 32451 | break; |
| 31084 | 32452 | } |
| 31085 | 32453 | if( seenInterrupt ){ |
| 31086 | 32454 | if( p->in!=0 ) break; |
| 31087 | 32455 | seenInterrupt = 0; |
| @@ -31297,19 +32665,19 @@ | ||
| 31297 | 32665 | } |
| 31298 | 32666 | zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); |
| 31299 | 32667 | shell_check_oom(zBuf); |
| 31300 | 32668 | sqliterc = zBuf; |
| 31301 | 32669 | } |
| 31302 | - p->in = fopen(sqliterc,"rb"); | |
| 32670 | + p->in = sqlite3_fopen(sqliterc,"rb"); | |
| 31303 | 32671 | if( p->in ){ |
| 31304 | 32672 | if( stdin_is_interactive ){ |
| 31305 | - eputf("-- Loading resources from %s\n", sqliterc); | |
| 32673 | + sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc); | |
| 31306 | 32674 | } |
| 31307 | 32675 | if( process_input(p) && bail_on_error ) exit(1); |
| 31308 | 32676 | fclose(p->in); |
| 31309 | 32677 | }else if( sqliterc_override!=0 ){ |
| 31310 | - eputf("cannot open: \"%s\"\n", sqliterc); | |
| 32678 | + sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc); | |
| 31311 | 32679 | if( bail_on_error ) exit(1); |
| 31312 | 32680 | } |
| 31313 | 32681 | p->in = inSaved; |
| 31314 | 32682 | p->lineno = savedLineno; |
| 31315 | 32683 | sqlite3_free(zBuf); |
| @@ -31374,23 +32742,21 @@ | ||
| 31374 | 32742 | " -table set output mode to 'table'\n" |
| 31375 | 32743 | " -tabs set output mode to 'tabs'\n" |
| 31376 | 32744 | " -unsafe-testing allow unsafe commands and modes for testing\n" |
| 31377 | 32745 | " -version show SQLite version\n" |
| 31378 | 32746 | " -vfs NAME use NAME as the default VFS\n" |
| 31379 | -#ifdef SQLITE_ENABLE_VFSTRACE | |
| 31380 | 32747 | " -vfstrace enable tracing of all VFS calls\n" |
| 31381 | -#endif | |
| 31382 | 32748 | #ifdef SQLITE_HAVE_ZLIB |
| 31383 | 32749 | " -zip open the file as a ZIP Archive\n" |
| 31384 | 32750 | #endif |
| 31385 | 32751 | ; |
| 31386 | 32752 | static void usage(int showDetail){ |
| 31387 | - eputf("Usage: %s [OPTIONS] [FILENAME [SQL]]\n" | |
| 32753 | + sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL]]\n" | |
| 31388 | 32754 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 31389 | 32755 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 31390 | 32756 | if( showDetail ){ |
| 31391 | - eputf("OPTIONS include:\n%s", zOptions); | |
| 32757 | + sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); | |
| 31392 | 32758 | }else{ |
| 31393 | 32759 | eputz("Use the -help option for additional information\n"); |
| 31394 | 32760 | } |
| 31395 | 32761 | exit(0); |
| 31396 | 32762 | } |
| @@ -31411,10 +32777,13 @@ | ||
| 31411 | 32777 | */ |
| 31412 | 32778 | static void main_init(ShellState *data) { |
| 31413 | 32779 | memset(data, 0, sizeof(*data)); |
| 31414 | 32780 | data->normalMode = data->cMode = data->mode = MODE_List; |
| 31415 | 32781 | data->autoExplain = 1; |
| 32782 | +#ifdef _WIN32 | |
| 32783 | + data->crlfMode = 1; | |
| 32784 | +#endif | |
| 31416 | 32785 | data->pAuxDb = &data->aAuxDb[0]; |
| 31417 | 32786 | memcpy(data->colSeparator,SEP_Column, 2); |
| 31418 | 32787 | memcpy(data->rowSeparator,SEP_Row, 2); |
| 31419 | 32788 | data->showHeader = 0; |
| 31420 | 32789 | data->shellFlgs = SHFLG_Lookaside; |
| @@ -31446,29 +32815,39 @@ | ||
| 31446 | 32815 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 31447 | 32816 | #endif |
| 31448 | 32817 | } |
| 31449 | 32818 | #else |
| 31450 | 32819 | static void printBold(const char *zText){ |
| 31451 | - sputf(stdout, "\033[1m%s\033[0m", zText); | |
| 32820 | + sqlite3_fprintf(stdout, "\033[1m%s\033[0m", zText); | |
| 31452 | 32821 | } |
| 31453 | 32822 | #endif |
| 31454 | 32823 | |
| 31455 | 32824 | /* |
| 31456 | 32825 | ** Get the argument to an --option. Throw an error and die if no argument |
| 31457 | 32826 | ** is available. |
| 31458 | 32827 | */ |
| 31459 | 32828 | static char *cmdline_option_value(int argc, char **argv, int i){ |
| 31460 | 32829 | if( i==argc ){ |
| 31461 | - eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); | |
| 32830 | + sqlite3_fprintf(stderr, | |
| 32831 | + "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); | |
| 31462 | 32832 | exit(1); |
| 31463 | 32833 | } |
| 31464 | 32834 | return argv[i]; |
| 31465 | 32835 | } |
| 31466 | 32836 | |
| 31467 | 32837 | static void sayAbnormalExit(void){ |
| 31468 | 32838 | if( seenInterrupt ) eputz("Program interrupted.\n"); |
| 31469 | 32839 | } |
| 32840 | + | |
| 32841 | +/* Routine to output from vfstrace | |
| 32842 | +*/ | |
| 32843 | +static int vfstraceOut(const char *z, void *pArg){ | |
| 32844 | + ShellState *p = (ShellState*)pArg; | |
| 32845 | + sqlite3_fputs(z, p->out); | |
| 32846 | + fflush(p->out); | |
| 32847 | + return 1; | |
| 32848 | +} | |
| 31470 | 32849 | |
| 31471 | 32850 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 31472 | 32851 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 31473 | 32852 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 31474 | 32853 | # define SQLITE_SHELL_IS_UTF8 (0) |
| @@ -31493,19 +32872,19 @@ | ||
| 31493 | 32872 | char *zErrMsg = 0; |
| 31494 | 32873 | #ifdef SQLITE_SHELL_FIDDLE |
| 31495 | 32874 | # define data shellState |
| 31496 | 32875 | #else |
| 31497 | 32876 | ShellState data; |
| 31498 | - StreamsAreConsole consStreams = SAC_NoConsole; | |
| 31499 | 32877 | #endif |
| 31500 | 32878 | const char *zInitFile = 0; |
| 31501 | 32879 | int i; |
| 31502 | 32880 | int rc = 0; |
| 31503 | 32881 | int warnInmemoryDb = 0; |
| 31504 | 32882 | int readStdin = 1; |
| 31505 | 32883 | int nCmd = 0; |
| 31506 | 32884 | int nOptsEnd = argc; |
| 32885 | + int bEnableVfstrace = 0; | |
| 31507 | 32886 | char **azCmd = 0; |
| 31508 | 32887 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 31509 | 32888 | #if !SQLITE_SHELL_IS_UTF8 |
| 31510 | 32889 | char **argvToFree = 0; |
| 31511 | 32890 | int argcToFree = 0; |
| @@ -31515,25 +32894,29 @@ | ||
| 31515 | 32894 | #ifdef SQLITE_SHELL_FIDDLE |
| 31516 | 32895 | stdin_is_interactive = 0; |
| 31517 | 32896 | stdout_is_console = 1; |
| 31518 | 32897 | data.wasm.zDefaultDbName = "/fiddle.sqlite3"; |
| 31519 | 32898 | #else |
| 31520 | - consStreams = consoleClassifySetup(stdin, stdout, stderr); | |
| 31521 | - stdin_is_interactive = (consStreams & SAC_InConsole)!=0; | |
| 31522 | - stdout_is_console = (consStreams & SAC_OutConsole)!=0; | |
| 31523 | - atexit(consoleRestore); | |
| 32899 | + stdin_is_interactive = isatty(0); | |
| 32900 | + stdout_is_console = isatty(1); | |
| 31524 | 32901 | #endif |
| 31525 | 32902 | atexit(sayAbnormalExit); |
| 31526 | 32903 | #ifdef SQLITE_DEBUG |
| 31527 | 32904 | mem_main_enter = sqlite3_memory_used(); |
| 31528 | 32905 | #endif |
| 31529 | 32906 | #if !defined(_WIN32_WCE) |
| 31530 | 32907 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 31531 | 32908 | if( isatty(0) && isatty(2) ){ |
| 31532 | - eputf("attach debugger to process %d and press any key to continue.\n", | |
| 32909 | + char zLine[100]; | |
| 32910 | + sqlite3_fprintf(stderr, | |
| 32911 | + "attach debugger to process %d and press ENTER to continue...", | |
| 31533 | 32912 | GETPID()); |
| 31534 | - fgetc(stdin); | |
| 32913 | + if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0 | |
| 32914 | + && cli_strcmp(zLine,"stop")==0 | |
| 32915 | + ){ | |
| 32916 | + exit(1); | |
| 32917 | + } | |
| 31535 | 32918 | }else{ |
| 31536 | 32919 | #if defined(_WIN32) || defined(WIN32) |
| 31537 | 32920 | #if SQLITE_OS_WINRT |
| 31538 | 32921 | __debugbreak(); |
| 31539 | 32922 | #else |
| @@ -31554,11 +32937,12 @@ | ||
| 31554 | 32937 | } |
| 31555 | 32938 | #endif |
| 31556 | 32939 | |
| 31557 | 32940 | #if USE_SYSTEM_SQLITE+0!=1 |
| 31558 | 32941 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 31559 | - eputf("SQLite header and source version mismatch\n%s\n%s\n", | |
| 32942 | + sqlite3_fprintf(stderr, | |
| 32943 | + "SQLite header and source version mismatch\n%s\n%s\n", | |
| 31560 | 32944 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 31561 | 32945 | exit(1); |
| 31562 | 32946 | } |
| 31563 | 32947 | #endif |
| 31564 | 32948 | main_init(&data); |
| @@ -31696,21 +33080,12 @@ | ||
| 31696 | 33080 | switch( n ){ |
| 31697 | 33081 | case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break; |
| 31698 | 33082 | case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break; |
| 31699 | 33083 | default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break; |
| 31700 | 33084 | } |
| 31701 | -#ifdef SQLITE_ENABLE_VFSTRACE | |
| 31702 | 33085 | }else if( cli_strcmp(z,"-vfstrace")==0 ){ |
| 31703 | - extern int vfstrace_register( | |
| 31704 | - const char *zTraceName, | |
| 31705 | - const char *zOldVfsName, | |
| 31706 | - int (*xOut)(const char*,void*), | |
| 31707 | - void *pOutArg, | |
| 31708 | - int makeDefault | |
| 31709 | - ); | |
| 31710 | - vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); | |
| 31711 | -#endif | |
| 33086 | + bEnableVfstrace = 1; | |
| 31712 | 33087 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 31713 | 33088 | }else if( cli_strcmp(z,"-multiplex")==0 ){ |
| 31714 | 33089 | extern int sqlite3_multiplex_initialize(const char*,int); |
| 31715 | 33090 | sqlite3_multiplex_initialize(0, 1); |
| 31716 | 33091 | #endif |
| @@ -31762,11 +33137,11 @@ | ||
| 31762 | 33137 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 31763 | 33138 | /* no-op - catch this on the second pass */ |
| 31764 | 33139 | } |
| 31765 | 33140 | } |
| 31766 | 33141 | #ifndef SQLITE_SHELL_FIDDLE |
| 31767 | - verify_uninitialized(); | |
| 33142 | + if( !bEnableVfstrace ) verify_uninitialized(); | |
| 31768 | 33143 | #endif |
| 31769 | 33144 | |
| 31770 | 33145 | |
| 31771 | 33146 | #ifdef SQLITE_SHELL_INIT_PROC |
| 31772 | 33147 | { |
| @@ -31786,25 +33161,29 @@ | ||
| 31786 | 33161 | if( zVfs ){ |
| 31787 | 33162 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 31788 | 33163 | if( pVfs ){ |
| 31789 | 33164 | sqlite3_vfs_register(pVfs, 1); |
| 31790 | 33165 | }else{ |
| 31791 | - eputf("no such VFS: \"%s\"\n", zVfs); | |
| 33166 | + sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs); | |
| 31792 | 33167 | exit(1); |
| 31793 | 33168 | } |
| 31794 | 33169 | } |
| 31795 | 33170 | |
| 31796 | 33171 | if( data.pAuxDb->zDbFilename==0 ){ |
| 31797 | 33172 | #ifndef SQLITE_OMIT_MEMORYDB |
| 31798 | 33173 | data.pAuxDb->zDbFilename = ":memory:"; |
| 31799 | 33174 | warnInmemoryDb = argc==1; |
| 31800 | 33175 | #else |
| 31801 | - eputf("%s: Error: no database filename specified\n", Argv0); | |
| 33176 | + sqlite3_fprintf(stderr, | |
| 33177 | + "%s: Error: no database filename specified\n", Argv0); | |
| 31802 | 33178 | return 1; |
| 31803 | 33179 | #endif |
| 31804 | 33180 | } |
| 31805 | 33181 | data.out = stdout; |
| 33182 | + if( bEnableVfstrace ){ | |
| 33183 | + vfstrace_register("trace",0,vfstraceOut, &data, 1); | |
| 33184 | + } | |
| 31806 | 33185 | #ifndef SQLITE_SHELL_FIDDLE |
| 31807 | 33186 | sqlite3_appendvfs_init(0,0,0); |
| 31808 | 33187 | #endif |
| 31809 | 33188 | |
| 31810 | 33189 | /* Go ahead and open the database file if it already exists. If the |
| @@ -31913,11 +33292,11 @@ | ||
| 31913 | 33292 | */ |
| 31914 | 33293 | ShellSetFlag(&data, SHFLG_Backslash); |
| 31915 | 33294 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 31916 | 33295 | /* No-op. The bail_on_error flag should already be set. */ |
| 31917 | 33296 | }else if( cli_strcmp(z,"-version")==0 ){ |
| 31918 | - sputf(stdout, "%s %s (%d-bit)\n", | |
| 33297 | + sqlite3_fprintf(stdout, "%s %s (%d-bit)\n", | |
| 31919 | 33298 | sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); |
| 31920 | 33299 | return 0; |
| 31921 | 33300 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 31922 | 33301 | /* Need to check for interactive override here to so that it can |
| 31923 | 33302 | ** affect console setup (for Windows only) and testing thereof. |
| @@ -31951,14 +33330,12 @@ | ||
| 31951 | 33330 | }else if( cli_strcmp(z,"-sorterref")==0 ){ |
| 31952 | 33331 | i++; |
| 31953 | 33332 | #endif |
| 31954 | 33333 | }else if( cli_strcmp(z,"-vfs")==0 ){ |
| 31955 | 33334 | i++; |
| 31956 | -#ifdef SQLITE_ENABLE_VFSTRACE | |
| 31957 | 33335 | }else if( cli_strcmp(z,"-vfstrace")==0 ){ |
| 31958 | 33336 | i++; |
| 31959 | -#endif | |
| 31960 | 33337 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 31961 | 33338 | }else if( cli_strcmp(z,"-multiplex")==0 ){ |
| 31962 | 33339 | i++; |
| 31963 | 33340 | #endif |
| 31964 | 33341 | }else if( cli_strcmp(z,"-help")==0 ){ |
| @@ -31978,18 +33355,18 @@ | ||
| 31978 | 33355 | rc = shell_exec(&data, z, &zErrMsg); |
| 31979 | 33356 | if( zErrMsg!=0 ){ |
| 31980 | 33357 | shellEmitError(zErrMsg); |
| 31981 | 33358 | if( bail_on_error ) return rc!=0 ? rc : 1; |
| 31982 | 33359 | }else if( rc!=0 ){ |
| 31983 | - eputf("Error: unable to process SQL \"%s\"\n", z); | |
| 33360 | + sqlite3_fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z); | |
| 31984 | 33361 | if( bail_on_error ) return rc; |
| 31985 | 33362 | } |
| 31986 | 33363 | } |
| 31987 | 33364 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) |
| 31988 | 33365 | }else if( cli_strncmp(z, "-A", 2)==0 ){ |
| 31989 | 33366 | if( nCmd>0 ){ |
| 31990 | - eputf("Error: cannot mix regular SQL or dot-commands" | |
| 33367 | + sqlite3_fprintf(stderr,"Error: cannot mix regular SQL or dot-commands" | |
| 31991 | 33368 | " with \"%s\"\n", z); |
| 31992 | 33369 | return 1; |
| 31993 | 33370 | } |
| 31994 | 33371 | open_db(&data, OPEN_DB_ZIPFILE); |
| 31995 | 33372 | if( z[2] ){ |
| @@ -32004,11 +33381,11 @@ | ||
| 32004 | 33381 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 32005 | 33382 | data.bSafeMode = data.bSafeModePersist = 1; |
| 32006 | 33383 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 32007 | 33384 | /* Acted upon in first pass. */ |
| 32008 | 33385 | }else{ |
| 32009 | - eputf("%s: Error: unknown option: %s\n", Argv0, z); | |
| 33386 | + sqlite3_fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); | |
| 32010 | 33387 | eputz("Use -help for a list of options.\n"); |
| 32011 | 33388 | return 1; |
| 32012 | 33389 | } |
| 32013 | 33390 | data.cMode = data.mode; |
| 32014 | 33391 | } |
| @@ -32031,11 +33408,12 @@ | ||
| 32031 | 33408 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 32032 | 33409 | if( zErrMsg || rc ){ |
| 32033 | 33410 | if( zErrMsg!=0 ){ |
| 32034 | 33411 | shellEmitError(zErrMsg); |
| 32035 | 33412 | }else{ |
| 32036 | - eputf("Error: unable to process SQL: %s\n", azCmd[i]); | |
| 33413 | + sqlite3_fprintf(stderr, | |
| 33414 | + "Error: unable to process SQL: %s\n", azCmd[i]); | |
| 32037 | 33415 | } |
| 32038 | 33416 | sqlite3_free(zErrMsg); |
| 32039 | 33417 | if( rc==0 ) rc = 1; |
| 32040 | 33418 | goto shell_main_exit; |
| 32041 | 33419 | } |
| @@ -32046,18 +33424,14 @@ | ||
| 32046 | 33424 | */ |
| 32047 | 33425 | if( stdin_is_interactive ){ |
| 32048 | 33426 | char *zHome; |
| 32049 | 33427 | char *zHistory; |
| 32050 | 33428 | int nHistory; |
| 32051 | -#if CIO_WIN_WC_XLATE | |
| 32052 | -# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "") | |
| 32053 | -#else | |
| 32054 | -# define SHELL_CIO_CHAR_SET "" | |
| 32055 | -#endif | |
| 32056 | - sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/ | |
| 33429 | + sqlite3_fprintf(stdout, | |
| 33430 | + "SQLite version %s %.19s\n" /*extra-version-info*/ | |
| 32057 | 33431 | "Enter \".help\" for usage hints.\n", |
| 32058 | - sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); | |
| 33432 | + sqlite3_libversion(), sqlite3_sourceid()); | |
| 32059 | 33433 | if( warnInmemoryDb ){ |
| 32060 | 33434 | sputz(stdout, "Connected to a "); |
| 32061 | 33435 | printBold("transient in-memory database"); |
| 32062 | 33436 | sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a" |
| 32063 | 33437 | " persistent database.\n"); |
| @@ -32070,13 +33444,15 @@ | ||
| 32070 | 33444 | if( (zHistory = malloc(nHistory))!=0 ){ |
| 32071 | 33445 | sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
| 32072 | 33446 | } |
| 32073 | 33447 | } |
| 32074 | 33448 | if( zHistory ){ shell_read_history(zHistory); } |
| 32075 | -#if HAVE_READLINE || HAVE_EDITLINE | |
| 33449 | +#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION) | |
| 32076 | 33450 | rl_attempted_completion_function = readline_completion; |
| 32077 | -#elif HAVE_LINENOISE | |
| 33451 | +#elif HAVE_LINENOISE==1 | |
| 33452 | + linenoiseSetCompletionCallback(linenoise_completion); | |
| 33453 | +#elif HAVE_LINENOISE==2 | |
| 32078 | 33454 | linenoiseSetCompletionCallback(linenoise_completion, NULL); |
| 32079 | 33455 | #endif |
| 32080 | 33456 | data.in = 0; |
| 32081 | 33457 | rc = process_input(&data); |
| 32082 | 33458 | if( zHistory ){ |
| @@ -32122,13 +33498,16 @@ | ||
| 32122 | 33498 | free(data.colWidth); |
| 32123 | 33499 | free(data.zNonce); |
| 32124 | 33500 | /* Clear the global data structure so that valgrind will detect memory |
| 32125 | 33501 | ** leaks */ |
| 32126 | 33502 | memset(&data, 0, sizeof(data)); |
| 33503 | + if( bEnableVfstrace ){ | |
| 33504 | + vfstrace_unregister("trace"); | |
| 33505 | + } | |
| 32127 | 33506 | #ifdef SQLITE_DEBUG |
| 32128 | 33507 | if( sqlite3_memory_used()>mem_main_enter ){ |
| 32129 | - eputf("Memory leaked: %u bytes\n", | |
| 33508 | + sqlite3_fprintf(stderr,"Memory leaked: %u bytes\n", | |
| 32130 | 33509 | (unsigned int)(sqlite3_memory_used()-mem_main_enter)); |
| 32131 | 33510 | } |
| 32132 | 33511 | #endif |
| 32133 | 33512 | #else /* SQLITE_SHELL_FIDDLE... */ |
| 32134 | 33513 | shell_main_exit: |
| @@ -32164,11 +33543,11 @@ | ||
| 32164 | 33543 | return pVfs; |
| 32165 | 33544 | } |
| 32166 | 33545 | |
| 32167 | 33546 | /* Only for emcc experimentation purposes. */ |
| 32168 | 33547 | sqlite3 * fiddle_db_arg(sqlite3 *arg){ |
| 32169 | - oputf("fiddle_db_arg(%p)\n", (const void*)arg); | |
| 33548 | + sqlite3_fprintf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg); | |
| 32170 | 33549 | return arg; |
| 32171 | 33550 | } |
| 32172 | 33551 | |
| 32173 | 33552 | /* |
| 32174 | 33553 | ** Intended to be called via a SharedWorker() while a separate |
| @@ -32201,11 +33580,11 @@ | ||
| 32201 | 33580 | while( sqlite3_txn_state(globalDb,0)>0 ){ |
| 32202 | 33581 | /* |
| 32203 | 33582 | ** Resolve problem reported in |
| 32204 | 33583 | ** https://sqlite.org/forum/forumpost/0b41a25d65 |
| 32205 | 33584 | */ |
| 32206 | - oputz("Rolling back in-progress transaction.\n"); | |
| 33585 | + sqlite3_fputs("Rolling back in-progress transaction.\n", stdout); | |
| 32207 | 33586 | sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0); |
| 32208 | 33587 | } |
| 32209 | 33588 | rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); |
| 32210 | 33589 | if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0); |
| 32211 | 33590 | sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); |
| 32212 | 33591 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -120,13 +120,10 @@ | |
| 120 | #include <math.h> |
| 121 | #include "sqlite3.h" |
| 122 | typedef sqlite3_int64 i64; |
| 123 | typedef sqlite3_uint64 u64; |
| 124 | typedef unsigned char u8; |
| 125 | #if SQLITE_USER_AUTHENTICATION |
| 126 | # include "sqlite3userauth.h" |
| 127 | #endif |
| 128 | #include <ctype.h> |
| 129 | #include <stdarg.h> |
| 130 | |
| 131 | #if !defined(_WIN32) && !defined(WIN32) |
| 132 | # include <signal.h> |
| @@ -208,12 +205,10 @@ | |
| 208 | # define unlink _unlink |
| 209 | # endif |
| 210 | # ifndef strdup |
| 211 | # define strdup _strdup |
| 212 | # endif |
| 213 | # undef popen |
| 214 | # define popen _popen |
| 215 | # undef pclose |
| 216 | # define pclose _pclose |
| 217 | # endif |
| 218 | #else |
| 219 | /* Make sure isatty() has a prototype. */ |
| @@ -253,10 +248,370 @@ | |
| 253 | /* string conversion routines only needed on Win32 */ |
| 254 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 255 | extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); |
| 256 | #endif |
| 257 | |
| 258 | /* Use console I/O package as a direct INCLUDE. */ |
| 259 | #define SQLITE_INTERNAL_LINKAGE static |
| 260 | |
| 261 | #ifdef SQLITE_SHELL_FIDDLE |
| 262 | /* Deselect most features from the console I/O package for Fiddle. */ |
| @@ -264,1118 +619,13 @@ | |
| 264 | # define SQLITE_CIO_NO_CLASSIFY |
| 265 | # define SQLITE_CIO_NO_TRANSLATE |
| 266 | # define SQLITE_CIO_NO_SETMODE |
| 267 | # define SQLITE_CIO_NO_FLUSH |
| 268 | #endif |
| 269 | /************************* Begin ../ext/consio/console_io.h ******************/ |
| 270 | /* |
| 271 | ** 2023 November 1 |
| 272 | ** |
| 273 | ** The author disclaims copyright to this source code. In place of |
| 274 | ** a legal notice, here is a blessing: |
| 275 | ** |
| 276 | ** May you do good and not evil. |
| 277 | ** May you find forgiveness for yourself and forgive others. |
| 278 | ** May you share freely, never taking more than you give. |
| 279 | ** |
| 280 | ******************************************************************************** |
| 281 | ** This file exposes various interfaces used for console and other I/O |
| 282 | ** by the SQLite project command-line tools. These interfaces are used |
| 283 | ** at either source conglomeration time, compilation time, or run time. |
| 284 | ** This source provides for either inclusion into conglomerated, |
| 285 | ** "single-source" forms or separate compilation then linking. |
| 286 | ** |
| 287 | ** Platform dependencies are "hidden" here by various stratagems so |
| 288 | ** that, provided certain conditions are met, the programs using this |
| 289 | ** source or object code compiled from it need no explicit conditional |
| 290 | ** compilation in their source for their console and stream I/O. |
| 291 | ** |
| 292 | ** The symbols and functionality exposed here are not a public API. |
| 293 | ** This code may change in tandem with other project code as needed. |
| 294 | ** |
| 295 | ** When this .h file and its companion .c are directly incorporated into |
| 296 | ** a source conglomeration (such as shell.c), the preprocessor symbol |
| 297 | ** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O |
| 298 | ** translation for Windows is effected for the build. |
| 299 | */ |
| 300 | #define HAVE_CONSOLE_IO_H 1 |
| 301 | #ifndef SQLITE_INTERNAL_LINKAGE |
| 302 | # define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ |
| 303 | # include <stdio.h> |
| 304 | #else |
| 305 | # define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ |
| 306 | #endif |
| 307 | |
| 308 | #ifndef SQLITE3_H |
| 309 | /* # include "sqlite3.h" */ |
| 310 | #endif |
| 311 | |
| 312 | #ifndef SQLITE_CIO_NO_CLASSIFY |
| 313 | |
| 314 | /* Define enum for use with following function. */ |
| 315 | typedef enum StreamsAreConsole { |
| 316 | SAC_NoConsole = 0, |
| 317 | SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4, |
| 318 | SAC_AnyConsole = 0x7 |
| 319 | } StreamsAreConsole; |
| 320 | |
| 321 | /* |
| 322 | ** Classify the three standard I/O streams according to whether |
| 323 | ** they are connected to a console attached to the process. |
| 324 | ** |
| 325 | ** Returns the bit-wise OR of SAC_{In,Out,Err}Console values, |
| 326 | ** or SAC_NoConsole if none of the streams reaches a console. |
| 327 | ** |
| 328 | ** This function should be called before any I/O is done with |
| 329 | ** the given streams. As a side-effect, the given inputs are |
| 330 | ** recorded so that later I/O operations on them may be done |
| 331 | ** differently than the C library FILE* I/O would be done, |
| 332 | ** iff the stream is used for the I/O functions that follow, |
| 333 | ** and to support the ones that use an implicit stream. |
| 334 | ** |
| 335 | ** On some platforms, stream or console mode alteration (aka |
| 336 | ** "Setup") may be made which is undone by consoleRestore(). |
| 337 | */ |
| 338 | SQLITE_INTERNAL_LINKAGE StreamsAreConsole |
| 339 | consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); |
| 340 | /* A usual call for convenience: */ |
| 341 | #define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr) |
| 342 | |
| 343 | /* |
| 344 | ** After an initial call to consoleClassifySetup(...), renew |
| 345 | ** the same setup it effected. (A call not after is an error.) |
| 346 | ** This will restore state altered by consoleRestore(); |
| 347 | ** |
| 348 | ** Applications which run an inferior (child) process which |
| 349 | ** inherits the same I/O streams may call this function after |
| 350 | ** such a process exits to guard against console mode changes. |
| 351 | */ |
| 352 | SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void); |
| 353 | |
| 354 | /* |
| 355 | ** Undo any side-effects left by consoleClassifySetup(...). |
| 356 | ** |
| 357 | ** This should be called after consoleClassifySetup() and |
| 358 | ** before the process terminates normally. It is suitable |
| 359 | ** for use with the atexit() C library procedure. After |
| 360 | ** this call, no console I/O should be done until one of |
| 361 | ** console{Classify or Renew}Setup(...) is called again. |
| 362 | ** |
| 363 | ** Applications which run an inferior (child) process that |
| 364 | ** inherits the same I/O streams might call this procedure |
| 365 | ** before so that said process will have a console setup |
| 366 | ** however users have configured it or come to expect. |
| 367 | */ |
| 368 | SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); |
| 369 | |
| 370 | #else /* defined(SQLITE_CIO_NO_CLASSIFY) */ |
| 371 | # define consoleClassifySetup(i,o,e) |
| 372 | # define consoleRenewSetup() |
| 373 | # define consoleRestore() |
| 374 | #endif /* defined(SQLITE_CIO_NO_CLASSIFY) */ |
| 375 | |
| 376 | #ifndef SQLITE_CIO_NO_REDIRECT |
| 377 | /* |
| 378 | ** Set stream to be used for the functions below which write |
| 379 | ** to "the designated X stream", where X is Output or Error. |
| 380 | ** Returns the previous value. |
| 381 | ** |
| 382 | ** Alternatively, pass the special value, invalidFileStream, |
| 383 | ** to get the designated stream value without setting it. |
| 384 | ** |
| 385 | ** Before the designated streams are set, they default to |
| 386 | ** those passed to consoleClassifySetup(...), and before |
| 387 | ** that is called they default to stdout and stderr. |
| 388 | ** |
| 389 | ** It is error to close a stream so designated, then, without |
| 390 | ** designating another, use the corresponding {o,e}Emit(...). |
| 391 | */ |
| 392 | SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream; |
| 393 | SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf); |
| 394 | # ifdef CONSIO_SET_ERROR_STREAM |
| 395 | SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); |
| 396 | # endif |
| 397 | #else |
| 398 | # define setOutputStream(pf) |
| 399 | # define setErrorStream(pf) |
| 400 | #endif /* !defined(SQLITE_CIO_NO_REDIRECT) */ |
| 401 | |
| 402 | #ifndef SQLITE_CIO_NO_TRANSLATE |
| 403 | /* |
| 404 | ** Emit output like fprintf(). If the output is going to the |
| 405 | ** console and translation from UTF-8 is necessary, perform |
| 406 | ** the needed translation. Otherwise, write formatted output |
| 407 | ** to the provided stream almost as-is, possibly with newline |
| 408 | ** translation as specified by set{Binary,Text}Mode(). |
| 409 | */ |
| 410 | SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...); |
| 411 | /* Like fPrintfUtf8 except stream is always the designated output. */ |
| 412 | SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...); |
| 413 | /* Like fPrintfUtf8 except stream is always the designated error. */ |
| 414 | SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...); |
| 415 | |
| 416 | /* |
| 417 | ** Emit output like fputs(). If the output is going to the |
| 418 | ** console and translation from UTF-8 is necessary, perform |
| 419 | ** the needed translation. Otherwise, write given text to the |
| 420 | ** provided stream almost as-is, possibly with newline |
| 421 | ** translation as specified by set{Binary,Text}Mode(). |
| 422 | */ |
| 423 | SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO); |
| 424 | /* Like fPutsUtf8 except stream is always the designated output. */ |
| 425 | SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z); |
| 426 | /* Like fPutsUtf8 except stream is always the designated error. */ |
| 427 | SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); |
| 428 | |
| 429 | /* |
| 430 | ** Emit output like fPutsUtf8(), except that the length of the |
| 431 | ** accepted char or character sequence is limited by nAccept. |
| 432 | ** |
| 433 | ** Returns the number of accepted char values. |
| 434 | */ |
| 435 | #ifdef CONSIO_SPUTB |
| 436 | SQLITE_INTERNAL_LINKAGE int |
| 437 | fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); |
| 438 | /* Like fPutbUtf8 except stream is always the designated output. */ |
| 439 | #endif |
| 440 | SQLITE_INTERNAL_LINKAGE int |
| 441 | oPutbUtf8(const char *cBuf, int nAccept); |
| 442 | /* Like fPutbUtf8 except stream is always the designated error. */ |
| 443 | #ifdef CONSIO_EPUTB |
| 444 | SQLITE_INTERNAL_LINKAGE int |
| 445 | ePutbUtf8(const char *cBuf, int nAccept); |
| 446 | #endif |
| 447 | |
| 448 | /* |
| 449 | ** Flush the given output stream. Return non-zero for success, else 0. |
| 450 | */ |
| 451 | #if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE) |
| 452 | SQLITE_INTERNAL_LINKAGE int |
| 453 | fFlushBuffer(FILE *pfOut); |
| 454 | #endif |
| 455 | |
| 456 | /* |
| 457 | ** Collect input like fgets(...) with special provisions for input |
| 458 | ** from the console on such platforms as require same. Newline |
| 459 | ** translation may be done as set by set{Binary,Text}Mode(). |
| 460 | ** As a convenience, pfIn==NULL is treated as stdin. |
| 461 | */ |
| 462 | SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn); |
| 463 | /* Like fGetsUtf8 except stream is always the designated input. */ |
| 464 | /* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ |
| 465 | |
| 466 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ |
| 467 | |
| 468 | #ifndef SQLITE_CIO_NO_SETMODE |
| 469 | /* |
| 470 | ** Set given stream for binary mode, where newline translation is |
| 471 | ** not done, or for text mode where, for some platforms, newlines |
| 472 | ** are translated to the platform's conventional char sequence. |
| 473 | ** If bFlush true, flush the stream. |
| 474 | ** |
| 475 | ** An additional side-effect is that if the stream is one passed |
| 476 | ** to consoleClassifySetup() as an output, it is flushed first. |
| 477 | ** |
| 478 | ** Note that binary/text mode has no effect on console I/O |
| 479 | ** translation. On all platforms, newline to the console starts |
| 480 | ** a new line and CR,LF chars from the console become a newline. |
| 481 | */ |
| 482 | SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush); |
| 483 | SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush); |
| 484 | #endif |
| 485 | |
| 486 | #ifdef SQLITE_CIO_PROMPTED_IN |
| 487 | typedef struct Prompts { |
| 488 | int numPrompts; |
| 489 | const char **azPrompts; |
| 490 | } Prompts; |
| 491 | |
| 492 | /* |
| 493 | ** Macros for use of a line editor. |
| 494 | ** |
| 495 | ** The following macros define operations involving use of a |
| 496 | ** line-editing library or simple console interaction. |
| 497 | ** A "T" argument is a text (char *) buffer or filename. |
| 498 | ** A "N" argument is an integer. |
| 499 | ** |
| 500 | ** SHELL_ADD_HISTORY(T) // Record text as line(s) of history. |
| 501 | ** SHELL_READ_HISTORY(T) // Read history from file named by T. |
| 502 | ** SHELL_WRITE_HISTORY(T) // Write history to file named by T. |
| 503 | ** SHELL_STIFLE_HISTORY(N) // Limit history to N entries. |
| 504 | ** |
| 505 | ** A console program which does interactive console input is |
| 506 | ** expected to call: |
| 507 | ** SHELL_READ_HISTORY(T) before collecting such input; |
| 508 | ** SHELL_ADD_HISTORY(T) as record-worthy input is taken; |
| 509 | ** SHELL_STIFLE_HISTORY(N) after console input ceases; then |
| 510 | ** SHELL_WRITE_HISTORY(T) before the program exits. |
| 511 | */ |
| 512 | |
| 513 | /* |
| 514 | ** Retrieve a single line of input text from an input stream. |
| 515 | ** |
| 516 | ** If pfIn is the input stream passed to consoleClassifySetup(), |
| 517 | ** and azPrompt is not NULL, then a prompt is issued before the |
| 518 | ** line is collected, as selected by the isContinuation flag. |
| 519 | ** Array azPrompt[{0,1}] holds the {main,continuation} prompt. |
| 520 | ** |
| 521 | ** If zBufPrior is not NULL then it is a buffer from a prior |
| 522 | ** call to this routine that can be reused, or will be freed. |
| 523 | ** |
| 524 | ** The result is stored in space obtained from malloc() and |
| 525 | ** must either be freed by the caller or else passed back to |
| 526 | ** this function as zBufPrior for reuse. |
| 527 | ** |
| 528 | ** This function may call upon services of a line-editing |
| 529 | ** library to interactively collect line edited input. |
| 530 | */ |
| 531 | SQLITE_INTERNAL_LINKAGE char * |
| 532 | shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, |
| 533 | short isContinuation, Prompts azPrompt); |
| 534 | #endif /* defined(SQLITE_CIO_PROMPTED_IN) */ |
| 535 | /* |
| 536 | ** TBD: Define an interface for application(s) to generate |
| 537 | ** completion candidates for use by the line-editor. |
| 538 | ** |
| 539 | ** This may be premature; the CLI is the only application |
| 540 | ** that does this. Yet, getting line-editing melded into |
| 541 | ** console I/O is desirable because a line-editing library |
| 542 | ** may have to establish console operating mode, possibly |
| 543 | ** in a way that interferes with the above functionality. |
| 544 | */ |
| 545 | |
| 546 | #if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE)) |
| 547 | /* Skip over as much z[] input char sequence as is valid UTF-8, |
| 548 | ** limited per nAccept char's or whole characters and containing |
| 549 | ** no char cn such that ((1<<cn) & ccm)!=0. On return, the |
| 550 | ** sequence z:return (inclusive:exclusive) is validated UTF-8. |
| 551 | ** Limit: nAccept>=0 => char count, nAccept<0 => character |
| 552 | */ |
| 553 | SQLITE_INTERNAL_LINKAGE const char* |
| 554 | zSkipValidUtf8(const char *z, int nAccept, long ccm); |
| 555 | |
| 556 | #endif |
| 557 | |
| 558 | /************************* End ../ext/consio/console_io.h ********************/ |
| 559 | /************************* Begin ../ext/consio/console_io.c ******************/ |
| 560 | /* |
| 561 | ** 2023 November 4 |
| 562 | ** |
| 563 | ** The author disclaims copyright to this source code. In place of |
| 564 | ** a legal notice, here is a blessing: |
| 565 | ** |
| 566 | ** May you do good and not evil. |
| 567 | ** May you find forgiveness for yourself and forgive others. |
| 568 | ** May you share freely, never taking more than you give. |
| 569 | ** |
| 570 | ******************************************************************************** |
| 571 | ** This file implements various interfaces used for console and stream I/O |
| 572 | ** by the SQLite project command-line tools, as explained in console_io.h . |
| 573 | ** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there. |
| 574 | */ |
| 575 | |
| 576 | #ifndef SQLITE_CDECL |
| 577 | # define SQLITE_CDECL |
| 578 | #endif |
| 579 | |
| 580 | #ifndef SHELL_NO_SYSINC |
| 581 | # include <stdarg.h> |
| 582 | # include <string.h> |
| 583 | # include <stdlib.h> |
| 584 | # include <limits.h> |
| 585 | # include <assert.h> |
| 586 | /* # include "sqlite3.h" */ |
| 587 | #endif |
| 588 | #ifndef HAVE_CONSOLE_IO_H |
| 589 | # include "console_io.h" |
| 590 | #endif |
| 591 | #if defined(_MSC_VER) |
| 592 | # pragma warning(disable : 4204) |
| 593 | #endif |
| 594 | |
| 595 | #ifndef SQLITE_CIO_NO_TRANSLATE |
| 596 | # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT |
| 597 | # ifndef SHELL_NO_SYSINC |
| 598 | # include <io.h> |
| 599 | # include <fcntl.h> |
| 600 | # undef WIN32_LEAN_AND_MEAN |
| 601 | # define WIN32_LEAN_AND_MEAN |
| 602 | # include <windows.h> |
| 603 | # endif |
| 604 | # define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */ |
| 605 | # else |
| 606 | # ifndef SHELL_NO_SYSINC |
| 607 | # include <unistd.h> |
| 608 | # endif |
| 609 | # define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ |
| 610 | # endif |
| 611 | #else |
| 612 | # define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ |
| 613 | #endif |
| 614 | |
| 615 | #if CIO_WIN_WC_XLATE |
| 616 | static HANDLE handleOfFile(FILE *pf){ |
| 617 | int fileDesc = _fileno(pf); |
| 618 | union { intptr_t osfh; HANDLE fh; } fid = { |
| 619 | (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE |
| 620 | }; |
| 621 | return fid.fh; |
| 622 | } |
| 623 | #endif |
| 624 | |
| 625 | #ifndef SQLITE_CIO_NO_TRANSLATE |
| 626 | typedef struct PerStreamTags { |
| 627 | # if CIO_WIN_WC_XLATE |
| 628 | HANDLE hx; |
| 629 | DWORD consMode; |
| 630 | char acIncomplete[4]; |
| 631 | # else |
| 632 | short reachesConsole; |
| 633 | # endif |
| 634 | FILE *pf; |
| 635 | } PerStreamTags; |
| 636 | |
| 637 | /* Define NULL-like value for things which can validly be 0. */ |
| 638 | # define SHELL_INVALID_FILE_PTR ((FILE *)~0) |
| 639 | # if CIO_WIN_WC_XLATE |
| 640 | # define SHELL_INVALID_CONS_MODE 0xFFFF0000 |
| 641 | # endif |
| 642 | |
| 643 | # if CIO_WIN_WC_XLATE |
| 644 | # define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ |
| 645 | {0,0,0,0}, SHELL_INVALID_FILE_PTR } |
| 646 | # else |
| 647 | # define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } |
| 648 | # endif |
| 649 | |
| 650 | /* Quickly say whether a known output is going to the console. */ |
| 651 | # if CIO_WIN_WC_XLATE |
| 652 | static short pstReachesConsole(PerStreamTags *ppst){ |
| 653 | return (ppst->hx != INVALID_HANDLE_VALUE); |
| 654 | } |
| 655 | # else |
| 656 | # define pstReachesConsole(ppst) 0 |
| 657 | # endif |
| 658 | |
| 659 | # if CIO_WIN_WC_XLATE |
| 660 | static void restoreConsoleArb(PerStreamTags *ppst){ |
| 661 | if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode); |
| 662 | } |
| 663 | # else |
| 664 | # define restoreConsoleArb(ppst) |
| 665 | # endif |
| 666 | |
| 667 | /* Say whether FILE* appears to be a console, collect associated info. */ |
| 668 | static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){ |
| 669 | # if CIO_WIN_WC_XLATE |
| 670 | short rv = 0; |
| 671 | DWORD dwCM = SHELL_INVALID_CONS_MODE; |
| 672 | HANDLE fh = handleOfFile(pf); |
| 673 | ppst->pf = pf; |
| 674 | if( INVALID_HANDLE_VALUE != fh ){ |
| 675 | rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM)); |
| 676 | } |
| 677 | ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE; |
| 678 | ppst->consMode = dwCM; |
| 679 | return rv; |
| 680 | # else |
| 681 | ppst->pf = pf; |
| 682 | ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); |
| 683 | return ppst->reachesConsole; |
| 684 | # endif |
| 685 | } |
| 686 | |
| 687 | # ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING |
| 688 | # define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4) |
| 689 | # endif |
| 690 | |
| 691 | # if CIO_WIN_WC_XLATE |
| 692 | /* Define console modes for use with the Windows Console API. */ |
| 693 | # define SHELL_CONI_MODE \ |
| 694 | (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ |
| 695 | | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) |
| 696 | # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ |
| 697 | | ENABLE_VIRTUAL_TERMINAL_PROCESSING) |
| 698 | # endif |
| 699 | |
| 700 | typedef struct ConsoleInfo { |
| 701 | PerStreamTags pstSetup[3]; |
| 702 | PerStreamTags pstDesignated[3]; |
| 703 | StreamsAreConsole sacSetup; |
| 704 | } ConsoleInfo; |
| 705 | |
| 706 | static short isValidStreamInfo(PerStreamTags *ppst){ |
| 707 | return (ppst->pf != SHELL_INVALID_FILE_PTR); |
| 708 | } |
| 709 | |
| 710 | static ConsoleInfo consoleInfo = { |
| 711 | { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, |
| 712 | { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, |
| 713 | SAC_NoConsole /* sacSetup */ |
| 714 | }; |
| 715 | |
| 716 | SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; |
| 717 | |
| 718 | # if CIO_WIN_WC_XLATE |
| 719 | static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ |
| 720 | if( pstReachesConsole(ppst) ){ |
| 721 | DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; |
| 722 | SetConsoleMode(ppst->hx, cm); |
| 723 | } |
| 724 | } |
| 725 | # else |
| 726 | # define maybeSetupAsConsole(ppst,odir) |
| 727 | # endif |
| 728 | |
| 729 | SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ |
| 730 | # if CIO_WIN_WC_XLATE |
| 731 | int ix = 0; |
| 732 | while( ix < 6 ){ |
| 733 | PerStreamTags *ppst = (ix<3)? |
| 734 | &consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3]; |
| 735 | maybeSetupAsConsole(ppst, (ix % 3)>0); |
| 736 | ++ix; |
| 737 | } |
| 738 | # endif |
| 739 | } |
| 740 | |
| 741 | SQLITE_INTERNAL_LINKAGE StreamsAreConsole |
| 742 | consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ |
| 743 | StreamsAreConsole rv = SAC_NoConsole; |
| 744 | FILE* apf[3] = { pfIn, pfOut, pfErr }; |
| 745 | int ix; |
| 746 | for( ix = 2; ix >= 0; --ix ){ |
| 747 | PerStreamTags *ppst = &consoleInfo.pstSetup[ix]; |
| 748 | if( streamOfConsole(apf[ix], ppst) ){ |
| 749 | rv |= (SAC_InConsole<<ix); |
| 750 | } |
| 751 | consoleInfo.pstDesignated[ix] = *ppst; |
| 752 | if( ix > 0 ) fflush(apf[ix]); |
| 753 | } |
| 754 | consoleInfo.sacSetup = rv; |
| 755 | consoleRenewSetup(); |
| 756 | return rv; |
| 757 | } |
| 758 | |
| 759 | SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ |
| 760 | # if CIO_WIN_WC_XLATE |
| 761 | static ConsoleInfo *pci = &consoleInfo; |
| 762 | if( pci->sacSetup ){ |
| 763 | int ix; |
| 764 | for( ix=0; ix<3; ++ix ){ |
| 765 | if( pci->sacSetup & (SAC_InConsole<<ix) ){ |
| 766 | PerStreamTags *ppst = &pci->pstSetup[ix]; |
| 767 | SetConsoleMode(ppst->hx, ppst->consMode); |
| 768 | } |
| 769 | } |
| 770 | } |
| 771 | # endif |
| 772 | } |
| 773 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ |
| 774 | |
| 775 | #ifdef SQLITE_CIO_INPUT_REDIR |
| 776 | /* Say whether given FILE* is among those known, via either |
| 777 | ** consoleClassifySetup() or set{Output,Error}Stream, as |
| 778 | ** readable, and return an associated PerStreamTags pointer |
| 779 | ** if so. Otherwise, return 0. |
| 780 | */ |
| 781 | static PerStreamTags * isKnownReadable(FILE *pf){ |
| 782 | static PerStreamTags *apst[] = { |
| 783 | &consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0 |
| 784 | }; |
| 785 | int ix = 0; |
| 786 | do { |
| 787 | if( apst[ix]->pf == pf ) break; |
| 788 | } while( apst[++ix] != 0 ); |
| 789 | return apst[ix]; |
| 790 | } |
| 791 | #endif |
| 792 | |
| 793 | #ifndef SQLITE_CIO_NO_TRANSLATE |
| 794 | /* Say whether given FILE* is among those known, via either |
| 795 | ** consoleClassifySetup() or set{Output,Error}Stream, as |
| 796 | ** writable, and return an associated PerStreamTags pointer |
| 797 | ** if so. Otherwise, return 0. |
| 798 | */ |
| 799 | static PerStreamTags * isKnownWritable(FILE *pf){ |
| 800 | static PerStreamTags *apst[] = { |
| 801 | &consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2], |
| 802 | &consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0 |
| 803 | }; |
| 804 | int ix = 0; |
| 805 | do { |
| 806 | if( apst[ix]->pf == pf ) break; |
| 807 | } while( apst[++ix] != 0 ); |
| 808 | return apst[ix]; |
| 809 | } |
| 810 | |
| 811 | static FILE *designateEmitStream(FILE *pf, unsigned chix){ |
| 812 | FILE *rv = consoleInfo.pstDesignated[chix].pf; |
| 813 | if( pf == invalidFileStream ) return rv; |
| 814 | else{ |
| 815 | /* Setting a possibly new output stream. */ |
| 816 | PerStreamTags *ppst = isKnownWritable(pf); |
| 817 | if( ppst != 0 ){ |
| 818 | PerStreamTags pst = *ppst; |
| 819 | consoleInfo.pstDesignated[chix] = pst; |
| 820 | }else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]); |
| 821 | } |
| 822 | return rv; |
| 823 | } |
| 824 | |
| 825 | SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){ |
| 826 | return designateEmitStream(pf, 1); |
| 827 | } |
| 828 | # ifdef CONSIO_SET_ERROR_STREAM |
| 829 | SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ |
| 830 | return designateEmitStream(pf, 2); |
| 831 | } |
| 832 | # endif |
| 833 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ |
| 834 | |
| 835 | #ifndef SQLITE_CIO_NO_SETMODE |
| 836 | # if CIO_WIN_WC_XLATE |
| 837 | static void setModeFlushQ(FILE *pf, short bFlush, int mode){ |
| 838 | if( bFlush ) fflush(pf); |
| 839 | _setmode(_fileno(pf), mode); |
| 840 | } |
| 841 | # else |
| 842 | # define setModeFlushQ(f, b, m) if(b) fflush(f) |
| 843 | # endif |
| 844 | |
| 845 | SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ |
| 846 | setModeFlushQ(pf, bFlush, _O_BINARY); |
| 847 | } |
| 848 | SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ |
| 849 | setModeFlushQ(pf, bFlush, _O_TEXT); |
| 850 | } |
| 851 | # undef setModeFlushQ |
| 852 | |
| 853 | #else /* defined(SQLITE_CIO_NO_SETMODE) */ |
| 854 | # define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) |
| 855 | # define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) |
| 856 | #endif /* defined(SQLITE_CIO_NO_SETMODE) */ |
| 857 | |
| 858 | #ifndef SQLITE_CIO_NO_TRANSLATE |
| 859 | # if CIO_WIN_WC_XLATE |
| 860 | /* Write buffer cBuf as output to stream known to reach console, |
| 861 | ** limited to ncTake char's. Return ncTake on success, else 0. */ |
| 862 | static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ |
| 863 | int rv = 0; |
| 864 | if( z!=NULL ){ |
| 865 | int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0); |
| 866 | if( nwc > 0 ){ |
| 867 | WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); |
| 868 | if( zw!=NULL ){ |
| 869 | nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc); |
| 870 | if( nwc > 0 ){ |
| 871 | /* Translation from UTF-8 to UTF-16, then WCHARs out. */ |
| 872 | if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ |
| 873 | rv = ncTake; |
| 874 | } |
| 875 | } |
| 876 | sqlite3_free(zw); |
| 877 | } |
| 878 | } |
| 879 | } |
| 880 | return rv; |
| 881 | } |
| 882 | |
| 883 | /* For {f,o,e}PrintfUtf8() when stream is known to reach console. */ |
| 884 | static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ |
| 885 | char *z = sqlite3_vmprintf(zFormat, ap); |
| 886 | if( z ){ |
| 887 | int rv = conZstrEmit(ppst, z, (int)strlen(z)); |
| 888 | sqlite3_free(z); |
| 889 | return rv; |
| 890 | }else return 0; |
| 891 | } |
| 892 | # endif /* CIO_WIN_WC_XLATE */ |
| 893 | |
| 894 | # ifdef CONSIO_GET_EMIT_STREAM |
| 895 | static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, |
| 896 | PerStreamTags *ppst){ |
| 897 | PerStreamTags *rv = isKnownWritable(pf); |
| 898 | short isValid = (rv!=0)? isValidStreamInfo(rv) : 0; |
| 899 | if( rv != 0 && isValid ) return rv; |
| 900 | streamOfConsole(pf, ppst); |
| 901 | return ppst; |
| 902 | } |
| 903 | # endif |
| 904 | |
| 905 | /* Get stream info, either for designated output or error stream when |
| 906 | ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. |
| 907 | ** In either case, ppst references a caller-owned PerStreamTags |
| 908 | ** struct which may be filled in if none of the known writable |
| 909 | ** streams is being held by consoleInfo. The ppf parameter is a |
| 910 | ** byref output when chix!=0 and a byref input when chix==0. |
| 911 | */ |
| 912 | static PerStreamTags * |
| 913 | getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, |
| 914 | /* in/out */ FILE **ppf){ |
| 915 | PerStreamTags *ppstTry; |
| 916 | FILE *pfEmit; |
| 917 | if( chix > 0 ){ |
| 918 | ppstTry = &consoleInfo.pstDesignated[chix]; |
| 919 | if( !isValidStreamInfo(ppstTry) ){ |
| 920 | ppstTry = &consoleInfo.pstSetup[chix]; |
| 921 | pfEmit = ppst->pf; |
| 922 | }else pfEmit = ppstTry->pf; |
| 923 | if( !isValidStreamInfo(ppstTry) ){ |
| 924 | pfEmit = (chix > 1)? stderr : stdout; |
| 925 | ppstTry = ppst; |
| 926 | streamOfConsole(pfEmit, ppstTry); |
| 927 | } |
| 928 | *ppf = pfEmit; |
| 929 | }else{ |
| 930 | ppstTry = isKnownWritable(*ppf); |
| 931 | if( ppstTry != 0 ) return ppstTry; |
| 932 | streamOfConsole(*ppf, ppst); |
| 933 | return ppst; |
| 934 | } |
| 935 | return ppstTry; |
| 936 | } |
| 937 | |
| 938 | SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){ |
| 939 | va_list ap; |
| 940 | int rv; |
| 941 | FILE *pfOut; |
| 942 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 943 | # if CIO_WIN_WC_XLATE |
| 944 | PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); |
| 945 | # else |
| 946 | getEmitStreamInfo(1, &pst, &pfOut); |
| 947 | # endif |
| 948 | assert(zFormat!=0); |
| 949 | va_start(ap, zFormat); |
| 950 | # if CIO_WIN_WC_XLATE |
| 951 | if( pstReachesConsole(ppst) ){ |
| 952 | rv = conioVmPrintf(ppst, zFormat, ap); |
| 953 | }else{ |
| 954 | # endif |
| 955 | rv = vfprintf(pfOut, zFormat, ap); |
| 956 | # if CIO_WIN_WC_XLATE |
| 957 | } |
| 958 | # endif |
| 959 | va_end(ap); |
| 960 | return rv; |
| 961 | } |
| 962 | |
| 963 | SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ |
| 964 | va_list ap; |
| 965 | int rv; |
| 966 | FILE *pfErr; |
| 967 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 968 | # if CIO_WIN_WC_XLATE |
| 969 | PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); |
| 970 | # else |
| 971 | getEmitStreamInfo(2, &pst, &pfErr); |
| 972 | # endif |
| 973 | assert(zFormat!=0); |
| 974 | va_start(ap, zFormat); |
| 975 | # if CIO_WIN_WC_XLATE |
| 976 | if( pstReachesConsole(ppst) ){ |
| 977 | rv = conioVmPrintf(ppst, zFormat, ap); |
| 978 | }else{ |
| 979 | # endif |
| 980 | rv = vfprintf(pfErr, zFormat, ap); |
| 981 | # if CIO_WIN_WC_XLATE |
| 982 | } |
| 983 | # endif |
| 984 | va_end(ap); |
| 985 | return rv; |
| 986 | } |
| 987 | |
| 988 | SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ |
| 989 | va_list ap; |
| 990 | int rv; |
| 991 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 992 | # if CIO_WIN_WC_XLATE |
| 993 | PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); |
| 994 | # else |
| 995 | getEmitStreamInfo(0, &pst, &pfO); |
| 996 | # endif |
| 997 | assert(zFormat!=0); |
| 998 | va_start(ap, zFormat); |
| 999 | # if CIO_WIN_WC_XLATE |
| 1000 | if( pstReachesConsole(ppst) ){ |
| 1001 | maybeSetupAsConsole(ppst, 1); |
| 1002 | rv = conioVmPrintf(ppst, zFormat, ap); |
| 1003 | if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); |
| 1004 | }else{ |
| 1005 | # endif |
| 1006 | rv = vfprintf(pfO, zFormat, ap); |
| 1007 | # if CIO_WIN_WC_XLATE |
| 1008 | } |
| 1009 | # endif |
| 1010 | va_end(ap); |
| 1011 | return rv; |
| 1012 | } |
| 1013 | |
| 1014 | SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ |
| 1015 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 1016 | # if CIO_WIN_WC_XLATE |
| 1017 | PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); |
| 1018 | # else |
| 1019 | getEmitStreamInfo(0, &pst, &pfO); |
| 1020 | # endif |
| 1021 | assert(z!=0); |
| 1022 | # if CIO_WIN_WC_XLATE |
| 1023 | if( pstReachesConsole(ppst) ){ |
| 1024 | int rv; |
| 1025 | maybeSetupAsConsole(ppst, 1); |
| 1026 | rv = conZstrEmit(ppst, z, (int)strlen(z)); |
| 1027 | if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); |
| 1028 | return rv; |
| 1029 | }else { |
| 1030 | # endif |
| 1031 | return (fputs(z, pfO)<0)? 0 : (int)strlen(z); |
| 1032 | # if CIO_WIN_WC_XLATE |
| 1033 | } |
| 1034 | # endif |
| 1035 | } |
| 1036 | |
| 1037 | SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ |
| 1038 | FILE *pfErr; |
| 1039 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 1040 | # if CIO_WIN_WC_XLATE |
| 1041 | PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); |
| 1042 | # else |
| 1043 | getEmitStreamInfo(2, &pst, &pfErr); |
| 1044 | # endif |
| 1045 | assert(z!=0); |
| 1046 | # if CIO_WIN_WC_XLATE |
| 1047 | if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); |
| 1048 | else { |
| 1049 | # endif |
| 1050 | return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); |
| 1051 | # if CIO_WIN_WC_XLATE |
| 1052 | } |
| 1053 | # endif |
| 1054 | } |
| 1055 | |
| 1056 | SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ |
| 1057 | FILE *pfOut; |
| 1058 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 1059 | # if CIO_WIN_WC_XLATE |
| 1060 | PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); |
| 1061 | # else |
| 1062 | getEmitStreamInfo(1, &pst, &pfOut); |
| 1063 | # endif |
| 1064 | assert(z!=0); |
| 1065 | # if CIO_WIN_WC_XLATE |
| 1066 | if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); |
| 1067 | else { |
| 1068 | # endif |
| 1069 | return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); |
| 1070 | # if CIO_WIN_WC_XLATE |
| 1071 | } |
| 1072 | # endif |
| 1073 | } |
| 1074 | |
| 1075 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ |
| 1076 | |
| 1077 | #if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE)) |
| 1078 | /* Skip over as much z[] input char sequence as is valid UTF-8, |
| 1079 | ** limited per nAccept char's or whole characters and containing |
| 1080 | ** no char cn such that ((1<<cn) & ccm)!=0. On return, the |
| 1081 | ** sequence z:return (inclusive:exclusive) is validated UTF-8. |
| 1082 | ** Limit: nAccept>=0 => char count, nAccept<0 => character |
| 1083 | */ |
| 1084 | SQLITE_INTERNAL_LINKAGE const char* |
| 1085 | zSkipValidUtf8(const char *z, int nAccept, long ccm){ |
| 1086 | int ng = (nAccept<0)? -nAccept : 0; |
| 1087 | const char *pcLimit = (nAccept>=0)? z+nAccept : 0; |
| 1088 | assert(z!=0); |
| 1089 | while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){ |
| 1090 | char c = *z; |
| 1091 | if( (c & 0x80) == 0 ){ |
| 1092 | if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z; |
| 1093 | ++z; /* ASCII */ |
| 1094 | }else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */ |
| 1095 | else{ |
| 1096 | const char *zt = z+1; /* Got lead byte, look at trail bytes.*/ |
| 1097 | do{ |
| 1098 | if( pcLimit && zt >= pcLimit ) return z; |
| 1099 | else{ |
| 1100 | char ct = *zt++; |
| 1101 | if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ |
| 1102 | /* Trailing bytes are too few, too many, or invalid. */ |
| 1103 | return z; |
| 1104 | } |
| 1105 | } |
| 1106 | } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ |
| 1107 | z = zt; |
| 1108 | } |
| 1109 | } |
| 1110 | return z; |
| 1111 | } |
| 1112 | #endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ |
| 1113 | |
| 1114 | #ifndef SQLITE_CIO_NO_TRANSLATE |
| 1115 | # ifdef CONSIO_SPUTB |
| 1116 | SQLITE_INTERNAL_LINKAGE int |
| 1117 | fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ |
| 1118 | assert(pfO!=0); |
| 1119 | # if CIO_WIN_WC_XLATE |
| 1120 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 1121 | PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); |
| 1122 | if( pstReachesConsole(ppst) ){ |
| 1123 | int rv; |
| 1124 | maybeSetupAsConsole(ppst, 1); |
| 1125 | rv = conZstrEmit(ppst, cBuf, nAccept); |
| 1126 | if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); |
| 1127 | return rv; |
| 1128 | }else { |
| 1129 | # endif |
| 1130 | return (int)fwrite(cBuf, 1, nAccept, pfO); |
| 1131 | # if CIO_WIN_WC_XLATE |
| 1132 | } |
| 1133 | # endif |
| 1134 | } |
| 1135 | # endif |
| 1136 | |
| 1137 | SQLITE_INTERNAL_LINKAGE int |
| 1138 | oPutbUtf8(const char *cBuf, int nAccept){ |
| 1139 | FILE *pfOut; |
| 1140 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 1141 | # if CIO_WIN_WC_XLATE |
| 1142 | PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); |
| 1143 | # else |
| 1144 | getEmitStreamInfo(1, &pst, &pfOut); |
| 1145 | # endif |
| 1146 | # if CIO_WIN_WC_XLATE |
| 1147 | if( pstReachesConsole(ppst) ){ |
| 1148 | return conZstrEmit(ppst, cBuf, nAccept); |
| 1149 | }else { |
| 1150 | # endif |
| 1151 | return (int)fwrite(cBuf, 1, nAccept, pfOut); |
| 1152 | # if CIO_WIN_WC_XLATE |
| 1153 | } |
| 1154 | # endif |
| 1155 | } |
| 1156 | |
| 1157 | /* |
| 1158 | ** Flush the given output stream. Return non-zero for success, else 0. |
| 1159 | */ |
| 1160 | #if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE) |
| 1161 | SQLITE_INTERNAL_LINKAGE int |
| 1162 | fFlushBuffer(FILE *pfOut){ |
| 1163 | # if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE) |
| 1164 | return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0; |
| 1165 | # else |
| 1166 | return fflush(pfOut); |
| 1167 | # endif |
| 1168 | } |
| 1169 | #endif |
| 1170 | |
| 1171 | #if CIO_WIN_WC_XLATE \ |
| 1172 | && !defined(SHELL_OMIT_FIO_DUPE) \ |
| 1173 | && defined(SQLITE_USE_ONLY_WIN32) |
| 1174 | static struct FileAltIds { |
| 1175 | int fd; |
| 1176 | HANDLE fh; |
| 1177 | } altIdsOfFile(FILE *pf){ |
| 1178 | struct FileAltIds rv = { _fileno(pf) }; |
| 1179 | union { intptr_t osfh; HANDLE fh; } fid = { |
| 1180 | (rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE |
| 1181 | }; |
| 1182 | rv.fh = fid.fh; |
| 1183 | return rv; |
| 1184 | } |
| 1185 | |
| 1186 | SQLITE_INTERNAL_LINKAGE size_t |
| 1187 | cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){ |
| 1188 | size_t rv = 0; |
| 1189 | struct FileAltIds fai = altIdsOfFile(pf); |
| 1190 | int fmode = _setmode(fai.fd, _O_BINARY); |
| 1191 | _setmode(fai.fd, fmode); |
| 1192 | while( rv < ocnt ){ |
| 1193 | size_t nbo = osz; |
| 1194 | while( nbo > 0 ){ |
| 1195 | DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo; |
| 1196 | BOOL wrc = TRUE; |
| 1197 | BOOL genCR = (fmode & _O_TEXT)!=0; |
| 1198 | if( genCR ){ |
| 1199 | const char *pnl = (const char*)memchr(buf, '\n', nbo); |
| 1200 | if( pnl ) nbo = pnl - (const char*)buf; |
| 1201 | else genCR = 0; |
| 1202 | } |
| 1203 | if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0); |
| 1204 | if( genCR && wrc ){ |
| 1205 | wrc = WriteFile(fai.fh, "\r\n", 2, 0,0); |
| 1206 | ++dwno; /* Skip over the LF */ |
| 1207 | } |
| 1208 | if( !wrc ) return rv; |
| 1209 | buf = (const char*)buf + dwno; |
| 1210 | nbo += dwno; |
| 1211 | } |
| 1212 | ++rv; |
| 1213 | } |
| 1214 | return rv; |
| 1215 | } |
| 1216 | |
| 1217 | SQLITE_INTERNAL_LINKAGE char * |
| 1218 | cfGets(char *cBuf, int n, FILE *pf){ |
| 1219 | int nci = 0; |
| 1220 | struct FileAltIds fai = altIdsOfFile(pf); |
| 1221 | int fmode = _setmode(fai.fd, _O_BINARY); |
| 1222 | BOOL eatCR = (fmode & _O_TEXT)!=0; |
| 1223 | _setmode(fai.fd, fmode); |
| 1224 | while( nci < n-1 ){ |
| 1225 | DWORD nr; |
| 1226 | if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break; |
| 1227 | if( nr>0 && (!eatCR || cBuf[nci]!='\r') ) nci += nr; |
| 1228 | } |
| 1229 | if( nci < n ) cBuf[nci] = 0; |
| 1230 | return (nci>0)? cBuf : 0; |
| 1231 | } |
| 1232 | # else |
| 1233 | # define cfWrite(b,os,no,f) fwrite(b,os,no,f) |
| 1234 | # define cfGets(b,n,f) fgets(b,n,f) |
| 1235 | # endif |
| 1236 | |
| 1237 | # ifdef CONSIO_EPUTB |
| 1238 | SQLITE_INTERNAL_LINKAGE int |
| 1239 | ePutbUtf8(const char *cBuf, int nAccept){ |
| 1240 | FILE *pfErr; |
| 1241 | PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ |
| 1242 | PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); |
| 1243 | # if CIO_WIN_WC_XLATE |
| 1244 | if( pstReachesConsole(ppst) ){ |
| 1245 | return conZstrEmit(ppst, cBuf, nAccept); |
| 1246 | }else { |
| 1247 | # endif |
| 1248 | return (int)cfWrite(cBuf, 1, nAccept, pfErr); |
| 1249 | # if CIO_WIN_WC_XLATE |
| 1250 | } |
| 1251 | # endif |
| 1252 | } |
| 1253 | # endif /* defined(CONSIO_EPUTB) */ |
| 1254 | |
| 1255 | SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ |
| 1256 | if( pfIn==0 ) pfIn = stdin; |
| 1257 | # if CIO_WIN_WC_XLATE |
| 1258 | if( pfIn == consoleInfo.pstSetup[0].pf |
| 1259 | && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ |
| 1260 | # if CIO_WIN_WC_XLATE==1 |
| 1261 | # define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ |
| 1262 | WCHAR wcBuf[SHELL_GULP+1]; |
| 1263 | int lend = 0, noc = 0; |
| 1264 | if( ncMax > 0 ) cBuf[0] = 0; |
| 1265 | while( noc < ncMax-8-1 && !lend ){ |
| 1266 | /* There is room for at least 2 more characters and a 0-terminator. */ |
| 1267 | int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; |
| 1268 | # undef SHELL_GULP |
| 1269 | DWORD nbr = 0; |
| 1270 | BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0); |
| 1271 | if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ |
| 1272 | /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */ |
| 1273 | DWORD nbrx; |
| 1274 | bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0); |
| 1275 | if( bRC ) nbr += nbrx; |
| 1276 | } |
| 1277 | if( !bRC || (noc==0 && nbr==0) ) return 0; |
| 1278 | if( nbr > 0 ){ |
| 1279 | int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); |
| 1280 | if( nmb != 0 && noc+nmb <= ncMax ){ |
| 1281 | int iseg = noc; |
| 1282 | nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0); |
| 1283 | noc += nmb; |
| 1284 | /* Fixup line-ends as coded by Windows for CR (or "Enter".) |
| 1285 | ** This is done without regard for any setMode{Text,Binary}() |
| 1286 | ** call that might have been done on the interactive input. |
| 1287 | */ |
| 1288 | if( noc > 0 ){ |
| 1289 | if( cBuf[noc-1]=='\n' ){ |
| 1290 | lend = 1; |
| 1291 | if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n'; |
| 1292 | } |
| 1293 | } |
| 1294 | /* Check for ^Z (anywhere in line) too, to act as EOF. */ |
| 1295 | while( iseg < noc ){ |
| 1296 | if( cBuf[iseg]=='\x1a' ){ |
| 1297 | noc = iseg; /* Chop ^Z and anything following. */ |
| 1298 | lend = 1; /* Counts as end of line too. */ |
| 1299 | break; |
| 1300 | } |
| 1301 | ++iseg; |
| 1302 | } |
| 1303 | }else break; /* Drop apparent garbage in. (Could assert.) */ |
| 1304 | }else break; |
| 1305 | } |
| 1306 | /* If got nothing, (after ^Z chop), must be at end-of-file. */ |
| 1307 | if( noc > 0 ){ |
| 1308 | cBuf[noc] = 0; |
| 1309 | return cBuf; |
| 1310 | }else return 0; |
| 1311 | # endif |
| 1312 | }else{ |
| 1313 | # endif |
| 1314 | return cfGets(cBuf, ncMax, pfIn); |
| 1315 | # if CIO_WIN_WC_XLATE |
| 1316 | } |
| 1317 | # endif |
| 1318 | } |
| 1319 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ |
| 1320 | |
| 1321 | #if defined(_MSC_VER) |
| 1322 | # pragma warning(default : 4204) |
| 1323 | #endif |
| 1324 | |
| 1325 | #undef SHELL_INVALID_FILE_PTR |
| 1326 | |
| 1327 | /************************* End ../ext/consio/console_io.c ********************/ |
| 1328 | |
| 1329 | #ifndef SQLITE_SHELL_FIDDLE |
| 1330 | |
| 1331 | /* From here onward, fgets() is redirected to the console_io library. */ |
| 1332 | # define fgets(b,n,f) fGetsUtf8(b,n,f) |
| 1333 | /* |
| 1334 | * Define macros for emitting output text in various ways: |
| 1335 | * sputz(s, z) => emit 0-terminated string z to given stream s |
| 1336 | * sputf(s, f, ...) => emit varargs per format f to given stream s |
| 1337 | * oputz(z) => emit 0-terminated string z to default stream |
| 1338 | * oputf(f, ...) => emit varargs per format f to default stream |
| 1339 | * eputz(z) => emit 0-terminated string z to error stream |
| 1340 | * eputf(f, ...) => emit varargs per format f to error stream |
| 1341 | * oputb(b, n) => emit char buffer b[0..n-1] to default stream |
| 1342 | * |
| 1343 | * Note that the default stream is whatever has been last set via: |
| 1344 | * setOutputStream(FILE *pf) |
| 1345 | * This is normally the stream that CLI normal output goes to. |
| 1346 | * For the stand-alone CLI, it is stdout with no .output redirect. |
| 1347 | * |
| 1348 | * The ?putz(z) forms are required for the Fiddle builds for string literal |
| 1349 | * output, in aid of enforcing format string to argument correspondence. |
| 1350 | */ |
| 1351 | # define sputz(s,z) fPutsUtf8(z,s) |
| 1352 | # define sputf fPrintfUtf8 |
| 1353 | # define oputz(z) oPutsUtf8(z) |
| 1354 | # define oputf oPrintfUtf8 |
| 1355 | # define eputz(z) ePutsUtf8(z) |
| 1356 | # define eputf ePrintfUtf8 |
| 1357 | # define oputb(buf,na) oPutbUtf8(buf,na) |
| 1358 | # define fflush(s) fFlushBuffer(s); |
| 1359 | |
| 1360 | #else |
| 1361 | /* For Fiddle, all console handling and emit redirection is omitted. */ |
| 1362 | /* These next 3 macros are for emitting formatted output. When complaints |
| 1363 | * from the WASM build are issued for non-formatted output, when a mere |
| 1364 | * string literal is to be emitted, the ?putz(z) forms should be used. |
| 1365 | * (This permits compile-time checking of format string / argument mismatch.) |
| 1366 | */ |
| 1367 | # define oputf(fmt, ...) printf(fmt,__VA_ARGS__) |
| 1368 | # define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) |
| 1369 | # define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) |
| 1370 | /* These next 3 macros are for emitting simple string literals. */ |
| 1371 | # define oputz(z) fputs(z,stdout) |
| 1372 | # define eputz(z) fputs(z,stderr) |
| 1373 | # define sputz(fp,z) fputs(z,fp) |
| 1374 | # define oputb(buf,na) fwrite(buf,1,na,stdout) |
| 1375 | # undef fflush |
| 1376 | #endif |
| 1377 | |
| 1378 | /* True if the timer is enabled */ |
| 1379 | static int enableTimer = 0; |
| 1380 | |
| 1381 | /* A version of strcmp() that works with NULL values */ |
| @@ -1416,10 +666,11 @@ | |
| 1416 | struct timeval ru_utime; /* user CPU time used */ |
| 1417 | struct timeval ru_stime; /* system CPU time used */ |
| 1418 | }; |
| 1419 | #define getrusage(A,B) memset(B,0,sizeof(*B)) |
| 1420 | #endif |
| 1421 | |
| 1422 | /* Saved resource information for the beginning of an operation */ |
| 1423 | static struct rusage sBegin; /* CPU time at start */ |
| 1424 | static sqlite3_int64 iBegin; /* Wall-clock time at start */ |
| 1425 | |
| @@ -1440,24 +691,24 @@ | |
| 1440 | } |
| 1441 | |
| 1442 | /* |
| 1443 | ** Print the timing results. |
| 1444 | */ |
| 1445 | static void endTimer(void){ |
| 1446 | if( enableTimer ){ |
| 1447 | sqlite3_int64 iEnd = timeOfDay(); |
| 1448 | struct rusage sEnd; |
| 1449 | getrusage(RUSAGE_SELF, &sEnd); |
| 1450 | sputf(stdout, "Run Time: real %.3f user %f sys %f\n", |
| 1451 | (iEnd - iBegin)*0.001, |
| 1452 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 1453 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 1454 | } |
| 1455 | } |
| 1456 | |
| 1457 | #define BEGIN_TIMER beginTimer() |
| 1458 | #define END_TIMER endTimer() |
| 1459 | #define HAS_TIMER 1 |
| 1460 | |
| 1461 | #elif (defined(_WIN32) || defined(WIN32)) |
| 1462 | |
| 1463 | /* Saved resource information for the beginning of an operation */ |
| @@ -1519,29 +770,29 @@ | |
| 1519 | } |
| 1520 | |
| 1521 | /* |
| 1522 | ** Print the timing results. |
| 1523 | */ |
| 1524 | static void endTimer(void){ |
| 1525 | if( enableTimer && getProcessTimesAddr){ |
| 1526 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 1527 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 1528 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
| 1529 | sputf(stdout, "Run Time: real %.3f user %f sys %f\n", |
| 1530 | (ftWallEnd - ftWallBegin)*0.001, |
| 1531 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 1532 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 1533 | } |
| 1534 | } |
| 1535 | |
| 1536 | #define BEGIN_TIMER beginTimer() |
| 1537 | #define END_TIMER endTimer() |
| 1538 | #define HAS_TIMER hasTimer() |
| 1539 | |
| 1540 | #else |
| 1541 | #define BEGIN_TIMER |
| 1542 | #define END_TIMER |
| 1543 | #define HAS_TIMER 0 |
| 1544 | #endif |
| 1545 | |
| 1546 | /* |
| 1547 | ** Used to prevent warnings about unused parameters |
| @@ -1738,41 +989,216 @@ | |
| 1738 | char *z; |
| 1739 | if( iotrace==0 ) return; |
| 1740 | va_start(ap, zFormat); |
| 1741 | z = sqlite3_vmprintf(zFormat, ap); |
| 1742 | va_end(ap); |
| 1743 | sputf(iotrace, "%s", z); |
| 1744 | sqlite3_free(z); |
| 1745 | } |
| 1746 | #endif |
| 1747 | |
| 1748 | /* |
| 1749 | ** Output string zUtf to Out stream as w characters. If w is negative, |
| 1750 | ** then right-justify the text. W is the width in UTF-8 characters, not |
| 1751 | ** in bytes. This is different from the %*.*s specification in printf |
| 1752 | ** since with %*.*s the width is measured in bytes, not characters. |
| 1753 | */ |
| 1754 | static void utf8_width_print(int w, const char *zUtf){ |
| 1755 | int i; |
| 1756 | int n; |
| 1757 | int aw = w<0 ? -w : w; |
| 1758 | if( zUtf==0 ) zUtf = ""; |
| 1759 | for(i=n=0; zUtf[i]; i++){ |
| 1760 | if( (zUtf[i]&0xc0)!=0x80 ){ |
| 1761 | n++; |
| 1762 | if( n==aw ){ |
| 1763 | do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); |
| 1764 | break; |
| 1765 | } |
| 1766 | } |
| 1767 | } |
| 1768 | if( n>=aw ){ |
| 1769 | oputf("%.*s", i, zUtf); |
| 1770 | }else if( w<0 ){ |
| 1771 | oputf("%*s%s", aw-n, "", zUtf); |
| 1772 | }else{ |
| 1773 | oputf("%s%*s", zUtf, aw-n, ""); |
| 1774 | } |
| 1775 | } |
| 1776 | |
| 1777 | |
| 1778 | /* |
| @@ -1834,11 +1260,11 @@ | |
| 1834 | struct __stat64 x = {0}; |
| 1835 | # define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) |
| 1836 | /* On Windows, open first, then check the stream nature. This order |
| 1837 | ** is necessary because _stat() and sibs, when checking a named pipe, |
| 1838 | ** effectively break the pipe as its supplier sees it. */ |
| 1839 | FILE *rv = fopen(zFile, "rb"); |
| 1840 | if( rv==0 ) return 0; |
| 1841 | if( _fstat64(_fileno(rv), &x) != 0 |
| 1842 | || !STAT_CHR_SRC(x.st_mode)){ |
| 1843 | fclose(rv); |
| 1844 | rv = 0; |
| @@ -1848,11 +1274,11 @@ | |
| 1848 | struct stat x = {0}; |
| 1849 | int rc = stat(zFile, &x); |
| 1850 | # define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) |
| 1851 | if( rc!=0 ) return 0; |
| 1852 | if( STAT_CHR_SRC(x.st_mode) ){ |
| 1853 | return fopen(zFile, "rb"); |
| 1854 | }else{ |
| 1855 | return 0; |
| 1856 | } |
| 1857 | #endif |
| 1858 | #undef STAT_CHR_SRC |
| @@ -1875,11 +1301,11 @@ | |
| 1875 | if( n+100>nLine ){ |
| 1876 | nLine = nLine*2 + 100; |
| 1877 | zLine = realloc(zLine, nLine); |
| 1878 | shell_check_oom(zLine); |
| 1879 | } |
| 1880 | if( fgets(&zLine[n], nLine - n, in)==0 ){ |
| 1881 | if( n==0 ){ |
| 1882 | free(zLine); |
| 1883 | return 0; |
| 1884 | } |
| 1885 | zLine[n] = 0; |
| @@ -2941,11 +2367,11 @@ | |
| 2941 | ** |
| 2942 | ****************************************************************************** |
| 2943 | ** |
| 2944 | ** This SQLite extension implements functions that compute SHA3 hashes |
| 2945 | ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. |
| 2946 | ** Two SQL functions are implemented: |
| 2947 | ** |
| 2948 | ** sha3(X,SIZE) |
| 2949 | ** sha3_agg(Y,SIZE) |
| 2950 | ** sha3_query(Z,SIZE) |
| 2951 | ** |
| @@ -3783,10 +3209,422 @@ | |
| 3783 | } |
| 3784 | return rc; |
| 3785 | } |
| 3786 | |
| 3787 | /************************* End ../ext/misc/shathree.c ********************/ |
| 3788 | /************************* Begin ../ext/misc/uint.c ******************/ |
| 3789 | /* |
| 3790 | ** 2020-04-14 |
| 3791 | ** |
| 3792 | ** The author disclaims copyright to this source code. In place of |
| @@ -5075,11 +4913,11 @@ | |
| 5075 | }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){ |
| 5076 | p->a[p->nUsed++] = y; |
| 5077 | }else if( p->bKeepSorted ){ |
| 5078 | int i; |
| 5079 | i = percentBinarySearch(p, y, 0); |
| 5080 | if( i<p->nUsed ){ |
| 5081 | memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0])); |
| 5082 | } |
| 5083 | p->a[i] = y; |
| 5084 | p->nUsed++; |
| 5085 | }else{ |
| @@ -5191,11 +5029,11 @@ | |
| 5191 | |
| 5192 | /* Find and remove the row */ |
| 5193 | i = percentBinarySearch(p, y, 1); |
| 5194 | if( i>=0 ){ |
| 5195 | p->nUsed--; |
| 5196 | if( i<p->nUsed ){ |
| 5197 | memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0])); |
| 5198 | } |
| 5199 | } |
| 5200 | } |
| 5201 | |
| @@ -5251,15 +5089,15 @@ | |
| 5251 | sqlite3 *db, |
| 5252 | char **pzErrMsg, |
| 5253 | const sqlite3_api_routines *pApi |
| 5254 | ){ |
| 5255 | int rc = SQLITE_OK; |
| 5256 | int i; |
| 5257 | #if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE) |
| 5258 | (void)pApi; /* Unused parameter */ |
| 5259 | #else |
| 5260 | SQLITE_EXTENSION_INIT2(pApi); |
| 5261 | #endif |
| 5262 | (void)pzErrMsg; /* Unused parameter */ |
| 5263 | for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){ |
| 5264 | rc = sqlite3_create_window_function(db, |
| 5265 | aPercentFunc[i].zName, |
| @@ -7012,50 +6850,58 @@ | |
| 7012 | aIdx[4] = i; |
| 7013 | idxNum |= 0x40; |
| 7014 | } |
| 7015 | continue; |
| 7016 | } |
| 7017 | if( pConstraint->iColumn==SERIES_COLUMN_VALUE ){ |
| 7018 | switch( op ){ |
| 7019 | case SQLITE_INDEX_CONSTRAINT_EQ: |
| 7020 | case SQLITE_INDEX_CONSTRAINT_IS: { |
| 7021 | idxNum |= 0x0080; |
| 7022 | idxNum &= ~0x3300; |
| 7023 | aIdx[5] = i; |
| 7024 | aIdx[6] = -1; |
| 7025 | bStartSeen = 1; |
| 7026 | break; |
| 7027 | } |
| 7028 | case SQLITE_INDEX_CONSTRAINT_GE: { |
| 7029 | if( idxNum & 0x0080 ) break; |
| 7030 | idxNum |= 0x0100; |
| 7031 | idxNum &= ~0x0200; |
| 7032 | aIdx[5] = i; |
| 7033 | bStartSeen = 1; |
| 7034 | break; |
| 7035 | } |
| 7036 | case SQLITE_INDEX_CONSTRAINT_GT: { |
| 7037 | if( idxNum & 0x0080 ) break; |
| 7038 | idxNum |= 0x0200; |
| 7039 | idxNum &= ~0x0100; |
| 7040 | aIdx[5] = i; |
| 7041 | bStartSeen = 1; |
| 7042 | break; |
| 7043 | } |
| 7044 | case SQLITE_INDEX_CONSTRAINT_LE: { |
| 7045 | if( idxNum & 0x0080 ) break; |
| 7046 | idxNum |= 0x1000; |
| 7047 | idxNum &= ~0x2000; |
| 7048 | aIdx[6] = i; |
| 7049 | break; |
| 7050 | } |
| 7051 | case SQLITE_INDEX_CONSTRAINT_LT: { |
| 7052 | if( idxNum & 0x0080 ) break; |
| 7053 | idxNum |= 0x2000; |
| 7054 | idxNum &= ~0x1000; |
| 7055 | aIdx[6] = i; |
| 7056 | break; |
| 7057 | } |
| 7058 | } |
| 7059 | continue; |
| 7060 | } |
| 7061 | iCol = pConstraint->iColumn - SERIES_COLUMN_START; |
| @@ -8115,11 +7961,11 @@ | |
| 8115 | ** If the optional MTIME argument is present, then it is interpreted |
| 8116 | ** as an integer - the number of seconds since the unix epoch. The |
| 8117 | ** modification-time of the target file is set to this value before |
| 8118 | ** returning. |
| 8119 | ** |
| 8120 | ** If three or more arguments are passed to this function and an |
| 8121 | ** error is encountered, an exception is raised. |
| 8122 | ** |
| 8123 | ** READFILE(FILE): |
| 8124 | ** |
| 8125 | ** Read and return the contents of file FILE (type blob) from disk. |
| @@ -8185,10 +8031,17 @@ | |
| 8185 | # define lstat(path,buf) stat(path,buf) |
| 8186 | #endif |
| 8187 | #include <time.h> |
| 8188 | #include <errno.h> |
| 8189 | |
| 8190 | |
| 8191 | /* |
| 8192 | ** Structure of the fsdir() table-valued function |
| 8193 | */ |
| 8194 | /* 0 1 2 3 4 5 */ |
| @@ -8217,11 +8070,11 @@ | |
| 8217 | sqlite3_int64 nIn; |
| 8218 | void *pBuf; |
| 8219 | sqlite3 *db; |
| 8220 | int mxBlob; |
| 8221 | |
| 8222 | in = fopen(zName, "rb"); |
| 8223 | if( in==0 ){ |
| 8224 | /* File does not exist or is unreadable. Leave the result set to NULL. */ |
| 8225 | return; |
| 8226 | } |
| 8227 | fseek(in, 0, SEEK_END); |
| @@ -8472,11 +8325,11 @@ | |
| 8472 | } |
| 8473 | }else{ |
| 8474 | sqlite3_int64 nWrite = 0; |
| 8475 | const char *z; |
| 8476 | int rc = 0; |
| 8477 | FILE *out = fopen(zFile, "wb"); |
| 8478 | if( out==0 ) return 1; |
| 8479 | z = (const char*)sqlite3_value_blob(pData); |
| 8480 | if( z ){ |
| 8481 | sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out); |
| 8482 | nWrite = sqlite3_value_bytes(pData); |
| @@ -10333,10 +10186,18 @@ | |
| 10333 | #ifndef SQLITE_NO_STDINT |
| 10334 | # include <stdint.h> |
| 10335 | #endif |
| 10336 | |
| 10337 | #include <zlib.h> |
| 10338 | |
| 10339 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 10340 | |
| 10341 | #ifndef SQLITE_AMALGAMATION |
| 10342 | |
| @@ -11590,11 +11451,11 @@ | |
| 11590 | }else{ |
| 11591 | zFile = (const char*)sqlite3_value_text(argv[0]); |
| 11592 | } |
| 11593 | |
| 11594 | if( 0==pTab->pWriteFd && 0==bInMemory ){ |
| 11595 | pCsr->pFile = zFile ? fopen(zFile, "rb") : 0; |
| 11596 | if( pCsr->pFile==0 ){ |
| 11597 | zipfileCursorErr(pCsr, "cannot open file: %s", zFile); |
| 11598 | rc = SQLITE_ERROR; |
| 11599 | }else{ |
| 11600 | rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd); |
| @@ -11780,11 +11641,11 @@ | |
| 11780 | |
| 11781 | /* Open a write fd on the file. Also load the entire central directory |
| 11782 | ** structure into memory. During the transaction any new file data is |
| 11783 | ** appended to the archive file, but the central directory is accumulated |
| 11784 | ** in main-memory until the transaction is committed. */ |
| 11785 | pTab->pWriteFd = fopen(pTab->zFile, "ab+"); |
| 11786 | if( pTab->pWriteFd==0 ){ |
| 11787 | pTab->base.zErrMsg = sqlite3_mprintf( |
| 11788 | "zipfile: failed to open file %s for writing", pTab->zFile |
| 11789 | ); |
| 11790 | rc = SQLITE_ERROR; |
| @@ -14233,10 +14094,70 @@ | |
| 14233 | } |
| 14234 | |
| 14235 | return rc; |
| 14236 | } |
| 14237 | |
| 14238 | |
| 14239 | static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){ |
| 14240 | int rc = idxRegisterVtab(p); |
| 14241 | sqlite3_stmt *pSchema = 0; |
| 14242 | |
| @@ -14244,30 +14165,39 @@ | |
| 14244 | ** |
| 14245 | ** 1) Add an entry to the p->pTable list, and |
| 14246 | ** 2) Create the equivalent virtual table in dbv. |
| 14247 | */ |
| 14248 | rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg, |
| 14249 | "SELECT type, name, sql, 1 FROM sqlite_schema " |
| 14250 | "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' " |
| 14251 | " UNION ALL " |
| 14252 | "SELECT type, name, sql, 2 FROM sqlite_schema " |
| 14253 | "WHERE type = 'trigger'" |
| 14254 | " AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') " |
| 14255 | "ORDER BY 4, 1" |
| 14256 | ); |
| 14257 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){ |
| 14258 | const char *zType = (const char*)sqlite3_column_text(pSchema, 0); |
| 14259 | const char *zName = (const char*)sqlite3_column_text(pSchema, 1); |
| 14260 | const char *zSql = (const char*)sqlite3_column_text(pSchema, 2); |
| 14261 | |
| 14262 | if( zType==0 || zName==0 ) continue; |
| 14263 | if( zType[0]=='v' || zType[1]=='r' ){ |
| 14264 | if( zSql ) rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg); |
| 14265 | }else{ |
| 14266 | IdxTable *pTab; |
| 14267 | rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg); |
| 14268 | if( rc==SQLITE_OK ){ |
| 14269 | int i; |
| 14270 | char *zInner = 0; |
| 14271 | char *zOuter = 0; |
| 14272 | pTab->pNext = p->pTable; |
| 14273 | p->pTable = pTab; |
| @@ -14464,10 +14394,16 @@ | |
| 14464 | sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC); |
| 14465 | while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){ |
| 14466 | const char *zComma = zCols==0 ? "" : ", "; |
| 14467 | const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0); |
| 14468 | const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1); |
| 14469 | zCols = idxAppendText(&rc, zCols, |
| 14470 | "%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s", |
| 14471 | zComma, zName, nCol, zName, zColl |
| 14472 | ); |
| 14473 | zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol); |
| @@ -14792,16 +14728,22 @@ | |
| 14792 | |
| 14793 | /* Copy the entire schema of database [db] into [dbm]. */ |
| 14794 | if( rc==SQLITE_OK ){ |
| 14795 | sqlite3_stmt *pSql = 0; |
| 14796 | rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, |
| 14797 | "SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'" |
| 14798 | " AND sql NOT LIKE 'CREATE VIRTUAL %%' ORDER BY rowid" |
| 14799 | ); |
| 14800 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 14801 | const char *zSql = (const char*)sqlite3_column_text(pSql, 0); |
| 14802 | if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg); |
| 14803 | } |
| 14804 | idxFinalize(&rc, pSql); |
| 14805 | } |
| 14806 | |
| 14807 | /* Create the vtab schema */ |
| @@ -16211,10 +16153,1197 @@ | |
| 16211 | } |
| 16212 | return rc; |
| 16213 | } |
| 16214 | |
| 16215 | /************************* End ../ext/misc/stmtrand.c ********************/ |
| 16216 | |
| 16217 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 16218 | #define SQLITE_SHELL_HAVE_RECOVER 1 |
| 16219 | #else |
| 16220 | #define SQLITE_SHELL_HAVE_RECOVER 0 |
| @@ -18241,11 +19370,11 @@ | |
| 18241 | }while( strstr(z,zBuf)!=0 ); |
| 18242 | return zBuf; |
| 18243 | } |
| 18244 | |
| 18245 | /* |
| 18246 | ** Implementation of scalar SQL function "escape_crnl". The argument passed to |
| 18247 | ** this function is the output of built-in function quote(). If the first |
| 18248 | ** character of the input is "'", indicating that the value passed to quote() |
| 18249 | ** was a text value, then this function searches the input for "\n" and "\r" |
| 18250 | ** characters and adds a wrapper similar to the following: |
| 18251 | ** |
| @@ -18252,11 +19381,11 @@ | |
| 18252 | ** replace(replace(<input>, '\n', char(10), '\r', char(13)); |
| 18253 | ** |
| 18254 | ** Or, if the first character of the input is not "'", then a copy of the input |
| 18255 | ** is returned. |
| 18256 | */ |
| 18257 | static void recoverEscapeCrnl( |
| 18258 | sqlite3_context *context, |
| 18259 | int argc, |
| 18260 | sqlite3_value **argv |
| 18261 | ){ |
| 18262 | const char *zText = (const char*)sqlite3_value_text(argv[0]); |
| @@ -18467,11 +19596,11 @@ | |
| 18467 | void (*xFunc)(sqlite3_context*,int,sqlite3_value **); |
| 18468 | } aFunc[] = { |
| 18469 | { "getpage", 1, recoverGetPage }, |
| 18470 | { "page_is_used", 1, recoverPageIsUsed }, |
| 18471 | { "read_i32", 2, recoverReadI32 }, |
| 18472 | { "escape_crnl", 1, recoverEscapeCrnl }, |
| 18473 | }; |
| 18474 | |
| 18475 | const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; |
| 18476 | sqlite3 *db = 0; /* New database handle */ |
| 18477 | int ii; /* For iterating through aFunc[] */ |
| @@ -18820,11 +19949,11 @@ | |
| 18820 | assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); |
| 18821 | zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); |
| 18822 | |
| 18823 | if( bSql ){ |
| 18824 | zBind = recoverMPrintf(p, |
| 18825 | "%z%sescape_crnl(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind |
| 18826 | ); |
| 18827 | zSqlSep = "||', '||"; |
| 18828 | }else{ |
| 18829 | zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); |
| 18830 | } |
| @@ -19322,10 +20451,12 @@ | |
| 19322 | apVal[iField] = sqlite3_value_dup( pVal ); |
| 19323 | if( apVal[iField]==0 ){ |
| 19324 | recoverError(p, SQLITE_NOMEM, 0); |
| 19325 | } |
| 19326 | p1->nVal = iField+1; |
| 19327 | } |
| 19328 | p1->iPrevCell = iCell; |
| 19329 | p1->iPrevPage = iPage; |
| 19330 | } |
| 19331 | }else{ |
| @@ -20437,10 +21568,11 @@ | |
| 20437 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 20438 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 20439 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 20440 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 20441 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 20442 | ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 20443 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 20444 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 20445 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 20446 | int outCount; /* Revert to stdout when reaching zero */ |
| @@ -20582,10 +21714,11 @@ | |
| 20582 | #define MODE_Table 15 /* MySQL-style table formatting */ |
| 20583 | #define MODE_Box 16 /* Unicode box-drawing characters */ |
| 20584 | #define MODE_Count 17 /* Output only a count of the rows of output */ |
| 20585 | #define MODE_Off 18 /* No query output shown */ |
| 20586 | #define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */ |
| 20587 | |
| 20588 | static const char *modeDescr[] = { |
| 20589 | "line", |
| 20590 | "column", |
| 20591 | "list", |
| @@ -20602,11 +21735,13 @@ | |
| 20602 | "json", |
| 20603 | "markdown", |
| 20604 | "table", |
| 20605 | "box", |
| 20606 | "count", |
| 20607 | "off" |
| 20608 | }; |
| 20609 | |
| 20610 | /* |
| 20611 | ** These are the column/row/line separators used by the various |
| 20612 | ** import/export modes. |
| @@ -20630,11 +21765,11 @@ | |
| 20630 | ** A callback for the sqlite3_log() interface. |
| 20631 | */ |
| 20632 | static void shellLog(void *pArg, int iErrCode, const char *zMsg){ |
| 20633 | ShellState *p = (ShellState*)pArg; |
| 20634 | if( p->pLog==0 ) return; |
| 20635 | sputf(p->pLog, "(%d) %s\n", iErrCode, zMsg); |
| 20636 | fflush(p->pLog); |
| 20637 | } |
| 20638 | |
| 20639 | /* |
| 20640 | ** SQL function: shell_putsnl(X) |
| @@ -20645,13 +21780,13 @@ | |
| 20645 | static void shellPutsFunc( |
| 20646 | sqlite3_context *pCtx, |
| 20647 | int nVal, |
| 20648 | sqlite3_value **apVal |
| 20649 | ){ |
| 20650 | /* Unused: (ShellState*)sqlite3_user_data(pCtx); */ |
| 20651 | (void)nVal; |
| 20652 | oputf("%s\n", sqlite3_value_text(apVal[0])); |
| 20653 | sqlite3_result_value(pCtx, apVal[0]); |
| 20654 | } |
| 20655 | |
| 20656 | /* |
| 20657 | ** If in safe mode, print an error message described by the arguments |
| @@ -20666,11 +21801,11 @@ | |
| 20666 | va_list ap; |
| 20667 | char *zMsg; |
| 20668 | va_start(ap, zErrMsg); |
| 20669 | zMsg = sqlite3_vmprintf(zErrMsg, ap); |
| 20670 | va_end(ap); |
| 20671 | eputf("line %d: %s\n", p->lineno, zMsg); |
| 20672 | exit(1); |
| 20673 | } |
| 20674 | } |
| 20675 | |
| 20676 | /* |
| @@ -20699,11 +21834,11 @@ | |
| 20699 | char *zTempFile = 0; |
| 20700 | sqlite3 *db; |
| 20701 | char *zCmd = 0; |
| 20702 | int bBin; |
| 20703 | int rc; |
| 20704 | int hasCRNL = 0; |
| 20705 | FILE *f = 0; |
| 20706 | sqlite3_int64 sz; |
| 20707 | sqlite3_int64 x; |
| 20708 | unsigned char *p = 0; |
| 20709 | |
| @@ -20733,11 +21868,11 @@ | |
| 20733 | } |
| 20734 | } |
| 20735 | bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; |
| 20736 | /* When writing the file to be edited, do \n to \r\n conversions on systems |
| 20737 | ** that want \r\n line endings */ |
| 20738 | f = fopen(zTempFile, bBin ? "wb" : "w"); |
| 20739 | if( f==0 ){ |
| 20740 | sqlite3_result_error(context, "edit() cannot open temp file", -1); |
| 20741 | goto edit_func_end; |
| 20742 | } |
| 20743 | sz = sqlite3_value_bytes(argv[0]); |
| @@ -20744,11 +21879,11 @@ | |
| 20744 | if( bBin ){ |
| 20745 | x = fwrite(sqlite3_value_blob(argv[0]), 1, (size_t)sz, f); |
| 20746 | }else{ |
| 20747 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 20748 | /* Remember whether or not the value originally contained \r\n */ |
| 20749 | if( z && strstr(z,"\r\n")!=0 ) hasCRNL = 1; |
| 20750 | x = fwrite(sqlite3_value_text(argv[0]), 1, (size_t)sz, f); |
| 20751 | } |
| 20752 | fclose(f); |
| 20753 | f = 0; |
| 20754 | if( x!=sz ){ |
| @@ -20764,11 +21899,11 @@ | |
| 20764 | sqlite3_free(zCmd); |
| 20765 | if( rc ){ |
| 20766 | sqlite3_result_error(context, "EDITOR returned non-zero", -1); |
| 20767 | goto edit_func_end; |
| 20768 | } |
| 20769 | f = fopen(zTempFile, "rb"); |
| 20770 | if( f==0 ){ |
| 20771 | sqlite3_result_error(context, |
| 20772 | "edit() cannot reopen temp file after edit", -1); |
| 20773 | goto edit_func_end; |
| 20774 | } |
| @@ -20789,11 +21924,11 @@ | |
| 20789 | } |
| 20790 | if( bBin ){ |
| 20791 | sqlite3_result_blob64(context, p, sz, sqlite3_free); |
| 20792 | }else{ |
| 20793 | sqlite3_int64 i, j; |
| 20794 | if( hasCRNL ){ |
| 20795 | /* If the original contains \r\n then do no conversions back to \n */ |
| 20796 | }else{ |
| 20797 | /* If the file did not originally contain \r\n then convert any new |
| 20798 | ** \r\n back into \n */ |
| 20799 | p[sz] = 0; |
| @@ -20830,15 +21965,30 @@ | |
| 20830 | p->mode = p->modePrior; |
| 20831 | p->shellFlgs = p->priorShFlgs; |
| 20832 | memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 20833 | memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 20834 | } |
| 20835 | |
| 20836 | /* |
| 20837 | ** Output the given string as a hex-encoded blob (eg. X'1234' ) |
| 20838 | */ |
| 20839 | static void output_hex_blob(const void *pBlob, int nBlob){ |
| 20840 | int i; |
| 20841 | unsigned char *aBlob = (unsigned char*)pBlob; |
| 20842 | |
| 20843 | char *zStr = sqlite3_malloc(nBlob*2 + 1); |
| 20844 | shell_check_oom(zStr); |
| @@ -20851,11 +22001,11 @@ | |
| 20851 | zStr[i*2] = aHex[ (aBlob[i] >> 4) ]; |
| 20852 | zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ]; |
| 20853 | } |
| 20854 | zStr[i*2] = '\0'; |
| 20855 | |
| 20856 | oputf("X'%s'", zStr); |
| 20857 | sqlite3_free(zStr); |
| 20858 | } |
| 20859 | |
| 20860 | /* |
| 20861 | ** Find a string that is not found anywhere in z[]. Return a pointer |
| @@ -20881,46 +22031,40 @@ | |
| 20881 | /* |
| 20882 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 20883 | ** |
| 20884 | ** See also: output_quoted_escaped_string() |
| 20885 | */ |
| 20886 | static void output_quoted_string(const char *z){ |
| 20887 | int i; |
| 20888 | char c; |
| 20889 | #ifndef SQLITE_SHELL_FIDDLE |
| 20890 | FILE *pfO = setOutputStream(invalidFileStream); |
| 20891 | setBinaryMode(pfO, 1); |
| 20892 | #endif |
| 20893 | if( z==0 ) return; |
| 20894 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 20895 | if( c==0 ){ |
| 20896 | oputf("'%s'",z); |
| 20897 | }else{ |
| 20898 | oputz("'"); |
| 20899 | while( *z ){ |
| 20900 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 20901 | if( c=='\'' ) i++; |
| 20902 | if( i ){ |
| 20903 | oputf("%.*s", i, z); |
| 20904 | z += i; |
| 20905 | } |
| 20906 | if( c=='\'' ){ |
| 20907 | oputz("'"); |
| 20908 | continue; |
| 20909 | } |
| 20910 | if( c==0 ){ |
| 20911 | break; |
| 20912 | } |
| 20913 | z++; |
| 20914 | } |
| 20915 | oputz("'"); |
| 20916 | } |
| 20917 | #ifndef SQLITE_SHELL_FIDDLE |
| 20918 | setTextMode(pfO, 1); |
| 20919 | #else |
| 20920 | setTextMode(stdout, 1); |
| 20921 | #endif |
| 20922 | } |
| 20923 | |
| 20924 | /* |
| 20925 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 20926 | ** Additionallly , escape the "\n" and "\r" characters so that they do not |
| @@ -20928,20 +22072,18 @@ | |
| 20928 | ** systems. |
| 20929 | ** |
| 20930 | ** This is like output_quoted_string() but with the addition of the \r\n |
| 20931 | ** escape mechanism. |
| 20932 | */ |
| 20933 | static void output_quoted_escaped_string(const char *z){ |
| 20934 | int i; |
| 20935 | char c; |
| 20936 | #ifndef SQLITE_SHELL_FIDDLE |
| 20937 | FILE *pfO = setOutputStream(invalidFileStream); |
| 20938 | setBinaryMode(pfO, 1); |
| 20939 | #endif |
| 20940 | for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} |
| 20941 | if( c==0 ){ |
| 20942 | oputf("'%s'",z); |
| 20943 | }else{ |
| 20944 | const char *zNL = 0; |
| 20945 | const char *zCR = 0; |
| 20946 | int nNL = 0; |
| 20947 | int nCR = 0; |
| @@ -20949,52 +22091,48 @@ | |
| 20949 | for(i=0; z[i]; i++){ |
| 20950 | if( z[i]=='\n' ) nNL++; |
| 20951 | if( z[i]=='\r' ) nCR++; |
| 20952 | } |
| 20953 | if( nNL ){ |
| 20954 | oputz("replace("); |
| 20955 | zNL = unused_string(z, "\\n", "\\012", zBuf1); |
| 20956 | } |
| 20957 | if( nCR ){ |
| 20958 | oputz("replace("); |
| 20959 | zCR = unused_string(z, "\\r", "\\015", zBuf2); |
| 20960 | } |
| 20961 | oputz("'"); |
| 20962 | while( *z ){ |
| 20963 | for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} |
| 20964 | if( c=='\'' ) i++; |
| 20965 | if( i ){ |
| 20966 | oputf("%.*s", i, z); |
| 20967 | z += i; |
| 20968 | } |
| 20969 | if( c=='\'' ){ |
| 20970 | oputz("'"); |
| 20971 | continue; |
| 20972 | } |
| 20973 | if( c==0 ){ |
| 20974 | break; |
| 20975 | } |
| 20976 | z++; |
| 20977 | if( c=='\n' ){ |
| 20978 | oputz(zNL); |
| 20979 | continue; |
| 20980 | } |
| 20981 | oputz(zCR); |
| 20982 | } |
| 20983 | oputz("'"); |
| 20984 | if( nCR ){ |
| 20985 | oputf(",'%s',char(13))", zCR); |
| 20986 | } |
| 20987 | if( nNL ){ |
| 20988 | oputf(",'%s',char(10))", zNL); |
| 20989 | } |
| 20990 | } |
| 20991 | #ifndef SQLITE_SHELL_FIDDLE |
| 20992 | setTextMode(pfO, 1); |
| 20993 | #else |
| 20994 | setTextMode(stdout, 1); |
| 20995 | #endif |
| 20996 | } |
| 20997 | |
| 20998 | /* |
| 20999 | ** Find earliest of chars within s specified in zAny. |
| 21000 | ** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated. |
| @@ -21010,26 +22148,64 @@ | |
| 21010 | } |
| 21011 | ++zAny; |
| 21012 | } |
| 21013 | return pcFirst; |
| 21014 | } |
| 21015 | /* |
| 21016 | ** Output the given string as a quoted according to C or TCL quoting rules. |
| 21017 | */ |
| 21018 | static void output_c_string(const char *z){ |
| 21019 | char c; |
| 21020 | static const char *zq = "\""; |
| 21021 | static long ctrlMask = ~0L; |
| 21022 | static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ |
| 21023 | char ace[3] = "\\?"; |
| 21024 | char cbsSay; |
| 21025 | oputz(zq); |
| 21026 | while( *z!=0 ){ |
| 21027 | const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); |
| 21028 | const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); |
| 21029 | const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; |
| 21030 | if( pcEnd > z ) oputb(z, (int)(pcEnd-z)); |
| 21031 | if( (c = *pcEnd)==0 ) break; |
| 21032 | ++pcEnd; |
| 21033 | switch( c ){ |
| 21034 | case '\\': case '"': |
| 21035 | cbsSay = (char)c; |
| @@ -21040,47 +22216,47 @@ | |
| 21040 | case '\f': cbsSay = 'f'; break; |
| 21041 | default: cbsSay = 0; break; |
| 21042 | } |
| 21043 | if( cbsSay ){ |
| 21044 | ace[1] = cbsSay; |
| 21045 | oputz(ace); |
| 21046 | }else if( !isprint(c&0xff) ){ |
| 21047 | oputf("\\%03o", c&0xff); |
| 21048 | }else{ |
| 21049 | ace[1] = (char)c; |
| 21050 | oputz(ace+1); |
| 21051 | } |
| 21052 | z = pcEnd; |
| 21053 | } |
| 21054 | oputz(zq); |
| 21055 | } |
| 21056 | |
| 21057 | /* |
| 21058 | ** Output the given string as a quoted according to JSON quoting rules. |
| 21059 | */ |
| 21060 | static void output_json_string(const char *z, i64 n){ |
| 21061 | char c; |
| 21062 | static const char *zq = "\""; |
| 21063 | static long ctrlMask = ~0L; |
| 21064 | static const char *zDQBS = "\"\\"; |
| 21065 | const char *pcLimit; |
| 21066 | char ace[3] = "\\?"; |
| 21067 | char cbsSay; |
| 21068 | |
| 21069 | if( z==0 ) z = ""; |
| 21070 | pcLimit = z + ((n<0)? strlen(z) : (size_t)n); |
| 21071 | oputz(zq); |
| 21072 | while( z < pcLimit ){ |
| 21073 | const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); |
| 21074 | const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); |
| 21075 | const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; |
| 21076 | if( pcEnd > z ){ |
| 21077 | oputb(z, (int)(pcEnd-z)); |
| 21078 | z = pcEnd; |
| 21079 | } |
| 21080 | if( z >= pcLimit ) break; |
| 21081 | c = *(z++); |
| 21082 | switch( c ){ |
| 21083 | case '"': case '\\': |
| 21084 | cbsSay = (char)c; |
| 21085 | break; |
| 21086 | case '\b': cbsSay = 'b'; break; |
| @@ -21090,26 +22266,26 @@ | |
| 21090 | case '\t': cbsSay = 't'; break; |
| 21091 | default: cbsSay = 0; break; |
| 21092 | } |
| 21093 | if( cbsSay ){ |
| 21094 | ace[1] = cbsSay; |
| 21095 | oputz(ace); |
| 21096 | }else if( c<=0x1f ){ |
| 21097 | oputf("u%04x", c); |
| 21098 | }else{ |
| 21099 | ace[1] = (char)c; |
| 21100 | oputz(ace+1); |
| 21101 | } |
| 21102 | } |
| 21103 | oputz(zq); |
| 21104 | } |
| 21105 | |
| 21106 | /* |
| 21107 | ** Output the given string with characters that are special to |
| 21108 | ** HTML escaped. |
| 21109 | */ |
| 21110 | static void output_html_string(const char *z){ |
| 21111 | int i; |
| 21112 | if( z==0 ) z = ""; |
| 21113 | while( *z ){ |
| 21114 | for(i=0; z[i] |
| 21115 | && z[i]!='<' |
| @@ -21117,22 +22293,22 @@ | |
| 21117 | && z[i]!='>' |
| 21118 | && z[i]!='\"' |
| 21119 | && z[i]!='\''; |
| 21120 | i++){} |
| 21121 | if( i>0 ){ |
| 21122 | oputf("%.*s",i,z); |
| 21123 | } |
| 21124 | if( z[i]=='<' ){ |
| 21125 | oputz("<"); |
| 21126 | }else if( z[i]=='&' ){ |
| 21127 | oputz("&"); |
| 21128 | }else if( z[i]=='>' ){ |
| 21129 | oputz(">"); |
| 21130 | }else if( z[i]=='\"' ){ |
| 21131 | oputz("""); |
| 21132 | }else if( z[i]=='\'' ){ |
| 21133 | oputz("'"); |
| 21134 | }else{ |
| 21135 | break; |
| 21136 | } |
| 21137 | z += i + 1; |
| 21138 | } |
| @@ -21167,11 +22343,11 @@ | |
| 21167 | ** the null value. Strings are quoted if necessary. The separator |
| 21168 | ** is only issued if bSep is true. |
| 21169 | */ |
| 21170 | static void output_csv(ShellState *p, const char *z, int bSep){ |
| 21171 | if( z==0 ){ |
| 21172 | oputf("%s",p->nullValue); |
| 21173 | }else{ |
| 21174 | unsigned i; |
| 21175 | for(i=0; z[i]; i++){ |
| 21176 | if( needCsvQuote[((unsigned char*)z)[i]] ){ |
| 21177 | i = 0; |
| @@ -21179,18 +22355,18 @@ | |
| 21179 | } |
| 21180 | } |
| 21181 | if( i==0 || strstr(z, p->colSeparator)!=0 ){ |
| 21182 | char *zQuoted = sqlite3_mprintf("\"%w\"", z); |
| 21183 | shell_check_oom(zQuoted); |
| 21184 | oputz(zQuoted); |
| 21185 | sqlite3_free(zQuoted); |
| 21186 | }else{ |
| 21187 | oputz(z); |
| 21188 | } |
| 21189 | } |
| 21190 | if( bSep ){ |
| 21191 | oputz(p->colSeparator); |
| 21192 | } |
| 21193 | } |
| 21194 | |
| 21195 | /* |
| 21196 | ** This routine runs when the user presses Ctrl-C |
| @@ -21294,20 +22470,20 @@ | |
| 21294 | const char *az[4]; |
| 21295 | az[0] = zA1; |
| 21296 | az[1] = zA2; |
| 21297 | az[2] = zA3; |
| 21298 | az[3] = zA4; |
| 21299 | oputf("authorizer: %s", azAction[op]); |
| 21300 | for(i=0; i<4; i++){ |
| 21301 | oputz(" "); |
| 21302 | if( az[i] ){ |
| 21303 | output_c_string(az[i]); |
| 21304 | }else{ |
| 21305 | oputz("NULL"); |
| 21306 | } |
| 21307 | } |
| 21308 | oputz("\n"); |
| 21309 | if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); |
| 21310 | return SQLITE_OK; |
| 21311 | } |
| 21312 | #endif |
| 21313 | |
| @@ -21319,11 +22495,11 @@ | |
| 21319 | ** |
| 21320 | ** If the schema statement in z[] contains a start-of-comment and if |
| 21321 | ** sqlite3_complete() returns false, try to terminate the comment before |
| 21322 | ** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c |
| 21323 | */ |
| 21324 | static void printSchemaLine(const char *z, const char *zTail){ |
| 21325 | char *zToFree = 0; |
| 21326 | if( z==0 ) return; |
| 21327 | if( zTail==0 ) return; |
| 21328 | if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){ |
| 21329 | const char *zOrig = z; |
| @@ -21341,20 +22517,20 @@ | |
| 21341 | } |
| 21342 | sqlite3_free(zNew); |
| 21343 | } |
| 21344 | } |
| 21345 | if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ |
| 21346 | oputf("CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); |
| 21347 | }else{ |
| 21348 | oputf("%s%s", z, zTail); |
| 21349 | } |
| 21350 | sqlite3_free(zToFree); |
| 21351 | } |
| 21352 | static void printSchemaLineN(char *z, int n, const char *zTail){ |
| 21353 | char c = z[n]; |
| 21354 | z[n] = 0; |
| 21355 | printSchemaLine(z, zTail); |
| 21356 | z[n] = c; |
| 21357 | } |
| 21358 | |
| 21359 | /* |
| 21360 | ** Return true if string z[] has nothing but whitespace and comments to the |
| @@ -21378,11 +22554,11 @@ | |
| 21378 | EQPGraphRow *pNew; |
| 21379 | i64 nText; |
| 21380 | if( zText==0 ) return; |
| 21381 | nText = strlen(zText); |
| 21382 | if( p->autoEQPtest ){ |
| 21383 | oputf("%d,%d,%s\n", iEqpId, p2, zText); |
| 21384 | } |
| 21385 | pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); |
| 21386 | shell_check_oom(pNew); |
| 21387 | pNew->iEqpId = iEqpId; |
| 21388 | pNew->iParentId = p2; |
| @@ -21426,11 +22602,12 @@ | |
| 21426 | i64 n = strlen(p->sGraph.zPrefix); |
| 21427 | char *z; |
| 21428 | for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ |
| 21429 | pNext = eqp_next_row(p, iEqpId, pRow); |
| 21430 | z = pRow->zText; |
| 21431 | oputf("%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); |
| 21432 | if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ |
| 21433 | memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); |
| 21434 | eqp_render_level(p, pRow->iEqpId); |
| 21435 | p->sGraph.zPrefix[n] = 0; |
| 21436 | } |
| @@ -21446,17 +22623,17 @@ | |
| 21446 | if( pRow->zText[0]=='-' ){ |
| 21447 | if( pRow->pNext==0 ){ |
| 21448 | eqp_reset(p); |
| 21449 | return; |
| 21450 | } |
| 21451 | oputf("%s\n", pRow->zText+3); |
| 21452 | p->sGraph.pRow = pRow->pNext; |
| 21453 | sqlite3_free(pRow); |
| 21454 | }else if( nCycle>0 ){ |
| 21455 | oputf("QUERY PLAN (cycles=%lld [100%%])\n", nCycle); |
| 21456 | }else{ |
| 21457 | oputz("QUERY PLAN\n"); |
| 21458 | } |
| 21459 | p->sGraph.zPrefix[0] = 0; |
| 21460 | eqp_render_level(p, 0); |
| 21461 | eqp_reset(p); |
| 21462 | } |
| @@ -21468,33 +22645,33 @@ | |
| 21468 | */ |
| 21469 | static int progress_handler(void *pClientData) { |
| 21470 | ShellState *p = (ShellState*)pClientData; |
| 21471 | p->nProgress++; |
| 21472 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 21473 | oputf("Progress limit reached (%u)\n", p->nProgress); |
| 21474 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 21475 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 21476 | return 1; |
| 21477 | } |
| 21478 | if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ |
| 21479 | oputf("Progress %u\n", p->nProgress); |
| 21480 | } |
| 21481 | return 0; |
| 21482 | } |
| 21483 | #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
| 21484 | |
| 21485 | /* |
| 21486 | ** Print N dashes |
| 21487 | */ |
| 21488 | static void print_dashes(int N){ |
| 21489 | const char zDash[] = "--------------------------------------------------"; |
| 21490 | const int nDash = sizeof(zDash) - 1; |
| 21491 | while( N>nDash ){ |
| 21492 | oputz(zDash); |
| 21493 | N -= nDash; |
| 21494 | } |
| 21495 | oputf("%.*s", N, zDash); |
| 21496 | } |
| 21497 | |
| 21498 | /* |
| 21499 | ** Print a markdown or table-style row separator using ascii-art |
| 21500 | */ |
| @@ -21503,19 +22680,19 @@ | |
| 21503 | int nArg, |
| 21504 | const char *zSep |
| 21505 | ){ |
| 21506 | int i; |
| 21507 | if( nArg>0 ){ |
| 21508 | oputz(zSep); |
| 21509 | print_dashes(p->actualWidth[0]+2); |
| 21510 | for(i=1; i<nArg; i++){ |
| 21511 | oputz(zSep); |
| 21512 | print_dashes(p->actualWidth[i]+2); |
| 21513 | } |
| 21514 | oputz(zSep); |
| 21515 | } |
| 21516 | oputz("\n"); |
| 21517 | } |
| 21518 | |
| 21519 | /* |
| 21520 | ** This is the callback routine that the shell |
| 21521 | ** invokes for each row of a query result. |
| @@ -21541,13 +22718,13 @@ | |
| 21541 | if( azArg==0 ) break; |
| 21542 | for(i=0; i<nArg; i++){ |
| 21543 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 21544 | if( len>w ) w = len; |
| 21545 | } |
| 21546 | if( p->cnt++>0 ) oputz(p->rowSeparator); |
| 21547 | for(i=0; i<nArg; i++){ |
| 21548 | oputf("%*s = %s%s", w, azCol[i], |
| 21549 | azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
| 21550 | } |
| 21551 | break; |
| 21552 | } |
| 21553 | case MODE_ScanExp: |
| @@ -21571,16 +22748,16 @@ | |
| 21571 | if( nArg>nWidth ) nArg = nWidth; |
| 21572 | |
| 21573 | /* If this is the first row seen, print out the headers */ |
| 21574 | if( p->cnt++==0 ){ |
| 21575 | for(i=0; i<nArg; i++){ |
| 21576 | utf8_width_print(aWidth[i], azCol[ aMap[i] ]); |
| 21577 | oputz(i==nArg-1 ? "\n" : " "); |
| 21578 | } |
| 21579 | for(i=0; i<nArg; i++){ |
| 21580 | print_dashes(aWidth[i]); |
| 21581 | oputz(i==nArg-1 ? "\n" : " "); |
| 21582 | } |
| 21583 | } |
| 21584 | |
| 21585 | /* If there is no data, exit early. */ |
| 21586 | if( azArg==0 ) break; |
| @@ -21594,21 +22771,21 @@ | |
| 21594 | w = strlenChar(zVal); |
| 21595 | zSep = " "; |
| 21596 | } |
| 21597 | if( i==iIndent && p->aiIndent && p->pStmt ){ |
| 21598 | if( p->iIndent<p->nIndent ){ |
| 21599 | oputf("%*.s", p->aiIndent[p->iIndent], ""); |
| 21600 | } |
| 21601 | p->iIndent++; |
| 21602 | } |
| 21603 | utf8_width_print(w, zVal ? zVal : p->nullValue); |
| 21604 | oputz(i==nArg-1 ? "\n" : zSep); |
| 21605 | } |
| 21606 | break; |
| 21607 | } |
| 21608 | case MODE_Semi: { /* .schema and .fullschema output */ |
| 21609 | printSchemaLine(azArg[0], ";\n"); |
| 21610 | break; |
| 21611 | } |
| 21612 | case MODE_Pretty: { /* .schema and .fullschema with --indent */ |
| 21613 | char *z; |
| 21614 | int j; |
| @@ -21619,11 +22796,11 @@ | |
| 21619 | assert( nArg==1 ); |
| 21620 | if( azArg[0]==0 ) break; |
| 21621 | if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 21622 | || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 21623 | ){ |
| 21624 | oputf("%s;\n", azArg[0]); |
| 21625 | break; |
| 21626 | } |
| 21627 | z = sqlite3_mprintf("%s", azArg[0]); |
| 21628 | shell_check_oom(z); |
| 21629 | j = 0; |
| @@ -21652,255 +22829,265 @@ | |
| 21652 | }else if( c=='(' ){ |
| 21653 | nParen++; |
| 21654 | }else if( c==')' ){ |
| 21655 | nParen--; |
| 21656 | if( nLine>0 && nParen==0 && j>0 ){ |
| 21657 | printSchemaLineN(z, j, "\n"); |
| 21658 | j = 0; |
| 21659 | } |
| 21660 | } |
| 21661 | z[j++] = c; |
| 21662 | if( nParen==1 && cEnd==0 |
| 21663 | && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 21664 | ){ |
| 21665 | if( c=='\n' ) j--; |
| 21666 | printSchemaLineN(z, j, "\n "); |
| 21667 | j = 0; |
| 21668 | nLine++; |
| 21669 | while( IsSpace(z[i+1]) ){ i++; } |
| 21670 | } |
| 21671 | } |
| 21672 | z[j] = 0; |
| 21673 | } |
| 21674 | printSchemaLine(z, ";\n"); |
| 21675 | sqlite3_free(z); |
| 21676 | break; |
| 21677 | } |
| 21678 | case MODE_List: { |
| 21679 | if( p->cnt++==0 && p->showHeader ){ |
| 21680 | for(i=0; i<nArg; i++){ |
| 21681 | oputf("%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 21682 | } |
| 21683 | } |
| 21684 | if( azArg==0 ) break; |
| 21685 | for(i=0; i<nArg; i++){ |
| 21686 | char *z = azArg[i]; |
| 21687 | if( z==0 ) z = p->nullValue; |
| 21688 | oputz(z); |
| 21689 | oputz((i<nArg-1)? p->colSeparator : p->rowSeparator); |
| 21690 | } |
| 21691 | break; |
| 21692 | } |
| 21693 | case MODE_Html: { |
| 21694 | if( p->cnt++==0 && p->showHeader ){ |
| 21695 | oputz("<TR>"); |
| 21696 | for(i=0; i<nArg; i++){ |
| 21697 | oputz("<TH>"); |
| 21698 | output_html_string(azCol[i]); |
| 21699 | oputz("</TH>\n"); |
| 21700 | } |
| 21701 | oputz("</TR>\n"); |
| 21702 | } |
| 21703 | if( azArg==0 ) break; |
| 21704 | oputz("<TR>"); |
| 21705 | for(i=0; i<nArg; i++){ |
| 21706 | oputz("<TD>"); |
| 21707 | output_html_string(azArg[i] ? azArg[i] : p->nullValue); |
| 21708 | oputz("</TD>\n"); |
| 21709 | } |
| 21710 | oputz("</TR>\n"); |
| 21711 | break; |
| 21712 | } |
| 21713 | case MODE_Tcl: { |
| 21714 | if( p->cnt++==0 && p->showHeader ){ |
| 21715 | for(i=0; i<nArg; i++){ |
| 21716 | output_c_string(azCol[i] ? azCol[i] : ""); |
| 21717 | if(i<nArg-1) oputz(p->colSeparator); |
| 21718 | } |
| 21719 | oputz(p->rowSeparator); |
| 21720 | } |
| 21721 | if( azArg==0 ) break; |
| 21722 | for(i=0; i<nArg; i++){ |
| 21723 | output_c_string(azArg[i] ? azArg[i] : p->nullValue); |
| 21724 | if(i<nArg-1) oputz(p->colSeparator); |
| 21725 | } |
| 21726 | oputz(p->rowSeparator); |
| 21727 | break; |
| 21728 | } |
| 21729 | case MODE_Csv: { |
| 21730 | setBinaryMode(p->out, 1); |
| 21731 | if( p->cnt++==0 && p->showHeader ){ |
| 21732 | for(i=0; i<nArg; i++){ |
| 21733 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 21734 | } |
| 21735 | oputz(p->rowSeparator); |
| 21736 | } |
| 21737 | if( nArg>0 ){ |
| 21738 | for(i=0; i<nArg; i++){ |
| 21739 | output_csv(p, azArg[i], i<nArg-1); |
| 21740 | } |
| 21741 | oputz(p->rowSeparator); |
| 21742 | } |
| 21743 | setTextMode(p->out, 1); |
| 21744 | break; |
| 21745 | } |
| 21746 | case MODE_Insert: { |
| 21747 | if( azArg==0 ) break; |
| 21748 | oputf("INSERT INTO %s",p->zDestTable); |
| 21749 | if( p->showHeader ){ |
| 21750 | oputz("("); |
| 21751 | for(i=0; i<nArg; i++){ |
| 21752 | if( i>0 ) oputz(","); |
| 21753 | if( quoteChar(azCol[i]) ){ |
| 21754 | char *z = sqlite3_mprintf("\"%w\"", azCol[i]); |
| 21755 | shell_check_oom(z); |
| 21756 | oputz(z); |
| 21757 | sqlite3_free(z); |
| 21758 | }else{ |
| 21759 | oputf("%s", azCol[i]); |
| 21760 | } |
| 21761 | } |
| 21762 | oputz(")"); |
| 21763 | } |
| 21764 | p->cnt++; |
| 21765 | for(i=0; i<nArg; i++){ |
| 21766 | oputz(i>0 ? "," : " VALUES("); |
| 21767 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 21768 | oputz("NULL"); |
| 21769 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 21770 | if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 21771 | output_quoted_string(azArg[i]); |
| 21772 | }else{ |
| 21773 | output_quoted_escaped_string(azArg[i]); |
| 21774 | } |
| 21775 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 21776 | oputz(azArg[i]); |
| 21777 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 21778 | char z[50]; |
| 21779 | double r = sqlite3_column_double(p->pStmt, i); |
| 21780 | sqlite3_uint64 ur; |
| 21781 | memcpy(&ur,&r,sizeof(r)); |
| 21782 | if( ur==0x7ff0000000000000LL ){ |
| 21783 | oputz("9.0e+999"); |
| 21784 | }else if( ur==0xfff0000000000000LL ){ |
| 21785 | oputz("-9.0e+999"); |
| 21786 | }else{ |
| 21787 | sqlite3_int64 ir = (sqlite3_int64)r; |
| 21788 | if( r==(double)ir ){ |
| 21789 | sqlite3_snprintf(50,z,"%lld.0", ir); |
| 21790 | }else{ |
| 21791 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 21792 | } |
| 21793 | oputz(z); |
| 21794 | } |
| 21795 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 21796 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 21797 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 21798 | output_hex_blob(pBlob, nBlob); |
| 21799 | }else if( isNumber(azArg[i], 0) ){ |
| 21800 | oputz(azArg[i]); |
| 21801 | }else if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 21802 | output_quoted_string(azArg[i]); |
| 21803 | }else{ |
| 21804 | output_quoted_escaped_string(azArg[i]); |
| 21805 | } |
| 21806 | } |
| 21807 | oputz(");\n"); |
| 21808 | break; |
| 21809 | } |
| 21810 | case MODE_Json: { |
| 21811 | if( azArg==0 ) break; |
| 21812 | if( p->cnt==0 ){ |
| 21813 | fputs("[{", p->out); |
| 21814 | }else{ |
| 21815 | fputs(",\n{", p->out); |
| 21816 | } |
| 21817 | p->cnt++; |
| 21818 | for(i=0; i<nArg; i++){ |
| 21819 | output_json_string(azCol[i], -1); |
| 21820 | oputz(":"); |
| 21821 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 21822 | oputz("null"); |
| 21823 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 21824 | char z[50]; |
| 21825 | double r = sqlite3_column_double(p->pStmt, i); |
| 21826 | sqlite3_uint64 ur; |
| 21827 | memcpy(&ur,&r,sizeof(r)); |
| 21828 | if( ur==0x7ff0000000000000LL ){ |
| 21829 | oputz("9.0e+999"); |
| 21830 | }else if( ur==0xfff0000000000000LL ){ |
| 21831 | oputz("-9.0e+999"); |
| 21832 | }else{ |
| 21833 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 21834 | oputz(z); |
| 21835 | } |
| 21836 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 21837 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 21838 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 21839 | output_json_string(pBlob, nBlob); |
| 21840 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 21841 | output_json_string(azArg[i], -1); |
| 21842 | }else{ |
| 21843 | oputz(azArg[i]); |
| 21844 | } |
| 21845 | if( i<nArg-1 ){ |
| 21846 | oputz(","); |
| 21847 | } |
| 21848 | } |
| 21849 | oputz("}"); |
| 21850 | break; |
| 21851 | } |
| 21852 | case MODE_Quote: { |
| 21853 | if( azArg==0 ) break; |
| 21854 | if( p->cnt==0 && p->showHeader ){ |
| 21855 | for(i=0; i<nArg; i++){ |
| 21856 | if( i>0 ) fputs(p->colSeparator, p->out); |
| 21857 | output_quoted_string(azCol[i]); |
| 21858 | } |
| 21859 | fputs(p->rowSeparator, p->out); |
| 21860 | } |
| 21861 | p->cnt++; |
| 21862 | for(i=0; i<nArg; i++){ |
| 21863 | if( i>0 ) fputs(p->colSeparator, p->out); |
| 21864 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 21865 | oputz("NULL"); |
| 21866 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 21867 | output_quoted_string(azArg[i]); |
| 21868 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 21869 | oputz(azArg[i]); |
| 21870 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 21871 | char z[50]; |
| 21872 | double r = sqlite3_column_double(p->pStmt, i); |
| 21873 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 21874 | oputz(z); |
| 21875 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 21876 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 21877 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 21878 | output_hex_blob(pBlob, nBlob); |
| 21879 | }else if( isNumber(azArg[i], 0) ){ |
| 21880 | oputz(azArg[i]); |
| 21881 | }else{ |
| 21882 | output_quoted_string(azArg[i]); |
| 21883 | } |
| 21884 | } |
| 21885 | fputs(p->rowSeparator, p->out); |
| 21886 | break; |
| 21887 | } |
| 21888 | case MODE_Ascii: { |
| 21889 | if( p->cnt++==0 && p->showHeader ){ |
| 21890 | for(i=0; i<nArg; i++){ |
| 21891 | if( i>0 ) oputz(p->colSeparator); |
| 21892 | oputz(azCol[i] ? azCol[i] : ""); |
| 21893 | } |
| 21894 | oputz(p->rowSeparator); |
| 21895 | } |
| 21896 | if( azArg==0 ) break; |
| 21897 | for(i=0; i<nArg; i++){ |
| 21898 | if( i>0 ) oputz(p->colSeparator); |
| 21899 | oputz(azArg[i] ? azArg[i] : p->nullValue); |
| 21900 | } |
| 21901 | oputz(p->rowSeparator); |
| 21902 | break; |
| 21903 | } |
| 21904 | case MODE_EQP: { |
| 21905 | eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); |
| 21906 | break; |
| @@ -21975,11 +23162,11 @@ | |
| 21975 | "INSERT INTO selftest(tno,op,cmd,ans)" |
| 21976 | " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" |
| 21977 | "DROP TABLE [_shell$self];" |
| 21978 | ,0,0,&zErrMsg); |
| 21979 | if( zErrMsg ){ |
| 21980 | eputf("SELFTEST initialization failure: %s\n", zErrMsg); |
| 21981 | sqlite3_free(zErrMsg); |
| 21982 | } |
| 21983 | sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); |
| 21984 | } |
| 21985 | |
| @@ -22078,36 +23265,37 @@ | |
| 22078 | int i; |
| 22079 | const char *z; |
| 22080 | rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); |
| 22081 | if( rc!=SQLITE_OK || !pSelect ){ |
| 22082 | char *zContext = shell_error_context(zSelect, p->db); |
| 22083 | oputf("/**** ERROR: (%d) %s *****/\n%s", |
| 22084 | rc, sqlite3_errmsg(p->db), zContext); |
| 22085 | sqlite3_free(zContext); |
| 22086 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 22087 | return rc; |
| 22088 | } |
| 22089 | rc = sqlite3_step(pSelect); |
| 22090 | nResult = sqlite3_column_count(pSelect); |
| 22091 | while( rc==SQLITE_ROW ){ |
| 22092 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 22093 | oputf("%s", z); |
| 22094 | for(i=1; i<nResult; i++){ |
| 22095 | oputf(",%s", sqlite3_column_text(pSelect, i)); |
| 22096 | } |
| 22097 | if( z==0 ) z = ""; |
| 22098 | while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; |
| 22099 | if( z[0] ){ |
| 22100 | oputz("\n;\n"); |
| 22101 | }else{ |
| 22102 | oputz(";\n"); |
| 22103 | } |
| 22104 | rc = sqlite3_step(pSelect); |
| 22105 | } |
| 22106 | rc = sqlite3_finalize(pSelect); |
| 22107 | if( rc!=SQLITE_OK ){ |
| 22108 | oputf("/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); |
| 22109 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 22110 | } |
| 22111 | return rc; |
| 22112 | } |
| 22113 | |
| @@ -22139,17 +23327,17 @@ | |
| 22139 | |
| 22140 | #ifdef __linux__ |
| 22141 | /* |
| 22142 | ** Attempt to display I/O stats on Linux using /proc/PID/io |
| 22143 | */ |
| 22144 | static void displayLinuxIoStats(void){ |
| 22145 | FILE *in; |
| 22146 | char z[200]; |
| 22147 | sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); |
| 22148 | in = fopen(z, "rb"); |
| 22149 | if( in==0 ) return; |
| 22150 | while( fgets(z, sizeof(z), in)!=0 ){ |
| 22151 | static const struct { |
| 22152 | const char *zPattern; |
| 22153 | const char *zDesc; |
| 22154 | } aTrans[] = { |
| 22155 | { "rchar: ", "Bytes received by read():" }, |
| @@ -22162,11 +23350,11 @@ | |
| 22162 | }; |
| 22163 | int i; |
| 22164 | for(i=0; i<ArraySize(aTrans); i++){ |
| 22165 | int n = strlen30(aTrans[i].zPattern); |
| 22166 | if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){ |
| 22167 | oputf("%-36s %s", aTrans[i].zDesc, &z[n]); |
| 22168 | break; |
| 22169 | } |
| 22170 | } |
| 22171 | } |
| 22172 | fclose(in); |
| @@ -22175,10 +23363,11 @@ | |
| 22175 | |
| 22176 | /* |
| 22177 | ** Display a single line of status using 64-bit values. |
| 22178 | */ |
| 22179 | static void displayStatLine( |
| 22180 | char *zLabel, /* Label for this one line */ |
| 22181 | char *zFormat, /* Format for the result */ |
| 22182 | int iStatusCtrl, /* Which status to display */ |
| 22183 | int bReset /* True to reset the stats */ |
| 22184 | ){ |
| @@ -22193,11 +23382,11 @@ | |
| 22193 | if( nPercent>1 ){ |
| 22194 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); |
| 22195 | }else{ |
| 22196 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); |
| 22197 | } |
| 22198 | oputf("%-36s %s\n", zLabel, zLine); |
| 22199 | } |
| 22200 | |
| 22201 | /* |
| 22202 | ** Display memory stats. |
| 22203 | */ |
| @@ -22206,130 +23395,152 @@ | |
| 22206 | ShellState *pArg, /* Pointer to ShellState */ |
| 22207 | int bReset /* True to reset the stats */ |
| 22208 | ){ |
| 22209 | int iCur; |
| 22210 | int iHiwtr; |
| 22211 | if( pArg==0 || pArg->out==0 ) return 0; |
| 22212 | |
| 22213 | if( pArg->pStmt && pArg->statsOn==2 ){ |
| 22214 | int nCol, i, x; |
| 22215 | sqlite3_stmt *pStmt = pArg->pStmt; |
| 22216 | char z[100]; |
| 22217 | nCol = sqlite3_column_count(pStmt); |
| 22218 | oputf("%-36s %d\n", "Number of output columns:", nCol); |
| 22219 | for(i=0; i<nCol; i++){ |
| 22220 | sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); |
| 22221 | oputf("%-36s %s\n", z, sqlite3_column_name(pStmt,i)); |
| 22222 | #ifndef SQLITE_OMIT_DECLTYPE |
| 22223 | sqlite3_snprintf(30, z+x, "declared type:"); |
| 22224 | oputf("%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); |
| 22225 | #endif |
| 22226 | #ifdef SQLITE_ENABLE_COLUMN_METADATA |
| 22227 | sqlite3_snprintf(30, z+x, "database name:"); |
| 22228 | oputf("%-36s %s\n", z, sqlite3_column_database_name(pStmt,i)); |
| 22229 | sqlite3_snprintf(30, z+x, "table name:"); |
| 22230 | oputf("%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); |
| 22231 | sqlite3_snprintf(30, z+x, "origin name:"); |
| 22232 | oputf("%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i)); |
| 22233 | #endif |
| 22234 | } |
| 22235 | } |
| 22236 | |
| 22237 | if( pArg->statsOn==3 ){ |
| 22238 | if( pArg->pStmt ){ |
| 22239 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); |
| 22240 | oputf("VM-steps: %d\n", iCur); |
| 22241 | } |
| 22242 | return 0; |
| 22243 | } |
| 22244 | |
| 22245 | displayStatLine("Memory Used:", |
| 22246 | "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); |
| 22247 | displayStatLine("Number of Outstanding Allocations:", |
| 22248 | "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); |
| 22249 | if( pArg->shellFlgs & SHFLG_Pagecache ){ |
| 22250 | displayStatLine("Number of Pcache Pages Used:", |
| 22251 | "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); |
| 22252 | } |
| 22253 | displayStatLine("Number of Pcache Overflow Bytes:", |
| 22254 | "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); |
| 22255 | displayStatLine("Largest Allocation:", |
| 22256 | "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); |
| 22257 | displayStatLine("Largest Pcache Allocation:", |
| 22258 | "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); |
| 22259 | #ifdef YYTRACKMAXSTACKDEPTH |
| 22260 | displayStatLine("Deepest Parser Stack:", |
| 22261 | "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); |
| 22262 | #endif |
| 22263 | |
| 22264 | if( db ){ |
| 22265 | if( pArg->shellFlgs & SHFLG_Lookaside ){ |
| 22266 | iHiwtr = iCur = -1; |
| 22267 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, |
| 22268 | &iCur, &iHiwtr, bReset); |
| 22269 | oputf("Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); |
| 22270 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, |
| 22271 | &iCur, &iHiwtr, bReset); |
| 22272 | oputf("Successful lookaside attempts: %d\n", iHiwtr); |
| 22273 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, |
| 22274 | &iCur, &iHiwtr, bReset); |
| 22275 | oputf("Lookaside failures due to size: %d\n", iHiwtr); |
| 22276 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, |
| 22277 | &iCur, &iHiwtr, bReset); |
| 22278 | oputf("Lookaside failures due to OOM: %d\n", iHiwtr); |
| 22279 | } |
| 22280 | iHiwtr = iCur = -1; |
| 22281 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); |
| 22282 | oputf("Pager Heap Usage: %d bytes\n", iCur); |
| 22283 | iHiwtr = iCur = -1; |
| 22284 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); |
| 22285 | oputf("Page cache hits: %d\n", iCur); |
| 22286 | iHiwtr = iCur = -1; |
| 22287 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); |
| 22288 | oputf("Page cache misses: %d\n", iCur); |
| 22289 | iHiwtr = iCur = -1; |
| 22290 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); |
| 22291 | oputf("Page cache writes: %d\n", iCur); |
| 22292 | iHiwtr = iCur = -1; |
| 22293 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); |
| 22294 | oputf("Page cache spills: %d\n", iCur); |
| 22295 | iHiwtr = iCur = -1; |
| 22296 | sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); |
| 22297 | oputf("Schema Heap Usage: %d bytes\n", iCur); |
| 22298 | iHiwtr = iCur = -1; |
| 22299 | sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); |
| 22300 | oputf("Statement Heap/Lookaside Usage: %d bytes\n", iCur); |
| 22301 | } |
| 22302 | |
| 22303 | if( pArg->pStmt ){ |
| 22304 | int iHit, iMiss; |
| 22305 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, |
| 22306 | bReset); |
| 22307 | oputf("Fullscan Steps: %d\n", iCur); |
| 22308 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 22309 | oputf("Sort Operations: %d\n", iCur); |
| 22310 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); |
| 22311 | oputf("Autoindex Inserts: %d\n", iCur); |
| 22312 | iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, |
| 22313 | bReset); |
| 22314 | iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, |
| 22315 | bReset); |
| 22316 | if( iHit || iMiss ){ |
| 22317 | oputf("Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); |
| 22318 | } |
| 22319 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 22320 | oputf("Virtual Machine Steps: %d\n", iCur); |
| 22321 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); |
| 22322 | oputf("Reprepare operations: %d\n", iCur); |
| 22323 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); |
| 22324 | oputf("Number of times run: %d\n", iCur); |
| 22325 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); |
| 22326 | oputf("Memory used by prepared stmt: %d\n", iCur); |
| 22327 | } |
| 22328 | |
| 22329 | #ifdef __linux__ |
| 22330 | displayLinuxIoStats(); |
| 22331 | #endif |
| 22332 | |
| 22333 | /* Do not remove this machine readable comment: extra-stats-output-here */ |
| 22334 | |
| 22335 | return 0; |
| @@ -22721,21 +23932,21 @@ | |
| 22721 | #define BOX_1234 "\342\224\274" /* U+253c -|- */ |
| 22722 | |
| 22723 | /* Draw horizontal line N characters long using unicode box |
| 22724 | ** characters |
| 22725 | */ |
| 22726 | static void print_box_line(int N){ |
| 22727 | const char zDash[] = |
| 22728 | BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 |
| 22729 | BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; |
| 22730 | const int nDash = sizeof(zDash) - 1; |
| 22731 | N *= 3; |
| 22732 | while( N>nDash ){ |
| 22733 | oputz(zDash); |
| 22734 | N -= nDash; |
| 22735 | } |
| 22736 | oputf("%.*s", N, zDash); |
| 22737 | } |
| 22738 | |
| 22739 | /* |
| 22740 | ** Draw a horizontal separator for a MODE_Box table. |
| 22741 | */ |
| @@ -22746,19 +23957,19 @@ | |
| 22746 | const char *zSep2, |
| 22747 | const char *zSep3 |
| 22748 | ){ |
| 22749 | int i; |
| 22750 | if( nArg>0 ){ |
| 22751 | oputz(zSep1); |
| 22752 | print_box_line(p->actualWidth[0]+2); |
| 22753 | for(i=1; i<nArg; i++){ |
| 22754 | oputz(zSep2); |
| 22755 | print_box_line(p->actualWidth[i]+2); |
| 22756 | } |
| 22757 | oputz(zSep3); |
| 22758 | } |
| 22759 | oputz("\n"); |
| 22760 | } |
| 22761 | |
| 22762 | /* |
| 22763 | ** z[] is a line of text that is to be displayed the .mode box or table or |
| 22764 | ** similar tabular formats. z[] might contain control characters such |
| @@ -22788,16 +23999,26 @@ | |
| 22788 | } |
| 22789 | if( mxWidth<0 ) mxWidth = -mxWidth; |
| 22790 | if( mxWidth==0 ) mxWidth = 1000000; |
| 22791 | i = j = n = 0; |
| 22792 | while( n<mxWidth ){ |
| 22793 | if( z[i]>=' ' ){ |
| 22794 | n++; |
| 22795 | do{ i++; j++; }while( (z[i]&0xc0)==0x80 ); |
| 22796 | continue; |
| 22797 | } |
| 22798 | if( z[i]=='\t' ){ |
| 22799 | do{ |
| 22800 | n++; |
| 22801 | j++; |
| 22802 | }while( (n&7)!=0 && n<mxWidth ); |
| 22803 | i++; |
| @@ -22835,13 +24056,21 @@ | |
| 22835 | } |
| 22836 | zOut = malloc( j+1 ); |
| 22837 | shell_check_oom(zOut); |
| 22838 | i = j = n = 0; |
| 22839 | while( i<k ){ |
| 22840 | if( z[i]>=' ' ){ |
| 22841 | n++; |
| 22842 | do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 ); |
| 22843 | continue; |
| 22844 | } |
| 22845 | if( z[i]=='\t' ){ |
| 22846 | do{ |
| 22847 | n++; |
| @@ -23017,97 +24246,99 @@ | |
| 23017 | rowSep = "\n"; |
| 23018 | if( p->showHeader ){ |
| 23019 | for(i=0; i<nColumn; i++){ |
| 23020 | w = p->actualWidth[i]; |
| 23021 | if( p->colWidth[i]<0 ) w = -w; |
| 23022 | utf8_width_print(w, azData[i]); |
| 23023 | fputs(i==nColumn-1?"\n":" ", p->out); |
| 23024 | } |
| 23025 | for(i=0; i<nColumn; i++){ |
| 23026 | print_dashes(p->actualWidth[i]); |
| 23027 | fputs(i==nColumn-1?"\n":" ", p->out); |
| 23028 | } |
| 23029 | } |
| 23030 | break; |
| 23031 | } |
| 23032 | case MODE_Table: { |
| 23033 | colSep = " | "; |
| 23034 | rowSep = " |\n"; |
| 23035 | print_row_separator(p, nColumn, "+"); |
| 23036 | fputs("| ", p->out); |
| 23037 | for(i=0; i<nColumn; i++){ |
| 23038 | w = p->actualWidth[i]; |
| 23039 | n = strlenChar(azData[i]); |
| 23040 | oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); |
| 23041 | oputz(i==nColumn-1?" |\n":" | "); |
| 23042 | } |
| 23043 | print_row_separator(p, nColumn, "+"); |
| 23044 | break; |
| 23045 | } |
| 23046 | case MODE_Markdown: { |
| 23047 | colSep = " | "; |
| 23048 | rowSep = " |\n"; |
| 23049 | fputs("| ", p->out); |
| 23050 | for(i=0; i<nColumn; i++){ |
| 23051 | w = p->actualWidth[i]; |
| 23052 | n = strlenChar(azData[i]); |
| 23053 | oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); |
| 23054 | oputz(i==nColumn-1?" |\n":" | "); |
| 23055 | } |
| 23056 | print_row_separator(p, nColumn, "|"); |
| 23057 | break; |
| 23058 | } |
| 23059 | case MODE_Box: { |
| 23060 | colSep = " " BOX_13 " "; |
| 23061 | rowSep = " " BOX_13 "\n"; |
| 23062 | print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); |
| 23063 | oputz(BOX_13 " "); |
| 23064 | for(i=0; i<nColumn; i++){ |
| 23065 | w = p->actualWidth[i]; |
| 23066 | n = strlenChar(azData[i]); |
| 23067 | oputf("%*s%s%*s%s", |
| 23068 | (w-n)/2, "", azData[i], (w-n+1)/2, "", |
| 23069 | i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); |
| 23070 | } |
| 23071 | print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 23072 | break; |
| 23073 | } |
| 23074 | } |
| 23075 | for(i=nColumn, j=0; i<nTotal; i++, j++){ |
| 23076 | if( j==0 && p->cMode!=MODE_Column ){ |
| 23077 | oputz(p->cMode==MODE_Box?BOX_13" ":"| "); |
| 23078 | } |
| 23079 | z = azData[i]; |
| 23080 | if( z==0 ) z = p->nullValue; |
| 23081 | w = p->actualWidth[j]; |
| 23082 | if( p->colWidth[j]<0 ) w = -w; |
| 23083 | utf8_width_print(w, z); |
| 23084 | if( j==nColumn-1 ){ |
| 23085 | oputz(rowSep); |
| 23086 | if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){ |
| 23087 | if( p->cMode==MODE_Table ){ |
| 23088 | print_row_separator(p, nColumn, "+"); |
| 23089 | }else if( p->cMode==MODE_Box ){ |
| 23090 | print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 23091 | }else if( p->cMode==MODE_Column ){ |
| 23092 | oputz("\n"); |
| 23093 | } |
| 23094 | } |
| 23095 | j = -1; |
| 23096 | if( seenInterrupt ) goto columnar_end; |
| 23097 | }else{ |
| 23098 | oputz(colSep); |
| 23099 | } |
| 23100 | } |
| 23101 | if( p->cMode==MODE_Table ){ |
| 23102 | print_row_separator(p, nColumn, "+"); |
| 23103 | }else if( p->cMode==MODE_Box ){ |
| 23104 | print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); |
| 23105 | } |
| 23106 | columnar_end: |
| 23107 | if( seenInterrupt ){ |
| 23108 | oputz("Interrupt\n"); |
| 23109 | } |
| 23110 | nData = (nRow+1)*nColumn; |
| 23111 | for(i=0; i<nData; i++){ |
| 23112 | z = azData[i]; |
| 23113 | if( z!=zEmpty && z!=zShowNull ) free(azData[i]); |
| @@ -23190,11 +24421,13 @@ | |
| 23190 | } |
| 23191 | } |
| 23192 | } while( SQLITE_ROW == rc ); |
| 23193 | sqlite3_free(pData); |
| 23194 | if( pArg->cMode==MODE_Json ){ |
| 23195 | fputs("]\n", pArg->out); |
| 23196 | }else if( pArg->cMode==MODE_Count ){ |
| 23197 | char zBuf[200]; |
| 23198 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n", |
| 23199 | nRow, nRow!=1 ? "s" : ""); |
| 23200 | printf("%s", zBuf); |
| @@ -23239,10 +24472,11 @@ | |
| 23239 | int bCancel, |
| 23240 | char **pzErr |
| 23241 | ){ |
| 23242 | int rc = SQLITE_OK; |
| 23243 | sqlite3expert *p = pState->expert.pExpert; |
| 23244 | assert( p ); |
| 23245 | assert( bCancel || pzErr==0 || *pzErr==0 ); |
| 23246 | if( bCancel==0 ){ |
| 23247 | int bVerbose = pState->expert.bVerbose; |
| 23248 | |
| @@ -23251,24 +24485,25 @@ | |
| 23251 | int nQuery = sqlite3_expert_count(p); |
| 23252 | int i; |
| 23253 | |
| 23254 | if( bVerbose ){ |
| 23255 | const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); |
| 23256 | oputz("-- Candidates -----------------------------\n"); |
| 23257 | oputf("%s\n", zCand); |
| 23258 | } |
| 23259 | for(i=0; i<nQuery; i++){ |
| 23260 | const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL); |
| 23261 | const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES); |
| 23262 | const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN); |
| 23263 | if( zIdx==0 ) zIdx = "(no new indexes)\n"; |
| 23264 | if( bVerbose ){ |
| 23265 | oputf("-- Query %d --------------------------------\n",i+1); |
| 23266 | oputf("%s\n\n", zSql); |
| 23267 | } |
| 23268 | oputf("%s\n", zIdx); |
| 23269 | oputf("%s\n", zEQP); |
| 23270 | } |
| 23271 | } |
| 23272 | } |
| 23273 | sqlite3_expert_destroy(p); |
| 23274 | pState->expert.pExpert = 0; |
| @@ -23299,30 +24534,31 @@ | |
| 23299 | if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){ |
| 23300 | pState->expert.bVerbose = 1; |
| 23301 | } |
| 23302 | else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){ |
| 23303 | if( i==(nArg-1) ){ |
| 23304 | eputf("option requires an argument: %s\n", z); |
| 23305 | rc = SQLITE_ERROR; |
| 23306 | }else{ |
| 23307 | iSample = (int)integerValue(azArg[++i]); |
| 23308 | if( iSample<0 || iSample>100 ){ |
| 23309 | eputf("value out of range: %s\n", azArg[i]); |
| 23310 | rc = SQLITE_ERROR; |
| 23311 | } |
| 23312 | } |
| 23313 | } |
| 23314 | else{ |
| 23315 | eputf("unknown option: %s\n", z); |
| 23316 | rc = SQLITE_ERROR; |
| 23317 | } |
| 23318 | } |
| 23319 | |
| 23320 | if( rc==SQLITE_OK ){ |
| 23321 | pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); |
| 23322 | if( pState->expert.pExpert==0 ){ |
| 23323 | eputf("sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); |
| 23324 | rc = SQLITE_ERROR; |
| 23325 | }else{ |
| 23326 | sqlite3_expert_config( |
| 23327 | pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample |
| 23328 | ); |
| @@ -23647,33 +24883,33 @@ | |
| 23647 | if( zType==0 ) return 0; |
| 23648 | dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0; |
| 23649 | noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; |
| 23650 | |
| 23651 | if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ |
| 23652 | if( !dataOnly ) oputz("DELETE FROM sqlite_sequence;\n"); |
| 23653 | }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ |
| 23654 | if( !dataOnly ) oputz("ANALYZE sqlite_schema;\n"); |
| 23655 | }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ |
| 23656 | return 0; |
| 23657 | }else if( dataOnly ){ |
| 23658 | /* no-op */ |
| 23659 | }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ |
| 23660 | char *zIns; |
| 23661 | if( !p->writableSchema ){ |
| 23662 | oputz("PRAGMA writable_schema=ON;\n"); |
| 23663 | p->writableSchema = 1; |
| 23664 | } |
| 23665 | zIns = sqlite3_mprintf( |
| 23666 | "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" |
| 23667 | "VALUES('table','%q','%q',0,'%q');", |
| 23668 | zTable, zTable, zSql); |
| 23669 | shell_check_oom(zIns); |
| 23670 | oputf("%s\n", zIns); |
| 23671 | sqlite3_free(zIns); |
| 23672 | return 0; |
| 23673 | }else{ |
| 23674 | printSchemaLine(zSql, ";\n"); |
| 23675 | } |
| 23676 | |
| 23677 | if( cli_strcmp(zType, "table")==0 ){ |
| 23678 | ShellText sSelect; |
| 23679 | ShellText sTable; |
| @@ -23727,11 +24963,11 @@ | |
| 23727 | savedMode = p->mode; |
| 23728 | p->zDestTable = sTable.z; |
| 23729 | p->mode = p->cMode = MODE_Insert; |
| 23730 | rc = shell_exec(p, sSelect.z, 0); |
| 23731 | if( (rc&0xff)==SQLITE_CORRUPT ){ |
| 23732 | oputz("/****** CORRUPTION ERROR *******/\n"); |
| 23733 | toggleSelectOrder(p->db); |
| 23734 | shell_exec(p, sSelect.z, 0); |
| 23735 | toggleSelectOrder(p->db); |
| 23736 | } |
| 23737 | p->zDestTable = savedDestTable; |
| @@ -23758,28 +24994,28 @@ | |
| 23758 | char *zErr = 0; |
| 23759 | rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); |
| 23760 | if( rc==SQLITE_CORRUPT ){ |
| 23761 | char *zQ2; |
| 23762 | int len = strlen30(zQuery); |
| 23763 | oputz("/****** CORRUPTION ERROR *******/\n"); |
| 23764 | if( zErr ){ |
| 23765 | oputf("/****** %s ******/\n", zErr); |
| 23766 | sqlite3_free(zErr); |
| 23767 | zErr = 0; |
| 23768 | } |
| 23769 | zQ2 = malloc( len+100 ); |
| 23770 | if( zQ2==0 ) return rc; |
| 23771 | sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); |
| 23772 | rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); |
| 23773 | if( rc ){ |
| 23774 | oputf("/****** ERROR: %s ******/\n", zErr); |
| 23775 | }else{ |
| 23776 | rc = SQLITE_CORRUPT; |
| 23777 | } |
| 23778 | sqlite3_free(zErr); |
| 23779 | free(zQ2); |
| 23780 | } |
| 23781 | return rc; |
| 23782 | } |
| 23783 | |
| 23784 | /* |
| 23785 | ** Text of help messages. |
| @@ -23832,18 +25068,17 @@ | |
| 23832 | #ifndef SQLITE_SHELL_FIDDLE |
| 23833 | ".check GLOB Fail if output since .testcase does not match", |
| 23834 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 23835 | #endif |
| 23836 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 23837 | #if defined(_WIN32) || defined(WIN32) |
| 23838 | ".crnl on|off Translate \\n to \\r\\n. Default ON", |
| 23839 | #endif |
| 23840 | ".databases List names and files of attached databases", |
| 23841 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 23842 | #if SQLITE_SHELL_HAVE_RECOVER |
| 23843 | ".dbinfo ?DB? Show status information about the database", |
| 23844 | #endif |
| 23845 | ".dump ?OBJECTS? Render database content as SQL", |
| 23846 | " Options:", |
| 23847 | " --data-only Output only INSERT statements", |
| 23848 | " --newlines Allow unescaped newline characters in output", |
| 23849 | " --nosys Omit system tables (ex: \"sqlite_stat1\")", |
| @@ -23940,13 +25175,15 @@ | |
| 23940 | #endif |
| 23941 | ".nullvalue STRING Use STRING in place of NULL values", |
| 23942 | #ifndef SQLITE_SHELL_FIDDLE |
| 23943 | ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", |
| 23944 | " If FILE begins with '|' then open as a pipe", |
| 23945 | " --bom Put a UTF8 byte-order mark at the beginning", |
| 23946 | " -e Send output to the system text editor", |
| 23947 | " -x Send output as CSV to a spreadsheet (same as \".excel\")", |
| 23948 | /* Note that .open is (partially) available in WASM builds but is |
| 23949 | ** currently only intended to be used by the fiddle tool, not |
| 23950 | ** end users, so is "undocumented." */ |
| 23951 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 23952 | " Options:", |
| @@ -23965,10 +25202,12 @@ | |
| 23965 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 23966 | " If FILE begins with '|' then open it as a pipe.", |
| 23967 | " Options:", |
| 23968 | " --bom Prefix output with a UTF8 byte-order mark", |
| 23969 | " -e Send output to the system text editor", |
| 23970 | " -x Send output as CSV to a spreadsheet", |
| 23971 | #endif |
| 23972 | ".parameter CMD ... Manage SQL parameter bindings", |
| 23973 | " clear Erase all bindings", |
| 23974 | " init Initialize the TEMP table that holds bindings", |
| @@ -24078,10 +25317,14 @@ | |
| 24078 | ".vfsinfo ?AUX? Information about the top-level VFS", |
| 24079 | ".vfslist List all available VFSes", |
| 24080 | ".vfsname ?AUX? Print the name of the VFS stack", |
| 24081 | ".width NUM1 NUM2 ... Set minimum column widths for columnar output", |
| 24082 | " Negative values right-justify", |
| 24083 | }; |
| 24084 | |
| 24085 | /* |
| 24086 | ** Output help text. |
| 24087 | ** |
| @@ -24126,24 +25369,24 @@ | |
| 24126 | hh &= ~HH_Summary; |
| 24127 | break; |
| 24128 | } |
| 24129 | if( ((hw^hh)&HH_Undoc)==0 ){ |
| 24130 | if( (hh&HH_Summary)!=0 ){ |
| 24131 | sputf(out, ".%s\n", azHelp[i]+1); |
| 24132 | ++n; |
| 24133 | }else if( (hw&HW_SummaryOnly)==0 ){ |
| 24134 | sputf(out, "%s\n", azHelp[i]); |
| 24135 | } |
| 24136 | } |
| 24137 | } |
| 24138 | }else{ |
| 24139 | /* Seek documented commands for which zPattern is an exact prefix */ |
| 24140 | zPat = sqlite3_mprintf(".%s*", zPattern); |
| 24141 | shell_check_oom(zPat); |
| 24142 | for(i=0; i<ArraySize(azHelp); i++){ |
| 24143 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 24144 | sputf(out, "%s\n", azHelp[i]); |
| 24145 | j = i+1; |
| 24146 | n++; |
| 24147 | } |
| 24148 | } |
| 24149 | sqlite3_free(zPat); |
| @@ -24150,11 +25393,11 @@ | |
| 24150 | if( n ){ |
| 24151 | if( n==1 ){ |
| 24152 | /* when zPattern is a prefix of exactly one command, then include |
| 24153 | ** the details of that command, which should begin at offset j */ |
| 24154 | while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
| 24155 | sputf(out, "%s\n", azHelp[j]); |
| 24156 | j++; |
| 24157 | } |
| 24158 | } |
| 24159 | return n; |
| 24160 | } |
| @@ -24167,14 +25410,14 @@ | |
| 24167 | while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; |
| 24168 | continue; |
| 24169 | } |
| 24170 | if( azHelp[i][0]=='.' ) j = i; |
| 24171 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 24172 | sputf(out, "%s\n", azHelp[j]); |
| 24173 | while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ |
| 24174 | j++; |
| 24175 | sputf(out, "%s\n", azHelp[j]); |
| 24176 | } |
| 24177 | i = j; |
| 24178 | n++; |
| 24179 | } |
| 24180 | } |
| @@ -24200,35 +25443,35 @@ | |
| 24200 | ** |
| 24201 | ** NULL is returned if any error is encountered. The final value of *pnByte |
| 24202 | ** is undefined in this case. |
| 24203 | */ |
| 24204 | static char *readFile(const char *zName, int *pnByte){ |
| 24205 | FILE *in = fopen(zName, "rb"); |
| 24206 | long nIn; |
| 24207 | size_t nRead; |
| 24208 | char *pBuf; |
| 24209 | int rc; |
| 24210 | if( in==0 ) return 0; |
| 24211 | rc = fseek(in, 0, SEEK_END); |
| 24212 | if( rc!=0 ){ |
| 24213 | eputf("Error: '%s' not seekable\n", zName); |
| 24214 | fclose(in); |
| 24215 | return 0; |
| 24216 | } |
| 24217 | nIn = ftell(in); |
| 24218 | rewind(in); |
| 24219 | pBuf = sqlite3_malloc64( nIn+1 ); |
| 24220 | if( pBuf==0 ){ |
| 24221 | eputz("Error: out of memory\n"); |
| 24222 | fclose(in); |
| 24223 | return 0; |
| 24224 | } |
| 24225 | nRead = fread(pBuf, nIn, 1, in); |
| 24226 | fclose(in); |
| 24227 | if( nRead!=1 ){ |
| 24228 | sqlite3_free(pBuf); |
| 24229 | eputf("Error: cannot read '%s'\n", zName); |
| 24230 | return 0; |
| 24231 | } |
| 24232 | pBuf[nIn] = 0; |
| 24233 | if( pnByte ) *pnByte = nIn; |
| 24234 | return pBuf; |
| @@ -24290,11 +25533,11 @@ | |
| 24290 | ** archive and the dfltZip flag is true, then assume it is a ZIP archive. |
| 24291 | ** Otherwise, assume an ordinary database regardless of the filename if |
| 24292 | ** the type cannot be determined from content. |
| 24293 | */ |
| 24294 | int deduceDatabaseType(const char *zName, int dfltZip){ |
| 24295 | FILE *f = fopen(zName, "rb"); |
| 24296 | size_t n; |
| 24297 | int rc = SHELL_OPEN_UNSPEC; |
| 24298 | char zBuf[100]; |
| 24299 | if( f==0 ){ |
| 24300 | if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ |
| @@ -24343,13 +25586,13 @@ | |
| 24343 | FILE *in; |
| 24344 | const char *zDbFilename = p->pAuxDb->zDbFilename; |
| 24345 | unsigned int x[16]; |
| 24346 | char zLine[1000]; |
| 24347 | if( zDbFilename ){ |
| 24348 | in = fopen(zDbFilename, "r"); |
| 24349 | if( in==0 ){ |
| 24350 | eputf("cannot open \"%s\" for reading\n", zDbFilename); |
| 24351 | return 0; |
| 24352 | } |
| 24353 | nLine = 0; |
| 24354 | }else{ |
| 24355 | in = p->in; |
| @@ -24356,24 +25599,24 @@ | |
| 24356 | nLine = p->lineno; |
| 24357 | if( in==0 ) in = stdin; |
| 24358 | } |
| 24359 | *pnData = 0; |
| 24360 | nLine++; |
| 24361 | if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; |
| 24362 | rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); |
| 24363 | if( rc!=2 ) goto readHexDb_error; |
| 24364 | if( n<0 ) goto readHexDb_error; |
| 24365 | if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error; |
| 24366 | n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */ |
| 24367 | a = sqlite3_malloc( n ? n : 1 ); |
| 24368 | shell_check_oom(a); |
| 24369 | memset(a, 0, n); |
| 24370 | if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ |
| 24371 | eputz("invalid pagesize\n"); |
| 24372 | goto readHexDb_error; |
| 24373 | } |
| 24374 | for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ |
| 24375 | rc = sscanf(zLine, "| page %d offset %d", &j, &k); |
| 24376 | if( rc==2 ){ |
| 24377 | iOffset = k; |
| 24378 | continue; |
| 24379 | } |
| @@ -24401,18 +25644,18 @@ | |
| 24401 | |
| 24402 | readHexDb_error: |
| 24403 | if( in!=p->in ){ |
| 24404 | fclose(in); |
| 24405 | }else{ |
| 24406 | while( fgets(zLine, sizeof(zLine), p->in)!=0 ){ |
| 24407 | nLine++; |
| 24408 | if(cli_strncmp(zLine, "| end ", 6)==0 ) break; |
| 24409 | } |
| 24410 | p->lineno = nLine; |
| 24411 | } |
| 24412 | sqlite3_free(a); |
| 24413 | eputf("Error on line %d of --hexdb input\n", nLine); |
| 24414 | return 0; |
| 24415 | } |
| 24416 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 24417 | |
| 24418 | /* |
| @@ -24483,22 +25726,24 @@ | |
| 24483 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); |
| 24484 | break; |
| 24485 | } |
| 24486 | } |
| 24487 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 24488 | eputf("Error: unable to open database \"%s\": %s\n", |
| 24489 | zDbFilename, sqlite3_errmsg(p->db)); |
| 24490 | if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ |
| 24491 | exit(1); |
| 24492 | } |
| 24493 | sqlite3_close(p->db); |
| 24494 | sqlite3_open(":memory:", &p->db); |
| 24495 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 24496 | eputz("Also: unable to open substitute in-memory database.\n"); |
| 24497 | exit(1); |
| 24498 | }else{ |
| 24499 | eputf("Notice: using substitute in-memory database instead of \"%s\"\n", |
| 24500 | zDbFilename); |
| 24501 | } |
| 24502 | } |
| 24503 | globalDb = p->db; |
| 24504 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); |
| @@ -24511,10 +25756,11 @@ | |
| 24511 | } |
| 24512 | |
| 24513 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 24514 | sqlite3_enable_load_extension(p->db, 1); |
| 24515 | #endif |
| 24516 | sqlite3_shathree_init(p->db, 0, 0); |
| 24517 | sqlite3_uint_init(p->db, 0, 0); |
| 24518 | sqlite3_stmtrand_init(p->db, 0, 0); |
| 24519 | sqlite3_decimal_init(p->db, 0, 0); |
| 24520 | sqlite3_percentile_init(p->db, 0, 0); |
| @@ -24605,11 +25851,11 @@ | |
| 24605 | } |
| 24606 | rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, |
| 24607 | SQLITE_DESERIALIZE_RESIZEABLE | |
| 24608 | SQLITE_DESERIALIZE_FREEONCLOSE); |
| 24609 | if( rc ){ |
| 24610 | eputf("Error: sqlite3_deserialize() returns %d\n", rc); |
| 24611 | } |
| 24612 | if( p->szMax>0 ){ |
| 24613 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); |
| 24614 | } |
| 24615 | } |
| @@ -24629,15 +25875,17 @@ | |
| 24629 | ** Attempt to close the database connection. Report errors. |
| 24630 | */ |
| 24631 | void close_db(sqlite3 *db){ |
| 24632 | int rc = sqlite3_close(db); |
| 24633 | if( rc ){ |
| 24634 | eputf("Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); |
| 24635 | } |
| 24636 | } |
| 24637 | |
| 24638 | #if HAVE_READLINE || HAVE_EDITLINE |
| 24639 | /* |
| 24640 | ** Readline completion callbacks |
| 24641 | */ |
| 24642 | static char *readline_completion_generator(const char *text, int state){ |
| 24643 | static sqlite3_stmt *pStmt = 0; |
| @@ -24671,19 +25919,26 @@ | |
| 24671 | #elif HAVE_LINENOISE |
| 24672 | /* |
| 24673 | ** Linenoise completion callback. Note that the 3rd argument is from |
| 24674 | ** the "msteveb" version of linenoise, not the "antirez" version. |
| 24675 | */ |
| 24676 | static void linenoise_completion(const char *zLine, linenoiseCompletions *lc, |
| 24677 | void *pUserData){ |
| 24678 | i64 nLine = strlen(zLine); |
| 24679 | i64 i, iStart; |
| 24680 | sqlite3_stmt *pStmt = 0; |
| 24681 | char *zSql; |
| 24682 | char zBuf[1000]; |
| 24683 | |
| 24684 | UNUSED_PARAMETER(pUserData); |
| 24685 | if( nLine>(i64)sizeof(zBuf)-30 ) return; |
| 24686 | if( zLine[0]=='.' || zLine[0]=='#') return; |
| 24687 | for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} |
| 24688 | if( i==nLine-1 ) return; |
| 24689 | iStart = i+1; |
| @@ -24793,11 +26048,12 @@ | |
| 24793 | return 1; |
| 24794 | } |
| 24795 | if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ |
| 24796 | return 0; |
| 24797 | } |
| 24798 | eputf("ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); |
| 24799 | return 0; |
| 24800 | } |
| 24801 | |
| 24802 | /* |
| 24803 | ** Set or clear a shell flag according to a boolean value. |
| @@ -24820,22 +26076,22 @@ | |
| 24820 | /* |
| 24821 | ** Try to open an output file. The names "stdout" and "stderr" are |
| 24822 | ** recognized and do the right thing. NULL is returned if the output |
| 24823 | ** filename is "off". |
| 24824 | */ |
| 24825 | static FILE *output_file_open(const char *zFile, int bTextMode){ |
| 24826 | FILE *f; |
| 24827 | if( cli_strcmp(zFile,"stdout")==0 ){ |
| 24828 | f = stdout; |
| 24829 | }else if( cli_strcmp(zFile, "stderr")==0 ){ |
| 24830 | f = stderr; |
| 24831 | }else if( cli_strcmp(zFile, "off")==0 ){ |
| 24832 | f = 0; |
| 24833 | }else{ |
| 24834 | f = fopen(zFile, bTextMode ? "w" : "wb"); |
| 24835 | if( f==0 ){ |
| 24836 | eputf("Error: cannot open \"%s\"\n", zFile); |
| 24837 | } |
| 24838 | } |
| 24839 | return f; |
| 24840 | } |
| 24841 | |
| @@ -24884,16 +26140,17 @@ | |
| 24884 | if( nSql>1000000000 ) nSql = 1000000000; |
| 24885 | while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; } |
| 24886 | switch( mType ){ |
| 24887 | case SQLITE_TRACE_ROW: |
| 24888 | case SQLITE_TRACE_STMT: { |
| 24889 | sputf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 24890 | break; |
| 24891 | } |
| 24892 | case SQLITE_TRACE_PROFILE: { |
| 24893 | sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; |
| 24894 | sputf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); |
| 24895 | break; |
| 24896 | } |
| 24897 | } |
| 24898 | return 0; |
| 24899 | } |
| @@ -24996,14 +26253,15 @@ | |
| 24996 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 24997 | p->cTerm = c; |
| 24998 | break; |
| 24999 | } |
| 25000 | if( pc==cQuote && c!='\r' ){ |
| 25001 | eputf("%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote); |
| 25002 | } |
| 25003 | if( c==EOF ){ |
| 25004 | eputf("%s:%d: unterminated %c-quoted field\n", |
| 25005 | p->zFile, startLine, cQuote); |
| 25006 | p->cTerm = c; |
| 25007 | break; |
| 25008 | } |
| 25009 | import_append_char(p, c); |
| @@ -25098,11 +26356,11 @@ | |
| 25098 | |
| 25099 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); |
| 25100 | shell_check_oom(zQuery); |
| 25101 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25102 | if( rc ){ |
| 25103 | eputf("Error %d: %s on [%s]\n", |
| 25104 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 25105 | goto end_data_xfer; |
| 25106 | } |
| 25107 | n = sqlite3_column_count(pQuery); |
| 25108 | zInsert = sqlite3_malloc64(200 + nTable + n*3); |
| @@ -25115,11 +26373,11 @@ | |
| 25115 | i += 2; |
| 25116 | } |
| 25117 | memcpy(zInsert+i, ");", 3); |
| 25118 | rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); |
| 25119 | if( rc ){ |
| 25120 | eputf("Error %d: %s on [%s]\n", |
| 25121 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); |
| 25122 | goto end_data_xfer; |
| 25123 | } |
| 25124 | for(k=0; k<2; k++){ |
| 25125 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| @@ -25151,11 +26409,11 @@ | |
| 25151 | } |
| 25152 | } |
| 25153 | } /* End for */ |
| 25154 | rc = sqlite3_step(pInsert); |
| 25155 | if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 25156 | eputf("Error %d: %s\n", |
| 25157 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); |
| 25158 | } |
| 25159 | sqlite3_reset(pInsert); |
| 25160 | cnt++; |
| 25161 | if( (cnt%spinRate)==0 ){ |
| @@ -25169,11 +26427,11 @@ | |
| 25169 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", |
| 25170 | zTable); |
| 25171 | shell_check_oom(zQuery); |
| 25172 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25173 | if( rc ){ |
| 25174 | eputf("Warning: cannot step \"%s\" backwards", zTable); |
| 25175 | break; |
| 25176 | } |
| 25177 | } /* End for(k=0...) */ |
| 25178 | |
| 25179 | end_data_xfer: |
| @@ -25206,23 +26464,24 @@ | |
| 25206 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 25207 | " WHERE %s ORDER BY rowid ASC", zWhere); |
| 25208 | shell_check_oom(zQuery); |
| 25209 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25210 | if( rc ){ |
| 25211 | eputf("Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), |
| 25212 | sqlite3_errmsg(p->db), zQuery); |
| 25213 | goto end_schema_xfer; |
| 25214 | } |
| 25215 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 25216 | zName = sqlite3_column_text(pQuery, 0); |
| 25217 | zSql = sqlite3_column_text(pQuery, 1); |
| 25218 | if( zName==0 || zSql==0 ) continue; |
| 25219 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ |
| 25220 | sputf(stdout, "%s... ", zName); fflush(stdout); |
| 25221 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 25222 | if( zErrMsg ){ |
| 25223 | eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 25224 | sqlite3_free(zErrMsg); |
| 25225 | zErrMsg = 0; |
| 25226 | } |
| 25227 | } |
| 25228 | if( xForEach ){ |
| @@ -25236,23 +26495,23 @@ | |
| 25236 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 25237 | " WHERE %s ORDER BY rowid DESC", zWhere); |
| 25238 | shell_check_oom(zQuery); |
| 25239 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 25240 | if( rc ){ |
| 25241 | eputf("Error: (%d) %s on [%s]\n", |
| 25242 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 25243 | goto end_schema_xfer; |
| 25244 | } |
| 25245 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 25246 | zName = sqlite3_column_text(pQuery, 0); |
| 25247 | zSql = sqlite3_column_text(pQuery, 1); |
| 25248 | if( zName==0 || zSql==0 ) continue; |
| 25249 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; |
| 25250 | sputf(stdout, "%s... ", zName); fflush(stdout); |
| 25251 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 25252 | if( zErrMsg ){ |
| 25253 | eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 25254 | sqlite3_free(zErrMsg); |
| 25255 | zErrMsg = 0; |
| 25256 | } |
| 25257 | if( xForEach ){ |
| 25258 | xForEach(p, newDb, (const char*)zName); |
| @@ -25272,16 +26531,17 @@ | |
| 25272 | */ |
| 25273 | static void tryToClone(ShellState *p, const char *zNewDb){ |
| 25274 | int rc; |
| 25275 | sqlite3 *newDb = 0; |
| 25276 | if( access(zNewDb,0)==0 ){ |
| 25277 | eputf("File \"%s\" already exists.\n", zNewDb); |
| 25278 | return; |
| 25279 | } |
| 25280 | rc = sqlite3_open(zNewDb, &newDb); |
| 25281 | if( rc ){ |
| 25282 | eputf("Cannot create output database: %s\n", sqlite3_errmsg(newDb)); |
| 25283 | }else{ |
| 25284 | sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); |
| 25285 | sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); |
| 25286 | tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); |
| 25287 | tryToCloneSchema(p, newDb, "type!='table'", 0); |
| @@ -25294,14 +26554,22 @@ | |
| 25294 | #ifndef SQLITE_SHELL_FIDDLE |
| 25295 | /* |
| 25296 | ** Change the output stream (file or pipe or console) to something else. |
| 25297 | */ |
| 25298 | static void output_redir(ShellState *p, FILE *pfNew){ |
| 25299 | if( p->out != stdout ) eputz("Output already redirected.\n"); |
| 25300 | else{ |
| 25301 | p->out = pfNew; |
| 25302 | setOutputStream(pfNew); |
| 25303 | } |
| 25304 | } |
| 25305 | |
| 25306 | /* |
| 25307 | ** Change the output file back to stdout. |
| @@ -25314,10 +26582,13 @@ | |
| 25314 | if( p->outfile[0]=='|' ){ |
| 25315 | #ifndef SQLITE_OMIT_POPEN |
| 25316 | pclose(p->out); |
| 25317 | #endif |
| 25318 | }else{ |
| 25319 | output_file_close(p->out); |
| 25320 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 25321 | if( p->doXdgOpen ){ |
| 25322 | const char *zXdgOpenCmd = |
| 25323 | #if defined(_WIN32) |
| @@ -25328,11 +26599,11 @@ | |
| 25328 | "xdg-open"; |
| 25329 | #endif |
| 25330 | char *zCmd; |
| 25331 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 25332 | if( system(zCmd) ){ |
| 25333 | eputf("Failed: [%s]\n", zCmd); |
| 25334 | }else{ |
| 25335 | /* Give the start/open/xdg-open command some time to get |
| 25336 | ** going before we continue, and potential delete the |
| 25337 | ** p->zTempFile data file out from under it */ |
| 25338 | sqlite3_sleep(2000); |
| @@ -25343,28 +26614,34 @@ | |
| 25343 | } |
| 25344 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 25345 | } |
| 25346 | p->outfile[0] = 0; |
| 25347 | p->out = stdout; |
| 25348 | setOutputStream(stdout); |
| 25349 | } |
| 25350 | #else |
| 25351 | # define output_redir(SS,pfO) |
| 25352 | # define output_reset(SS) |
| 25353 | #endif |
| 25354 | |
| 25355 | /* |
| 25356 | ** Run an SQL command and return the single integer result. |
| 25357 | */ |
| 25358 | static int db_int(sqlite3 *db, const char *zSql){ |
| 25359 | sqlite3_stmt *pStmt; |
| 25360 | int res = 0; |
| 25361 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 25362 | if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 25363 | res = sqlite3_column_int(pStmt,0); |
| 25364 | } |
| 25365 | sqlite3_finalize(pStmt); |
| 25366 | return res; |
| 25367 | } |
| 25368 | |
| 25369 | #if SQLITE_SHELL_HAVE_RECOVER |
| 25370 | /* |
| @@ -25419,11 +26696,11 @@ | |
| 25419 | if( p->db==0 ) return 1; |
| 25420 | rc = sqlite3_prepare_v2(p->db, |
| 25421 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 25422 | -1, &pStmt, 0); |
| 25423 | if( rc ){ |
| 25424 | eputf("error: %s\n", sqlite3_errmsg(p->db)); |
| 25425 | sqlite3_finalize(pStmt); |
| 25426 | return 1; |
| 25427 | } |
| 25428 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 25429 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| @@ -25432,58 +26709,149 @@ | |
| 25432 | const u8 *pb = sqlite3_column_blob(pStmt,0); |
| 25433 | shell_check_oom(pb); |
| 25434 | memcpy(aHdr, pb, 100); |
| 25435 | sqlite3_finalize(pStmt); |
| 25436 | }else{ |
| 25437 | eputz("unable to read database header\n"); |
| 25438 | sqlite3_finalize(pStmt); |
| 25439 | return 1; |
| 25440 | } |
| 25441 | i = get2byteInt(aHdr+16); |
| 25442 | if( i==1 ) i = 65536; |
| 25443 | oputf("%-20s %d\n", "database page size:", i); |
| 25444 | oputf("%-20s %d\n", "write format:", aHdr[18]); |
| 25445 | oputf("%-20s %d\n", "read format:", aHdr[19]); |
| 25446 | oputf("%-20s %d\n", "reserved bytes:", aHdr[20]); |
| 25447 | for(i=0; i<ArraySize(aField); i++){ |
| 25448 | int ofst = aField[i].ofst; |
| 25449 | unsigned int val = get4byteInt(aHdr + ofst); |
| 25450 | oputf("%-20s %u", aField[i].zName, val); |
| 25451 | switch( ofst ){ |
| 25452 | case 56: { |
| 25453 | if( val==1 ) oputz(" (utf8)"); |
| 25454 | if( val==2 ) oputz(" (utf16le)"); |
| 25455 | if( val==3 ) oputz(" (utf16be)"); |
| 25456 | } |
| 25457 | } |
| 25458 | oputz("\n"); |
| 25459 | } |
| 25460 | if( zDb==0 ){ |
| 25461 | zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); |
| 25462 | }else if( cli_strcmp(zDb,"temp")==0 ){ |
| 25463 | zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema"); |
| 25464 | }else{ |
| 25465 | zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); |
| 25466 | } |
| 25467 | for(i=0; i<ArraySize(aQuery); i++){ |
| 25468 | char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); |
| 25469 | int val = db_int(p->db, zSql); |
| 25470 | sqlite3_free(zSql); |
| 25471 | oputf("%-20s %d\n", aQuery[i].zName, val); |
| 25472 | } |
| 25473 | sqlite3_free(zSchemaTab); |
| 25474 | sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); |
| 25475 | oputf("%-20s %u\n", "data version", iDataVersion); |
| 25476 | return 0; |
| 25477 | } |
| 25478 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 25479 | |
| 25480 | /* |
| 25481 | ** Print the given string as an error message. |
| 25482 | */ |
| 25483 | static void shellEmitError(const char *zErr){ |
| 25484 | eputf("Error: %s\n", zErr); |
| 25485 | } |
| 25486 | /* |
| 25487 | ** Print the current sqlite3_errmsg() value to stderr and return 1. |
| 25488 | */ |
| 25489 | static int shellDatabaseError(sqlite3 *db){ |
| @@ -25726,10 +27094,11 @@ | |
| 25726 | int bGroupByParent = 0; /* If -groupbyparent is present */ |
| 25727 | int i; /* To iterate through azArg[] */ |
| 25728 | const char *zIndent = ""; /* How much to indent CREATE INDEX by */ |
| 25729 | int rc; /* Return code */ |
| 25730 | sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ |
| 25731 | |
| 25732 | /* |
| 25733 | ** This SELECT statement returns one row for each foreign key constraint |
| 25734 | ** in the schema of the main database. The column values are: |
| 25735 | ** |
| @@ -25801,11 +27170,12 @@ | |
| 25801 | else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ |
| 25802 | bGroupByParent = 1; |
| 25803 | zIndent = " "; |
| 25804 | } |
| 25805 | else{ |
| 25806 | eputf("Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); |
| 25807 | return SQLITE_ERROR; |
| 25808 | } |
| 25809 | } |
| 25810 | |
| 25811 | /* Register the fkey_collate_clause() SQL function */ |
| @@ -25845,44 +27215,45 @@ | |
| 25845 | } |
| 25846 | rc = sqlite3_finalize(pExplain); |
| 25847 | if( rc!=SQLITE_OK ) break; |
| 25848 | |
| 25849 | if( res<0 ){ |
| 25850 | eputz("Error: internal error"); |
| 25851 | break; |
| 25852 | }else{ |
| 25853 | if( bGroupByParent |
| 25854 | && (bVerbose || res==0) |
| 25855 | && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) |
| 25856 | ){ |
| 25857 | oputf("-- Parent table %s\n", zParent); |
| 25858 | sqlite3_free(zPrev); |
| 25859 | zPrev = sqlite3_mprintf("%s", zParent); |
| 25860 | } |
| 25861 | |
| 25862 | if( res==0 ){ |
| 25863 | oputf("%s%s --> %s\n", zIndent, zCI, zTarget); |
| 25864 | }else if( bVerbose ){ |
| 25865 | oputf("%s/* no extra indexes required for %s -> %s */\n", |
| 25866 | zIndent, zFrom, zTarget |
| 25867 | ); |
| 25868 | } |
| 25869 | } |
| 25870 | } |
| 25871 | sqlite3_free(zPrev); |
| 25872 | |
| 25873 | if( rc!=SQLITE_OK ){ |
| 25874 | eputf("%s\n", sqlite3_errmsg(db)); |
| 25875 | } |
| 25876 | |
| 25877 | rc2 = sqlite3_finalize(pSql); |
| 25878 | if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ |
| 25879 | rc = rc2; |
| 25880 | eputf("%s\n", sqlite3_errmsg(db)); |
| 25881 | } |
| 25882 | }else{ |
| 25883 | eputf("%s\n", sqlite3_errmsg(db)); |
| 25884 | } |
| 25885 | |
| 25886 | return rc; |
| 25887 | } |
| 25888 | |
| @@ -25898,13 +27269,13 @@ | |
| 25898 | n = (nArg>=2 ? strlen30(azArg[1]) : 0); |
| 25899 | if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; |
| 25900 | return lintFkeyIndexes(pState, azArg, nArg); |
| 25901 | |
| 25902 | usage: |
| 25903 | eputf("Usage %s sub-command ?switches...?\n", azArg[0]); |
| 25904 | eputz("Where sub-commands are:\n"); |
| 25905 | eputz(" fkey-indexes\n"); |
| 25906 | return SQLITE_ERROR; |
| 25907 | } |
| 25908 | |
| 25909 | static void shellPrepare( |
| 25910 | sqlite3 *db, |
| @@ -25914,11 +27285,12 @@ | |
| 25914 | ){ |
| 25915 | *ppStmt = 0; |
| 25916 | if( *pRc==SQLITE_OK ){ |
| 25917 | int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); |
| 25918 | if( rc!=SQLITE_OK ){ |
| 25919 | eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); |
| 25920 | *pRc = rc; |
| 25921 | } |
| 25922 | } |
| 25923 | } |
| 25924 | |
| @@ -25958,11 +27330,11 @@ | |
| 25958 | if( pStmt ){ |
| 25959 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 25960 | int rc = sqlite3_finalize(pStmt); |
| 25961 | if( *pRc==SQLITE_OK ){ |
| 25962 | if( rc!=SQLITE_OK ){ |
| 25963 | eputf("SQL error: %s\n", sqlite3_errmsg(db)); |
| 25964 | } |
| 25965 | *pRc = rc; |
| 25966 | } |
| 25967 | } |
| 25968 | } |
| @@ -25980,11 +27352,11 @@ | |
| 25980 | ){ |
| 25981 | int rc = sqlite3_reset(pStmt); |
| 25982 | if( *pRc==SQLITE_OK ){ |
| 25983 | if( rc!=SQLITE_OK ){ |
| 25984 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 25985 | eputf("SQL error: %s\n", sqlite3_errmsg(db)); |
| 25986 | } |
| 25987 | *pRc = rc; |
| 25988 | } |
| 25989 | } |
| 25990 | #endif /* !defined SQLITE_OMIT_VIRTUALTABLE */ |
| @@ -26009,10 +27381,11 @@ | |
| 26009 | char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ |
| 26010 | const char *zFile; /* --file argument, or NULL */ |
| 26011 | const char *zDir; /* --directory argument, or NULL */ |
| 26012 | char **azArg; /* Array of command arguments */ |
| 26013 | ShellState *p; /* Shell state */ |
| 26014 | sqlite3 *db; /* Database containing the archive */ |
| 26015 | }; |
| 26016 | |
| 26017 | /* |
| 26018 | ** Print a usage message for the .ar command to stderr and return SQLITE_ERROR. |
| @@ -26032,13 +27405,13 @@ | |
| 26032 | va_start(ap, zFmt); |
| 26033 | z = sqlite3_vmprintf(zFmt, ap); |
| 26034 | va_end(ap); |
| 26035 | shellEmitError(z); |
| 26036 | if( pAr->fromCmdLine ){ |
| 26037 | eputz("Use \"-A\" for more help\n"); |
| 26038 | }else{ |
| 26039 | eputz("Use \".archive --help\" for more help\n"); |
| 26040 | } |
| 26041 | sqlite3_free(z); |
| 26042 | return SQLITE_ERROR; |
| 26043 | } |
| 26044 | |
| @@ -26134,11 +27507,11 @@ | |
| 26134 | }; |
| 26135 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); |
| 26136 | struct ArSwitch *pEnd = &aSwitch[nSwitch]; |
| 26137 | |
| 26138 | if( nArg<=1 ){ |
| 26139 | eputz("Wrong number of arguments. Usage:\n"); |
| 26140 | return arUsage(stderr); |
| 26141 | }else{ |
| 26142 | char *z = azArg[1]; |
| 26143 | if( z[0]!='-' ){ |
| 26144 | /* Traditional style [tar] invocation */ |
| @@ -26240,11 +27613,11 @@ | |
| 26240 | } |
| 26241 | } |
| 26242 | } |
| 26243 | } |
| 26244 | if( pAr->eCmd==0 ){ |
| 26245 | eputz("Required argument missing. Usage:\n"); |
| 26246 | return arUsage(stderr); |
| 26247 | } |
| 26248 | return SQLITE_OK; |
| 26249 | } |
| 26250 | |
| @@ -26283,11 +27656,11 @@ | |
| 26283 | if( SQLITE_ROW==sqlite3_step(pTest) ){ |
| 26284 | bOk = 1; |
| 26285 | } |
| 26286 | shellReset(&rc, pTest); |
| 26287 | if( rc==SQLITE_OK && bOk==0 ){ |
| 26288 | eputf("not found in archive: %s\n", z); |
| 26289 | rc = SQLITE_ERROR; |
| 26290 | } |
| 26291 | } |
| 26292 | shellFinalize(&rc, pTest); |
| 26293 | } |
| @@ -26350,19 +27723,19 @@ | |
| 26350 | arWhereClause(&rc, pAr, &zWhere); |
| 26351 | |
| 26352 | shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], |
| 26353 | pAr->zSrcTable, zWhere); |
| 26354 | if( pAr->bDryRun ){ |
| 26355 | oputf("%s\n", sqlite3_sql(pSql)); |
| 26356 | }else{ |
| 26357 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 26358 | if( pAr->bVerbose ){ |
| 26359 | oputf("%s % 10d %s %s\n", |
| 26360 | sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), |
| 26361 | sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); |
| 26362 | }else{ |
| 26363 | oputf("%s\n", sqlite3_column_text(pSql, 0)); |
| 26364 | } |
| 26365 | } |
| 26366 | } |
| 26367 | shellFinalize(&rc, pSql); |
| 26368 | sqlite3_free(zWhere); |
| @@ -26385,11 +27758,11 @@ | |
| 26385 | } |
| 26386 | if( rc==SQLITE_OK ){ |
| 26387 | zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", |
| 26388 | pAr->zSrcTable, zWhere); |
| 26389 | if( pAr->bDryRun ){ |
| 26390 | oputf("%s\n", zSql); |
| 26391 | }else{ |
| 26392 | char *zErr = 0; |
| 26393 | rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); |
| 26394 | if( rc==SQLITE_OK ){ |
| 26395 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| @@ -26398,11 +27771,11 @@ | |
| 26398 | }else{ |
| 26399 | rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0); |
| 26400 | } |
| 26401 | } |
| 26402 | if( zErr ){ |
| 26403 | sputf(stdout, "ERROR: %s\n", zErr); /* stdout? */ |
| 26404 | sqlite3_free(zErr); |
| 26405 | } |
| 26406 | } |
| 26407 | } |
| 26408 | sqlite3_free(zWhere); |
| @@ -26462,15 +27835,15 @@ | |
| 26462 | ** populating them changes the timestamp). */ |
| 26463 | for(i=0; i<2; i++){ |
| 26464 | j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); |
| 26465 | sqlite3_bind_int(pSql, j, i); |
| 26466 | if( pAr->bDryRun ){ |
| 26467 | oputf("%s\n", sqlite3_sql(pSql)); |
| 26468 | }else{ |
| 26469 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 26470 | if( i==0 && pAr->bVerbose ){ |
| 26471 | oputf("%s\n", sqlite3_column_text(pSql, 0)); |
| 26472 | } |
| 26473 | } |
| 26474 | } |
| 26475 | shellReset(&rc, pSql); |
| 26476 | } |
| @@ -26486,17 +27859,17 @@ | |
| 26486 | ** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out. |
| 26487 | */ |
| 26488 | static int arExecSql(ArCommand *pAr, const char *zSql){ |
| 26489 | int rc; |
| 26490 | if( pAr->bDryRun ){ |
| 26491 | oputf("%s\n", zSql); |
| 26492 | rc = SQLITE_OK; |
| 26493 | }else{ |
| 26494 | char *zErr = 0; |
| 26495 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| 26496 | if( zErr ){ |
| 26497 | sputf(stdout, "ERROR: %s\n", zErr); |
| 26498 | sqlite3_free(zErr); |
| 26499 | } |
| 26500 | } |
| 26501 | return rc; |
| 26502 | } |
| @@ -26641,10 +28014,11 @@ | |
| 26641 | cmd.fromCmdLine = fromCmdLine; |
| 26642 | rc = arParseCommand(azArg, nArg, &cmd); |
| 26643 | if( rc==SQLITE_OK ){ |
| 26644 | int eDbType = SHELL_OPEN_UNSPEC; |
| 26645 | cmd.p = pState; |
| 26646 | cmd.db = pState->db; |
| 26647 | if( cmd.zFile ){ |
| 26648 | eDbType = deduceDatabaseType(cmd.zFile, 1); |
| 26649 | }else{ |
| 26650 | eDbType = pState->openMode; |
| @@ -26667,17 +28041,18 @@ | |
| 26667 | }else{ |
| 26668 | flags = SQLITE_OPEN_READONLY; |
| 26669 | } |
| 26670 | cmd.db = 0; |
| 26671 | if( cmd.bDryRun ){ |
| 26672 | oputf("-- open database '%s'%s\n", cmd.zFile, |
| 26673 | eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); |
| 26674 | } |
| 26675 | rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, |
| 26676 | eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); |
| 26677 | if( rc!=SQLITE_OK ){ |
| 26678 | eputf("cannot open file: %s (%s)\n", cmd.zFile, sqlite3_errmsg(cmd.db)); |
| 26679 | goto end_ar_command; |
| 26680 | } |
| 26681 | sqlite3_fileio_init(cmd.db, 0, 0); |
| 26682 | sqlite3_sqlar_init(cmd.db, 0, 0); |
| 26683 | sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, |
| @@ -26686,11 +28061,11 @@ | |
| 26686 | } |
| 26687 | if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ |
| 26688 | if( cmd.eCmd!=AR_CMD_CREATE |
| 26689 | && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) |
| 26690 | ){ |
| 26691 | eputz("database does not contain an 'sqlar' table\n"); |
| 26692 | rc = SQLITE_ERROR; |
| 26693 | goto end_ar_command; |
| 26694 | } |
| 26695 | cmd.zSrcTable = sqlite3_mprintf("sqlar"); |
| 26696 | } |
| @@ -26744,11 +28119,11 @@ | |
| 26744 | ** This function is used as a callback by the recover extension. Simply |
| 26745 | ** print the supplied SQL statement to stdout. |
| 26746 | */ |
| 26747 | static int recoverSqlCb(void *pCtx, const char *zSql){ |
| 26748 | ShellState *pState = (ShellState*)pCtx; |
| 26749 | sputf(pState->out, "%s;\n", zSql); |
| 26750 | return SQLITE_OK; |
| 26751 | } |
| 26752 | |
| 26753 | /* |
| 26754 | ** This function is called to recover data from the database. A script |
| @@ -26787,11 +28162,11 @@ | |
| 26787 | }else |
| 26788 | if( n<=10 && memcmp("-no-rowids", z, n)==0 ){ |
| 26789 | bRowids = 0; |
| 26790 | } |
| 26791 | else{ |
| 26792 | eputf("unexpected option: %s\n", azArg[i]); |
| 26793 | showHelp(pState->out, azArg[0]); |
| 26794 | return 1; |
| 26795 | } |
| 26796 | } |
| 26797 | |
| @@ -26806,11 +28181,11 @@ | |
| 26806 | |
| 26807 | sqlite3_recover_run(p); |
| 26808 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 26809 | const char *zErr = sqlite3_recover_errmsg(p); |
| 26810 | int errCode = sqlite3_recover_errcode(p); |
| 26811 | eputf("sql error: %s (%d)\n", zErr, errCode); |
| 26812 | } |
| 26813 | rc = sqlite3_recover_finish(p); |
| 26814 | return rc; |
| 26815 | } |
| 26816 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| @@ -26828,25 +28203,25 @@ | |
| 26828 | i64 nError = 0; |
| 26829 | const char *zErr = 0; |
| 26830 | while( SQLITE_OK==sqlite3_intck_step(p) ){ |
| 26831 | const char *zMsg = sqlite3_intck_message(p); |
| 26832 | if( zMsg ){ |
| 26833 | oputf("%s\n", zMsg); |
| 26834 | nError++; |
| 26835 | } |
| 26836 | nStep++; |
| 26837 | if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){ |
| 26838 | sqlite3_intck_unlock(p); |
| 26839 | } |
| 26840 | } |
| 26841 | rc = sqlite3_intck_error(p, &zErr); |
| 26842 | if( zErr ){ |
| 26843 | eputf("%s\n", zErr); |
| 26844 | } |
| 26845 | sqlite3_intck_close(p); |
| 26846 | |
| 26847 | oputf("%lld steps, %lld errors\n", nStep, nError); |
| 26848 | } |
| 26849 | |
| 26850 | return rc; |
| 26851 | } |
| 26852 | |
| @@ -26865,11 +28240,11 @@ | |
| 26865 | */ |
| 26866 | #ifdef SHELL_DEBUG |
| 26867 | #define rc_err_oom_die(rc) \ |
| 26868 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ |
| 26869 | else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ |
| 26870 | eputf("E:%d\n",rc), assert(0) |
| 26871 | #else |
| 26872 | static void rc_err_oom_die(int rc){ |
| 26873 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); |
| 26874 | assert(rc==SQLITE_OK||rc==SQLITE_DONE); |
| 26875 | } |
| @@ -27023,12 +28398,12 @@ | |
| 27023 | }else if( *pDb==0 ){ |
| 27024 | return 0; |
| 27025 | }else{ |
| 27026 | /* Formulate the columns spec, close the DB, zero *pDb. */ |
| 27027 | char *zColsSpec = 0; |
| 27028 | int hasDupes = db_int(*pDb, zHasDupes); |
| 27029 | int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0; |
| 27030 | if( hasDupes ){ |
| 27031 | #ifdef SHELL_COLUMN_RENAME_CLEAN |
| 27032 | rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0); |
| 27033 | rc_err_oom_die(rc); |
| 27034 | #endif |
| @@ -27039,11 +28414,11 @@ | |
| 27039 | sqlite3_bind_int(pStmt, 1, nDigits); |
| 27040 | rc = sqlite3_step(pStmt); |
| 27041 | sqlite3_finalize(pStmt); |
| 27042 | if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); |
| 27043 | } |
| 27044 | assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ |
| 27045 | rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 27046 | rc_err_oom_die(rc); |
| 27047 | rc = sqlite3_step(pStmt); |
| 27048 | if( rc==SQLITE_ROW ){ |
| 27049 | zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); |
| @@ -27082,12 +28457,13 @@ | |
| 27082 | shellPreparePrintf(p->db, &rc, &pStmt, |
| 27083 | "SELECT 1 FROM sqlite_schema o WHERE " |
| 27084 | "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true" |
| 27085 | ); |
| 27086 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 27087 | oputz("/* WARNING: " |
| 27088 | "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n" |
| 27089 | ); |
| 27090 | } |
| 27091 | shellFinalize(&rc, pStmt); |
| 27092 | return rc; |
| 27093 | } |
| @@ -27114,16 +28490,18 @@ | |
| 27114 | return SQLITE_OK; |
| 27115 | } |
| 27116 | if( faultsim_state.iCnt ){ |
| 27117 | if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; |
| 27118 | if( faultsim_state.eVerbose>=2 ){ |
| 27119 | oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); |
| 27120 | } |
| 27121 | return SQLITE_OK; |
| 27122 | } |
| 27123 | if( faultsim_state.eVerbose>=1 ){ |
| 27124 | oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); |
| 27125 | } |
| 27126 | faultsim_state.iCnt = faultsim_state.iInterval; |
| 27127 | faultsim_state.nHit++; |
| 27128 | if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){ |
| 27129 | faultsim_state.iCnt = -1; |
| @@ -27182,11 +28560,11 @@ | |
| 27182 | clearTempFile(p); |
| 27183 | |
| 27184 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 27185 | if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ |
| 27186 | if( nArg!=2 ){ |
| 27187 | eputz("Usage: .auth ON|OFF\n"); |
| 27188 | rc = 1; |
| 27189 | goto meta_command_exit; |
| 27190 | } |
| 27191 | open_db(p, 0); |
| 27192 | if( booleanValue(azArg[1]) ){ |
| @@ -27229,32 +28607,32 @@ | |
| 27229 | }else |
| 27230 | if( cli_strcmp(z, "-async")==0 ){ |
| 27231 | bAsync = 1; |
| 27232 | }else |
| 27233 | { |
| 27234 | eputf("unknown option: %s\n", azArg[j]); |
| 27235 | return 1; |
| 27236 | } |
| 27237 | }else if( zDestFile==0 ){ |
| 27238 | zDestFile = azArg[j]; |
| 27239 | }else if( zDb==0 ){ |
| 27240 | zDb = zDestFile; |
| 27241 | zDestFile = azArg[j]; |
| 27242 | }else{ |
| 27243 | eputz("Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); |
| 27244 | return 1; |
| 27245 | } |
| 27246 | } |
| 27247 | if( zDestFile==0 ){ |
| 27248 | eputz("missing FILENAME argument on .backup\n"); |
| 27249 | return 1; |
| 27250 | } |
| 27251 | if( zDb==0 ) zDb = "main"; |
| 27252 | rc = sqlite3_open_v2(zDestFile, &pDest, |
| 27253 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); |
| 27254 | if( rc!=SQLITE_OK ){ |
| 27255 | eputf("Error: cannot open \"%s\"\n", zDestFile); |
| 27256 | close_db(pDest); |
| 27257 | return 1; |
| 27258 | } |
| 27259 | if( bAsync ){ |
| 27260 | sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;", |
| @@ -27286,23 +28664,14 @@ | |
| 27286 | eputz("Usage: .bail on|off\n"); |
| 27287 | rc = 1; |
| 27288 | } |
| 27289 | }else |
| 27290 | |
| 27291 | /* Undocumented. Legacy only. See "crnl" below */ |
| 27292 | if( c=='b' && n>=3 && cli_strncmp(azArg[0], "binary", n)==0 ){ |
| 27293 | if( nArg==2 ){ |
| 27294 | if( booleanValue(azArg[1]) ){ |
| 27295 | setBinaryMode(p->out, 1); |
| 27296 | }else{ |
| 27297 | setTextMode(p->out, 1); |
| 27298 | } |
| 27299 | }else{ |
| 27300 | eputz("The \".binary\" command is deprecated. Use \".crnl\" instead.\n" |
| 27301 | "Usage: .binary on|off\n"); |
| 27302 | rc = 1; |
| 27303 | } |
| 27304 | }else |
| 27305 | |
| 27306 | /* The undocumented ".breakpoint" command causes a call to the no-op |
| 27307 | ** routine named test_breakpoint(). |
| 27308 | */ |
| @@ -27320,11 +28689,11 @@ | |
| 27320 | sqlite3_free(z); |
| 27321 | #else |
| 27322 | rc = chdir(azArg[1]); |
| 27323 | #endif |
| 27324 | if( rc ){ |
| 27325 | eputf("Cannot change to directory \"%s\"\n", azArg[1]); |
| 27326 | rc = 1; |
| 27327 | } |
| 27328 | }else{ |
| 27329 | eputz("Usage: .cd DIRECTORY\n"); |
| 27330 | rc = 1; |
| @@ -27353,15 +28722,16 @@ | |
| 27353 | eputz("Usage: .check GLOB-PATTERN\n"); |
| 27354 | rc = 2; |
| 27355 | }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ |
| 27356 | rc = 2; |
| 27357 | }else if( testcase_glob(azArg[1],zRes)==0 ){ |
| 27358 | eputf("testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", |
| 27359 | p->zTestcase, azArg[1], zRes); |
| 27360 | rc = 1; |
| 27361 | }else{ |
| 27362 | oputf("testcase-%s ok\n", p->zTestcase); |
| 27363 | p->nCheck++; |
| 27364 | } |
| 27365 | sqlite3_free(zRes); |
| 27366 | }else |
| 27367 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| @@ -27390,13 +28760,13 @@ | |
| 27390 | zFile = "(memory)"; |
| 27391 | }else if( zFile[0]==0 ){ |
| 27392 | zFile = "(temporary-file)"; |
| 27393 | } |
| 27394 | if( p->pAuxDb == &p->aAuxDb[i] ){ |
| 27395 | sputf(stdout, "ACTIVE %d: %s\n", i, zFile); |
| 27396 | }else if( p->aAuxDb[i].db!=0 ){ |
| 27397 | sputf(stdout, " %d: %s\n", i, zFile); |
| 27398 | } |
| 27399 | } |
| 27400 | }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ |
| 27401 | int i = azArg[1][0] - '0'; |
| 27402 | if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){ |
| @@ -27422,24 +28792,22 @@ | |
| 27422 | eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n"); |
| 27423 | rc = 1; |
| 27424 | } |
| 27425 | }else |
| 27426 | |
| 27427 | if( c=='c' && n==4 && cli_strncmp(azArg[0], "crnl", n)==0 ){ |
| 27428 | if( nArg==2 ){ |
| 27429 | if( booleanValue(azArg[1]) ){ |
| 27430 | setTextMode(p->out, 1); |
| 27431 | }else{ |
| 27432 | setBinaryMode(p->out, 1); |
| 27433 | } |
| 27434 | }else{ |
| 27435 | #if !defined(_WIN32) && !defined(WIN32) |
| 27436 | eputz("The \".crnl\" is a no-op on non-Windows machines.\n"); |
| 27437 | #endif |
| 27438 | eputz("Usage: .crnl on|off\n"); |
| 27439 | rc = 1; |
| 27440 | } |
| 27441 | }else |
| 27442 | |
| 27443 | if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){ |
| 27444 | char **azName = 0; |
| 27445 | int nName = 0; |
| @@ -27465,11 +28833,11 @@ | |
| 27465 | sqlite3_finalize(pStmt); |
| 27466 | for(i=0; i<nName; i++){ |
| 27467 | int eTxn = sqlite3_txn_state(p->db, azName[i*2]); |
| 27468 | int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); |
| 27469 | const char *z = azName[i*2+1]; |
| 27470 | oputf("%s: %s %s%s\n", |
| 27471 | azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", |
| 27472 | eTxn==SQLITE_TXN_NONE ? "" : |
| 27473 | eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); |
| 27474 | free(azName[i*2]); |
| 27475 | free(azName[i*2+1]); |
| @@ -27507,15 +28875,16 @@ | |
| 27507 | if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; |
| 27508 | if( nArg>=3 ){ |
| 27509 | sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); |
| 27510 | } |
| 27511 | sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); |
| 27512 | oputf("%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); |
| 27513 | if( nArg>1 ) break; |
| 27514 | } |
| 27515 | if( nArg>1 && ii==ArraySize(aDbConfig) ){ |
| 27516 | eputf("Error: unknown dbconfig \"%s\"\n", azArg[1]); |
| 27517 | eputz("Enter \".dbconfig\" with no arguments for a list\n"); |
| 27518 | } |
| 27519 | }else |
| 27520 | |
| 27521 | #if SQLITE_SHELL_HAVE_RECOVER |
| @@ -27561,11 +28930,12 @@ | |
| 27561 | }else |
| 27562 | if( cli_strcmp(z,"nosys")==0 ){ |
| 27563 | ShellSetFlag(p, SHFLG_DumpNoSys); |
| 27564 | }else |
| 27565 | { |
| 27566 | eputf("Unknown option \"%s\" on \".dump\"\n", azArg[i]); |
| 27567 | rc = 1; |
| 27568 | sqlite3_free(zLike); |
| 27569 | goto meta_command_exit; |
| 27570 | } |
| 27571 | }else{ |
| @@ -27596,12 +28966,12 @@ | |
| 27596 | outputDumpWarning(p, zLike); |
| 27597 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 27598 | /* When playing back a "dump", the content might appear in an order |
| 27599 | ** which causes immediate foreign key constraints to be violated. |
| 27600 | ** So disable foreign-key constraint enforcement to prevent problems. */ |
| 27601 | oputz("PRAGMA foreign_keys=OFF;\n"); |
| 27602 | oputz("BEGIN TRANSACTION;\n"); |
| 27603 | } |
| 27604 | p->writableSchema = 0; |
| 27605 | p->showHeader = 0; |
| 27606 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 27607 | ** as much of the schema as it can even if the sqlite_schema table is |
| @@ -27629,17 +28999,17 @@ | |
| 27629 | run_table_dump_query(p, zSql); |
| 27630 | sqlite3_free(zSql); |
| 27631 | } |
| 27632 | sqlite3_free(zLike); |
| 27633 | if( p->writableSchema ){ |
| 27634 | oputz("PRAGMA writable_schema=OFF;\n"); |
| 27635 | p->writableSchema = 0; |
| 27636 | } |
| 27637 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| 27638 | sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); |
| 27639 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 27640 | oputz(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); |
| 27641 | } |
| 27642 | p->showHeader = savedShowHeader; |
| 27643 | p->shellFlgs = savedShellFlags; |
| 27644 | }else |
| 27645 | |
| @@ -27649,10 +29019,14 @@ | |
| 27649 | }else{ |
| 27650 | eputz("Usage: .echo on|off\n"); |
| 27651 | rc = 1; |
| 27652 | } |
| 27653 | }else |
| 27654 | |
| 27655 | if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){ |
| 27656 | if( nArg==2 ){ |
| 27657 | p->autoEQPtest = 0; |
| 27658 | if( p->autoEQPtrace ){ |
| @@ -27715,11 +29089,12 @@ | |
| 27715 | }else |
| 27716 | |
| 27717 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 27718 | if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ |
| 27719 | if( p->bSafeMode ){ |
| 27720 | eputf("Cannot run experimental commands such as \"%s\" in safe mode\n", |
| 27721 | azArg[0]); |
| 27722 | rc = 1; |
| 27723 | }else{ |
| 27724 | open_db(p, 0); |
| 27725 | expertDotCommand(p, azArg, nArg); |
| @@ -27772,13 +29147,14 @@ | |
| 27772 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 27773 | } |
| 27774 | |
| 27775 | /* --help lists all file-controls */ |
| 27776 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 27777 | oputz("Available file-controls:\n"); |
| 27778 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 27779 | oputf(" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 27780 | } |
| 27781 | rc = 1; |
| 27782 | goto meta_command_exit; |
| 27783 | } |
| 27784 | |
| @@ -27789,19 +29165,19 @@ | |
| 27789 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 27790 | if( filectrl<0 ){ |
| 27791 | filectrl = aCtrl[i].ctrlCode; |
| 27792 | iCtrl = i; |
| 27793 | }else{ |
| 27794 | eputf("Error: ambiguous file-control: \"%s\"\n" |
| 27795 | "Use \".filectrl --help\" for help\n", zCmd); |
| 27796 | rc = 1; |
| 27797 | goto meta_command_exit; |
| 27798 | } |
| 27799 | } |
| 27800 | } |
| 27801 | if( filectrl<0 ){ |
| 27802 | eputf("Error: unknown file-control: %s\n" |
| 27803 | "Use \".filectrl --help\" for help\n", zCmd); |
| 27804 | }else{ |
| 27805 | switch(filectrl){ |
| 27806 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 27807 | if( nArg!=2 && nArg!=3 ) break; |
| @@ -27841,11 +29217,11 @@ | |
| 27841 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 27842 | char *z = 0; |
| 27843 | if( nArg!=2 ) break; |
| 27844 | sqlite3_file_control(p->db, zSchema, filectrl, &z); |
| 27845 | if( z ){ |
| 27846 | oputf("%s\n", z); |
| 27847 | sqlite3_free(z); |
| 27848 | } |
| 27849 | isOk = 2; |
| 27850 | break; |
| 27851 | } |
| @@ -27855,23 +29231,24 @@ | |
| 27855 | x = atoi(azArg[2]); |
| 27856 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 27857 | } |
| 27858 | x = -1; |
| 27859 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 27860 | oputf("%d\n", x); |
| 27861 | isOk = 2; |
| 27862 | break; |
| 27863 | } |
| 27864 | } |
| 27865 | } |
| 27866 | if( isOk==0 && iCtrl>=0 ){ |
| 27867 | oputf("Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| 27868 | rc = 1; |
| 27869 | }else if( isOk==1 ){ |
| 27870 | char zBuf[100]; |
| 27871 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); |
| 27872 | oputf("%s\n", zBuf); |
| 27873 | } |
| 27874 | }else |
| 27875 | |
| 27876 | if( c=='f' && cli_strncmp(azArg[0], "fullschema", n)==0 ){ |
| 27877 | ShellState data; |
| @@ -27908,19 +29285,19 @@ | |
| 27908 | doStats = sqlite3_step(pStmt)==SQLITE_ROW; |
| 27909 | sqlite3_finalize(pStmt); |
| 27910 | } |
| 27911 | } |
| 27912 | if( doStats==0 ){ |
| 27913 | oputz("/* No STAT tables available */\n"); |
| 27914 | }else{ |
| 27915 | oputz("ANALYZE sqlite_schema;\n"); |
| 27916 | data.cMode = data.mode = MODE_Insert; |
| 27917 | data.zDestTable = "sqlite_stat1"; |
| 27918 | shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); |
| 27919 | data.zDestTable = "sqlite_stat4"; |
| 27920 | shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); |
| 27921 | oputz("ANALYZE sqlite_schema;\n"); |
| 27922 | } |
| 27923 | }else |
| 27924 | |
| 27925 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 27926 | if( nArg==2 ){ |
| @@ -27934,11 +29311,11 @@ | |
| 27934 | |
| 27935 | if( c=='h' && cli_strncmp(azArg[0], "help", n)==0 ){ |
| 27936 | if( nArg>=2 ){ |
| 27937 | n = showHelp(p->out, azArg[1]); |
| 27938 | if( n==0 ){ |
| 27939 | oputf("Nothing matches '%s'\n", azArg[1]); |
| 27940 | } |
| 27941 | }else{ |
| 27942 | showHelp(p->out, 0); |
| 27943 | } |
| 27944 | }else |
| @@ -27977,11 +29354,11 @@ | |
| 27977 | if( zFile==0 ){ |
| 27978 | zFile = z; |
| 27979 | }else if( zTable==0 ){ |
| 27980 | zTable = z; |
| 27981 | }else{ |
| 27982 | oputf("ERROR: extra argument: \"%s\". Usage:\n", z); |
| 27983 | showHelp(p->out, "import"); |
| 27984 | goto meta_command_exit; |
| 27985 | } |
| 27986 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 27987 | eVerbose++; |
| @@ -27998,17 +29375,17 @@ | |
| 27998 | sCtx.cColSep = ','; |
| 27999 | sCtx.cRowSep = '\n'; |
| 28000 | xRead = csv_read_one_field; |
| 28001 | useOutputMode = 0; |
| 28002 | }else{ |
| 28003 | oputf("ERROR: unknown option: \"%s\". Usage:\n", z); |
| 28004 | showHelp(p->out, "import"); |
| 28005 | goto meta_command_exit; |
| 28006 | } |
| 28007 | } |
| 28008 | if( zTable==0 ){ |
| 28009 | oputf("ERROR: missing %s argument. Usage:\n", |
| 28010 | zFile==0 ? "FILE" : "TABLE"); |
| 28011 | showHelp(p->out, "import"); |
| 28012 | goto meta_command_exit; |
| 28013 | } |
| 28014 | seenInterrupt = 0; |
| @@ -28054,32 +29431,32 @@ | |
| 28054 | if( sCtx.zFile[0]=='|' ){ |
| 28055 | #ifdef SQLITE_OMIT_POPEN |
| 28056 | eputz("Error: pipes are not supported in this OS\n"); |
| 28057 | goto meta_command_exit; |
| 28058 | #else |
| 28059 | sCtx.in = popen(sCtx.zFile+1, "r"); |
| 28060 | sCtx.zFile = "<pipe>"; |
| 28061 | sCtx.xCloser = pclose; |
| 28062 | #endif |
| 28063 | }else{ |
| 28064 | sCtx.in = fopen(sCtx.zFile, "rb"); |
| 28065 | sCtx.xCloser = fclose; |
| 28066 | } |
| 28067 | if( sCtx.in==0 ){ |
| 28068 | eputf("Error: cannot open \"%s\"\n", zFile); |
| 28069 | goto meta_command_exit; |
| 28070 | } |
| 28071 | if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 28072 | char zSep[2]; |
| 28073 | zSep[1] = 0; |
| 28074 | zSep[0] = sCtx.cColSep; |
| 28075 | oputz("Column separator "); |
| 28076 | output_c_string(zSep); |
| 28077 | oputz(", row separator "); |
| 28078 | zSep[0] = sCtx.cRowSep; |
| 28079 | output_c_string(zSep); |
| 28080 | oputz("\n"); |
| 28081 | } |
| 28082 | sCtx.z = sqlite3_malloc64(120); |
| 28083 | if( sCtx.z==0 ){ |
| 28084 | import_cleanup(&sCtx); |
| 28085 | shell_out_of_memory(); |
| @@ -28087,11 +29464,15 @@ | |
| 28087 | /* Below, resources must be freed before exit. */ |
| 28088 | while( (nSkip--)>0 ){ |
| 28089 | while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} |
| 28090 | } |
| 28091 | import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 28092 | if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){ |
| 28093 | /* Table does not exist. Create it. */ |
| 28094 | sqlite3 *dbCols = 0; |
| 28095 | char *zRenames = 0; |
| 28096 | char *zColDefs; |
| 28097 | zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
| @@ -28100,18 +29481,18 @@ | |
| 28100 | zAutoColumn(sCtx.z, &dbCols, 0); |
| 28101 | if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 28102 | } |
| 28103 | zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
| 28104 | if( zRenames!=0 ){ |
| 28105 | sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr, |
| 28106 | "Columns renamed during .import %s due to duplicates:\n" |
| 28107 | "%s\n", sCtx.zFile, zRenames); |
| 28108 | sqlite3_free(zRenames); |
| 28109 | } |
| 28110 | assert(dbCols==0); |
| 28111 | if( zColDefs==0 ){ |
| 28112 | eputf("%s: empty file\n", sCtx.zFile); |
| 28113 | import_cleanup(&sCtx); |
| 28114 | rc = 1; |
| 28115 | sqlite3_free(zCreate); |
| 28116 | goto meta_command_exit; |
| 28117 | } |
| @@ -28119,17 +29500,20 @@ | |
| 28119 | if( zCreate==0 ){ |
| 28120 | import_cleanup(&sCtx); |
| 28121 | shell_out_of_memory(); |
| 28122 | } |
| 28123 | if( eVerbose>=1 ){ |
| 28124 | oputf("%s\n", zCreate); |
| 28125 | } |
| 28126 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 28127 | sqlite3_free(zCreate); |
| 28128 | zCreate = 0; |
| 28129 | if( rc ){ |
| 28130 | eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); |
| 28131 | import_cleanup(&sCtx); |
| 28132 | rc = 1; |
| 28133 | goto meta_command_exit; |
| 28134 | } |
| 28135 | } |
| @@ -28180,11 +29564,11 @@ | |
| 28180 | } |
| 28181 | zSql[j++] = ')'; |
| 28182 | zSql[j] = 0; |
| 28183 | assert( j<nByte ); |
| 28184 | if( eVerbose>=2 ){ |
| 28185 | oputf("Insert using: %s\n", zSql); |
| 28186 | } |
| 28187 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 28188 | sqlite3_free(zSql); |
| 28189 | zSql = 0; |
| 28190 | if( rc ){ |
| @@ -28219,11 +29603,11 @@ | |
| 28219 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 28220 | z = ""; |
| 28221 | } |
| 28222 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 28223 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 28224 | eputf("%s:%d: expected %d columns but found %d" |
| 28225 | " - filling the rest with NULL\n", |
| 28226 | sCtx.zFile, startLine, nCol, i+1); |
| 28227 | i += 2; |
| 28228 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 28229 | } |
| @@ -28231,18 +29615,19 @@ | |
| 28231 | if( sCtx.cTerm==sCtx.cColSep ){ |
| 28232 | do{ |
| 28233 | xRead(&sCtx); |
| 28234 | i++; |
| 28235 | }while( sCtx.cTerm==sCtx.cColSep ); |
| 28236 | eputf("%s:%d: expected %d columns but found %d - extras ignored\n", |
| 28237 | sCtx.zFile, startLine, nCol, i); |
| 28238 | } |
| 28239 | if( i>=nCol ){ |
| 28240 | sqlite3_step(pStmt); |
| 28241 | rc = sqlite3_reset(pStmt); |
| 28242 | if( rc!=SQLITE_OK ){ |
| 28243 | eputf("%s:%d: INSERT failed: %s\n", |
| 28244 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 28245 | sCtx.nErr++; |
| 28246 | }else{ |
| 28247 | sCtx.nRow++; |
| 28248 | } |
| @@ -28251,11 +29636,12 @@ | |
| 28251 | |
| 28252 | import_cleanup(&sCtx); |
| 28253 | sqlite3_finalize(pStmt); |
| 28254 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 28255 | if( eVerbose>0 ){ |
| 28256 | oputf("Added %d rows with %d errors using %d lines of input\n", |
| 28257 | sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 28258 | } |
| 28259 | }else |
| 28260 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 28261 | |
| @@ -28267,11 +29653,11 @@ | |
| 28267 | int tnum = 0; |
| 28268 | int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ |
| 28269 | int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ |
| 28270 | int i; |
| 28271 | if( !ShellHasFlag(p,SHFLG_TestingMode) ){ |
| 28272 | eputf(".%s unavailable without --unsafe-testing\n", |
| 28273 | "imposter"); |
| 28274 | rc = 1; |
| 28275 | goto meta_command_exit; |
| 28276 | } |
| 28277 | if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ |
| @@ -28333,11 +29719,11 @@ | |
| 28333 | zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); |
| 28334 | } |
| 28335 | } |
| 28336 | sqlite3_finalize(pStmt); |
| 28337 | if( i==0 || tnum==0 ){ |
| 28338 | eputf("no such index: \"%s\"\n", azArg[1]); |
| 28339 | rc = 1; |
| 28340 | sqlite3_free(zCollist); |
| 28341 | goto meta_command_exit; |
| 28342 | } |
| 28343 | if( lenPK==0 ) lenPK = 100000; |
| @@ -28348,18 +29734,20 @@ | |
| 28348 | rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); |
| 28349 | if( rc==SQLITE_OK ){ |
| 28350 | rc = sqlite3_exec(p->db, zSql, 0, 0, 0); |
| 28351 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); |
| 28352 | if( rc ){ |
| 28353 | eputf("Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); |
| 28354 | }else{ |
| 28355 | sputf(stdout, "%s;\n", zSql); |
| 28356 | sputf(stdout, "WARNING: writing to an imposter table will corrupt" |
| 28357 | " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index"); |
| 28358 | } |
| 28359 | }else{ |
| 28360 | eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); |
| 28361 | rc = 1; |
| 28362 | } |
| 28363 | sqlite3_free(zSql); |
| 28364 | }else |
| 28365 | #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ |
| @@ -28369,11 +29757,11 @@ | |
| 28369 | if( nArg==2 ){ |
| 28370 | iArg = integerValue(azArg[1]); |
| 28371 | if( iArg==0 ) iArg = -1; |
| 28372 | } |
| 28373 | if( (nArg!=1 && nArg!=2) || iArg<0 ){ |
| 28374 | eputf("%s","Usage: .intck STEPS_PER_UNLOCK\n"); |
| 28375 | rc = 1; |
| 28376 | goto meta_command_exit; |
| 28377 | } |
| 28378 | open_db(p, 0); |
| 28379 | rc = intckDatabaseCmd(p, iArg); |
| @@ -28388,13 +29776,13 @@ | |
| 28388 | sqlite3IoTrace = 0; |
| 28389 | }else if( cli_strcmp(azArg[1], "-")==0 ){ |
| 28390 | sqlite3IoTrace = iotracePrintf; |
| 28391 | iotrace = stdout; |
| 28392 | }else{ |
| 28393 | iotrace = fopen(azArg[1], "w"); |
| 28394 | if( iotrace==0 ){ |
| 28395 | eputf("Error: cannot open \"%s\"\n", azArg[1]); |
| 28396 | sqlite3IoTrace = 0; |
| 28397 | rc = 1; |
| 28398 | }else{ |
| 28399 | sqlite3IoTrace = iotracePrintf; |
| 28400 | } |
| @@ -28422,11 +29810,11 @@ | |
| 28422 | }; |
| 28423 | int i, n2; |
| 28424 | open_db(p, 0); |
| 28425 | if( nArg==1 ){ |
| 28426 | for(i=0; i<ArraySize(aLimit); i++){ |
| 28427 | sputf(stdout, "%20s %d\n", aLimit[i].zLimitName, |
| 28428 | sqlite3_limit(p->db, aLimit[i].limitCode, -1)); |
| 28429 | } |
| 28430 | }else if( nArg>3 ){ |
| 28431 | eputz("Usage: .limit NAME ?NEW-VALUE?\n"); |
| 28432 | rc = 1; |
| @@ -28437,28 +29825,28 @@ | |
| 28437 | for(i=0; i<ArraySize(aLimit); i++){ |
| 28438 | if( sqlite3_strnicmp(aLimit[i].zLimitName, azArg[1], n2)==0 ){ |
| 28439 | if( iLimit<0 ){ |
| 28440 | iLimit = i; |
| 28441 | }else{ |
| 28442 | eputf("ambiguous limit: \"%s\"\n", azArg[1]); |
| 28443 | rc = 1; |
| 28444 | goto meta_command_exit; |
| 28445 | } |
| 28446 | } |
| 28447 | } |
| 28448 | if( iLimit<0 ){ |
| 28449 | eputf("unknown limit: \"%s\"\n" |
| 28450 | "enter \".limits\" with no arguments for a list.\n", |
| 28451 | azArg[1]); |
| 28452 | rc = 1; |
| 28453 | goto meta_command_exit; |
| 28454 | } |
| 28455 | if( nArg==3 ){ |
| 28456 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 28457 | (int)integerValue(azArg[2])); |
| 28458 | } |
| 28459 | sputf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 28460 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 28461 | } |
| 28462 | }else |
| 28463 | |
| 28464 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| @@ -28503,11 +29891,11 @@ | |
| 28503 | " than \"on\" or \"off\"\n"); |
| 28504 | zFile = "off"; |
| 28505 | } |
| 28506 | output_file_close(p->pLog); |
| 28507 | if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout"; |
| 28508 | p->pLog = output_file_open(zFile, 0); |
| 28509 | } |
| 28510 | }else |
| 28511 | |
| 28512 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 28513 | const char *zMode = 0; |
| @@ -28537,35 +29925,37 @@ | |
| 28537 | cmOpts = cmo; |
| 28538 | } |
| 28539 | }else if( zTabname==0 ){ |
| 28540 | zTabname = z; |
| 28541 | }else if( z[0]=='-' ){ |
| 28542 | eputf("unknown option: %s\n", z); |
| 28543 | eputz("options:\n" |
| 28544 | " --noquote\n" |
| 28545 | " --quote\n" |
| 28546 | " --wordwrap on/off\n" |
| 28547 | " --wrap N\n" |
| 28548 | " --ww\n"); |
| 28549 | rc = 1; |
| 28550 | goto meta_command_exit; |
| 28551 | }else{ |
| 28552 | eputf("extra argument: \"%s\"\n", z); |
| 28553 | rc = 1; |
| 28554 | goto meta_command_exit; |
| 28555 | } |
| 28556 | } |
| 28557 | if( zMode==0 ){ |
| 28558 | if( p->mode==MODE_Column |
| 28559 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 28560 | ){ |
| 28561 | oputf("current output mode: %s --wrap %d --wordwrap %s --%squote\n", |
| 28562 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 28563 | p->cmOpts.bWordWrap ? "on" : "off", |
| 28564 | p->cmOpts.bQuote ? "" : "no"); |
| 28565 | }else{ |
| 28566 | oputf("current output mode: %s\n", modeDescr[p->mode]); |
| 28567 | } |
| 28568 | zMode = modeDescr[p->mode]; |
| 28569 | } |
| 28570 | n2 = strlen30(zMode); |
| 28571 | if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| @@ -28634,11 +30024,11 @@ | |
| 28634 | if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ |
| 28635 | if( nArg!=2 ){ |
| 28636 | eputz("Usage: .nonce NONCE\n"); |
| 28637 | rc = 1; |
| 28638 | }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ |
| 28639 | eputf("line %d: incorrect nonce: \"%s\"\n", |
| 28640 | p->lineno, azArg[1]); |
| 28641 | exit(1); |
| 28642 | }else{ |
| 28643 | p->bSafeMode = 0; |
| 28644 | return 0; /* Return immediately to bypass the safe mode reset |
| @@ -28689,15 +30079,15 @@ | |
| 28689 | p->szMax = integerValue(azArg[++iName]); |
| 28690 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 28691 | }else |
| 28692 | #endif /* !SQLITE_SHELL_FIDDLE */ |
| 28693 | if( z[0]=='-' ){ |
| 28694 | eputf("unknown option: %s\n", z); |
| 28695 | rc = 1; |
| 28696 | goto meta_command_exit; |
| 28697 | }else if( zFN ){ |
| 28698 | eputf("extra argument: \"%s\"\n", z); |
| 28699 | rc = 1; |
| 28700 | goto meta_command_exit; |
| 28701 | }else{ |
| 28702 | zFN = z; |
| 28703 | } |
| @@ -28735,11 +30125,11 @@ | |
| 28735 | zNewFilename = 0; |
| 28736 | } |
| 28737 | p->pAuxDb->zDbFilename = zNewFilename; |
| 28738 | open_db(p, OPEN_DB_KEEPALIVE); |
| 28739 | if( p->db==0 ){ |
| 28740 | eputf("Error: cannot open '%s'\n", zNewFilename); |
| 28741 | sqlite3_free(zNewFilename); |
| 28742 | }else{ |
| 28743 | p->pAuxDb->zFreeOnClose = zNewFilename; |
| 28744 | } |
| 28745 | } |
| @@ -28753,22 +30143,26 @@ | |
| 28753 | #ifndef SQLITE_SHELL_FIDDLE |
| 28754 | if( (c=='o' |
| 28755 | && (cli_strncmp(azArg[0], "output", n)==0 |
| 28756 | || cli_strncmp(azArg[0], "once", n)==0)) |
| 28757 | || (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0) |
| 28758 | ){ |
| 28759 | char *zFile = 0; |
| 28760 | int bTxtMode = 0; |
| 28761 | int i; |
| 28762 | int eMode = 0; |
| 28763 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ |
| 28764 | static const char *zBomUtf8 = "\xef\xbb\xbf"; |
| 28765 | const char *zBom = 0; |
| 28766 | |
| 28767 | failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); |
| 28768 | if( c=='e' ){ |
| 28769 | eMode = 'x'; |
| 28770 | bOnce = 2; |
| 28771 | }else if( cli_strncmp(azArg[0],"once",n)==0 ){ |
| 28772 | bOnce = 1; |
| 28773 | } |
| 28774 | for(i=1; i<nArg; i++){ |
| @@ -28775,28 +30169,34 @@ | |
| 28775 | char *z = azArg[i]; |
| 28776 | if( z[0]=='-' ){ |
| 28777 | if( z[1]=='-' ) z++; |
| 28778 | if( cli_strcmp(z,"-bom")==0 ){ |
| 28779 | zBom = zBomUtf8; |
| 28780 | }else if( c!='e' && cli_strcmp(z,"-x")==0 ){ |
| 28781 | eMode = 'x'; /* spreadsheet */ |
| 28782 | }else if( c!='e' && cli_strcmp(z,"-e")==0 ){ |
| 28783 | eMode = 'e'; /* text editor */ |
| 28784 | }else{ |
| 28785 | oputf("ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); |
| 28786 | showHelp(p->out, azArg[0]); |
| 28787 | rc = 1; |
| 28788 | goto meta_command_exit; |
| 28789 | } |
| 28790 | }else if( zFile==0 && eMode!='e' && eMode!='x' ){ |
| 28791 | zFile = sqlite3_mprintf("%s", z); |
| 28792 | if( zFile && zFile[0]=='|' ){ |
| 28793 | while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); |
| 28794 | break; |
| 28795 | } |
| 28796 | }else{ |
| 28797 | oputf("ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); |
| 28798 | showHelp(p->out, azArg[0]); |
| 28799 | rc = 1; |
| 28800 | sqlite3_free(zFile); |
| 28801 | goto meta_command_exit; |
| 28802 | } |
| @@ -28809,24 +30209,31 @@ | |
| 28809 | }else{ |
| 28810 | p->outCount = 0; |
| 28811 | } |
| 28812 | output_reset(p); |
| 28813 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 28814 | if( eMode=='e' || eMode=='x' ){ |
| 28815 | p->doXdgOpen = 1; |
| 28816 | outputModePush(p); |
| 28817 | if( eMode=='x' ){ |
| 28818 | /* spreadsheet mode. Output as CSV. */ |
| 28819 | newTempFile(p, "csv"); |
| 28820 | ShellClearFlag(p, SHFLG_Echo); |
| 28821 | p->mode = MODE_Csv; |
| 28822 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 28823 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 28824 | }else{ |
| 28825 | /* text editor mode */ |
| 28826 | newTempFile(p, "txt"); |
| 28827 | bTxtMode = 1; |
| 28828 | } |
| 28829 | sqlite3_free(zFile); |
| 28830 | zFile = sqlite3_mprintf("%s", p->zTempFile); |
| 28831 | } |
| 28832 | #endif /* SQLITE_NOHAVE_SYSTEM */ |
| @@ -28835,30 +30242,36 @@ | |
| 28835 | #ifdef SQLITE_OMIT_POPEN |
| 28836 | eputz("Error: pipes are not supported in this OS\n"); |
| 28837 | rc = 1; |
| 28838 | output_redir(p, stdout); |
| 28839 | #else |
| 28840 | FILE *pfPipe = popen(zFile + 1, "w"); |
| 28841 | if( pfPipe==0 ){ |
| 28842 | eputf("Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 28843 | rc = 1; |
| 28844 | }else{ |
| 28845 | output_redir(p, pfPipe); |
| 28846 | if( zBom ) oputz(zBom); |
| 28847 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 28848 | } |
| 28849 | #endif |
| 28850 | }else{ |
| 28851 | FILE *pfFile = output_file_open(zFile, bTxtMode); |
| 28852 | if( pfFile==0 ){ |
| 28853 | if( cli_strcmp(zFile,"off")!=0 ){ |
| 28854 | eputf("Error: cannot write to \"%s\"\n", zFile); |
| 28855 | } |
| 28856 | rc = 1; |
| 28857 | } else { |
| 28858 | output_redir(p, pfFile); |
| 28859 | if( zBom ) oputz(zBom); |
| 28860 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 28861 | } |
| 28862 | } |
| 28863 | sqlite3_free(zFile); |
| 28864 | }else |
| @@ -28895,11 +30308,12 @@ | |
| 28895 | if( len ){ |
| 28896 | rx = sqlite3_prepare_v2(p->db, |
| 28897 | "SELECT key, quote(value) " |
| 28898 | "FROM temp.sqlite_parameters;", -1, &pStmt, 0); |
| 28899 | while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 28900 | oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), |
| 28901 | sqlite3_column_text(pStmt,1)); |
| 28902 | } |
| 28903 | sqlite3_finalize(pStmt); |
| 28904 | } |
| 28905 | }else |
| @@ -28940,11 +30354,11 @@ | |
| 28940 | "VALUES(%Q,%Q);", zKey, zValue); |
| 28941 | shell_check_oom(zSql); |
| 28942 | rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 28943 | sqlite3_free(zSql); |
| 28944 | if( rx!=SQLITE_OK ){ |
| 28945 | oputf("Error: %s\n", sqlite3_errmsg(p->db)); |
| 28946 | sqlite3_finalize(pStmt); |
| 28947 | pStmt = 0; |
| 28948 | rc = 1; |
| 28949 | } |
| 28950 | } |
| @@ -28969,14 +30383,14 @@ | |
| 28969 | }else |
| 28970 | |
| 28971 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ |
| 28972 | int i; |
| 28973 | for(i=1; i<nArg; i++){ |
| 28974 | if( i>1 ) oputz(" "); |
| 28975 | oputz(azArg[i]); |
| 28976 | } |
| 28977 | oputz("\n"); |
| 28978 | }else |
| 28979 | |
| 28980 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 28981 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "progress", n)==0 ){ |
| 28982 | int i; |
| @@ -29009,11 +30423,11 @@ | |
| 29009 | }else{ |
| 29010 | p->mxProgress = (int)integerValue(azArg[++i]); |
| 29011 | } |
| 29012 | continue; |
| 29013 | } |
| 29014 | eputf("Error: unknown option: \"%s\"\n", azArg[i]); |
| 29015 | rc = 1; |
| 29016 | goto meta_command_exit; |
| 29017 | }else{ |
| 29018 | nn = (int)integerValue(z); |
| 29019 | } |
| @@ -29050,23 +30464,22 @@ | |
| 29050 | } |
| 29051 | if( azArg[1][0]=='|' ){ |
| 29052 | #ifdef SQLITE_OMIT_POPEN |
| 29053 | eputz("Error: pipes are not supported in this OS\n"); |
| 29054 | rc = 1; |
| 29055 | p->out = stdout; |
| 29056 | #else |
| 29057 | p->in = popen(azArg[1]+1, "r"); |
| 29058 | if( p->in==0 ){ |
| 29059 | eputf("Error: cannot open \"%s\"\n", azArg[1]); |
| 29060 | rc = 1; |
| 29061 | }else{ |
| 29062 | rc = process_input(p); |
| 29063 | pclose(p->in); |
| 29064 | } |
| 29065 | #endif |
| 29066 | }else if( (p->in = openChrSource(azArg[1]))==0 ){ |
| 29067 | eputf("Error: cannot open \"%s\"\n", azArg[1]); |
| 29068 | rc = 1; |
| 29069 | }else{ |
| 29070 | rc = process_input(p); |
| 29071 | fclose(p->in); |
| 29072 | } |
| @@ -29095,11 +30508,11 @@ | |
| 29095 | rc = 1; |
| 29096 | goto meta_command_exit; |
| 29097 | } |
| 29098 | rc = sqlite3_open(zSrcFile, &pSrc); |
| 29099 | if( rc!=SQLITE_OK ){ |
| 29100 | eputf("Error: cannot open \"%s\"\n", zSrcFile); |
| 29101 | close_db(pSrc); |
| 29102 | return 1; |
| 29103 | } |
| 29104 | open_db(p, 0); |
| 29105 | pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); |
| @@ -29127,11 +30540,14 @@ | |
| 29127 | } |
| 29128 | close_db(pSrc); |
| 29129 | }else |
| 29130 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 29131 | |
| 29132 | if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){ |
| 29133 | if( nArg==2 ){ |
| 29134 | if( cli_strcmp(azArg[1], "vm")==0 ){ |
| 29135 | p->scanstatsOn = 3; |
| 29136 | }else |
| 29137 | if( cli_strcmp(azArg[1], "est")==0 ){ |
| @@ -29178,11 +30594,11 @@ | |
| 29178 | }else if( optionMatch(azArg[ii],"debug") ){ |
| 29179 | bDebug = 1; |
| 29180 | }else if( optionMatch(azArg[ii],"nosys") ){ |
| 29181 | bNoSystemTabs = 1; |
| 29182 | }else if( azArg[ii][0]=='-' ){ |
| 29183 | eputf("Unknown option: \"%s\"\n", azArg[ii]); |
| 29184 | rc = 1; |
| 29185 | goto meta_command_exit; |
| 29186 | }else if( zName==0 ){ |
| 29187 | zName = azArg[ii]; |
| 29188 | }else{ |
| @@ -29279,11 +30695,11 @@ | |
| 29279 | appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0); |
| 29280 | } |
| 29281 | appendText(&sSelect, "sql IS NOT NULL" |
| 29282 | " ORDER BY snum, rowid", 0); |
| 29283 | if( bDebug ){ |
| 29284 | oputf("SQL: %s;\n", sSelect.z); |
| 29285 | }else{ |
| 29286 | rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); |
| 29287 | } |
| 29288 | freeText(&sSelect); |
| 29289 | } |
| @@ -29340,11 +30756,12 @@ | |
| 29340 | session_not_open: |
| 29341 | eputz("ERROR: No sessions are open\n"); |
| 29342 | }else{ |
| 29343 | rc = sqlite3session_attach(pSession->p, azCmd[1]); |
| 29344 | if( rc ){ |
| 29345 | eputf("ERROR: sqlite3session_attach() returns %d\n",rc); |
| 29346 | rc = 0; |
| 29347 | } |
| 29348 | } |
| 29349 | }else |
| 29350 | |
| @@ -29357,13 +30774,13 @@ | |
| 29357 | ){ |
| 29358 | FILE *out = 0; |
| 29359 | failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]); |
| 29360 | if( nCmd!=2 ) goto session_syntax_error; |
| 29361 | if( pSession->p==0 ) goto session_not_open; |
| 29362 | out = fopen(azCmd[1], "wb"); |
| 29363 | if( out==0 ){ |
| 29364 | eputf("ERROR: cannot open \"%s\" for writing\n", |
| 29365 | azCmd[1]); |
| 29366 | }else{ |
| 29367 | int szChng; |
| 29368 | void *pChng; |
| 29369 | if( azCmd[0][0]=='c' ){ |
| @@ -29370,16 +30787,17 @@ | |
| 29370 | rc = sqlite3session_changeset(pSession->p, &szChng, &pChng); |
| 29371 | }else{ |
| 29372 | rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); |
| 29373 | } |
| 29374 | if( rc ){ |
| 29375 | sputf(stdout, "Error: error code %d\n", rc); |
| 29376 | rc = 0; |
| 29377 | } |
| 29378 | if( pChng |
| 29379 | && fwrite(pChng, szChng, 1, out)!=1 ){ |
| 29380 | eputf("ERROR: Failed to write entire %d-byte output\n", szChng); |
| 29381 | } |
| 29382 | sqlite3_free(pChng); |
| 29383 | fclose(out); |
| 29384 | } |
| 29385 | }else |
| @@ -29402,11 +30820,12 @@ | |
| 29402 | int ii; |
| 29403 | if( nCmd>2 ) goto session_syntax_error; |
| 29404 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 29405 | if( pAuxDb->nSession ){ |
| 29406 | ii = sqlite3session_enable(pSession->p, ii); |
| 29407 | oputf("session %s enable flag = %d\n", pSession->zName, ii); |
| 29408 | } |
| 29409 | }else |
| 29410 | |
| 29411 | /* .session filter GLOB .... |
| 29412 | ** Set a list of GLOB patterns of table names to be excluded. |
| @@ -29437,11 +30856,12 @@ | |
| 29437 | int ii; |
| 29438 | if( nCmd>2 ) goto session_syntax_error; |
| 29439 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 29440 | if( pAuxDb->nSession ){ |
| 29441 | ii = sqlite3session_indirect(pSession->p, ii); |
| 29442 | oputf("session %s indirect flag = %d\n", pSession->zName, ii); |
| 29443 | } |
| 29444 | }else |
| 29445 | |
| 29446 | /* .session isempty |
| 29447 | ** Determine if the session is empty |
| @@ -29449,20 +30869,21 @@ | |
| 29449 | if( cli_strcmp(azCmd[0], "isempty")==0 ){ |
| 29450 | int ii; |
| 29451 | if( nCmd!=1 ) goto session_syntax_error; |
| 29452 | if( pAuxDb->nSession ){ |
| 29453 | ii = sqlite3session_isempty(pSession->p); |
| 29454 | oputf("session %s isempty flag = %d\n", pSession->zName, ii); |
| 29455 | } |
| 29456 | }else |
| 29457 | |
| 29458 | /* .session list |
| 29459 | ** List all currently open sessions |
| 29460 | */ |
| 29461 | if( cli_strcmp(azCmd[0],"list")==0 ){ |
| 29462 | for(i=0; i<pAuxDb->nSession; i++){ |
| 29463 | oputf("%d %s\n", i, pAuxDb->aSession[i].zName); |
| 29464 | } |
| 29465 | }else |
| 29466 | |
| 29467 | /* .session open DB NAME |
| 29468 | ** Open a new session called NAME on the attached database DB. |
| @@ -29473,22 +30894,23 @@ | |
| 29473 | if( nCmd!=3 ) goto session_syntax_error; |
| 29474 | zName = azCmd[2]; |
| 29475 | if( zName[0]==0 ) goto session_syntax_error; |
| 29476 | for(i=0; i<pAuxDb->nSession; i++){ |
| 29477 | if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){ |
| 29478 | eputf("Session \"%s\" already exists\n", zName); |
| 29479 | goto meta_command_exit; |
| 29480 | } |
| 29481 | } |
| 29482 | if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){ |
| 29483 | eputf("Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); |
| 29484 | goto meta_command_exit; |
| 29485 | } |
| 29486 | pSession = &pAuxDb->aSession[pAuxDb->nSession]; |
| 29487 | rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); |
| 29488 | if( rc ){ |
| 29489 | eputf("Cannot open session: error code=%d\n", rc); |
| 29490 | rc = 0; |
| 29491 | goto meta_command_exit; |
| 29492 | } |
| 29493 | pSession->nFilter = 0; |
| 29494 | sqlite3session_table_filter(pSession->p, session_filter, pSession); |
| @@ -29508,20 +30930,20 @@ | |
| 29508 | if( c=='s' && n>=10 && cli_strncmp(azArg[0], "selftest-", 9)==0 ){ |
| 29509 | if( cli_strncmp(azArg[0]+9, "boolean", n-9)==0 ){ |
| 29510 | int i, v; |
| 29511 | for(i=1; i<nArg; i++){ |
| 29512 | v = booleanValue(azArg[i]); |
| 29513 | oputf("%s: %d 0x%x\n", azArg[i], v, v); |
| 29514 | } |
| 29515 | } |
| 29516 | if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ |
| 29517 | int i; sqlite3_int64 v; |
| 29518 | for(i=1; i<nArg; i++){ |
| 29519 | char zBuf[200]; |
| 29520 | v = integerValue(azArg[i]); |
| 29521 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); |
| 29522 | oputz(zBuf); |
| 29523 | } |
| 29524 | } |
| 29525 | }else |
| 29526 | #endif |
| 29527 | |
| @@ -29544,12 +30966,13 @@ | |
| 29544 | }else |
| 29545 | if( cli_strcmp(z,"-v")==0 ){ |
| 29546 | bVerbose++; |
| 29547 | }else |
| 29548 | { |
| 29549 | eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); |
| 29550 | eputz("Should be one of: --init -v\n"); |
| 29551 | rc = 1; |
| 29552 | goto meta_command_exit; |
| 29553 | } |
| 29554 | } |
| 29555 | if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0) |
| @@ -29590,46 +31013,47 @@ | |
| 29590 | if( zOp==0 ) continue; |
| 29591 | if( zSql==0 ) continue; |
| 29592 | if( zAns==0 ) continue; |
| 29593 | k = 0; |
| 29594 | if( bVerbose>0 ){ |
| 29595 | sputf(stdout, "%d: %s %s\n", tno, zOp, zSql); |
| 29596 | } |
| 29597 | if( cli_strcmp(zOp,"memo")==0 ){ |
| 29598 | oputf("%s\n", zSql); |
| 29599 | }else |
| 29600 | if( cli_strcmp(zOp,"run")==0 ){ |
| 29601 | char *zErrMsg = 0; |
| 29602 | str.n = 0; |
| 29603 | str.z[0] = 0; |
| 29604 | rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); |
| 29605 | nTest++; |
| 29606 | if( bVerbose ){ |
| 29607 | oputf("Result: %s\n", str.z); |
| 29608 | } |
| 29609 | if( rc || zErrMsg ){ |
| 29610 | nErr++; |
| 29611 | rc = 1; |
| 29612 | oputf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); |
| 29613 | sqlite3_free(zErrMsg); |
| 29614 | }else if( cli_strcmp(zAns,str.z)!=0 ){ |
| 29615 | nErr++; |
| 29616 | rc = 1; |
| 29617 | oputf("%d: Expected: [%s]\n", tno, zAns); |
| 29618 | oputf("%d: Got: [%s]\n", tno, str.z); |
| 29619 | } |
| 29620 | } |
| 29621 | else{ |
| 29622 | eputf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno); |
| 29623 | rc = 1; |
| 29624 | break; |
| 29625 | } |
| 29626 | } /* End loop over rows of content from SELFTEST */ |
| 29627 | sqlite3_finalize(pStmt); |
| 29628 | } /* End loop over k */ |
| 29629 | freeText(&str); |
| 29630 | oputf("%d errors out of %d tests\n", nErr, nTest); |
| 29631 | }else |
| 29632 | |
| 29633 | if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ |
| 29634 | if( nArg<2 || nArg>3 ){ |
| 29635 | eputz("Usage: .separator COL ?ROW?\n"); |
| @@ -29673,11 +31097,12 @@ | |
| 29673 | }else |
| 29674 | if( cli_strcmp(z,"debug")==0 ){ |
| 29675 | bDebug = 1; |
| 29676 | }else |
| 29677 | { |
| 29678 | eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); |
| 29679 | showHelp(p->out, azArg[0]); |
| 29680 | rc = 1; |
| 29681 | goto meta_command_exit; |
| 29682 | } |
| 29683 | }else if( zLike ){ |
| @@ -29751,11 +31176,11 @@ | |
| 29751 | } |
| 29752 | shell_check_oom(zSql); |
| 29753 | freeText(&sQuery); |
| 29754 | freeText(&sSql); |
| 29755 | if( bDebug ){ |
| 29756 | oputf("%s\n", zSql); |
| 29757 | }else{ |
| 29758 | shell_exec(p, zSql, 0); |
| 29759 | } |
| 29760 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 29761 | { |
| @@ -29781,11 +31206,11 @@ | |
| 29781 | "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n" |
| 29782 | "|| ' AND typeof('||cname||')=''text'' ',\n" |
| 29783 | "' OR ') as query, tname from tabcols group by tname)" |
| 29784 | , zRevText); |
| 29785 | shell_check_oom(zRevText); |
| 29786 | if( bDebug ) oputf("%s\n", zRevText); |
| 29787 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 29788 | if( lrc!=SQLITE_OK ){ |
| 29789 | /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the |
| 29790 | ** user does cruel and unnatural things like ".limit expr_depth 0". */ |
| 29791 | rc = 1; |
| @@ -29794,19 +31219,20 @@ | |
| 29794 | lrc = SQLITE_ROW==sqlite3_step(pStmt); |
| 29795 | if( lrc ){ |
| 29796 | const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); |
| 29797 | sqlite3_stmt *pCheckStmt; |
| 29798 | lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); |
| 29799 | if( bDebug ) oputf("%s\n", zGenQuery); |
| 29800 | if( lrc!=SQLITE_OK ){ |
| 29801 | rc = 1; |
| 29802 | }else{ |
| 29803 | if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ |
| 29804 | double countIrreversible = sqlite3_column_double(pCheckStmt, 0); |
| 29805 | if( countIrreversible>0 ){ |
| 29806 | int sz = (int)(countIrreversible + 0.5); |
| 29807 | eputf("Digest includes %d invalidly encoded text field%s.\n", |
| 29808 | sz, (sz>1)? "s": ""); |
| 29809 | } |
| 29810 | } |
| 29811 | sqlite3_finalize(pCheckStmt); |
| 29812 | } |
| @@ -29836,15 +31262,15 @@ | |
| 29836 | zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); |
| 29837 | for(i=2; i<nArg && zCmd!=0; i++){ |
| 29838 | zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", |
| 29839 | zCmd, azArg[i]); |
| 29840 | } |
| 29841 | consoleRestore(); |
| 29842 | x = zCmd!=0 ? system(zCmd) : 1; |
| 29843 | consoleRenewSetup(); |
| 29844 | sqlite3_free(zCmd); |
| 29845 | if( x ) eputf("System command returns %d\n", x); |
| 29846 | }else |
| 29847 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ |
| 29848 | |
| 29849 | if( c=='s' && cli_strncmp(azArg[0], "show", n)==0 ){ |
| 29850 | static const char *azBool[] = { "off", "on", "trigger", "full"}; |
| @@ -29853,50 +31279,52 @@ | |
| 29853 | if( nArg!=1 ){ |
| 29854 | eputz("Usage: .show\n"); |
| 29855 | rc = 1; |
| 29856 | goto meta_command_exit; |
| 29857 | } |
| 29858 | oputf("%12.12s: %s\n","echo", |
| 29859 | azBool[ShellHasFlag(p, SHFLG_Echo)]); |
| 29860 | oputf("%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); |
| 29861 | oputf("%12.12s: %s\n","explain", |
| 29862 | p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); |
| 29863 | oputf("%12.12s: %s\n","headers", azBool[p->showHeader!=0]); |
| 29864 | if( p->mode==MODE_Column |
| 29865 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 29866 | ){ |
| 29867 | oputf("%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", |
| 29868 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 29869 | p->cmOpts.bWordWrap ? "on" : "off", |
| 29870 | p->cmOpts.bQuote ? "" : "no"); |
| 29871 | }else{ |
| 29872 | oputf("%12.12s: %s\n","mode", modeDescr[p->mode]); |
| 29873 | } |
| 29874 | oputf("%12.12s: ", "nullvalue"); |
| 29875 | output_c_string(p->nullValue); |
| 29876 | oputz("\n"); |
| 29877 | oputf("%12.12s: %s\n","output", |
| 29878 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 29879 | oputf("%12.12s: ", "colseparator"); |
| 29880 | output_c_string(p->colSeparator); |
| 29881 | oputz("\n"); |
| 29882 | oputf("%12.12s: ", "rowseparator"); |
| 29883 | output_c_string(p->rowSeparator); |
| 29884 | oputz("\n"); |
| 29885 | switch( p->statsOn ){ |
| 29886 | case 0: zOut = "off"; break; |
| 29887 | default: zOut = "on"; break; |
| 29888 | case 2: zOut = "stmt"; break; |
| 29889 | case 3: zOut = "vmstep"; break; |
| 29890 | } |
| 29891 | oputf("%12.12s: %s\n","stats", zOut); |
| 29892 | oputf("%12.12s: ", "width"); |
| 29893 | for (i=0;i<p->nWidth;i++) { |
| 29894 | oputf("%d ", p->colWidth[i]); |
| 29895 | } |
| 29896 | oputz("\n"); |
| 29897 | oputf("%12.12s: %s\n", "filename", |
| 29898 | p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); |
| 29899 | }else |
| 29900 | |
| 29901 | if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){ |
| 29902 | if( nArg==2 ){ |
| @@ -30010,13 +31438,14 @@ | |
| 30010 | if( nPrintCol<1 ) nPrintCol = 1; |
| 30011 | nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; |
| 30012 | for(i=0; i<nPrintRow; i++){ |
| 30013 | for(j=i; j<nRow; j+=nPrintRow){ |
| 30014 | char *zSp = j<nPrintRow ? "" : " "; |
| 30015 | oputf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); |
| 30016 | } |
| 30017 | oputz("\n"); |
| 30018 | } |
| 30019 | } |
| 30020 | |
| 30021 | for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); |
| 30022 | sqlite3_free(azResult); |
| @@ -30024,11 +31453,11 @@ | |
| 30024 | |
| 30025 | #ifndef SQLITE_SHELL_FIDDLE |
| 30026 | /* Begin redirecting output to the file "testcase-out.txt" */ |
| 30027 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 30028 | output_reset(p); |
| 30029 | p->out = output_file_open("testcase-out.txt", 0); |
| 30030 | if( p->out==0 ){ |
| 30031 | eputz("Error: cannot open 'testcase-out.txt'\n"); |
| 30032 | } |
| 30033 | if( nArg>=2 ){ |
| 30034 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| @@ -30068,11 +31497,10 @@ | |
| 30068 | {"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, |
| 30069 | {"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, |
| 30070 | {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, |
| 30071 | {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, |
| 30072 | {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, |
| 30073 | {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, |
| 30074 | }; |
| 30075 | int testctrl = -1; |
| 30076 | int iCtrl = -1; |
| 30077 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 30078 | int isOk = 0; |
| @@ -30088,14 +31516,14 @@ | |
| 30088 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 30089 | } |
| 30090 | |
| 30091 | /* --help lists all test-controls */ |
| 30092 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 30093 | oputz("Available test-controls:\n"); |
| 30094 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 30095 | if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue; |
| 30096 | oputf(" .testctrl %s %s\n", |
| 30097 | aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 30098 | } |
| 30099 | rc = 1; |
| 30100 | goto meta_command_exit; |
| 30101 | } |
| @@ -30108,19 +31536,19 @@ | |
| 30108 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 30109 | if( testctrl<0 ){ |
| 30110 | testctrl = aCtrl[i].ctrlCode; |
| 30111 | iCtrl = i; |
| 30112 | }else{ |
| 30113 | eputf("Error: ambiguous test-control: \"%s\"\n" |
| 30114 | "Use \".testctrl --help\" for help\n", zCmd); |
| 30115 | rc = 1; |
| 30116 | goto meta_command_exit; |
| 30117 | } |
| 30118 | } |
| 30119 | } |
| 30120 | if( testctrl<0 ){ |
| 30121 | eputf("Error: unknown test-control: %s\n" |
| 30122 | "Use \".testctrl --help\" for help\n", zCmd); |
| 30123 | }else{ |
| 30124 | switch(testctrl){ |
| 30125 | |
| 30126 | /* Special processing for .testctrl opt MASK ... |
| @@ -30170,11 +31598,13 @@ | |
| 30170 | { 0x10000000, 1, "OrderBySubq" }, |
| 30171 | { 0xffffffff, 0, "All" }, |
| 30172 | }; |
| 30173 | unsigned int curOpt; |
| 30174 | unsigned int newOpt; |
| 30175 | int ii; |
| 30176 | sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt); |
| 30177 | newOpt = curOpt; |
| 30178 | for(ii=2; ii<nArg; ii++){ |
| 30179 | const char *z = azArg[ii]; |
| 30180 | int useLabel = 0; |
| @@ -30192,16 +31622,17 @@ | |
| 30192 | int jj; |
| 30193 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 30194 | if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break; |
| 30195 | } |
| 30196 | if( jj>=ArraySize(aLabel) ){ |
| 30197 | eputf("Error: no such optimization: \"%s\"\n", zLabel); |
| 30198 | eputz("Should be one of:"); |
| 30199 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 30200 | eputf(" %s", aLabel[jj].zLabel); |
| 30201 | } |
| 30202 | eputz("\n"); |
| 30203 | rc = 1; |
| 30204 | goto meta_command_exit; |
| 30205 | } |
| 30206 | if( useLabel=='+' ){ |
| 30207 | newOpt &= ~aLabel[jj].mask; |
| @@ -30210,28 +31641,32 @@ | |
| 30210 | } |
| 30211 | } |
| 30212 | } |
| 30213 | if( curOpt!=newOpt ){ |
| 30214 | sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt); |
| 30215 | }else if( nArg<3 ){ |
| 30216 | curOpt = ~newOpt; |
| 30217 | } |
| 30218 | if( newOpt==0 ){ |
| 30219 | oputz("+All\n"); |
| 30220 | }else if( newOpt==0xffffffff ){ |
| 30221 | oputz("-All\n"); |
| 30222 | }else{ |
| 30223 | int jj; |
| 30224 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 30225 | unsigned int m = aLabel[jj].mask; |
| 30226 | if( !aLabel[jj].bDsply ) continue; |
| 30227 | if( (curOpt&m)!=(newOpt&m) ){ |
| 30228 | oputf("%c%s\n", (newOpt & m)==0 ? '+' : '-', |
| 30229 | aLabel[jj].zLabel); |
| 30230 | } |
| 30231 | } |
| 30232 | } |
| 30233 | rc2 = isOk = 3; |
| 30234 | break; |
| 30235 | } |
| 30236 | |
| 30237 | /* sqlite3_test_control(int, db, int) */ |
| @@ -30267,11 +31702,11 @@ | |
| 30267 | if( nArg==3 || nArg==4 ){ |
| 30268 | int ii = (int)integerValue(azArg[2]); |
| 30269 | sqlite3 *db; |
| 30270 | if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){ |
| 30271 | sqlite3_randomness(sizeof(ii),&ii); |
| 30272 | sputf(stdout, "-- random seed: %d\n", ii); |
| 30273 | } |
| 30274 | if( nArg==3 ){ |
| 30275 | db = 0; |
| 30276 | }else{ |
| 30277 | db = p->db; |
| @@ -30301,25 +31736,10 @@ | |
| 30301 | rc2 = sqlite3_test_control(testctrl, opt); |
| 30302 | isOk = 3; |
| 30303 | } |
| 30304 | break; |
| 30305 | |
| 30306 | /* sqlite3_test_control(int, int) */ |
| 30307 | case SQLITE_TESTCTRL_USELONGDOUBLE: { |
| 30308 | int opt = -1; |
| 30309 | if( nArg==3 ){ |
| 30310 | if( cli_strcmp(azArg[2],"default")==0 ){ |
| 30311 | opt = 2; |
| 30312 | }else{ |
| 30313 | opt = booleanValue(azArg[2]); |
| 30314 | } |
| 30315 | } |
| 30316 | rc2 = sqlite3_test_control(testctrl, opt); |
| 30317 | isOk = 1; |
| 30318 | break; |
| 30319 | } |
| 30320 | |
| 30321 | /* sqlite3_test_control(sqlite3*) */ |
| 30322 | case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: |
| 30323 | rc2 = sqlite3_test_control(testctrl, p->db); |
| 30324 | isOk = 3; |
| 30325 | break; |
| @@ -30335,11 +31755,11 @@ | |
| 30335 | break; |
| 30336 | |
| 30337 | case SQLITE_TESTCTRL_SEEK_COUNT: { |
| 30338 | u64 x = 0; |
| 30339 | rc2 = sqlite3_test_control(testctrl, p->db, &x); |
| 30340 | oputf("%llu\n", x); |
| 30341 | isOk = 3; |
| 30342 | break; |
| 30343 | } |
| 30344 | |
| 30345 | #ifdef YYCOVERAGE |
| @@ -30366,15 +31786,15 @@ | |
| 30366 | int id = 1; |
| 30367 | while(1){ |
| 30368 | int val = 0; |
| 30369 | rc2 = sqlite3_test_control(testctrl, -id, &val); |
| 30370 | if( rc2!=SQLITE_OK ) break; |
| 30371 | if( id>1 ) oputz(" "); |
| 30372 | oputf("%d: %d", id, val); |
| 30373 | id++; |
| 30374 | } |
| 30375 | if( id>1 ) oputz("\n"); |
| 30376 | isOk = 3; |
| 30377 | } |
| 30378 | break; |
| 30379 | } |
| 30380 | #endif |
| @@ -30412,18 +31832,26 @@ | |
| 30412 | }else if( cli_strcmp(z,"reset")==0 ){ |
| 30413 | faultsim_state.iCnt = faultsim_state.nSkip; |
| 30414 | faultsim_state.nHit = 0; |
| 30415 | sqlite3_test_control(testctrl, faultsim_callback); |
| 30416 | }else if( cli_strcmp(z,"status")==0 ){ |
| 30417 | oputf("faultsim.iId: %d\n", faultsim_state.iId); |
| 30418 | oputf("faultsim.iErr: %d\n", faultsim_state.iErr); |
| 30419 | oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt); |
| 30420 | oputf("faultsim.nHit: %d\n", faultsim_state.nHit); |
| 30421 | oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval); |
| 30422 | oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose); |
| 30423 | oputf("faultsim.nRepeat: %d\n", faultsim_state.nRepeat); |
| 30424 | oputf("faultsim.nSkip: %d\n", faultsim_state.nSkip); |
| 30425 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 30426 | if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; |
| 30427 | }else if( cli_strcmp(z,"-q")==0 ){ |
| 30428 | if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--; |
| 30429 | }else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){ |
| @@ -30437,19 +31865,20 @@ | |
| 30437 | }else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){ |
| 30438 | faultsim_state.nSkip = atoi(azArg[++kk]); |
| 30439 | }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ |
| 30440 | bShowHelp = 1; |
| 30441 | }else{ |
| 30442 | eputf("Unrecognized fault_install argument: \"%s\"\n", |
| 30443 | azArg[kk]); |
| 30444 | rc = 1; |
| 30445 | bShowHelp = 1; |
| 30446 | break; |
| 30447 | } |
| 30448 | } |
| 30449 | if( bShowHelp ){ |
| 30450 | oputz( |
| 30451 | "Usage: .testctrl fault_install ARGS\n" |
| 30452 | "Possible arguments:\n" |
| 30453 | " off Disable faultsim\n" |
| 30454 | " on Activate faultsim\n" |
| 30455 | " reset Reset the trigger counter\n" |
| @@ -30459,23 +31888,25 @@ | |
| 30459 | " --errcode N When triggered, return N as error code\n" |
| 30460 | " --id ID Trigger only for the ID specified\n" |
| 30461 | " --interval N Trigger only after every N-th call\n" |
| 30462 | " --repeat N Turn off after N hits. 0 means never\n" |
| 30463 | " --skip N Skip the first N encounters\n" |
| 30464 | ); |
| 30465 | } |
| 30466 | break; |
| 30467 | } |
| 30468 | } |
| 30469 | } |
| 30470 | if( isOk==0 && iCtrl>=0 ){ |
| 30471 | oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| 30472 | rc = 1; |
| 30473 | }else if( isOk==1 ){ |
| 30474 | oputf("%d\n", rc2); |
| 30475 | }else if( isOk==2 ){ |
| 30476 | oputf("0x%08x\n", rc2); |
| 30477 | } |
| 30478 | }else |
| 30479 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 30480 | |
| 30481 | if( c=='t' && n>4 && cli_strncmp(azArg[0], "timeout", n)==0 ){ |
| @@ -30526,17 +31957,17 @@ | |
| 30526 | } |
| 30527 | else if( optionMatch(z, "close") ){ |
| 30528 | mType |= SQLITE_TRACE_CLOSE; |
| 30529 | } |
| 30530 | else { |
| 30531 | eputf("Unknown option \"%s\" on \".trace\"\n", z); |
| 30532 | rc = 1; |
| 30533 | goto meta_command_exit; |
| 30534 | } |
| 30535 | }else{ |
| 30536 | output_file_close(p->traceOut); |
| 30537 | p->traceOut = output_file_open(z, 0); |
| 30538 | } |
| 30539 | } |
| 30540 | if( p->traceOut==0 ){ |
| 30541 | sqlite3_trace_v2(p->db, 0, 0, 0); |
| 30542 | }else{ |
| @@ -30569,103 +32000,40 @@ | |
| 30569 | } |
| 30570 | } |
| 30571 | }else |
| 30572 | #endif |
| 30573 | |
| 30574 | #if SQLITE_USER_AUTHENTICATION |
| 30575 | if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){ |
| 30576 | if( nArg<2 ){ |
| 30577 | eputz("Usage: .user SUBCOMMAND ...\n"); |
| 30578 | rc = 1; |
| 30579 | goto meta_command_exit; |
| 30580 | } |
| 30581 | open_db(p, 0); |
| 30582 | if( cli_strcmp(azArg[1],"login")==0 ){ |
| 30583 | if( nArg!=4 ){ |
| 30584 | eputz("Usage: .user login USER PASSWORD\n"); |
| 30585 | rc = 1; |
| 30586 | goto meta_command_exit; |
| 30587 | } |
| 30588 | rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], |
| 30589 | strlen30(azArg[3])); |
| 30590 | if( rc ){ |
| 30591 | eputf("Authentication failed for user %s\n", azArg[2]); |
| 30592 | rc = 1; |
| 30593 | } |
| 30594 | }else if( cli_strcmp(azArg[1],"add")==0 ){ |
| 30595 | if( nArg!=5 ){ |
| 30596 | eputz("Usage: .user add USER PASSWORD ISADMIN\n"); |
| 30597 | rc = 1; |
| 30598 | goto meta_command_exit; |
| 30599 | } |
| 30600 | rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), |
| 30601 | booleanValue(azArg[4])); |
| 30602 | if( rc ){ |
| 30603 | eputf("User-Add failed: %d\n", rc); |
| 30604 | rc = 1; |
| 30605 | } |
| 30606 | }else if( cli_strcmp(azArg[1],"edit")==0 ){ |
| 30607 | if( nArg!=5 ){ |
| 30608 | eputz("Usage: .user edit USER PASSWORD ISADMIN\n"); |
| 30609 | rc = 1; |
| 30610 | goto meta_command_exit; |
| 30611 | } |
| 30612 | rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), |
| 30613 | booleanValue(azArg[4])); |
| 30614 | if( rc ){ |
| 30615 | eputf("User-Edit failed: %d\n", rc); |
| 30616 | rc = 1; |
| 30617 | } |
| 30618 | }else if( cli_strcmp(azArg[1],"delete")==0 ){ |
| 30619 | if( nArg!=3 ){ |
| 30620 | eputz("Usage: .user delete USER\n"); |
| 30621 | rc = 1; |
| 30622 | goto meta_command_exit; |
| 30623 | } |
| 30624 | rc = sqlite3_user_delete(p->db, azArg[2]); |
| 30625 | if( rc ){ |
| 30626 | eputf("User-Delete failed: %d\n", rc); |
| 30627 | rc = 1; |
| 30628 | } |
| 30629 | }else{ |
| 30630 | eputz("Usage: .user login|add|edit|delete ...\n"); |
| 30631 | rc = 1; |
| 30632 | goto meta_command_exit; |
| 30633 | } |
| 30634 | }else |
| 30635 | #endif /* SQLITE_USER_AUTHENTICATION */ |
| 30636 | |
| 30637 | if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ |
| 30638 | char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; |
| 30639 | oputf("SQLite %s %s\n" /*extra-version-info*/, |
| 30640 | sqlite3_libversion(), sqlite3_sourceid()); |
| 30641 | #if SQLITE_HAVE_ZLIB |
| 30642 | oputf("zlib version %s\n", zlibVersion()); |
| 30643 | #endif |
| 30644 | #define CTIMEOPT_VAL_(opt) #opt |
| 30645 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 30646 | #if defined(__clang__) && defined(__clang_major__) |
| 30647 | oputf("clang-" CTIMEOPT_VAL(__clang_major__) "." |
| 30648 | CTIMEOPT_VAL(__clang_minor__) "." |
| 30649 | CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); |
| 30650 | #elif defined(_MSC_VER) |
| 30651 | oputf("msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); |
| 30652 | #elif defined(__GNUC__) && defined(__VERSION__) |
| 30653 | oputf("gcc-" __VERSION__ " (%s)\n", zPtrSz); |
| 30654 | #endif |
| 30655 | }else |
| 30656 | |
| 30657 | if( c=='v' && cli_strncmp(azArg[0], "vfsinfo", n)==0 ){ |
| 30658 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 30659 | sqlite3_vfs *pVfs = 0; |
| 30660 | if( p->db ){ |
| 30661 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); |
| 30662 | if( pVfs ){ |
| 30663 | oputf("vfs.zName = \"%s\"\n", pVfs->zName); |
| 30664 | oputf("vfs.iVersion = %d\n", pVfs->iVersion); |
| 30665 | oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 30666 | oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 30667 | } |
| 30668 | } |
| 30669 | }else |
| 30670 | |
| 30671 | if( c=='v' && cli_strncmp(azArg[0], "vfslist", n)==0 ){ |
| @@ -30673,17 +32041,17 @@ | |
| 30673 | sqlite3_vfs *pCurrent = 0; |
| 30674 | if( p->db ){ |
| 30675 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); |
| 30676 | } |
| 30677 | for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ |
| 30678 | oputf("vfs.zName = \"%s\"%s\n", pVfs->zName, |
| 30679 | pVfs==pCurrent ? " <--- CURRENT" : ""); |
| 30680 | oputf("vfs.iVersion = %d\n", pVfs->iVersion); |
| 30681 | oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 30682 | oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 30683 | if( pVfs->pNext ){ |
| 30684 | oputz("-----------------------------------\n"); |
| 30685 | } |
| 30686 | } |
| 30687 | }else |
| 30688 | |
| 30689 | if( c=='v' && cli_strncmp(azArg[0], "vfsname", n)==0 ){ |
| @@ -30690,11 +32058,11 @@ | |
| 30690 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 30691 | char *zVfsName = 0; |
| 30692 | if( p->db ){ |
| 30693 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); |
| 30694 | if( zVfsName ){ |
| 30695 | oputf("%s\n", zVfsName); |
| 30696 | sqlite3_free(zVfsName); |
| 30697 | } |
| 30698 | } |
| 30699 | }else |
| 30700 | |
| @@ -30714,11 +32082,11 @@ | |
| 30714 | p->colWidth[j-1] = (int)integerValue(azArg[j]); |
| 30715 | } |
| 30716 | }else |
| 30717 | |
| 30718 | { |
| 30719 | eputf("Error: unknown command or invalid arguments: " |
| 30720 | " \"%s\". Enter \".help\" for help\n", azArg[0]); |
| 30721 | rc = 1; |
| 30722 | } |
| 30723 | |
| 30724 | meta_command_exit: |
| @@ -30755,11 +32123,10 @@ | |
| 30755 | SCAN_TRACKER_REFTYPE pst){ |
| 30756 | char cin; |
| 30757 | char cWait = (char)qss; /* intentional narrowing loss */ |
| 30758 | if( cWait==0 ){ |
| 30759 | PlainScan: |
| 30760 | assert( cWait==0 ); |
| 30761 | while( (cin = *zLine++)!=0 ){ |
| 30762 | if( IsSpace(cin) ) |
| 30763 | continue; |
| 30764 | switch (cin){ |
| 30765 | case '-': |
| @@ -30807,11 +32174,10 @@ | |
| 30807 | switch( cWait ){ |
| 30808 | case '*': |
| 30809 | if( *zLine != '/' ) |
| 30810 | continue; |
| 30811 | ++zLine; |
| 30812 | cWait = 0; |
| 30813 | CONTINUE_PROMPT_AWAITC(pst, 0); |
| 30814 | qss = QSS_SETV(qss, 0); |
| 30815 | goto PlainScan; |
| 30816 | case '`': case '\'': case '"': |
| 30817 | if(*zLine==cWait){ |
| @@ -30819,11 +32185,10 @@ | |
| 30819 | ++zLine; |
| 30820 | continue; |
| 30821 | } |
| 30822 | deliberate_fall_through; |
| 30823 | case ']': |
| 30824 | cWait = 0; |
| 30825 | CONTINUE_PROMPT_AWAITC(pst, 0); |
| 30826 | qss = QSS_SETV(qss, 0); |
| 30827 | goto PlainScan; |
| 30828 | default: assert(0); |
| 30829 | } |
| @@ -30966,11 +32331,11 @@ | |
| 30966 | open_db(p, 0); |
| 30967 | if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
| 30968 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 30969 | BEGIN_TIMER; |
| 30970 | rc = shell_exec(p, zSql, &zErrMsg); |
| 30971 | END_TIMER; |
| 30972 | if( rc || zErrMsg ){ |
| 30973 | char zPrefix[100]; |
| 30974 | const char *zErrorTail; |
| 30975 | const char *zErrorType; |
| 30976 | if( zErrMsg==0 ){ |
| @@ -30990,28 +32355,31 @@ | |
| 30990 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
| 30991 | "%s near line %d:", zErrorType, startline); |
| 30992 | }else{ |
| 30993 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType); |
| 30994 | } |
| 30995 | eputf("%s %s\n", zPrefix, zErrorTail); |
| 30996 | sqlite3_free(zErrMsg); |
| 30997 | zErrMsg = 0; |
| 30998 | return 1; |
| 30999 | }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ |
| 31000 | char zLineBuf[2000]; |
| 31001 | sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, |
| 31002 | "changes: %lld total_changes: %lld", |
| 31003 | sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); |
| 31004 | oputf("%s\n", zLineBuf); |
| 31005 | } |
| 31006 | |
| 31007 | if( doAutoDetectRestore(p, zSql) ) return 1; |
| 31008 | return 0; |
| 31009 | } |
| 31010 | |
| 31011 | static void echo_group_input(ShellState *p, const char *zDo){ |
| 31012 | if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); |
| 31013 | } |
| 31014 | |
| 31015 | #ifdef SQLITE_SHELL_FIDDLE |
| 31016 | /* |
| 31017 | ** Alternate one_input_line() impl for wasm mode. This is not in the primary |
| @@ -31065,11 +32433,11 @@ | |
| 31065 | i64 startline = 0; /* Line number for start of current input */ |
| 31066 | QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ |
| 31067 | |
| 31068 | if( p->inputNesting==MAX_INPUT_NESTING ){ |
| 31069 | /* This will be more informative in a later version. */ |
| 31070 | eputf("Input nesting limit (%d) reached at line %d." |
| 31071 | " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); |
| 31072 | return 1; |
| 31073 | } |
| 31074 | ++p->inputNesting; |
| 31075 | p->lineno = 0; |
| @@ -31077,11 +32445,11 @@ | |
| 31077 | while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ |
| 31078 | fflush(p->out); |
| 31079 | zLine = one_input_line(p->in, zLine, nSql>0); |
| 31080 | if( zLine==0 ){ |
| 31081 | /* End of input */ |
| 31082 | if( p->in==0 && stdin_is_interactive ) oputz("\n"); |
| 31083 | break; |
| 31084 | } |
| 31085 | if( seenInterrupt ){ |
| 31086 | if( p->in!=0 ) break; |
| 31087 | seenInterrupt = 0; |
| @@ -31297,19 +32665,19 @@ | |
| 31297 | } |
| 31298 | zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); |
| 31299 | shell_check_oom(zBuf); |
| 31300 | sqliterc = zBuf; |
| 31301 | } |
| 31302 | p->in = fopen(sqliterc,"rb"); |
| 31303 | if( p->in ){ |
| 31304 | if( stdin_is_interactive ){ |
| 31305 | eputf("-- Loading resources from %s\n", sqliterc); |
| 31306 | } |
| 31307 | if( process_input(p) && bail_on_error ) exit(1); |
| 31308 | fclose(p->in); |
| 31309 | }else if( sqliterc_override!=0 ){ |
| 31310 | eputf("cannot open: \"%s\"\n", sqliterc); |
| 31311 | if( bail_on_error ) exit(1); |
| 31312 | } |
| 31313 | p->in = inSaved; |
| 31314 | p->lineno = savedLineno; |
| 31315 | sqlite3_free(zBuf); |
| @@ -31374,23 +32742,21 @@ | |
| 31374 | " -table set output mode to 'table'\n" |
| 31375 | " -tabs set output mode to 'tabs'\n" |
| 31376 | " -unsafe-testing allow unsafe commands and modes for testing\n" |
| 31377 | " -version show SQLite version\n" |
| 31378 | " -vfs NAME use NAME as the default VFS\n" |
| 31379 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 31380 | " -vfstrace enable tracing of all VFS calls\n" |
| 31381 | #endif |
| 31382 | #ifdef SQLITE_HAVE_ZLIB |
| 31383 | " -zip open the file as a ZIP Archive\n" |
| 31384 | #endif |
| 31385 | ; |
| 31386 | static void usage(int showDetail){ |
| 31387 | eputf("Usage: %s [OPTIONS] [FILENAME [SQL]]\n" |
| 31388 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 31389 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 31390 | if( showDetail ){ |
| 31391 | eputf("OPTIONS include:\n%s", zOptions); |
| 31392 | }else{ |
| 31393 | eputz("Use the -help option for additional information\n"); |
| 31394 | } |
| 31395 | exit(0); |
| 31396 | } |
| @@ -31411,10 +32777,13 @@ | |
| 31411 | */ |
| 31412 | static void main_init(ShellState *data) { |
| 31413 | memset(data, 0, sizeof(*data)); |
| 31414 | data->normalMode = data->cMode = data->mode = MODE_List; |
| 31415 | data->autoExplain = 1; |
| 31416 | data->pAuxDb = &data->aAuxDb[0]; |
| 31417 | memcpy(data->colSeparator,SEP_Column, 2); |
| 31418 | memcpy(data->rowSeparator,SEP_Row, 2); |
| 31419 | data->showHeader = 0; |
| 31420 | data->shellFlgs = SHFLG_Lookaside; |
| @@ -31446,29 +32815,39 @@ | |
| 31446 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 31447 | #endif |
| 31448 | } |
| 31449 | #else |
| 31450 | static void printBold(const char *zText){ |
| 31451 | sputf(stdout, "\033[1m%s\033[0m", zText); |
| 31452 | } |
| 31453 | #endif |
| 31454 | |
| 31455 | /* |
| 31456 | ** Get the argument to an --option. Throw an error and die if no argument |
| 31457 | ** is available. |
| 31458 | */ |
| 31459 | static char *cmdline_option_value(int argc, char **argv, int i){ |
| 31460 | if( i==argc ){ |
| 31461 | eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); |
| 31462 | exit(1); |
| 31463 | } |
| 31464 | return argv[i]; |
| 31465 | } |
| 31466 | |
| 31467 | static void sayAbnormalExit(void){ |
| 31468 | if( seenInterrupt ) eputz("Program interrupted.\n"); |
| 31469 | } |
| 31470 | |
| 31471 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 31472 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 31473 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 31474 | # define SQLITE_SHELL_IS_UTF8 (0) |
| @@ -31493,19 +32872,19 @@ | |
| 31493 | char *zErrMsg = 0; |
| 31494 | #ifdef SQLITE_SHELL_FIDDLE |
| 31495 | # define data shellState |
| 31496 | #else |
| 31497 | ShellState data; |
| 31498 | StreamsAreConsole consStreams = SAC_NoConsole; |
| 31499 | #endif |
| 31500 | const char *zInitFile = 0; |
| 31501 | int i; |
| 31502 | int rc = 0; |
| 31503 | int warnInmemoryDb = 0; |
| 31504 | int readStdin = 1; |
| 31505 | int nCmd = 0; |
| 31506 | int nOptsEnd = argc; |
| 31507 | char **azCmd = 0; |
| 31508 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 31509 | #if !SQLITE_SHELL_IS_UTF8 |
| 31510 | char **argvToFree = 0; |
| 31511 | int argcToFree = 0; |
| @@ -31515,25 +32894,29 @@ | |
| 31515 | #ifdef SQLITE_SHELL_FIDDLE |
| 31516 | stdin_is_interactive = 0; |
| 31517 | stdout_is_console = 1; |
| 31518 | data.wasm.zDefaultDbName = "/fiddle.sqlite3"; |
| 31519 | #else |
| 31520 | consStreams = consoleClassifySetup(stdin, stdout, stderr); |
| 31521 | stdin_is_interactive = (consStreams & SAC_InConsole)!=0; |
| 31522 | stdout_is_console = (consStreams & SAC_OutConsole)!=0; |
| 31523 | atexit(consoleRestore); |
| 31524 | #endif |
| 31525 | atexit(sayAbnormalExit); |
| 31526 | #ifdef SQLITE_DEBUG |
| 31527 | mem_main_enter = sqlite3_memory_used(); |
| 31528 | #endif |
| 31529 | #if !defined(_WIN32_WCE) |
| 31530 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 31531 | if( isatty(0) && isatty(2) ){ |
| 31532 | eputf("attach debugger to process %d and press any key to continue.\n", |
| 31533 | GETPID()); |
| 31534 | fgetc(stdin); |
| 31535 | }else{ |
| 31536 | #if defined(_WIN32) || defined(WIN32) |
| 31537 | #if SQLITE_OS_WINRT |
| 31538 | __debugbreak(); |
| 31539 | #else |
| @@ -31554,11 +32937,12 @@ | |
| 31554 | } |
| 31555 | #endif |
| 31556 | |
| 31557 | #if USE_SYSTEM_SQLITE+0!=1 |
| 31558 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 31559 | eputf("SQLite header and source version mismatch\n%s\n%s\n", |
| 31560 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 31561 | exit(1); |
| 31562 | } |
| 31563 | #endif |
| 31564 | main_init(&data); |
| @@ -31696,21 +33080,12 @@ | |
| 31696 | switch( n ){ |
| 31697 | case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break; |
| 31698 | case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break; |
| 31699 | default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break; |
| 31700 | } |
| 31701 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 31702 | }else if( cli_strcmp(z,"-vfstrace")==0 ){ |
| 31703 | extern int vfstrace_register( |
| 31704 | const char *zTraceName, |
| 31705 | const char *zOldVfsName, |
| 31706 | int (*xOut)(const char*,void*), |
| 31707 | void *pOutArg, |
| 31708 | int makeDefault |
| 31709 | ); |
| 31710 | vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); |
| 31711 | #endif |
| 31712 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 31713 | }else if( cli_strcmp(z,"-multiplex")==0 ){ |
| 31714 | extern int sqlite3_multiplex_initialize(const char*,int); |
| 31715 | sqlite3_multiplex_initialize(0, 1); |
| 31716 | #endif |
| @@ -31762,11 +33137,11 @@ | |
| 31762 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 31763 | /* no-op - catch this on the second pass */ |
| 31764 | } |
| 31765 | } |
| 31766 | #ifndef SQLITE_SHELL_FIDDLE |
| 31767 | verify_uninitialized(); |
| 31768 | #endif |
| 31769 | |
| 31770 | |
| 31771 | #ifdef SQLITE_SHELL_INIT_PROC |
| 31772 | { |
| @@ -31786,25 +33161,29 @@ | |
| 31786 | if( zVfs ){ |
| 31787 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 31788 | if( pVfs ){ |
| 31789 | sqlite3_vfs_register(pVfs, 1); |
| 31790 | }else{ |
| 31791 | eputf("no such VFS: \"%s\"\n", zVfs); |
| 31792 | exit(1); |
| 31793 | } |
| 31794 | } |
| 31795 | |
| 31796 | if( data.pAuxDb->zDbFilename==0 ){ |
| 31797 | #ifndef SQLITE_OMIT_MEMORYDB |
| 31798 | data.pAuxDb->zDbFilename = ":memory:"; |
| 31799 | warnInmemoryDb = argc==1; |
| 31800 | #else |
| 31801 | eputf("%s: Error: no database filename specified\n", Argv0); |
| 31802 | return 1; |
| 31803 | #endif |
| 31804 | } |
| 31805 | data.out = stdout; |
| 31806 | #ifndef SQLITE_SHELL_FIDDLE |
| 31807 | sqlite3_appendvfs_init(0,0,0); |
| 31808 | #endif |
| 31809 | |
| 31810 | /* Go ahead and open the database file if it already exists. If the |
| @@ -31913,11 +33292,11 @@ | |
| 31913 | */ |
| 31914 | ShellSetFlag(&data, SHFLG_Backslash); |
| 31915 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 31916 | /* No-op. The bail_on_error flag should already be set. */ |
| 31917 | }else if( cli_strcmp(z,"-version")==0 ){ |
| 31918 | sputf(stdout, "%s %s (%d-bit)\n", |
| 31919 | sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); |
| 31920 | return 0; |
| 31921 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 31922 | /* Need to check for interactive override here to so that it can |
| 31923 | ** affect console setup (for Windows only) and testing thereof. |
| @@ -31951,14 +33330,12 @@ | |
| 31951 | }else if( cli_strcmp(z,"-sorterref")==0 ){ |
| 31952 | i++; |
| 31953 | #endif |
| 31954 | }else if( cli_strcmp(z,"-vfs")==0 ){ |
| 31955 | i++; |
| 31956 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 31957 | }else if( cli_strcmp(z,"-vfstrace")==0 ){ |
| 31958 | i++; |
| 31959 | #endif |
| 31960 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 31961 | }else if( cli_strcmp(z,"-multiplex")==0 ){ |
| 31962 | i++; |
| 31963 | #endif |
| 31964 | }else if( cli_strcmp(z,"-help")==0 ){ |
| @@ -31978,18 +33355,18 @@ | |
| 31978 | rc = shell_exec(&data, z, &zErrMsg); |
| 31979 | if( zErrMsg!=0 ){ |
| 31980 | shellEmitError(zErrMsg); |
| 31981 | if( bail_on_error ) return rc!=0 ? rc : 1; |
| 31982 | }else if( rc!=0 ){ |
| 31983 | eputf("Error: unable to process SQL \"%s\"\n", z); |
| 31984 | if( bail_on_error ) return rc; |
| 31985 | } |
| 31986 | } |
| 31987 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) |
| 31988 | }else if( cli_strncmp(z, "-A", 2)==0 ){ |
| 31989 | if( nCmd>0 ){ |
| 31990 | eputf("Error: cannot mix regular SQL or dot-commands" |
| 31991 | " with \"%s\"\n", z); |
| 31992 | return 1; |
| 31993 | } |
| 31994 | open_db(&data, OPEN_DB_ZIPFILE); |
| 31995 | if( z[2] ){ |
| @@ -32004,11 +33381,11 @@ | |
| 32004 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 32005 | data.bSafeMode = data.bSafeModePersist = 1; |
| 32006 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 32007 | /* Acted upon in first pass. */ |
| 32008 | }else{ |
| 32009 | eputf("%s: Error: unknown option: %s\n", Argv0, z); |
| 32010 | eputz("Use -help for a list of options.\n"); |
| 32011 | return 1; |
| 32012 | } |
| 32013 | data.cMode = data.mode; |
| 32014 | } |
| @@ -32031,11 +33408,12 @@ | |
| 32031 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 32032 | if( zErrMsg || rc ){ |
| 32033 | if( zErrMsg!=0 ){ |
| 32034 | shellEmitError(zErrMsg); |
| 32035 | }else{ |
| 32036 | eputf("Error: unable to process SQL: %s\n", azCmd[i]); |
| 32037 | } |
| 32038 | sqlite3_free(zErrMsg); |
| 32039 | if( rc==0 ) rc = 1; |
| 32040 | goto shell_main_exit; |
| 32041 | } |
| @@ -32046,18 +33424,14 @@ | |
| 32046 | */ |
| 32047 | if( stdin_is_interactive ){ |
| 32048 | char *zHome; |
| 32049 | char *zHistory; |
| 32050 | int nHistory; |
| 32051 | #if CIO_WIN_WC_XLATE |
| 32052 | # define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "") |
| 32053 | #else |
| 32054 | # define SHELL_CIO_CHAR_SET "" |
| 32055 | #endif |
| 32056 | sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/ |
| 32057 | "Enter \".help\" for usage hints.\n", |
| 32058 | sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); |
| 32059 | if( warnInmemoryDb ){ |
| 32060 | sputz(stdout, "Connected to a "); |
| 32061 | printBold("transient in-memory database"); |
| 32062 | sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a" |
| 32063 | " persistent database.\n"); |
| @@ -32070,13 +33444,15 @@ | |
| 32070 | if( (zHistory = malloc(nHistory))!=0 ){ |
| 32071 | sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
| 32072 | } |
| 32073 | } |
| 32074 | if( zHistory ){ shell_read_history(zHistory); } |
| 32075 | #if HAVE_READLINE || HAVE_EDITLINE |
| 32076 | rl_attempted_completion_function = readline_completion; |
| 32077 | #elif HAVE_LINENOISE |
| 32078 | linenoiseSetCompletionCallback(linenoise_completion, NULL); |
| 32079 | #endif |
| 32080 | data.in = 0; |
| 32081 | rc = process_input(&data); |
| 32082 | if( zHistory ){ |
| @@ -32122,13 +33498,16 @@ | |
| 32122 | free(data.colWidth); |
| 32123 | free(data.zNonce); |
| 32124 | /* Clear the global data structure so that valgrind will detect memory |
| 32125 | ** leaks */ |
| 32126 | memset(&data, 0, sizeof(data)); |
| 32127 | #ifdef SQLITE_DEBUG |
| 32128 | if( sqlite3_memory_used()>mem_main_enter ){ |
| 32129 | eputf("Memory leaked: %u bytes\n", |
| 32130 | (unsigned int)(sqlite3_memory_used()-mem_main_enter)); |
| 32131 | } |
| 32132 | #endif |
| 32133 | #else /* SQLITE_SHELL_FIDDLE... */ |
| 32134 | shell_main_exit: |
| @@ -32164,11 +33543,11 @@ | |
| 32164 | return pVfs; |
| 32165 | } |
| 32166 | |
| 32167 | /* Only for emcc experimentation purposes. */ |
| 32168 | sqlite3 * fiddle_db_arg(sqlite3 *arg){ |
| 32169 | oputf("fiddle_db_arg(%p)\n", (const void*)arg); |
| 32170 | return arg; |
| 32171 | } |
| 32172 | |
| 32173 | /* |
| 32174 | ** Intended to be called via a SharedWorker() while a separate |
| @@ -32201,11 +33580,11 @@ | |
| 32201 | while( sqlite3_txn_state(globalDb,0)>0 ){ |
| 32202 | /* |
| 32203 | ** Resolve problem reported in |
| 32204 | ** https://sqlite.org/forum/forumpost/0b41a25d65 |
| 32205 | */ |
| 32206 | oputz("Rolling back in-progress transaction.\n"); |
| 32207 | sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0); |
| 32208 | } |
| 32209 | rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); |
| 32210 | if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0); |
| 32211 | sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); |
| 32212 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -120,13 +120,10 @@ | |
| 120 | #include <math.h> |
| 121 | #include "sqlite3.h" |
| 122 | typedef sqlite3_int64 i64; |
| 123 | typedef sqlite3_uint64 u64; |
| 124 | typedef unsigned char u8; |
| 125 | #include <ctype.h> |
| 126 | #include <stdarg.h> |
| 127 | |
| 128 | #if !defined(_WIN32) && !defined(WIN32) |
| 129 | # include <signal.h> |
| @@ -208,12 +205,10 @@ | |
| 205 | # define unlink _unlink |
| 206 | # endif |
| 207 | # ifndef strdup |
| 208 | # define strdup _strdup |
| 209 | # endif |
| 210 | # undef pclose |
| 211 | # define pclose _pclose |
| 212 | # endif |
| 213 | #else |
| 214 | /* Make sure isatty() has a prototype. */ |
| @@ -253,10 +248,370 @@ | |
| 248 | /* string conversion routines only needed on Win32 */ |
| 249 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 250 | extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); |
| 251 | #endif |
| 252 | |
| 253 | /************************* Begin ../ext/misc/sqlite3_stdio.h ******************/ |
| 254 | /* |
| 255 | ** 2024-09-24 |
| 256 | ** |
| 257 | ** The author disclaims copyright to this source code. In place of |
| 258 | ** a legal notice, here is a blessing: |
| 259 | ** |
| 260 | ** May you do good and not evil. |
| 261 | ** May you find forgiveness for yourself and forgive others. |
| 262 | ** May you share freely, never taking more than you give. |
| 263 | ** |
| 264 | ************************************************************************* |
| 265 | ** |
| 266 | ** This header file contains definitions of interfaces that provide |
| 267 | ** cross-platform I/O for UTF-8 content. |
| 268 | ** |
| 269 | ** On most platforms, the interfaces definitions in this file are |
| 270 | ** just #defines. For example sqlite3_fopen() is a macro that resolves |
| 271 | ** to the standard fopen() in the C-library. |
| 272 | ** |
| 273 | ** But Windows does not have a standard C-library, at least not one that |
| 274 | ** can handle UTF-8. So for windows build, the interfaces resolve to new |
| 275 | ** C-language routines contained in the separate sqlite3_stdio.c source file. |
| 276 | ** |
| 277 | ** So on all non-Windows platforms, simply #include this header file and |
| 278 | ** use the interfaces defined herein. Then to run your application on Windows, |
| 279 | ** also link in the accompanying sqlite3_stdio.c source file when compiling |
| 280 | ** to get compatible interfaces. |
| 281 | */ |
| 282 | #ifndef _SQLITE3_STDIO_H_ |
| 283 | #define _SQLITE3_STDIO_H_ 1 |
| 284 | #ifdef _WIN32 |
| 285 | /**** Definitions For Windows ****/ |
| 286 | #include <stdio.h> |
| 287 | #include <windows.h> |
| 288 | |
| 289 | FILE *sqlite3_fopen(const char *zFilename, const char *zMode); |
| 290 | FILE *sqlite3_popen(const char *zCommand, const char *type); |
| 291 | char *sqlite3_fgets(char *s, int size, FILE *stream); |
| 292 | int sqlite3_fputs(const char *s, FILE *stream); |
| 293 | int sqlite3_fprintf(FILE *stream, const char *format, ...); |
| 294 | void sqlite3_fsetmode(FILE *stream, int mode); |
| 295 | |
| 296 | |
| 297 | #else |
| 298 | /**** Definitions For All Other Platforms ****/ |
| 299 | #include <stdio.h> |
| 300 | #define sqlite3_fopen fopen |
| 301 | #define sqlite3_popen popen |
| 302 | #define sqlite3_fgets fgets |
| 303 | #define sqlite3_fputs fputs |
| 304 | #define sqlite3_fprintf fprintf |
| 305 | #define sqlite3_fsetmode(F,X) /*no-op*/ |
| 306 | |
| 307 | #endif |
| 308 | #endif /* _SQLITE3_STDIO_H_ */ |
| 309 | |
| 310 | /************************* End ../ext/misc/sqlite3_stdio.h ********************/ |
| 311 | /************************* Begin ../ext/misc/sqlite3_stdio.c ******************/ |
| 312 | /* |
| 313 | ** 2024-09-24 |
| 314 | ** |
| 315 | ** The author disclaims copyright to this source code. In place of |
| 316 | ** a legal notice, here is a blessing: |
| 317 | ** |
| 318 | ** May you do good and not evil. |
| 319 | ** May you find forgiveness for yourself and forgive others. |
| 320 | ** May you share freely, never taking more than you give. |
| 321 | ** |
| 322 | ************************************************************************* |
| 323 | ** |
| 324 | ** Implementation of standard I/O interfaces for UTF-8 that are missing |
| 325 | ** on Windows. |
| 326 | */ |
| 327 | #ifdef _WIN32 /* This file is a no-op on all platforms except Windows */ |
| 328 | #ifndef _SQLITE3_STDIO_H_ |
| 329 | /* #include "sqlite3_stdio.h" */ |
| 330 | #endif |
| 331 | #undef WIN32_LEAN_AND_MEAN |
| 332 | #define WIN32_LEAN_AND_MEAN |
| 333 | #include <windows.h> |
| 334 | #include <stdlib.h> |
| 335 | #include <string.h> |
| 336 | #include <stdio.h> |
| 337 | #include <assert.h> |
| 338 | /* #include "sqlite3.h" */ |
| 339 | #include <ctype.h> |
| 340 | #include <stdarg.h> |
| 341 | #include <io.h> |
| 342 | #include <fcntl.h> |
| 343 | |
| 344 | /* |
| 345 | ** If the SQLITE_U8TEXT_ONLY option is defined, then use O_U8TEXT |
| 346 | ** when appropriate on all output. (Sometimes use O_BINARY when |
| 347 | ** rendering ASCII text in cases where NL-to-CRLF expansion would |
| 348 | ** not be correct.) |
| 349 | ** |
| 350 | ** If the SQLITE_U8TEXT_STDIO option is defined, then use O_U8TEXT |
| 351 | ** when appropriate when writing to stdout or stderr. Use O_BINARY |
| 352 | ** or O_TEXT (depending on things like the .mode and the .crlf setting |
| 353 | ** in the CLI, or other context clues in other applications) for all |
| 354 | ** other output channels. |
| 355 | ** |
| 356 | ** The default behavior, if neither of the above is defined is to |
| 357 | ** use O_U8TEXT when writing to the Windows console (or anything |
| 358 | ** else for which _isatty() returns true) and to use O_BINARY or O_TEXT |
| 359 | ** for all other output channels. |
| 360 | */ |
| 361 | #if defined(SQLITE_U8TEXT_ONLY) |
| 362 | # define UseWtextForOutput(fd) 1 |
| 363 | # define UseWtextForInput(fd) 1 |
| 364 | # define IsConsole(fd) _isatty(_fileno(fd)) |
| 365 | #elif defined(SQLITE_U8TEXT_STDIO) |
| 366 | # define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr) |
| 367 | # define UseWtextForInput(fd) ((fd)==stdin) |
| 368 | # define IsConsole(fd) _isatty(_fileno(fd)) |
| 369 | #else |
| 370 | # define UseWtextForOutput(fd) _isatty(_fileno(fd)) |
| 371 | # define UseWtextForInput(fd) _isatty(_fileno(fd)) |
| 372 | # define IsConsole(fd) 1 |
| 373 | #endif |
| 374 | |
| 375 | /* |
| 376 | ** Global variables determine if simulated O_BINARY mode is to be |
| 377 | ** used for stdout or other, respectively. Simulated O_BINARY mode |
| 378 | ** means the mode is usually O_BINARY, but switches to O_U8TEXT for |
| 379 | ** unicode characters U+0080 or greater (any character that has a |
| 380 | ** multi-byte representation in UTF-8). This is the only way we |
| 381 | ** have found to render Unicode characters on a Windows console while |
| 382 | ** at the same time avoiding undesirable \n to \r\n translation. |
| 383 | */ |
| 384 | static int simBinaryStdout = 0; |
| 385 | static int simBinaryOther = 0; |
| 386 | |
| 387 | |
| 388 | /* |
| 389 | ** Determine if simulated binary mode should be used for output to fd |
| 390 | */ |
| 391 | static int UseBinaryWText(FILE *fd){ |
| 392 | if( fd==stdout || fd==stderr ){ |
| 393 | return simBinaryStdout; |
| 394 | }else{ |
| 395 | return simBinaryOther; |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | |
| 400 | /* |
| 401 | ** Work-alike for the fopen() routine from the standard C library. |
| 402 | */ |
| 403 | FILE *sqlite3_fopen(const char *zFilename, const char *zMode){ |
| 404 | FILE *fp = 0; |
| 405 | wchar_t *b1, *b2; |
| 406 | int sz1, sz2; |
| 407 | |
| 408 | sz1 = (int)strlen(zFilename); |
| 409 | sz2 = (int)strlen(zMode); |
| 410 | b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) ); |
| 411 | b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) ); |
| 412 | if( b1 && b2 ){ |
| 413 | sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1); |
| 414 | b1[sz1] = 0; |
| 415 | sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2); |
| 416 | b2[sz2] = 0; |
| 417 | fp = _wfopen(b1, b2); |
| 418 | } |
| 419 | sqlite3_free(b1); |
| 420 | sqlite3_free(b2); |
| 421 | simBinaryOther = 0; |
| 422 | return fp; |
| 423 | } |
| 424 | |
| 425 | |
| 426 | /* |
| 427 | ** Work-alike for the popen() routine from the standard C library. |
| 428 | */ |
| 429 | FILE *sqlite3_popen(const char *zCommand, const char *zMode){ |
| 430 | FILE *fp = 0; |
| 431 | wchar_t *b1, *b2; |
| 432 | int sz1, sz2; |
| 433 | |
| 434 | sz1 = (int)strlen(zCommand); |
| 435 | sz2 = (int)strlen(zMode); |
| 436 | b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) ); |
| 437 | b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) ); |
| 438 | if( b1 && b2 ){ |
| 439 | sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1); |
| 440 | b1[sz1] = 0; |
| 441 | sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2); |
| 442 | b2[sz2] = 0; |
| 443 | fp = _wpopen(b1, b2); |
| 444 | } |
| 445 | sqlite3_free(b1); |
| 446 | sqlite3_free(b2); |
| 447 | return fp; |
| 448 | } |
| 449 | |
| 450 | /* |
| 451 | ** Work-alike for fgets() from the standard C library. |
| 452 | */ |
| 453 | char *sqlite3_fgets(char *buf, int sz, FILE *in){ |
| 454 | if( UseWtextForInput(in) ){ |
| 455 | /* When reading from the command-prompt in Windows, it is necessary |
| 456 | ** to use _O_WTEXT input mode to read UTF-16 characters, then translate |
| 457 | ** that into UTF-8. Otherwise, non-ASCII characters all get translated |
| 458 | ** into '?'. |
| 459 | */ |
| 460 | wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) ); |
| 461 | if( b1==0 ) return 0; |
| 462 | #ifndef SQLITE_USE_STDIO_FOR_CONSOLE |
| 463 | DWORD nRead = 0; |
| 464 | if( IsConsole(in) |
| 465 | && ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0) |
| 466 | ){ |
| 467 | b1[nRead] = 0; |
| 468 | }else |
| 469 | #endif |
| 470 | { |
| 471 | _setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT); |
| 472 | if( fgetws(b1, sz/4, in)==0 ){ |
| 473 | sqlite3_free(b1); |
| 474 | return 0; |
| 475 | } |
| 476 | } |
| 477 | WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0); |
| 478 | sqlite3_free(b1); |
| 479 | return buf; |
| 480 | }else{ |
| 481 | /* Reading from a file or other input source, just read bytes without |
| 482 | ** any translation. */ |
| 483 | return fgets(buf, sz, in); |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | /* |
| 488 | ** Send ASCII text as O_BINARY. But for Unicode characters U+0080 and |
| 489 | ** greater, switch to O_U8TEXT. |
| 490 | */ |
| 491 | static void piecemealOutput(wchar_t *b1, int sz, FILE *out){ |
| 492 | int i; |
| 493 | wchar_t c; |
| 494 | while( sz>0 ){ |
| 495 | for(i=0; i<sz && b1[i]>=0x80; i++){} |
| 496 | if( i>0 ){ |
| 497 | c = b1[i]; |
| 498 | b1[i] = 0; |
| 499 | fflush(out); |
| 500 | _setmode(_fileno(out), _O_U8TEXT); |
| 501 | fputws(b1, out); |
| 502 | fflush(out); |
| 503 | b1 += i; |
| 504 | b1[0] = c; |
| 505 | sz -= i; |
| 506 | }else{ |
| 507 | fflush(out); |
| 508 | _setmode(_fileno(out), _O_TEXT); |
| 509 | _setmode(_fileno(out), _O_BINARY); |
| 510 | fwrite(&b1[0], 1, 1, out); |
| 511 | for(i=1; i<sz && b1[i]<0x80; i++){ |
| 512 | fwrite(&b1[i], 1, 1, out); |
| 513 | } |
| 514 | fflush(out); |
| 515 | _setmode(_fileno(out), _O_U8TEXT); |
| 516 | b1 += i; |
| 517 | sz -= i; |
| 518 | } |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | /* |
| 523 | ** Work-alike for fputs() from the standard C library. |
| 524 | */ |
| 525 | int sqlite3_fputs(const char *z, FILE *out){ |
| 526 | if( !UseWtextForOutput(out) ){ |
| 527 | /* Writing to a file or other destination, just write bytes without |
| 528 | ** any translation. */ |
| 529 | return fputs(z, out); |
| 530 | }else{ |
| 531 | /* One must use UTF16 in order to get unicode support when writing |
| 532 | ** to the console on Windows. |
| 533 | */ |
| 534 | int sz = (int)strlen(z); |
| 535 | wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) ); |
| 536 | if( b1==0 ) return 0; |
| 537 | sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz); |
| 538 | b1[sz] = 0; |
| 539 | |
| 540 | #ifndef SQLITE_STDIO_FOR_CONSOLE |
| 541 | DWORD nWr = 0; |
| 542 | if( IsConsole(out) |
| 543 | && WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0) |
| 544 | ){ |
| 545 | /* If writing to the console, then the WriteConsoleW() is all we |
| 546 | ** need to do. */ |
| 547 | }else |
| 548 | #endif |
| 549 | { |
| 550 | /* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined |
| 551 | ** then write using the standard library. */ |
| 552 | _setmode(_fileno(out), _O_U8TEXT); |
| 553 | if( UseBinaryWText(out) ){ |
| 554 | piecemealOutput(b1, sz, out); |
| 555 | }else{ |
| 556 | fputws(b1, out); |
| 557 | } |
| 558 | } |
| 559 | sqlite3_free(b1); |
| 560 | return 0; |
| 561 | } |
| 562 | } |
| 563 | |
| 564 | |
| 565 | /* |
| 566 | ** Work-alike for fprintf() from the standard C library. |
| 567 | */ |
| 568 | int sqlite3_fprintf(FILE *out, const char *zFormat, ...){ |
| 569 | int rc; |
| 570 | if( UseWtextForOutput(out) ){ |
| 571 | /* When writing to the command-prompt in Windows, it is necessary |
| 572 | ** to use _O_WTEXT input mode and write UTF-16 characters. |
| 573 | */ |
| 574 | char *z; |
| 575 | va_list ap; |
| 576 | |
| 577 | va_start(ap, zFormat); |
| 578 | z = sqlite3_vmprintf(zFormat, ap); |
| 579 | va_end(ap); |
| 580 | sqlite3_fputs(z, out); |
| 581 | rc = (int)strlen(z); |
| 582 | sqlite3_free(z); |
| 583 | }else{ |
| 584 | /* Writing to a file or other destination, just write bytes without |
| 585 | ** any translation. */ |
| 586 | va_list ap; |
| 587 | va_start(ap, zFormat); |
| 588 | rc = vfprintf(out, zFormat, ap); |
| 589 | va_end(ap); |
| 590 | } |
| 591 | return rc; |
| 592 | } |
| 593 | |
| 594 | /* |
| 595 | ** Set the mode for an output stream. mode argument is typically _O_BINARY or |
| 596 | ** _O_TEXT. |
| 597 | */ |
| 598 | void sqlite3_fsetmode(FILE *fp, int mode){ |
| 599 | if( !UseWtextForOutput(fp) ){ |
| 600 | fflush(fp); |
| 601 | _setmode(_fileno(fp), mode); |
| 602 | }else if( fp==stdout || fp==stderr ){ |
| 603 | simBinaryStdout = (mode==_O_BINARY); |
| 604 | }else{ |
| 605 | simBinaryOther = (mode==_O_BINARY); |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | #endif /* defined(_WIN32) */ |
| 610 | |
| 611 | /************************* End ../ext/misc/sqlite3_stdio.c ********************/ |
| 612 | |
| 613 | /* Use console I/O package as a direct INCLUDE. */ |
| 614 | #define SQLITE_INTERNAL_LINKAGE static |
| 615 | |
| 616 | #ifdef SQLITE_SHELL_FIDDLE |
| 617 | /* Deselect most features from the console I/O package for Fiddle. */ |
| @@ -264,1118 +619,13 @@ | |
| 619 | # define SQLITE_CIO_NO_CLASSIFY |
| 620 | # define SQLITE_CIO_NO_TRANSLATE |
| 621 | # define SQLITE_CIO_NO_SETMODE |
| 622 | # define SQLITE_CIO_NO_FLUSH |
| 623 | #endif |
| 624 | |
| 625 | #define eputz(z) sqlite3_fputs(z,stderr) |
| 626 | #define sputz(fp,z) sqlite3_fputs(z,fp) |
| 627 | |
| 628 | /* True if the timer is enabled */ |
| 629 | static int enableTimer = 0; |
| 630 | |
| 631 | /* A version of strcmp() that works with NULL values */ |
| @@ -1416,10 +666,11 @@ | |
| 666 | struct timeval ru_utime; /* user CPU time used */ |
| 667 | struct timeval ru_stime; /* system CPU time used */ |
| 668 | }; |
| 669 | #define getrusage(A,B) memset(B,0,sizeof(*B)) |
| 670 | #endif |
| 671 | |
| 672 | |
| 673 | /* Saved resource information for the beginning of an operation */ |
| 674 | static struct rusage sBegin; /* CPU time at start */ |
| 675 | static sqlite3_int64 iBegin; /* Wall-clock time at start */ |
| 676 | |
| @@ -1440,24 +691,24 @@ | |
| 691 | } |
| 692 | |
| 693 | /* |
| 694 | ** Print the timing results. |
| 695 | */ |
| 696 | static void endTimer(FILE *out){ |
| 697 | if( enableTimer ){ |
| 698 | sqlite3_int64 iEnd = timeOfDay(); |
| 699 | struct rusage sEnd; |
| 700 | getrusage(RUSAGE_SELF, &sEnd); |
| 701 | sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", |
| 702 | (iEnd - iBegin)*0.001, |
| 703 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 704 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 705 | } |
| 706 | } |
| 707 | |
| 708 | #define BEGIN_TIMER beginTimer() |
| 709 | #define END_TIMER(X) endTimer(X) |
| 710 | #define HAS_TIMER 1 |
| 711 | |
| 712 | #elif (defined(_WIN32) || defined(WIN32)) |
| 713 | |
| 714 | /* Saved resource information for the beginning of an operation */ |
| @@ -1519,29 +770,29 @@ | |
| 770 | } |
| 771 | |
| 772 | /* |
| 773 | ** Print the timing results. |
| 774 | */ |
| 775 | static void endTimer(FILE *out){ |
| 776 | if( enableTimer && getProcessTimesAddr){ |
| 777 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 778 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 779 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
| 780 | sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", |
| 781 | (ftWallEnd - ftWallBegin)*0.001, |
| 782 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 783 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 784 | } |
| 785 | } |
| 786 | |
| 787 | #define BEGIN_TIMER beginTimer() |
| 788 | #define END_TIMER(X) endTimer(X) |
| 789 | #define HAS_TIMER hasTimer() |
| 790 | |
| 791 | #else |
| 792 | #define BEGIN_TIMER |
| 793 | #define END_TIMER(X) /*no-op*/ |
| 794 | #define HAS_TIMER 0 |
| 795 | #endif |
| 796 | |
| 797 | /* |
| 798 | ** Used to prevent warnings about unused parameters |
| @@ -1738,41 +989,216 @@ | |
| 989 | char *z; |
| 990 | if( iotrace==0 ) return; |
| 991 | va_start(ap, zFormat); |
| 992 | z = sqlite3_vmprintf(zFormat, ap); |
| 993 | va_end(ap); |
| 994 | sqlite3_fprintf(iotrace, "%s", z); |
| 995 | sqlite3_free(z); |
| 996 | } |
| 997 | #endif |
| 998 | |
| 999 | /* Lookup table to estimate the number of columns consumed by a Unicode |
| 1000 | ** character. |
| 1001 | */ |
| 1002 | static const struct { |
| 1003 | unsigned char w; /* Width of the character in columns */ |
| 1004 | int iFirst; /* First character in a span having this width */ |
| 1005 | } aUWidth[] = { |
| 1006 | /* {1, 0x00000}, */ |
| 1007 | {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, |
| 1008 | {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, |
| 1009 | {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, |
| 1010 | {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, |
| 1011 | {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, |
| 1012 | {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, |
| 1013 | {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, |
| 1014 | {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, |
| 1015 | {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, |
| 1016 | {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, |
| 1017 | {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, |
| 1018 | {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, |
| 1019 | {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, |
| 1020 | {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, |
| 1021 | {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, |
| 1022 | {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, |
| 1023 | {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, |
| 1024 | {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, |
| 1025 | {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, |
| 1026 | {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, |
| 1027 | {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, |
| 1028 | {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, |
| 1029 | {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, |
| 1030 | {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, |
| 1031 | {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, |
| 1032 | {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, |
| 1033 | {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, |
| 1034 | {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, |
| 1035 | {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, |
| 1036 | {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, |
| 1037 | {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, |
| 1038 | {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, |
| 1039 | {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, |
| 1040 | {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, |
| 1041 | {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, |
| 1042 | {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, |
| 1043 | {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, |
| 1044 | {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, |
| 1045 | {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, |
| 1046 | {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, |
| 1047 | {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, |
| 1048 | {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, |
| 1049 | {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, |
| 1050 | {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, |
| 1051 | {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, |
| 1052 | {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, |
| 1053 | {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, |
| 1054 | {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, |
| 1055 | {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, |
| 1056 | {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, |
| 1057 | {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, |
| 1058 | {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, |
| 1059 | {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, |
| 1060 | {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, |
| 1061 | {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, |
| 1062 | {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, |
| 1063 | {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, |
| 1064 | {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, |
| 1065 | {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, |
| 1066 | {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, |
| 1067 | {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} |
| 1068 | }; |
| 1069 | |
| 1070 | /* |
| 1071 | ** Return an estimate of the width, in columns, for the single Unicode |
| 1072 | ** character c. For normal characters, the answer is always 1. But the |
| 1073 | ** estimate might be 0 or 2 for zero-width and double-width characters. |
| 1074 | ** |
| 1075 | ** Different display devices display unicode using different widths. So |
| 1076 | ** it is impossible to know that true display width with 100% accuracy. |
| 1077 | ** Inaccuracies in the width estimates might cause columns to be misaligned. |
| 1078 | ** Unfortunately, there is nothing we can do about that. |
| 1079 | */ |
| 1080 | int cli_wcwidth(int c){ |
| 1081 | int iFirst, iLast; |
| 1082 | |
| 1083 | /* Fast path for common characters */ |
| 1084 | if( c<=0x300 ) return 1; |
| 1085 | |
| 1086 | /* The general case */ |
| 1087 | iFirst = 0; |
| 1088 | iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; |
| 1089 | while( iFirst<iLast-1 ){ |
| 1090 | int iMid = (iFirst+iLast)/2; |
| 1091 | int cMid = aUWidth[iMid].iFirst; |
| 1092 | if( cMid < c ){ |
| 1093 | iFirst = iMid; |
| 1094 | }else if( cMid > c ){ |
| 1095 | iLast = iMid - 1; |
| 1096 | }else{ |
| 1097 | return aUWidth[iMid].w; |
| 1098 | } |
| 1099 | } |
| 1100 | if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; |
| 1101 | return aUWidth[iLast].w; |
| 1102 | } |
| 1103 | |
| 1104 | /* |
| 1105 | ** Compute the value and length of a multi-byte UTF-8 character that |
| 1106 | ** begins at z[0]. Return the length. Write the Unicode value into *pU. |
| 1107 | ** |
| 1108 | ** This routine only works for *multi-byte* UTF-8 characters. |
| 1109 | */ |
| 1110 | static int decodeUtf8(const unsigned char *z, int *pU){ |
| 1111 | if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ |
| 1112 | *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); |
| 1113 | return 2; |
| 1114 | } |
| 1115 | if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ |
| 1116 | *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); |
| 1117 | return 3; |
| 1118 | } |
| 1119 | if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 |
| 1120 | && (z[3] & 0xc0)==0x80 |
| 1121 | ){ |
| 1122 | *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 |
| 1123 | | (z[4] & 0x3f); |
| 1124 | return 4; |
| 1125 | } |
| 1126 | *pU = 0; |
| 1127 | return 1; |
| 1128 | } |
| 1129 | |
| 1130 | |
| 1131 | #if 0 /* NOT USED */ |
| 1132 | /* |
| 1133 | ** Return the width, in display columns, of a UTF-8 string. |
| 1134 | ** |
| 1135 | ** Each normal character counts as 1. Zero-width characters count |
| 1136 | ** as zero, and double-width characters count as 2. |
| 1137 | */ |
| 1138 | int cli_wcswidth(const char *z){ |
| 1139 | const unsigned char *a = (const unsigned char*)z; |
| 1140 | int n = 0; |
| 1141 | int i = 0; |
| 1142 | unsigned char c; |
| 1143 | while( (c = a[i])!=0 ){ |
| 1144 | if( c>=0xc0 ){ |
| 1145 | int u; |
| 1146 | int len = decodeUtf8(&a[i], &u); |
| 1147 | i += len; |
| 1148 | n += cli_wcwidth(u); |
| 1149 | }else if( c>=' ' ){ |
| 1150 | n++; |
| 1151 | i++; |
| 1152 | }else{ |
| 1153 | i++; |
| 1154 | } |
| 1155 | } |
| 1156 | return n; |
| 1157 | } |
| 1158 | #endif |
| 1159 | |
| 1160 | /* |
| 1161 | ** Output string zUtf to stdout as w characters. If w is negative, |
| 1162 | ** then right-justify the text. W is the width in UTF-8 characters, not |
| 1163 | ** in bytes. This is different from the %*.*s specification in printf |
| 1164 | ** since with %*.*s the width is measured in bytes, not characters. |
| 1165 | ** |
| 1166 | ** Take into account zero-width and double-width Unicode characters. |
| 1167 | ** In other words, a zero-width character does not count toward the |
| 1168 | ** the w limit. A double-width character counts as two. |
| 1169 | */ |
| 1170 | static void utf8_width_print(FILE *out, int w, const char *zUtf){ |
| 1171 | const unsigned char *a = (const unsigned char*)zUtf; |
| 1172 | unsigned char c; |
| 1173 | int i = 0; |
| 1174 | int n = 0; |
| 1175 | int aw = w<0 ? -w : w; |
| 1176 | if( zUtf==0 ) zUtf = ""; |
| 1177 | while( (c = a[i])!=0 ){ |
| 1178 | if( (c&0xc0)==0xc0 ){ |
| 1179 | int u; |
| 1180 | int len = decodeUtf8(a+i, &u); |
| 1181 | int x = cli_wcwidth(u); |
| 1182 | if( x+n>aw ){ |
| 1183 | break; |
| 1184 | } |
| 1185 | i += len; |
| 1186 | n += x; |
| 1187 | }else if( n>=aw ){ |
| 1188 | break; |
| 1189 | }else{ |
| 1190 | n++; |
| 1191 | i++; |
| 1192 | } |
| 1193 | } |
| 1194 | if( n>=aw ){ |
| 1195 | sqlite3_fprintf(out, "%.*s", i, zUtf); |
| 1196 | }else if( w<0 ){ |
| 1197 | sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf); |
| 1198 | }else{ |
| 1199 | sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, ""); |
| 1200 | } |
| 1201 | } |
| 1202 | |
| 1203 | |
| 1204 | /* |
| @@ -1834,11 +1260,11 @@ | |
| 1260 | struct __stat64 x = {0}; |
| 1261 | # define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) |
| 1262 | /* On Windows, open first, then check the stream nature. This order |
| 1263 | ** is necessary because _stat() and sibs, when checking a named pipe, |
| 1264 | ** effectively break the pipe as its supplier sees it. */ |
| 1265 | FILE *rv = sqlite3_fopen(zFile, "rb"); |
| 1266 | if( rv==0 ) return 0; |
| 1267 | if( _fstat64(_fileno(rv), &x) != 0 |
| 1268 | || !STAT_CHR_SRC(x.st_mode)){ |
| 1269 | fclose(rv); |
| 1270 | rv = 0; |
| @@ -1848,11 +1274,11 @@ | |
| 1274 | struct stat x = {0}; |
| 1275 | int rc = stat(zFile, &x); |
| 1276 | # define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) |
| 1277 | if( rc!=0 ) return 0; |
| 1278 | if( STAT_CHR_SRC(x.st_mode) ){ |
| 1279 | return sqlite3_fopen(zFile, "rb"); |
| 1280 | }else{ |
| 1281 | return 0; |
| 1282 | } |
| 1283 | #endif |
| 1284 | #undef STAT_CHR_SRC |
| @@ -1875,11 +1301,11 @@ | |
| 1301 | if( n+100>nLine ){ |
| 1302 | nLine = nLine*2 + 100; |
| 1303 | zLine = realloc(zLine, nLine); |
| 1304 | shell_check_oom(zLine); |
| 1305 | } |
| 1306 | if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){ |
| 1307 | if( n==0 ){ |
| 1308 | free(zLine); |
| 1309 | return 0; |
| 1310 | } |
| 1311 | zLine[n] = 0; |
| @@ -2941,11 +2367,11 @@ | |
| 2367 | ** |
| 2368 | ****************************************************************************** |
| 2369 | ** |
| 2370 | ** This SQLite extension implements functions that compute SHA3 hashes |
| 2371 | ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. |
| 2372 | ** Three SQL functions are implemented: |
| 2373 | ** |
| 2374 | ** sha3(X,SIZE) |
| 2375 | ** sha3_agg(Y,SIZE) |
| 2376 | ** sha3_query(Z,SIZE) |
| 2377 | ** |
| @@ -3783,10 +3209,422 @@ | |
| 3209 | } |
| 3210 | return rc; |
| 3211 | } |
| 3212 | |
| 3213 | /************************* End ../ext/misc/shathree.c ********************/ |
| 3214 | /************************* Begin ../ext/misc/sha1.c ******************/ |
| 3215 | /* |
| 3216 | ** 2017-01-27 |
| 3217 | ** |
| 3218 | ** The author disclaims copyright to this source code. In place of |
| 3219 | ** a legal notice, here is a blessing: |
| 3220 | ** |
| 3221 | ** May you do good and not evil. |
| 3222 | ** May you find forgiveness for yourself and forgive others. |
| 3223 | ** May you share freely, never taking more than you give. |
| 3224 | ** |
| 3225 | ****************************************************************************** |
| 3226 | ** |
| 3227 | ** This SQLite extension implements functions that compute SHA1 hashes. |
| 3228 | ** Two SQL functions are implemented: |
| 3229 | ** |
| 3230 | ** sha1(X) |
| 3231 | ** sha1_query(Y) |
| 3232 | ** |
| 3233 | ** The sha1(X) function computes the SHA1 hash of the input X, or NULL if |
| 3234 | ** X is NULL. |
| 3235 | ** |
| 3236 | ** The sha1_query(Y) function evalutes all queries in the SQL statements of Y |
| 3237 | ** and returns a hash of their results. |
| 3238 | */ |
| 3239 | /* #include "sqlite3ext.h" */ |
| 3240 | SQLITE_EXTENSION_INIT1 |
| 3241 | #include <assert.h> |
| 3242 | #include <string.h> |
| 3243 | #include <stdarg.h> |
| 3244 | |
| 3245 | /****************************************************************************** |
| 3246 | ** The Hash Engine |
| 3247 | */ |
| 3248 | /* Context for the SHA1 hash */ |
| 3249 | typedef struct SHA1Context SHA1Context; |
| 3250 | struct SHA1Context { |
| 3251 | unsigned int state[5]; |
| 3252 | unsigned int count[2]; |
| 3253 | unsigned char buffer[64]; |
| 3254 | }; |
| 3255 | |
| 3256 | #define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) |
| 3257 | #define rol(x,k) SHA_ROT(x,k,32-(k)) |
| 3258 | #define ror(x,k) SHA_ROT(x,32-(k),k) |
| 3259 | |
| 3260 | #define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ |
| 3261 | |(rol(block[i],8)&0x00FF00FF)) |
| 3262 | #define blk0be(i) block[i] |
| 3263 | #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ |
| 3264 | ^block[(i+2)&15]^block[i&15],1)) |
| 3265 | |
| 3266 | /* |
| 3267 | * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 |
| 3268 | * |
| 3269 | * Rl0() for little-endian and Rb0() for big-endian. Endianness is |
| 3270 | * determined at run-time. |
| 3271 | */ |
| 3272 | #define Rl0(v,w,x,y,z,i) \ |
| 3273 | z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); |
| 3274 | #define Rb0(v,w,x,y,z,i) \ |
| 3275 | z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); |
| 3276 | #define R1(v,w,x,y,z,i) \ |
| 3277 | z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); |
| 3278 | #define R2(v,w,x,y,z,i) \ |
| 3279 | z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); |
| 3280 | #define R3(v,w,x,y,z,i) \ |
| 3281 | z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); |
| 3282 | #define R4(v,w,x,y,z,i) \ |
| 3283 | z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); |
| 3284 | |
| 3285 | /* |
| 3286 | * Hash a single 512-bit block. This is the core of the algorithm. |
| 3287 | */ |
| 3288 | static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){ |
| 3289 | unsigned int qq[5]; /* a, b, c, d, e; */ |
| 3290 | static int one = 1; |
| 3291 | unsigned int block[16]; |
| 3292 | memcpy(block, buffer, 64); |
| 3293 | memcpy(qq,state,5*sizeof(unsigned int)); |
| 3294 | |
| 3295 | #define a qq[0] |
| 3296 | #define b qq[1] |
| 3297 | #define c qq[2] |
| 3298 | #define d qq[3] |
| 3299 | #define e qq[4] |
| 3300 | |
| 3301 | /* Copy p->state[] to working vars */ |
| 3302 | /* |
| 3303 | a = state[0]; |
| 3304 | b = state[1]; |
| 3305 | c = state[2]; |
| 3306 | d = state[3]; |
| 3307 | e = state[4]; |
| 3308 | */ |
| 3309 | |
| 3310 | /* 4 rounds of 20 operations each. Loop unrolled. */ |
| 3311 | if( 1 == *(unsigned char*)&one ){ |
| 3312 | Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); |
| 3313 | Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); |
| 3314 | Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); |
| 3315 | Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); |
| 3316 | }else{ |
| 3317 | Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); |
| 3318 | Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); |
| 3319 | Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); |
| 3320 | Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); |
| 3321 | } |
| 3322 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); |
| 3323 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); |
| 3324 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); |
| 3325 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); |
| 3326 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); |
| 3327 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); |
| 3328 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); |
| 3329 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); |
| 3330 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); |
| 3331 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); |
| 3332 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); |
| 3333 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); |
| 3334 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); |
| 3335 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); |
| 3336 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); |
| 3337 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); |
| 3338 | |
| 3339 | /* Add the working vars back into context.state[] */ |
| 3340 | state[0] += a; |
| 3341 | state[1] += b; |
| 3342 | state[2] += c; |
| 3343 | state[3] += d; |
| 3344 | state[4] += e; |
| 3345 | |
| 3346 | #undef a |
| 3347 | #undef b |
| 3348 | #undef c |
| 3349 | #undef d |
| 3350 | #undef e |
| 3351 | } |
| 3352 | |
| 3353 | |
| 3354 | /* Initialize a SHA1 context */ |
| 3355 | static void hash_init(SHA1Context *p){ |
| 3356 | /* SHA1 initialization constants */ |
| 3357 | p->state[0] = 0x67452301; |
| 3358 | p->state[1] = 0xEFCDAB89; |
| 3359 | p->state[2] = 0x98BADCFE; |
| 3360 | p->state[3] = 0x10325476; |
| 3361 | p->state[4] = 0xC3D2E1F0; |
| 3362 | p->count[0] = p->count[1] = 0; |
| 3363 | } |
| 3364 | |
| 3365 | /* Add new content to the SHA1 hash */ |
| 3366 | static void hash_step( |
| 3367 | SHA1Context *p, /* Add content to this context */ |
| 3368 | const unsigned char *data, /* Data to be added */ |
| 3369 | unsigned int len /* Number of bytes in data */ |
| 3370 | ){ |
| 3371 | unsigned int i, j; |
| 3372 | |
| 3373 | j = p->count[0]; |
| 3374 | if( (p->count[0] += len << 3) < j ){ |
| 3375 | p->count[1] += (len>>29)+1; |
| 3376 | } |
| 3377 | j = (j >> 3) & 63; |
| 3378 | if( (j + len) > 63 ){ |
| 3379 | (void)memcpy(&p->buffer[j], data, (i = 64-j)); |
| 3380 | SHA1Transform(p->state, p->buffer); |
| 3381 | for(; i + 63 < len; i += 64){ |
| 3382 | SHA1Transform(p->state, &data[i]); |
| 3383 | } |
| 3384 | j = 0; |
| 3385 | }else{ |
| 3386 | i = 0; |
| 3387 | } |
| 3388 | (void)memcpy(&p->buffer[j], &data[i], len - i); |
| 3389 | } |
| 3390 | |
| 3391 | /* Compute a string using sqlite3_vsnprintf() and hash it */ |
| 3392 | static void hash_step_vformat( |
| 3393 | SHA1Context *p, /* Add content to this context */ |
| 3394 | const char *zFormat, |
| 3395 | ... |
| 3396 | ){ |
| 3397 | va_list ap; |
| 3398 | int n; |
| 3399 | char zBuf[50]; |
| 3400 | va_start(ap, zFormat); |
| 3401 | sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); |
| 3402 | va_end(ap); |
| 3403 | n = (int)strlen(zBuf); |
| 3404 | hash_step(p, (unsigned char*)zBuf, n); |
| 3405 | } |
| 3406 | |
| 3407 | |
| 3408 | /* Add padding and compute the message digest. Render the |
| 3409 | ** message digest as lower-case hexadecimal and put it into |
| 3410 | ** zOut[]. zOut[] must be at least 41 bytes long. */ |
| 3411 | static void hash_finish( |
| 3412 | SHA1Context *p, /* The SHA1 context to finish and render */ |
| 3413 | char *zOut, /* Store hex or binary hash here */ |
| 3414 | int bAsBinary /* 1 for binary hash, 0 for hex hash */ |
| 3415 | ){ |
| 3416 | unsigned int i; |
| 3417 | unsigned char finalcount[8]; |
| 3418 | unsigned char digest[20]; |
| 3419 | static const char zEncode[] = "0123456789abcdef"; |
| 3420 | |
| 3421 | for (i = 0; i < 8; i++){ |
| 3422 | finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)] |
| 3423 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ |
| 3424 | } |
| 3425 | hash_step(p, (const unsigned char *)"\200", 1); |
| 3426 | while ((p->count[0] & 504) != 448){ |
| 3427 | hash_step(p, (const unsigned char *)"\0", 1); |
| 3428 | } |
| 3429 | hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */ |
| 3430 | for (i = 0; i < 20; i++){ |
| 3431 | digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 3432 | } |
| 3433 | if( bAsBinary ){ |
| 3434 | memcpy(zOut, digest, 20); |
| 3435 | }else{ |
| 3436 | for(i=0; i<20; i++){ |
| 3437 | zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; |
| 3438 | zOut[i*2+1] = zEncode[digest[i] & 0xf]; |
| 3439 | } |
| 3440 | zOut[i*2]= 0; |
| 3441 | } |
| 3442 | } |
| 3443 | /* End of the hashing logic |
| 3444 | *****************************************************************************/ |
| 3445 | |
| 3446 | /* |
| 3447 | ** Implementation of the sha1(X) function. |
| 3448 | ** |
| 3449 | ** Return a lower-case hexadecimal rendering of the SHA1 hash of the |
| 3450 | ** argument X. If X is a BLOB, it is hashed as is. For all other |
| 3451 | ** types of input, X is converted into a UTF-8 string and the string |
| 3452 | ** is hash without the trailing 0x00 terminator. The hash of a NULL |
| 3453 | ** value is NULL. |
| 3454 | */ |
| 3455 | static void sha1Func( |
| 3456 | sqlite3_context *context, |
| 3457 | int argc, |
| 3458 | sqlite3_value **argv |
| 3459 | ){ |
| 3460 | SHA1Context cx; |
| 3461 | int eType = sqlite3_value_type(argv[0]); |
| 3462 | int nByte = sqlite3_value_bytes(argv[0]); |
| 3463 | char zOut[44]; |
| 3464 | |
| 3465 | assert( argc==1 ); |
| 3466 | if( eType==SQLITE_NULL ) return; |
| 3467 | hash_init(&cx); |
| 3468 | if( eType==SQLITE_BLOB ){ |
| 3469 | hash_step(&cx, sqlite3_value_blob(argv[0]), nByte); |
| 3470 | }else{ |
| 3471 | hash_step(&cx, sqlite3_value_text(argv[0]), nByte); |
| 3472 | } |
| 3473 | if( sqlite3_user_data(context)!=0 ){ |
| 3474 | hash_finish(&cx, zOut, 1); |
| 3475 | sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT); |
| 3476 | }else{ |
| 3477 | hash_finish(&cx, zOut, 0); |
| 3478 | sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT); |
| 3479 | } |
| 3480 | } |
| 3481 | |
| 3482 | /* |
| 3483 | ** Implementation of the sha1_query(SQL) function. |
| 3484 | ** |
| 3485 | ** This function compiles and runs the SQL statement(s) given in the |
| 3486 | ** argument. The results are hashed using SHA1 and that hash is returned. |
| 3487 | ** |
| 3488 | ** The original SQL text is included as part of the hash. |
| 3489 | ** |
| 3490 | ** The hash is not just a concatenation of the outputs. Each query |
| 3491 | ** is delimited and each row and value within the query is delimited, |
| 3492 | ** with all values being marked with their datatypes. |
| 3493 | */ |
| 3494 | static void sha1QueryFunc( |
| 3495 | sqlite3_context *context, |
| 3496 | int argc, |
| 3497 | sqlite3_value **argv |
| 3498 | ){ |
| 3499 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 3500 | const char *zSql = (const char*)sqlite3_value_text(argv[0]); |
| 3501 | sqlite3_stmt *pStmt = 0; |
| 3502 | int nCol; /* Number of columns in the result set */ |
| 3503 | int i; /* Loop counter */ |
| 3504 | int rc; |
| 3505 | int n; |
| 3506 | const char *z; |
| 3507 | SHA1Context cx; |
| 3508 | char zOut[44]; |
| 3509 | |
| 3510 | assert( argc==1 ); |
| 3511 | if( zSql==0 ) return; |
| 3512 | hash_init(&cx); |
| 3513 | while( zSql[0] ){ |
| 3514 | rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); |
| 3515 | if( rc ){ |
| 3516 | char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", |
| 3517 | zSql, sqlite3_errmsg(db)); |
| 3518 | sqlite3_finalize(pStmt); |
| 3519 | sqlite3_result_error(context, zMsg, -1); |
| 3520 | sqlite3_free(zMsg); |
| 3521 | return; |
| 3522 | } |
| 3523 | if( !sqlite3_stmt_readonly(pStmt) ){ |
| 3524 | char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); |
| 3525 | sqlite3_finalize(pStmt); |
| 3526 | sqlite3_result_error(context, zMsg, -1); |
| 3527 | sqlite3_free(zMsg); |
| 3528 | return; |
| 3529 | } |
| 3530 | nCol = sqlite3_column_count(pStmt); |
| 3531 | z = sqlite3_sql(pStmt); |
| 3532 | n = (int)strlen(z); |
| 3533 | hash_step_vformat(&cx,"S%d:",n); |
| 3534 | hash_step(&cx,(unsigned char*)z,n); |
| 3535 | |
| 3536 | /* Compute a hash over the result of the query */ |
| 3537 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 3538 | hash_step(&cx,(const unsigned char*)"R",1); |
| 3539 | for(i=0; i<nCol; i++){ |
| 3540 | switch( sqlite3_column_type(pStmt,i) ){ |
| 3541 | case SQLITE_NULL: { |
| 3542 | hash_step(&cx, (const unsigned char*)"N",1); |
| 3543 | break; |
| 3544 | } |
| 3545 | case SQLITE_INTEGER: { |
| 3546 | sqlite3_uint64 u; |
| 3547 | int j; |
| 3548 | unsigned char x[9]; |
| 3549 | sqlite3_int64 v = sqlite3_column_int64(pStmt,i); |
| 3550 | memcpy(&u, &v, 8); |
| 3551 | for(j=8; j>=1; j--){ |
| 3552 | x[j] = u & 0xff; |
| 3553 | u >>= 8; |
| 3554 | } |
| 3555 | x[0] = 'I'; |
| 3556 | hash_step(&cx, x, 9); |
| 3557 | break; |
| 3558 | } |
| 3559 | case SQLITE_FLOAT: { |
| 3560 | sqlite3_uint64 u; |
| 3561 | int j; |
| 3562 | unsigned char x[9]; |
| 3563 | double r = sqlite3_column_double(pStmt,i); |
| 3564 | memcpy(&u, &r, 8); |
| 3565 | for(j=8; j>=1; j--){ |
| 3566 | x[j] = u & 0xff; |
| 3567 | u >>= 8; |
| 3568 | } |
| 3569 | x[0] = 'F'; |
| 3570 | hash_step(&cx,x,9); |
| 3571 | break; |
| 3572 | } |
| 3573 | case SQLITE_TEXT: { |
| 3574 | int n2 = sqlite3_column_bytes(pStmt, i); |
| 3575 | const unsigned char *z2 = sqlite3_column_text(pStmt, i); |
| 3576 | hash_step_vformat(&cx,"T%d:",n2); |
| 3577 | hash_step(&cx, z2, n2); |
| 3578 | break; |
| 3579 | } |
| 3580 | case SQLITE_BLOB: { |
| 3581 | int n2 = sqlite3_column_bytes(pStmt, i); |
| 3582 | const unsigned char *z2 = sqlite3_column_blob(pStmt, i); |
| 3583 | hash_step_vformat(&cx,"B%d:",n2); |
| 3584 | hash_step(&cx, z2, n2); |
| 3585 | break; |
| 3586 | } |
| 3587 | } |
| 3588 | } |
| 3589 | } |
| 3590 | sqlite3_finalize(pStmt); |
| 3591 | } |
| 3592 | hash_finish(&cx, zOut, 0); |
| 3593 | sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); |
| 3594 | } |
| 3595 | |
| 3596 | |
| 3597 | #ifdef _WIN32 |
| 3598 | |
| 3599 | #endif |
| 3600 | int sqlite3_sha_init( |
| 3601 | sqlite3 *db, |
| 3602 | char **pzErrMsg, |
| 3603 | const sqlite3_api_routines *pApi |
| 3604 | ){ |
| 3605 | int rc = SQLITE_OK; |
| 3606 | static int one = 1; |
| 3607 | SQLITE_EXTENSION_INIT2(pApi); |
| 3608 | (void)pzErrMsg; /* Unused parameter */ |
| 3609 | rc = sqlite3_create_function(db, "sha1", 1, |
| 3610 | SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, |
| 3611 | 0, sha1Func, 0, 0); |
| 3612 | if( rc==SQLITE_OK ){ |
| 3613 | rc = sqlite3_create_function(db, "sha1b", 1, |
| 3614 | SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, |
| 3615 | (void*)&one, sha1Func, 0, 0); |
| 3616 | } |
| 3617 | if( rc==SQLITE_OK ){ |
| 3618 | rc = sqlite3_create_function(db, "sha1_query", 1, |
| 3619 | SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| 3620 | sha1QueryFunc, 0, 0); |
| 3621 | } |
| 3622 | return rc; |
| 3623 | } |
| 3624 | |
| 3625 | /************************* End ../ext/misc/sha1.c ********************/ |
| 3626 | /************************* Begin ../ext/misc/uint.c ******************/ |
| 3627 | /* |
| 3628 | ** 2020-04-14 |
| 3629 | ** |
| 3630 | ** The author disclaims copyright to this source code. In place of |
| @@ -5075,11 +4913,11 @@ | |
| 4913 | }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){ |
| 4914 | p->a[p->nUsed++] = y; |
| 4915 | }else if( p->bKeepSorted ){ |
| 4916 | int i; |
| 4917 | i = percentBinarySearch(p, y, 0); |
| 4918 | if( i<(int)p->nUsed ){ |
| 4919 | memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0])); |
| 4920 | } |
| 4921 | p->a[i] = y; |
| 4922 | p->nUsed++; |
| 4923 | }else{ |
| @@ -5191,11 +5029,11 @@ | |
| 5029 | |
| 5030 | /* Find and remove the row */ |
| 5031 | i = percentBinarySearch(p, y, 1); |
| 5032 | if( i>=0 ){ |
| 5033 | p->nUsed--; |
| 5034 | if( i<(int)p->nUsed ){ |
| 5035 | memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0])); |
| 5036 | } |
| 5037 | } |
| 5038 | } |
| 5039 | |
| @@ -5251,15 +5089,15 @@ | |
| 5089 | sqlite3 *db, |
| 5090 | char **pzErrMsg, |
| 5091 | const sqlite3_api_routines *pApi |
| 5092 | ){ |
| 5093 | int rc = SQLITE_OK; |
| 5094 | unsigned int i; |
| 5095 | #ifdef SQLITE3EXT_H |
| 5096 | SQLITE_EXTENSION_INIT2(pApi); |
| 5097 | #else |
| 5098 | (void)pApi; /* Unused parameter */ |
| 5099 | #endif |
| 5100 | (void)pzErrMsg; /* Unused parameter */ |
| 5101 | for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){ |
| 5102 | rc = sqlite3_create_window_function(db, |
| 5103 | aPercentFunc[i].zName, |
| @@ -7012,50 +6850,58 @@ | |
| 6850 | aIdx[4] = i; |
| 6851 | idxNum |= 0x40; |
| 6852 | } |
| 6853 | continue; |
| 6854 | } |
| 6855 | if( pConstraint->iColumn<SERIES_COLUMN_START ){ |
| 6856 | if( pConstraint->iColumn==SERIES_COLUMN_VALUE && pConstraint->usable ){ |
| 6857 | switch( op ){ |
| 6858 | case SQLITE_INDEX_CONSTRAINT_EQ: |
| 6859 | case SQLITE_INDEX_CONSTRAINT_IS: { |
| 6860 | idxNum |= 0x0080; |
| 6861 | idxNum &= ~0x3300; |
| 6862 | aIdx[5] = i; |
| 6863 | aIdx[6] = -1; |
| 6864 | #ifndef ZERO_ARGUMENT_GENERATE_SERIES |
| 6865 | bStartSeen = 1; |
| 6866 | #endif |
| 6867 | break; |
| 6868 | } |
| 6869 | case SQLITE_INDEX_CONSTRAINT_GE: { |
| 6870 | if( idxNum & 0x0080 ) break; |
| 6871 | idxNum |= 0x0100; |
| 6872 | idxNum &= ~0x0200; |
| 6873 | aIdx[5] = i; |
| 6874 | #ifndef ZERO_ARGUMENT_GENERATE_SERIES |
| 6875 | bStartSeen = 1; |
| 6876 | #endif |
| 6877 | break; |
| 6878 | } |
| 6879 | case SQLITE_INDEX_CONSTRAINT_GT: { |
| 6880 | if( idxNum & 0x0080 ) break; |
| 6881 | idxNum |= 0x0200; |
| 6882 | idxNum &= ~0x0100; |
| 6883 | aIdx[5] = i; |
| 6884 | #ifndef ZERO_ARGUMENT_GENERATE_SERIES |
| 6885 | bStartSeen = 1; |
| 6886 | #endif |
| 6887 | break; |
| 6888 | } |
| 6889 | case SQLITE_INDEX_CONSTRAINT_LE: { |
| 6890 | if( idxNum & 0x0080 ) break; |
| 6891 | idxNum |= 0x1000; |
| 6892 | idxNum &= ~0x2000; |
| 6893 | aIdx[6] = i; |
| 6894 | break; |
| 6895 | } |
| 6896 | case SQLITE_INDEX_CONSTRAINT_LT: { |
| 6897 | if( idxNum & 0x0080 ) break; |
| 6898 | idxNum |= 0x2000; |
| 6899 | idxNum &= ~0x1000; |
| 6900 | aIdx[6] = i; |
| 6901 | break; |
| 6902 | } |
| 6903 | } |
| 6904 | } |
| 6905 | continue; |
| 6906 | } |
| 6907 | iCol = pConstraint->iColumn - SERIES_COLUMN_START; |
| @@ -8115,11 +7961,11 @@ | |
| 7961 | ** If the optional MTIME argument is present, then it is interpreted |
| 7962 | ** as an integer - the number of seconds since the unix epoch. The |
| 7963 | ** modification-time of the target file is set to this value before |
| 7964 | ** returning. |
| 7965 | ** |
| 7966 | ** If five or more arguments are passed to this function and an |
| 7967 | ** error is encountered, an exception is raised. |
| 7968 | ** |
| 7969 | ** READFILE(FILE): |
| 7970 | ** |
| 7971 | ** Read and return the contents of file FILE (type blob) from disk. |
| @@ -8185,10 +8031,17 @@ | |
| 8031 | # define lstat(path,buf) stat(path,buf) |
| 8032 | #endif |
| 8033 | #include <time.h> |
| 8034 | #include <errno.h> |
| 8035 | |
| 8036 | /* When used as part of the CLI, the sqlite3_stdio.h module will have |
| 8037 | ** been included before this one. In that case use the sqlite3_stdio.h |
| 8038 | ** #defines. If not, create our own for fopen(). |
| 8039 | */ |
| 8040 | #ifndef _SQLITE3_STDIO_H_ |
| 8041 | # define sqlite3_fopen fopen |
| 8042 | #endif |
| 8043 | |
| 8044 | /* |
| 8045 | ** Structure of the fsdir() table-valued function |
| 8046 | */ |
| 8047 | /* 0 1 2 3 4 5 */ |
| @@ -8217,11 +8070,11 @@ | |
| 8070 | sqlite3_int64 nIn; |
| 8071 | void *pBuf; |
| 8072 | sqlite3 *db; |
| 8073 | int mxBlob; |
| 8074 | |
| 8075 | in = sqlite3_fopen(zName, "rb"); |
| 8076 | if( in==0 ){ |
| 8077 | /* File does not exist or is unreadable. Leave the result set to NULL. */ |
| 8078 | return; |
| 8079 | } |
| 8080 | fseek(in, 0, SEEK_END); |
| @@ -8472,11 +8325,11 @@ | |
| 8325 | } |
| 8326 | }else{ |
| 8327 | sqlite3_int64 nWrite = 0; |
| 8328 | const char *z; |
| 8329 | int rc = 0; |
| 8330 | FILE *out = sqlite3_fopen(zFile, "wb"); |
| 8331 | if( out==0 ) return 1; |
| 8332 | z = (const char*)sqlite3_value_blob(pData); |
| 8333 | if( z ){ |
| 8334 | sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out); |
| 8335 | nWrite = sqlite3_value_bytes(pData); |
| @@ -10333,10 +10186,18 @@ | |
| 10186 | #ifndef SQLITE_NO_STDINT |
| 10187 | # include <stdint.h> |
| 10188 | #endif |
| 10189 | |
| 10190 | #include <zlib.h> |
| 10191 | |
| 10192 | /* When used as part of the CLI, the sqlite3_stdio.h module will have |
| 10193 | ** been included before this one. In that case use the sqlite3_stdio.h |
| 10194 | ** #defines. If not, create our own for fopen(). |
| 10195 | */ |
| 10196 | #ifndef _SQLITE3_STDIO_H_ |
| 10197 | # define sqlite3_fopen fopen |
| 10198 | #endif |
| 10199 | |
| 10200 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 10201 | |
| 10202 | #ifndef SQLITE_AMALGAMATION |
| 10203 | |
| @@ -11590,11 +11451,11 @@ | |
| 11451 | }else{ |
| 11452 | zFile = (const char*)sqlite3_value_text(argv[0]); |
| 11453 | } |
| 11454 | |
| 11455 | if( 0==pTab->pWriteFd && 0==bInMemory ){ |
| 11456 | pCsr->pFile = zFile ? sqlite3_fopen(zFile, "rb") : 0; |
| 11457 | if( pCsr->pFile==0 ){ |
| 11458 | zipfileCursorErr(pCsr, "cannot open file: %s", zFile); |
| 11459 | rc = SQLITE_ERROR; |
| 11460 | }else{ |
| 11461 | rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd); |
| @@ -11780,11 +11641,11 @@ | |
| 11641 | |
| 11642 | /* Open a write fd on the file. Also load the entire central directory |
| 11643 | ** structure into memory. During the transaction any new file data is |
| 11644 | ** appended to the archive file, but the central directory is accumulated |
| 11645 | ** in main-memory until the transaction is committed. */ |
| 11646 | pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+"); |
| 11647 | if( pTab->pWriteFd==0 ){ |
| 11648 | pTab->base.zErrMsg = sqlite3_mprintf( |
| 11649 | "zipfile: failed to open file %s for writing", pTab->zFile |
| 11650 | ); |
| 11651 | rc = SQLITE_ERROR; |
| @@ -14233,10 +14094,70 @@ | |
| 14094 | } |
| 14095 | |
| 14096 | return rc; |
| 14097 | } |
| 14098 | |
| 14099 | /* |
| 14100 | ** This function tests if the schema of the main database of database handle |
| 14101 | ** db contains an object named zTab. Assuming no error occurs, output parameter |
| 14102 | ** (*pbContains) is set to true if zTab exists, or false if it does not. |
| 14103 | ** |
| 14104 | ** Or, if an error occurs, an SQLite error code is returned. The final value |
| 14105 | ** of (*pbContains) is undefined in this case. |
| 14106 | */ |
| 14107 | static int expertDbContainsObject( |
| 14108 | sqlite3 *db, |
| 14109 | const char *zTab, |
| 14110 | int *pbContains /* OUT: True if object exists */ |
| 14111 | ){ |
| 14112 | const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?"; |
| 14113 | sqlite3_stmt *pSql = 0; |
| 14114 | int rc = SQLITE_OK; |
| 14115 | int ret = 0; |
| 14116 | |
| 14117 | rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); |
| 14118 | if( rc==SQLITE_OK ){ |
| 14119 | sqlite3_bind_text(pSql, 1, zTab, -1, SQLITE_STATIC); |
| 14120 | if( SQLITE_ROW==sqlite3_step(pSql) ){ |
| 14121 | ret = 1; |
| 14122 | } |
| 14123 | rc = sqlite3_finalize(pSql); |
| 14124 | } |
| 14125 | |
| 14126 | *pbContains = ret; |
| 14127 | return rc; |
| 14128 | } |
| 14129 | |
| 14130 | /* |
| 14131 | ** Execute SQL command zSql using database handle db. If no error occurs, |
| 14132 | ** set (*pzErr) to NULL and return SQLITE_OK. |
| 14133 | ** |
| 14134 | ** If an error does occur, return an SQLite error code and set (*pzErr) to |
| 14135 | ** point to a buffer containing an English language error message. Except, |
| 14136 | ** if the error message begins with "no such module:", then ignore the |
| 14137 | ** error and return as if the SQL statement had succeeded. |
| 14138 | ** |
| 14139 | ** This is used to copy as much of the database schema as possible while |
| 14140 | ** ignoring any errors related to missing virtual table modules. |
| 14141 | */ |
| 14142 | static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){ |
| 14143 | int rc = SQLITE_OK; |
| 14144 | char *zErr = 0; |
| 14145 | |
| 14146 | rc = sqlite3_exec(db, zSql, 0, 0, &zErr); |
| 14147 | if( rc!=SQLITE_OK && zErr ){ |
| 14148 | int nErr = STRLEN(zErr); |
| 14149 | if( nErr>=15 && memcmp(zErr, "no such module:", 15)==0 ){ |
| 14150 | sqlite3_free(zErr); |
| 14151 | rc = SQLITE_OK; |
| 14152 | zErr = 0; |
| 14153 | } |
| 14154 | } |
| 14155 | |
| 14156 | *pzErr = zErr; |
| 14157 | return rc; |
| 14158 | } |
| 14159 | |
| 14160 | static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){ |
| 14161 | int rc = idxRegisterVtab(p); |
| 14162 | sqlite3_stmt *pSchema = 0; |
| 14163 | |
| @@ -14244,30 +14165,39 @@ | |
| 14165 | ** |
| 14166 | ** 1) Add an entry to the p->pTable list, and |
| 14167 | ** 2) Create the equivalent virtual table in dbv. |
| 14168 | */ |
| 14169 | rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg, |
| 14170 | "SELECT type, name, sql, 1, " |
| 14171 | " substr(sql,1,14)=='create virtual' COLLATE nocase " |
| 14172 | "FROM sqlite_schema " |
| 14173 | "WHERE type IN ('table','view') AND " |
| 14174 | " substr(name,1,7)!='sqlite_' COLLATE nocase " |
| 14175 | " UNION ALL " |
| 14176 | "SELECT type, name, sql, 2, 0 FROM sqlite_schema " |
| 14177 | "WHERE type = 'trigger'" |
| 14178 | " AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') " |
| 14179 | "ORDER BY 4, 5 DESC, 1" |
| 14180 | ); |
| 14181 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){ |
| 14182 | const char *zType = (const char*)sqlite3_column_text(pSchema, 0); |
| 14183 | const char *zName = (const char*)sqlite3_column_text(pSchema, 1); |
| 14184 | const char *zSql = (const char*)sqlite3_column_text(pSchema, 2); |
| 14185 | int bVirtual = sqlite3_column_int(pSchema, 4); |
| 14186 | int bExists = 0; |
| 14187 | |
| 14188 | if( zType==0 || zName==0 ) continue; |
| 14189 | rc = expertDbContainsObject(p->dbv, zName, &bExists); |
| 14190 | if( rc || bExists ) continue; |
| 14191 | |
| 14192 | if( zType[0]=='v' || zType[1]=='r' || bVirtual ){ |
| 14193 | /* A view. Or a trigger on a view. */ |
| 14194 | if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg); |
| 14195 | }else{ |
| 14196 | IdxTable *pTab; |
| 14197 | rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg); |
| 14198 | if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){ |
| 14199 | int i; |
| 14200 | char *zInner = 0; |
| 14201 | char *zOuter = 0; |
| 14202 | pTab->pNext = p->pTable; |
| 14203 | p->pTable = pTab; |
| @@ -14464,10 +14394,16 @@ | |
| 14394 | sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC); |
| 14395 | while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){ |
| 14396 | const char *zComma = zCols==0 ? "" : ", "; |
| 14397 | const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0); |
| 14398 | const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1); |
| 14399 | if( zName==0 ){ |
| 14400 | /* This index contains an expression. Ignore it. */ |
| 14401 | sqlite3_free(zCols); |
| 14402 | sqlite3_free(zOrder); |
| 14403 | return sqlite3_reset(pIndexXInfo); |
| 14404 | } |
| 14405 | zCols = idxAppendText(&rc, zCols, |
| 14406 | "%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s", |
| 14407 | zComma, zName, nCol, zName, zColl |
| 14408 | ); |
| 14409 | zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol); |
| @@ -14792,16 +14728,22 @@ | |
| 14728 | |
| 14729 | /* Copy the entire schema of database [db] into [dbm]. */ |
| 14730 | if( rc==SQLITE_OK ){ |
| 14731 | sqlite3_stmt *pSql = 0; |
| 14732 | rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, |
| 14733 | "SELECT sql, name, substr(sql,1,14)=='create virtual' COLLATE nocase" |
| 14734 | " FROM sqlite_schema WHERE substr(name,1,7)!='sqlite_' COLLATE nocase" |
| 14735 | " ORDER BY 3 DESC, rowid" |
| 14736 | ); |
| 14737 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 14738 | const char *zSql = (const char*)sqlite3_column_text(pSql, 0); |
| 14739 | const char *zName = (const char*)sqlite3_column_text(pSql, 1); |
| 14740 | int bExists = 0; |
| 14741 | rc = expertDbContainsObject(pNew->dbm, zName, &bExists); |
| 14742 | if( rc==SQLITE_OK && zSql && bExists==0 ){ |
| 14743 | rc = expertSchemaSql(pNew->dbm, zSql, pzErrmsg); |
| 14744 | } |
| 14745 | } |
| 14746 | idxFinalize(&rc, pSql); |
| 14747 | } |
| 14748 | |
| 14749 | /* Create the vtab schema */ |
| @@ -16211,10 +16153,1197 @@ | |
| 16153 | } |
| 16154 | return rc; |
| 16155 | } |
| 16156 | |
| 16157 | /************************* End ../ext/misc/stmtrand.c ********************/ |
| 16158 | /************************* Begin ../ext/misc/vfstrace.c ******************/ |
| 16159 | /* |
| 16160 | ** 2011 March 16 |
| 16161 | ** |
| 16162 | ** The author disclaims copyright to this source code. In place of |
| 16163 | ** a legal notice, here is a blessing: |
| 16164 | ** |
| 16165 | ** May you do good and not evil. |
| 16166 | ** May you find forgiveness for yourself and forgive others. |
| 16167 | ** May you share freely, never taking more than you give. |
| 16168 | ** |
| 16169 | ****************************************************************************** |
| 16170 | ** |
| 16171 | ** This file contains code implements a VFS shim that writes diagnostic |
| 16172 | ** output for each VFS call, similar to "strace". |
| 16173 | ** |
| 16174 | ** USAGE: |
| 16175 | ** |
| 16176 | ** This source file exports a single symbol which is the name of a |
| 16177 | ** function: |
| 16178 | ** |
| 16179 | ** int vfstrace_register( |
| 16180 | ** const char *zTraceName, // Name of the newly constructed VFS |
| 16181 | ** const char *zOldVfsName, // Name of the underlying VFS |
| 16182 | ** int (*xOut)(const char*,void*), // Output routine. ex: fputs |
| 16183 | ** void *pOutArg, // 2nd argument to xOut. ex: stderr |
| 16184 | ** int makeDefault // Make the new VFS the default |
| 16185 | ** ); |
| 16186 | ** |
| 16187 | ** Applications that want to trace their VFS usage must provide a callback |
| 16188 | ** function with this prototype: |
| 16189 | ** |
| 16190 | ** int traceOutput(const char *zMessage, void *pAppData); |
| 16191 | ** |
| 16192 | ** This function will "output" the trace messages, where "output" can |
| 16193 | ** mean different things to different applications. The traceOutput function |
| 16194 | ** for the command-line shell (see shell.c) is "fputs" from the standard |
| 16195 | ** library, which means that all trace output is written on the stream |
| 16196 | ** specified by the second argument. In the case of the command-line shell |
| 16197 | ** the second argument is stderr. Other applications might choose to output |
| 16198 | ** trace information to a file, over a socket, or write it into a buffer. |
| 16199 | ** |
| 16200 | ** The vfstrace_register() function creates a new "shim" VFS named by |
| 16201 | ** the zTraceName parameter. A "shim" VFS is an SQLite backend that does |
| 16202 | ** not really perform the duties of a true backend, but simply filters or |
| 16203 | ** interprets VFS calls before passing them off to another VFS which does |
| 16204 | ** the actual work. In this case the other VFS - the one that does the |
| 16205 | ** real work - is identified by the second parameter, zOldVfsName. If |
| 16206 | ** the 2nd parameter is NULL then the default VFS is used. The common |
| 16207 | ** case is for the 2nd parameter to be NULL. |
| 16208 | ** |
| 16209 | ** The third and fourth parameters are the pointer to the output function |
| 16210 | ** and the second argument to the output function. For the SQLite |
| 16211 | ** command-line shell, when the -vfstrace option is used, these parameters |
| 16212 | ** are fputs and stderr, respectively. |
| 16213 | ** |
| 16214 | ** The fifth argument is true (non-zero) to cause the newly created VFS |
| 16215 | ** to become the default VFS. The common case is for the fifth parameter |
| 16216 | ** to be true. |
| 16217 | ** |
| 16218 | ** The call to vfstrace_register() simply creates the shim VFS that does |
| 16219 | ** tracing. The application must also arrange to use the new VFS for |
| 16220 | ** all database connections that are created and for which tracing is |
| 16221 | ** desired. This can be done by specifying the trace VFS using URI filename |
| 16222 | ** notation, or by specifying the trace VFS as the 4th parameter to |
| 16223 | ** sqlite3_open_v2() or by making the trace VFS be the default (by setting |
| 16224 | ** the 5th parameter of vfstrace_register() to 1). |
| 16225 | ** |
| 16226 | ** |
| 16227 | ** ENABLING VFSTRACE IN A COMMAND-LINE SHELL |
| 16228 | ** |
| 16229 | ** The SQLite command line shell implemented by the shell.c source file |
| 16230 | ** can be used with this module. To compile in -vfstrace support, first |
| 16231 | ** gather this file (test_vfstrace.c), the shell source file (shell.c), |
| 16232 | ** and the SQLite amalgamation source files (sqlite3.c, sqlite3.h) into |
| 16233 | ** the working directory. Then compile using a command like the following: |
| 16234 | ** |
| 16235 | ** gcc -o sqlite3 -Os -I. -DSQLITE_ENABLE_VFSTRACE \ |
| 16236 | ** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE \ |
| 16237 | ** -DHAVE_READLINE -DHAVE_USLEEP=1 \ |
| 16238 | ** shell.c test_vfstrace.c sqlite3.c -ldl -lreadline -lncurses |
| 16239 | ** |
| 16240 | ** The gcc command above works on Linux and provides (in addition to the |
| 16241 | ** -vfstrace option) support for FTS3 and FTS4, RTREE, and command-line |
| 16242 | ** editing using the readline library. The command-line shell does not |
| 16243 | ** use threads so we added -DSQLITE_THREADSAFE=0 just to make the code |
| 16244 | ** run a little faster. For compiling on a Mac, you'll probably need |
| 16245 | ** to omit the -DHAVE_READLINE, the -lreadline, and the -lncurses options. |
| 16246 | ** The compilation could be simplified to just this: |
| 16247 | ** |
| 16248 | ** gcc -DSQLITE_ENABLE_VFSTRACE \ |
| 16249 | ** shell.c test_vfstrace.c sqlite3.c -ldl -lpthread |
| 16250 | ** |
| 16251 | ** In this second example, all unnecessary options have been removed |
| 16252 | ** Note that since the code is now threadsafe, we had to add the -lpthread |
| 16253 | ** option to pull in the pthreads library. |
| 16254 | ** |
| 16255 | ** To cross-compile for windows using MinGW, a command like this might |
| 16256 | ** work: |
| 16257 | ** |
| 16258 | ** /opt/mingw/bin/i386-mingw32msvc-gcc -o sqlite3.exe -Os -I \ |
| 16259 | ** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_VFSTRACE \ |
| 16260 | ** shell.c test_vfstrace.c sqlite3.c |
| 16261 | ** |
| 16262 | ** Similar compiler commands will work on different systems. The key |
| 16263 | ** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that |
| 16264 | ** the shell.c source file will know to include the -vfstrace command-line |
| 16265 | ** option and (2) you must compile and link the three source files |
| 16266 | ** shell,c, test_vfstrace.c, and sqlite3.c. |
| 16267 | ** |
| 16268 | ** RUNTIME CONTROL OF VFSTRACE OUTPUT |
| 16269 | ** |
| 16270 | ** The application can use the "vfstrace" pragma to control which VFS |
| 16271 | ** APIs are traced. To disable all output: |
| 16272 | ** |
| 16273 | ** PRAGMA vfstrace('-all'); |
| 16274 | ** |
| 16275 | ** To enable all output (which is the default setting): |
| 16276 | ** |
| 16277 | ** PRAGMA vfstrace('+all'); |
| 16278 | ** |
| 16279 | ** Individual APIs can be enabled or disabled by name, with or without |
| 16280 | ** the initial "x" character. For example, to set up for tracing lock |
| 16281 | ** primatives only: |
| 16282 | ** |
| 16283 | ** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock'); |
| 16284 | ** |
| 16285 | ** The argument to the vfstrace pragma ignores capitalization and any |
| 16286 | ** characters other than alphabetics, '+', and '-'. |
| 16287 | */ |
| 16288 | #include <stdlib.h> |
| 16289 | #include <string.h> |
| 16290 | /* #include "sqlite3.h" */ |
| 16291 | |
| 16292 | /* |
| 16293 | ** An instance of this structure is attached to the each trace VFS to |
| 16294 | ** provide auxiliary information. |
| 16295 | */ |
| 16296 | typedef struct vfstrace_info vfstrace_info; |
| 16297 | struct vfstrace_info { |
| 16298 | sqlite3_vfs *pRootVfs; /* The underlying real VFS */ |
| 16299 | int (*xOut)(const char*, void*); /* Send output here */ |
| 16300 | unsigned int mTrace; /* Mask of interfaces to trace */ |
| 16301 | u8 bOn; /* Tracing on/off */ |
| 16302 | void *pOutArg; /* First argument to xOut */ |
| 16303 | const char *zVfsName; /* Name of this trace-VFS */ |
| 16304 | sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */ |
| 16305 | }; |
| 16306 | |
| 16307 | /* |
| 16308 | ** The sqlite3_file object for the trace VFS |
| 16309 | */ |
| 16310 | typedef struct vfstrace_file vfstrace_file; |
| 16311 | struct vfstrace_file { |
| 16312 | sqlite3_file base; /* Base class. Must be first */ |
| 16313 | vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */ |
| 16314 | const char *zFName; /* Base name of the file */ |
| 16315 | sqlite3_file *pReal; /* The real underlying file */ |
| 16316 | }; |
| 16317 | |
| 16318 | /* |
| 16319 | ** Bit values for vfstrace_info.mTrace. |
| 16320 | */ |
| 16321 | #define VTR_CLOSE 0x00000001 |
| 16322 | #define VTR_READ 0x00000002 |
| 16323 | #define VTR_WRITE 0x00000004 |
| 16324 | #define VTR_TRUNC 0x00000008 |
| 16325 | #define VTR_SYNC 0x00000010 |
| 16326 | #define VTR_FSIZE 0x00000020 |
| 16327 | #define VTR_LOCK 0x00000040 |
| 16328 | #define VTR_UNLOCK 0x00000080 |
| 16329 | #define VTR_CRL 0x00000100 |
| 16330 | #define VTR_FCTRL 0x00000200 |
| 16331 | #define VTR_SECSZ 0x00000400 |
| 16332 | #define VTR_DEVCHAR 0x00000800 |
| 16333 | #define VTR_SHMLOCK 0x00001000 |
| 16334 | #define VTR_SHMMAP 0x00002000 |
| 16335 | #define VTR_SHMBAR 0x00004000 |
| 16336 | #define VTR_SHMUNMAP 0x00008000 |
| 16337 | #define VTR_OPEN 0x00010000 |
| 16338 | #define VTR_DELETE 0x00020000 |
| 16339 | #define VTR_ACCESS 0x00040000 |
| 16340 | #define VTR_FULLPATH 0x00080000 |
| 16341 | #define VTR_DLOPEN 0x00100000 |
| 16342 | #define VTR_DLERR 0x00200000 |
| 16343 | #define VTR_DLSYM 0x00400000 |
| 16344 | #define VTR_DLCLOSE 0x00800000 |
| 16345 | #define VTR_RAND 0x01000000 |
| 16346 | #define VTR_SLEEP 0x02000000 |
| 16347 | #define VTR_CURTIME 0x04000000 |
| 16348 | #define VTR_LASTERR 0x08000000 |
| 16349 | |
| 16350 | /* |
| 16351 | ** Method declarations for vfstrace_file. |
| 16352 | */ |
| 16353 | static int vfstraceClose(sqlite3_file*); |
| 16354 | static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); |
| 16355 | static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); |
| 16356 | static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size); |
| 16357 | static int vfstraceSync(sqlite3_file*, int flags); |
| 16358 | static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize); |
| 16359 | static int vfstraceLock(sqlite3_file*, int); |
| 16360 | static int vfstraceUnlock(sqlite3_file*, int); |
| 16361 | static int vfstraceCheckReservedLock(sqlite3_file*, int *); |
| 16362 | static int vfstraceFileControl(sqlite3_file*, int op, void *pArg); |
| 16363 | static int vfstraceSectorSize(sqlite3_file*); |
| 16364 | static int vfstraceDeviceCharacteristics(sqlite3_file*); |
| 16365 | static int vfstraceShmLock(sqlite3_file*,int,int,int); |
| 16366 | static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **); |
| 16367 | static void vfstraceShmBarrier(sqlite3_file*); |
| 16368 | static int vfstraceShmUnmap(sqlite3_file*,int); |
| 16369 | |
| 16370 | /* |
| 16371 | ** Method declarations for vfstrace_vfs. |
| 16372 | */ |
| 16373 | static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); |
| 16374 | static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir); |
| 16375 | static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *); |
| 16376 | static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *); |
| 16377 | static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename); |
| 16378 | static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg); |
| 16379 | static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); |
| 16380 | static void vfstraceDlClose(sqlite3_vfs*, void*); |
| 16381 | static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut); |
| 16382 | static int vfstraceSleep(sqlite3_vfs*, int microseconds); |
| 16383 | static int vfstraceCurrentTime(sqlite3_vfs*, double*); |
| 16384 | static int vfstraceGetLastError(sqlite3_vfs*, int, char*); |
| 16385 | static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); |
| 16386 | static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr); |
| 16387 | static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *); |
| 16388 | static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName); |
| 16389 | |
| 16390 | /* |
| 16391 | ** Return a pointer to the tail of the pathname. Examples: |
| 16392 | ** |
| 16393 | ** /home/drh/xyzzy.txt -> xyzzy.txt |
| 16394 | ** xyzzy.txt -> xyzzy.txt |
| 16395 | */ |
| 16396 | static const char *fileTail(const char *z){ |
| 16397 | size_t i; |
| 16398 | if( z==0 ) return 0; |
| 16399 | i = strlen(z)-1; |
| 16400 | while( i>0 && z[i-1]!='/' ){ i--; } |
| 16401 | return &z[i]; |
| 16402 | } |
| 16403 | |
| 16404 | /* |
| 16405 | ** Send trace output defined by zFormat and subsequent arguments. |
| 16406 | */ |
| 16407 | static void vfstrace_printf( |
| 16408 | vfstrace_info *pInfo, |
| 16409 | const char *zFormat, |
| 16410 | ... |
| 16411 | ){ |
| 16412 | va_list ap; |
| 16413 | char *zMsg; |
| 16414 | if( pInfo->bOn ){ |
| 16415 | va_start(ap, zFormat); |
| 16416 | zMsg = sqlite3_vmprintf(zFormat, ap); |
| 16417 | va_end(ap); |
| 16418 | pInfo->xOut(zMsg, pInfo->pOutArg); |
| 16419 | sqlite3_free(zMsg); |
| 16420 | } |
| 16421 | } |
| 16422 | |
| 16423 | /* |
| 16424 | ** Try to convert an error code into a symbolic name for that error code. |
| 16425 | */ |
| 16426 | static const char *vfstrace_errcode_name(int rc ){ |
| 16427 | const char *zVal = 0; |
| 16428 | switch( rc ){ |
| 16429 | case SQLITE_OK: zVal = "SQLITE_OK"; break; |
| 16430 | case SQLITE_INTERNAL: zVal = "SQLITE_INTERNAL"; break; |
| 16431 | case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break; |
| 16432 | case SQLITE_PERM: zVal = "SQLITE_PERM"; break; |
| 16433 | case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break; |
| 16434 | case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break; |
| 16435 | case SQLITE_LOCKED: zVal = "SQLITE_LOCKED"; break; |
| 16436 | case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break; |
| 16437 | case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break; |
| 16438 | case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break; |
| 16439 | case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break; |
| 16440 | case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break; |
| 16441 | case SQLITE_NOTFOUND: zVal = "SQLITE_NOTFOUND"; break; |
| 16442 | case SQLITE_FULL: zVal = "SQLITE_FULL"; break; |
| 16443 | case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break; |
| 16444 | case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break; |
| 16445 | case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break; |
| 16446 | case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break; |
| 16447 | case SQLITE_TOOBIG: zVal = "SQLITE_TOOBIG"; break; |
| 16448 | case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break; |
| 16449 | case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break; |
| 16450 | case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break; |
| 16451 | case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break; |
| 16452 | case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break; |
| 16453 | case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break; |
| 16454 | case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break; |
| 16455 | case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break; |
| 16456 | case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break; |
| 16457 | case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break; |
| 16458 | case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break; |
| 16459 | case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break; |
| 16460 | case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break; |
| 16461 | case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break; |
| 16462 | case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break; |
| 16463 | case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break; |
| 16464 | case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break; |
| 16465 | case SQLITE_IOERR_CHECKRESERVEDLOCK: |
| 16466 | zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; |
| 16467 | case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break; |
| 16468 | case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break; |
| 16469 | case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break; |
| 16470 | case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break; |
| 16471 | case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break; |
| 16472 | case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break; |
| 16473 | case SQLITE_IOERR_SHMMAP: zVal = "SQLITE_IOERR_SHMMAP"; break; |
| 16474 | case SQLITE_IOERR_SEEK: zVal = "SQLITE_IOERR_SEEK"; break; |
| 16475 | case SQLITE_IOERR_GETTEMPPATH: zVal = "SQLITE_IOERR_GETTEMPPATH"; break; |
| 16476 | case SQLITE_IOERR_CONVPATH: zVal = "SQLITE_IOERR_CONVPATH"; break; |
| 16477 | case SQLITE_READONLY_DBMOVED: zVal = "SQLITE_READONLY_DBMOVED"; break; |
| 16478 | case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break; |
| 16479 | case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break; |
| 16480 | case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break; |
| 16481 | } |
| 16482 | return zVal; |
| 16483 | } |
| 16484 | |
| 16485 | /* |
| 16486 | ** Convert value rc into a string and print it using zFormat. zFormat |
| 16487 | ** should have exactly one %s |
| 16488 | */ |
| 16489 | static void vfstrace_print_errcode( |
| 16490 | vfstrace_info *pInfo, |
| 16491 | const char *zFormat, |
| 16492 | int rc |
| 16493 | ){ |
| 16494 | const char *zVal; |
| 16495 | char zBuf[50]; |
| 16496 | zVal = vfstrace_errcode_name(rc); |
| 16497 | if( zVal==0 ){ |
| 16498 | zVal = vfstrace_errcode_name(rc&0xff); |
| 16499 | if( zVal ){ |
| 16500 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%s | 0x%x", zVal, rc&0xffff00); |
| 16501 | }else{ |
| 16502 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%d (0x%x)", rc, rc); |
| 16503 | } |
| 16504 | zVal = zBuf; |
| 16505 | } |
| 16506 | vfstrace_printf(pInfo, zFormat, zVal); |
| 16507 | } |
| 16508 | |
| 16509 | /* |
| 16510 | ** Append to a buffer. |
| 16511 | */ |
| 16512 | static void strappend(char *z, int *pI, const char *zAppend){ |
| 16513 | int i = *pI; |
| 16514 | while( zAppend[0] ){ z[i++] = *(zAppend++); } |
| 16515 | z[i] = 0; |
| 16516 | *pI = i; |
| 16517 | } |
| 16518 | |
| 16519 | /* |
| 16520 | ** Turn tracing output on or off according to mMask. |
| 16521 | */ |
| 16522 | static void vfstraceOnOff(vfstrace_info *pInfo, unsigned int mMask){ |
| 16523 | pInfo->bOn = (pInfo->mTrace & mMask)!=0; |
| 16524 | } |
| 16525 | |
| 16526 | /* |
| 16527 | ** Close an vfstrace-file. |
| 16528 | */ |
| 16529 | static int vfstraceClose(sqlite3_file *pFile){ |
| 16530 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16531 | vfstrace_info *pInfo = p->pInfo; |
| 16532 | int rc; |
| 16533 | vfstraceOnOff(pInfo, VTR_CLOSE); |
| 16534 | vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName); |
| 16535 | rc = p->pReal->pMethods->xClose(p->pReal); |
| 16536 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16537 | if( rc==SQLITE_OK ){ |
| 16538 | sqlite3_free((void*)p->base.pMethods); |
| 16539 | p->base.pMethods = 0; |
| 16540 | } |
| 16541 | return rc; |
| 16542 | } |
| 16543 | |
| 16544 | /* |
| 16545 | ** Read data from an vfstrace-file. |
| 16546 | */ |
| 16547 | static int vfstraceRead( |
| 16548 | sqlite3_file *pFile, |
| 16549 | void *zBuf, |
| 16550 | int iAmt, |
| 16551 | sqlite_int64 iOfst |
| 16552 | ){ |
| 16553 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16554 | vfstrace_info *pInfo = p->pInfo; |
| 16555 | int rc; |
| 16556 | vfstraceOnOff(pInfo, VTR_READ); |
| 16557 | vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)", |
| 16558 | pInfo->zVfsName, p->zFName, iAmt, iOfst); |
| 16559 | rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); |
| 16560 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16561 | return rc; |
| 16562 | } |
| 16563 | |
| 16564 | /* |
| 16565 | ** Write data to an vfstrace-file. |
| 16566 | */ |
| 16567 | static int vfstraceWrite( |
| 16568 | sqlite3_file *pFile, |
| 16569 | const void *zBuf, |
| 16570 | int iAmt, |
| 16571 | sqlite_int64 iOfst |
| 16572 | ){ |
| 16573 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16574 | vfstrace_info *pInfo = p->pInfo; |
| 16575 | int rc; |
| 16576 | vfstraceOnOff(pInfo, VTR_WRITE); |
| 16577 | vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)", |
| 16578 | pInfo->zVfsName, p->zFName, iAmt, iOfst); |
| 16579 | rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); |
| 16580 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16581 | return rc; |
| 16582 | } |
| 16583 | |
| 16584 | /* |
| 16585 | ** Truncate an vfstrace-file. |
| 16586 | */ |
| 16587 | static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
| 16588 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16589 | vfstrace_info *pInfo = p->pInfo; |
| 16590 | int rc; |
| 16591 | vfstraceOnOff(pInfo, VTR_TRUNC); |
| 16592 | vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName, |
| 16593 | size); |
| 16594 | rc = p->pReal->pMethods->xTruncate(p->pReal, size); |
| 16595 | vfstrace_printf(pInfo, " -> %d\n", rc); |
| 16596 | return rc; |
| 16597 | } |
| 16598 | |
| 16599 | /* |
| 16600 | ** Sync an vfstrace-file. |
| 16601 | */ |
| 16602 | static int vfstraceSync(sqlite3_file *pFile, int flags){ |
| 16603 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16604 | vfstrace_info *pInfo = p->pInfo; |
| 16605 | int rc; |
| 16606 | int i; |
| 16607 | char zBuf[100]; |
| 16608 | memcpy(zBuf, "|0", 3); |
| 16609 | i = 0; |
| 16610 | if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL"); |
| 16611 | else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL"); |
| 16612 | if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY"); |
| 16613 | if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){ |
| 16614 | sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags); |
| 16615 | } |
| 16616 | vfstraceOnOff(pInfo, VTR_SYNC); |
| 16617 | vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName, |
| 16618 | &zBuf[1]); |
| 16619 | rc = p->pReal->pMethods->xSync(p->pReal, flags); |
| 16620 | vfstrace_printf(pInfo, " -> %d\n", rc); |
| 16621 | return rc; |
| 16622 | } |
| 16623 | |
| 16624 | /* |
| 16625 | ** Return the current file-size of an vfstrace-file. |
| 16626 | */ |
| 16627 | static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ |
| 16628 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16629 | vfstrace_info *pInfo = p->pInfo; |
| 16630 | int rc; |
| 16631 | vfstraceOnOff(pInfo, VTR_FSIZE); |
| 16632 | vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName); |
| 16633 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 16634 | vfstrace_print_errcode(pInfo, " -> %s,", rc); |
| 16635 | vfstrace_printf(pInfo, " size=%lld\n", *pSize); |
| 16636 | return rc; |
| 16637 | } |
| 16638 | |
| 16639 | /* |
| 16640 | ** Return the name of a lock. |
| 16641 | */ |
| 16642 | static const char *lockName(int eLock){ |
| 16643 | const char *azLockNames[] = { |
| 16644 | "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE" |
| 16645 | }; |
| 16646 | if( eLock<0 || eLock>=(int)(sizeof(azLockNames)/sizeof(azLockNames[0])) ){ |
| 16647 | return "???"; |
| 16648 | }else{ |
| 16649 | return azLockNames[eLock]; |
| 16650 | } |
| 16651 | } |
| 16652 | |
| 16653 | /* |
| 16654 | ** Lock an vfstrace-file. |
| 16655 | */ |
| 16656 | static int vfstraceLock(sqlite3_file *pFile, int eLock){ |
| 16657 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16658 | vfstrace_info *pInfo = p->pInfo; |
| 16659 | int rc; |
| 16660 | vfstraceOnOff(pInfo, VTR_LOCK); |
| 16661 | vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName, |
| 16662 | lockName(eLock)); |
| 16663 | rc = p->pReal->pMethods->xLock(p->pReal, eLock); |
| 16664 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16665 | return rc; |
| 16666 | } |
| 16667 | |
| 16668 | /* |
| 16669 | ** Unlock an vfstrace-file. |
| 16670 | */ |
| 16671 | static int vfstraceUnlock(sqlite3_file *pFile, int eLock){ |
| 16672 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16673 | vfstrace_info *pInfo = p->pInfo; |
| 16674 | int rc; |
| 16675 | vfstraceOnOff(pInfo, VTR_UNLOCK); |
| 16676 | vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName, |
| 16677 | lockName(eLock)); |
| 16678 | rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); |
| 16679 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16680 | return rc; |
| 16681 | } |
| 16682 | |
| 16683 | /* |
| 16684 | ** Check if another file-handle holds a RESERVED lock on an vfstrace-file. |
| 16685 | */ |
| 16686 | static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){ |
| 16687 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16688 | vfstrace_info *pInfo = p->pInfo; |
| 16689 | int rc; |
| 16690 | vfstraceOnOff(pInfo, VTR_CRL); |
| 16691 | vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)", |
| 16692 | pInfo->zVfsName, p->zFName); |
| 16693 | rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); |
| 16694 | vfstrace_print_errcode(pInfo, " -> %s", rc); |
| 16695 | vfstrace_printf(pInfo, ", out=%d\n", *pResOut); |
| 16696 | return rc; |
| 16697 | } |
| 16698 | |
| 16699 | /* |
| 16700 | ** File control method. For custom operations on an vfstrace-file. |
| 16701 | */ |
| 16702 | static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){ |
| 16703 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16704 | vfstrace_info *pInfo = p->pInfo; |
| 16705 | int rc; |
| 16706 | char zBuf[100]; |
| 16707 | char zBuf2[100]; |
| 16708 | char *zOp; |
| 16709 | char *zRVal = 0; |
| 16710 | vfstraceOnOff(pInfo, VTR_FCTRL); |
| 16711 | switch( op ){ |
| 16712 | case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break; |
| 16713 | case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break; |
| 16714 | case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break; |
| 16715 | case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break; |
| 16716 | case SQLITE_FCNTL_SIZE_HINT: { |
| 16717 | sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld", |
| 16718 | *(sqlite3_int64*)pArg); |
| 16719 | zOp = zBuf; |
| 16720 | break; |
| 16721 | } |
| 16722 | case SQLITE_FCNTL_CHUNK_SIZE: { |
| 16723 | sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg); |
| 16724 | zOp = zBuf; |
| 16725 | break; |
| 16726 | } |
| 16727 | case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break; |
| 16728 | case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break; |
| 16729 | case SQLITE_FCNTL_PERSIST_WAL: { |
| 16730 | sqlite3_snprintf(sizeof(zBuf), zBuf, "PERSIST_WAL,%d", *(int*)pArg); |
| 16731 | zOp = zBuf; |
| 16732 | break; |
| 16733 | } |
| 16734 | case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break; |
| 16735 | case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break; |
| 16736 | case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break; |
| 16737 | case SQLITE_FCNTL_PRAGMA: { |
| 16738 | const char *const* a = (const char*const*)pArg; |
| 16739 | if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){ |
| 16740 | const u8 *zArg = (const u8*)a[2]; |
| 16741 | if( zArg[0]>='0' && zArg[0]<=9 ){ |
| 16742 | pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0); |
| 16743 | }else{ |
| 16744 | static const struct { |
| 16745 | const char *z; |
| 16746 | unsigned int m; |
| 16747 | } aKw[] = { |
| 16748 | { "all", 0xffffffff }, |
| 16749 | { "close", VTR_CLOSE }, |
| 16750 | { "read", VTR_READ }, |
| 16751 | { "write", VTR_WRITE }, |
| 16752 | { "truncate", VTR_TRUNC }, |
| 16753 | { "sync", VTR_SYNC }, |
| 16754 | { "filesize", VTR_FSIZE }, |
| 16755 | { "lock", VTR_LOCK }, |
| 16756 | { "unlock", VTR_UNLOCK }, |
| 16757 | { "checkreservedlock", VTR_CRL }, |
| 16758 | { "filecontrol", VTR_FCTRL }, |
| 16759 | { "sectorsize", VTR_SECSZ }, |
| 16760 | { "devicecharacteristics", VTR_DEVCHAR }, |
| 16761 | { "shmlock", VTR_SHMLOCK }, |
| 16762 | { "shmmap", VTR_SHMMAP }, |
| 16763 | { "shmummap", VTR_SHMUNMAP }, |
| 16764 | { "shmbarrier", VTR_SHMBAR }, |
| 16765 | { "open", VTR_OPEN }, |
| 16766 | { "delete", VTR_DELETE }, |
| 16767 | { "access", VTR_ACCESS }, |
| 16768 | { "fullpathname", VTR_FULLPATH }, |
| 16769 | { "dlopen", VTR_DLOPEN }, |
| 16770 | { "dlerror", VTR_DLERR }, |
| 16771 | { "dlsym", VTR_DLSYM }, |
| 16772 | { "dlclose", VTR_DLCLOSE }, |
| 16773 | { "randomness", VTR_RAND }, |
| 16774 | { "sleep", VTR_SLEEP }, |
| 16775 | { "currenttime", VTR_CURTIME }, |
| 16776 | { "currenttimeint64", VTR_CURTIME }, |
| 16777 | { "getlasterror", VTR_LASTERR }, |
| 16778 | }; |
| 16779 | int onOff = 1; |
| 16780 | while( zArg[0] ){ |
| 16781 | int jj, n; |
| 16782 | while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+' |
| 16783 | && !isalpha(zArg[0]) ) zArg++; |
| 16784 | if( zArg[0]==0 ) break; |
| 16785 | if( zArg[0]=='-' ){ |
| 16786 | onOff = 0; |
| 16787 | zArg++; |
| 16788 | }else if( zArg[0]=='+' ){ |
| 16789 | onOff = 1; |
| 16790 | zArg++; |
| 16791 | } |
| 16792 | while( !isalpha(zArg[0]) ){ |
| 16793 | if( zArg[0]==0 ) break; |
| 16794 | zArg++; |
| 16795 | } |
| 16796 | if( zArg[0]=='x' && isalpha(zArg[1]) ) zArg++; |
| 16797 | for(n=0; isalpha(zArg[n]); n++){} |
| 16798 | for(jj=0; jj<(int)(sizeof(aKw)/sizeof(aKw[0])); jj++){ |
| 16799 | if( sqlite3_strnicmp(aKw[jj].z,(const char*)zArg,n)==0 ){ |
| 16800 | if( onOff ){ |
| 16801 | pInfo->mTrace |= aKw[jj].m; |
| 16802 | }else{ |
| 16803 | pInfo->mTrace &= ~aKw[jj].m; |
| 16804 | } |
| 16805 | break; |
| 16806 | } |
| 16807 | } |
| 16808 | zArg += n; |
| 16809 | } |
| 16810 | } |
| 16811 | } |
| 16812 | sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]); |
| 16813 | zOp = zBuf; |
| 16814 | break; |
| 16815 | } |
| 16816 | case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break; |
| 16817 | case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break; |
| 16818 | case SQLITE_FCNTL_MMAP_SIZE: { |
| 16819 | sqlite3_int64 iMMap = *(sqlite3_int64*)pArg; |
| 16820 | sqlite3_snprintf(sizeof(zBuf), zBuf, "MMAP_SIZE,%lld",iMMap); |
| 16821 | zOp = zBuf; |
| 16822 | break; |
| 16823 | } |
| 16824 | case SQLITE_FCNTL_TRACE: zOp = "TRACE"; break; |
| 16825 | case SQLITE_FCNTL_HAS_MOVED: zOp = "HAS_MOVED"; break; |
| 16826 | case SQLITE_FCNTL_SYNC: zOp = "SYNC"; break; |
| 16827 | case SQLITE_FCNTL_COMMIT_PHASETWO: zOp = "COMMIT_PHASETWO"; break; |
| 16828 | case SQLITE_FCNTL_WIN32_SET_HANDLE: zOp = "WIN32_SET_HANDLE"; break; |
| 16829 | case SQLITE_FCNTL_WAL_BLOCK: zOp = "WAL_BLOCK"; break; |
| 16830 | case SQLITE_FCNTL_ZIPVFS: zOp = "ZIPVFS"; break; |
| 16831 | case SQLITE_FCNTL_RBU: zOp = "RBU"; break; |
| 16832 | case SQLITE_FCNTL_VFS_POINTER: zOp = "VFS_POINTER"; break; |
| 16833 | case SQLITE_FCNTL_JOURNAL_POINTER: zOp = "JOURNAL_POINTER"; break; |
| 16834 | case SQLITE_FCNTL_WIN32_GET_HANDLE: zOp = "WIN32_GET_HANDLE"; break; |
| 16835 | case SQLITE_FCNTL_PDB: zOp = "PDB"; break; |
| 16836 | case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: zOp = "BEGIN_ATOMIC_WRITE"; break; |
| 16837 | case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: zOp = "COMMIT_ATOMIC_WRITE"; break; |
| 16838 | case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { |
| 16839 | zOp = "ROLLBACK_ATOMIC_WRITE"; |
| 16840 | break; |
| 16841 | } |
| 16842 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 16843 | sqlite3_snprintf(sizeof(zBuf), zBuf, "LOCK_TIMEOUT,%d", *(int*)pArg); |
| 16844 | zOp = zBuf; |
| 16845 | break; |
| 16846 | } |
| 16847 | case SQLITE_FCNTL_DATA_VERSION: zOp = "DATA_VERSION"; break; |
| 16848 | case SQLITE_FCNTL_SIZE_LIMIT: zOp = "SIZE_LIMIT"; break; |
| 16849 | case SQLITE_FCNTL_CKPT_DONE: zOp = "CKPT_DONE"; break; |
| 16850 | case SQLITE_FCNTL_RESERVE_BYTES: zOp = "RESERVED_BYTES"; break; |
| 16851 | case SQLITE_FCNTL_CKPT_START: zOp = "CKPT_START"; break; |
| 16852 | case SQLITE_FCNTL_EXTERNAL_READER: zOp = "EXTERNAL_READER"; break; |
| 16853 | case SQLITE_FCNTL_CKSM_FILE: zOp = "CKSM_FILE"; break; |
| 16854 | case SQLITE_FCNTL_RESET_CACHE: zOp = "RESET_CACHE"; break; |
| 16855 | case 0xca093fa0: zOp = "DB_UNCHANGED"; break; |
| 16856 | default: { |
| 16857 | sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op); |
| 16858 | zOp = zBuf; |
| 16859 | break; |
| 16860 | } |
| 16861 | } |
| 16862 | vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)", |
| 16863 | pInfo->zVfsName, p->zFName, zOp); |
| 16864 | rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); |
| 16865 | if( rc==SQLITE_OK ){ |
| 16866 | switch( op ){ |
| 16867 | case SQLITE_FCNTL_VFSNAME: { |
| 16868 | *(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z", |
| 16869 | pInfo->zVfsName, *(char**)pArg); |
| 16870 | zRVal = *(char**)pArg; |
| 16871 | break; |
| 16872 | } |
| 16873 | case SQLITE_FCNTL_MMAP_SIZE: { |
| 16874 | sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%lld", *(sqlite3_int64*)pArg); |
| 16875 | zRVal = zBuf2; |
| 16876 | break; |
| 16877 | } |
| 16878 | case SQLITE_FCNTL_HAS_MOVED: |
| 16879 | case SQLITE_FCNTL_PERSIST_WAL: { |
| 16880 | sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%d", *(int*)pArg); |
| 16881 | zRVal = zBuf2; |
| 16882 | break; |
| 16883 | } |
| 16884 | case SQLITE_FCNTL_PRAGMA: |
| 16885 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 16886 | zRVal = *(char**)pArg; |
| 16887 | break; |
| 16888 | } |
| 16889 | } |
| 16890 | } |
| 16891 | if( zRVal ){ |
| 16892 | vfstrace_print_errcode(pInfo, " -> %s", rc); |
| 16893 | vfstrace_printf(pInfo, ", %s\n", zRVal); |
| 16894 | }else{ |
| 16895 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16896 | } |
| 16897 | return rc; |
| 16898 | } |
| 16899 | |
| 16900 | /* |
| 16901 | ** Return the sector-size in bytes for an vfstrace-file. |
| 16902 | */ |
| 16903 | static int vfstraceSectorSize(sqlite3_file *pFile){ |
| 16904 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16905 | vfstrace_info *pInfo = p->pInfo; |
| 16906 | int rc; |
| 16907 | vfstraceOnOff(pInfo, VTR_SECSZ); |
| 16908 | vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName); |
| 16909 | rc = p->pReal->pMethods->xSectorSize(p->pReal); |
| 16910 | vfstrace_printf(pInfo, " -> %d\n", rc); |
| 16911 | return rc; |
| 16912 | } |
| 16913 | |
| 16914 | /* |
| 16915 | ** Return the device characteristic flags supported by an vfstrace-file. |
| 16916 | */ |
| 16917 | static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){ |
| 16918 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16919 | vfstrace_info *pInfo = p->pInfo; |
| 16920 | int rc; |
| 16921 | vfstraceOnOff(pInfo, VTR_DEVCHAR); |
| 16922 | vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)", |
| 16923 | pInfo->zVfsName, p->zFName); |
| 16924 | rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); |
| 16925 | vfstrace_printf(pInfo, " -> 0x%08x\n", rc); |
| 16926 | return rc; |
| 16927 | } |
| 16928 | |
| 16929 | /* |
| 16930 | ** Shared-memory operations. |
| 16931 | */ |
| 16932 | static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ |
| 16933 | static const char *azLockName[] = { |
| 16934 | "WRITE", |
| 16935 | "CKPT", |
| 16936 | "RECOVER", |
| 16937 | "READ0", |
| 16938 | "READ1", |
| 16939 | "READ2", |
| 16940 | "READ3", |
| 16941 | "READ4", |
| 16942 | }; |
| 16943 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16944 | vfstrace_info *pInfo = p->pInfo; |
| 16945 | int rc; |
| 16946 | char zLck[100]; |
| 16947 | int i = 0; |
| 16948 | vfstraceOnOff(pInfo, VTR_SHMLOCK); |
| 16949 | memcpy(zLck, "|0", 3); |
| 16950 | if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK"); |
| 16951 | if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK"); |
| 16952 | if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED"); |
| 16953 | if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE"); |
| 16954 | if( flags & ~(0xf) ){ |
| 16955 | sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags); |
| 16956 | } |
| 16957 | if( ofst>=0 && ofst<(int)(sizeof(azLockName)/sizeof(azLockName[0])) ){ |
| 16958 | vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)", |
| 16959 | pInfo->zVfsName, p->zFName, ofst, azLockName[ofst], |
| 16960 | n, &zLck[1]); |
| 16961 | }else{ |
| 16962 | vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)", |
| 16963 | pInfo->zVfsName, p->zFName, ofst, |
| 16964 | n, &zLck[1]); |
| 16965 | } |
| 16966 | rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); |
| 16967 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16968 | return rc; |
| 16969 | } |
| 16970 | static int vfstraceShmMap( |
| 16971 | sqlite3_file *pFile, |
| 16972 | int iRegion, |
| 16973 | int szRegion, |
| 16974 | int isWrite, |
| 16975 | void volatile **pp |
| 16976 | ){ |
| 16977 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16978 | vfstrace_info *pInfo = p->pInfo; |
| 16979 | int rc; |
| 16980 | vfstraceOnOff(pInfo, VTR_SHMMAP); |
| 16981 | vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)", |
| 16982 | pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite); |
| 16983 | rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); |
| 16984 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 16985 | return rc; |
| 16986 | } |
| 16987 | static void vfstraceShmBarrier(sqlite3_file *pFile){ |
| 16988 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16989 | vfstrace_info *pInfo = p->pInfo; |
| 16990 | vfstraceOnOff(pInfo, VTR_SHMBAR); |
| 16991 | vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName); |
| 16992 | p->pReal->pMethods->xShmBarrier(p->pReal); |
| 16993 | } |
| 16994 | static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){ |
| 16995 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 16996 | vfstrace_info *pInfo = p->pInfo; |
| 16997 | int rc; |
| 16998 | vfstraceOnOff(pInfo, VTR_SHMUNMAP); |
| 16999 | vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)", |
| 17000 | pInfo->zVfsName, p->zFName, delFlag); |
| 17001 | rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag); |
| 17002 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 17003 | return rc; |
| 17004 | } |
| 17005 | |
| 17006 | |
| 17007 | |
| 17008 | /* |
| 17009 | ** Open an vfstrace file handle. |
| 17010 | */ |
| 17011 | static int vfstraceOpen( |
| 17012 | sqlite3_vfs *pVfs, |
| 17013 | const char *zName, |
| 17014 | sqlite3_file *pFile, |
| 17015 | int flags, |
| 17016 | int *pOutFlags |
| 17017 | ){ |
| 17018 | int rc; |
| 17019 | vfstrace_file *p = (vfstrace_file *)pFile; |
| 17020 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17021 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17022 | p->pInfo = pInfo; |
| 17023 | p->zFName = zName ? fileTail(zName) : "<temp>"; |
| 17024 | p->pReal = (sqlite3_file *)&p[1]; |
| 17025 | rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags); |
| 17026 | vfstraceOnOff(pInfo, VTR_OPEN); |
| 17027 | vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)", |
| 17028 | pInfo->zVfsName, p->zFName, flags); |
| 17029 | if( p->pReal->pMethods ){ |
| 17030 | sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) ); |
| 17031 | const sqlite3_io_methods *pSub = p->pReal->pMethods; |
| 17032 | memset(pNew, 0, sizeof(*pNew)); |
| 17033 | pNew->iVersion = pSub->iVersion; |
| 17034 | pNew->xClose = vfstraceClose; |
| 17035 | pNew->xRead = vfstraceRead; |
| 17036 | pNew->xWrite = vfstraceWrite; |
| 17037 | pNew->xTruncate = vfstraceTruncate; |
| 17038 | pNew->xSync = vfstraceSync; |
| 17039 | pNew->xFileSize = vfstraceFileSize; |
| 17040 | pNew->xLock = vfstraceLock; |
| 17041 | pNew->xUnlock = vfstraceUnlock; |
| 17042 | pNew->xCheckReservedLock = vfstraceCheckReservedLock; |
| 17043 | pNew->xFileControl = vfstraceFileControl; |
| 17044 | pNew->xSectorSize = vfstraceSectorSize; |
| 17045 | pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics; |
| 17046 | if( pNew->iVersion>=2 ){ |
| 17047 | pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0; |
| 17048 | pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0; |
| 17049 | pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0; |
| 17050 | pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0; |
| 17051 | } |
| 17052 | pFile->pMethods = pNew; |
| 17053 | } |
| 17054 | vfstrace_print_errcode(pInfo, " -> %s", rc); |
| 17055 | if( pOutFlags ){ |
| 17056 | vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags); |
| 17057 | }else{ |
| 17058 | vfstrace_printf(pInfo, "\n"); |
| 17059 | } |
| 17060 | return rc; |
| 17061 | } |
| 17062 | |
| 17063 | /* |
| 17064 | ** Delete the file located at zPath. If the dirSync argument is true, |
| 17065 | ** ensure the file-system modifications are synced to disk before |
| 17066 | ** returning. |
| 17067 | */ |
| 17068 | static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ |
| 17069 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17070 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17071 | int rc; |
| 17072 | vfstraceOnOff(pInfo, VTR_DELETE); |
| 17073 | vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", |
| 17074 | pInfo->zVfsName, zPath, dirSync); |
| 17075 | rc = pRoot->xDelete(pRoot, zPath, dirSync); |
| 17076 | vfstrace_print_errcode(pInfo, " -> %s\n", rc); |
| 17077 | return rc; |
| 17078 | } |
| 17079 | |
| 17080 | /* |
| 17081 | ** Test for access permissions. Return true if the requested permission |
| 17082 | ** is available, or false otherwise. |
| 17083 | */ |
| 17084 | static int vfstraceAccess( |
| 17085 | sqlite3_vfs *pVfs, |
| 17086 | const char *zPath, |
| 17087 | int flags, |
| 17088 | int *pResOut |
| 17089 | ){ |
| 17090 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17091 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17092 | int rc; |
| 17093 | vfstraceOnOff(pInfo, VTR_ACCESS); |
| 17094 | vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)", |
| 17095 | pInfo->zVfsName, zPath, flags); |
| 17096 | rc = pRoot->xAccess(pRoot, zPath, flags, pResOut); |
| 17097 | vfstrace_print_errcode(pInfo, " -> %s", rc); |
| 17098 | vfstrace_printf(pInfo, ", out=%d\n", *pResOut); |
| 17099 | return rc; |
| 17100 | } |
| 17101 | |
| 17102 | /* |
| 17103 | ** Populate buffer zOut with the full canonical pathname corresponding |
| 17104 | ** to the pathname in zPath. zOut is guaranteed to point to a buffer |
| 17105 | ** of at least (DEVSYM_MAX_PATHNAME+1) bytes. |
| 17106 | */ |
| 17107 | static int vfstraceFullPathname( |
| 17108 | sqlite3_vfs *pVfs, |
| 17109 | const char *zPath, |
| 17110 | int nOut, |
| 17111 | char *zOut |
| 17112 | ){ |
| 17113 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17114 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17115 | int rc; |
| 17116 | vfstraceOnOff(pInfo, VTR_FULLPATH); |
| 17117 | vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")", |
| 17118 | pInfo->zVfsName, zPath); |
| 17119 | rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut); |
| 17120 | vfstrace_print_errcode(pInfo, " -> %s", rc); |
| 17121 | vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut); |
| 17122 | return rc; |
| 17123 | } |
| 17124 | |
| 17125 | /* |
| 17126 | ** Open the dynamic library located at zPath and return a handle. |
| 17127 | */ |
| 17128 | static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){ |
| 17129 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17130 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17131 | vfstraceOnOff(pInfo, VTR_DLOPEN); |
| 17132 | vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath); |
| 17133 | return pRoot->xDlOpen(pRoot, zPath); |
| 17134 | } |
| 17135 | |
| 17136 | /* |
| 17137 | ** Populate the buffer zErrMsg (size nByte bytes) with a human readable |
| 17138 | ** utf-8 string describing the most recent error encountered associated |
| 17139 | ** with dynamic libraries. |
| 17140 | */ |
| 17141 | static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ |
| 17142 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17143 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17144 | vfstraceOnOff(pInfo, VTR_DLERR); |
| 17145 | vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte); |
| 17146 | pRoot->xDlError(pRoot, nByte, zErrMsg); |
| 17147 | vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg); |
| 17148 | } |
| 17149 | |
| 17150 | /* |
| 17151 | ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. |
| 17152 | */ |
| 17153 | static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){ |
| 17154 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17155 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17156 | vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym); |
| 17157 | return pRoot->xDlSym(pRoot, p, zSym); |
| 17158 | } |
| 17159 | |
| 17160 | /* |
| 17161 | ** Close the dynamic library handle pHandle. |
| 17162 | */ |
| 17163 | static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){ |
| 17164 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17165 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17166 | vfstraceOnOff(pInfo, VTR_DLCLOSE); |
| 17167 | vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName); |
| 17168 | pRoot->xDlClose(pRoot, pHandle); |
| 17169 | } |
| 17170 | |
| 17171 | /* |
| 17172 | ** Populate the buffer pointed to by zBufOut with nByte bytes of |
| 17173 | ** random data. |
| 17174 | */ |
| 17175 | static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ |
| 17176 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17177 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17178 | vfstraceOnOff(pInfo, VTR_RAND); |
| 17179 | vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte); |
| 17180 | return pRoot->xRandomness(pRoot, nByte, zBufOut); |
| 17181 | } |
| 17182 | |
| 17183 | /* |
| 17184 | ** Sleep for nMicro microseconds. Return the number of microseconds |
| 17185 | ** actually slept. |
| 17186 | */ |
| 17187 | static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){ |
| 17188 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17189 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17190 | vfstraceOnOff(pInfo, VTR_SLEEP); |
| 17191 | vfstrace_printf(pInfo, "%s.xSleep(%d)\n", pInfo->zVfsName, nMicro); |
| 17192 | return pRoot->xSleep(pRoot, nMicro); |
| 17193 | } |
| 17194 | |
| 17195 | /* |
| 17196 | ** Return the current time as a Julian Day number in *pTimeOut. |
| 17197 | */ |
| 17198 | static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ |
| 17199 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17200 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17201 | int rc; |
| 17202 | vfstraceOnOff(pInfo, VTR_CURTIME); |
| 17203 | vfstrace_printf(pInfo, "%s.xCurrentTime()", pInfo->zVfsName); |
| 17204 | rc = pRoot->xCurrentTime(pRoot, pTimeOut); |
| 17205 | vfstrace_printf(pInfo, " -> %.17g\n", *pTimeOut); |
| 17206 | return rc; |
| 17207 | } |
| 17208 | static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ |
| 17209 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17210 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17211 | int rc; |
| 17212 | vfstraceOnOff(pInfo, VTR_CURTIME); |
| 17213 | vfstrace_printf(pInfo, "%s.xCurrentTimeInt64()", pInfo->zVfsName); |
| 17214 | rc = pRoot->xCurrentTimeInt64(pRoot, pTimeOut); |
| 17215 | vfstrace_printf(pInfo, " -> %lld\n", *pTimeOut); |
| 17216 | return rc; |
| 17217 | } |
| 17218 | |
| 17219 | /* |
| 17220 | ** Return the most recent error code and message |
| 17221 | */ |
| 17222 | static int vfstraceGetLastError(sqlite3_vfs *pVfs, int nErr, char *zErr){ |
| 17223 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17224 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17225 | int rc; |
| 17226 | vfstraceOnOff(pInfo, VTR_LASTERR); |
| 17227 | vfstrace_printf(pInfo, "%s.xGetLastError(%d,zBuf)", pInfo->zVfsName, nErr); |
| 17228 | if( nErr ) zErr[0] = 0; |
| 17229 | rc = pRoot->xGetLastError(pRoot, nErr, zErr); |
| 17230 | vfstrace_printf(pInfo, " -> zBuf[] = \"%s\", rc = %d\n", nErr?zErr:"", rc); |
| 17231 | return rc; |
| 17232 | } |
| 17233 | |
| 17234 | /* |
| 17235 | ** Override system calls. |
| 17236 | */ |
| 17237 | static int vfstraceSetSystemCall( |
| 17238 | sqlite3_vfs *pVfs, |
| 17239 | const char *zName, |
| 17240 | sqlite3_syscall_ptr pFunc |
| 17241 | ){ |
| 17242 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17243 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17244 | return pRoot->xSetSystemCall(pRoot, zName, pFunc); |
| 17245 | } |
| 17246 | static sqlite3_syscall_ptr vfstraceGetSystemCall( |
| 17247 | sqlite3_vfs *pVfs, |
| 17248 | const char *zName |
| 17249 | ){ |
| 17250 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17251 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17252 | return pRoot->xGetSystemCall(pRoot, zName); |
| 17253 | } |
| 17254 | static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ |
| 17255 | vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; |
| 17256 | sqlite3_vfs *pRoot = pInfo->pRootVfs; |
| 17257 | return pRoot->xNextSystemCall(pRoot, zName); |
| 17258 | } |
| 17259 | |
| 17260 | |
| 17261 | /* |
| 17262 | ** Clients invoke this routine to construct a new trace-vfs shim. |
| 17263 | ** |
| 17264 | ** Return SQLITE_OK on success. |
| 17265 | ** |
| 17266 | ** SQLITE_NOMEM is returned in the case of a memory allocation error. |
| 17267 | ** SQLITE_NOTFOUND is returned if zOldVfsName does not exist. |
| 17268 | */ |
| 17269 | int vfstrace_register( |
| 17270 | const char *zTraceName, /* Name of the newly constructed VFS */ |
| 17271 | const char *zOldVfsName, /* Name of the underlying VFS */ |
| 17272 | int (*xOut)(const char*,void*), /* Output routine. ex: fputs */ |
| 17273 | void *pOutArg, /* 2nd argument to xOut. ex: stderr */ |
| 17274 | int makeDefault /* True to make the new VFS the default */ |
| 17275 | ){ |
| 17276 | sqlite3_vfs *pNew; |
| 17277 | sqlite3_vfs *pRoot; |
| 17278 | vfstrace_info *pInfo; |
| 17279 | size_t nName; |
| 17280 | size_t nByte; |
| 17281 | |
| 17282 | pRoot = sqlite3_vfs_find(zOldVfsName); |
| 17283 | if( pRoot==0 ) return SQLITE_NOTFOUND; |
| 17284 | nName = strlen(zTraceName); |
| 17285 | nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1; |
| 17286 | pNew = sqlite3_malloc64( nByte ); |
| 17287 | if( pNew==0 ) return SQLITE_NOMEM; |
| 17288 | memset(pNew, 0, nByte); |
| 17289 | pInfo = (vfstrace_info*)&pNew[1]; |
| 17290 | pNew->iVersion = pRoot->iVersion; |
| 17291 | pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file); |
| 17292 | pNew->mxPathname = pRoot->mxPathname; |
| 17293 | pNew->zName = (char*)&pInfo[1]; |
| 17294 | memcpy((char*)&pInfo[1], zTraceName, nName+1); |
| 17295 | pNew->pAppData = pInfo; |
| 17296 | pNew->xOpen = vfstraceOpen; |
| 17297 | pNew->xDelete = vfstraceDelete; |
| 17298 | pNew->xAccess = vfstraceAccess; |
| 17299 | pNew->xFullPathname = vfstraceFullPathname; |
| 17300 | pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen; |
| 17301 | pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError; |
| 17302 | pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym; |
| 17303 | pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose; |
| 17304 | pNew->xRandomness = vfstraceRandomness; |
| 17305 | pNew->xSleep = vfstraceSleep; |
| 17306 | pNew->xCurrentTime = vfstraceCurrentTime; |
| 17307 | pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError; |
| 17308 | if( pNew->iVersion>=2 ){ |
| 17309 | pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 : |
| 17310 | vfstraceCurrentTimeInt64; |
| 17311 | if( pNew->iVersion>=3 ){ |
| 17312 | pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 : |
| 17313 | vfstraceSetSystemCall; |
| 17314 | pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 : |
| 17315 | vfstraceGetSystemCall; |
| 17316 | pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 : |
| 17317 | vfstraceNextSystemCall; |
| 17318 | } |
| 17319 | } |
| 17320 | pInfo->pRootVfs = pRoot; |
| 17321 | pInfo->xOut = xOut; |
| 17322 | pInfo->pOutArg = pOutArg; |
| 17323 | pInfo->zVfsName = pNew->zName; |
| 17324 | pInfo->pTraceVfs = pNew; |
| 17325 | pInfo->mTrace = 0xffffffff; |
| 17326 | pInfo->bOn = 1; |
| 17327 | vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n", |
| 17328 | pInfo->zVfsName, pRoot->zName); |
| 17329 | return sqlite3_vfs_register(pNew, makeDefault); |
| 17330 | } |
| 17331 | |
| 17332 | /* |
| 17333 | ** Look for the named VFS. If it is a TRACEVFS, then unregister it |
| 17334 | ** and delete it. |
| 17335 | */ |
| 17336 | void vfstrace_unregister(const char *zTraceName){ |
| 17337 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zTraceName); |
| 17338 | if( pVfs==0 ) return; |
| 17339 | if( pVfs->xOpen!=vfstraceOpen ) return; |
| 17340 | sqlite3_vfs_unregister(pVfs); |
| 17341 | sqlite3_free(pVfs); |
| 17342 | } |
| 17343 | |
| 17344 | /************************* End ../ext/misc/vfstrace.c ********************/ |
| 17345 | |
| 17346 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 17347 | #define SQLITE_SHELL_HAVE_RECOVER 1 |
| 17348 | #else |
| 17349 | #define SQLITE_SHELL_HAVE_RECOVER 0 |
| @@ -18241,11 +19370,11 @@ | |
| 19370 | }while( strstr(z,zBuf)!=0 ); |
| 19371 | return zBuf; |
| 19372 | } |
| 19373 | |
| 19374 | /* |
| 19375 | ** Implementation of scalar SQL function "escape_crlf". The argument passed to |
| 19376 | ** this function is the output of built-in function quote(). If the first |
| 19377 | ** character of the input is "'", indicating that the value passed to quote() |
| 19378 | ** was a text value, then this function searches the input for "\n" and "\r" |
| 19379 | ** characters and adds a wrapper similar to the following: |
| 19380 | ** |
| @@ -18252,11 +19381,11 @@ | |
| 19381 | ** replace(replace(<input>, '\n', char(10), '\r', char(13)); |
| 19382 | ** |
| 19383 | ** Or, if the first character of the input is not "'", then a copy of the input |
| 19384 | ** is returned. |
| 19385 | */ |
| 19386 | static void recoverEscapeCrlf( |
| 19387 | sqlite3_context *context, |
| 19388 | int argc, |
| 19389 | sqlite3_value **argv |
| 19390 | ){ |
| 19391 | const char *zText = (const char*)sqlite3_value_text(argv[0]); |
| @@ -18467,11 +19596,11 @@ | |
| 19596 | void (*xFunc)(sqlite3_context*,int,sqlite3_value **); |
| 19597 | } aFunc[] = { |
| 19598 | { "getpage", 1, recoverGetPage }, |
| 19599 | { "page_is_used", 1, recoverPageIsUsed }, |
| 19600 | { "read_i32", 2, recoverReadI32 }, |
| 19601 | { "escape_crlf", 1, recoverEscapeCrlf }, |
| 19602 | }; |
| 19603 | |
| 19604 | const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; |
| 19605 | sqlite3 *db = 0; /* New database handle */ |
| 19606 | int ii; /* For iterating through aFunc[] */ |
| @@ -18820,11 +19949,11 @@ | |
| 19949 | assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); |
| 19950 | zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); |
| 19951 | |
| 19952 | if( bSql ){ |
| 19953 | zBind = recoverMPrintf(p, |
| 19954 | "%z%sescape_crlf(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind |
| 19955 | ); |
| 19956 | zSqlSep = "||', '||"; |
| 19957 | }else{ |
| 19958 | zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); |
| 19959 | } |
| @@ -19322,10 +20451,12 @@ | |
| 20451 | apVal[iField] = sqlite3_value_dup( pVal ); |
| 20452 | if( apVal[iField]==0 ){ |
| 20453 | recoverError(p, SQLITE_NOMEM, 0); |
| 20454 | } |
| 20455 | p1->nVal = iField+1; |
| 20456 | }else if( pTab->nCol==0 ){ |
| 20457 | p1->nVal = pTab->nCol; |
| 20458 | } |
| 20459 | p1->iPrevCell = iCell; |
| 20460 | p1->iPrevPage = iPage; |
| 20461 | } |
| 20462 | }else{ |
| @@ -20437,10 +21568,11 @@ | |
| 21568 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 21569 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 21570 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 21571 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 21572 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 21573 | u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ |
| 21574 | ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 21575 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 21576 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 21577 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 21578 | int outCount; /* Revert to stdout when reaching zero */ |
| @@ -20582,10 +21714,11 @@ | |
| 21714 | #define MODE_Table 15 /* MySQL-style table formatting */ |
| 21715 | #define MODE_Box 16 /* Unicode box-drawing characters */ |
| 21716 | #define MODE_Count 17 /* Output only a count of the rows of output */ |
| 21717 | #define MODE_Off 18 /* No query output shown */ |
| 21718 | #define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */ |
| 21719 | #define MODE_Www 20 /* Full web-page output */ |
| 21720 | |
| 21721 | static const char *modeDescr[] = { |
| 21722 | "line", |
| 21723 | "column", |
| 21724 | "list", |
| @@ -20602,11 +21735,13 @@ | |
| 21735 | "json", |
| 21736 | "markdown", |
| 21737 | "table", |
| 21738 | "box", |
| 21739 | "count", |
| 21740 | "off", |
| 21741 | "scanexp", |
| 21742 | "www", |
| 21743 | }; |
| 21744 | |
| 21745 | /* |
| 21746 | ** These are the column/row/line separators used by the various |
| 21747 | ** import/export modes. |
| @@ -20630,11 +21765,11 @@ | |
| 21765 | ** A callback for the sqlite3_log() interface. |
| 21766 | */ |
| 21767 | static void shellLog(void *pArg, int iErrCode, const char *zMsg){ |
| 21768 | ShellState *p = (ShellState*)pArg; |
| 21769 | if( p->pLog==0 ) return; |
| 21770 | sqlite3_fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); |
| 21771 | fflush(p->pLog); |
| 21772 | } |
| 21773 | |
| 21774 | /* |
| 21775 | ** SQL function: shell_putsnl(X) |
| @@ -20645,13 +21780,13 @@ | |
| 21780 | static void shellPutsFunc( |
| 21781 | sqlite3_context *pCtx, |
| 21782 | int nVal, |
| 21783 | sqlite3_value **apVal |
| 21784 | ){ |
| 21785 | ShellState *p = (ShellState*)sqlite3_user_data(pCtx); |
| 21786 | (void)nVal; |
| 21787 | sqlite3_fprintf(p->out, "%s\n", sqlite3_value_text(apVal[0])); |
| 21788 | sqlite3_result_value(pCtx, apVal[0]); |
| 21789 | } |
| 21790 | |
| 21791 | /* |
| 21792 | ** If in safe mode, print an error message described by the arguments |
| @@ -20666,11 +21801,11 @@ | |
| 21801 | va_list ap; |
| 21802 | char *zMsg; |
| 21803 | va_start(ap, zErrMsg); |
| 21804 | zMsg = sqlite3_vmprintf(zErrMsg, ap); |
| 21805 | va_end(ap); |
| 21806 | sqlite3_fprintf(stderr, "line %d: %s\n", p->lineno, zMsg); |
| 21807 | exit(1); |
| 21808 | } |
| 21809 | } |
| 21810 | |
| 21811 | /* |
| @@ -20699,11 +21834,11 @@ | |
| 21834 | char *zTempFile = 0; |
| 21835 | sqlite3 *db; |
| 21836 | char *zCmd = 0; |
| 21837 | int bBin; |
| 21838 | int rc; |
| 21839 | int hasCRLF = 0; |
| 21840 | FILE *f = 0; |
| 21841 | sqlite3_int64 sz; |
| 21842 | sqlite3_int64 x; |
| 21843 | unsigned char *p = 0; |
| 21844 | |
| @@ -20733,11 +21868,11 @@ | |
| 21868 | } |
| 21869 | } |
| 21870 | bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; |
| 21871 | /* When writing the file to be edited, do \n to \r\n conversions on systems |
| 21872 | ** that want \r\n line endings */ |
| 21873 | f = sqlite3_fopen(zTempFile, bBin ? "wb" : "w"); |
| 21874 | if( f==0 ){ |
| 21875 | sqlite3_result_error(context, "edit() cannot open temp file", -1); |
| 21876 | goto edit_func_end; |
| 21877 | } |
| 21878 | sz = sqlite3_value_bytes(argv[0]); |
| @@ -20744,11 +21879,11 @@ | |
| 21879 | if( bBin ){ |
| 21880 | x = fwrite(sqlite3_value_blob(argv[0]), 1, (size_t)sz, f); |
| 21881 | }else{ |
| 21882 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 21883 | /* Remember whether or not the value originally contained \r\n */ |
| 21884 | if( z && strstr(z,"\r\n")!=0 ) hasCRLF = 1; |
| 21885 | x = fwrite(sqlite3_value_text(argv[0]), 1, (size_t)sz, f); |
| 21886 | } |
| 21887 | fclose(f); |
| 21888 | f = 0; |
| 21889 | if( x!=sz ){ |
| @@ -20764,11 +21899,11 @@ | |
| 21899 | sqlite3_free(zCmd); |
| 21900 | if( rc ){ |
| 21901 | sqlite3_result_error(context, "EDITOR returned non-zero", -1); |
| 21902 | goto edit_func_end; |
| 21903 | } |
| 21904 | f = sqlite3_fopen(zTempFile, "rb"); |
| 21905 | if( f==0 ){ |
| 21906 | sqlite3_result_error(context, |
| 21907 | "edit() cannot reopen temp file after edit", -1); |
| 21908 | goto edit_func_end; |
| 21909 | } |
| @@ -20789,11 +21924,11 @@ | |
| 21924 | } |
| 21925 | if( bBin ){ |
| 21926 | sqlite3_result_blob64(context, p, sz, sqlite3_free); |
| 21927 | }else{ |
| 21928 | sqlite3_int64 i, j; |
| 21929 | if( hasCRLF ){ |
| 21930 | /* If the original contains \r\n then do no conversions back to \n */ |
| 21931 | }else{ |
| 21932 | /* If the file did not originally contain \r\n then convert any new |
| 21933 | ** \r\n back into \n */ |
| 21934 | p[sz] = 0; |
| @@ -20830,15 +21965,30 @@ | |
| 21965 | p->mode = p->modePrior; |
| 21966 | p->shellFlgs = p->priorShFlgs; |
| 21967 | memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 21968 | memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 21969 | } |
| 21970 | |
| 21971 | /* |
| 21972 | ** Set output mode to text or binary for Windows. |
| 21973 | */ |
| 21974 | static void setCrlfMode(ShellState *p){ |
| 21975 | #ifdef _WIN32 |
| 21976 | if( p->crlfMode ){ |
| 21977 | sqlite3_fsetmode(p->out, _O_TEXT); |
| 21978 | }else{ |
| 21979 | sqlite3_fsetmode(p->out, _O_BINARY); |
| 21980 | } |
| 21981 | #else |
| 21982 | UNUSED_PARAMETER(p); |
| 21983 | #endif |
| 21984 | } |
| 21985 | |
| 21986 | /* |
| 21987 | ** Output the given string as a hex-encoded blob (eg. X'1234' ) |
| 21988 | */ |
| 21989 | static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ |
| 21990 | int i; |
| 21991 | unsigned char *aBlob = (unsigned char*)pBlob; |
| 21992 | |
| 21993 | char *zStr = sqlite3_malloc(nBlob*2 + 1); |
| 21994 | shell_check_oom(zStr); |
| @@ -20851,11 +22001,11 @@ | |
| 22001 | zStr[i*2] = aHex[ (aBlob[i] >> 4) ]; |
| 22002 | zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ]; |
| 22003 | } |
| 22004 | zStr[i*2] = '\0'; |
| 22005 | |
| 22006 | sqlite3_fprintf(out, "X'%s'", zStr); |
| 22007 | sqlite3_free(zStr); |
| 22008 | } |
| 22009 | |
| 22010 | /* |
| 22011 | ** Find a string that is not found anywhere in z[]. Return a pointer |
| @@ -20881,46 +22031,40 @@ | |
| 22031 | /* |
| 22032 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 22033 | ** |
| 22034 | ** See also: output_quoted_escaped_string() |
| 22035 | */ |
| 22036 | static void output_quoted_string(ShellState *p, const char *z){ |
| 22037 | int i; |
| 22038 | char c; |
| 22039 | FILE *out = p->out; |
| 22040 | sqlite3_fsetmode(out, _O_BINARY); |
| 22041 | if( z==0 ) return; |
| 22042 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 22043 | if( c==0 ){ |
| 22044 | sqlite3_fprintf(out, "'%s'",z); |
| 22045 | }else{ |
| 22046 | sqlite3_fputs("'", out); |
| 22047 | while( *z ){ |
| 22048 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 22049 | if( c=='\'' ) i++; |
| 22050 | if( i ){ |
| 22051 | sqlite3_fprintf(out, "%.*s", i, z); |
| 22052 | z += i; |
| 22053 | } |
| 22054 | if( c=='\'' ){ |
| 22055 | sqlite3_fputs("'", out); |
| 22056 | continue; |
| 22057 | } |
| 22058 | if( c==0 ){ |
| 22059 | break; |
| 22060 | } |
| 22061 | z++; |
| 22062 | } |
| 22063 | sqlite3_fputs("'", out); |
| 22064 | } |
| 22065 | setCrlfMode(p); |
| 22066 | } |
| 22067 | |
| 22068 | /* |
| 22069 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 22070 | ** Additionallly , escape the "\n" and "\r" characters so that they do not |
| @@ -20928,20 +22072,18 @@ | |
| 22072 | ** systems. |
| 22073 | ** |
| 22074 | ** This is like output_quoted_string() but with the addition of the \r\n |
| 22075 | ** escape mechanism. |
| 22076 | */ |
| 22077 | static void output_quoted_escaped_string(ShellState *p, const char *z){ |
| 22078 | int i; |
| 22079 | char c; |
| 22080 | FILE *out = p->out; |
| 22081 | sqlite3_fsetmode(out, _O_BINARY); |
| 22082 | for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} |
| 22083 | if( c==0 ){ |
| 22084 | sqlite3_fprintf(out, "'%s'",z); |
| 22085 | }else{ |
| 22086 | const char *zNL = 0; |
| 22087 | const char *zCR = 0; |
| 22088 | int nNL = 0; |
| 22089 | int nCR = 0; |
| @@ -20949,52 +22091,48 @@ | |
| 22091 | for(i=0; z[i]; i++){ |
| 22092 | if( z[i]=='\n' ) nNL++; |
| 22093 | if( z[i]=='\r' ) nCR++; |
| 22094 | } |
| 22095 | if( nNL ){ |
| 22096 | sqlite3_fputs("replace(", out); |
| 22097 | zNL = unused_string(z, "\\n", "\\012", zBuf1); |
| 22098 | } |
| 22099 | if( nCR ){ |
| 22100 | sqlite3_fputs("replace(", out); |
| 22101 | zCR = unused_string(z, "\\r", "\\015", zBuf2); |
| 22102 | } |
| 22103 | sqlite3_fputs("'", out); |
| 22104 | while( *z ){ |
| 22105 | for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} |
| 22106 | if( c=='\'' ) i++; |
| 22107 | if( i ){ |
| 22108 | sqlite3_fprintf(out, "%.*s", i, z); |
| 22109 | z += i; |
| 22110 | } |
| 22111 | if( c=='\'' ){ |
| 22112 | sqlite3_fputs("'", out); |
| 22113 | continue; |
| 22114 | } |
| 22115 | if( c==0 ){ |
| 22116 | break; |
| 22117 | } |
| 22118 | z++; |
| 22119 | if( c=='\n' ){ |
| 22120 | sqlite3_fputs(zNL, out); |
| 22121 | continue; |
| 22122 | } |
| 22123 | sqlite3_fputs(zCR, out); |
| 22124 | } |
| 22125 | sqlite3_fputs("'", out); |
| 22126 | if( nCR ){ |
| 22127 | sqlite3_fprintf(out, ",'%s',char(13))", zCR); |
| 22128 | } |
| 22129 | if( nNL ){ |
| 22130 | sqlite3_fprintf(out, ",'%s',char(10))", zNL); |
| 22131 | } |
| 22132 | } |
| 22133 | setCrlfMode(p); |
| 22134 | } |
| 22135 | |
| 22136 | /* |
| 22137 | ** Find earliest of chars within s specified in zAny. |
| 22138 | ** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated. |
| @@ -21010,26 +22148,64 @@ | |
| 22148 | } |
| 22149 | ++zAny; |
| 22150 | } |
| 22151 | return pcFirst; |
| 22152 | } |
| 22153 | |
| 22154 | /* Skip over as much z[] input char sequence as is valid UTF-8, |
| 22155 | ** limited per nAccept char's or whole characters and containing |
| 22156 | ** no char cn such that ((1<<cn) & ccm)!=0. On return, the |
| 22157 | ** sequence z:return (inclusive:exclusive) is validated UTF-8. |
| 22158 | ** Limit: nAccept>=0 => char count, nAccept<0 => character |
| 22159 | */ |
| 22160 | const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){ |
| 22161 | int ng = (nAccept<0)? -nAccept : 0; |
| 22162 | const char *pcLimit = (nAccept>=0)? z+nAccept : 0; |
| 22163 | assert(z!=0); |
| 22164 | while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){ |
| 22165 | unsigned char c = *(u8*)z; |
| 22166 | if( c<0x7f ){ |
| 22167 | if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z; |
| 22168 | ++z; /* ASCII */ |
| 22169 | }else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */ |
| 22170 | else{ |
| 22171 | const char *zt = z+1; /* Got lead byte, look at trail bytes.*/ |
| 22172 | do{ |
| 22173 | if( pcLimit && zt >= pcLimit ) return z; |
| 22174 | else{ |
| 22175 | char ct = *zt++; |
| 22176 | if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ |
| 22177 | /* Trailing bytes are too few, too many, or invalid. */ |
| 22178 | return z; |
| 22179 | } |
| 22180 | } |
| 22181 | } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ |
| 22182 | z = zt; |
| 22183 | } |
| 22184 | } |
| 22185 | return z; |
| 22186 | } |
| 22187 | |
| 22188 | |
| 22189 | /* |
| 22190 | ** Output the given string as a quoted according to C or TCL quoting rules. |
| 22191 | */ |
| 22192 | static void output_c_string(FILE *out, const char *z){ |
| 22193 | char c; |
| 22194 | static const char *zq = "\""; |
| 22195 | static long ctrlMask = ~0L; |
| 22196 | static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ |
| 22197 | char ace[3] = "\\?"; |
| 22198 | char cbsSay; |
| 22199 | sqlite3_fputs(zq, out); |
| 22200 | while( *z!=0 ){ |
| 22201 | const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); |
| 22202 | const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); |
| 22203 | const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; |
| 22204 | if( pcEnd > z ){ |
| 22205 | sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); |
| 22206 | } |
| 22207 | if( (c = *pcEnd)==0 ) break; |
| 22208 | ++pcEnd; |
| 22209 | switch( c ){ |
| 22210 | case '\\': case '"': |
| 22211 | cbsSay = (char)c; |
| @@ -21040,47 +22216,47 @@ | |
| 22216 | case '\f': cbsSay = 'f'; break; |
| 22217 | default: cbsSay = 0; break; |
| 22218 | } |
| 22219 | if( cbsSay ){ |
| 22220 | ace[1] = cbsSay; |
| 22221 | sqlite3_fputs(ace, out); |
| 22222 | }else if( !isprint(c&0xff) ){ |
| 22223 | sqlite3_fprintf(out, "\\%03o", c&0xff); |
| 22224 | }else{ |
| 22225 | ace[1] = (char)c; |
| 22226 | sqlite3_fputs(ace+1, out); |
| 22227 | } |
| 22228 | z = pcEnd; |
| 22229 | } |
| 22230 | sqlite3_fputs(zq, out); |
| 22231 | } |
| 22232 | |
| 22233 | /* |
| 22234 | ** Output the given string as quoted according to JSON quoting rules. |
| 22235 | */ |
| 22236 | static void output_json_string(FILE *out, const char *z, i64 n){ |
| 22237 | unsigned char c; |
| 22238 | static const char *zq = "\""; |
| 22239 | static long ctrlMask = ~0L; |
| 22240 | static const char *zDQBS = "\"\\"; |
| 22241 | const char *pcLimit; |
| 22242 | char ace[3] = "\\?"; |
| 22243 | char cbsSay; |
| 22244 | |
| 22245 | if( z==0 ) z = ""; |
| 22246 | pcLimit = z + ((n<0)? strlen(z) : (size_t)n); |
| 22247 | sqlite3_fputs(zq, out); |
| 22248 | while( z < pcLimit ){ |
| 22249 | const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); |
| 22250 | const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); |
| 22251 | const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; |
| 22252 | if( pcEnd > z ){ |
| 22253 | sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); |
| 22254 | z = pcEnd; |
| 22255 | } |
| 22256 | if( z >= pcLimit ) break; |
| 22257 | c = (unsigned char)*(z++); |
| 22258 | switch( c ){ |
| 22259 | case '"': case '\\': |
| 22260 | cbsSay = (char)c; |
| 22261 | break; |
| 22262 | case '\b': cbsSay = 'b'; break; |
| @@ -21090,26 +22266,26 @@ | |
| 22266 | case '\t': cbsSay = 't'; break; |
| 22267 | default: cbsSay = 0; break; |
| 22268 | } |
| 22269 | if( cbsSay ){ |
| 22270 | ace[1] = cbsSay; |
| 22271 | sqlite3_fputs(ace, out); |
| 22272 | }else if( c<=0x1f || c>=0x7f ){ |
| 22273 | sqlite3_fprintf(out, "\\u%04x", c); |
| 22274 | }else{ |
| 22275 | ace[1] = (char)c; |
| 22276 | sqlite3_fputs(ace+1, out); |
| 22277 | } |
| 22278 | } |
| 22279 | sqlite3_fputs(zq, out); |
| 22280 | } |
| 22281 | |
| 22282 | /* |
| 22283 | ** Output the given string with characters that are special to |
| 22284 | ** HTML escaped. |
| 22285 | */ |
| 22286 | static void output_html_string(FILE *out, const char *z){ |
| 22287 | int i; |
| 22288 | if( z==0 ) z = ""; |
| 22289 | while( *z ){ |
| 22290 | for(i=0; z[i] |
| 22291 | && z[i]!='<' |
| @@ -21117,22 +22293,22 @@ | |
| 22293 | && z[i]!='>' |
| 22294 | && z[i]!='\"' |
| 22295 | && z[i]!='\''; |
| 22296 | i++){} |
| 22297 | if( i>0 ){ |
| 22298 | sqlite3_fprintf(out, "%.*s",i,z); |
| 22299 | } |
| 22300 | if( z[i]=='<' ){ |
| 22301 | sqlite3_fputs("<", out); |
| 22302 | }else if( z[i]=='&' ){ |
| 22303 | sqlite3_fputs("&", out); |
| 22304 | }else if( z[i]=='>' ){ |
| 22305 | sqlite3_fputs(">", out); |
| 22306 | }else if( z[i]=='\"' ){ |
| 22307 | sqlite3_fputs(""", out); |
| 22308 | }else if( z[i]=='\'' ){ |
| 22309 | sqlite3_fputs("'", out); |
| 22310 | }else{ |
| 22311 | break; |
| 22312 | } |
| 22313 | z += i + 1; |
| 22314 | } |
| @@ -21167,11 +22343,11 @@ | |
| 22343 | ** the null value. Strings are quoted if necessary. The separator |
| 22344 | ** is only issued if bSep is true. |
| 22345 | */ |
| 22346 | static void output_csv(ShellState *p, const char *z, int bSep){ |
| 22347 | if( z==0 ){ |
| 22348 | sqlite3_fprintf(p->out, "%s",p->nullValue); |
| 22349 | }else{ |
| 22350 | unsigned i; |
| 22351 | for(i=0; z[i]; i++){ |
| 22352 | if( needCsvQuote[((unsigned char*)z)[i]] ){ |
| 22353 | i = 0; |
| @@ -21179,18 +22355,18 @@ | |
| 22355 | } |
| 22356 | } |
| 22357 | if( i==0 || strstr(z, p->colSeparator)!=0 ){ |
| 22358 | char *zQuoted = sqlite3_mprintf("\"%w\"", z); |
| 22359 | shell_check_oom(zQuoted); |
| 22360 | sqlite3_fputs(zQuoted, p->out); |
| 22361 | sqlite3_free(zQuoted); |
| 22362 | }else{ |
| 22363 | sqlite3_fputs(z, p->out); |
| 22364 | } |
| 22365 | } |
| 22366 | if( bSep ){ |
| 22367 | sqlite3_fputs(p->colSeparator, p->out); |
| 22368 | } |
| 22369 | } |
| 22370 | |
| 22371 | /* |
| 22372 | ** This routine runs when the user presses Ctrl-C |
| @@ -21294,20 +22470,20 @@ | |
| 22470 | const char *az[4]; |
| 22471 | az[0] = zA1; |
| 22472 | az[1] = zA2; |
| 22473 | az[2] = zA3; |
| 22474 | az[3] = zA4; |
| 22475 | sqlite3_fprintf(p->out, "authorizer: %s", azAction[op]); |
| 22476 | for(i=0; i<4; i++){ |
| 22477 | sqlite3_fputs(" ", p->out); |
| 22478 | if( az[i] ){ |
| 22479 | output_c_string(p->out, az[i]); |
| 22480 | }else{ |
| 22481 | sqlite3_fputs("NULL", p->out); |
| 22482 | } |
| 22483 | } |
| 22484 | sqlite3_fputs("\n", p->out); |
| 22485 | if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); |
| 22486 | return SQLITE_OK; |
| 22487 | } |
| 22488 | #endif |
| 22489 | |
| @@ -21319,11 +22495,11 @@ | |
| 22495 | ** |
| 22496 | ** If the schema statement in z[] contains a start-of-comment and if |
| 22497 | ** sqlite3_complete() returns false, try to terminate the comment before |
| 22498 | ** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c |
| 22499 | */ |
| 22500 | static void printSchemaLine(FILE *out, const char *z, const char *zTail){ |
| 22501 | char *zToFree = 0; |
| 22502 | if( z==0 ) return; |
| 22503 | if( zTail==0 ) return; |
| 22504 | if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){ |
| 22505 | const char *zOrig = z; |
| @@ -21341,20 +22517,20 @@ | |
| 22517 | } |
| 22518 | sqlite3_free(zNew); |
| 22519 | } |
| 22520 | } |
| 22521 | if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ |
| 22522 | sqlite3_fprintf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); |
| 22523 | }else{ |
| 22524 | sqlite3_fprintf(out, "%s%s", z, zTail); |
| 22525 | } |
| 22526 | sqlite3_free(zToFree); |
| 22527 | } |
| 22528 | static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ |
| 22529 | char c = z[n]; |
| 22530 | z[n] = 0; |
| 22531 | printSchemaLine(out, z, zTail); |
| 22532 | z[n] = c; |
| 22533 | } |
| 22534 | |
| 22535 | /* |
| 22536 | ** Return true if string z[] has nothing but whitespace and comments to the |
| @@ -21378,11 +22554,11 @@ | |
| 22554 | EQPGraphRow *pNew; |
| 22555 | i64 nText; |
| 22556 | if( zText==0 ) return; |
| 22557 | nText = strlen(zText); |
| 22558 | if( p->autoEQPtest ){ |
| 22559 | sqlite3_fprintf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); |
| 22560 | } |
| 22561 | pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); |
| 22562 | shell_check_oom(pNew); |
| 22563 | pNew->iEqpId = iEqpId; |
| 22564 | pNew->iParentId = p2; |
| @@ -21426,11 +22602,12 @@ | |
| 22602 | i64 n = strlen(p->sGraph.zPrefix); |
| 22603 | char *z; |
| 22604 | for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ |
| 22605 | pNext = eqp_next_row(p, iEqpId, pRow); |
| 22606 | z = pRow->zText; |
| 22607 | sqlite3_fprintf(p->out, "%s%s%s\n", p->sGraph.zPrefix, |
| 22608 | pNext ? "|--" : "`--", z); |
| 22609 | if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ |
| 22610 | memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); |
| 22611 | eqp_render_level(p, pRow->iEqpId); |
| 22612 | p->sGraph.zPrefix[n] = 0; |
| 22613 | } |
| @@ -21446,17 +22623,17 @@ | |
| 22623 | if( pRow->zText[0]=='-' ){ |
| 22624 | if( pRow->pNext==0 ){ |
| 22625 | eqp_reset(p); |
| 22626 | return; |
| 22627 | } |
| 22628 | sqlite3_fprintf(p->out, "%s\n", pRow->zText+3); |
| 22629 | p->sGraph.pRow = pRow->pNext; |
| 22630 | sqlite3_free(pRow); |
| 22631 | }else if( nCycle>0 ){ |
| 22632 | sqlite3_fprintf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); |
| 22633 | }else{ |
| 22634 | sqlite3_fputs("QUERY PLAN\n", p->out); |
| 22635 | } |
| 22636 | p->sGraph.zPrefix[0] = 0; |
| 22637 | eqp_render_level(p, 0); |
| 22638 | eqp_reset(p); |
| 22639 | } |
| @@ -21468,33 +22645,33 @@ | |
| 22645 | */ |
| 22646 | static int progress_handler(void *pClientData) { |
| 22647 | ShellState *p = (ShellState*)pClientData; |
| 22648 | p->nProgress++; |
| 22649 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 22650 | sqlite3_fprintf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
| 22651 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 22652 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 22653 | return 1; |
| 22654 | } |
| 22655 | if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ |
| 22656 | sqlite3_fprintf(p->out, "Progress %u\n", p->nProgress); |
| 22657 | } |
| 22658 | return 0; |
| 22659 | } |
| 22660 | #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
| 22661 | |
| 22662 | /* |
| 22663 | ** Print N dashes |
| 22664 | */ |
| 22665 | static void print_dashes(FILE *out, int N){ |
| 22666 | const char zDash[] = "--------------------------------------------------"; |
| 22667 | const int nDash = sizeof(zDash) - 1; |
| 22668 | while( N>nDash ){ |
| 22669 | sqlite3_fputs(zDash, out); |
| 22670 | N -= nDash; |
| 22671 | } |
| 22672 | sqlite3_fprintf(out, "%.*s", N, zDash); |
| 22673 | } |
| 22674 | |
| 22675 | /* |
| 22676 | ** Print a markdown or table-style row separator using ascii-art |
| 22677 | */ |
| @@ -21503,19 +22680,19 @@ | |
| 22680 | int nArg, |
| 22681 | const char *zSep |
| 22682 | ){ |
| 22683 | int i; |
| 22684 | if( nArg>0 ){ |
| 22685 | sqlite3_fputs(zSep, p->out); |
| 22686 | print_dashes(p->out, p->actualWidth[0]+2); |
| 22687 | for(i=1; i<nArg; i++){ |
| 22688 | sqlite3_fputs(zSep, p->out); |
| 22689 | print_dashes(p->out, p->actualWidth[i]+2); |
| 22690 | } |
| 22691 | sqlite3_fputs(zSep, p->out); |
| 22692 | } |
| 22693 | sqlite3_fputs("\n", p->out); |
| 22694 | } |
| 22695 | |
| 22696 | /* |
| 22697 | ** This is the callback routine that the shell |
| 22698 | ** invokes for each row of a query result. |
| @@ -21541,13 +22718,13 @@ | |
| 22718 | if( azArg==0 ) break; |
| 22719 | for(i=0; i<nArg; i++){ |
| 22720 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 22721 | if( len>w ) w = len; |
| 22722 | } |
| 22723 | if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); |
| 22724 | for(i=0; i<nArg; i++){ |
| 22725 | sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], |
| 22726 | azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
| 22727 | } |
| 22728 | break; |
| 22729 | } |
| 22730 | case MODE_ScanExp: |
| @@ -21571,16 +22748,16 @@ | |
| 22748 | if( nArg>nWidth ) nArg = nWidth; |
| 22749 | |
| 22750 | /* If this is the first row seen, print out the headers */ |
| 22751 | if( p->cnt++==0 ){ |
| 22752 | for(i=0; i<nArg; i++){ |
| 22753 | utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]); |
| 22754 | sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); |
| 22755 | } |
| 22756 | for(i=0; i<nArg; i++){ |
| 22757 | print_dashes(p->out, aWidth[i]); |
| 22758 | sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); |
| 22759 | } |
| 22760 | } |
| 22761 | |
| 22762 | /* If there is no data, exit early. */ |
| 22763 | if( azArg==0 ) break; |
| @@ -21594,21 +22771,21 @@ | |
| 22771 | w = strlenChar(zVal); |
| 22772 | zSep = " "; |
| 22773 | } |
| 22774 | if( i==iIndent && p->aiIndent && p->pStmt ){ |
| 22775 | if( p->iIndent<p->nIndent ){ |
| 22776 | sqlite3_fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); |
| 22777 | } |
| 22778 | p->iIndent++; |
| 22779 | } |
| 22780 | utf8_width_print(p->out, w, zVal ? zVal : p->nullValue); |
| 22781 | sqlite3_fputs(i==nArg-1 ? "\n" : zSep, p->out); |
| 22782 | } |
| 22783 | break; |
| 22784 | } |
| 22785 | case MODE_Semi: { /* .schema and .fullschema output */ |
| 22786 | printSchemaLine(p->out, azArg[0], ";\n"); |
| 22787 | break; |
| 22788 | } |
| 22789 | case MODE_Pretty: { /* .schema and .fullschema with --indent */ |
| 22790 | char *z; |
| 22791 | int j; |
| @@ -21619,11 +22796,11 @@ | |
| 22796 | assert( nArg==1 ); |
| 22797 | if( azArg[0]==0 ) break; |
| 22798 | if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 22799 | || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 22800 | ){ |
| 22801 | sqlite3_fprintf(p->out, "%s;\n", azArg[0]); |
| 22802 | break; |
| 22803 | } |
| 22804 | z = sqlite3_mprintf("%s", azArg[0]); |
| 22805 | shell_check_oom(z); |
| 22806 | j = 0; |
| @@ -21652,255 +22829,265 @@ | |
| 22829 | }else if( c=='(' ){ |
| 22830 | nParen++; |
| 22831 | }else if( c==')' ){ |
| 22832 | nParen--; |
| 22833 | if( nLine>0 && nParen==0 && j>0 ){ |
| 22834 | printSchemaLineN(p->out, z, j, "\n"); |
| 22835 | j = 0; |
| 22836 | } |
| 22837 | } |
| 22838 | z[j++] = c; |
| 22839 | if( nParen==1 && cEnd==0 |
| 22840 | && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 22841 | ){ |
| 22842 | if( c=='\n' ) j--; |
| 22843 | printSchemaLineN(p->out, z, j, "\n "); |
| 22844 | j = 0; |
| 22845 | nLine++; |
| 22846 | while( IsSpace(z[i+1]) ){ i++; } |
| 22847 | } |
| 22848 | } |
| 22849 | z[j] = 0; |
| 22850 | } |
| 22851 | printSchemaLine(p->out, z, ";\n"); |
| 22852 | sqlite3_free(z); |
| 22853 | break; |
| 22854 | } |
| 22855 | case MODE_List: { |
| 22856 | if( p->cnt++==0 && p->showHeader ){ |
| 22857 | for(i=0; i<nArg; i++){ |
| 22858 | sqlite3_fprintf(p->out, "%s%s", azCol[i], |
| 22859 | i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 22860 | } |
| 22861 | } |
| 22862 | if( azArg==0 ) break; |
| 22863 | for(i=0; i<nArg; i++){ |
| 22864 | char *z = azArg[i]; |
| 22865 | if( z==0 ) z = p->nullValue; |
| 22866 | sqlite3_fputs(z, p->out); |
| 22867 | sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); |
| 22868 | } |
| 22869 | break; |
| 22870 | } |
| 22871 | case MODE_Www: |
| 22872 | case MODE_Html: { |
| 22873 | if( p->cnt==0 && p->cMode==MODE_Www ){ |
| 22874 | sqlite3_fputs( |
| 22875 | "</PRE>\n" |
| 22876 | "<TABLE border='1' cellspacing='0' cellpadding='2'>\n" |
| 22877 | ,p->out |
| 22878 | ); |
| 22879 | } |
| 22880 | if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){ |
| 22881 | sqlite3_fputs("<TR>", p->out); |
| 22882 | for(i=0; i<nArg; i++){ |
| 22883 | sqlite3_fputs("<TH>", p->out); |
| 22884 | output_html_string(p->out, azCol[i]); |
| 22885 | sqlite3_fputs("</TH>\n", p->out); |
| 22886 | } |
| 22887 | sqlite3_fputs("</TR>\n", p->out); |
| 22888 | } |
| 22889 | p->cnt++; |
| 22890 | if( azArg==0 ) break; |
| 22891 | sqlite3_fputs("<TR>", p->out); |
| 22892 | for(i=0; i<nArg; i++){ |
| 22893 | sqlite3_fputs("<TD>", p->out); |
| 22894 | output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
| 22895 | sqlite3_fputs("</TD>\n", p->out); |
| 22896 | } |
| 22897 | sqlite3_fputs("</TR>\n", p->out); |
| 22898 | break; |
| 22899 | } |
| 22900 | case MODE_Tcl: { |
| 22901 | if( p->cnt++==0 && p->showHeader ){ |
| 22902 | for(i=0; i<nArg; i++){ |
| 22903 | output_c_string(p->out, azCol[i] ? azCol[i] : ""); |
| 22904 | if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); |
| 22905 | } |
| 22906 | sqlite3_fputs(p->rowSeparator, p->out); |
| 22907 | } |
| 22908 | if( azArg==0 ) break; |
| 22909 | for(i=0; i<nArg; i++){ |
| 22910 | output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
| 22911 | if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); |
| 22912 | } |
| 22913 | sqlite3_fputs(p->rowSeparator, p->out); |
| 22914 | break; |
| 22915 | } |
| 22916 | case MODE_Csv: { |
| 22917 | sqlite3_fsetmode(p->out, _O_BINARY); |
| 22918 | if( p->cnt++==0 && p->showHeader ){ |
| 22919 | for(i=0; i<nArg; i++){ |
| 22920 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 22921 | } |
| 22922 | sqlite3_fputs(p->rowSeparator, p->out); |
| 22923 | } |
| 22924 | if( nArg>0 ){ |
| 22925 | for(i=0; i<nArg; i++){ |
| 22926 | output_csv(p, azArg[i], i<nArg-1); |
| 22927 | } |
| 22928 | sqlite3_fputs(p->rowSeparator, p->out); |
| 22929 | } |
| 22930 | setCrlfMode(p); |
| 22931 | break; |
| 22932 | } |
| 22933 | case MODE_Insert: { |
| 22934 | if( azArg==0 ) break; |
| 22935 | sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable); |
| 22936 | if( p->showHeader ){ |
| 22937 | sqlite3_fputs("(", p->out); |
| 22938 | for(i=0; i<nArg; i++){ |
| 22939 | if( i>0 ) sqlite3_fputs(",", p->out); |
| 22940 | if( quoteChar(azCol[i]) ){ |
| 22941 | char *z = sqlite3_mprintf("\"%w\"", azCol[i]); |
| 22942 | shell_check_oom(z); |
| 22943 | sqlite3_fputs(z, p->out); |
| 22944 | sqlite3_free(z); |
| 22945 | }else{ |
| 22946 | sqlite3_fprintf(p->out, "%s", azCol[i]); |
| 22947 | } |
| 22948 | } |
| 22949 | sqlite3_fputs(")", p->out); |
| 22950 | } |
| 22951 | p->cnt++; |
| 22952 | for(i=0; i<nArg; i++){ |
| 22953 | sqlite3_fputs(i>0 ? "," : " VALUES(", p->out); |
| 22954 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 22955 | sqlite3_fputs("NULL", p->out); |
| 22956 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 22957 | if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 22958 | output_quoted_string(p, azArg[i]); |
| 22959 | }else{ |
| 22960 | output_quoted_escaped_string(p, azArg[i]); |
| 22961 | } |
| 22962 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 22963 | sqlite3_fputs(azArg[i], p->out); |
| 22964 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 22965 | char z[50]; |
| 22966 | double r = sqlite3_column_double(p->pStmt, i); |
| 22967 | sqlite3_uint64 ur; |
| 22968 | memcpy(&ur,&r,sizeof(r)); |
| 22969 | if( ur==0x7ff0000000000000LL ){ |
| 22970 | sqlite3_fputs("9.0e+999", p->out); |
| 22971 | }else if( ur==0xfff0000000000000LL ){ |
| 22972 | sqlite3_fputs("-9.0e+999", p->out); |
| 22973 | }else{ |
| 22974 | sqlite3_int64 ir = (sqlite3_int64)r; |
| 22975 | if( r==(double)ir ){ |
| 22976 | sqlite3_snprintf(50,z,"%lld.0", ir); |
| 22977 | }else{ |
| 22978 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 22979 | } |
| 22980 | sqlite3_fputs(z, p->out); |
| 22981 | } |
| 22982 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 22983 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 22984 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 22985 | output_hex_blob(p->out, pBlob, nBlob); |
| 22986 | }else if( isNumber(azArg[i], 0) ){ |
| 22987 | sqlite3_fputs(azArg[i], p->out); |
| 22988 | }else if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 22989 | output_quoted_string(p, azArg[i]); |
| 22990 | }else{ |
| 22991 | output_quoted_escaped_string(p, azArg[i]); |
| 22992 | } |
| 22993 | } |
| 22994 | sqlite3_fputs(");\n", p->out); |
| 22995 | break; |
| 22996 | } |
| 22997 | case MODE_Json: { |
| 22998 | if( azArg==0 ) break; |
| 22999 | if( p->cnt==0 ){ |
| 23000 | sqlite3_fputs("[{", p->out); |
| 23001 | }else{ |
| 23002 | sqlite3_fputs(",\n{", p->out); |
| 23003 | } |
| 23004 | p->cnt++; |
| 23005 | for(i=0; i<nArg; i++){ |
| 23006 | output_json_string(p->out, azCol[i], -1); |
| 23007 | sqlite3_fputs(":", p->out); |
| 23008 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 23009 | sqlite3_fputs("null", p->out); |
| 23010 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 23011 | char z[50]; |
| 23012 | double r = sqlite3_column_double(p->pStmt, i); |
| 23013 | sqlite3_uint64 ur; |
| 23014 | memcpy(&ur,&r,sizeof(r)); |
| 23015 | if( ur==0x7ff0000000000000LL ){ |
| 23016 | sqlite3_fputs("9.0e+999", p->out); |
| 23017 | }else if( ur==0xfff0000000000000LL ){ |
| 23018 | sqlite3_fputs("-9.0e+999", p->out); |
| 23019 | }else{ |
| 23020 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 23021 | sqlite3_fputs(z, p->out); |
| 23022 | } |
| 23023 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 23024 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 23025 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 23026 | output_json_string(p->out, pBlob, nBlob); |
| 23027 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 23028 | output_json_string(p->out, azArg[i], -1); |
| 23029 | }else{ |
| 23030 | sqlite3_fputs(azArg[i], p->out); |
| 23031 | } |
| 23032 | if( i<nArg-1 ){ |
| 23033 | sqlite3_fputs(",", p->out); |
| 23034 | } |
| 23035 | } |
| 23036 | sqlite3_fputs("}", p->out); |
| 23037 | break; |
| 23038 | } |
| 23039 | case MODE_Quote: { |
| 23040 | if( azArg==0 ) break; |
| 23041 | if( p->cnt==0 && p->showHeader ){ |
| 23042 | for(i=0; i<nArg; i++){ |
| 23043 | if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 23044 | output_quoted_string(p, azCol[i]); |
| 23045 | } |
| 23046 | sqlite3_fputs(p->rowSeparator, p->out); |
| 23047 | } |
| 23048 | p->cnt++; |
| 23049 | for(i=0; i<nArg; i++){ |
| 23050 | if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 23051 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 23052 | sqlite3_fputs("NULL", p->out); |
| 23053 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 23054 | output_quoted_string(p, azArg[i]); |
| 23055 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 23056 | sqlite3_fputs(azArg[i], p->out); |
| 23057 | }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 23058 | char z[50]; |
| 23059 | double r = sqlite3_column_double(p->pStmt, i); |
| 23060 | sqlite3_snprintf(50,z,"%!.20g", r); |
| 23061 | sqlite3_fputs(z, p->out); |
| 23062 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 23063 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 23064 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 23065 | output_hex_blob(p->out, pBlob, nBlob); |
| 23066 | }else if( isNumber(azArg[i], 0) ){ |
| 23067 | sqlite3_fputs(azArg[i], p->out); |
| 23068 | }else{ |
| 23069 | output_quoted_string(p, azArg[i]); |
| 23070 | } |
| 23071 | } |
| 23072 | sqlite3_fputs(p->rowSeparator, p->out); |
| 23073 | break; |
| 23074 | } |
| 23075 | case MODE_Ascii: { |
| 23076 | if( p->cnt++==0 && p->showHeader ){ |
| 23077 | for(i=0; i<nArg; i++){ |
| 23078 | if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 23079 | sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out); |
| 23080 | } |
| 23081 | sqlite3_fputs(p->rowSeparator, p->out); |
| 23082 | } |
| 23083 | if( azArg==0 ) break; |
| 23084 | for(i=0; i<nArg; i++){ |
| 23085 | if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 23086 | sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out); |
| 23087 | } |
| 23088 | sqlite3_fputs(p->rowSeparator, p->out); |
| 23089 | break; |
| 23090 | } |
| 23091 | case MODE_EQP: { |
| 23092 | eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); |
| 23093 | break; |
| @@ -21975,11 +23162,11 @@ | |
| 23162 | "INSERT INTO selftest(tno,op,cmd,ans)" |
| 23163 | " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" |
| 23164 | "DROP TABLE [_shell$self];" |
| 23165 | ,0,0,&zErrMsg); |
| 23166 | if( zErrMsg ){ |
| 23167 | sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); |
| 23168 | sqlite3_free(zErrMsg); |
| 23169 | } |
| 23170 | sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); |
| 23171 | } |
| 23172 | |
| @@ -22078,36 +23265,37 @@ | |
| 23265 | int i; |
| 23266 | const char *z; |
| 23267 | rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); |
| 23268 | if( rc!=SQLITE_OK || !pSelect ){ |
| 23269 | char *zContext = shell_error_context(zSelect, p->db); |
| 23270 | sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s", |
| 23271 | rc, sqlite3_errmsg(p->db), zContext); |
| 23272 | sqlite3_free(zContext); |
| 23273 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 23274 | return rc; |
| 23275 | } |
| 23276 | rc = sqlite3_step(pSelect); |
| 23277 | nResult = sqlite3_column_count(pSelect); |
| 23278 | while( rc==SQLITE_ROW ){ |
| 23279 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 23280 | sqlite3_fprintf(p->out, "%s", z); |
| 23281 | for(i=1; i<nResult; i++){ |
| 23282 | sqlite3_fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
| 23283 | } |
| 23284 | if( z==0 ) z = ""; |
| 23285 | while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; |
| 23286 | if( z[0] ){ |
| 23287 | sqlite3_fputs("\n;\n", p->out); |
| 23288 | }else{ |
| 23289 | sqlite3_fputs(";\n", p->out); |
| 23290 | } |
| 23291 | rc = sqlite3_step(pSelect); |
| 23292 | } |
| 23293 | rc = sqlite3_finalize(pSelect); |
| 23294 | if( rc!=SQLITE_OK ){ |
| 23295 | sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", |
| 23296 | rc, sqlite3_errmsg(p->db)); |
| 23297 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 23298 | } |
| 23299 | return rc; |
| 23300 | } |
| 23301 | |
| @@ -22139,17 +23327,17 @@ | |
| 23327 | |
| 23328 | #ifdef __linux__ |
| 23329 | /* |
| 23330 | ** Attempt to display I/O stats on Linux using /proc/PID/io |
| 23331 | */ |
| 23332 | static void displayLinuxIoStats(FILE *out){ |
| 23333 | FILE *in; |
| 23334 | char z[200]; |
| 23335 | sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); |
| 23336 | in = sqlite3_fopen(z, "rb"); |
| 23337 | if( in==0 ) return; |
| 23338 | while( sqlite3_fgets(z, sizeof(z), in)!=0 ){ |
| 23339 | static const struct { |
| 23340 | const char *zPattern; |
| 23341 | const char *zDesc; |
| 23342 | } aTrans[] = { |
| 23343 | { "rchar: ", "Bytes received by read():" }, |
| @@ -22162,11 +23350,11 @@ | |
| 23350 | }; |
| 23351 | int i; |
| 23352 | for(i=0; i<ArraySize(aTrans); i++){ |
| 23353 | int n = strlen30(aTrans[i].zPattern); |
| 23354 | if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){ |
| 23355 | sqlite3_fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); |
| 23356 | break; |
| 23357 | } |
| 23358 | } |
| 23359 | } |
| 23360 | fclose(in); |
| @@ -22175,10 +23363,11 @@ | |
| 23363 | |
| 23364 | /* |
| 23365 | ** Display a single line of status using 64-bit values. |
| 23366 | */ |
| 23367 | static void displayStatLine( |
| 23368 | FILE *out, /* Write to this channel */ |
| 23369 | char *zLabel, /* Label for this one line */ |
| 23370 | char *zFormat, /* Format for the result */ |
| 23371 | int iStatusCtrl, /* Which status to display */ |
| 23372 | int bReset /* True to reset the stats */ |
| 23373 | ){ |
| @@ -22193,11 +23382,11 @@ | |
| 23382 | if( nPercent>1 ){ |
| 23383 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); |
| 23384 | }else{ |
| 23385 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); |
| 23386 | } |
| 23387 | sqlite3_fprintf(out, "%-36s %s\n", zLabel, zLine); |
| 23388 | } |
| 23389 | |
| 23390 | /* |
| 23391 | ** Display memory stats. |
| 23392 | */ |
| @@ -22206,130 +23395,152 @@ | |
| 23395 | ShellState *pArg, /* Pointer to ShellState */ |
| 23396 | int bReset /* True to reset the stats */ |
| 23397 | ){ |
| 23398 | int iCur; |
| 23399 | int iHiwtr; |
| 23400 | FILE *out; |
| 23401 | if( pArg==0 || pArg->out==0 ) return 0; |
| 23402 | out = pArg->out; |
| 23403 | |
| 23404 | if( pArg->pStmt && pArg->statsOn==2 ){ |
| 23405 | int nCol, i, x; |
| 23406 | sqlite3_stmt *pStmt = pArg->pStmt; |
| 23407 | char z[100]; |
| 23408 | nCol = sqlite3_column_count(pStmt); |
| 23409 | sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol); |
| 23410 | for(i=0; i<nCol; i++){ |
| 23411 | sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); |
| 23412 | sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); |
| 23413 | #ifndef SQLITE_OMIT_DECLTYPE |
| 23414 | sqlite3_snprintf(30, z+x, "declared type:"); |
| 23415 | sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); |
| 23416 | #endif |
| 23417 | #ifdef SQLITE_ENABLE_COLUMN_METADATA |
| 23418 | sqlite3_snprintf(30, z+x, "database name:"); |
| 23419 | sqlite3_fprintf(out, "%-36s %s\n", z, |
| 23420 | sqlite3_column_database_name(pStmt,i)); |
| 23421 | sqlite3_snprintf(30, z+x, "table name:"); |
| 23422 | sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); |
| 23423 | sqlite3_snprintf(30, z+x, "origin name:"); |
| 23424 | sqlite3_fprintf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i)); |
| 23425 | #endif |
| 23426 | } |
| 23427 | } |
| 23428 | |
| 23429 | if( pArg->statsOn==3 ){ |
| 23430 | if( pArg->pStmt ){ |
| 23431 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); |
| 23432 | sqlite3_fprintf(out, "VM-steps: %d\n", iCur); |
| 23433 | } |
| 23434 | return 0; |
| 23435 | } |
| 23436 | |
| 23437 | displayStatLine(out, "Memory Used:", |
| 23438 | "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); |
| 23439 | displayStatLine(out, "Number of Outstanding Allocations:", |
| 23440 | "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); |
| 23441 | if( pArg->shellFlgs & SHFLG_Pagecache ){ |
| 23442 | displayStatLine(out, "Number of Pcache Pages Used:", |
| 23443 | "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); |
| 23444 | } |
| 23445 | displayStatLine(out, "Number of Pcache Overflow Bytes:", |
| 23446 | "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); |
| 23447 | displayStatLine(out, "Largest Allocation:", |
| 23448 | "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); |
| 23449 | displayStatLine(out, "Largest Pcache Allocation:", |
| 23450 | "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); |
| 23451 | #ifdef YYTRACKMAXSTACKDEPTH |
| 23452 | displayStatLine(out, "Deepest Parser Stack:", |
| 23453 | "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); |
| 23454 | #endif |
| 23455 | |
| 23456 | if( db ){ |
| 23457 | if( pArg->shellFlgs & SHFLG_Lookaside ){ |
| 23458 | iHiwtr = iCur = -1; |
| 23459 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, |
| 23460 | &iCur, &iHiwtr, bReset); |
| 23461 | sqlite3_fprintf(out, |
| 23462 | "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); |
| 23463 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, |
| 23464 | &iCur, &iHiwtr, bReset); |
| 23465 | sqlite3_fprintf(out, |
| 23466 | "Successful lookaside attempts: %d\n", iHiwtr); |
| 23467 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, |
| 23468 | &iCur, &iHiwtr, bReset); |
| 23469 | sqlite3_fprintf(out, |
| 23470 | "Lookaside failures due to size: %d\n", iHiwtr); |
| 23471 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, |
| 23472 | &iCur, &iHiwtr, bReset); |
| 23473 | sqlite3_fprintf(out, |
| 23474 | "Lookaside failures due to OOM: %d\n", iHiwtr); |
| 23475 | } |
| 23476 | iHiwtr = iCur = -1; |
| 23477 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); |
| 23478 | sqlite3_fprintf(out, |
| 23479 | "Pager Heap Usage: %d bytes\n", iCur); |
| 23480 | iHiwtr = iCur = -1; |
| 23481 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); |
| 23482 | sqlite3_fprintf(out, |
| 23483 | "Page cache hits: %d\n", iCur); |
| 23484 | iHiwtr = iCur = -1; |
| 23485 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); |
| 23486 | sqlite3_fprintf(out, |
| 23487 | "Page cache misses: %d\n", iCur); |
| 23488 | iHiwtr = iCur = -1; |
| 23489 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); |
| 23490 | sqlite3_fprintf(out, |
| 23491 | "Page cache writes: %d\n", iCur); |
| 23492 | iHiwtr = iCur = -1; |
| 23493 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); |
| 23494 | sqlite3_fprintf(out, |
| 23495 | "Page cache spills: %d\n", iCur); |
| 23496 | iHiwtr = iCur = -1; |
| 23497 | sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); |
| 23498 | sqlite3_fprintf(out, |
| 23499 | "Schema Heap Usage: %d bytes\n", iCur); |
| 23500 | iHiwtr = iCur = -1; |
| 23501 | sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); |
| 23502 | sqlite3_fprintf(out, |
| 23503 | "Statement Heap/Lookaside Usage: %d bytes\n", iCur); |
| 23504 | } |
| 23505 | |
| 23506 | if( pArg->pStmt ){ |
| 23507 | int iHit, iMiss; |
| 23508 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, |
| 23509 | bReset); |
| 23510 | sqlite3_fprintf(out, |
| 23511 | "Fullscan Steps: %d\n", iCur); |
| 23512 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 23513 | sqlite3_fprintf(out, |
| 23514 | "Sort Operations: %d\n", iCur); |
| 23515 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); |
| 23516 | sqlite3_fprintf(out, |
| 23517 | "Autoindex Inserts: %d\n", iCur); |
| 23518 | iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, |
| 23519 | bReset); |
| 23520 | iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, |
| 23521 | bReset); |
| 23522 | if( iHit || iMiss ){ |
| 23523 | sqlite3_fprintf(out, |
| 23524 | "Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); |
| 23525 | } |
| 23526 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 23527 | sqlite3_fprintf(out, |
| 23528 | "Virtual Machine Steps: %d\n", iCur); |
| 23529 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); |
| 23530 | sqlite3_fprintf(out, |
| 23531 | "Reprepare operations: %d\n", iCur); |
| 23532 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); |
| 23533 | sqlite3_fprintf(out, |
| 23534 | "Number of times run: %d\n", iCur); |
| 23535 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); |
| 23536 | sqlite3_fprintf(out, |
| 23537 | "Memory used by prepared stmt: %d\n", iCur); |
| 23538 | } |
| 23539 | |
| 23540 | #ifdef __linux__ |
| 23541 | displayLinuxIoStats(pArg->out); |
| 23542 | #endif |
| 23543 | |
| 23544 | /* Do not remove this machine readable comment: extra-stats-output-here */ |
| 23545 | |
| 23546 | return 0; |
| @@ -22721,21 +23932,21 @@ | |
| 23932 | #define BOX_1234 "\342\224\274" /* U+253c -|- */ |
| 23933 | |
| 23934 | /* Draw horizontal line N characters long using unicode box |
| 23935 | ** characters |
| 23936 | */ |
| 23937 | static void print_box_line(FILE *out, int N){ |
| 23938 | const char zDash[] = |
| 23939 | BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 |
| 23940 | BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; |
| 23941 | const int nDash = sizeof(zDash) - 1; |
| 23942 | N *= 3; |
| 23943 | while( N>nDash ){ |
| 23944 | sqlite3_fputs(zDash, out); |
| 23945 | N -= nDash; |
| 23946 | } |
| 23947 | sqlite3_fprintf(out, "%.*s", N, zDash); |
| 23948 | } |
| 23949 | |
| 23950 | /* |
| 23951 | ** Draw a horizontal separator for a MODE_Box table. |
| 23952 | */ |
| @@ -22746,19 +23957,19 @@ | |
| 23957 | const char *zSep2, |
| 23958 | const char *zSep3 |
| 23959 | ){ |
| 23960 | int i; |
| 23961 | if( nArg>0 ){ |
| 23962 | sqlite3_fputs(zSep1, p->out); |
| 23963 | print_box_line(p->out, p->actualWidth[0]+2); |
| 23964 | for(i=1; i<nArg; i++){ |
| 23965 | sqlite3_fputs(zSep2, p->out); |
| 23966 | print_box_line(p->out, p->actualWidth[i]+2); |
| 23967 | } |
| 23968 | sqlite3_fputs(zSep3, p->out); |
| 23969 | } |
| 23970 | sqlite3_fputs("\n", p->out); |
| 23971 | } |
| 23972 | |
| 23973 | /* |
| 23974 | ** z[] is a line of text that is to be displayed the .mode box or table or |
| 23975 | ** similar tabular formats. z[] might contain control characters such |
| @@ -22788,16 +23999,26 @@ | |
| 23999 | } |
| 24000 | if( mxWidth<0 ) mxWidth = -mxWidth; |
| 24001 | if( mxWidth==0 ) mxWidth = 1000000; |
| 24002 | i = j = n = 0; |
| 24003 | while( n<mxWidth ){ |
| 24004 | unsigned char c = z[i]; |
| 24005 | if( c>=0xc0 ){ |
| 24006 | int u; |
| 24007 | int len = decodeUtf8(&z[i], &u); |
| 24008 | i += len; |
| 24009 | j += len; |
| 24010 | n += cli_wcwidth(u); |
| 24011 | continue; |
| 24012 | } |
| 24013 | if( c>=' ' ){ |
| 24014 | n++; |
| 24015 | i++; |
| 24016 | j++; |
| 24017 | continue; |
| 24018 | } |
| 24019 | if( c=='\t' ){ |
| 24020 | do{ |
| 24021 | n++; |
| 24022 | j++; |
| 24023 | }while( (n&7)!=0 && n<mxWidth ); |
| 24024 | i++; |
| @@ -22835,13 +24056,21 @@ | |
| 24056 | } |
| 24057 | zOut = malloc( j+1 ); |
| 24058 | shell_check_oom(zOut); |
| 24059 | i = j = n = 0; |
| 24060 | while( i<k ){ |
| 24061 | unsigned char c = z[i]; |
| 24062 | if( c>=0xc0 ){ |
| 24063 | int u; |
| 24064 | int len = decodeUtf8(&z[i], &u); |
| 24065 | do{ zOut[j++] = z[i++]; }while( (--len)>0 ); |
| 24066 | n += cli_wcwidth(u); |
| 24067 | continue; |
| 24068 | } |
| 24069 | if( c>=' ' ){ |
| 24070 | n++; |
| 24071 | zOut[j++] = z[i++]; |
| 24072 | continue; |
| 24073 | } |
| 24074 | if( z[i]=='\t' ){ |
| 24075 | do{ |
| 24076 | n++; |
| @@ -23017,97 +24246,99 @@ | |
| 24246 | rowSep = "\n"; |
| 24247 | if( p->showHeader ){ |
| 24248 | for(i=0; i<nColumn; i++){ |
| 24249 | w = p->actualWidth[i]; |
| 24250 | if( p->colWidth[i]<0 ) w = -w; |
| 24251 | utf8_width_print(p->out, w, azData[i]); |
| 24252 | sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); |
| 24253 | } |
| 24254 | for(i=0; i<nColumn; i++){ |
| 24255 | print_dashes(p->out, p->actualWidth[i]); |
| 24256 | sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); |
| 24257 | } |
| 24258 | } |
| 24259 | break; |
| 24260 | } |
| 24261 | case MODE_Table: { |
| 24262 | colSep = " | "; |
| 24263 | rowSep = " |\n"; |
| 24264 | print_row_separator(p, nColumn, "+"); |
| 24265 | sqlite3_fputs("| ", p->out); |
| 24266 | for(i=0; i<nColumn; i++){ |
| 24267 | w = p->actualWidth[i]; |
| 24268 | n = strlenChar(azData[i]); |
| 24269 | sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", |
| 24270 | azData[i], (w-n+1)/2, ""); |
| 24271 | sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); |
| 24272 | } |
| 24273 | print_row_separator(p, nColumn, "+"); |
| 24274 | break; |
| 24275 | } |
| 24276 | case MODE_Markdown: { |
| 24277 | colSep = " | "; |
| 24278 | rowSep = " |\n"; |
| 24279 | sqlite3_fputs("| ", p->out); |
| 24280 | for(i=0; i<nColumn; i++){ |
| 24281 | w = p->actualWidth[i]; |
| 24282 | n = strlenChar(azData[i]); |
| 24283 | sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", |
| 24284 | azData[i], (w-n+1)/2, ""); |
| 24285 | sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); |
| 24286 | } |
| 24287 | print_row_separator(p, nColumn, "|"); |
| 24288 | break; |
| 24289 | } |
| 24290 | case MODE_Box: { |
| 24291 | colSep = " " BOX_13 " "; |
| 24292 | rowSep = " " BOX_13 "\n"; |
| 24293 | print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); |
| 24294 | sqlite3_fputs(BOX_13 " ", p->out); |
| 24295 | for(i=0; i<nColumn; i++){ |
| 24296 | w = p->actualWidth[i]; |
| 24297 | n = strlenChar(azData[i]); |
| 24298 | sqlite3_fprintf(p->out, "%*s%s%*s%s", |
| 24299 | (w-n)/2, "", azData[i], (w-n+1)/2, "", |
| 24300 | i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); |
| 24301 | } |
| 24302 | print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 24303 | break; |
| 24304 | } |
| 24305 | } |
| 24306 | for(i=nColumn, j=0; i<nTotal; i++, j++){ |
| 24307 | if( j==0 && p->cMode!=MODE_Column ){ |
| 24308 | sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out); |
| 24309 | } |
| 24310 | z = azData[i]; |
| 24311 | if( z==0 ) z = p->nullValue; |
| 24312 | w = p->actualWidth[j]; |
| 24313 | if( p->colWidth[j]<0 ) w = -w; |
| 24314 | utf8_width_print(p->out, w, z); |
| 24315 | if( j==nColumn-1 ){ |
| 24316 | sqlite3_fputs(rowSep, p->out); |
| 24317 | if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){ |
| 24318 | if( p->cMode==MODE_Table ){ |
| 24319 | print_row_separator(p, nColumn, "+"); |
| 24320 | }else if( p->cMode==MODE_Box ){ |
| 24321 | print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 24322 | }else if( p->cMode==MODE_Column ){ |
| 24323 | sqlite3_fputs("\n", p->out); |
| 24324 | } |
| 24325 | } |
| 24326 | j = -1; |
| 24327 | if( seenInterrupt ) goto columnar_end; |
| 24328 | }else{ |
| 24329 | sqlite3_fputs(colSep, p->out); |
| 24330 | } |
| 24331 | } |
| 24332 | if( p->cMode==MODE_Table ){ |
| 24333 | print_row_separator(p, nColumn, "+"); |
| 24334 | }else if( p->cMode==MODE_Box ){ |
| 24335 | print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); |
| 24336 | } |
| 24337 | columnar_end: |
| 24338 | if( seenInterrupt ){ |
| 24339 | sqlite3_fputs("Interrupt\n", p->out); |
| 24340 | } |
| 24341 | nData = (nRow+1)*nColumn; |
| 24342 | for(i=0; i<nData; i++){ |
| 24343 | z = azData[i]; |
| 24344 | if( z!=zEmpty && z!=zShowNull ) free(azData[i]); |
| @@ -23190,11 +24421,13 @@ | |
| 24421 | } |
| 24422 | } |
| 24423 | } while( SQLITE_ROW == rc ); |
| 24424 | sqlite3_free(pData); |
| 24425 | if( pArg->cMode==MODE_Json ){ |
| 24426 | sqlite3_fputs("]\n", pArg->out); |
| 24427 | }else if( pArg->cMode==MODE_Www ){ |
| 24428 | sqlite3_fputs("</TABLE>\n<PRE>\n", pArg->out); |
| 24429 | }else if( pArg->cMode==MODE_Count ){ |
| 24430 | char zBuf[200]; |
| 24431 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n", |
| 24432 | nRow, nRow!=1 ? "s" : ""); |
| 24433 | printf("%s", zBuf); |
| @@ -23239,10 +24472,11 @@ | |
| 24472 | int bCancel, |
| 24473 | char **pzErr |
| 24474 | ){ |
| 24475 | int rc = SQLITE_OK; |
| 24476 | sqlite3expert *p = pState->expert.pExpert; |
| 24477 | FILE *out = pState->out; |
| 24478 | assert( p ); |
| 24479 | assert( bCancel || pzErr==0 || *pzErr==0 ); |
| 24480 | if( bCancel==0 ){ |
| 24481 | int bVerbose = pState->expert.bVerbose; |
| 24482 | |
| @@ -23251,24 +24485,25 @@ | |
| 24485 | int nQuery = sqlite3_expert_count(p); |
| 24486 | int i; |
| 24487 | |
| 24488 | if( bVerbose ){ |
| 24489 | const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); |
| 24490 | sqlite3_fputs("-- Candidates -----------------------------\n", out); |
| 24491 | sqlite3_fprintf(out, "%s\n", zCand); |
| 24492 | } |
| 24493 | for(i=0; i<nQuery; i++){ |
| 24494 | const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL); |
| 24495 | const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES); |
| 24496 | const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN); |
| 24497 | if( zIdx==0 ) zIdx = "(no new indexes)\n"; |
| 24498 | if( bVerbose ){ |
| 24499 | sqlite3_fprintf(out, |
| 24500 | "-- Query %d --------------------------------\n" |
| 24501 | "%s\n\n" |
| 24502 | ,i+1, zSql); |
| 24503 | } |
| 24504 | sqlite3_fprintf(out, "%s\n%s\n", zIdx, zEQP); |
| 24505 | } |
| 24506 | } |
| 24507 | } |
| 24508 | sqlite3_expert_destroy(p); |
| 24509 | pState->expert.pExpert = 0; |
| @@ -23299,30 +24534,31 @@ | |
| 24534 | if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){ |
| 24535 | pState->expert.bVerbose = 1; |
| 24536 | } |
| 24537 | else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){ |
| 24538 | if( i==(nArg-1) ){ |
| 24539 | sqlite3_fprintf(stderr, "option requires an argument: %s\n", z); |
| 24540 | rc = SQLITE_ERROR; |
| 24541 | }else{ |
| 24542 | iSample = (int)integerValue(azArg[++i]); |
| 24543 | if( iSample<0 || iSample>100 ){ |
| 24544 | sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]); |
| 24545 | rc = SQLITE_ERROR; |
| 24546 | } |
| 24547 | } |
| 24548 | } |
| 24549 | else{ |
| 24550 | sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 24551 | rc = SQLITE_ERROR; |
| 24552 | } |
| 24553 | } |
| 24554 | |
| 24555 | if( rc==SQLITE_OK ){ |
| 24556 | pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); |
| 24557 | if( pState->expert.pExpert==0 ){ |
| 24558 | sqlite3_fprintf(stderr, |
| 24559 | "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); |
| 24560 | rc = SQLITE_ERROR; |
| 24561 | }else{ |
| 24562 | sqlite3_expert_config( |
| 24563 | pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample |
| 24564 | ); |
| @@ -23647,33 +24883,33 @@ | |
| 24883 | if( zType==0 ) return 0; |
| 24884 | dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0; |
| 24885 | noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; |
| 24886 | |
| 24887 | if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ |
| 24888 | /* no-op */ |
| 24889 | }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ |
| 24890 | if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); |
| 24891 | }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ |
| 24892 | return 0; |
| 24893 | }else if( dataOnly ){ |
| 24894 | /* no-op */ |
| 24895 | }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ |
| 24896 | char *zIns; |
| 24897 | if( !p->writableSchema ){ |
| 24898 | sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out); |
| 24899 | p->writableSchema = 1; |
| 24900 | } |
| 24901 | zIns = sqlite3_mprintf( |
| 24902 | "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" |
| 24903 | "VALUES('table','%q','%q',0,'%q');", |
| 24904 | zTable, zTable, zSql); |
| 24905 | shell_check_oom(zIns); |
| 24906 | sqlite3_fprintf(p->out, "%s\n", zIns); |
| 24907 | sqlite3_free(zIns); |
| 24908 | return 0; |
| 24909 | }else{ |
| 24910 | printSchemaLine(p->out, zSql, ";\n"); |
| 24911 | } |
| 24912 | |
| 24913 | if( cli_strcmp(zType, "table")==0 ){ |
| 24914 | ShellText sSelect; |
| 24915 | ShellText sTable; |
| @@ -23727,11 +24963,11 @@ | |
| 24963 | savedMode = p->mode; |
| 24964 | p->zDestTable = sTable.z; |
| 24965 | p->mode = p->cMode = MODE_Insert; |
| 24966 | rc = shell_exec(p, sSelect.z, 0); |
| 24967 | if( (rc&0xff)==SQLITE_CORRUPT ){ |
| 24968 | sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); |
| 24969 | toggleSelectOrder(p->db); |
| 24970 | shell_exec(p, sSelect.z, 0); |
| 24971 | toggleSelectOrder(p->db); |
| 24972 | } |
| 24973 | p->zDestTable = savedDestTable; |
| @@ -23758,28 +24994,28 @@ | |
| 24994 | char *zErr = 0; |
| 24995 | rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); |
| 24996 | if( rc==SQLITE_CORRUPT ){ |
| 24997 | char *zQ2; |
| 24998 | int len = strlen30(zQuery); |
| 24999 | sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); |
| 25000 | if( zErr ){ |
| 25001 | sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr); |
| 25002 | sqlite3_free(zErr); |
| 25003 | zErr = 0; |
| 25004 | } |
| 25005 | zQ2 = malloc( len+100 ); |
| 25006 | if( zQ2==0 ) return rc; |
| 25007 | sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); |
| 25008 | rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); |
| 25009 | if( rc ){ |
| 25010 | sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); |
| 25011 | }else{ |
| 25012 | rc = SQLITE_CORRUPT; |
| 25013 | } |
| 25014 | free(zQ2); |
| 25015 | } |
| 25016 | sqlite3_free(zErr); |
| 25017 | return rc; |
| 25018 | } |
| 25019 | |
| 25020 | /* |
| 25021 | ** Text of help messages. |
| @@ -23832,18 +25068,17 @@ | |
| 25068 | #ifndef SQLITE_SHELL_FIDDLE |
| 25069 | ".check GLOB Fail if output since .testcase does not match", |
| 25070 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 25071 | #endif |
| 25072 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 25073 | ".crlf ?on|off? Whether or not to use \\r\\n line endings", |
| 25074 | ".databases List names and files of attached databases", |
| 25075 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 25076 | #if SQLITE_SHELL_HAVE_RECOVER |
| 25077 | ".dbinfo ?DB? Show status information about the database", |
| 25078 | #endif |
| 25079 | ".dbtotxt Hex dump of the database file", |
| 25080 | ".dump ?OBJECTS? Render database content as SQL", |
| 25081 | " Options:", |
| 25082 | " --data-only Output only INSERT statements", |
| 25083 | " --newlines Allow unescaped newline characters in output", |
| 25084 | " --nosys Omit system tables (ex: \"sqlite_stat1\")", |
| @@ -23940,13 +25175,15 @@ | |
| 25175 | #endif |
| 25176 | ".nullvalue STRING Use STRING in place of NULL values", |
| 25177 | #ifndef SQLITE_SHELL_FIDDLE |
| 25178 | ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", |
| 25179 | " If FILE begins with '|' then open as a pipe", |
| 25180 | " --bom Put a UTF8 byte-order mark at the beginning", |
| 25181 | " -e Send output to the system text editor", |
| 25182 | " --plain Use text/plain output instead of HTML for -w option", |
| 25183 | " -w Send output as HTML to a web browser (same as \".www\")", |
| 25184 | " -x Send output as CSV to a spreadsheet (same as \".excel\")", |
| 25185 | /* Note that .open is (partially) available in WASM builds but is |
| 25186 | ** currently only intended to be used by the fiddle tool, not |
| 25187 | ** end users, so is "undocumented." */ |
| 25188 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 25189 | " Options:", |
| @@ -23965,10 +25202,12 @@ | |
| 25202 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 25203 | " If FILE begins with '|' then open it as a pipe.", |
| 25204 | " Options:", |
| 25205 | " --bom Prefix output with a UTF8 byte-order mark", |
| 25206 | " -e Send output to the system text editor", |
| 25207 | " --plain Use text/plain for -w option", |
| 25208 | " -w Send output to a web browser", |
| 25209 | " -x Send output as CSV to a spreadsheet", |
| 25210 | #endif |
| 25211 | ".parameter CMD ... Manage SQL parameter bindings", |
| 25212 | " clear Erase all bindings", |
| 25213 | " init Initialize the TEMP table that holds bindings", |
| @@ -24078,10 +25317,14 @@ | |
| 25317 | ".vfsinfo ?AUX? Information about the top-level VFS", |
| 25318 | ".vfslist List all available VFSes", |
| 25319 | ".vfsname ?AUX? Print the name of the VFS stack", |
| 25320 | ".width NUM1 NUM2 ... Set minimum column widths for columnar output", |
| 25321 | " Negative values right-justify", |
| 25322 | #ifndef SQLITE_SHELL_FIDDLE |
| 25323 | ".www Display output of the next command in web browser", |
| 25324 | " --plain Show results as text/plain, not as HTML", |
| 25325 | #endif |
| 25326 | }; |
| 25327 | |
| 25328 | /* |
| 25329 | ** Output help text. |
| 25330 | ** |
| @@ -24126,24 +25369,24 @@ | |
| 25369 | hh &= ~HH_Summary; |
| 25370 | break; |
| 25371 | } |
| 25372 | if( ((hw^hh)&HH_Undoc)==0 ){ |
| 25373 | if( (hh&HH_Summary)!=0 ){ |
| 25374 | sqlite3_fprintf(out, ".%s\n", azHelp[i]+1); |
| 25375 | ++n; |
| 25376 | }else if( (hw&HW_SummaryOnly)==0 ){ |
| 25377 | sqlite3_fprintf(out, "%s\n", azHelp[i]); |
| 25378 | } |
| 25379 | } |
| 25380 | } |
| 25381 | }else{ |
| 25382 | /* Seek documented commands for which zPattern is an exact prefix */ |
| 25383 | zPat = sqlite3_mprintf(".%s*", zPattern); |
| 25384 | shell_check_oom(zPat); |
| 25385 | for(i=0; i<ArraySize(azHelp); i++){ |
| 25386 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 25387 | sqlite3_fprintf(out, "%s\n", azHelp[i]); |
| 25388 | j = i+1; |
| 25389 | n++; |
| 25390 | } |
| 25391 | } |
| 25392 | sqlite3_free(zPat); |
| @@ -24150,11 +25393,11 @@ | |
| 25393 | if( n ){ |
| 25394 | if( n==1 ){ |
| 25395 | /* when zPattern is a prefix of exactly one command, then include |
| 25396 | ** the details of that command, which should begin at offset j */ |
| 25397 | while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
| 25398 | sqlite3_fprintf(out, "%s\n", azHelp[j]); |
| 25399 | j++; |
| 25400 | } |
| 25401 | } |
| 25402 | return n; |
| 25403 | } |
| @@ -24167,14 +25410,14 @@ | |
| 25410 | while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; |
| 25411 | continue; |
| 25412 | } |
| 25413 | if( azHelp[i][0]=='.' ) j = i; |
| 25414 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 25415 | sqlite3_fprintf(out, "%s\n", azHelp[j]); |
| 25416 | while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ |
| 25417 | j++; |
| 25418 | sqlite3_fprintf(out, "%s\n", azHelp[j]); |
| 25419 | } |
| 25420 | i = j; |
| 25421 | n++; |
| 25422 | } |
| 25423 | } |
| @@ -24200,35 +25443,35 @@ | |
| 25443 | ** |
| 25444 | ** NULL is returned if any error is encountered. The final value of *pnByte |
| 25445 | ** is undefined in this case. |
| 25446 | */ |
| 25447 | static char *readFile(const char *zName, int *pnByte){ |
| 25448 | FILE *in = sqlite3_fopen(zName, "rb"); |
| 25449 | long nIn; |
| 25450 | size_t nRead; |
| 25451 | char *pBuf; |
| 25452 | int rc; |
| 25453 | if( in==0 ) return 0; |
| 25454 | rc = fseek(in, 0, SEEK_END); |
| 25455 | if( rc!=0 ){ |
| 25456 | sqlite3_fprintf(stderr,"Error: '%s' not seekable\n", zName); |
| 25457 | fclose(in); |
| 25458 | return 0; |
| 25459 | } |
| 25460 | nIn = ftell(in); |
| 25461 | rewind(in); |
| 25462 | pBuf = sqlite3_malloc64( nIn+1 ); |
| 25463 | if( pBuf==0 ){ |
| 25464 | sqlite3_fputs("Error: out of memory\n", stderr); |
| 25465 | fclose(in); |
| 25466 | return 0; |
| 25467 | } |
| 25468 | nRead = fread(pBuf, nIn, 1, in); |
| 25469 | fclose(in); |
| 25470 | if( nRead!=1 ){ |
| 25471 | sqlite3_free(pBuf); |
| 25472 | sqlite3_fprintf(stderr,"Error: cannot read '%s'\n", zName); |
| 25473 | return 0; |
| 25474 | } |
| 25475 | pBuf[nIn] = 0; |
| 25476 | if( pnByte ) *pnByte = nIn; |
| 25477 | return pBuf; |
| @@ -24290,11 +25533,11 @@ | |
| 25533 | ** archive and the dfltZip flag is true, then assume it is a ZIP archive. |
| 25534 | ** Otherwise, assume an ordinary database regardless of the filename if |
| 25535 | ** the type cannot be determined from content. |
| 25536 | */ |
| 25537 | int deduceDatabaseType(const char *zName, int dfltZip){ |
| 25538 | FILE *f = sqlite3_fopen(zName, "rb"); |
| 25539 | size_t n; |
| 25540 | int rc = SHELL_OPEN_UNSPEC; |
| 25541 | char zBuf[100]; |
| 25542 | if( f==0 ){ |
| 25543 | if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ |
| @@ -24343,13 +25586,13 @@ | |
| 25586 | FILE *in; |
| 25587 | const char *zDbFilename = p->pAuxDb->zDbFilename; |
| 25588 | unsigned int x[16]; |
| 25589 | char zLine[1000]; |
| 25590 | if( zDbFilename ){ |
| 25591 | in = sqlite3_fopen(zDbFilename, "r"); |
| 25592 | if( in==0 ){ |
| 25593 | sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename); |
| 25594 | return 0; |
| 25595 | } |
| 25596 | nLine = 0; |
| 25597 | }else{ |
| 25598 | in = p->in; |
| @@ -24356,24 +25599,24 @@ | |
| 25599 | nLine = p->lineno; |
| 25600 | if( in==0 ) in = stdin; |
| 25601 | } |
| 25602 | *pnData = 0; |
| 25603 | nLine++; |
| 25604 | if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; |
| 25605 | rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); |
| 25606 | if( rc!=2 ) goto readHexDb_error; |
| 25607 | if( n<0 ) goto readHexDb_error; |
| 25608 | if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error; |
| 25609 | n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */ |
| 25610 | a = sqlite3_malloc( n ? n : 1 ); |
| 25611 | shell_check_oom(a); |
| 25612 | memset(a, 0, n); |
| 25613 | if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ |
| 25614 | sqlite3_fputs("invalid pagesize\n", stderr); |
| 25615 | goto readHexDb_error; |
| 25616 | } |
| 25617 | for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){ |
| 25618 | rc = sscanf(zLine, "| page %d offset %d", &j, &k); |
| 25619 | if( rc==2 ){ |
| 25620 | iOffset = k; |
| 25621 | continue; |
| 25622 | } |
| @@ -24401,18 +25644,18 @@ | |
| 25644 | |
| 25645 | readHexDb_error: |
| 25646 | if( in!=p->in ){ |
| 25647 | fclose(in); |
| 25648 | }else{ |
| 25649 | while( sqlite3_fgets(zLine, sizeof(zLine), p->in)!=0 ){ |
| 25650 | nLine++; |
| 25651 | if(cli_strncmp(zLine, "| end ", 6)==0 ) break; |
| 25652 | } |
| 25653 | p->lineno = nLine; |
| 25654 | } |
| 25655 | sqlite3_free(a); |
| 25656 | sqlite3_fprintf(stderr,"Error on line %d of --hexdb input\n", nLine); |
| 25657 | return 0; |
| 25658 | } |
| 25659 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 25660 | |
| 25661 | /* |
| @@ -24483,22 +25726,24 @@ | |
| 25726 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); |
| 25727 | break; |
| 25728 | } |
| 25729 | } |
| 25730 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 25731 | sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n", |
| 25732 | zDbFilename, sqlite3_errmsg(p->db)); |
| 25733 | if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ |
| 25734 | exit(1); |
| 25735 | } |
| 25736 | sqlite3_close(p->db); |
| 25737 | sqlite3_open(":memory:", &p->db); |
| 25738 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 25739 | sqlite3_fputs("Also: unable to open substitute in-memory database.\n", |
| 25740 | stderr); |
| 25741 | exit(1); |
| 25742 | }else{ |
| 25743 | sqlite3_fprintf(stderr, |
| 25744 | "Notice: using substitute in-memory database instead of \"%s\"\n", |
| 25745 | zDbFilename); |
| 25746 | } |
| 25747 | } |
| 25748 | globalDb = p->db; |
| 25749 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); |
| @@ -24511,10 +25756,11 @@ | |
| 25756 | } |
| 25757 | |
| 25758 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 25759 | sqlite3_enable_load_extension(p->db, 1); |
| 25760 | #endif |
| 25761 | sqlite3_sha_init(p->db, 0, 0); |
| 25762 | sqlite3_shathree_init(p->db, 0, 0); |
| 25763 | sqlite3_uint_init(p->db, 0, 0); |
| 25764 | sqlite3_stmtrand_init(p->db, 0, 0); |
| 25765 | sqlite3_decimal_init(p->db, 0, 0); |
| 25766 | sqlite3_percentile_init(p->db, 0, 0); |
| @@ -24605,11 +25851,11 @@ | |
| 25851 | } |
| 25852 | rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, |
| 25853 | SQLITE_DESERIALIZE_RESIZEABLE | |
| 25854 | SQLITE_DESERIALIZE_FREEONCLOSE); |
| 25855 | if( rc ){ |
| 25856 | sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc); |
| 25857 | } |
| 25858 | if( p->szMax>0 ){ |
| 25859 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); |
| 25860 | } |
| 25861 | } |
| @@ -24629,15 +25875,17 @@ | |
| 25875 | ** Attempt to close the database connection. Report errors. |
| 25876 | */ |
| 25877 | void close_db(sqlite3 *db){ |
| 25878 | int rc = sqlite3_close(db); |
| 25879 | if( rc ){ |
| 25880 | sqlite3_fprintf(stderr, |
| 25881 | "Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); |
| 25882 | } |
| 25883 | } |
| 25884 | |
| 25885 | #if (HAVE_READLINE || HAVE_EDITLINE) \ |
| 25886 | && !defined(SQLITE_OMIT_READLINE_COMPLETION) |
| 25887 | /* |
| 25888 | ** Readline completion callbacks |
| 25889 | */ |
| 25890 | static char *readline_completion_generator(const char *text, int state){ |
| 25891 | static sqlite3_stmt *pStmt = 0; |
| @@ -24671,19 +25919,26 @@ | |
| 25919 | #elif HAVE_LINENOISE |
| 25920 | /* |
| 25921 | ** Linenoise completion callback. Note that the 3rd argument is from |
| 25922 | ** the "msteveb" version of linenoise, not the "antirez" version. |
| 25923 | */ |
| 25924 | static void linenoise_completion( |
| 25925 | const char *zLine, |
| 25926 | linenoiseCompletions *lc |
| 25927 | #if HAVE_LINENOISE==2 |
| 25928 | ,void *pUserData |
| 25929 | #endif |
| 25930 | ){ |
| 25931 | i64 nLine = strlen(zLine); |
| 25932 | i64 i, iStart; |
| 25933 | sqlite3_stmt *pStmt = 0; |
| 25934 | char *zSql; |
| 25935 | char zBuf[1000]; |
| 25936 | |
| 25937 | #if HAVE_LINENOISE==2 |
| 25938 | UNUSED_PARAMETER(pUserData); |
| 25939 | #endif |
| 25940 | if( nLine>(i64)sizeof(zBuf)-30 ) return; |
| 25941 | if( zLine[0]=='.' || zLine[0]=='#') return; |
| 25942 | for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} |
| 25943 | if( i==nLine-1 ) return; |
| 25944 | iStart = i+1; |
| @@ -24793,11 +26048,12 @@ | |
| 26048 | return 1; |
| 26049 | } |
| 26050 | if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ |
| 26051 | return 0; |
| 26052 | } |
| 26053 | sqlite3_fprintf(stderr, |
| 26054 | "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); |
| 26055 | return 0; |
| 26056 | } |
| 26057 | |
| 26058 | /* |
| 26059 | ** Set or clear a shell flag according to a boolean value. |
| @@ -24820,22 +26076,22 @@ | |
| 26076 | /* |
| 26077 | ** Try to open an output file. The names "stdout" and "stderr" are |
| 26078 | ** recognized and do the right thing. NULL is returned if the output |
| 26079 | ** filename is "off". |
| 26080 | */ |
| 26081 | static FILE *output_file_open(const char *zFile){ |
| 26082 | FILE *f; |
| 26083 | if( cli_strcmp(zFile,"stdout")==0 ){ |
| 26084 | f = stdout; |
| 26085 | }else if( cli_strcmp(zFile, "stderr")==0 ){ |
| 26086 | f = stderr; |
| 26087 | }else if( cli_strcmp(zFile, "off")==0 ){ |
| 26088 | f = 0; |
| 26089 | }else{ |
| 26090 | f = sqlite3_fopen(zFile, "w"); |
| 26091 | if( f==0 ){ |
| 26092 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 26093 | } |
| 26094 | } |
| 26095 | return f; |
| 26096 | } |
| 26097 | |
| @@ -24884,16 +26140,17 @@ | |
| 26140 | if( nSql>1000000000 ) nSql = 1000000000; |
| 26141 | while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; } |
| 26142 | switch( mType ){ |
| 26143 | case SQLITE_TRACE_ROW: |
| 26144 | case SQLITE_TRACE_STMT: { |
| 26145 | sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 26146 | break; |
| 26147 | } |
| 26148 | case SQLITE_TRACE_PROFILE: { |
| 26149 | sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; |
| 26150 | sqlite3_fprintf(p->traceOut, |
| 26151 | "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); |
| 26152 | break; |
| 26153 | } |
| 26154 | } |
| 26155 | return 0; |
| 26156 | } |
| @@ -24996,14 +26253,15 @@ | |
| 26253 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 26254 | p->cTerm = c; |
| 26255 | break; |
| 26256 | } |
| 26257 | if( pc==cQuote && c!='\r' ){ |
| 26258 | sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n", |
| 26259 | p->zFile, p->nLine, cQuote); |
| 26260 | } |
| 26261 | if( c==EOF ){ |
| 26262 | sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n", |
| 26263 | p->zFile, startLine, cQuote); |
| 26264 | p->cTerm = c; |
| 26265 | break; |
| 26266 | } |
| 26267 | import_append_char(p, c); |
| @@ -25098,11 +26356,11 @@ | |
| 26356 | |
| 26357 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); |
| 26358 | shell_check_oom(zQuery); |
| 26359 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26360 | if( rc ){ |
| 26361 | sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", |
| 26362 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 26363 | goto end_data_xfer; |
| 26364 | } |
| 26365 | n = sqlite3_column_count(pQuery); |
| 26366 | zInsert = sqlite3_malloc64(200 + nTable + n*3); |
| @@ -25115,11 +26373,11 @@ | |
| 26373 | i += 2; |
| 26374 | } |
| 26375 | memcpy(zInsert+i, ");", 3); |
| 26376 | rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); |
| 26377 | if( rc ){ |
| 26378 | sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", |
| 26379 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); |
| 26380 | goto end_data_xfer; |
| 26381 | } |
| 26382 | for(k=0; k<2; k++){ |
| 26383 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| @@ -25151,11 +26409,11 @@ | |
| 26409 | } |
| 26410 | } |
| 26411 | } /* End for */ |
| 26412 | rc = sqlite3_step(pInsert); |
| 26413 | if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 26414 | sqlite3_fprintf(stderr,"Error %d: %s\n", |
| 26415 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); |
| 26416 | } |
| 26417 | sqlite3_reset(pInsert); |
| 26418 | cnt++; |
| 26419 | if( (cnt%spinRate)==0 ){ |
| @@ -25169,11 +26427,11 @@ | |
| 26427 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", |
| 26428 | zTable); |
| 26429 | shell_check_oom(zQuery); |
| 26430 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26431 | if( rc ){ |
| 26432 | sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable); |
| 26433 | break; |
| 26434 | } |
| 26435 | } /* End for(k=0...) */ |
| 26436 | |
| 26437 | end_data_xfer: |
| @@ -25206,23 +26464,24 @@ | |
| 26464 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 26465 | " WHERE %s ORDER BY rowid ASC", zWhere); |
| 26466 | shell_check_oom(zQuery); |
| 26467 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26468 | if( rc ){ |
| 26469 | sqlite3_fprintf(stderr, |
| 26470 | "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), |
| 26471 | sqlite3_errmsg(p->db), zQuery); |
| 26472 | goto end_schema_xfer; |
| 26473 | } |
| 26474 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 26475 | zName = sqlite3_column_text(pQuery, 0); |
| 26476 | zSql = sqlite3_column_text(pQuery, 1); |
| 26477 | if( zName==0 || zSql==0 ) continue; |
| 26478 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ |
| 26479 | sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); |
| 26480 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 26481 | if( zErrMsg ){ |
| 26482 | sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 26483 | sqlite3_free(zErrMsg); |
| 26484 | zErrMsg = 0; |
| 26485 | } |
| 26486 | } |
| 26487 | if( xForEach ){ |
| @@ -25236,23 +26495,23 @@ | |
| 26495 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 26496 | " WHERE %s ORDER BY rowid DESC", zWhere); |
| 26497 | shell_check_oom(zQuery); |
| 26498 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26499 | if( rc ){ |
| 26500 | sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n", |
| 26501 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 26502 | goto end_schema_xfer; |
| 26503 | } |
| 26504 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 26505 | zName = sqlite3_column_text(pQuery, 0); |
| 26506 | zSql = sqlite3_column_text(pQuery, 1); |
| 26507 | if( zName==0 || zSql==0 ) continue; |
| 26508 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; |
| 26509 | sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); |
| 26510 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 26511 | if( zErrMsg ){ |
| 26512 | sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 26513 | sqlite3_free(zErrMsg); |
| 26514 | zErrMsg = 0; |
| 26515 | } |
| 26516 | if( xForEach ){ |
| 26517 | xForEach(p, newDb, (const char*)zName); |
| @@ -25272,16 +26531,17 @@ | |
| 26531 | */ |
| 26532 | static void tryToClone(ShellState *p, const char *zNewDb){ |
| 26533 | int rc; |
| 26534 | sqlite3 *newDb = 0; |
| 26535 | if( access(zNewDb,0)==0 ){ |
| 26536 | sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb); |
| 26537 | return; |
| 26538 | } |
| 26539 | rc = sqlite3_open(zNewDb, &newDb); |
| 26540 | if( rc ){ |
| 26541 | sqlite3_fprintf(stderr, |
| 26542 | "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); |
| 26543 | }else{ |
| 26544 | sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); |
| 26545 | sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); |
| 26546 | tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); |
| 26547 | tryToCloneSchema(p, newDb, "type!='table'", 0); |
| @@ -25294,14 +26554,22 @@ | |
| 26554 | #ifndef SQLITE_SHELL_FIDDLE |
| 26555 | /* |
| 26556 | ** Change the output stream (file or pipe or console) to something else. |
| 26557 | */ |
| 26558 | static void output_redir(ShellState *p, FILE *pfNew){ |
| 26559 | if( p->out != stdout ){ |
| 26560 | sqlite3_fputs("Output already redirected.\n", stderr); |
| 26561 | }else{ |
| 26562 | p->out = pfNew; |
| 26563 | setCrlfMode(p); |
| 26564 | if( p->mode==MODE_Www ){ |
| 26565 | sqlite3_fputs( |
| 26566 | "<!DOCTYPE html>\n" |
| 26567 | "<HTML><BODY><PRE>\n", |
| 26568 | p->out |
| 26569 | ); |
| 26570 | } |
| 26571 | } |
| 26572 | } |
| 26573 | |
| 26574 | /* |
| 26575 | ** Change the output file back to stdout. |
| @@ -25314,10 +26582,13 @@ | |
| 26582 | if( p->outfile[0]=='|' ){ |
| 26583 | #ifndef SQLITE_OMIT_POPEN |
| 26584 | pclose(p->out); |
| 26585 | #endif |
| 26586 | }else{ |
| 26587 | if( p->mode==MODE_Www ){ |
| 26588 | sqlite3_fputs("</PRE></BODY></HTML>\n", p->out); |
| 26589 | } |
| 26590 | output_file_close(p->out); |
| 26591 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 26592 | if( p->doXdgOpen ){ |
| 26593 | const char *zXdgOpenCmd = |
| 26594 | #if defined(_WIN32) |
| @@ -25328,11 +26599,11 @@ | |
| 26599 | "xdg-open"; |
| 26600 | #endif |
| 26601 | char *zCmd; |
| 26602 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 26603 | if( system(zCmd) ){ |
| 26604 | sqlite3_fprintf(stderr,"Failed: [%s]\n", zCmd); |
| 26605 | }else{ |
| 26606 | /* Give the start/open/xdg-open command some time to get |
| 26607 | ** going before we continue, and potential delete the |
| 26608 | ** p->zTempFile data file out from under it */ |
| 26609 | sqlite3_sleep(2000); |
| @@ -25343,28 +26614,34 @@ | |
| 26614 | } |
| 26615 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 26616 | } |
| 26617 | p->outfile[0] = 0; |
| 26618 | p->out = stdout; |
| 26619 | setCrlfMode(p); |
| 26620 | } |
| 26621 | #else |
| 26622 | # define output_redir(SS,pfO) |
| 26623 | # define output_reset(SS) |
| 26624 | #endif |
| 26625 | |
| 26626 | /* |
| 26627 | ** Run an SQL command and return the single integer result. |
| 26628 | */ |
| 26629 | static int db_int(sqlite3 *db, const char *zSql, ...){ |
| 26630 | sqlite3_stmt *pStmt; |
| 26631 | int res = 0; |
| 26632 | char *z; |
| 26633 | va_list ap; |
| 26634 | va_start(ap, zSql); |
| 26635 | z = sqlite3_vmprintf(zSql, ap); |
| 26636 | va_end(ap); |
| 26637 | sqlite3_prepare_v2(db, z, -1, &pStmt, 0); |
| 26638 | if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 26639 | res = sqlite3_column_int(pStmt,0); |
| 26640 | } |
| 26641 | sqlite3_finalize(pStmt); |
| 26642 | sqlite3_free(z); |
| 26643 | return res; |
| 26644 | } |
| 26645 | |
| 26646 | #if SQLITE_SHELL_HAVE_RECOVER |
| 26647 | /* |
| @@ -25419,11 +26696,11 @@ | |
| 26696 | if( p->db==0 ) return 1; |
| 26697 | rc = sqlite3_prepare_v2(p->db, |
| 26698 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 26699 | -1, &pStmt, 0); |
| 26700 | if( rc ){ |
| 26701 | sqlite3_fprintf(stderr,"error: %s\n", sqlite3_errmsg(p->db)); |
| 26702 | sqlite3_finalize(pStmt); |
| 26703 | return 1; |
| 26704 | } |
| 26705 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 26706 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| @@ -25432,58 +26709,149 @@ | |
| 26709 | const u8 *pb = sqlite3_column_blob(pStmt,0); |
| 26710 | shell_check_oom(pb); |
| 26711 | memcpy(aHdr, pb, 100); |
| 26712 | sqlite3_finalize(pStmt); |
| 26713 | }else{ |
| 26714 | sqlite3_fputs("unable to read database header\n", stderr); |
| 26715 | sqlite3_finalize(pStmt); |
| 26716 | return 1; |
| 26717 | } |
| 26718 | i = get2byteInt(aHdr+16); |
| 26719 | if( i==1 ) i = 65536; |
| 26720 | sqlite3_fprintf(p->out, "%-20s %d\n", "database page size:", i); |
| 26721 | sqlite3_fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]); |
| 26722 | sqlite3_fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]); |
| 26723 | sqlite3_fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); |
| 26724 | for(i=0; i<ArraySize(aField); i++){ |
| 26725 | int ofst = aField[i].ofst; |
| 26726 | unsigned int val = get4byteInt(aHdr + ofst); |
| 26727 | sqlite3_fprintf(p->out, "%-20s %u", aField[i].zName, val); |
| 26728 | switch( ofst ){ |
| 26729 | case 56: { |
| 26730 | if( val==1 ) sqlite3_fputs(" (utf8)", p->out); |
| 26731 | if( val==2 ) sqlite3_fputs(" (utf16le)", p->out); |
| 26732 | if( val==3 ) sqlite3_fputs(" (utf16be)", p->out); |
| 26733 | } |
| 26734 | } |
| 26735 | sqlite3_fputs("\n", p->out); |
| 26736 | } |
| 26737 | if( zDb==0 ){ |
| 26738 | zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); |
| 26739 | }else if( cli_strcmp(zDb,"temp")==0 ){ |
| 26740 | zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema"); |
| 26741 | }else{ |
| 26742 | zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); |
| 26743 | } |
| 26744 | for(i=0; i<ArraySize(aQuery); i++){ |
| 26745 | int val = db_int(p->db, aQuery[i].zSql, zSchemaTab); |
| 26746 | sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val); |
| 26747 | } |
| 26748 | sqlite3_free(zSchemaTab); |
| 26749 | sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); |
| 26750 | sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion); |
| 26751 | return 0; |
| 26752 | } |
| 26753 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 26754 | |
| 26755 | /* |
| 26756 | ** Implementation of the ".dbtotxt" command. |
| 26757 | ** |
| 26758 | ** Return 1 on error, 2 to exit, and 0 otherwise. |
| 26759 | */ |
| 26760 | static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){ |
| 26761 | sqlite3_stmt *pStmt = 0; |
| 26762 | sqlite3_int64 nPage = 0; |
| 26763 | int pgSz = 0; |
| 26764 | const char *zTail; |
| 26765 | char *zName = 0; |
| 26766 | int rc, i, j; |
| 26767 | unsigned char bShow[256]; /* Characters ok to display */ |
| 26768 | |
| 26769 | UNUSED_PARAMETER(nArg); |
| 26770 | UNUSED_PARAMETER(azArg); |
| 26771 | memset(bShow, '.', sizeof(bShow)); |
| 26772 | for(i=' '; i<='~'; i++){ |
| 26773 | if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i; |
| 26774 | } |
| 26775 | rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0); |
| 26776 | if( rc ) goto dbtotxt_error; |
| 26777 | rc = 0; |
| 26778 | if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; |
| 26779 | pgSz = sqlite3_column_int(pStmt, 0); |
| 26780 | sqlite3_finalize(pStmt); |
| 26781 | pStmt = 0; |
| 26782 | if( pgSz<512 || pgSz>65536 || (pgSz&(pgSz-1))!=0 ) goto dbtotxt_error; |
| 26783 | rc = sqlite3_prepare_v2(p->db, "PRAGMA page_count", -1, &pStmt, 0); |
| 26784 | if( rc ) goto dbtotxt_error; |
| 26785 | rc = 0; |
| 26786 | if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; |
| 26787 | nPage = sqlite3_column_int64(pStmt, 0); |
| 26788 | sqlite3_finalize(pStmt); |
| 26789 | pStmt = 0; |
| 26790 | if( nPage<1 ) goto dbtotxt_error; |
| 26791 | rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0); |
| 26792 | if( rc ) goto dbtotxt_error; |
| 26793 | if( sqlite3_step(pStmt)!=SQLITE_ROW ){ |
| 26794 | zTail = "unk.db"; |
| 26795 | }else{ |
| 26796 | const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2); |
| 26797 | if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db"; |
| 26798 | zTail = strrchr(zFilename, '/'); |
| 26799 | #if defined(_WIN32) |
| 26800 | if( zTail==0 ) zTail = strrchr(zFilename, '\\'); |
| 26801 | #endif |
| 26802 | } |
| 26803 | zName = strdup(zTail); |
| 26804 | shell_check_oom(zName); |
| 26805 | sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n", |
| 26806 | nPage*pgSz, pgSz, zName); |
| 26807 | sqlite3_finalize(pStmt); |
| 26808 | pStmt = 0; |
| 26809 | rc = sqlite3_prepare_v2(p->db, |
| 26810 | "SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0); |
| 26811 | if( rc ) goto dbtotxt_error; |
| 26812 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 26813 | sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0); |
| 26814 | const u8 *aData = sqlite3_column_blob(pStmt, 1); |
| 26815 | int seenPageLabel = 0; |
| 26816 | for(i=0; i<pgSz; i+=16){ |
| 26817 | const u8 *aLine = aData+i; |
| 26818 | for(j=0; j<16 && aLine[j]==0; j++){} |
| 26819 | if( j==16 ) continue; |
| 26820 | if( !seenPageLabel ){ |
| 26821 | sqlite3_fprintf(p->out, "| page %lld offset %lld\n", pgno, pgno*pgSz); |
| 26822 | seenPageLabel = 1; |
| 26823 | } |
| 26824 | sqlite3_fprintf(p->out, "| %5d:", i); |
| 26825 | for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]); |
| 26826 | sqlite3_fprintf(p->out, " "); |
| 26827 | for(j=0; j<16; j++){ |
| 26828 | unsigned char c = (unsigned char)aLine[j]; |
| 26829 | sqlite3_fprintf(p->out, "%c", bShow[c]); |
| 26830 | } |
| 26831 | sqlite3_fprintf(p->out, "\n"); |
| 26832 | } |
| 26833 | } |
| 26834 | sqlite3_finalize(pStmt); |
| 26835 | sqlite3_fprintf(p->out, "| end %s\n", zName); |
| 26836 | free(zName); |
| 26837 | return 0; |
| 26838 | |
| 26839 | dbtotxt_error: |
| 26840 | if( rc ){ |
| 26841 | sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db)); |
| 26842 | } |
| 26843 | sqlite3_finalize(pStmt); |
| 26844 | free(zName); |
| 26845 | return 1; |
| 26846 | } |
| 26847 | |
| 26848 | /* |
| 26849 | ** Print the given string as an error message. |
| 26850 | */ |
| 26851 | static void shellEmitError(const char *zErr){ |
| 26852 | sqlite3_fprintf(stderr,"Error: %s\n", zErr); |
| 26853 | } |
| 26854 | /* |
| 26855 | ** Print the current sqlite3_errmsg() value to stderr and return 1. |
| 26856 | */ |
| 26857 | static int shellDatabaseError(sqlite3 *db){ |
| @@ -25726,10 +27094,11 @@ | |
| 27094 | int bGroupByParent = 0; /* If -groupbyparent is present */ |
| 27095 | int i; /* To iterate through azArg[] */ |
| 27096 | const char *zIndent = ""; /* How much to indent CREATE INDEX by */ |
| 27097 | int rc; /* Return code */ |
| 27098 | sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ |
| 27099 | FILE *out = pState->out; /* Send output here */ |
| 27100 | |
| 27101 | /* |
| 27102 | ** This SELECT statement returns one row for each foreign key constraint |
| 27103 | ** in the schema of the main database. The column values are: |
| 27104 | ** |
| @@ -25801,11 +27170,12 @@ | |
| 27170 | else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ |
| 27171 | bGroupByParent = 1; |
| 27172 | zIndent = " "; |
| 27173 | } |
| 27174 | else{ |
| 27175 | sqlite3_fprintf(stderr, |
| 27176 | "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); |
| 27177 | return SQLITE_ERROR; |
| 27178 | } |
| 27179 | } |
| 27180 | |
| 27181 | /* Register the fkey_collate_clause() SQL function */ |
| @@ -25845,44 +27215,45 @@ | |
| 27215 | } |
| 27216 | rc = sqlite3_finalize(pExplain); |
| 27217 | if( rc!=SQLITE_OK ) break; |
| 27218 | |
| 27219 | if( res<0 ){ |
| 27220 | sqlite3_fputs("Error: internal error", stderr); |
| 27221 | break; |
| 27222 | }else{ |
| 27223 | if( bGroupByParent |
| 27224 | && (bVerbose || res==0) |
| 27225 | && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) |
| 27226 | ){ |
| 27227 | sqlite3_fprintf(out, "-- Parent table %s\n", zParent); |
| 27228 | sqlite3_free(zPrev); |
| 27229 | zPrev = sqlite3_mprintf("%s", zParent); |
| 27230 | } |
| 27231 | |
| 27232 | if( res==0 ){ |
| 27233 | sqlite3_fprintf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); |
| 27234 | }else if( bVerbose ){ |
| 27235 | sqlite3_fprintf(out, |
| 27236 | "%s/* no extra indexes required for %s -> %s */\n", |
| 27237 | zIndent, zFrom, zTarget |
| 27238 | ); |
| 27239 | } |
| 27240 | } |
| 27241 | } |
| 27242 | sqlite3_free(zPrev); |
| 27243 | |
| 27244 | if( rc!=SQLITE_OK ){ |
| 27245 | sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 27246 | } |
| 27247 | |
| 27248 | rc2 = sqlite3_finalize(pSql); |
| 27249 | if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ |
| 27250 | rc = rc2; |
| 27251 | sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 27252 | } |
| 27253 | }else{ |
| 27254 | sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 27255 | } |
| 27256 | |
| 27257 | return rc; |
| 27258 | } |
| 27259 | |
| @@ -25898,13 +27269,13 @@ | |
| 27269 | n = (nArg>=2 ? strlen30(azArg[1]) : 0); |
| 27270 | if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; |
| 27271 | return lintFkeyIndexes(pState, azArg, nArg); |
| 27272 | |
| 27273 | usage: |
| 27274 | sqlite3_fprintf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]); |
| 27275 | sqlite3_fprintf(stderr, "Where sub-commands are:\n"); |
| 27276 | sqlite3_fprintf(stderr, " fkey-indexes\n"); |
| 27277 | return SQLITE_ERROR; |
| 27278 | } |
| 27279 | |
| 27280 | static void shellPrepare( |
| 27281 | sqlite3 *db, |
| @@ -25914,11 +27285,12 @@ | |
| 27285 | ){ |
| 27286 | *ppStmt = 0; |
| 27287 | if( *pRc==SQLITE_OK ){ |
| 27288 | int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); |
| 27289 | if( rc!=SQLITE_OK ){ |
| 27290 | sqlite3_fprintf(stderr, |
| 27291 | "sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); |
| 27292 | *pRc = rc; |
| 27293 | } |
| 27294 | } |
| 27295 | } |
| 27296 | |
| @@ -25958,11 +27330,11 @@ | |
| 27330 | if( pStmt ){ |
| 27331 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 27332 | int rc = sqlite3_finalize(pStmt); |
| 27333 | if( *pRc==SQLITE_OK ){ |
| 27334 | if( rc!=SQLITE_OK ){ |
| 27335 | sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
| 27336 | } |
| 27337 | *pRc = rc; |
| 27338 | } |
| 27339 | } |
| 27340 | } |
| @@ -25980,11 +27352,11 @@ | |
| 27352 | ){ |
| 27353 | int rc = sqlite3_reset(pStmt); |
| 27354 | if( *pRc==SQLITE_OK ){ |
| 27355 | if( rc!=SQLITE_OK ){ |
| 27356 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 27357 | sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
| 27358 | } |
| 27359 | *pRc = rc; |
| 27360 | } |
| 27361 | } |
| 27362 | #endif /* !defined SQLITE_OMIT_VIRTUALTABLE */ |
| @@ -26009,10 +27381,11 @@ | |
| 27381 | char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ |
| 27382 | const char *zFile; /* --file argument, or NULL */ |
| 27383 | const char *zDir; /* --directory argument, or NULL */ |
| 27384 | char **azArg; /* Array of command arguments */ |
| 27385 | ShellState *p; /* Shell state */ |
| 27386 | FILE *out; /* Output to this stream */ |
| 27387 | sqlite3 *db; /* Database containing the archive */ |
| 27388 | }; |
| 27389 | |
| 27390 | /* |
| 27391 | ** Print a usage message for the .ar command to stderr and return SQLITE_ERROR. |
| @@ -26032,13 +27405,13 @@ | |
| 27405 | va_start(ap, zFmt); |
| 27406 | z = sqlite3_vmprintf(zFmt, ap); |
| 27407 | va_end(ap); |
| 27408 | shellEmitError(z); |
| 27409 | if( pAr->fromCmdLine ){ |
| 27410 | sqlite3_fputs("Use \"-A\" for more help\n", stderr); |
| 27411 | }else{ |
| 27412 | sqlite3_fputs("Use \".archive --help\" for more help\n", stderr); |
| 27413 | } |
| 27414 | sqlite3_free(z); |
| 27415 | return SQLITE_ERROR; |
| 27416 | } |
| 27417 | |
| @@ -26134,11 +27507,11 @@ | |
| 27507 | }; |
| 27508 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); |
| 27509 | struct ArSwitch *pEnd = &aSwitch[nSwitch]; |
| 27510 | |
| 27511 | if( nArg<=1 ){ |
| 27512 | sqlite3_fprintf(stderr, "Wrong number of arguments. Usage:\n"); |
| 27513 | return arUsage(stderr); |
| 27514 | }else{ |
| 27515 | char *z = azArg[1]; |
| 27516 | if( z[0]!='-' ){ |
| 27517 | /* Traditional style [tar] invocation */ |
| @@ -26240,11 +27613,11 @@ | |
| 27613 | } |
| 27614 | } |
| 27615 | } |
| 27616 | } |
| 27617 | if( pAr->eCmd==0 ){ |
| 27618 | sqlite3_fprintf(stderr, "Required argument missing. Usage:\n"); |
| 27619 | return arUsage(stderr); |
| 27620 | } |
| 27621 | return SQLITE_OK; |
| 27622 | } |
| 27623 | |
| @@ -26283,11 +27656,11 @@ | |
| 27656 | if( SQLITE_ROW==sqlite3_step(pTest) ){ |
| 27657 | bOk = 1; |
| 27658 | } |
| 27659 | shellReset(&rc, pTest); |
| 27660 | if( rc==SQLITE_OK && bOk==0 ){ |
| 27661 | sqlite3_fprintf(stderr,"not found in archive: %s\n", z); |
| 27662 | rc = SQLITE_ERROR; |
| 27663 | } |
| 27664 | } |
| 27665 | shellFinalize(&rc, pTest); |
| 27666 | } |
| @@ -26350,19 +27723,19 @@ | |
| 27723 | arWhereClause(&rc, pAr, &zWhere); |
| 27724 | |
| 27725 | shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], |
| 27726 | pAr->zSrcTable, zWhere); |
| 27727 | if( pAr->bDryRun ){ |
| 27728 | sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
| 27729 | }else{ |
| 27730 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 27731 | if( pAr->bVerbose ){ |
| 27732 | sqlite3_fprintf(pAr->out, "%s % 10d %s %s\n", |
| 27733 | sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), |
| 27734 | sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); |
| 27735 | }else{ |
| 27736 | sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 27737 | } |
| 27738 | } |
| 27739 | } |
| 27740 | shellFinalize(&rc, pSql); |
| 27741 | sqlite3_free(zWhere); |
| @@ -26385,11 +27758,11 @@ | |
| 27758 | } |
| 27759 | if( rc==SQLITE_OK ){ |
| 27760 | zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", |
| 27761 | pAr->zSrcTable, zWhere); |
| 27762 | if( pAr->bDryRun ){ |
| 27763 | sqlite3_fprintf(pAr->out, "%s\n", zSql); |
| 27764 | }else{ |
| 27765 | char *zErr = 0; |
| 27766 | rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); |
| 27767 | if( rc==SQLITE_OK ){ |
| 27768 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| @@ -26398,11 +27771,11 @@ | |
| 27771 | }else{ |
| 27772 | rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0); |
| 27773 | } |
| 27774 | } |
| 27775 | if( zErr ){ |
| 27776 | sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); /* stdout? */ |
| 27777 | sqlite3_free(zErr); |
| 27778 | } |
| 27779 | } |
| 27780 | } |
| 27781 | sqlite3_free(zWhere); |
| @@ -26462,15 +27835,15 @@ | |
| 27835 | ** populating them changes the timestamp). */ |
| 27836 | for(i=0; i<2; i++){ |
| 27837 | j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); |
| 27838 | sqlite3_bind_int(pSql, j, i); |
| 27839 | if( pAr->bDryRun ){ |
| 27840 | sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
| 27841 | }else{ |
| 27842 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 27843 | if( i==0 && pAr->bVerbose ){ |
| 27844 | sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 27845 | } |
| 27846 | } |
| 27847 | } |
| 27848 | shellReset(&rc, pSql); |
| 27849 | } |
| @@ -26486,17 +27859,17 @@ | |
| 27859 | ** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out. |
| 27860 | */ |
| 27861 | static int arExecSql(ArCommand *pAr, const char *zSql){ |
| 27862 | int rc; |
| 27863 | if( pAr->bDryRun ){ |
| 27864 | sqlite3_fprintf(pAr->out, "%s\n", zSql); |
| 27865 | rc = SQLITE_OK; |
| 27866 | }else{ |
| 27867 | char *zErr = 0; |
| 27868 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| 27869 | if( zErr ){ |
| 27870 | sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); |
| 27871 | sqlite3_free(zErr); |
| 27872 | } |
| 27873 | } |
| 27874 | return rc; |
| 27875 | } |
| @@ -26641,10 +28014,11 @@ | |
| 28014 | cmd.fromCmdLine = fromCmdLine; |
| 28015 | rc = arParseCommand(azArg, nArg, &cmd); |
| 28016 | if( rc==SQLITE_OK ){ |
| 28017 | int eDbType = SHELL_OPEN_UNSPEC; |
| 28018 | cmd.p = pState; |
| 28019 | cmd.out = pState->out; |
| 28020 | cmd.db = pState->db; |
| 28021 | if( cmd.zFile ){ |
| 28022 | eDbType = deduceDatabaseType(cmd.zFile, 1); |
| 28023 | }else{ |
| 28024 | eDbType = pState->openMode; |
| @@ -26667,17 +28041,18 @@ | |
| 28041 | }else{ |
| 28042 | flags = SQLITE_OPEN_READONLY; |
| 28043 | } |
| 28044 | cmd.db = 0; |
| 28045 | if( cmd.bDryRun ){ |
| 28046 | sqlite3_fprintf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, |
| 28047 | eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); |
| 28048 | } |
| 28049 | rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, |
| 28050 | eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); |
| 28051 | if( rc!=SQLITE_OK ){ |
| 28052 | sqlite3_fprintf(stderr, "cannot open file: %s (%s)\n", |
| 28053 | cmd.zFile, sqlite3_errmsg(cmd.db)); |
| 28054 | goto end_ar_command; |
| 28055 | } |
| 28056 | sqlite3_fileio_init(cmd.db, 0, 0); |
| 28057 | sqlite3_sqlar_init(cmd.db, 0, 0); |
| 28058 | sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, |
| @@ -26686,11 +28061,11 @@ | |
| 28061 | } |
| 28062 | if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ |
| 28063 | if( cmd.eCmd!=AR_CMD_CREATE |
| 28064 | && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) |
| 28065 | ){ |
| 28066 | sqlite3_fprintf(stderr, "database does not contain an 'sqlar' table\n"); |
| 28067 | rc = SQLITE_ERROR; |
| 28068 | goto end_ar_command; |
| 28069 | } |
| 28070 | cmd.zSrcTable = sqlite3_mprintf("sqlar"); |
| 28071 | } |
| @@ -26744,11 +28119,11 @@ | |
| 28119 | ** This function is used as a callback by the recover extension. Simply |
| 28120 | ** print the supplied SQL statement to stdout. |
| 28121 | */ |
| 28122 | static int recoverSqlCb(void *pCtx, const char *zSql){ |
| 28123 | ShellState *pState = (ShellState*)pCtx; |
| 28124 | sqlite3_fprintf(pState->out, "%s;\n", zSql); |
| 28125 | return SQLITE_OK; |
| 28126 | } |
| 28127 | |
| 28128 | /* |
| 28129 | ** This function is called to recover data from the database. A script |
| @@ -26787,11 +28162,11 @@ | |
| 28162 | }else |
| 28163 | if( n<=10 && memcmp("-no-rowids", z, n)==0 ){ |
| 28164 | bRowids = 0; |
| 28165 | } |
| 28166 | else{ |
| 28167 | sqlite3_fprintf(stderr,"unexpected option: %s\n", azArg[i]); |
| 28168 | showHelp(pState->out, azArg[0]); |
| 28169 | return 1; |
| 28170 | } |
| 28171 | } |
| 28172 | |
| @@ -26806,11 +28181,11 @@ | |
| 28181 | |
| 28182 | sqlite3_recover_run(p); |
| 28183 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 28184 | const char *zErr = sqlite3_recover_errmsg(p); |
| 28185 | int errCode = sqlite3_recover_errcode(p); |
| 28186 | sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
| 28187 | } |
| 28188 | rc = sqlite3_recover_finish(p); |
| 28189 | return rc; |
| 28190 | } |
| 28191 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| @@ -26828,25 +28203,25 @@ | |
| 28203 | i64 nError = 0; |
| 28204 | const char *zErr = 0; |
| 28205 | while( SQLITE_OK==sqlite3_intck_step(p) ){ |
| 28206 | const char *zMsg = sqlite3_intck_message(p); |
| 28207 | if( zMsg ){ |
| 28208 | sqlite3_fprintf(pState->out, "%s\n", zMsg); |
| 28209 | nError++; |
| 28210 | } |
| 28211 | nStep++; |
| 28212 | if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){ |
| 28213 | sqlite3_intck_unlock(p); |
| 28214 | } |
| 28215 | } |
| 28216 | rc = sqlite3_intck_error(p, &zErr); |
| 28217 | if( zErr ){ |
| 28218 | sqlite3_fprintf(stderr,"%s\n", zErr); |
| 28219 | } |
| 28220 | sqlite3_intck_close(p); |
| 28221 | |
| 28222 | sqlite3_fprintf(pState->out, "%lld steps, %lld errors\n", nStep, nError); |
| 28223 | } |
| 28224 | |
| 28225 | return rc; |
| 28226 | } |
| 28227 | |
| @@ -26865,11 +28240,11 @@ | |
| 28240 | */ |
| 28241 | #ifdef SHELL_DEBUG |
| 28242 | #define rc_err_oom_die(rc) \ |
| 28243 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ |
| 28244 | else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ |
| 28245 | sqlite3_fprintf(stderr,"E:%d\n",rc), assert(0) |
| 28246 | #else |
| 28247 | static void rc_err_oom_die(int rc){ |
| 28248 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); |
| 28249 | assert(rc==SQLITE_OK||rc==SQLITE_DONE); |
| 28250 | } |
| @@ -27023,12 +28398,12 @@ | |
| 28398 | }else if( *pDb==0 ){ |
| 28399 | return 0; |
| 28400 | }else{ |
| 28401 | /* Formulate the columns spec, close the DB, zero *pDb. */ |
| 28402 | char *zColsSpec = 0; |
| 28403 | int hasDupes = db_int(*pDb, "%s", zHasDupes); |
| 28404 | int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0; |
| 28405 | if( hasDupes ){ |
| 28406 | #ifdef SHELL_COLUMN_RENAME_CLEAN |
| 28407 | rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0); |
| 28408 | rc_err_oom_die(rc); |
| 28409 | #endif |
| @@ -27039,11 +28414,11 @@ | |
| 28414 | sqlite3_bind_int(pStmt, 1, nDigits); |
| 28415 | rc = sqlite3_step(pStmt); |
| 28416 | sqlite3_finalize(pStmt); |
| 28417 | if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); |
| 28418 | } |
| 28419 | assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */ |
| 28420 | rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 28421 | rc_err_oom_die(rc); |
| 28422 | rc = sqlite3_step(pStmt); |
| 28423 | if( rc==SQLITE_ROW ){ |
| 28424 | zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); |
| @@ -27082,12 +28457,13 @@ | |
| 28457 | shellPreparePrintf(p->db, &rc, &pStmt, |
| 28458 | "SELECT 1 FROM sqlite_schema o WHERE " |
| 28459 | "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true" |
| 28460 | ); |
| 28461 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 28462 | sqlite3_fputs("/* WARNING: " |
| 28463 | "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n", |
| 28464 | p->out |
| 28465 | ); |
| 28466 | } |
| 28467 | shellFinalize(&rc, pStmt); |
| 28468 | return rc; |
| 28469 | } |
| @@ -27114,16 +28490,18 @@ | |
| 28490 | return SQLITE_OK; |
| 28491 | } |
| 28492 | if( faultsim_state.iCnt ){ |
| 28493 | if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; |
| 28494 | if( faultsim_state.eVerbose>=2 ){ |
| 28495 | sqlite3_fprintf(stdout, |
| 28496 | "FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); |
| 28497 | } |
| 28498 | return SQLITE_OK; |
| 28499 | } |
| 28500 | if( faultsim_state.eVerbose>=1 ){ |
| 28501 | sqlite3_fprintf(stdout, |
| 28502 | "FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); |
| 28503 | } |
| 28504 | faultsim_state.iCnt = faultsim_state.iInterval; |
| 28505 | faultsim_state.nHit++; |
| 28506 | if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){ |
| 28507 | faultsim_state.iCnt = -1; |
| @@ -27182,11 +28560,11 @@ | |
| 28560 | clearTempFile(p); |
| 28561 | |
| 28562 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 28563 | if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ |
| 28564 | if( nArg!=2 ){ |
| 28565 | sqlite3_fprintf(stderr, "Usage: .auth ON|OFF\n"); |
| 28566 | rc = 1; |
| 28567 | goto meta_command_exit; |
| 28568 | } |
| 28569 | open_db(p, 0); |
| 28570 | if( booleanValue(azArg[1]) ){ |
| @@ -27229,32 +28607,32 @@ | |
| 28607 | }else |
| 28608 | if( cli_strcmp(z, "-async")==0 ){ |
| 28609 | bAsync = 1; |
| 28610 | }else |
| 28611 | { |
| 28612 | sqlite3_fprintf(stderr,"unknown option: %s\n", azArg[j]); |
| 28613 | return 1; |
| 28614 | } |
| 28615 | }else if( zDestFile==0 ){ |
| 28616 | zDestFile = azArg[j]; |
| 28617 | }else if( zDb==0 ){ |
| 28618 | zDb = zDestFile; |
| 28619 | zDestFile = azArg[j]; |
| 28620 | }else{ |
| 28621 | sqlite3_fprintf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); |
| 28622 | return 1; |
| 28623 | } |
| 28624 | } |
| 28625 | if( zDestFile==0 ){ |
| 28626 | sqlite3_fprintf(stderr, "missing FILENAME argument on .backup\n"); |
| 28627 | return 1; |
| 28628 | } |
| 28629 | if( zDb==0 ) zDb = "main"; |
| 28630 | rc = sqlite3_open_v2(zDestFile, &pDest, |
| 28631 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); |
| 28632 | if( rc!=SQLITE_OK ){ |
| 28633 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zDestFile); |
| 28634 | close_db(pDest); |
| 28635 | return 1; |
| 28636 | } |
| 28637 | if( bAsync ){ |
| 28638 | sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;", |
| @@ -27286,23 +28664,14 @@ | |
| 28664 | eputz("Usage: .bail on|off\n"); |
| 28665 | rc = 1; |
| 28666 | } |
| 28667 | }else |
| 28668 | |
| 28669 | /* Undocumented. Legacy only. See "crlf" below */ |
| 28670 | if( c=='b' && n>=3 && cli_strncmp(azArg[0], "binary", n)==0 ){ |
| 28671 | eputz("The \".binary\" command is deprecated.\n"); |
| 28672 | rc = 1; |
| 28673 | }else |
| 28674 | |
| 28675 | /* The undocumented ".breakpoint" command causes a call to the no-op |
| 28676 | ** routine named test_breakpoint(). |
| 28677 | */ |
| @@ -27320,11 +28689,11 @@ | |
| 28689 | sqlite3_free(z); |
| 28690 | #else |
| 28691 | rc = chdir(azArg[1]); |
| 28692 | #endif |
| 28693 | if( rc ){ |
| 28694 | sqlite3_fprintf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]); |
| 28695 | rc = 1; |
| 28696 | } |
| 28697 | }else{ |
| 28698 | eputz("Usage: .cd DIRECTORY\n"); |
| 28699 | rc = 1; |
| @@ -27353,15 +28722,16 @@ | |
| 28722 | eputz("Usage: .check GLOB-PATTERN\n"); |
| 28723 | rc = 2; |
| 28724 | }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ |
| 28725 | rc = 2; |
| 28726 | }else if( testcase_glob(azArg[1],zRes)==0 ){ |
| 28727 | sqlite3_fprintf(stderr, |
| 28728 | "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", |
| 28729 | p->zTestcase, azArg[1], zRes); |
| 28730 | rc = 1; |
| 28731 | }else{ |
| 28732 | sqlite3_fprintf(p->out, "testcase-%s ok\n", p->zTestcase); |
| 28733 | p->nCheck++; |
| 28734 | } |
| 28735 | sqlite3_free(zRes); |
| 28736 | }else |
| 28737 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| @@ -27390,13 +28760,13 @@ | |
| 28760 | zFile = "(memory)"; |
| 28761 | }else if( zFile[0]==0 ){ |
| 28762 | zFile = "(temporary-file)"; |
| 28763 | } |
| 28764 | if( p->pAuxDb == &p->aAuxDb[i] ){ |
| 28765 | sqlite3_fprintf(stdout, "ACTIVE %d: %s\n", i, zFile); |
| 28766 | }else if( p->aAuxDb[i].db!=0 ){ |
| 28767 | sqlite3_fprintf(stdout, " %d: %s\n", i, zFile); |
| 28768 | } |
| 28769 | } |
| 28770 | }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ |
| 28771 | int i = azArg[1][0] - '0'; |
| 28772 | if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){ |
| @@ -27422,24 +28792,22 @@ | |
| 28792 | eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n"); |
| 28793 | rc = 1; |
| 28794 | } |
| 28795 | }else |
| 28796 | |
| 28797 | if( c=='c' && n==4 |
| 28798 | && (cli_strncmp(azArg[0], "crlf", n)==0 |
| 28799 | || cli_strncmp(azArg[0], "crnl",n)==0) |
| 28800 | ){ |
| 28801 | if( nArg==2 ){ |
| 28802 | #ifdef _WIN32 |
| 28803 | p->crlfMode = booleanValue(azArg[1]); |
| 28804 | #else |
| 28805 | p->crlfMode = 0; |
| 28806 | #endif |
| 28807 | } |
| 28808 | sqlite3_fprintf(stderr, "crlf is %s\n", p->crlfMode ? "ON" : "OFF"); |
| 28809 | }else |
| 28810 | |
| 28811 | if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){ |
| 28812 | char **azName = 0; |
| 28813 | int nName = 0; |
| @@ -27465,11 +28833,11 @@ | |
| 28833 | sqlite3_finalize(pStmt); |
| 28834 | for(i=0; i<nName; i++){ |
| 28835 | int eTxn = sqlite3_txn_state(p->db, azName[i*2]); |
| 28836 | int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); |
| 28837 | const char *z = azName[i*2+1]; |
| 28838 | sqlite3_fprintf(p->out, "%s: %s %s%s\n", |
| 28839 | azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", |
| 28840 | eTxn==SQLITE_TXN_NONE ? "" : |
| 28841 | eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); |
| 28842 | free(azName[i*2]); |
| 28843 | free(azName[i*2+1]); |
| @@ -27507,15 +28875,16 @@ | |
| 28875 | if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; |
| 28876 | if( nArg>=3 ){ |
| 28877 | sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); |
| 28878 | } |
| 28879 | sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); |
| 28880 | sqlite3_fprintf(p->out, "%19s %s\n", |
| 28881 | aDbConfig[ii].zName, v ? "on" : "off"); |
| 28882 | if( nArg>1 ) break; |
| 28883 | } |
| 28884 | if( nArg>1 && ii==ArraySize(aDbConfig) ){ |
| 28885 | sqlite3_fprintf(stderr,"Error: unknown dbconfig \"%s\"\n", azArg[1]); |
| 28886 | eputz("Enter \".dbconfig\" with no arguments for a list\n"); |
| 28887 | } |
| 28888 | }else |
| 28889 | |
| 28890 | #if SQLITE_SHELL_HAVE_RECOVER |
| @@ -27561,11 +28930,12 @@ | |
| 28930 | }else |
| 28931 | if( cli_strcmp(z,"nosys")==0 ){ |
| 28932 | ShellSetFlag(p, SHFLG_DumpNoSys); |
| 28933 | }else |
| 28934 | { |
| 28935 | sqlite3_fprintf(stderr, |
| 28936 | "Unknown option \"%s\" on \".dump\"\n", azArg[i]); |
| 28937 | rc = 1; |
| 28938 | sqlite3_free(zLike); |
| 28939 | goto meta_command_exit; |
| 28940 | } |
| 28941 | }else{ |
| @@ -27596,12 +28966,12 @@ | |
| 28966 | outputDumpWarning(p, zLike); |
| 28967 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 28968 | /* When playing back a "dump", the content might appear in an order |
| 28969 | ** which causes immediate foreign key constraints to be violated. |
| 28970 | ** So disable foreign-key constraint enforcement to prevent problems. */ |
| 28971 | sqlite3_fputs("PRAGMA foreign_keys=OFF;\n", p->out); |
| 28972 | sqlite3_fputs("BEGIN TRANSACTION;\n", p->out); |
| 28973 | } |
| 28974 | p->writableSchema = 0; |
| 28975 | p->showHeader = 0; |
| 28976 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 28977 | ** as much of the schema as it can even if the sqlite_schema table is |
| @@ -27629,17 +28999,17 @@ | |
| 28999 | run_table_dump_query(p, zSql); |
| 29000 | sqlite3_free(zSql); |
| 29001 | } |
| 29002 | sqlite3_free(zLike); |
| 29003 | if( p->writableSchema ){ |
| 29004 | sqlite3_fputs("PRAGMA writable_schema=OFF;\n", p->out); |
| 29005 | p->writableSchema = 0; |
| 29006 | } |
| 29007 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| 29008 | sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); |
| 29009 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 29010 | sqlite3_fputs(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out); |
| 29011 | } |
| 29012 | p->showHeader = savedShowHeader; |
| 29013 | p->shellFlgs = savedShellFlags; |
| 29014 | }else |
| 29015 | |
| @@ -27649,10 +29019,14 @@ | |
| 29019 | }else{ |
| 29020 | eputz("Usage: .echo on|off\n"); |
| 29021 | rc = 1; |
| 29022 | } |
| 29023 | }else |
| 29024 | |
| 29025 | if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){ |
| 29026 | rc = shell_dbtotxt_command(p, nArg, azArg); |
| 29027 | }else |
| 29028 | |
| 29029 | if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){ |
| 29030 | if( nArg==2 ){ |
| 29031 | p->autoEQPtest = 0; |
| 29032 | if( p->autoEQPtrace ){ |
| @@ -27715,11 +29089,12 @@ | |
| 29089 | }else |
| 29090 | |
| 29091 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 29092 | if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ |
| 29093 | if( p->bSafeMode ){ |
| 29094 | sqlite3_fprintf(stderr, |
| 29095 | "Cannot run experimental commands such as \"%s\" in safe mode\n", |
| 29096 | azArg[0]); |
| 29097 | rc = 1; |
| 29098 | }else{ |
| 29099 | open_db(p, 0); |
| 29100 | expertDotCommand(p, azArg, nArg); |
| @@ -27772,13 +29147,14 @@ | |
| 29147 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 29148 | } |
| 29149 | |
| 29150 | /* --help lists all file-controls */ |
| 29151 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 29152 | sqlite3_fputs("Available file-controls:\n", p->out); |
| 29153 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 29154 | sqlite3_fprintf(p->out, |
| 29155 | " .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 29156 | } |
| 29157 | rc = 1; |
| 29158 | goto meta_command_exit; |
| 29159 | } |
| 29160 | |
| @@ -27789,19 +29165,19 @@ | |
| 29165 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 29166 | if( filectrl<0 ){ |
| 29167 | filectrl = aCtrl[i].ctrlCode; |
| 29168 | iCtrl = i; |
| 29169 | }else{ |
| 29170 | sqlite3_fprintf(stderr,"Error: ambiguous file-control: \"%s\"\n" |
| 29171 | "Use \".filectrl --help\" for help\n", zCmd); |
| 29172 | rc = 1; |
| 29173 | goto meta_command_exit; |
| 29174 | } |
| 29175 | } |
| 29176 | } |
| 29177 | if( filectrl<0 ){ |
| 29178 | sqlite3_fprintf(stderr,"Error: unknown file-control: %s\n" |
| 29179 | "Use \".filectrl --help\" for help\n", zCmd); |
| 29180 | }else{ |
| 29181 | switch(filectrl){ |
| 29182 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 29183 | if( nArg!=2 && nArg!=3 ) break; |
| @@ -27841,11 +29217,11 @@ | |
| 29217 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 29218 | char *z = 0; |
| 29219 | if( nArg!=2 ) break; |
| 29220 | sqlite3_file_control(p->db, zSchema, filectrl, &z); |
| 29221 | if( z ){ |
| 29222 | sqlite3_fprintf(p->out, "%s\n", z); |
| 29223 | sqlite3_free(z); |
| 29224 | } |
| 29225 | isOk = 2; |
| 29226 | break; |
| 29227 | } |
| @@ -27855,23 +29231,24 @@ | |
| 29231 | x = atoi(azArg[2]); |
| 29232 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 29233 | } |
| 29234 | x = -1; |
| 29235 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 29236 | sqlite3_fprintf(p->out, "%d\n", x); |
| 29237 | isOk = 2; |
| 29238 | break; |
| 29239 | } |
| 29240 | } |
| 29241 | } |
| 29242 | if( isOk==0 && iCtrl>=0 ){ |
| 29243 | sqlite3_fprintf(p->out, "Usage: .filectrl %s %s\n", |
| 29244 | zCmd, aCtrl[iCtrl].zUsage); |
| 29245 | rc = 1; |
| 29246 | }else if( isOk==1 ){ |
| 29247 | char zBuf[100]; |
| 29248 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); |
| 29249 | sqlite3_fprintf(p->out, "%s\n", zBuf); |
| 29250 | } |
| 29251 | }else |
| 29252 | |
| 29253 | if( c=='f' && cli_strncmp(azArg[0], "fullschema", n)==0 ){ |
| 29254 | ShellState data; |
| @@ -27908,19 +29285,19 @@ | |
| 29285 | doStats = sqlite3_step(pStmt)==SQLITE_ROW; |
| 29286 | sqlite3_finalize(pStmt); |
| 29287 | } |
| 29288 | } |
| 29289 | if( doStats==0 ){ |
| 29290 | sqlite3_fputs("/* No STAT tables available */\n", p->out); |
| 29291 | }else{ |
| 29292 | sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); |
| 29293 | data.cMode = data.mode = MODE_Insert; |
| 29294 | data.zDestTable = "sqlite_stat1"; |
| 29295 | shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); |
| 29296 | data.zDestTable = "sqlite_stat4"; |
| 29297 | shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); |
| 29298 | sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); |
| 29299 | } |
| 29300 | }else |
| 29301 | |
| 29302 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 29303 | if( nArg==2 ){ |
| @@ -27934,11 +29311,11 @@ | |
| 29311 | |
| 29312 | if( c=='h' && cli_strncmp(azArg[0], "help", n)==0 ){ |
| 29313 | if( nArg>=2 ){ |
| 29314 | n = showHelp(p->out, azArg[1]); |
| 29315 | if( n==0 ){ |
| 29316 | sqlite3_fprintf(p->out, "Nothing matches '%s'\n", azArg[1]); |
| 29317 | } |
| 29318 | }else{ |
| 29319 | showHelp(p->out, 0); |
| 29320 | } |
| 29321 | }else |
| @@ -27977,11 +29354,11 @@ | |
| 29354 | if( zFile==0 ){ |
| 29355 | zFile = z; |
| 29356 | }else if( zTable==0 ){ |
| 29357 | zTable = z; |
| 29358 | }else{ |
| 29359 | sqlite3_fprintf(p->out, "ERROR: extra argument: \"%s\". Usage:\n",z); |
| 29360 | showHelp(p->out, "import"); |
| 29361 | goto meta_command_exit; |
| 29362 | } |
| 29363 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 29364 | eVerbose++; |
| @@ -27998,17 +29375,17 @@ | |
| 29375 | sCtx.cColSep = ','; |
| 29376 | sCtx.cRowSep = '\n'; |
| 29377 | xRead = csv_read_one_field; |
| 29378 | useOutputMode = 0; |
| 29379 | }else{ |
| 29380 | sqlite3_fprintf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); |
| 29381 | showHelp(p->out, "import"); |
| 29382 | goto meta_command_exit; |
| 29383 | } |
| 29384 | } |
| 29385 | if( zTable==0 ){ |
| 29386 | sqlite3_fprintf(p->out, "ERROR: missing %s argument. Usage:\n", |
| 29387 | zFile==0 ? "FILE" : "TABLE"); |
| 29388 | showHelp(p->out, "import"); |
| 29389 | goto meta_command_exit; |
| 29390 | } |
| 29391 | seenInterrupt = 0; |
| @@ -28054,32 +29431,32 @@ | |
| 29431 | if( sCtx.zFile[0]=='|' ){ |
| 29432 | #ifdef SQLITE_OMIT_POPEN |
| 29433 | eputz("Error: pipes are not supported in this OS\n"); |
| 29434 | goto meta_command_exit; |
| 29435 | #else |
| 29436 | sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 29437 | sCtx.zFile = "<pipe>"; |
| 29438 | sCtx.xCloser = pclose; |
| 29439 | #endif |
| 29440 | }else{ |
| 29441 | sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 29442 | sCtx.xCloser = fclose; |
| 29443 | } |
| 29444 | if( sCtx.in==0 ){ |
| 29445 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 29446 | goto meta_command_exit; |
| 29447 | } |
| 29448 | if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 29449 | char zSep[2]; |
| 29450 | zSep[1] = 0; |
| 29451 | zSep[0] = sCtx.cColSep; |
| 29452 | sqlite3_fputs("Column separator ", p->out); |
| 29453 | output_c_string(p->out, zSep); |
| 29454 | sqlite3_fputs(", row separator ", p->out); |
| 29455 | zSep[0] = sCtx.cRowSep; |
| 29456 | output_c_string(p->out, zSep); |
| 29457 | sqlite3_fputs("\n", p->out); |
| 29458 | } |
| 29459 | sCtx.z = sqlite3_malloc64(120); |
| 29460 | if( sCtx.z==0 ){ |
| 29461 | import_cleanup(&sCtx); |
| 29462 | shell_out_of_memory(); |
| @@ -28087,11 +29464,15 @@ | |
| 29464 | /* Below, resources must be freed before exit. */ |
| 29465 | while( (nSkip--)>0 ){ |
| 29466 | while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} |
| 29467 | } |
| 29468 | import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 29469 | if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) |
| 29470 | && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema" |
| 29471 | " WHERE name=%Q AND type='view'", |
| 29472 | zSchema ? zSchema : "main", zTable) |
| 29473 | ){ |
| 29474 | /* Table does not exist. Create it. */ |
| 29475 | sqlite3 *dbCols = 0; |
| 29476 | char *zRenames = 0; |
| 29477 | char *zColDefs; |
| 29478 | zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
| @@ -28100,18 +29481,18 @@ | |
| 29481 | zAutoColumn(sCtx.z, &dbCols, 0); |
| 29482 | if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 29483 | } |
| 29484 | zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
| 29485 | if( zRenames!=0 ){ |
| 29486 | sqlite3_fprintf((stdin_is_interactive && p->in==stdin)? p->out : stderr, |
| 29487 | "Columns renamed during .import %s due to duplicates:\n" |
| 29488 | "%s\n", sCtx.zFile, zRenames); |
| 29489 | sqlite3_free(zRenames); |
| 29490 | } |
| 29491 | assert(dbCols==0); |
| 29492 | if( zColDefs==0 ){ |
| 29493 | sqlite3_fprintf(stderr,"%s: empty file\n", sCtx.zFile); |
| 29494 | import_cleanup(&sCtx); |
| 29495 | rc = 1; |
| 29496 | sqlite3_free(zCreate); |
| 29497 | goto meta_command_exit; |
| 29498 | } |
| @@ -28119,17 +29500,20 @@ | |
| 29500 | if( zCreate==0 ){ |
| 29501 | import_cleanup(&sCtx); |
| 29502 | shell_out_of_memory(); |
| 29503 | } |
| 29504 | if( eVerbose>=1 ){ |
| 29505 | sqlite3_fprintf(p->out, "%s\n", zCreate); |
| 29506 | } |
| 29507 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 29508 | if( rc ){ |
| 29509 | sqlite3_fprintf(stderr, |
| 29510 | "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); |
| 29511 | } |
| 29512 | sqlite3_free(zCreate); |
| 29513 | zCreate = 0; |
| 29514 | if( rc ){ |
| 29515 | import_cleanup(&sCtx); |
| 29516 | rc = 1; |
| 29517 | goto meta_command_exit; |
| 29518 | } |
| 29519 | } |
| @@ -28180,11 +29564,11 @@ | |
| 29564 | } |
| 29565 | zSql[j++] = ')'; |
| 29566 | zSql[j] = 0; |
| 29567 | assert( j<nByte ); |
| 29568 | if( eVerbose>=2 ){ |
| 29569 | sqlite3_fprintf(p->out, "Insert using: %s\n", zSql); |
| 29570 | } |
| 29571 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 29572 | sqlite3_free(zSql); |
| 29573 | zSql = 0; |
| 29574 | if( rc ){ |
| @@ -28219,11 +29603,11 @@ | |
| 29603 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 29604 | z = ""; |
| 29605 | } |
| 29606 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 29607 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 29608 | sqlite3_fprintf(stderr,"%s:%d: expected %d columns but found %d" |
| 29609 | " - filling the rest with NULL\n", |
| 29610 | sCtx.zFile, startLine, nCol, i+1); |
| 29611 | i += 2; |
| 29612 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 29613 | } |
| @@ -28231,18 +29615,19 @@ | |
| 29615 | if( sCtx.cTerm==sCtx.cColSep ){ |
| 29616 | do{ |
| 29617 | xRead(&sCtx); |
| 29618 | i++; |
| 29619 | }while( sCtx.cTerm==sCtx.cColSep ); |
| 29620 | sqlite3_fprintf(stderr, |
| 29621 | "%s:%d: expected %d columns but found %d - extras ignored\n", |
| 29622 | sCtx.zFile, startLine, nCol, i); |
| 29623 | } |
| 29624 | if( i>=nCol ){ |
| 29625 | sqlite3_step(pStmt); |
| 29626 | rc = sqlite3_reset(pStmt); |
| 29627 | if( rc!=SQLITE_OK ){ |
| 29628 | sqlite3_fprintf(stderr,"%s:%d: INSERT failed: %s\n", |
| 29629 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 29630 | sCtx.nErr++; |
| 29631 | }else{ |
| 29632 | sCtx.nRow++; |
| 29633 | } |
| @@ -28251,11 +29636,12 @@ | |
| 29636 | |
| 29637 | import_cleanup(&sCtx); |
| 29638 | sqlite3_finalize(pStmt); |
| 29639 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 29640 | if( eVerbose>0 ){ |
| 29641 | sqlite3_fprintf(p->out, |
| 29642 | "Added %d rows with %d errors using %d lines of input\n", |
| 29643 | sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 29644 | } |
| 29645 | }else |
| 29646 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 29647 | |
| @@ -28267,11 +29653,11 @@ | |
| 29653 | int tnum = 0; |
| 29654 | int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ |
| 29655 | int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ |
| 29656 | int i; |
| 29657 | if( !ShellHasFlag(p,SHFLG_TestingMode) ){ |
| 29658 | sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n", |
| 29659 | "imposter"); |
| 29660 | rc = 1; |
| 29661 | goto meta_command_exit; |
| 29662 | } |
| 29663 | if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ |
| @@ -28333,11 +29719,11 @@ | |
| 29719 | zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); |
| 29720 | } |
| 29721 | } |
| 29722 | sqlite3_finalize(pStmt); |
| 29723 | if( i==0 || tnum==0 ){ |
| 29724 | sqlite3_fprintf(stderr,"no such index: \"%s\"\n", azArg[1]); |
| 29725 | rc = 1; |
| 29726 | sqlite3_free(zCollist); |
| 29727 | goto meta_command_exit; |
| 29728 | } |
| 29729 | if( lenPK==0 ) lenPK = 100000; |
| @@ -28348,18 +29734,20 @@ | |
| 29734 | rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); |
| 29735 | if( rc==SQLITE_OK ){ |
| 29736 | rc = sqlite3_exec(p->db, zSql, 0, 0, 0); |
| 29737 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); |
| 29738 | if( rc ){ |
| 29739 | sqlite3_fprintf(stderr, |
| 29740 | "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); |
| 29741 | }else{ |
| 29742 | sqlite3_fprintf(stdout, "%s;\n", zSql); |
| 29743 | sqlite3_fprintf(stdout, |
| 29744 | "WARNING: writing to an imposter table will corrupt" |
| 29745 | " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index"); |
| 29746 | } |
| 29747 | }else{ |
| 29748 | sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); |
| 29749 | rc = 1; |
| 29750 | } |
| 29751 | sqlite3_free(zSql); |
| 29752 | }else |
| 29753 | #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ |
| @@ -28369,11 +29757,11 @@ | |
| 29757 | if( nArg==2 ){ |
| 29758 | iArg = integerValue(azArg[1]); |
| 29759 | if( iArg==0 ) iArg = -1; |
| 29760 | } |
| 29761 | if( (nArg!=1 && nArg!=2) || iArg<0 ){ |
| 29762 | sqlite3_fprintf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); |
| 29763 | rc = 1; |
| 29764 | goto meta_command_exit; |
| 29765 | } |
| 29766 | open_db(p, 0); |
| 29767 | rc = intckDatabaseCmd(p, iArg); |
| @@ -28388,13 +29776,13 @@ | |
| 29776 | sqlite3IoTrace = 0; |
| 29777 | }else if( cli_strcmp(azArg[1], "-")==0 ){ |
| 29778 | sqlite3IoTrace = iotracePrintf; |
| 29779 | iotrace = stdout; |
| 29780 | }else{ |
| 29781 | iotrace = sqlite3_fopen(azArg[1], "w"); |
| 29782 | if( iotrace==0 ){ |
| 29783 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 29784 | sqlite3IoTrace = 0; |
| 29785 | rc = 1; |
| 29786 | }else{ |
| 29787 | sqlite3IoTrace = iotracePrintf; |
| 29788 | } |
| @@ -28422,11 +29810,11 @@ | |
| 29810 | }; |
| 29811 | int i, n2; |
| 29812 | open_db(p, 0); |
| 29813 | if( nArg==1 ){ |
| 29814 | for(i=0; i<ArraySize(aLimit); i++){ |
| 29815 | sqlite3_fprintf(stdout, "%20s %d\n", aLimit[i].zLimitName, |
| 29816 | sqlite3_limit(p->db, aLimit[i].limitCode, -1)); |
| 29817 | } |
| 29818 | }else if( nArg>3 ){ |
| 29819 | eputz("Usage: .limit NAME ?NEW-VALUE?\n"); |
| 29820 | rc = 1; |
| @@ -28437,28 +29825,28 @@ | |
| 29825 | for(i=0; i<ArraySize(aLimit); i++){ |
| 29826 | if( sqlite3_strnicmp(aLimit[i].zLimitName, azArg[1], n2)==0 ){ |
| 29827 | if( iLimit<0 ){ |
| 29828 | iLimit = i; |
| 29829 | }else{ |
| 29830 | sqlite3_fprintf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]); |
| 29831 | rc = 1; |
| 29832 | goto meta_command_exit; |
| 29833 | } |
| 29834 | } |
| 29835 | } |
| 29836 | if( iLimit<0 ){ |
| 29837 | sqlite3_fprintf(stderr,"unknown limit: \"%s\"\n" |
| 29838 | "enter \".limits\" with no arguments for a list.\n", |
| 29839 | azArg[1]); |
| 29840 | rc = 1; |
| 29841 | goto meta_command_exit; |
| 29842 | } |
| 29843 | if( nArg==3 ){ |
| 29844 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 29845 | (int)integerValue(azArg[2])); |
| 29846 | } |
| 29847 | sqlite3_fprintf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 29848 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 29849 | } |
| 29850 | }else |
| 29851 | |
| 29852 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| @@ -28503,11 +29891,11 @@ | |
| 29891 | " than \"on\" or \"off\"\n"); |
| 29892 | zFile = "off"; |
| 29893 | } |
| 29894 | output_file_close(p->pLog); |
| 29895 | if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout"; |
| 29896 | p->pLog = output_file_open(zFile); |
| 29897 | } |
| 29898 | }else |
| 29899 | |
| 29900 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 29901 | const char *zMode = 0; |
| @@ -28537,35 +29925,37 @@ | |
| 29925 | cmOpts = cmo; |
| 29926 | } |
| 29927 | }else if( zTabname==0 ){ |
| 29928 | zTabname = z; |
| 29929 | }else if( z[0]=='-' ){ |
| 29930 | sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 29931 | eputz("options:\n" |
| 29932 | " --noquote\n" |
| 29933 | " --quote\n" |
| 29934 | " --wordwrap on/off\n" |
| 29935 | " --wrap N\n" |
| 29936 | " --ww\n"); |
| 29937 | rc = 1; |
| 29938 | goto meta_command_exit; |
| 29939 | }else{ |
| 29940 | sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 29941 | rc = 1; |
| 29942 | goto meta_command_exit; |
| 29943 | } |
| 29944 | } |
| 29945 | if( zMode==0 ){ |
| 29946 | if( p->mode==MODE_Column |
| 29947 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 29948 | ){ |
| 29949 | sqlite3_fprintf(p->out, |
| 29950 | "current output mode: %s --wrap %d --wordwrap %s --%squote\n", |
| 29951 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 29952 | p->cmOpts.bWordWrap ? "on" : "off", |
| 29953 | p->cmOpts.bQuote ? "" : "no"); |
| 29954 | }else{ |
| 29955 | sqlite3_fprintf(p->out, |
| 29956 | "current output mode: %s\n", modeDescr[p->mode]); |
| 29957 | } |
| 29958 | zMode = modeDescr[p->mode]; |
| 29959 | } |
| 29960 | n2 = strlen30(zMode); |
| 29961 | if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| @@ -28634,11 +30024,11 @@ | |
| 30024 | if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ |
| 30025 | if( nArg!=2 ){ |
| 30026 | eputz("Usage: .nonce NONCE\n"); |
| 30027 | rc = 1; |
| 30028 | }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ |
| 30029 | sqlite3_fprintf(stderr,"line %d: incorrect nonce: \"%s\"\n", |
| 30030 | p->lineno, azArg[1]); |
| 30031 | exit(1); |
| 30032 | }else{ |
| 30033 | p->bSafeMode = 0; |
| 30034 | return 0; /* Return immediately to bypass the safe mode reset |
| @@ -28689,15 +30079,15 @@ | |
| 30079 | p->szMax = integerValue(azArg[++iName]); |
| 30080 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 30081 | }else |
| 30082 | #endif /* !SQLITE_SHELL_FIDDLE */ |
| 30083 | if( z[0]=='-' ){ |
| 30084 | sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 30085 | rc = 1; |
| 30086 | goto meta_command_exit; |
| 30087 | }else if( zFN ){ |
| 30088 | sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 30089 | rc = 1; |
| 30090 | goto meta_command_exit; |
| 30091 | }else{ |
| 30092 | zFN = z; |
| 30093 | } |
| @@ -28735,11 +30125,11 @@ | |
| 30125 | zNewFilename = 0; |
| 30126 | } |
| 30127 | p->pAuxDb->zDbFilename = zNewFilename; |
| 30128 | open_db(p, OPEN_DB_KEEPALIVE); |
| 30129 | if( p->db==0 ){ |
| 30130 | sqlite3_fprintf(stderr,"Error: cannot open '%s'\n", zNewFilename); |
| 30131 | sqlite3_free(zNewFilename); |
| 30132 | }else{ |
| 30133 | p->pAuxDb->zFreeOnClose = zNewFilename; |
| 30134 | } |
| 30135 | } |
| @@ -28753,22 +30143,26 @@ | |
| 30143 | #ifndef SQLITE_SHELL_FIDDLE |
| 30144 | if( (c=='o' |
| 30145 | && (cli_strncmp(azArg[0], "output", n)==0 |
| 30146 | || cli_strncmp(azArg[0], "once", n)==0)) |
| 30147 | || (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0) |
| 30148 | || (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0) |
| 30149 | ){ |
| 30150 | char *zFile = 0; |
| 30151 | int i; |
| 30152 | int eMode = 0; |
| 30153 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 30154 | int bPlain = 0; /* --plain option */ |
| 30155 | static const char *zBomUtf8 = "\357\273\277"; |
| 30156 | const char *zBom = 0; |
| 30157 | |
| 30158 | failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); |
| 30159 | if( c=='e' ){ |
| 30160 | eMode = 'x'; |
| 30161 | bOnce = 2; |
| 30162 | }else if( c=='w' ){ |
| 30163 | eMode = 'w'; |
| 30164 | bOnce = 2; |
| 30165 | }else if( cli_strncmp(azArg[0],"once",n)==0 ){ |
| 30166 | bOnce = 1; |
| 30167 | } |
| 30168 | for(i=1; i<nArg; i++){ |
| @@ -28775,28 +30169,34 @@ | |
| 30169 | char *z = azArg[i]; |
| 30170 | if( z[0]=='-' ){ |
| 30171 | if( z[1]=='-' ) z++; |
| 30172 | if( cli_strcmp(z,"-bom")==0 ){ |
| 30173 | zBom = zBomUtf8; |
| 30174 | }else if( cli_strcmp(z,"-plain")==0 ){ |
| 30175 | bPlain = 1; |
| 30176 | }else if( c=='o' && cli_strcmp(z,"-x")==0 ){ |
| 30177 | eMode = 'x'; /* spreadsheet */ |
| 30178 | }else if( c=='o' && cli_strcmp(z,"-e")==0 ){ |
| 30179 | eMode = 'e'; /* text editor */ |
| 30180 | }else if( c=='o' && cli_strcmp(z,"-w")==0 ){ |
| 30181 | eMode = 'w'; /* Web browser */ |
| 30182 | }else{ |
| 30183 | sqlite3_fprintf(p->out, |
| 30184 | "ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); |
| 30185 | showHelp(p->out, azArg[0]); |
| 30186 | rc = 1; |
| 30187 | goto meta_command_exit; |
| 30188 | } |
| 30189 | }else if( zFile==0 && eMode==0 ){ |
| 30190 | zFile = sqlite3_mprintf("%s", z); |
| 30191 | if( zFile && zFile[0]=='|' ){ |
| 30192 | while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); |
| 30193 | break; |
| 30194 | } |
| 30195 | }else{ |
| 30196 | sqlite3_fprintf(p->out, |
| 30197 | "ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); |
| 30198 | showHelp(p->out, azArg[0]); |
| 30199 | rc = 1; |
| 30200 | sqlite3_free(zFile); |
| 30201 | goto meta_command_exit; |
| 30202 | } |
| @@ -28809,24 +30209,31 @@ | |
| 30209 | }else{ |
| 30210 | p->outCount = 0; |
| 30211 | } |
| 30212 | output_reset(p); |
| 30213 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 30214 | if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 30215 | p->doXdgOpen = 1; |
| 30216 | outputModePush(p); |
| 30217 | if( eMode=='x' ){ |
| 30218 | /* spreadsheet mode. Output as CSV. */ |
| 30219 | newTempFile(p, "csv"); |
| 30220 | ShellClearFlag(p, SHFLG_Echo); |
| 30221 | p->mode = MODE_Csv; |
| 30222 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30223 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 30224 | #ifdef _WIN32 |
| 30225 | zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does |
| 30226 | ** not work without it. */ |
| 30227 | #endif |
| 30228 | }else if( eMode=='w' ){ |
| 30229 | /* web-browser mode. */ |
| 30230 | newTempFile(p, "html"); |
| 30231 | if( !bPlain ) p->mode = MODE_Www; |
| 30232 | }else{ |
| 30233 | /* text editor mode */ |
| 30234 | newTempFile(p, "txt"); |
| 30235 | } |
| 30236 | sqlite3_free(zFile); |
| 30237 | zFile = sqlite3_mprintf("%s", p->zTempFile); |
| 30238 | } |
| 30239 | #endif /* SQLITE_NOHAVE_SYSTEM */ |
| @@ -28835,30 +30242,36 @@ | |
| 30242 | #ifdef SQLITE_OMIT_POPEN |
| 30243 | eputz("Error: pipes are not supported in this OS\n"); |
| 30244 | rc = 1; |
| 30245 | output_redir(p, stdout); |
| 30246 | #else |
| 30247 | FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); |
| 30248 | if( pfPipe==0 ){ |
| 30249 | sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 30250 | rc = 1; |
| 30251 | }else{ |
| 30252 | output_redir(p, pfPipe); |
| 30253 | if( zBom ) sqlite3_fputs(zBom, pfPipe); |
| 30254 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 30255 | } |
| 30256 | #endif |
| 30257 | }else{ |
| 30258 | FILE *pfFile = output_file_open(zFile); |
| 30259 | if( pfFile==0 ){ |
| 30260 | if( cli_strcmp(zFile,"off")!=0 ){ |
| 30261 | sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 30262 | } |
| 30263 | rc = 1; |
| 30264 | } else { |
| 30265 | output_redir(p, pfFile); |
| 30266 | if( zBom ) sqlite3_fputs(zBom, pfFile); |
| 30267 | if( bPlain && eMode=='w' ){ |
| 30268 | sqlite3_fputs( |
| 30269 | "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n", |
| 30270 | pfFile |
| 30271 | ); |
| 30272 | } |
| 30273 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 30274 | } |
| 30275 | } |
| 30276 | sqlite3_free(zFile); |
| 30277 | }else |
| @@ -28895,11 +30308,12 @@ | |
| 30308 | if( len ){ |
| 30309 | rx = sqlite3_prepare_v2(p->db, |
| 30310 | "SELECT key, quote(value) " |
| 30311 | "FROM temp.sqlite_parameters;", -1, &pStmt, 0); |
| 30312 | while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 30313 | sqlite3_fprintf(p->out, |
| 30314 | "%-*s %s\n", len, sqlite3_column_text(pStmt,0), |
| 30315 | sqlite3_column_text(pStmt,1)); |
| 30316 | } |
| 30317 | sqlite3_finalize(pStmt); |
| 30318 | } |
| 30319 | }else |
| @@ -28940,11 +30354,11 @@ | |
| 30354 | "VALUES(%Q,%Q);", zKey, zValue); |
| 30355 | shell_check_oom(zSql); |
| 30356 | rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 30357 | sqlite3_free(zSql); |
| 30358 | if( rx!=SQLITE_OK ){ |
| 30359 | sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); |
| 30360 | sqlite3_finalize(pStmt); |
| 30361 | pStmt = 0; |
| 30362 | rc = 1; |
| 30363 | } |
| 30364 | } |
| @@ -28969,14 +30383,14 @@ | |
| 30383 | }else |
| 30384 | |
| 30385 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ |
| 30386 | int i; |
| 30387 | for(i=1; i<nArg; i++){ |
| 30388 | if( i>1 ) sqlite3_fputs(" ", p->out); |
| 30389 | sqlite3_fputs(azArg[i], p->out); |
| 30390 | } |
| 30391 | sqlite3_fputs("\n", p->out); |
| 30392 | }else |
| 30393 | |
| 30394 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 30395 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "progress", n)==0 ){ |
| 30396 | int i; |
| @@ -29009,11 +30423,11 @@ | |
| 30423 | }else{ |
| 30424 | p->mxProgress = (int)integerValue(azArg[++i]); |
| 30425 | } |
| 30426 | continue; |
| 30427 | } |
| 30428 | sqlite3_fprintf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]); |
| 30429 | rc = 1; |
| 30430 | goto meta_command_exit; |
| 30431 | }else{ |
| 30432 | nn = (int)integerValue(z); |
| 30433 | } |
| @@ -29050,23 +30464,22 @@ | |
| 30464 | } |
| 30465 | if( azArg[1][0]=='|' ){ |
| 30466 | #ifdef SQLITE_OMIT_POPEN |
| 30467 | eputz("Error: pipes are not supported in this OS\n"); |
| 30468 | rc = 1; |
| 30469 | #else |
| 30470 | p->in = sqlite3_popen(azArg[1]+1, "r"); |
| 30471 | if( p->in==0 ){ |
| 30472 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 30473 | rc = 1; |
| 30474 | }else{ |
| 30475 | rc = process_input(p); |
| 30476 | pclose(p->in); |
| 30477 | } |
| 30478 | #endif |
| 30479 | }else if( (p->in = openChrSource(azArg[1]))==0 ){ |
| 30480 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 30481 | rc = 1; |
| 30482 | }else{ |
| 30483 | rc = process_input(p); |
| 30484 | fclose(p->in); |
| 30485 | } |
| @@ -29095,11 +30508,11 @@ | |
| 30508 | rc = 1; |
| 30509 | goto meta_command_exit; |
| 30510 | } |
| 30511 | rc = sqlite3_open(zSrcFile, &pSrc); |
| 30512 | if( rc!=SQLITE_OK ){ |
| 30513 | sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zSrcFile); |
| 30514 | close_db(pSrc); |
| 30515 | return 1; |
| 30516 | } |
| 30517 | open_db(p, 0); |
| 30518 | pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); |
| @@ -29127,11 +30540,14 @@ | |
| 30540 | } |
| 30541 | close_db(pSrc); |
| 30542 | }else |
| 30543 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 30544 | |
| 30545 | if( c=='s' && |
| 30546 | (cli_strncmp(azArg[0], "scanstats", n)==0 || |
| 30547 | cli_strncmp(azArg[0], "scanstatus", n)==0) |
| 30548 | ){ |
| 30549 | if( nArg==2 ){ |
| 30550 | if( cli_strcmp(azArg[1], "vm")==0 ){ |
| 30551 | p->scanstatsOn = 3; |
| 30552 | }else |
| 30553 | if( cli_strcmp(azArg[1], "est")==0 ){ |
| @@ -29178,11 +30594,11 @@ | |
| 30594 | }else if( optionMatch(azArg[ii],"debug") ){ |
| 30595 | bDebug = 1; |
| 30596 | }else if( optionMatch(azArg[ii],"nosys") ){ |
| 30597 | bNoSystemTabs = 1; |
| 30598 | }else if( azArg[ii][0]=='-' ){ |
| 30599 | sqlite3_fprintf(stderr,"Unknown option: \"%s\"\n", azArg[ii]); |
| 30600 | rc = 1; |
| 30601 | goto meta_command_exit; |
| 30602 | }else if( zName==0 ){ |
| 30603 | zName = azArg[ii]; |
| 30604 | }else{ |
| @@ -29279,11 +30695,11 @@ | |
| 30695 | appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0); |
| 30696 | } |
| 30697 | appendText(&sSelect, "sql IS NOT NULL" |
| 30698 | " ORDER BY snum, rowid", 0); |
| 30699 | if( bDebug ){ |
| 30700 | sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z); |
| 30701 | }else{ |
| 30702 | rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); |
| 30703 | } |
| 30704 | freeText(&sSelect); |
| 30705 | } |
| @@ -29340,11 +30756,12 @@ | |
| 30756 | session_not_open: |
| 30757 | eputz("ERROR: No sessions are open\n"); |
| 30758 | }else{ |
| 30759 | rc = sqlite3session_attach(pSession->p, azCmd[1]); |
| 30760 | if( rc ){ |
| 30761 | sqlite3_fprintf(stderr, |
| 30762 | "ERROR: sqlite3session_attach() returns %d\n",rc); |
| 30763 | rc = 0; |
| 30764 | } |
| 30765 | } |
| 30766 | }else |
| 30767 | |
| @@ -29357,13 +30774,13 @@ | |
| 30774 | ){ |
| 30775 | FILE *out = 0; |
| 30776 | failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]); |
| 30777 | if( nCmd!=2 ) goto session_syntax_error; |
| 30778 | if( pSession->p==0 ) goto session_not_open; |
| 30779 | out = sqlite3_fopen(azCmd[1], "wb"); |
| 30780 | if( out==0 ){ |
| 30781 | sqlite3_fprintf(stderr,"ERROR: cannot open \"%s\" for writing\n", |
| 30782 | azCmd[1]); |
| 30783 | }else{ |
| 30784 | int szChng; |
| 30785 | void *pChng; |
| 30786 | if( azCmd[0][0]=='c' ){ |
| @@ -29370,16 +30787,17 @@ | |
| 30787 | rc = sqlite3session_changeset(pSession->p, &szChng, &pChng); |
| 30788 | }else{ |
| 30789 | rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); |
| 30790 | } |
| 30791 | if( rc ){ |
| 30792 | sqlite3_fprintf(stdout, "Error: error code %d\n", rc); |
| 30793 | rc = 0; |
| 30794 | } |
| 30795 | if( pChng |
| 30796 | && fwrite(pChng, szChng, 1, out)!=1 ){ |
| 30797 | sqlite3_fprintf(stderr, |
| 30798 | "ERROR: Failed to write entire %d-byte output\n", szChng); |
| 30799 | } |
| 30800 | sqlite3_free(pChng); |
| 30801 | fclose(out); |
| 30802 | } |
| 30803 | }else |
| @@ -29402,11 +30820,12 @@ | |
| 30820 | int ii; |
| 30821 | if( nCmd>2 ) goto session_syntax_error; |
| 30822 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 30823 | if( pAuxDb->nSession ){ |
| 30824 | ii = sqlite3session_enable(pSession->p, ii); |
| 30825 | sqlite3_fprintf(p->out, |
| 30826 | "session %s enable flag = %d\n", pSession->zName, ii); |
| 30827 | } |
| 30828 | }else |
| 30829 | |
| 30830 | /* .session filter GLOB .... |
| 30831 | ** Set a list of GLOB patterns of table names to be excluded. |
| @@ -29437,11 +30856,12 @@ | |
| 30856 | int ii; |
| 30857 | if( nCmd>2 ) goto session_syntax_error; |
| 30858 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 30859 | if( pAuxDb->nSession ){ |
| 30860 | ii = sqlite3session_indirect(pSession->p, ii); |
| 30861 | sqlite3_fprintf(p->out, |
| 30862 | "session %s indirect flag = %d\n", pSession->zName, ii); |
| 30863 | } |
| 30864 | }else |
| 30865 | |
| 30866 | /* .session isempty |
| 30867 | ** Determine if the session is empty |
| @@ -29449,20 +30869,21 @@ | |
| 30869 | if( cli_strcmp(azCmd[0], "isempty")==0 ){ |
| 30870 | int ii; |
| 30871 | if( nCmd!=1 ) goto session_syntax_error; |
| 30872 | if( pAuxDb->nSession ){ |
| 30873 | ii = sqlite3session_isempty(pSession->p); |
| 30874 | sqlite3_fprintf(p->out, |
| 30875 | "session %s isempty flag = %d\n", pSession->zName, ii); |
| 30876 | } |
| 30877 | }else |
| 30878 | |
| 30879 | /* .session list |
| 30880 | ** List all currently open sessions |
| 30881 | */ |
| 30882 | if( cli_strcmp(azCmd[0],"list")==0 ){ |
| 30883 | for(i=0; i<pAuxDb->nSession; i++){ |
| 30884 | sqlite3_fprintf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); |
| 30885 | } |
| 30886 | }else |
| 30887 | |
| 30888 | /* .session open DB NAME |
| 30889 | ** Open a new session called NAME on the attached database DB. |
| @@ -29473,22 +30894,23 @@ | |
| 30894 | if( nCmd!=3 ) goto session_syntax_error; |
| 30895 | zName = azCmd[2]; |
| 30896 | if( zName[0]==0 ) goto session_syntax_error; |
| 30897 | for(i=0; i<pAuxDb->nSession; i++){ |
| 30898 | if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){ |
| 30899 | sqlite3_fprintf(stderr,"Session \"%s\" already exists\n", zName); |
| 30900 | goto meta_command_exit; |
| 30901 | } |
| 30902 | } |
| 30903 | if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){ |
| 30904 | sqlite3_fprintf(stderr, |
| 30905 | "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); |
| 30906 | goto meta_command_exit; |
| 30907 | } |
| 30908 | pSession = &pAuxDb->aSession[pAuxDb->nSession]; |
| 30909 | rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); |
| 30910 | if( rc ){ |
| 30911 | sqlite3_fprintf(stderr,"Cannot open session: error code=%d\n", rc); |
| 30912 | rc = 0; |
| 30913 | goto meta_command_exit; |
| 30914 | } |
| 30915 | pSession->nFilter = 0; |
| 30916 | sqlite3session_table_filter(pSession->p, session_filter, pSession); |
| @@ -29508,20 +30930,20 @@ | |
| 30930 | if( c=='s' && n>=10 && cli_strncmp(azArg[0], "selftest-", 9)==0 ){ |
| 30931 | if( cli_strncmp(azArg[0]+9, "boolean", n-9)==0 ){ |
| 30932 | int i, v; |
| 30933 | for(i=1; i<nArg; i++){ |
| 30934 | v = booleanValue(azArg[i]); |
| 30935 | sqlite3_fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); |
| 30936 | } |
| 30937 | } |
| 30938 | if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ |
| 30939 | int i; sqlite3_int64 v; |
| 30940 | for(i=1; i<nArg; i++){ |
| 30941 | char zBuf[200]; |
| 30942 | v = integerValue(azArg[i]); |
| 30943 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); |
| 30944 | sqlite3_fputs(zBuf, p->out); |
| 30945 | } |
| 30946 | } |
| 30947 | }else |
| 30948 | #endif |
| 30949 | |
| @@ -29544,12 +30966,13 @@ | |
| 30966 | }else |
| 30967 | if( cli_strcmp(z,"-v")==0 ){ |
| 30968 | bVerbose++; |
| 30969 | }else |
| 30970 | { |
| 30971 | sqlite3_fprintf(stderr, |
| 30972 | "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); |
| 30973 | sqlite3_fputs("Should be one of: --init -v\n", stderr); |
| 30974 | rc = 1; |
| 30975 | goto meta_command_exit; |
| 30976 | } |
| 30977 | } |
| 30978 | if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0) |
| @@ -29590,46 +31013,47 @@ | |
| 31013 | if( zOp==0 ) continue; |
| 31014 | if( zSql==0 ) continue; |
| 31015 | if( zAns==0 ) continue; |
| 31016 | k = 0; |
| 31017 | if( bVerbose>0 ){ |
| 31018 | sqlite3_fprintf(stdout, "%d: %s %s\n", tno, zOp, zSql); |
| 31019 | } |
| 31020 | if( cli_strcmp(zOp,"memo")==0 ){ |
| 31021 | sqlite3_fprintf(p->out, "%s\n", zSql); |
| 31022 | }else |
| 31023 | if( cli_strcmp(zOp,"run")==0 ){ |
| 31024 | char *zErrMsg = 0; |
| 31025 | str.n = 0; |
| 31026 | str.z[0] = 0; |
| 31027 | rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); |
| 31028 | nTest++; |
| 31029 | if( bVerbose ){ |
| 31030 | sqlite3_fprintf(p->out, "Result: %s\n", str.z); |
| 31031 | } |
| 31032 | if( rc || zErrMsg ){ |
| 31033 | nErr++; |
| 31034 | rc = 1; |
| 31035 | sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg); |
| 31036 | sqlite3_free(zErrMsg); |
| 31037 | }else if( cli_strcmp(zAns,str.z)!=0 ){ |
| 31038 | nErr++; |
| 31039 | rc = 1; |
| 31040 | sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns); |
| 31041 | sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z); |
| 31042 | } |
| 31043 | } |
| 31044 | else{ |
| 31045 | sqlite3_fprintf(stderr, |
| 31046 | "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); |
| 31047 | rc = 1; |
| 31048 | break; |
| 31049 | } |
| 31050 | } /* End loop over rows of content from SELFTEST */ |
| 31051 | sqlite3_finalize(pStmt); |
| 31052 | } /* End loop over k */ |
| 31053 | freeText(&str); |
| 31054 | sqlite3_fprintf(p->out, "%d errors out of %d tests\n", nErr, nTest); |
| 31055 | }else |
| 31056 | |
| 31057 | if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ |
| 31058 | if( nArg<2 || nArg>3 ){ |
| 31059 | eputz("Usage: .separator COL ?ROW?\n"); |
| @@ -29673,11 +31097,12 @@ | |
| 31097 | }else |
| 31098 | if( cli_strcmp(z,"debug")==0 ){ |
| 31099 | bDebug = 1; |
| 31100 | }else |
| 31101 | { |
| 31102 | sqlite3_fprintf(stderr, |
| 31103 | "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); |
| 31104 | showHelp(p->out, azArg[0]); |
| 31105 | rc = 1; |
| 31106 | goto meta_command_exit; |
| 31107 | } |
| 31108 | }else if( zLike ){ |
| @@ -29751,11 +31176,11 @@ | |
| 31176 | } |
| 31177 | shell_check_oom(zSql); |
| 31178 | freeText(&sQuery); |
| 31179 | freeText(&sSql); |
| 31180 | if( bDebug ){ |
| 31181 | sqlite3_fprintf(p->out, "%s\n", zSql); |
| 31182 | }else{ |
| 31183 | shell_exec(p, zSql, 0); |
| 31184 | } |
| 31185 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 31186 | { |
| @@ -29781,11 +31206,11 @@ | |
| 31206 | "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n" |
| 31207 | "|| ' AND typeof('||cname||')=''text'' ',\n" |
| 31208 | "' OR ') as query, tname from tabcols group by tname)" |
| 31209 | , zRevText); |
| 31210 | shell_check_oom(zRevText); |
| 31211 | if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zRevText); |
| 31212 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 31213 | if( lrc!=SQLITE_OK ){ |
| 31214 | /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the |
| 31215 | ** user does cruel and unnatural things like ".limit expr_depth 0". */ |
| 31216 | rc = 1; |
| @@ -29794,19 +31219,20 @@ | |
| 31219 | lrc = SQLITE_ROW==sqlite3_step(pStmt); |
| 31220 | if( lrc ){ |
| 31221 | const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); |
| 31222 | sqlite3_stmt *pCheckStmt; |
| 31223 | lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); |
| 31224 | if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zGenQuery); |
| 31225 | if( lrc!=SQLITE_OK ){ |
| 31226 | rc = 1; |
| 31227 | }else{ |
| 31228 | if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ |
| 31229 | double countIrreversible = sqlite3_column_double(pCheckStmt, 0); |
| 31230 | if( countIrreversible>0 ){ |
| 31231 | int sz = (int)(countIrreversible + 0.5); |
| 31232 | sqlite3_fprintf(stderr, |
| 31233 | "Digest includes %d invalidly encoded text field%s.\n", |
| 31234 | sz, (sz>1)? "s": ""); |
| 31235 | } |
| 31236 | } |
| 31237 | sqlite3_finalize(pCheckStmt); |
| 31238 | } |
| @@ -29836,15 +31262,15 @@ | |
| 31262 | zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); |
| 31263 | for(i=2; i<nArg && zCmd!=0; i++){ |
| 31264 | zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", |
| 31265 | zCmd, azArg[i]); |
| 31266 | } |
| 31267 | /*consoleRestore();*/ |
| 31268 | x = zCmd!=0 ? system(zCmd) : 1; |
| 31269 | /*consoleRenewSetup();*/ |
| 31270 | sqlite3_free(zCmd); |
| 31271 | if( x ) sqlite3_fprintf(stderr,"System command returns %d\n", x); |
| 31272 | }else |
| 31273 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ |
| 31274 | |
| 31275 | if( c=='s' && cli_strncmp(azArg[0], "show", n)==0 ){ |
| 31276 | static const char *azBool[] = { "off", "on", "trigger", "full"}; |
| @@ -29853,50 +31279,52 @@ | |
| 31279 | if( nArg!=1 ){ |
| 31280 | eputz("Usage: .show\n"); |
| 31281 | rc = 1; |
| 31282 | goto meta_command_exit; |
| 31283 | } |
| 31284 | sqlite3_fprintf(p->out, "%12.12s: %s\n","echo", |
| 31285 | azBool[ShellHasFlag(p, SHFLG_Echo)]); |
| 31286 | sqlite3_fprintf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); |
| 31287 | sqlite3_fprintf(p->out, "%12.12s: %s\n","explain", |
| 31288 | p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); |
| 31289 | sqlite3_fprintf(p->out, "%12.12s: %s\n","headers", |
| 31290 | azBool[p->showHeader!=0]); |
| 31291 | if( p->mode==MODE_Column |
| 31292 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 31293 | ){ |
| 31294 | sqlite3_fprintf(p->out, |
| 31295 | "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", |
| 31296 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 31297 | p->cmOpts.bWordWrap ? "on" : "off", |
| 31298 | p->cmOpts.bQuote ? "" : "no"); |
| 31299 | }else{ |
| 31300 | sqlite3_fprintf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); |
| 31301 | } |
| 31302 | sqlite3_fprintf(p->out, "%12.12s: ", "nullvalue"); |
| 31303 | output_c_string(p->out, p->nullValue); |
| 31304 | sqlite3_fputs("\n", p->out); |
| 31305 | sqlite3_fprintf(p->out, "%12.12s: %s\n","output", |
| 31306 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 31307 | sqlite3_fprintf(p->out, "%12.12s: ", "colseparator"); |
| 31308 | output_c_string(p->out, p->colSeparator); |
| 31309 | sqlite3_fputs("\n", p->out); |
| 31310 | sqlite3_fprintf(p->out, "%12.12s: ", "rowseparator"); |
| 31311 | output_c_string(p->out, p->rowSeparator); |
| 31312 | sqlite3_fputs("\n", p->out); |
| 31313 | switch( p->statsOn ){ |
| 31314 | case 0: zOut = "off"; break; |
| 31315 | default: zOut = "on"; break; |
| 31316 | case 2: zOut = "stmt"; break; |
| 31317 | case 3: zOut = "vmstep"; break; |
| 31318 | } |
| 31319 | sqlite3_fprintf(p->out, "%12.12s: %s\n","stats", zOut); |
| 31320 | sqlite3_fprintf(p->out, "%12.12s: ", "width"); |
| 31321 | for (i=0;i<p->nWidth;i++) { |
| 31322 | sqlite3_fprintf(p->out, "%d ", p->colWidth[i]); |
| 31323 | } |
| 31324 | sqlite3_fputs("\n", p->out); |
| 31325 | sqlite3_fprintf(p->out, "%12.12s: %s\n", "filename", |
| 31326 | p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); |
| 31327 | }else |
| 31328 | |
| 31329 | if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){ |
| 31330 | if( nArg==2 ){ |
| @@ -30010,13 +31438,14 @@ | |
| 31438 | if( nPrintCol<1 ) nPrintCol = 1; |
| 31439 | nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; |
| 31440 | for(i=0; i<nPrintRow; i++){ |
| 31441 | for(j=i; j<nRow; j+=nPrintRow){ |
| 31442 | char *zSp = j<nPrintRow ? "" : " "; |
| 31443 | sqlite3_fprintf(p->out, |
| 31444 | "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); |
| 31445 | } |
| 31446 | sqlite3_fputs("\n", p->out); |
| 31447 | } |
| 31448 | } |
| 31449 | |
| 31450 | for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); |
| 31451 | sqlite3_free(azResult); |
| @@ -30024,11 +31453,11 @@ | |
| 31453 | |
| 31454 | #ifndef SQLITE_SHELL_FIDDLE |
| 31455 | /* Begin redirecting output to the file "testcase-out.txt" */ |
| 31456 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 31457 | output_reset(p); |
| 31458 | p->out = output_file_open("testcase-out.txt"); |
| 31459 | if( p->out==0 ){ |
| 31460 | eputz("Error: cannot open 'testcase-out.txt'\n"); |
| 31461 | } |
| 31462 | if( nArg>=2 ){ |
| 31463 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| @@ -30068,11 +31497,10 @@ | |
| 31497 | {"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, |
| 31498 | {"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, |
| 31499 | {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, |
| 31500 | {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, |
| 31501 | {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, |
| 31502 | }; |
| 31503 | int testctrl = -1; |
| 31504 | int iCtrl = -1; |
| 31505 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 31506 | int isOk = 0; |
| @@ -30088,14 +31516,14 @@ | |
| 31516 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 31517 | } |
| 31518 | |
| 31519 | /* --help lists all test-controls */ |
| 31520 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 31521 | sqlite3_fputs("Available test-controls:\n", p->out); |
| 31522 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 31523 | if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue; |
| 31524 | sqlite3_fprintf(p->out, " .testctrl %s %s\n", |
| 31525 | aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 31526 | } |
| 31527 | rc = 1; |
| 31528 | goto meta_command_exit; |
| 31529 | } |
| @@ -30108,19 +31536,19 @@ | |
| 31536 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 31537 | if( testctrl<0 ){ |
| 31538 | testctrl = aCtrl[i].ctrlCode; |
| 31539 | iCtrl = i; |
| 31540 | }else{ |
| 31541 | sqlite3_fprintf(stderr,"Error: ambiguous test-control: \"%s\"\n" |
| 31542 | "Use \".testctrl --help\" for help\n", zCmd); |
| 31543 | rc = 1; |
| 31544 | goto meta_command_exit; |
| 31545 | } |
| 31546 | } |
| 31547 | } |
| 31548 | if( testctrl<0 ){ |
| 31549 | sqlite3_fprintf(stderr,"Error: unknown test-control: %s\n" |
| 31550 | "Use \".testctrl --help\" for help\n", zCmd); |
| 31551 | }else{ |
| 31552 | switch(testctrl){ |
| 31553 | |
| 31554 | /* Special processing for .testctrl opt MASK ... |
| @@ -30170,11 +31598,13 @@ | |
| 31598 | { 0x10000000, 1, "OrderBySubq" }, |
| 31599 | { 0xffffffff, 0, "All" }, |
| 31600 | }; |
| 31601 | unsigned int curOpt; |
| 31602 | unsigned int newOpt; |
| 31603 | unsigned int m; |
| 31604 | int ii; |
| 31605 | int nOff; |
| 31606 | sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt); |
| 31607 | newOpt = curOpt; |
| 31608 | for(ii=2; ii<nArg; ii++){ |
| 31609 | const char *z = azArg[ii]; |
| 31610 | int useLabel = 0; |
| @@ -30192,16 +31622,17 @@ | |
| 31622 | int jj; |
| 31623 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 31624 | if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break; |
| 31625 | } |
| 31626 | if( jj>=ArraySize(aLabel) ){ |
| 31627 | sqlite3_fprintf(stderr, |
| 31628 | "Error: no such optimization: \"%s\"\n", zLabel); |
| 31629 | sqlite3_fputs("Should be one of:", stderr); |
| 31630 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 31631 | sqlite3_fprintf(stderr," %s", aLabel[jj].zLabel); |
| 31632 | } |
| 31633 | sqlite3_fputs("\n", stderr); |
| 31634 | rc = 1; |
| 31635 | goto meta_command_exit; |
| 31636 | } |
| 31637 | if( useLabel=='+' ){ |
| 31638 | newOpt &= ~aLabel[jj].mask; |
| @@ -30210,28 +31641,32 @@ | |
| 31641 | } |
| 31642 | } |
| 31643 | } |
| 31644 | if( curOpt!=newOpt ){ |
| 31645 | sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt); |
| 31646 | } |
| 31647 | for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){ |
| 31648 | if( m & newOpt ) nOff++; |
| 31649 | } |
| 31650 | if( nOff<12 ){ |
| 31651 | sqlite3_fputs("+All", p->out); |
| 31652 | for(ii=0; ii<ArraySize(aLabel); ii++){ |
| 31653 | if( !aLabel[ii].bDsply ) continue; |
| 31654 | if( (newOpt & aLabel[ii].mask)!=0 ){ |
| 31655 | sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel); |
| 31656 | } |
| 31657 | } |
| 31658 | }else{ |
| 31659 | sqlite3_fputs("-All", p->out); |
| 31660 | for(ii=0; ii<ArraySize(aLabel); ii++){ |
| 31661 | if( !aLabel[ii].bDsply ) continue; |
| 31662 | if( (newOpt & aLabel[ii].mask)==0 ){ |
| 31663 | sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel); |
| 31664 | } |
| 31665 | } |
| 31666 | } |
| 31667 | sqlite3_fputs("\n", p->out); |
| 31668 | rc2 = isOk = 3; |
| 31669 | break; |
| 31670 | } |
| 31671 | |
| 31672 | /* sqlite3_test_control(int, db, int) */ |
| @@ -30267,11 +31702,11 @@ | |
| 31702 | if( nArg==3 || nArg==4 ){ |
| 31703 | int ii = (int)integerValue(azArg[2]); |
| 31704 | sqlite3 *db; |
| 31705 | if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){ |
| 31706 | sqlite3_randomness(sizeof(ii),&ii); |
| 31707 | sqlite3_fprintf(stdout, "-- random seed: %d\n", ii); |
| 31708 | } |
| 31709 | if( nArg==3 ){ |
| 31710 | db = 0; |
| 31711 | }else{ |
| 31712 | db = p->db; |
| @@ -30301,25 +31736,10 @@ | |
| 31736 | rc2 = sqlite3_test_control(testctrl, opt); |
| 31737 | isOk = 3; |
| 31738 | } |
| 31739 | break; |
| 31740 | |
| 31741 | /* sqlite3_test_control(sqlite3*) */ |
| 31742 | case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: |
| 31743 | rc2 = sqlite3_test_control(testctrl, p->db); |
| 31744 | isOk = 3; |
| 31745 | break; |
| @@ -30335,11 +31755,11 @@ | |
| 31755 | break; |
| 31756 | |
| 31757 | case SQLITE_TESTCTRL_SEEK_COUNT: { |
| 31758 | u64 x = 0; |
| 31759 | rc2 = sqlite3_test_control(testctrl, p->db, &x); |
| 31760 | sqlite3_fprintf(p->out, "%llu\n", x); |
| 31761 | isOk = 3; |
| 31762 | break; |
| 31763 | } |
| 31764 | |
| 31765 | #ifdef YYCOVERAGE |
| @@ -30366,15 +31786,15 @@ | |
| 31786 | int id = 1; |
| 31787 | while(1){ |
| 31788 | int val = 0; |
| 31789 | rc2 = sqlite3_test_control(testctrl, -id, &val); |
| 31790 | if( rc2!=SQLITE_OK ) break; |
| 31791 | if( id>1 ) sqlite3_fputs(" ", p->out); |
| 31792 | sqlite3_fprintf(p->out, "%d: %d", id, val); |
| 31793 | id++; |
| 31794 | } |
| 31795 | if( id>1 ) sqlite3_fputs("\n", p->out); |
| 31796 | isOk = 3; |
| 31797 | } |
| 31798 | break; |
| 31799 | } |
| 31800 | #endif |
| @@ -30412,18 +31832,26 @@ | |
| 31832 | }else if( cli_strcmp(z,"reset")==0 ){ |
| 31833 | faultsim_state.iCnt = faultsim_state.nSkip; |
| 31834 | faultsim_state.nHit = 0; |
| 31835 | sqlite3_test_control(testctrl, faultsim_callback); |
| 31836 | }else if( cli_strcmp(z,"status")==0 ){ |
| 31837 | sqlite3_fprintf(p->out, "faultsim.iId: %d\n", |
| 31838 | faultsim_state.iId); |
| 31839 | sqlite3_fprintf(p->out, "faultsim.iErr: %d\n", |
| 31840 | faultsim_state.iErr); |
| 31841 | sqlite3_fprintf(p->out, "faultsim.iCnt: %d\n", |
| 31842 | faultsim_state.iCnt); |
| 31843 | sqlite3_fprintf(p->out, "faultsim.nHit: %d\n", |
| 31844 | faultsim_state.nHit); |
| 31845 | sqlite3_fprintf(p->out, "faultsim.iInterval: %d\n", |
| 31846 | faultsim_state.iInterval); |
| 31847 | sqlite3_fprintf(p->out, "faultsim.eVerbose: %d\n", |
| 31848 | faultsim_state.eVerbose); |
| 31849 | sqlite3_fprintf(p->out, "faultsim.nRepeat: %d\n", |
| 31850 | faultsim_state.nRepeat); |
| 31851 | sqlite3_fprintf(p->out, "faultsim.nSkip: %d\n", |
| 31852 | faultsim_state.nSkip); |
| 31853 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 31854 | if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; |
| 31855 | }else if( cli_strcmp(z,"-q")==0 ){ |
| 31856 | if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--; |
| 31857 | }else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){ |
| @@ -30437,19 +31865,20 @@ | |
| 31865 | }else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){ |
| 31866 | faultsim_state.nSkip = atoi(azArg[++kk]); |
| 31867 | }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ |
| 31868 | bShowHelp = 1; |
| 31869 | }else{ |
| 31870 | sqlite3_fprintf(stderr, |
| 31871 | "Unrecognized fault_install argument: \"%s\"\n", |
| 31872 | azArg[kk]); |
| 31873 | rc = 1; |
| 31874 | bShowHelp = 1; |
| 31875 | break; |
| 31876 | } |
| 31877 | } |
| 31878 | if( bShowHelp ){ |
| 31879 | sqlite3_fputs( |
| 31880 | "Usage: .testctrl fault_install ARGS\n" |
| 31881 | "Possible arguments:\n" |
| 31882 | " off Disable faultsim\n" |
| 31883 | " on Activate faultsim\n" |
| 31884 | " reset Reset the trigger counter\n" |
| @@ -30459,23 +31888,25 @@ | |
| 31888 | " --errcode N When triggered, return N as error code\n" |
| 31889 | " --id ID Trigger only for the ID specified\n" |
| 31890 | " --interval N Trigger only after every N-th call\n" |
| 31891 | " --repeat N Turn off after N hits. 0 means never\n" |
| 31892 | " --skip N Skip the first N encounters\n" |
| 31893 | ,p->out |
| 31894 | ); |
| 31895 | } |
| 31896 | break; |
| 31897 | } |
| 31898 | } |
| 31899 | } |
| 31900 | if( isOk==0 && iCtrl>=0 ){ |
| 31901 | sqlite3_fprintf(p->out, |
| 31902 | "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| 31903 | rc = 1; |
| 31904 | }else if( isOk==1 ){ |
| 31905 | sqlite3_fprintf(p->out, "%d\n", rc2); |
| 31906 | }else if( isOk==2 ){ |
| 31907 | sqlite3_fprintf(p->out, "0x%08x\n", rc2); |
| 31908 | } |
| 31909 | }else |
| 31910 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 31911 | |
| 31912 | if( c=='t' && n>4 && cli_strncmp(azArg[0], "timeout", n)==0 ){ |
| @@ -30526,17 +31957,17 @@ | |
| 31957 | } |
| 31958 | else if( optionMatch(z, "close") ){ |
| 31959 | mType |= SQLITE_TRACE_CLOSE; |
| 31960 | } |
| 31961 | else { |
| 31962 | sqlite3_fprintf(stderr,"Unknown option \"%s\" on \".trace\"\n", z); |
| 31963 | rc = 1; |
| 31964 | goto meta_command_exit; |
| 31965 | } |
| 31966 | }else{ |
| 31967 | output_file_close(p->traceOut); |
| 31968 | p->traceOut = output_file_open(z); |
| 31969 | } |
| 31970 | } |
| 31971 | if( p->traceOut==0 ){ |
| 31972 | sqlite3_trace_v2(p->db, 0, 0, 0); |
| 31973 | }else{ |
| @@ -30569,103 +32000,40 @@ | |
| 32000 | } |
| 32001 | } |
| 32002 | }else |
| 32003 | #endif |
| 32004 | |
| 32005 | if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ |
| 32006 | char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; |
| 32007 | sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/, |
| 32008 | sqlite3_libversion(), sqlite3_sourceid()); |
| 32009 | #if SQLITE_HAVE_ZLIB |
| 32010 | sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion()); |
| 32011 | #endif |
| 32012 | #define CTIMEOPT_VAL_(opt) #opt |
| 32013 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 32014 | #if defined(__clang__) && defined(__clang_major__) |
| 32015 | sqlite3_fprintf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." |
| 32016 | CTIMEOPT_VAL(__clang_minor__) "." |
| 32017 | CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); |
| 32018 | #elif defined(_MSC_VER) |
| 32019 | sqlite3_fprintf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); |
| 32020 | #elif defined(__GNUC__) && defined(__VERSION__) |
| 32021 | sqlite3_fprintf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); |
| 32022 | #endif |
| 32023 | }else |
| 32024 | |
| 32025 | if( c=='v' && cli_strncmp(azArg[0], "vfsinfo", n)==0 ){ |
| 32026 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 32027 | sqlite3_vfs *pVfs = 0; |
| 32028 | if( p->db ){ |
| 32029 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); |
| 32030 | if( pVfs ){ |
| 32031 | sqlite3_fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); |
| 32032 | sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
| 32033 | sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 32034 | sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 32035 | } |
| 32036 | } |
| 32037 | }else |
| 32038 | |
| 32039 | if( c=='v' && cli_strncmp(azArg[0], "vfslist", n)==0 ){ |
| @@ -30673,17 +32041,17 @@ | |
| 32041 | sqlite3_vfs *pCurrent = 0; |
| 32042 | if( p->db ){ |
| 32043 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); |
| 32044 | } |
| 32045 | for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ |
| 32046 | sqlite3_fprintf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, |
| 32047 | pVfs==pCurrent ? " <--- CURRENT" : ""); |
| 32048 | sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
| 32049 | sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 32050 | sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 32051 | if( pVfs->pNext ){ |
| 32052 | sqlite3_fputs("-----------------------------------\n", p->out); |
| 32053 | } |
| 32054 | } |
| 32055 | }else |
| 32056 | |
| 32057 | if( c=='v' && cli_strncmp(azArg[0], "vfsname", n)==0 ){ |
| @@ -30690,11 +32058,11 @@ | |
| 32058 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 32059 | char *zVfsName = 0; |
| 32060 | if( p->db ){ |
| 32061 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); |
| 32062 | if( zVfsName ){ |
| 32063 | sqlite3_fprintf(p->out, "%s\n", zVfsName); |
| 32064 | sqlite3_free(zVfsName); |
| 32065 | } |
| 32066 | } |
| 32067 | }else |
| 32068 | |
| @@ -30714,11 +32082,11 @@ | |
| 32082 | p->colWidth[j-1] = (int)integerValue(azArg[j]); |
| 32083 | } |
| 32084 | }else |
| 32085 | |
| 32086 | { |
| 32087 | sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: " |
| 32088 | " \"%s\". Enter \".help\" for help\n", azArg[0]); |
| 32089 | rc = 1; |
| 32090 | } |
| 32091 | |
| 32092 | meta_command_exit: |
| @@ -30755,11 +32123,10 @@ | |
| 32123 | SCAN_TRACKER_REFTYPE pst){ |
| 32124 | char cin; |
| 32125 | char cWait = (char)qss; /* intentional narrowing loss */ |
| 32126 | if( cWait==0 ){ |
| 32127 | PlainScan: |
| 32128 | while( (cin = *zLine++)!=0 ){ |
| 32129 | if( IsSpace(cin) ) |
| 32130 | continue; |
| 32131 | switch (cin){ |
| 32132 | case '-': |
| @@ -30807,11 +32174,10 @@ | |
| 32174 | switch( cWait ){ |
| 32175 | case '*': |
| 32176 | if( *zLine != '/' ) |
| 32177 | continue; |
| 32178 | ++zLine; |
| 32179 | CONTINUE_PROMPT_AWAITC(pst, 0); |
| 32180 | qss = QSS_SETV(qss, 0); |
| 32181 | goto PlainScan; |
| 32182 | case '`': case '\'': case '"': |
| 32183 | if(*zLine==cWait){ |
| @@ -30819,11 +32185,10 @@ | |
| 32185 | ++zLine; |
| 32186 | continue; |
| 32187 | } |
| 32188 | deliberate_fall_through; |
| 32189 | case ']': |
| 32190 | CONTINUE_PROMPT_AWAITC(pst, 0); |
| 32191 | qss = QSS_SETV(qss, 0); |
| 32192 | goto PlainScan; |
| 32193 | default: assert(0); |
| 32194 | } |
| @@ -30966,11 +32331,11 @@ | |
| 32331 | open_db(p, 0); |
| 32332 | if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
| 32333 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 32334 | BEGIN_TIMER; |
| 32335 | rc = shell_exec(p, zSql, &zErrMsg); |
| 32336 | END_TIMER(p->out); |
| 32337 | if( rc || zErrMsg ){ |
| 32338 | char zPrefix[100]; |
| 32339 | const char *zErrorTail; |
| 32340 | const char *zErrorType; |
| 32341 | if( zErrMsg==0 ){ |
| @@ -30990,28 +32355,31 @@ | |
| 32355 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
| 32356 | "%s near line %d:", zErrorType, startline); |
| 32357 | }else{ |
| 32358 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType); |
| 32359 | } |
| 32360 | sqlite3_fprintf(stderr,"%s %s\n", zPrefix, zErrorTail); |
| 32361 | sqlite3_free(zErrMsg); |
| 32362 | zErrMsg = 0; |
| 32363 | return 1; |
| 32364 | }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ |
| 32365 | char zLineBuf[2000]; |
| 32366 | sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, |
| 32367 | "changes: %lld total_changes: %lld", |
| 32368 | sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); |
| 32369 | sqlite3_fprintf(p->out, "%s\n", zLineBuf); |
| 32370 | } |
| 32371 | |
| 32372 | if( doAutoDetectRestore(p, zSql) ) return 1; |
| 32373 | return 0; |
| 32374 | } |
| 32375 | |
| 32376 | static void echo_group_input(ShellState *p, const char *zDo){ |
| 32377 | if( ShellHasFlag(p, SHFLG_Echo) ){ |
| 32378 | sqlite3_fprintf(p->out, "%s\n", zDo); |
| 32379 | fflush(p->out); |
| 32380 | } |
| 32381 | } |
| 32382 | |
| 32383 | #ifdef SQLITE_SHELL_FIDDLE |
| 32384 | /* |
| 32385 | ** Alternate one_input_line() impl for wasm mode. This is not in the primary |
| @@ -31065,11 +32433,11 @@ | |
| 32433 | i64 startline = 0; /* Line number for start of current input */ |
| 32434 | QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ |
| 32435 | |
| 32436 | if( p->inputNesting==MAX_INPUT_NESTING ){ |
| 32437 | /* This will be more informative in a later version. */ |
| 32438 | sqlite3_fprintf(stderr,"Input nesting limit (%d) reached at line %d." |
| 32439 | " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); |
| 32440 | return 1; |
| 32441 | } |
| 32442 | ++p->inputNesting; |
| 32443 | p->lineno = 0; |
| @@ -31077,11 +32445,11 @@ | |
| 32445 | while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ |
| 32446 | fflush(p->out); |
| 32447 | zLine = one_input_line(p->in, zLine, nSql>0); |
| 32448 | if( zLine==0 ){ |
| 32449 | /* End of input */ |
| 32450 | if( p->in==0 && stdin_is_interactive ) sqlite3_fputs("\n", p->out); |
| 32451 | break; |
| 32452 | } |
| 32453 | if( seenInterrupt ){ |
| 32454 | if( p->in!=0 ) break; |
| 32455 | seenInterrupt = 0; |
| @@ -31297,19 +32665,19 @@ | |
| 32665 | } |
| 32666 | zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); |
| 32667 | shell_check_oom(zBuf); |
| 32668 | sqliterc = zBuf; |
| 32669 | } |
| 32670 | p->in = sqlite3_fopen(sqliterc,"rb"); |
| 32671 | if( p->in ){ |
| 32672 | if( stdin_is_interactive ){ |
| 32673 | sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc); |
| 32674 | } |
| 32675 | if( process_input(p) && bail_on_error ) exit(1); |
| 32676 | fclose(p->in); |
| 32677 | }else if( sqliterc_override!=0 ){ |
| 32678 | sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc); |
| 32679 | if( bail_on_error ) exit(1); |
| 32680 | } |
| 32681 | p->in = inSaved; |
| 32682 | p->lineno = savedLineno; |
| 32683 | sqlite3_free(zBuf); |
| @@ -31374,23 +32742,21 @@ | |
| 32742 | " -table set output mode to 'table'\n" |
| 32743 | " -tabs set output mode to 'tabs'\n" |
| 32744 | " -unsafe-testing allow unsafe commands and modes for testing\n" |
| 32745 | " -version show SQLite version\n" |
| 32746 | " -vfs NAME use NAME as the default VFS\n" |
| 32747 | " -vfstrace enable tracing of all VFS calls\n" |
| 32748 | #ifdef SQLITE_HAVE_ZLIB |
| 32749 | " -zip open the file as a ZIP Archive\n" |
| 32750 | #endif |
| 32751 | ; |
| 32752 | static void usage(int showDetail){ |
| 32753 | sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL]]\n" |
| 32754 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 32755 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 32756 | if( showDetail ){ |
| 32757 | sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); |
| 32758 | }else{ |
| 32759 | eputz("Use the -help option for additional information\n"); |
| 32760 | } |
| 32761 | exit(0); |
| 32762 | } |
| @@ -31411,10 +32777,13 @@ | |
| 32777 | */ |
| 32778 | static void main_init(ShellState *data) { |
| 32779 | memset(data, 0, sizeof(*data)); |
| 32780 | data->normalMode = data->cMode = data->mode = MODE_List; |
| 32781 | data->autoExplain = 1; |
| 32782 | #ifdef _WIN32 |
| 32783 | data->crlfMode = 1; |
| 32784 | #endif |
| 32785 | data->pAuxDb = &data->aAuxDb[0]; |
| 32786 | memcpy(data->colSeparator,SEP_Column, 2); |
| 32787 | memcpy(data->rowSeparator,SEP_Row, 2); |
| 32788 | data->showHeader = 0; |
| 32789 | data->shellFlgs = SHFLG_Lookaside; |
| @@ -31446,29 +32815,39 @@ | |
| 32815 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 32816 | #endif |
| 32817 | } |
| 32818 | #else |
| 32819 | static void printBold(const char *zText){ |
| 32820 | sqlite3_fprintf(stdout, "\033[1m%s\033[0m", zText); |
| 32821 | } |
| 32822 | #endif |
| 32823 | |
| 32824 | /* |
| 32825 | ** Get the argument to an --option. Throw an error and die if no argument |
| 32826 | ** is available. |
| 32827 | */ |
| 32828 | static char *cmdline_option_value(int argc, char **argv, int i){ |
| 32829 | if( i==argc ){ |
| 32830 | sqlite3_fprintf(stderr, |
| 32831 | "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); |
| 32832 | exit(1); |
| 32833 | } |
| 32834 | return argv[i]; |
| 32835 | } |
| 32836 | |
| 32837 | static void sayAbnormalExit(void){ |
| 32838 | if( seenInterrupt ) eputz("Program interrupted.\n"); |
| 32839 | } |
| 32840 | |
| 32841 | /* Routine to output from vfstrace |
| 32842 | */ |
| 32843 | static int vfstraceOut(const char *z, void *pArg){ |
| 32844 | ShellState *p = (ShellState*)pArg; |
| 32845 | sqlite3_fputs(z, p->out); |
| 32846 | fflush(p->out); |
| 32847 | return 1; |
| 32848 | } |
| 32849 | |
| 32850 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 32851 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 32852 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 32853 | # define SQLITE_SHELL_IS_UTF8 (0) |
| @@ -31493,19 +32872,19 @@ | |
| 32872 | char *zErrMsg = 0; |
| 32873 | #ifdef SQLITE_SHELL_FIDDLE |
| 32874 | # define data shellState |
| 32875 | #else |
| 32876 | ShellState data; |
| 32877 | #endif |
| 32878 | const char *zInitFile = 0; |
| 32879 | int i; |
| 32880 | int rc = 0; |
| 32881 | int warnInmemoryDb = 0; |
| 32882 | int readStdin = 1; |
| 32883 | int nCmd = 0; |
| 32884 | int nOptsEnd = argc; |
| 32885 | int bEnableVfstrace = 0; |
| 32886 | char **azCmd = 0; |
| 32887 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 32888 | #if !SQLITE_SHELL_IS_UTF8 |
| 32889 | char **argvToFree = 0; |
| 32890 | int argcToFree = 0; |
| @@ -31515,25 +32894,29 @@ | |
| 32894 | #ifdef SQLITE_SHELL_FIDDLE |
| 32895 | stdin_is_interactive = 0; |
| 32896 | stdout_is_console = 1; |
| 32897 | data.wasm.zDefaultDbName = "/fiddle.sqlite3"; |
| 32898 | #else |
| 32899 | stdin_is_interactive = isatty(0); |
| 32900 | stdout_is_console = isatty(1); |
| 32901 | #endif |
| 32902 | atexit(sayAbnormalExit); |
| 32903 | #ifdef SQLITE_DEBUG |
| 32904 | mem_main_enter = sqlite3_memory_used(); |
| 32905 | #endif |
| 32906 | #if !defined(_WIN32_WCE) |
| 32907 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 32908 | if( isatty(0) && isatty(2) ){ |
| 32909 | char zLine[100]; |
| 32910 | sqlite3_fprintf(stderr, |
| 32911 | "attach debugger to process %d and press ENTER to continue...", |
| 32912 | GETPID()); |
| 32913 | if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0 |
| 32914 | && cli_strcmp(zLine,"stop")==0 |
| 32915 | ){ |
| 32916 | exit(1); |
| 32917 | } |
| 32918 | }else{ |
| 32919 | #if defined(_WIN32) || defined(WIN32) |
| 32920 | #if SQLITE_OS_WINRT |
| 32921 | __debugbreak(); |
| 32922 | #else |
| @@ -31554,11 +32937,12 @@ | |
| 32937 | } |
| 32938 | #endif |
| 32939 | |
| 32940 | #if USE_SYSTEM_SQLITE+0!=1 |
| 32941 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 32942 | sqlite3_fprintf(stderr, |
| 32943 | "SQLite header and source version mismatch\n%s\n%s\n", |
| 32944 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 32945 | exit(1); |
| 32946 | } |
| 32947 | #endif |
| 32948 | main_init(&data); |
| @@ -31696,21 +33080,12 @@ | |
| 33080 | switch( n ){ |
| 33081 | case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break; |
| 33082 | case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break; |
| 33083 | default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break; |
| 33084 | } |
| 33085 | }else if( cli_strcmp(z,"-vfstrace")==0 ){ |
| 33086 | bEnableVfstrace = 1; |
| 33087 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 33088 | }else if( cli_strcmp(z,"-multiplex")==0 ){ |
| 33089 | extern int sqlite3_multiplex_initialize(const char*,int); |
| 33090 | sqlite3_multiplex_initialize(0, 1); |
| 33091 | #endif |
| @@ -31762,11 +33137,11 @@ | |
| 33137 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 33138 | /* no-op - catch this on the second pass */ |
| 33139 | } |
| 33140 | } |
| 33141 | #ifndef SQLITE_SHELL_FIDDLE |
| 33142 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 33143 | #endif |
| 33144 | |
| 33145 | |
| 33146 | #ifdef SQLITE_SHELL_INIT_PROC |
| 33147 | { |
| @@ -31786,25 +33161,29 @@ | |
| 33161 | if( zVfs ){ |
| 33162 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 33163 | if( pVfs ){ |
| 33164 | sqlite3_vfs_register(pVfs, 1); |
| 33165 | }else{ |
| 33166 | sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 33167 | exit(1); |
| 33168 | } |
| 33169 | } |
| 33170 | |
| 33171 | if( data.pAuxDb->zDbFilename==0 ){ |
| 33172 | #ifndef SQLITE_OMIT_MEMORYDB |
| 33173 | data.pAuxDb->zDbFilename = ":memory:"; |
| 33174 | warnInmemoryDb = argc==1; |
| 33175 | #else |
| 33176 | sqlite3_fprintf(stderr, |
| 33177 | "%s: Error: no database filename specified\n", Argv0); |
| 33178 | return 1; |
| 33179 | #endif |
| 33180 | } |
| 33181 | data.out = stdout; |
| 33182 | if( bEnableVfstrace ){ |
| 33183 | vfstrace_register("trace",0,vfstraceOut, &data, 1); |
| 33184 | } |
| 33185 | #ifndef SQLITE_SHELL_FIDDLE |
| 33186 | sqlite3_appendvfs_init(0,0,0); |
| 33187 | #endif |
| 33188 | |
| 33189 | /* Go ahead and open the database file if it already exists. If the |
| @@ -31913,11 +33292,11 @@ | |
| 33292 | */ |
| 33293 | ShellSetFlag(&data, SHFLG_Backslash); |
| 33294 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 33295 | /* No-op. The bail_on_error flag should already be set. */ |
| 33296 | }else if( cli_strcmp(z,"-version")==0 ){ |
| 33297 | sqlite3_fprintf(stdout, "%s %s (%d-bit)\n", |
| 33298 | sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); |
| 33299 | return 0; |
| 33300 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 33301 | /* Need to check for interactive override here to so that it can |
| 33302 | ** affect console setup (for Windows only) and testing thereof. |
| @@ -31951,14 +33330,12 @@ | |
| 33330 | }else if( cli_strcmp(z,"-sorterref")==0 ){ |
| 33331 | i++; |
| 33332 | #endif |
| 33333 | }else if( cli_strcmp(z,"-vfs")==0 ){ |
| 33334 | i++; |
| 33335 | }else if( cli_strcmp(z,"-vfstrace")==0 ){ |
| 33336 | i++; |
| 33337 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 33338 | }else if( cli_strcmp(z,"-multiplex")==0 ){ |
| 33339 | i++; |
| 33340 | #endif |
| 33341 | }else if( cli_strcmp(z,"-help")==0 ){ |
| @@ -31978,18 +33355,18 @@ | |
| 33355 | rc = shell_exec(&data, z, &zErrMsg); |
| 33356 | if( zErrMsg!=0 ){ |
| 33357 | shellEmitError(zErrMsg); |
| 33358 | if( bail_on_error ) return rc!=0 ? rc : 1; |
| 33359 | }else if( rc!=0 ){ |
| 33360 | sqlite3_fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z); |
| 33361 | if( bail_on_error ) return rc; |
| 33362 | } |
| 33363 | } |
| 33364 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) |
| 33365 | }else if( cli_strncmp(z, "-A", 2)==0 ){ |
| 33366 | if( nCmd>0 ){ |
| 33367 | sqlite3_fprintf(stderr,"Error: cannot mix regular SQL or dot-commands" |
| 33368 | " with \"%s\"\n", z); |
| 33369 | return 1; |
| 33370 | } |
| 33371 | open_db(&data, OPEN_DB_ZIPFILE); |
| 33372 | if( z[2] ){ |
| @@ -32004,11 +33381,11 @@ | |
| 33381 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 33382 | data.bSafeMode = data.bSafeModePersist = 1; |
| 33383 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 33384 | /* Acted upon in first pass. */ |
| 33385 | }else{ |
| 33386 | sqlite3_fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 33387 | eputz("Use -help for a list of options.\n"); |
| 33388 | return 1; |
| 33389 | } |
| 33390 | data.cMode = data.mode; |
| 33391 | } |
| @@ -32031,11 +33408,12 @@ | |
| 33408 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33409 | if( zErrMsg || rc ){ |
| 33410 | if( zErrMsg!=0 ){ |
| 33411 | shellEmitError(zErrMsg); |
| 33412 | }else{ |
| 33413 | sqlite3_fprintf(stderr, |
| 33414 | "Error: unable to process SQL: %s\n", azCmd[i]); |
| 33415 | } |
| 33416 | sqlite3_free(zErrMsg); |
| 33417 | if( rc==0 ) rc = 1; |
| 33418 | goto shell_main_exit; |
| 33419 | } |
| @@ -32046,18 +33424,14 @@ | |
| 33424 | */ |
| 33425 | if( stdin_is_interactive ){ |
| 33426 | char *zHome; |
| 33427 | char *zHistory; |
| 33428 | int nHistory; |
| 33429 | sqlite3_fprintf(stdout, |
| 33430 | "SQLite version %s %.19s\n" /*extra-version-info*/ |
| 33431 | "Enter \".help\" for usage hints.\n", |
| 33432 | sqlite3_libversion(), sqlite3_sourceid()); |
| 33433 | if( warnInmemoryDb ){ |
| 33434 | sputz(stdout, "Connected to a "); |
| 33435 | printBold("transient in-memory database"); |
| 33436 | sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a" |
| 33437 | " persistent database.\n"); |
| @@ -32070,13 +33444,15 @@ | |
| 33444 | if( (zHistory = malloc(nHistory))!=0 ){ |
| 33445 | sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
| 33446 | } |
| 33447 | } |
| 33448 | if( zHistory ){ shell_read_history(zHistory); } |
| 33449 | #if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION) |
| 33450 | rl_attempted_completion_function = readline_completion; |
| 33451 | #elif HAVE_LINENOISE==1 |
| 33452 | linenoiseSetCompletionCallback(linenoise_completion); |
| 33453 | #elif HAVE_LINENOISE==2 |
| 33454 | linenoiseSetCompletionCallback(linenoise_completion, NULL); |
| 33455 | #endif |
| 33456 | data.in = 0; |
| 33457 | rc = process_input(&data); |
| 33458 | if( zHistory ){ |
| @@ -32122,13 +33498,16 @@ | |
| 33498 | free(data.colWidth); |
| 33499 | free(data.zNonce); |
| 33500 | /* Clear the global data structure so that valgrind will detect memory |
| 33501 | ** leaks */ |
| 33502 | memset(&data, 0, sizeof(data)); |
| 33503 | if( bEnableVfstrace ){ |
| 33504 | vfstrace_unregister("trace"); |
| 33505 | } |
| 33506 | #ifdef SQLITE_DEBUG |
| 33507 | if( sqlite3_memory_used()>mem_main_enter ){ |
| 33508 | sqlite3_fprintf(stderr,"Memory leaked: %u bytes\n", |
| 33509 | (unsigned int)(sqlite3_memory_used()-mem_main_enter)); |
| 33510 | } |
| 33511 | #endif |
| 33512 | #else /* SQLITE_SHELL_FIDDLE... */ |
| 33513 | shell_main_exit: |
| @@ -32164,11 +33543,11 @@ | |
| 33543 | return pVfs; |
| 33544 | } |
| 33545 | |
| 33546 | /* Only for emcc experimentation purposes. */ |
| 33547 | sqlite3 * fiddle_db_arg(sqlite3 *arg){ |
| 33548 | sqlite3_fprintf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg); |
| 33549 | return arg; |
| 33550 | } |
| 33551 | |
| 33552 | /* |
| 33553 | ** Intended to be called via a SharedWorker() while a separate |
| @@ -32201,11 +33580,11 @@ | |
| 33580 | while( sqlite3_txn_state(globalDb,0)>0 ){ |
| 33581 | /* |
| 33582 | ** Resolve problem reported in |
| 33583 | ** https://sqlite.org/forum/forumpost/0b41a25d65 |
| 33584 | */ |
| 33585 | sqlite3_fputs("Rolling back in-progress transaction.\n", stdout); |
| 33586 | sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0); |
| 33587 | } |
| 33588 | rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); |
| 33589 | if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0); |
| 33590 | sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); |
| 33591 |
+2361
-1405
| --- extsrc/sqlite3.c | ||
| +++ extsrc/sqlite3.c | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | /****************************************************************************** |
| 2 | 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | -** version 3.47.0. By combining all the individual C code files into this | |
| 3 | +** version 3.48.0. By combining all the individual C code files into this | |
| 4 | 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | 8 | ** translation unit. |
| @@ -16,12 +16,15 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** 7891a266c4425722ae8b9231397ef9e42e24. | |
| 21 | +** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: | |
| 22 | +** | |
| 23 | +** | |
| 22 | 24 | */ |
| 25 | +#ifndef SQLITE_AMALGAMATION | |
| 23 | 26 | #define SQLITE_CORE 1 |
| 24 | 27 | #define SQLITE_AMALGAMATION 1 |
| 25 | 28 | #ifndef SQLITE_PRIVATE |
| 26 | 29 | # define SQLITE_PRIVATE static |
| 27 | 30 | #endif |
| @@ -460,13 +463,13 @@ | ||
| 460 | 463 | ** |
| 461 | 464 | ** See also: [sqlite3_libversion()], |
| 462 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 463 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 464 | 467 | */ |
| 465 | -#define SQLITE_VERSION "3.47.0" | |
| 466 | -#define SQLITE_VERSION_NUMBER 3047000 | |
| 467 | -#define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" | |
| 468 | +#define SQLITE_VERSION "3.48.0" | |
| 469 | +#define SQLITE_VERSION_NUMBER 3048000 | |
| 470 | +#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" | |
| 468 | 471 | |
| 469 | 472 | /* |
| 470 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 471 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 472 | 475 | ** |
| @@ -966,10 +969,17 @@ | ||
| 966 | 969 | ** |
| 967 | 970 | ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying |
| 968 | 971 | ** filesystem supports doing multiple write operations atomically when those |
| 969 | 972 | ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and |
| 970 | 973 | ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. |
| 974 | +** | |
| 975 | +** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read | |
| 976 | +** from the database file in amounts that are not a multiple of the | |
| 977 | +** page size and that do not begin at a page boundary. Without this | |
| 978 | +** property, SQLite is careful to only do full-page reads and write | |
| 979 | +** on aligned pages, with the one exception that it will do a sub-page | |
| 980 | +** read of the first page to access the database header. | |
| 971 | 981 | */ |
| 972 | 982 | #define SQLITE_IOCAP_ATOMIC 0x00000001 |
| 973 | 983 | #define SQLITE_IOCAP_ATOMIC512 0x00000002 |
| 974 | 984 | #define SQLITE_IOCAP_ATOMIC1K 0x00000004 |
| 975 | 985 | #define SQLITE_IOCAP_ATOMIC2K 0x00000008 |
| @@ -982,10 +992,11 @@ | ||
| 982 | 992 | #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 |
| 983 | 993 | #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 |
| 984 | 994 | #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 |
| 985 | 995 | #define SQLITE_IOCAP_IMMUTABLE 0x00002000 |
| 986 | 996 | #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 |
| 997 | +#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 | |
| 987 | 998 | |
| 988 | 999 | /* |
| 989 | 1000 | ** CAPI3REF: File Locking Levels |
| 990 | 1001 | ** |
| 991 | 1002 | ** SQLite uses one of these integer values as the second |
| @@ -1128,10 +1139,11 @@ | ||
| 1128 | 1139 | ** <li> [SQLITE_IOCAP_SEQUENTIAL] |
| 1129 | 1140 | ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] |
| 1130 | 1141 | ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] |
| 1131 | 1142 | ** <li> [SQLITE_IOCAP_IMMUTABLE] |
| 1132 | 1143 | ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] |
| 1144 | +** <li> [SQLITE_IOCAP_SUBPAGE_READ] | |
| 1133 | 1145 | ** </ul> |
| 1134 | 1146 | ** |
| 1135 | 1147 | ** The SQLITE_IOCAP_ATOMIC property means that all writes of |
| 1136 | 1148 | ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values |
| 1137 | 1149 | ** mean that writes of blocks that are nnn bytes in size and |
| @@ -1405,10 +1417,15 @@ | ||
| 1405 | 1417 | ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This |
| 1406 | 1418 | ** opcode causes the xFileControl method to swap the file handle with the one |
| 1407 | 1419 | ** pointed to by the pArg argument. This capability is used during testing |
| 1408 | 1420 | ** and only needs to be supported when SQLITE_TEST is defined. |
| 1409 | 1421 | ** |
| 1422 | +** <li>[[SQLITE_FCNTL_NULL_IO]] | |
| 1423 | +** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor | |
| 1424 | +** or file handle for the [sqlite3_file] object such that it will no longer | |
| 1425 | +** read or write to the database file. | |
| 1426 | +** | |
| 1410 | 1427 | ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] |
| 1411 | 1428 | ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might |
| 1412 | 1429 | ** be advantageous to block on the next WAL lock if the lock is not immediately |
| 1413 | 1430 | ** available. The WAL subsystem issues this signal during rare |
| 1414 | 1431 | ** circumstances in order to fix a problem with priority inversion. |
| @@ -1558,10 +1575,11 @@ | ||
| 1558 | 1575 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1559 | 1576 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1560 | 1577 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1561 | 1578 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1562 | 1579 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1580 | +#define SQLITE_FCNTL_NULL_IO 43 | |
| 1563 | 1581 | |
| 1564 | 1582 | /* deprecated names */ |
| 1565 | 1583 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1566 | 1584 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1567 | 1585 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -2936,14 +2954,18 @@ | ||
| 2936 | 2954 | ** |
| 2937 | 2955 | ** ^These functions return the number of rows modified, inserted or |
| 2938 | 2956 | ** deleted by the most recently completed INSERT, UPDATE or DELETE |
| 2939 | 2957 | ** statement on the database connection specified by the only parameter. |
| 2940 | 2958 | ** The two functions are identical except for the type of the return value |
| 2941 | -** and that if the number of rows modified by the most recent INSERT, UPDATE | |
| 2959 | +** and that if the number of rows modified by the most recent INSERT, UPDATE, | |
| 2942 | 2960 | ** or DELETE is greater than the maximum value supported by type "int", then |
| 2943 | 2961 | ** the return value of sqlite3_changes() is undefined. ^Executing any other |
| 2944 | 2962 | ** type of SQL statement does not modify the value returned by these functions. |
| 2963 | +** For the purposes of this interface, a CREATE TABLE AS SELECT statement | |
| 2964 | +** does not count as an INSERT, UPDATE or DELETE statement and hence the rows | |
| 2965 | +** added to the new table by the CREATE TABLE AS SELECT statement are not | |
| 2966 | +** counted. | |
| 2945 | 2967 | ** |
| 2946 | 2968 | ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are |
| 2947 | 2969 | ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], |
| 2948 | 2970 | ** [foreign key actions] or [REPLACE] constraint resolution are not counted. |
| 2949 | 2971 | ** |
| @@ -4499,15 +4521,26 @@ | ||
| 4499 | 4521 | ** |
| 4500 | 4522 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4501 | 4523 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4502 | 4524 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4503 | 4525 | ** any virtual tables. |
| 4526 | +** | |
| 4527 | +** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt> | |
| 4528 | +** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler | |
| 4529 | +** errors from being sent to the error log defined by | |
| 4530 | +** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test | |
| 4531 | +** compiles to see if some SQL syntax is well-formed, without generating | |
| 4532 | +** messages on the global error log when it is not. If the test compile | |
| 4533 | +** fails, the sqlite3_prepare_v3() call returns the same error indications | |
| 4534 | +** with or without this flag; it just omits the call to [sqlite3_log()] that | |
| 4535 | +** logs the error. | |
| 4504 | 4536 | ** </dl> |
| 4505 | 4537 | */ |
| 4506 | 4538 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4507 | 4539 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4508 | 4540 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4541 | +#define SQLITE_PREPARE_DONT_LOG 0x10 | |
| 4509 | 4542 | |
| 4510 | 4543 | /* |
| 4511 | 4544 | ** CAPI3REF: Compiling An SQL Statement |
| 4512 | 4545 | ** KEYWORDS: {SQL statement compiler} |
| 4513 | 4546 | ** METHOD: sqlite3 |
| @@ -4536,17 +4569,21 @@ | ||
| 4536 | 4569 | ** and sqlite3_prepare_v3() |
| 4537 | 4570 | ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), |
| 4538 | 4571 | ** and sqlite3_prepare16_v3() use UTF-16. |
| 4539 | 4572 | ** |
| 4540 | 4573 | ** ^If the nByte argument is negative, then zSql is read up to the |
| 4541 | -** first zero terminator. ^If nByte is positive, then it is the | |
| 4542 | -** number of bytes read from zSql. ^If nByte is zero, then no prepared | |
| 4574 | +** first zero terminator. ^If nByte is positive, then it is the maximum | |
| 4575 | +** number of bytes read from zSql. When nByte is positive, zSql is read | |
| 4576 | +** up to the first zero terminator or until the nByte bytes have been read, | |
| 4577 | +** whichever comes first. ^If nByte is zero, then no prepared | |
| 4543 | 4578 | ** statement is generated. |
| 4544 | 4579 | ** If the caller knows that the supplied string is nul-terminated, then |
| 4545 | 4580 | ** there is a small performance advantage to passing an nByte parameter that |
| 4546 | 4581 | ** is the number of bytes in the input string <i>including</i> |
| 4547 | 4582 | ** the nul-terminator. |
| 4583 | +** Note that nByte measure the length of the input in bytes, not | |
| 4584 | +** characters, even for the UTF-16 interfaces. | |
| 4548 | 4585 | ** |
| 4549 | 4586 | ** ^If pzTail is not NULL then *pzTail is made to point to the first byte |
| 4550 | 4587 | ** past the end of the first SQL statement in zSql. These routines only |
| 4551 | 4588 | ** compile the first statement in zSql, so *pzTail is left pointing to |
| 4552 | 4589 | ** what remains uncompiled. |
| @@ -5913,11 +5950,11 @@ | ||
| 5913 | 5950 | ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call |
| 5914 | 5951 | ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. |
| 5915 | 5952 | ** This flag instructs SQLite to omit some corner-case optimizations that |
| 5916 | 5953 | ** might disrupt the operation of the [sqlite3_value_subtype()] function, |
| 5917 | 5954 | ** causing it to return zero rather than the correct subtype(). |
| 5918 | -** SQL functions that invokes [sqlite3_value_subtype()] should have this | |
| 5955 | +** All SQL functions that invoke [sqlite3_value_subtype()] should have this | |
| 5919 | 5956 | ** property. If the SQLITE_SUBTYPE property is omitted, then the return |
| 5920 | 5957 | ** value from [sqlite3_value_subtype()] might sometimes be zero even though |
| 5921 | 5958 | ** a non-zero subtype was specified by the function argument expression. |
| 5922 | 5959 | ** |
| 5923 | 5960 | ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> |
| @@ -8678,11 +8715,11 @@ | ||
| 8678 | 8715 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8679 | 8716 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8680 | 8717 | #define SQLITE_TESTCTRL_TRACEFLAGS 31 |
| 8681 | 8718 | #define SQLITE_TESTCTRL_TUNE 32 |
| 8682 | 8719 | #define SQLITE_TESTCTRL_LOGEST 33 |
| 8683 | -#define SQLITE_TESTCTRL_USELONGDOUBLE 34 | |
| 8720 | +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ | |
| 8684 | 8721 | #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ |
| 8685 | 8722 | |
| 8686 | 8723 | /* |
| 8687 | 8724 | ** CAPI3REF: SQL Keyword Checking |
| 8688 | 8725 | ** |
| @@ -9654,10 +9691,20 @@ | ||
| 9654 | 9691 | ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). |
| 9655 | 9692 | ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() |
| 9656 | 9693 | ** APIs are not strictly speaking threadsafe. If they are invoked at the |
| 9657 | 9694 | ** same time as another thread is invoking sqlite3_backup_step() it is |
| 9658 | 9695 | ** possible that they return invalid values. |
| 9696 | +** | |
| 9697 | +** <b>Alternatives To Using The Backup API</b> | |
| 9698 | +** | |
| 9699 | +** Other techniques for safely creating a consistent backup of an SQLite | |
| 9700 | +** database include: | |
| 9701 | +** | |
| 9702 | +** <ul> | |
| 9703 | +** <li> The [VACUUM INTO] command. | |
| 9704 | +** <li> The [sqlite3_rsync] utility program. | |
| 9705 | +** </ul> | |
| 9659 | 9706 | */ |
| 9660 | 9707 | SQLITE_API sqlite3_backup *sqlite3_backup_init( |
| 9661 | 9708 | sqlite3 *pDest, /* Destination database handle */ |
| 9662 | 9709 | const char *zDestName, /* Destination database name */ |
| 9663 | 9710 | sqlite3 *pSource, /* Source database handle */ |
| @@ -10852,10 +10899,18 @@ | ||
| 10852 | 10899 | ** schema S in database connection D. ^On success, the |
| 10853 | 10900 | ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly |
| 10854 | 10901 | ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. |
| 10855 | 10902 | ** If there is not already a read-transaction open on schema S when |
| 10856 | 10903 | ** this function is called, one is opened automatically. |
| 10904 | +** | |
| 10905 | +** If a read-transaction is opened by this function, then it is guaranteed | |
| 10906 | +** that the returned snapshot object may not be invalidated by a database | |
| 10907 | +** writer or checkpointer until after the read-transaction is closed. This | |
| 10908 | +** is not guaranteed if a read-transaction is already open when this | |
| 10909 | +** function is called. In that case, any subsequent write or checkpoint | |
| 10910 | +** operation on the database may invalidate the returned snapshot handle, | |
| 10911 | +** even while the read-transaction remains open. | |
| 10857 | 10912 | ** |
| 10858 | 10913 | ** The following must be true for this function to succeed. If any of |
| 10859 | 10914 | ** the following statements are false when sqlite3_snapshot_get() is |
| 10860 | 10915 | ** called, SQLITE_ERROR is returned. The final value of *P is undefined |
| 10861 | 10916 | ** in this case. |
| @@ -11172,11 +11227,11 @@ | ||
| 11172 | 11227 | #endif |
| 11173 | 11228 | |
| 11174 | 11229 | #if 0 |
| 11175 | 11230 | } /* End of the 'extern "C"' block */ |
| 11176 | 11231 | #endif |
| 11177 | -#endif /* SQLITE3_H */ | |
| 11232 | +/* #endif for SQLITE3_H will be added by mksqlite3.tcl */ | |
| 11178 | 11233 | |
| 11179 | 11234 | /******** Begin file sqlite3rtree.h *********/ |
| 11180 | 11235 | /* |
| 11181 | 11236 | ** 2010 August 30 |
| 11182 | 11237 | ** |
| @@ -13423,17 +13478,32 @@ | ||
| 13423 | 13478 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13424 | 13479 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13425 | 13480 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13426 | 13481 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13427 | 13482 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13428 | -** bytes. This API is not available if the specified token matches a | |
| 13429 | -** prefix query term. In that case both output variables are always set | |
| 13430 | -** to 0. | |
| 13483 | +** bytes. | |
| 13431 | 13484 | ** |
| 13432 | 13485 | ** The output text is not a copy of the document text that was tokenized. |
| 13433 | 13486 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13434 | 13487 | ** includes any embedded 0x00 and trailing data. |
| 13488 | +** | |
| 13489 | +** This API may be slow in some cases if the token identified by parameters | |
| 13490 | +** iIdx and iToken matched a prefix token in the query. In most cases, the | |
| 13491 | +** first call to this API for each prefix token in the query is forced | |
| 13492 | +** to scan the portion of the full-text index that matches the prefix | |
| 13493 | +** token to collect the extra data required by this API. If the prefix | |
| 13494 | +** token matches a large number of token instances in the document set, | |
| 13495 | +** this may be a performance problem. | |
| 13496 | +** | |
| 13497 | +** If the user knows in advance that a query may use this API for a | |
| 13498 | +** prefix token, FTS5 may be configured to collect all required data as part | |
| 13499 | +** of the initial querying of the full-text index, avoiding the second scan | |
| 13500 | +** entirely. This also causes prefix queries that do not use this API to | |
| 13501 | +** run more slowly and use more memory. FTS5 may be configured in this way | |
| 13502 | +** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] | |
| 13503 | +** option, or on a per-query basis using the | |
| 13504 | +** [fts5_insttoken | fts5_insttoken()] user function. | |
| 13435 | 13505 | ** |
| 13436 | 13506 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13437 | 13507 | ** "detail=none" or "detail=column" option. |
| 13438 | 13508 | ** |
| 13439 | 13509 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -13521,11 +13591,10 @@ | ||
| 13521 | 13591 | ** CUSTOM TOKENIZERS |
| 13522 | 13592 | ** |
| 13523 | 13593 | ** Applications may also register custom tokenizer types. A tokenizer |
| 13524 | 13594 | ** is registered by providing fts5 with a populated instance of the |
| 13525 | 13595 | ** following structure. All structure methods must be defined, setting |
| 13526 | -** | |
| 13527 | 13596 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 13528 | 13597 | ** behaviour. The structure methods are expected to function as follows: |
| 13529 | 13598 | ** |
| 13530 | 13599 | ** xCreate: |
| 13531 | 13600 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -13865,10 +13934,11 @@ | ||
| 13865 | 13934 | #endif |
| 13866 | 13935 | |
| 13867 | 13936 | #endif /* _FTS5_H */ |
| 13868 | 13937 | |
| 13869 | 13938 | /******** End of fts5.h *********/ |
| 13939 | +#endif /* SQLITE3_H */ | |
| 13870 | 13940 | |
| 13871 | 13941 | /************** End of sqlite3.h *********************************************/ |
| 13872 | 13942 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 13873 | 13943 | |
| 13874 | 13944 | /* |
| @@ -13910,10 +13980,11 @@ | ||
| 13910 | 13980 | ** to count the size: 2^31-1 or 2147483647. |
| 13911 | 13981 | */ |
| 13912 | 13982 | #ifndef SQLITE_MAX_LENGTH |
| 13913 | 13983 | # define SQLITE_MAX_LENGTH 1000000000 |
| 13914 | 13984 | #endif |
| 13985 | +#define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ | |
| 13915 | 13986 | |
| 13916 | 13987 | /* |
| 13917 | 13988 | ** This is the maximum number of |
| 13918 | 13989 | ** |
| 13919 | 13990 | ** * Columns in a table |
| @@ -14809,10 +14880,11 @@ | ||
| 14809 | 14880 | #include <stdio.h> |
| 14810 | 14881 | #include <stdlib.h> |
| 14811 | 14882 | #include <string.h> |
| 14812 | 14883 | #include <assert.h> |
| 14813 | 14884 | #include <stddef.h> |
| 14885 | +#include <ctype.h> | |
| 14814 | 14886 | |
| 14815 | 14887 | /* |
| 14816 | 14888 | ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. |
| 14817 | 14889 | ** This allows better measurements of where memcpy() is used when running |
| 14818 | 14890 | ** cachegrind. But this macro version of memcpy() is very slow so it |
| @@ -14831,11 +14903,10 @@ | ||
| 14831 | 14903 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 14832 | 14904 | # define double sqlite_int64 |
| 14833 | 14905 | # define float sqlite_int64 |
| 14834 | 14906 | # define fabs(X) ((X)<0?-(X):(X)) |
| 14835 | 14907 | # define sqlite3IsOverflow(X) 0 |
| 14836 | -# define LONGDOUBLE_TYPE sqlite_int64 | |
| 14837 | 14908 | # ifndef SQLITE_BIG_DBL |
| 14838 | 14909 | # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) |
| 14839 | 14910 | # endif |
| 14840 | 14911 | # define SQLITE_OMIT_DATETIME_FUNCS 1 |
| 14841 | 14912 | # define SQLITE_OMIT_TRACE 1 |
| @@ -15006,13 +15077,10 @@ | ||
| 15006 | 15077 | # define INT8_TYPE int8_t |
| 15007 | 15078 | # else |
| 15008 | 15079 | # define INT8_TYPE signed char |
| 15009 | 15080 | # endif |
| 15010 | 15081 | #endif |
| 15011 | -#ifndef LONGDOUBLE_TYPE | |
| 15012 | -# define LONGDOUBLE_TYPE long double | |
| 15013 | -#endif | |
| 15014 | 15082 | typedef sqlite_int64 i64; /* 8-byte signed integer */ |
| 15015 | 15083 | typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ |
| 15016 | 15084 | typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ |
| 15017 | 15085 | typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ |
| 15018 | 15086 | typedef INT16_TYPE i16; /* 2-byte signed integer */ |
| @@ -16391,10 +16459,13 @@ | ||
| 16391 | 16459 | struct KeyInfo*, /* First argument to compare function */ |
| 16392 | 16460 | BtCursor *pCursor /* Space to write cursor structure */ |
| 16393 | 16461 | ); |
| 16394 | 16462 | SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); |
| 16395 | 16463 | SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); |
| 16464 | +#ifdef SQLITE_DEBUG | |
| 16465 | +SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*); | |
| 16466 | +#endif | |
| 16396 | 16467 | SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); |
| 16397 | 16468 | SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); |
| 16398 | 16469 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 16399 | 16470 | SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); |
| 16400 | 16471 | #endif |
| @@ -17004,11 +17075,11 @@ | ||
| 17004 | 17075 | |
| 17005 | 17076 | /* |
| 17006 | 17077 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17007 | 17078 | */ |
| 17008 | 17079 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17009 | -#define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */ | |
| 17080 | +#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ | |
| 17010 | 17081 | |
| 17011 | 17082 | /* |
| 17012 | 17083 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17013 | 17084 | ** for a description of what each of these routines does. |
| 17014 | 17085 | */ |
| @@ -17719,51 +17790,15 @@ | ||
| 17719 | 17790 | struct FuncDefHash { |
| 17720 | 17791 | FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ |
| 17721 | 17792 | }; |
| 17722 | 17793 | #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) |
| 17723 | 17794 | |
| 17724 | -#if defined(SQLITE_USER_AUTHENTICATION) | |
| 17725 | -# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \ | |
| 17726 | - See ext/userauth/user-auth.txt for details." | |
| 17727 | -#endif | |
| 17728 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 17729 | -/* | |
| 17730 | -** Information held in the "sqlite3" database connection object and used | |
| 17731 | -** to manage user authentication. | |
| 17732 | -*/ | |
| 17733 | -typedef struct sqlite3_userauth sqlite3_userauth; | |
| 17734 | -struct sqlite3_userauth { | |
| 17735 | - u8 authLevel; /* Current authentication level */ | |
| 17736 | - int nAuthPW; /* Size of the zAuthPW in bytes */ | |
| 17737 | - char *zAuthPW; /* Password used to authenticate */ | |
| 17738 | - char *zAuthUser; /* User name used to authenticate */ | |
| 17739 | -}; | |
| 17740 | - | |
| 17741 | -/* Allowed values for sqlite3_userauth.authLevel */ | |
| 17742 | -#define UAUTH_Unknown 0 /* Authentication not yet checked */ | |
| 17743 | -#define UAUTH_Fail 1 /* User authentication failed */ | |
| 17744 | -#define UAUTH_User 2 /* Authenticated as a normal user */ | |
| 17745 | -#define UAUTH_Admin 3 /* Authenticated as an administrator */ | |
| 17746 | - | |
| 17747 | -/* Functions used only by user authorization logic */ | |
| 17748 | -SQLITE_PRIVATE int sqlite3UserAuthTable(const char*); | |
| 17749 | -SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*); | |
| 17750 | -SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*); | |
| 17751 | -SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); | |
| 17752 | - | |
| 17753 | -#endif /* SQLITE_USER_AUTHENTICATION */ | |
| 17754 | - | |
| 17755 | 17795 | /* |
| 17756 | 17796 | ** typedef for the authorization callback function. |
| 17757 | 17797 | */ |
| 17758 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 17759 | - typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, | |
| 17760 | - const char*, const char*); | |
| 17761 | -#else | |
| 17762 | - typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, | |
| 17763 | - const char*); | |
| 17764 | -#endif | |
| 17798 | +typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, | |
| 17799 | + const char*); | |
| 17765 | 17800 | |
| 17766 | 17801 | #ifndef SQLITE_OMIT_DEPRECATED |
| 17767 | 17802 | /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing |
| 17768 | 17803 | ** in the style of sqlite3_trace() |
| 17769 | 17804 | */ |
| @@ -17920,13 +17955,10 @@ | ||
| 17920 | 17955 | sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ |
| 17921 | 17956 | void *pUnlockArg; /* Argument to xUnlockNotify */ |
| 17922 | 17957 | void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ |
| 17923 | 17958 | sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ |
| 17924 | 17959 | #endif |
| 17925 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 17926 | - sqlite3_userauth auth; /* User authentication information */ | |
| 17927 | -#endif | |
| 17928 | 17960 | }; |
| 17929 | 17961 | |
| 17930 | 17962 | /* |
| 17931 | 17963 | ** A macro to discover the encoding of a database. |
| 17932 | 17964 | */ |
| @@ -19221,11 +19253,11 @@ | ||
| 19221 | 19253 | #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ |
| 19222 | 19254 | #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ |
| 19223 | 19255 | #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ |
| 19224 | 19256 | #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ |
| 19225 | 19257 | #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ |
| 19226 | - /* 0x80000000 // Available */ | |
| 19258 | +#define EP_SubtArg 0x80000000 /* Is argument to SQLITE_SUBTYPE function */ | |
| 19227 | 19259 | |
| 19228 | 19260 | /* The EP_Propagate mask is a set of properties that automatically propagate |
| 19229 | 19261 | ** upwards into parent nodes. |
| 19230 | 19262 | */ |
| 19231 | 19263 | #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) |
| @@ -19777,11 +19809,11 @@ | ||
| 19777 | 19809 | ** |
| 19778 | 19810 | ** SRT_Set The result must be a single column. Store each |
| 19779 | 19811 | ** row of result as the key in table pDest->iSDParm. |
| 19780 | 19812 | ** Apply the affinity pDest->affSdst before storing |
| 19781 | 19813 | ** results. if pDest->iSDParm2 is positive, then it is |
| 19782 | -** a regsiter holding a Bloom filter for the IN operator | |
| 19814 | +** a register holding a Bloom filter for the IN operator | |
| 19783 | 19815 | ** that should be populated in addition to the |
| 19784 | 19816 | ** pDest->iSDParm table. This SRT is used to |
| 19785 | 19817 | ** implement "IN (SELECT ...)". |
| 19786 | 19818 | ** |
| 19787 | 19819 | ** SRT_EphemTab Create an temporary table pDest->iSDParm and store |
| @@ -20376,11 +20408,10 @@ | ||
| 20376 | 20408 | u8 bFullMutex; /* True to enable full mutexing */ |
| 20377 | 20409 | u8 bOpenUri; /* True to interpret filenames as URIs */ |
| 20378 | 20410 | u8 bUseCis; /* Use covering indices for full-scans */ |
| 20379 | 20411 | u8 bSmallMalloc; /* Avoid large memory allocations if true */ |
| 20380 | 20412 | u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ |
| 20381 | - u8 bUseLongDouble; /* Make use of long double */ | |
| 20382 | 20413 | #ifdef SQLITE_DEBUG |
| 20383 | 20414 | u8 bJsonSelfcheck; /* Double-check JSON parsing */ |
| 20384 | 20415 | #endif |
| 20385 | 20416 | int mxStrlen; /* Maximum string length */ |
| 20386 | 20417 | int neverCorrupt; /* Database is always well-formed */ |
| @@ -20751,19 +20782,10 @@ | ||
| 20751 | 20782 | */ |
| 20752 | 20783 | #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) |
| 20753 | 20784 | # define SQLITE_ENABLE_FTS3 1 |
| 20754 | 20785 | #endif |
| 20755 | 20786 | |
| 20756 | -/* | |
| 20757 | -** The ctype.h header is needed for non-ASCII systems. It is also | |
| 20758 | -** needed by FTS3 when FTS3 is included in the amalgamation. | |
| 20759 | -*/ | |
| 20760 | -#if !defined(SQLITE_ASCII) || \ | |
| 20761 | - (defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_AMALGAMATION)) | |
| 20762 | -# include <ctype.h> | |
| 20763 | -#endif | |
| 20764 | - | |
| 20765 | 20787 | /* |
| 20766 | 20788 | ** The following macros mimic the standard library functions toupper(), |
| 20767 | 20789 | ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The |
| 20768 | 20790 | ** sqlite versions only work for ASCII characters, regardless of locale. |
| 20769 | 20791 | */ |
| @@ -21381,11 +21403,11 @@ | ||
| 21381 | 21403 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 21382 | 21404 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 21383 | 21405 | SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); |
| 21384 | 21406 | SQLITE_PRIVATE int sqlite3Atoi(const char*); |
| 21385 | 21407 | #ifndef SQLITE_OMIT_UTF16 |
| 21386 | -SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); | |
| 21408 | +SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nByte, int nChar); | |
| 21387 | 21409 | #endif |
| 21388 | 21410 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| 21389 | 21411 | SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); |
| 21390 | 21412 | SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); |
| 21391 | 21413 | SQLITE_PRIVATE LogEst sqlite3LogEst(u64); |
| @@ -22839,13 +22861,10 @@ | ||
| 22839 | 22861 | "UNLINK_AFTER_CLOSE", |
| 22840 | 22862 | #endif |
| 22841 | 22863 | #ifdef SQLITE_UNTESTABLE |
| 22842 | 22864 | "UNTESTABLE", |
| 22843 | 22865 | #endif |
| 22844 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 22845 | - "USER_AUTHENTICATION", | |
| 22846 | -#endif | |
| 22847 | 22866 | #ifdef SQLITE_USE_ALLOCA |
| 22848 | 22867 | "USE_ALLOCA", |
| 22849 | 22868 | #endif |
| 22850 | 22869 | #ifdef SQLITE_USE_FCNTL_TRACE |
| 22851 | 22870 | "USE_FCNTL_TRACE", |
| @@ -23117,11 +23136,10 @@ | ||
| 23117 | 23136 | SQLITE_THREADSAFE==1, /* bFullMutex */ |
| 23118 | 23137 | SQLITE_USE_URI, /* bOpenUri */ |
| 23119 | 23138 | SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ |
| 23120 | 23139 | 0, /* bSmallMalloc */ |
| 23121 | 23140 | 1, /* bExtraSchemaChecks */ |
| 23122 | - sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ | |
| 23123 | 23141 | #ifdef SQLITE_DEBUG |
| 23124 | 23142 | 0, /* bJsonSelfcheck */ |
| 23125 | 23143 | #endif |
| 23126 | 23144 | 0x7ffffffe, /* mxStrlen */ |
| 23127 | 23145 | 0, /* neverCorrupt */ |
| @@ -23837,13 +23855,15 @@ | ||
| 23837 | 23855 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 23838 | 23856 | int iNewReg; /* Register for new.* values */ |
| 23839 | 23857 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 23840 | 23858 | i64 iKey1; /* First key value passed to hook */ |
| 23841 | 23859 | i64 iKey2; /* Second key value passed to hook */ |
| 23860 | + Mem oldipk; /* Memory cell holding "old" IPK value */ | |
| 23842 | 23861 | Mem *aNew; /* Array of new.* values */ |
| 23843 | 23862 | Table *pTab; /* Schema object being updated */ |
| 23844 | 23863 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 23864 | + sqlite3_value **apDflt; /* Array of default values, if required */ | |
| 23845 | 23865 | }; |
| 23846 | 23866 | |
| 23847 | 23867 | /* |
| 23848 | 23868 | ** An instance of this object is used to pass an vector of values into |
| 23849 | 23869 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -29285,20 +29305,33 @@ | ||
| 29285 | 29305 | |
| 29286 | 29306 | #ifndef NDEBUG |
| 29287 | 29307 | /* |
| 29288 | 29308 | ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are |
| 29289 | 29309 | ** intended for use inside assert() statements. |
| 29310 | +** | |
| 29311 | +** Because these routines raise false-positive alerts in TSAN, disable | |
| 29312 | +** them (make them always return 1) when compiling with TSAN. | |
| 29290 | 29313 | */ |
| 29291 | 29314 | SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ |
| 29315 | +# if defined(__has_feature) | |
| 29316 | +# if __has_feature(thread_sanitizer) | |
| 29317 | + p = 0; | |
| 29318 | +# endif | |
| 29319 | +# endif | |
| 29292 | 29320 | assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld ); |
| 29293 | 29321 | return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); |
| 29294 | 29322 | } |
| 29295 | 29323 | SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ |
| 29324 | +# if defined(__has_feature) | |
| 29325 | +# if __has_feature(thread_sanitizer) | |
| 29326 | + p = 0; | |
| 29327 | +# endif | |
| 29328 | +# endif | |
| 29296 | 29329 | assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld ); |
| 29297 | 29330 | return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); |
| 29298 | 29331 | } |
| 29299 | -#endif | |
| 29332 | +#endif /* NDEBUG */ | |
| 29300 | 29333 | |
| 29301 | 29334 | #endif /* !defined(SQLITE_MUTEX_OMIT) */ |
| 29302 | 29335 | |
| 29303 | 29336 | /************** End of mutex.c ***********************************************/ |
| 29304 | 29337 | /************** Begin file mutex_noop.c **************************************/ |
| @@ -32272,10 +32305,11 @@ | ||
| 32272 | 32305 | && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) |
| 32273 | 32306 | ){ |
| 32274 | 32307 | pExpr = pExpr->pLeft; |
| 32275 | 32308 | } |
| 32276 | 32309 | if( pExpr==0 ) return; |
| 32310 | + if( ExprHasProperty(pExpr, EP_FromDDL) ) return; | |
| 32277 | 32311 | db->errByteOffset = pExpr->w.iOfst; |
| 32278 | 32312 | } |
| 32279 | 32313 | |
| 32280 | 32314 | /* |
| 32281 | 32315 | ** Enlarge the memory allocation on a StrAccum object so that it is |
| @@ -33001,11 +33035,11 @@ | ||
| 33001 | 33035 | } |
| 33002 | 33036 | if( pItem->fg.isCte ){ |
| 33003 | 33037 | sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); |
| 33004 | 33038 | } |
| 33005 | 33039 | if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ |
| 33006 | - sqlite3_str_appendf(&x, " ON"); | |
| 33040 | + sqlite3_str_appendf(&x, " isOn"); | |
| 33007 | 33041 | } |
| 33008 | 33042 | if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); |
| 33009 | 33043 | if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); |
| 33010 | 33044 | if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); |
| 33011 | 33045 | if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); |
| @@ -34085,10 +34119,14 @@ | ||
| 34085 | 34119 | ** |
| 34086 | 34120 | ** This routines are given external linkage so that they will always be |
| 34087 | 34121 | ** accessible to the debugging, and to avoid warnings about unused |
| 34088 | 34122 | ** functions. But these routines only exist in debugging builds, so they |
| 34089 | 34123 | ** do not contaminate the interface. |
| 34124 | +** | |
| 34125 | +** See Also: | |
| 34126 | +** | |
| 34127 | +** sqlite3ShowWhereTerm() in where.c | |
| 34090 | 34128 | */ |
| 34091 | 34129 | SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } |
| 34092 | 34130 | SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} |
| 34093 | 34131 | SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } |
| 34094 | 34132 | SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } |
| @@ -34687,11 +34725,11 @@ | ||
| 34687 | 34725 | */ |
| 34688 | 34726 | #define READ_UTF8(zIn, zTerm, c) \ |
| 34689 | 34727 | c = *(zIn++); \ |
| 34690 | 34728 | if( c>=0xc0 ){ \ |
| 34691 | 34729 | c = sqlite3Utf8Trans1[c-0xc0]; \ |
| 34692 | - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ | |
| 34730 | + while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \ | |
| 34693 | 34731 | c = (c<<6) + (0x3f & *(zIn++)); \ |
| 34694 | 34732 | } \ |
| 34695 | 34733 | if( c<0x80 \ |
| 34696 | 34734 | || (c&0xFFFFF800)==0xD800 \ |
| 34697 | 34735 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| @@ -35065,24 +35103,26 @@ | ||
| 35065 | 35103 | assert( m.z || db->mallocFailed ); |
| 35066 | 35104 | return m.z; |
| 35067 | 35105 | } |
| 35068 | 35106 | |
| 35069 | 35107 | /* |
| 35070 | -** zIn is a UTF-16 encoded unicode string at least nChar characters long. | |
| 35108 | +** zIn is a UTF-16 encoded unicode string at least nByte bytes long. | |
| 35071 | 35109 | ** Return the number of bytes in the first nChar unicode characters |
| 35072 | -** in pZ. nChar must be non-negative. | |
| 35110 | +** in pZ. nChar must be non-negative. Surrogate pairs count as a single | |
| 35111 | +** character. | |
| 35073 | 35112 | */ |
| 35074 | -SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nChar){ | |
| 35113 | +SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nByte, int nChar){ | |
| 35075 | 35114 | int c; |
| 35076 | 35115 | unsigned char const *z = zIn; |
| 35116 | + unsigned char const *zEnd = &z[nByte-1]; | |
| 35077 | 35117 | int n = 0; |
| 35078 | 35118 | |
| 35079 | 35119 | if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++; |
| 35080 | - while( n<nChar ){ | |
| 35120 | + while( n<nChar && ALWAYS(z<=zEnd) ){ | |
| 35081 | 35121 | c = z[0]; |
| 35082 | 35122 | z += 2; |
| 35083 | - if( c>=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; | |
| 35123 | + if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2; | |
| 35084 | 35124 | n++; |
| 35085 | 35125 | } |
| 35086 | 35126 | return (int)(z-(unsigned char const *)zIn) |
| 35087 | 35127 | - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE); |
| 35088 | 35128 | } |
| @@ -35659,10 +35699,12 @@ | ||
| 35659 | 35699 | int esign = 1; /* sign of exponent */ |
| 35660 | 35700 | int e = 0; /* exponent */ |
| 35661 | 35701 | int eValid = 1; /* True exponent is either not used or is well-formed */ |
| 35662 | 35702 | int nDigit = 0; /* Number of digits processed */ |
| 35663 | 35703 | int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ |
| 35704 | + u64 s2; /* round-tripped significand */ | |
| 35705 | + double rr[2]; | |
| 35664 | 35706 | |
| 35665 | 35707 | assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); |
| 35666 | 35708 | *pResult = 0.0; /* Default return value, in case of an error */ |
| 35667 | 35709 | if( length==0 ) return 0; |
| 35668 | 35710 | |
| @@ -35761,81 +35803,65 @@ | ||
| 35761 | 35803 | |
| 35762 | 35804 | /* adjust exponent by d, and update sign */ |
| 35763 | 35805 | e = (e*esign) + d; |
| 35764 | 35806 | |
| 35765 | 35807 | /* Try to adjust the exponent to make it smaller */ |
| 35766 | - while( e>0 && s<(LARGEST_UINT64/10) ){ | |
| 35808 | + while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){ | |
| 35767 | 35809 | s *= 10; |
| 35768 | 35810 | e--; |
| 35769 | 35811 | } |
| 35770 | 35812 | while( e<0 && (s%10)==0 ){ |
| 35771 | 35813 | s /= 10; |
| 35772 | 35814 | e++; |
| 35773 | 35815 | } |
| 35774 | 35816 | |
| 35775 | - if( e==0 ){ | |
| 35776 | - *pResult = s; | |
| 35777 | - }else if( sqlite3Config.bUseLongDouble ){ | |
| 35778 | - LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s; | |
| 35779 | - if( e>0 ){ | |
| 35780 | - while( e>=100 ){ e-=100; r *= 1.0e+100L; } | |
| 35781 | - while( e>=10 ){ e-=10; r *= 1.0e+10L; } | |
| 35782 | - while( e>=1 ){ e-=1; r *= 1.0e+01L; } | |
| 35783 | - }else{ | |
| 35784 | - while( e<=-100 ){ e+=100; r *= 1.0e-100L; } | |
| 35785 | - while( e<=-10 ){ e+=10; r *= 1.0e-10L; } | |
| 35786 | - while( e<=-1 ){ e+=1; r *= 1.0e-01L; } | |
| 35787 | - } | |
| 35788 | - assert( r>=0.0 ); | |
| 35789 | - if( r>+1.7976931348623157081452742373e+308L ){ | |
| 35790 | -#ifdef INFINITY | |
| 35791 | - *pResult = +INFINITY; | |
| 35792 | -#else | |
| 35793 | - *pResult = 1.0e308*10.0; | |
| 35794 | -#endif | |
| 35795 | - }else{ | |
| 35796 | - *pResult = (double)r; | |
| 35797 | - } | |
| 35798 | - }else{ | |
| 35799 | - double rr[2]; | |
| 35800 | - u64 s2; | |
| 35801 | - rr[0] = (double)s; | |
| 35802 | - s2 = (u64)rr[0]; | |
| 35803 | -#if defined(_MSC_VER) && _MSC_VER<1700 | |
| 35804 | - if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } | |
| 35805 | -#endif | |
| 35817 | + rr[0] = (double)s; | |
| 35818 | + assert( sizeof(s2)==sizeof(rr[0]) ); | |
| 35819 | +#ifdef SQLITE_DEBUG | |
| 35820 | + rr[1] = 18446744073709549568.0; | |
| 35821 | + memcpy(&s2, &rr[1], sizeof(s2)); | |
| 35822 | + assert( s2==0x43efffffffffffffLL ); | |
| 35823 | +#endif | |
| 35824 | + /* Largest double that can be safely converted to u64 | |
| 35825 | + ** vvvvvvvvvvvvvvvvvvvvvv */ | |
| 35826 | + if( rr[0]<=18446744073709549568.0 ){ | |
| 35827 | + s2 = (u64)rr[0]; | |
| 35806 | 35828 | rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); |
| 35807 | - if( e>0 ){ | |
| 35808 | - while( e>=100 ){ | |
| 35809 | - e -= 100; | |
| 35810 | - dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); | |
| 35811 | - } | |
| 35812 | - while( e>=10 ){ | |
| 35813 | - e -= 10; | |
| 35814 | - dekkerMul2(rr, 1.0e+10, 0.0); | |
| 35815 | - } | |
| 35816 | - while( e>=1 ){ | |
| 35817 | - e -= 1; | |
| 35818 | - dekkerMul2(rr, 1.0e+01, 0.0); | |
| 35819 | - } | |
| 35820 | - }else{ | |
| 35821 | - while( e<=-100 ){ | |
| 35822 | - e += 100; | |
| 35823 | - dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); | |
| 35824 | - } | |
| 35825 | - while( e<=-10 ){ | |
| 35826 | - e += 10; | |
| 35827 | - dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); | |
| 35828 | - } | |
| 35829 | - while( e<=-1 ){ | |
| 35830 | - e += 1; | |
| 35831 | - dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); | |
| 35832 | - } | |
| 35833 | - } | |
| 35834 | - *pResult = rr[0]+rr[1]; | |
| 35835 | - if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; | |
| 35836 | - } | |
| 35829 | + }else{ | |
| 35830 | + rr[1] = 0.0; | |
| 35831 | + } | |
| 35832 | + assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */ | |
| 35833 | + | |
| 35834 | + if( e>0 ){ | |
| 35835 | + while( e>=100 ){ | |
| 35836 | + e -= 100; | |
| 35837 | + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); | |
| 35838 | + } | |
| 35839 | + while( e>=10 ){ | |
| 35840 | + e -= 10; | |
| 35841 | + dekkerMul2(rr, 1.0e+10, 0.0); | |
| 35842 | + } | |
| 35843 | + while( e>=1 ){ | |
| 35844 | + e -= 1; | |
| 35845 | + dekkerMul2(rr, 1.0e+01, 0.0); | |
| 35846 | + } | |
| 35847 | + }else{ | |
| 35848 | + while( e<=-100 ){ | |
| 35849 | + e += 100; | |
| 35850 | + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); | |
| 35851 | + } | |
| 35852 | + while( e<=-10 ){ | |
| 35853 | + e += 10; | |
| 35854 | + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); | |
| 35855 | + } | |
| 35856 | + while( e<=-1 ){ | |
| 35857 | + e += 1; | |
| 35858 | + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); | |
| 35859 | + } | |
| 35860 | + } | |
| 35861 | + *pResult = rr[0]+rr[1]; | |
| 35862 | + if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; | |
| 35837 | 35863 | if( sign<0 ) *pResult = -*pResult; |
| 35838 | 35864 | assert( !sqlite3IsNaN(*pResult) ); |
| 35839 | 35865 | |
| 35840 | 35866 | atof_return: |
| 35841 | 35867 | /* return true if number and no extra non-whitespace characters after */ |
| @@ -36152,13 +36178,14 @@ | ||
| 36152 | 36178 | */ |
| 36153 | 36179 | SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ |
| 36154 | 36180 | int i; |
| 36155 | 36181 | u64 v; |
| 36156 | 36182 | int e, exp = 0; |
| 36183 | + double rr[2]; | |
| 36184 | + | |
| 36157 | 36185 | p->isSpecial = 0; |
| 36158 | 36186 | p->z = p->zBuf; |
| 36159 | - | |
| 36160 | 36187 | assert( mxRound>0 ); |
| 36161 | 36188 | |
| 36162 | 36189 | /* Convert negative numbers to positive. Deal with Infinity, 0.0, and |
| 36163 | 36190 | ** NaN. */ |
| 36164 | 36191 | if( r<0.0 ){ |
| @@ -36182,66 +36209,49 @@ | ||
| 36182 | 36209 | return; |
| 36183 | 36210 | } |
| 36184 | 36211 | |
| 36185 | 36212 | /* Multiply r by powers of ten until it lands somewhere in between |
| 36186 | 36213 | ** 1.0e+19 and 1.0e+17. |
| 36187 | - */ | |
| 36188 | - if( sqlite3Config.bUseLongDouble ){ | |
| 36189 | - LONGDOUBLE_TYPE rr = r; | |
| 36190 | - if( rr>=1.0e+19 ){ | |
| 36191 | - while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } | |
| 36192 | - while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; } | |
| 36193 | - while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; } | |
| 36194 | - }else{ | |
| 36195 | - while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; } | |
| 36196 | - while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; } | |
| 36197 | - while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; } | |
| 36198 | - } | |
| 36199 | - v = (u64)rr; | |
| 36200 | - }else{ | |
| 36201 | - /* If high-precision floating point is not available using "long double", | |
| 36202 | - ** then use Dekker-style double-double computation to increase the | |
| 36203 | - ** precision. | |
| 36204 | - ** | |
| 36205 | - ** The error terms on constants like 1.0e+100 computed using the | |
| 36206 | - ** decimal extension, for example as follows: | |
| 36207 | - ** | |
| 36208 | - ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); | |
| 36209 | - */ | |
| 36210 | - double rr[2]; | |
| 36211 | - rr[0] = r; | |
| 36212 | - rr[1] = 0.0; | |
| 36213 | - if( rr[0]>9.223372036854774784e+18 ){ | |
| 36214 | - while( rr[0]>9.223372036854774784e+118 ){ | |
| 36215 | - exp += 100; | |
| 36216 | - dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); | |
| 36217 | - } | |
| 36218 | - while( rr[0]>9.223372036854774784e+28 ){ | |
| 36219 | - exp += 10; | |
| 36220 | - dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); | |
| 36221 | - } | |
| 36222 | - while( rr[0]>9.223372036854774784e+18 ){ | |
| 36223 | - exp += 1; | |
| 36224 | - dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); | |
| 36225 | - } | |
| 36226 | - }else{ | |
| 36227 | - while( rr[0]<9.223372036854774784e-83 ){ | |
| 36228 | - exp -= 100; | |
| 36229 | - dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); | |
| 36230 | - } | |
| 36231 | - while( rr[0]<9.223372036854774784e+07 ){ | |
| 36232 | - exp -= 10; | |
| 36233 | - dekkerMul2(rr, 1.0e+10, 0.0); | |
| 36234 | - } | |
| 36235 | - while( rr[0]<9.22337203685477478e+17 ){ | |
| 36236 | - exp -= 1; | |
| 36237 | - dekkerMul2(rr, 1.0e+01, 0.0); | |
| 36238 | - } | |
| 36239 | - } | |
| 36240 | - v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; | |
| 36241 | - } | |
| 36242 | - | |
| 36214 | + ** | |
| 36215 | + ** Use Dekker-style double-double computation to increase the | |
| 36216 | + ** precision. | |
| 36217 | + ** | |
| 36218 | + ** The error terms on constants like 1.0e+100 computed using the | |
| 36219 | + ** decimal extension, for example as follows: | |
| 36220 | + ** | |
| 36221 | + ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); | |
| 36222 | + */ | |
| 36223 | + rr[0] = r; | |
| 36224 | + rr[1] = 0.0; | |
| 36225 | + if( rr[0]>9.223372036854774784e+18 ){ | |
| 36226 | + while( rr[0]>9.223372036854774784e+118 ){ | |
| 36227 | + exp += 100; | |
| 36228 | + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); | |
| 36229 | + } | |
| 36230 | + while( rr[0]>9.223372036854774784e+28 ){ | |
| 36231 | + exp += 10; | |
| 36232 | + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); | |
| 36233 | + } | |
| 36234 | + while( rr[0]>9.223372036854774784e+18 ){ | |
| 36235 | + exp += 1; | |
| 36236 | + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); | |
| 36237 | + } | |
| 36238 | + }else{ | |
| 36239 | + while( rr[0]<9.223372036854774784e-83 ){ | |
| 36240 | + exp -= 100; | |
| 36241 | + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); | |
| 36242 | + } | |
| 36243 | + while( rr[0]<9.223372036854774784e+07 ){ | |
| 36244 | + exp -= 10; | |
| 36245 | + dekkerMul2(rr, 1.0e+10, 0.0); | |
| 36246 | + } | |
| 36247 | + while( rr[0]<9.22337203685477478e+17 ){ | |
| 36248 | + exp -= 1; | |
| 36249 | + dekkerMul2(rr, 1.0e+01, 0.0); | |
| 36250 | + } | |
| 36251 | + } | |
| 36252 | + v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; | |
| 36243 | 36253 | |
| 36244 | 36254 | /* Extract significant digits. */ |
| 36245 | 36255 | i = sizeof(p->zBuf)-1; |
| 36246 | 36256 | assert( v>0 ); |
| 36247 | 36257 | while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } |
| @@ -37008,108 +37018,10 @@ | ||
| 37008 | 37018 | i += pIn[i+1]; |
| 37009 | 37019 | }while( i<mx ); |
| 37010 | 37020 | return 0; |
| 37011 | 37021 | } |
| 37012 | 37022 | |
| 37013 | -/* | |
| 37014 | -** High-resolution hardware timer used for debugging and testing only. | |
| 37015 | -*/ | |
| 37016 | -#if defined(VDBE_PROFILE) \ | |
| 37017 | - || defined(SQLITE_PERFORMANCE_TRACE) \ | |
| 37018 | - || defined(SQLITE_ENABLE_STMT_SCANSTATUS) | |
| 37019 | -/************** Include hwtime.h in the middle of util.c *********************/ | |
| 37020 | -/************** Begin file hwtime.h ******************************************/ | |
| 37021 | -/* | |
| 37022 | -** 2008 May 27 | |
| 37023 | -** | |
| 37024 | -** The author disclaims copyright to this source code. In place of | |
| 37025 | -** a legal notice, here is a blessing: | |
| 37026 | -** | |
| 37027 | -** May you do good and not evil. | |
| 37028 | -** May you find forgiveness for yourself and forgive others. | |
| 37029 | -** May you share freely, never taking more than you give. | |
| 37030 | -** | |
| 37031 | -****************************************************************************** | |
| 37032 | -** | |
| 37033 | -** This file contains inline asm code for retrieving "high-performance" | |
| 37034 | -** counters for x86 and x86_64 class CPUs. | |
| 37035 | -*/ | |
| 37036 | -#ifndef SQLITE_HWTIME_H | |
| 37037 | -#define SQLITE_HWTIME_H | |
| 37038 | - | |
| 37039 | -/* | |
| 37040 | -** The following routine only works on Pentium-class (or newer) processors. | |
| 37041 | -** It uses the RDTSC opcode to read the cycle count value out of the | |
| 37042 | -** processor and returns that value. This can be used for high-res | |
| 37043 | -** profiling. | |
| 37044 | -*/ | |
| 37045 | -#if !defined(__STRICT_ANSI__) && \ | |
| 37046 | - (defined(__GNUC__) || defined(_MSC_VER)) && \ | |
| 37047 | - (defined(i386) || defined(__i386__) || defined(_M_IX86)) | |
| 37048 | - | |
| 37049 | - #if defined(__GNUC__) | |
| 37050 | - | |
| 37051 | - __inline__ sqlite_uint64 sqlite3Hwtime(void){ | |
| 37052 | - unsigned int lo, hi; | |
| 37053 | - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | |
| 37054 | - return (sqlite_uint64)hi << 32 | lo; | |
| 37055 | - } | |
| 37056 | - | |
| 37057 | - #elif defined(_MSC_VER) | |
| 37058 | - | |
| 37059 | - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ | |
| 37060 | - __asm { | |
| 37061 | - rdtsc | |
| 37062 | - ret ; return value at EDX:EAX | |
| 37063 | - } | |
| 37064 | - } | |
| 37065 | - | |
| 37066 | - #endif | |
| 37067 | - | |
| 37068 | -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) | |
| 37069 | - | |
| 37070 | - __inline__ sqlite_uint64 sqlite3Hwtime(void){ | |
| 37071 | - unsigned int lo, hi; | |
| 37072 | - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | |
| 37073 | - return (sqlite_uint64)hi << 32 | lo; | |
| 37074 | - } | |
| 37075 | - | |
| 37076 | -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) | |
| 37077 | - | |
| 37078 | - __inline__ sqlite_uint64 sqlite3Hwtime(void){ | |
| 37079 | - unsigned long long retval; | |
| 37080 | - unsigned long junk; | |
| 37081 | - __asm__ __volatile__ ("\n\ | |
| 37082 | - 1: mftbu %1\n\ | |
| 37083 | - mftb %L0\n\ | |
| 37084 | - mftbu %0\n\ | |
| 37085 | - cmpw %0,%1\n\ | |
| 37086 | - bne 1b" | |
| 37087 | - : "=r" (retval), "=r" (junk)); | |
| 37088 | - return retval; | |
| 37089 | - } | |
| 37090 | - | |
| 37091 | -#else | |
| 37092 | - | |
| 37093 | - /* | |
| 37094 | - ** asm() is needed for hardware timing support. Without asm(), | |
| 37095 | - ** disable the sqlite3Hwtime() routine. | |
| 37096 | - ** | |
| 37097 | - ** sqlite3Hwtime() is only used for some obscure debugging | |
| 37098 | - ** and analysis configurations, not in any deliverable, so this | |
| 37099 | - ** should not be a great loss. | |
| 37100 | - */ | |
| 37101 | -SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } | |
| 37102 | - | |
| 37103 | -#endif | |
| 37104 | - | |
| 37105 | -#endif /* !defined(SQLITE_HWTIME_H) */ | |
| 37106 | - | |
| 37107 | -/************** End of hwtime.h **********************************************/ | |
| 37108 | -/************** Continuing where we left off in util.c ***********************/ | |
| 37109 | -#endif | |
| 37110 | - | |
| 37111 | 37023 | /************** End of util.c ************************************************/ |
| 37112 | 37024 | /************** Begin file hash.c ********************************************/ |
| 37113 | 37025 | /* |
| 37114 | 37026 | ** 2001 September 22 |
| 37115 | 37027 | ** |
| @@ -38787,11 +38699,11 @@ | ||
| 38787 | 38699 | # define F_SETLKW 7 |
| 38788 | 38700 | # endif |
| 38789 | 38701 | # endif |
| 38790 | 38702 | #else /* !SQLITE_WASI */ |
| 38791 | 38703 | # ifndef HAVE_FCHMOD |
| 38792 | -# define HAVE_FCHMOD | |
| 38704 | +# define HAVE_FCHMOD 1 | |
| 38793 | 38705 | # endif |
| 38794 | 38706 | #endif /* SQLITE_WASI */ |
| 38795 | 38707 | |
| 38796 | 38708 | #ifdef SQLITE_WASI |
| 38797 | 38709 | # define osGetpid(X) (pid_t)1 |
| @@ -41038,58 +40950,37 @@ | ||
| 41038 | 40950 | ** file by this or any other process. If such a lock is held, set *pResOut |
| 41039 | 40951 | ** to a non-zero value otherwise *pResOut is set to zero. The return value |
| 41040 | 40952 | ** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
| 41041 | 40953 | */ |
| 41042 | 40954 | static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ |
| 41043 | - int rc = SQLITE_OK; | |
| 41044 | - int reserved = 0; | |
| 40955 | +#ifdef SQLITE_DEBUG | |
| 41045 | 40956 | unixFile *pFile = (unixFile*)id; |
| 40957 | +#else | |
| 40958 | + UNUSED_PARAMETER(id); | |
| 40959 | +#endif | |
| 41046 | 40960 | |
| 41047 | 40961 | SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
| 41048 | 40962 | |
| 41049 | 40963 | assert( pFile ); |
| 41050 | - | |
| 41051 | - /* Check if a thread in this process holds such a lock */ | |
| 41052 | - if( pFile->eFileLock>SHARED_LOCK ){ | |
| 41053 | - reserved = 1; | |
| 41054 | - } | |
| 41055 | - | |
| 41056 | - /* Otherwise see if some other process holds it. */ | |
| 41057 | - if( !reserved ){ | |
| 41058 | - /* attempt to get the lock */ | |
| 41059 | - int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); | |
| 41060 | - if( !lrc ){ | |
| 41061 | - /* got the lock, unlock it */ | |
| 41062 | - lrc = robust_flock(pFile->h, LOCK_UN); | |
| 41063 | - if ( lrc ) { | |
| 41064 | - int tErrno = errno; | |
| 41065 | - /* unlock failed with an error */ | |
| 41066 | - lrc = SQLITE_IOERR_UNLOCK; | |
| 41067 | - storeLastErrno(pFile, tErrno); | |
| 41068 | - rc = lrc; | |
| 41069 | - } | |
| 41070 | - } else { | |
| 41071 | - int tErrno = errno; | |
| 41072 | - reserved = 1; | |
| 41073 | - /* someone else might have it reserved */ | |
| 41074 | - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); | |
| 41075 | - if( IS_LOCK_ERROR(lrc) ){ | |
| 41076 | - storeLastErrno(pFile, tErrno); | |
| 41077 | - rc = lrc; | |
| 41078 | - } | |
| 41079 | - } | |
| 41080 | - } | |
| 41081 | - OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); | |
| 41082 | - | |
| 41083 | -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS | |
| 41084 | - if( (rc & 0xff) == SQLITE_IOERR ){ | |
| 41085 | - rc = SQLITE_OK; | |
| 41086 | - reserved=1; | |
| 41087 | - } | |
| 41088 | -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ | |
| 41089 | - *pResOut = reserved; | |
| 41090 | - return rc; | |
| 40964 | + assert( pFile->eFileLock<=SHARED_LOCK ); | |
| 40965 | + | |
| 40966 | + /* The flock VFS only ever takes exclusive locks (see function flockLock). | |
| 40967 | + ** Therefore, if this connection is holding any lock at all, no other | |
| 40968 | + ** connection may be holding a RESERVED lock. So set *pResOut to 0 | |
| 40969 | + ** in this case. | |
| 40970 | + ** | |
| 40971 | + ** Or, this connection may be holding no lock. In that case, set *pResOut to | |
| 40972 | + ** 0 as well. The caller will then attempt to take an EXCLUSIVE lock on the | |
| 40973 | + ** db in order to roll the hot journal back. If there is another connection | |
| 40974 | + ** holding a lock, that attempt will fail and an SQLITE_BUSY returned to | |
| 40975 | + ** the user. With other VFS, we try to avoid this, in order to allow a reader | |
| 40976 | + ** to proceed while a writer is preparing its transaction. But that won't | |
| 40977 | + ** work with the flock VFS - as it always takes EXCLUSIVE locks - so it is | |
| 40978 | + ** not a problem in this case. */ | |
| 40979 | + *pResOut = 0; | |
| 40980 | + | |
| 40981 | + return SQLITE_OK; | |
| 41091 | 40982 | } |
| 41092 | 40983 | |
| 41093 | 40984 | /* |
| 41094 | 40985 | ** Lock the file with the lock specified by parameter eFileLock - one |
| 41095 | 40986 | ** of the following: |
| @@ -42582,10 +42473,15 @@ | ||
| 42582 | 42473 | int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); |
| 42583 | 42474 | return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; |
| 42584 | 42475 | } |
| 42585 | 42476 | #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ |
| 42586 | 42477 | |
| 42478 | + case SQLITE_FCNTL_NULL_IO: { | |
| 42479 | + osClose(pFile->h); | |
| 42480 | + pFile->h = -1; | |
| 42481 | + return SQLITE_OK; | |
| 42482 | + } | |
| 42587 | 42483 | case SQLITE_FCNTL_LOCKSTATE: { |
| 42588 | 42484 | *(int*)pArg = pFile->eFileLock; |
| 42589 | 42485 | return SQLITE_OK; |
| 42590 | 42486 | } |
| 42591 | 42487 | case SQLITE_FCNTL_LAST_ERRNO: { |
| @@ -42723,10 +42619,11 @@ | ||
| 42723 | 42619 | |
| 42724 | 42620 | /* Set the POWERSAFE_OVERWRITE flag if requested. */ |
| 42725 | 42621 | if( pFd->ctrlFlags & UNIXFILE_PSOW ){ |
| 42726 | 42622 | pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; |
| 42727 | 42623 | } |
| 42624 | + pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ; | |
| 42728 | 42625 | |
| 42729 | 42626 | pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; |
| 42730 | 42627 | } |
| 42731 | 42628 | } |
| 42732 | 42629 | #else |
| @@ -42773,19 +42670,19 @@ | ||
| 42773 | 42670 | 0; |
| 42774 | 42671 | }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ |
| 42775 | 42672 | pFile->sectorSize = fsInfo.f_bsize; |
| 42776 | 42673 | pFile->deviceCharacteristics = |
| 42777 | 42674 | /* full bitset of atomics from max sector size and smaller */ |
| 42778 | - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | | |
| 42675 | + (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | | |
| 42779 | 42676 | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
| 42780 | 42677 | ** so it is ordered */ |
| 42781 | 42678 | 0; |
| 42782 | 42679 | }else if( strstr(fsInfo.f_basetype, "dos") ){ |
| 42783 | 42680 | pFile->sectorSize = fsInfo.f_bsize; |
| 42784 | 42681 | pFile->deviceCharacteristics = |
| 42785 | 42682 | /* full bitset of atomics from max sector size and smaller */ |
| 42786 | - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | | |
| 42683 | + (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | | |
| 42787 | 42684 | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
| 42788 | 42685 | ** so it is ordered */ |
| 42789 | 42686 | 0; |
| 42790 | 42687 | }else{ |
| 42791 | 42688 | pFile->deviceCharacteristics = |
| @@ -50462,10 +50359,15 @@ | ||
| 50462 | 50359 | OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n", |
| 50463 | 50360 | hOldFile, pFile->h)); |
| 50464 | 50361 | return SQLITE_OK; |
| 50465 | 50362 | } |
| 50466 | 50363 | #endif |
| 50364 | + case SQLITE_FCNTL_NULL_IO: { | |
| 50365 | + (void)osCloseHandle(pFile->h); | |
| 50366 | + pFile->h = NULL; | |
| 50367 | + return SQLITE_OK; | |
| 50368 | + } | |
| 50467 | 50369 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 50468 | 50370 | char *zTFile = 0; |
| 50469 | 50371 | int rc = winGetTempname(pFile->pVfs, &zTFile); |
| 50470 | 50372 | if( rc==SQLITE_OK ){ |
| 50471 | 50373 | *(char**)pArg = zTFile; |
| @@ -50523,11 +50425,11 @@ | ||
| 50523 | 50425 | /* |
| 50524 | 50426 | ** Return a vector of device characteristics. |
| 50525 | 50427 | */ |
| 50526 | 50428 | static int winDeviceCharacteristics(sqlite3_file *id){ |
| 50527 | 50429 | winFile *p = (winFile*)id; |
| 50528 | - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | | |
| 50430 | + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ | | |
| 50529 | 50431 | ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); |
| 50530 | 50432 | } |
| 50531 | 50433 | |
| 50532 | 50434 | /* |
| 50533 | 50435 | ** Windows will only let you create file view mappings |
| @@ -51911,11 +51813,11 @@ | ||
| 51911 | 51813 | */ |
| 51912 | 51814 | char *zTmpname = 0; /* For temporary filename, if necessary. */ |
| 51913 | 51815 | |
| 51914 | 51816 | int rc = SQLITE_OK; /* Function Return Code */ |
| 51915 | 51817 | #if !defined(NDEBUG) || SQLITE_OS_WINCE |
| 51916 | - int eType = flags&0xFFFFFF00; /* Type of file to open */ | |
| 51818 | + int eType = flags&0x0FFF00; /* Type of file to open */ | |
| 51917 | 51819 | #endif |
| 51918 | 51820 | |
| 51919 | 51821 | int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); |
| 51920 | 51822 | int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); |
| 51921 | 51823 | int isCreate = (flags & SQLITE_OPEN_CREATE); |
| @@ -58131,24 +58033,32 @@ | ||
| 58131 | 58033 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58132 | 58034 | /* |
| 58133 | 58035 | ** Return true if page pgno can be read directly from the database file |
| 58134 | 58036 | ** by the b-tree layer. This is the case if: |
| 58135 | 58037 | ** |
| 58136 | -** * the database file is open, | |
| 58137 | -** * there are no dirty pages in the cache, and | |
| 58138 | -** * the desired page is not currently in the wal file. | |
| 58038 | +** (1) the database file is open | |
| 58039 | +** (2) the VFS for the database is able to do unaligned sub-page reads | |
| 58040 | +** (3) there are no dirty pages in the cache, and | |
| 58041 | +** (4) the desired page is not currently in the wal file. | |
| 58139 | 58042 | */ |
| 58140 | 58043 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ |
| 58141 | - if( pPager->fd->pMethods==0 ) return 0; | |
| 58142 | - if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; | |
| 58044 | + assert( pPager!=0 ); | |
| 58045 | + assert( pPager->fd!=0 ); | |
| 58046 | + if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */ | |
| 58047 | + if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */ | |
| 58143 | 58048 | #ifndef SQLITE_OMIT_WAL |
| 58144 | 58049 | if( pPager->pWal ){ |
| 58145 | 58050 | u32 iRead = 0; |
| 58146 | 58051 | (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); |
| 58147 | - return iRead==0; | |
| 58052 | + return iRead==0; /* Condition (4) */ | |
| 58148 | 58053 | } |
| 58149 | 58054 | #endif |
| 58055 | + assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); | |
| 58056 | + if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) | |
| 58057 | + & SQLITE_IOCAP_SUBPAGE_READ)==0 ){ | |
| 58058 | + return 0; /* Case (2) */ | |
| 58059 | + } | |
| 58150 | 58060 | return 1; |
| 58151 | 58061 | } |
| 58152 | 58062 | #endif |
| 58153 | 58063 | |
| 58154 | 58064 | #ifndef SQLITE_OMIT_WAL |
| @@ -65171,11 +65081,11 @@ | ||
| 65171 | 65081 | ** 20: Salt-2, a different random integer changing with each ckpt |
| 65172 | 65082 | ** 24: Checksum-1 (first part of checksum for first 24 bytes of header). |
| 65173 | 65083 | ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). |
| 65174 | 65084 | ** |
| 65175 | 65085 | ** Immediately following the wal-header are zero or more frames. Each |
| 65176 | -** frame consists of a 24-byte frame-header followed by a <page-size> bytes | |
| 65086 | +** frame consists of a 24-byte frame-header followed by <page-size> bytes | |
| 65177 | 65087 | ** of page data. The frame-header is six big-endian 32-bit unsigned |
| 65178 | 65088 | ** integer values, as follows: |
| 65179 | 65089 | ** |
| 65180 | 65090 | ** 0: Page number. |
| 65181 | 65091 | ** 4: For commit records, the size of the database image in pages |
| @@ -65668,10 +65578,11 @@ | ||
| 65668 | 65578 | int nSehTry; /* Number of nested SEH_TRY{} blocks */ |
| 65669 | 65579 | u8 lockError; /* True if a locking error has occurred */ |
| 65670 | 65580 | #endif |
| 65671 | 65581 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 65672 | 65582 | WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ |
| 65583 | + int bGetSnapshot; /* Transaction opened for sqlite3_get_snapshot() */ | |
| 65673 | 65584 | #endif |
| 65674 | 65585 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 65675 | 65586 | sqlite3 *db; |
| 65676 | 65587 | #endif |
| 65677 | 65588 | }; |
| @@ -67560,11 +67471,11 @@ | ||
| 67560 | 67471 | return SQLITE_IOERR_IN_PAGE; |
| 67561 | 67472 | } |
| 67562 | 67473 | |
| 67563 | 67474 | /* |
| 67564 | 67475 | ** Assert that the Wal.lockMask mask, which indicates the locks held |
| 67565 | -** by the connenction, is consistent with the Wal.readLock, Wal.writeLock | |
| 67476 | +** by the connection, is consistent with the Wal.readLock, Wal.writeLock | |
| 67566 | 67477 | ** and Wal.ckptLock variables. To be used as: |
| 67567 | 67478 | ** |
| 67568 | 67479 | ** assert( walAssertLockmask(pWal) ); |
| 67569 | 67480 | */ |
| 67570 | 67481 | static int walAssertLockmask(Wal *pWal){ |
| @@ -68224,11 +68135,11 @@ | ||
| 68224 | 68135 | assert( pWal->apWiData[0]!=0 ); |
| 68225 | 68136 | pInfo = walCkptInfo(pWal); |
| 68226 | 68137 | SEH_INJECT_FAULT; |
| 68227 | 68138 | if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame |
| 68228 | 68139 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68229 | - && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) | |
| 68140 | + && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) | |
| 68230 | 68141 | #endif |
| 68231 | 68142 | ){ |
| 68232 | 68143 | /* The WAL has been completely backfilled (or it is empty). |
| 68233 | 68144 | ** and can be safely ignored. |
| 68234 | 68145 | */ |
| @@ -69624,11 +69535,24 @@ | ||
| 69624 | 69535 | */ |
| 69625 | 69536 | SQLITE_PRIVATE void sqlite3WalSnapshotOpen( |
| 69626 | 69537 | Wal *pWal, |
| 69627 | 69538 | sqlite3_snapshot *pSnapshot |
| 69628 | 69539 | ){ |
| 69629 | - pWal->pSnapshot = (WalIndexHdr*)pSnapshot; | |
| 69540 | + if( pSnapshot && ((WalIndexHdr*)pSnapshot)->iVersion==0 ){ | |
| 69541 | + /* iVersion==0 means that this is a call to sqlite3_snapshot_get(). In | |
| 69542 | + ** this case set the bGetSnapshot flag so that if the call to | |
| 69543 | + ** sqlite3_snapshot_get() is about to read transaction on this wal | |
| 69544 | + ** file, it does not take read-lock 0 if the wal file has been completely | |
| 69545 | + ** checkpointed. Taking read-lock 0 would work, but then it would be | |
| 69546 | + ** possible for a subsequent writer to destroy the snapshot even while | |
| 69547 | + ** this connection is holding its read-transaction open. This is contrary | |
| 69548 | + ** to user expectations, so we avoid it by not taking read-lock 0. */ | |
| 69549 | + pWal->bGetSnapshot = 1; | |
| 69550 | + }else{ | |
| 69551 | + pWal->pSnapshot = (WalIndexHdr*)pSnapshot; | |
| 69552 | + pWal->bGetSnapshot = 0; | |
| 69553 | + } | |
| 69630 | 69554 | } |
| 69631 | 69555 | |
| 69632 | 69556 | /* |
| 69633 | 69557 | ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if |
| 69634 | 69558 | ** p1 is older than p2 and zero if p1 and p2 are the same snapshot. |
| @@ -75504,10 +75428,29 @@ | ||
| 75504 | 75428 | ** this routine. |
| 75505 | 75429 | */ |
| 75506 | 75430 | SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ |
| 75507 | 75431 | return ROUND8(sizeof(BtCursor)); |
| 75508 | 75432 | } |
| 75433 | + | |
| 75434 | +#ifdef SQLITE_DEBUG | |
| 75435 | +/* | |
| 75436 | +** Return true if and only if the Btree object will be automatically | |
| 75437 | +** closed with the BtCursor closes. This is used within assert() statements | |
| 75438 | +** only. | |
| 75439 | +*/ | |
| 75440 | +SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor( | |
| 75441 | + Btree *pBtree, /* the btree object */ | |
| 75442 | + BtCursor *pCur /* Corresponding cursor */ | |
| 75443 | +){ | |
| 75444 | + BtShared *pBt = pBtree->pBt; | |
| 75445 | + if( (pBt->openFlags & BTREE_SINGLE)==0 ) return 0; | |
| 75446 | + if( pBt->pCursor!=pCur ) return 0; | |
| 75447 | + if( pCur->pNext!=0 ) return 0; | |
| 75448 | + if( pCur->pBtree!=pBtree ) return 0; | |
| 75449 | + return 1; | |
| 75450 | +} | |
| 75451 | +#endif | |
| 75509 | 75452 | |
| 75510 | 75453 | /* |
| 75511 | 75454 | ** Initialize memory that will be converted into a BtCursor object. |
| 75512 | 75455 | ** |
| 75513 | 75456 | ** The simple approach here would be to memset() the entire object |
| @@ -84538,11 +84481,12 @@ | ||
| 84538 | 84481 | if( apVal==0 ){ |
| 84539 | 84482 | rc = SQLITE_NOMEM_BKPT; |
| 84540 | 84483 | goto value_from_function_out; |
| 84541 | 84484 | } |
| 84542 | 84485 | for(i=0; i<nVal; i++){ |
| 84543 | - rc = sqlite3ValueFromExpr(db, pList->a[i].pExpr, enc, aff, &apVal[i]); | |
| 84486 | + rc = sqlite3Stat4ValueFromExpr(pCtx->pParse, pList->a[i].pExpr, aff, | |
| 84487 | + &apVal[i]); | |
| 84544 | 84488 | if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; |
| 84545 | 84489 | } |
| 84546 | 84490 | } |
| 84547 | 84491 | |
| 84548 | 84492 | pVal = valueNew(db, pCtx); |
| @@ -89571,11 +89515,11 @@ | ||
| 89571 | 89515 | /* The following two functions are used only within testcase() to prove |
| 89572 | 89516 | ** test coverage. These functions do no exist for production builds. |
| 89573 | 89517 | ** We must use separate SQLITE_NOINLINE functions here, since otherwise |
| 89574 | 89518 | ** optimizer code movement causes gcov to become very confused. |
| 89575 | 89519 | */ |
| 89576 | -#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) | |
| 89520 | +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) | |
| 89577 | 89521 | static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; } |
| 89578 | 89522 | static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; } |
| 89579 | 89523 | #endif |
| 89580 | 89524 | |
| 89581 | 89525 | /* |
| @@ -89586,17 +89530,10 @@ | ||
| 89586 | 89530 | SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ |
| 89587 | 89531 | if( sqlite3IsNaN(r) ){ |
| 89588 | 89532 | /* SQLite considers NaN to be a NULL. And all integer values are greater |
| 89589 | 89533 | ** than NULL */ |
| 89590 | 89534 | return 1; |
| 89591 | - } | |
| 89592 | - if( sqlite3Config.bUseLongDouble ){ | |
| 89593 | - LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; | |
| 89594 | - testcase( x<r ); | |
| 89595 | - testcase( x>r ); | |
| 89596 | - testcase( x==r ); | |
| 89597 | - return (x<r) ? -1 : (x>r); | |
| 89598 | 89535 | }else{ |
| 89599 | 89536 | i64 y; |
| 89600 | 89537 | if( r<-9223372036854775808.0 ) return +1; |
| 89601 | 89538 | if( r>=9223372036854775808.0 ) return -1; |
| 89602 | 89539 | y = (i64)r; |
| @@ -90595,17 +90532,25 @@ | ||
| 90595 | 90532 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 90596 | 90533 | db->pPreUpdate = 0; |
| 90597 | 90534 | sqlite3DbFree(db, preupdate.aRecord); |
| 90598 | 90535 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); |
| 90599 | 90536 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); |
| 90537 | + sqlite3VdbeMemRelease(&preupdate.oldipk); | |
| 90600 | 90538 | if( preupdate.aNew ){ |
| 90601 | 90539 | int i; |
| 90602 | 90540 | for(i=0; i<pCsr->nField; i++){ |
| 90603 | 90541 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| 90604 | 90542 | } |
| 90605 | 90543 | sqlite3DbNNFreeNN(db, preupdate.aNew); |
| 90606 | 90544 | } |
| 90545 | + if( preupdate.apDflt ){ | |
| 90546 | + int i; | |
| 90547 | + for(i=0; i<pTab->nCol; i++){ | |
| 90548 | + sqlite3ValueFree(preupdate.apDflt[i]); | |
| 90549 | + } | |
| 90550 | + sqlite3DbFree(db, preupdate.apDflt); | |
| 90551 | + } | |
| 90607 | 90552 | } |
| 90608 | 90553 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 90609 | 90554 | |
| 90610 | 90555 | /************** End of vdbeaux.c *********************************************/ |
| 90611 | 90556 | /************** Begin file vdbeapi.c *****************************************/ |
| @@ -92230,10 +92175,21 @@ | ||
| 92230 | 92175 | ** A successful evaluation of this routine acquires the mutex on p. |
| 92231 | 92176 | ** the mutex is released if any kind of error occurs. |
| 92232 | 92177 | ** |
| 92233 | 92178 | ** The error code stored in database p->db is overwritten with the return |
| 92234 | 92179 | ** value in any case. |
| 92180 | +** | |
| 92181 | +** (tag-20240917-01) If vdbeUnbind(p,(u32)(i-1)) returns SQLITE_OK, | |
| 92182 | +** that means all of the the following will be true: | |
| 92183 | +** | |
| 92184 | +** p!=0 | |
| 92185 | +** p->pVar!=0 | |
| 92186 | +** i>0 | |
| 92187 | +** i<=p->nVar | |
| 92188 | +** | |
| 92189 | +** An assert() is normally added after vdbeUnbind() to help static analyzers | |
| 92190 | +** realize this. | |
| 92235 | 92191 | */ |
| 92236 | 92192 | static int vdbeUnbind(Vdbe *p, unsigned int i){ |
| 92237 | 92193 | Mem *pVar; |
| 92238 | 92194 | if( vdbeSafetyNotNull(p) ){ |
| 92239 | 92195 | return SQLITE_MISUSE_BKPT; |
| @@ -92287,10 +92243,11 @@ | ||
| 92287 | 92243 | Mem *pVar; |
| 92288 | 92244 | int rc; |
| 92289 | 92245 | |
| 92290 | 92246 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92291 | 92247 | if( rc==SQLITE_OK ){ |
| 92248 | + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ | |
| 92292 | 92249 | if( zData!=0 ){ |
| 92293 | 92250 | pVar = &p->aVar[i-1]; |
| 92294 | 92251 | rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); |
| 92295 | 92252 | if( rc==SQLITE_OK && encoding!=0 ){ |
| 92296 | 92253 | rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); |
| @@ -92336,10 +92293,11 @@ | ||
| 92336 | 92293 | SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ |
| 92337 | 92294 | int rc; |
| 92338 | 92295 | Vdbe *p = (Vdbe *)pStmt; |
| 92339 | 92296 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92340 | 92297 | if( rc==SQLITE_OK ){ |
| 92298 | + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ | |
| 92341 | 92299 | sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); |
| 92342 | 92300 | sqlite3_mutex_leave(p->db->mutex); |
| 92343 | 92301 | } |
| 92344 | 92302 | return rc; |
| 92345 | 92303 | } |
| @@ -92349,10 +92307,11 @@ | ||
| 92349 | 92307 | SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ |
| 92350 | 92308 | int rc; |
| 92351 | 92309 | Vdbe *p = (Vdbe *)pStmt; |
| 92352 | 92310 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92353 | 92311 | if( rc==SQLITE_OK ){ |
| 92312 | + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ | |
| 92354 | 92313 | sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); |
| 92355 | 92314 | sqlite3_mutex_leave(p->db->mutex); |
| 92356 | 92315 | } |
| 92357 | 92316 | return rc; |
| 92358 | 92317 | } |
| @@ -92359,10 +92318,11 @@ | ||
| 92359 | 92318 | SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ |
| 92360 | 92319 | int rc; |
| 92361 | 92320 | Vdbe *p = (Vdbe*)pStmt; |
| 92362 | 92321 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92363 | 92322 | if( rc==SQLITE_OK ){ |
| 92323 | + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ | |
| 92364 | 92324 | sqlite3_mutex_leave(p->db->mutex); |
| 92365 | 92325 | } |
| 92366 | 92326 | return rc; |
| 92367 | 92327 | } |
| 92368 | 92328 | SQLITE_API int sqlite3_bind_pointer( |
| @@ -92374,10 +92334,11 @@ | ||
| 92374 | 92334 | ){ |
| 92375 | 92335 | int rc; |
| 92376 | 92336 | Vdbe *p = (Vdbe*)pStmt; |
| 92377 | 92337 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92378 | 92338 | if( rc==SQLITE_OK ){ |
| 92339 | + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ | |
| 92379 | 92340 | sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); |
| 92380 | 92341 | sqlite3_mutex_leave(p->db->mutex); |
| 92381 | 92342 | }else if( xDestructor ){ |
| 92382 | 92343 | xDestructor(pPtr); |
| 92383 | 92344 | } |
| @@ -92455,10 +92416,11 @@ | ||
| 92455 | 92416 | SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ |
| 92456 | 92417 | int rc; |
| 92457 | 92418 | Vdbe *p = (Vdbe *)pStmt; |
| 92458 | 92419 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92459 | 92420 | if( rc==SQLITE_OK ){ |
| 92421 | + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ | |
| 92460 | 92422 | #ifndef SQLITE_OMIT_INCRBLOB |
| 92461 | 92423 | sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); |
| 92462 | 92424 | #else |
| 92463 | 92425 | rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); |
| 92464 | 92426 | #endif |
| @@ -92789,41 +92751,68 @@ | ||
| 92789 | 92751 | if( iIdx>=p->pCsr->nField || iIdx<0 ){ |
| 92790 | 92752 | rc = SQLITE_RANGE; |
| 92791 | 92753 | goto preupdate_old_out; |
| 92792 | 92754 | } |
| 92793 | 92755 | |
| 92794 | - /* If the old.* record has not yet been loaded into memory, do so now. */ | |
| 92795 | - if( p->pUnpacked==0 ){ | |
| 92796 | - u32 nRec; | |
| 92797 | - u8 *aRec; | |
| 92798 | - | |
| 92799 | - assert( p->pCsr->eCurType==CURTYPE_BTREE ); | |
| 92800 | - nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); | |
| 92801 | - aRec = sqlite3DbMallocRaw(db, nRec); | |
| 92802 | - if( !aRec ) goto preupdate_old_out; | |
| 92803 | - rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); | |
| 92804 | - if( rc==SQLITE_OK ){ | |
| 92805 | - p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); | |
| 92806 | - if( !p->pUnpacked ) rc = SQLITE_NOMEM; | |
| 92807 | - } | |
| 92808 | - if( rc!=SQLITE_OK ){ | |
| 92809 | - sqlite3DbFree(db, aRec); | |
| 92810 | - goto preupdate_old_out; | |
| 92811 | - } | |
| 92812 | - p->aRecord = aRec; | |
| 92813 | - } | |
| 92814 | - | |
| 92815 | - pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; | |
| 92816 | 92756 | if( iIdx==p->pTab->iPKey ){ |
| 92757 | + *ppValue = pMem = &p->oldipk; | |
| 92817 | 92758 | sqlite3VdbeMemSetInt64(pMem, p->iKey1); |
| 92818 | - }else if( iIdx>=p->pUnpacked->nField ){ | |
| 92819 | - *ppValue = (sqlite3_value *)columnNullValue(); | |
| 92820 | - }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ | |
| 92821 | - if( pMem->flags & (MEM_Int|MEM_IntReal) ){ | |
| 92822 | - testcase( pMem->flags & MEM_Int ); | |
| 92823 | - testcase( pMem->flags & MEM_IntReal ); | |
| 92824 | - sqlite3VdbeMemRealify(pMem); | |
| 92759 | + }else{ | |
| 92760 | + | |
| 92761 | + /* If the old.* record has not yet been loaded into memory, do so now. */ | |
| 92762 | + if( p->pUnpacked==0 ){ | |
| 92763 | + u32 nRec; | |
| 92764 | + u8 *aRec; | |
| 92765 | + | |
| 92766 | + assert( p->pCsr->eCurType==CURTYPE_BTREE ); | |
| 92767 | + nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); | |
| 92768 | + aRec = sqlite3DbMallocRaw(db, nRec); | |
| 92769 | + if( !aRec ) goto preupdate_old_out; | |
| 92770 | + rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); | |
| 92771 | + if( rc==SQLITE_OK ){ | |
| 92772 | + p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); | |
| 92773 | + if( !p->pUnpacked ) rc = SQLITE_NOMEM; | |
| 92774 | + } | |
| 92775 | + if( rc!=SQLITE_OK ){ | |
| 92776 | + sqlite3DbFree(db, aRec); | |
| 92777 | + goto preupdate_old_out; | |
| 92778 | + } | |
| 92779 | + p->aRecord = aRec; | |
| 92780 | + } | |
| 92781 | + | |
| 92782 | + pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; | |
| 92783 | + if( iIdx>=p->pUnpacked->nField ){ | |
| 92784 | + /* This occurs when the table has been extended using ALTER TABLE | |
| 92785 | + ** ADD COLUMN. The value to return is the default value of the column. */ | |
| 92786 | + Column *pCol = &p->pTab->aCol[iIdx]; | |
| 92787 | + if( pCol->iDflt>0 ){ | |
| 92788 | + if( p->apDflt==0 ){ | |
| 92789 | + int nByte = sizeof(sqlite3_value*)*p->pTab->nCol; | |
| 92790 | + p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); | |
| 92791 | + if( p->apDflt==0 ) goto preupdate_old_out; | |
| 92792 | + } | |
| 92793 | + if( p->apDflt[iIdx]==0 ){ | |
| 92794 | + sqlite3_value *pVal = 0; | |
| 92795 | + Expr *pDflt; | |
| 92796 | + assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); | |
| 92797 | + pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; | |
| 92798 | + rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); | |
| 92799 | + if( rc==SQLITE_OK && pVal==0 ){ | |
| 92800 | + rc = SQLITE_CORRUPT_BKPT; | |
| 92801 | + } | |
| 92802 | + p->apDflt[iIdx] = pVal; | |
| 92803 | + } | |
| 92804 | + *ppValue = p->apDflt[iIdx]; | |
| 92805 | + }else{ | |
| 92806 | + *ppValue = (sqlite3_value *)columnNullValue(); | |
| 92807 | + } | |
| 92808 | + }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ | |
| 92809 | + if( pMem->flags & (MEM_Int|MEM_IntReal) ){ | |
| 92810 | + testcase( pMem->flags & MEM_Int ); | |
| 92811 | + testcase( pMem->flags & MEM_IntReal ); | |
| 92812 | + sqlite3VdbeMemRealify(pMem); | |
| 92813 | + } | |
| 92825 | 92814 | } |
| 92826 | 92815 | } |
| 92827 | 92816 | |
| 92828 | 92817 | preupdate_old_out: |
| 92829 | 92818 | sqlite3Error(db, rc); |
| @@ -93367,10 +93356,108 @@ | ||
| 93367 | 93356 | ** commenting and indentation practices when changing or adding code. |
| 93368 | 93357 | */ |
| 93369 | 93358 | /* #include "sqliteInt.h" */ |
| 93370 | 93359 | /* #include "vdbeInt.h" */ |
| 93371 | 93360 | |
| 93361 | +/* | |
| 93362 | +** High-resolution hardware timer used for debugging and testing only. | |
| 93363 | +*/ | |
| 93364 | +#if defined(VDBE_PROFILE) \ | |
| 93365 | + || defined(SQLITE_PERFORMANCE_TRACE) \ | |
| 93366 | + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) | |
| 93367 | +/************** Include hwtime.h in the middle of vdbe.c *********************/ | |
| 93368 | +/************** Begin file hwtime.h ******************************************/ | |
| 93369 | +/* | |
| 93370 | +** 2008 May 27 | |
| 93371 | +** | |
| 93372 | +** The author disclaims copyright to this source code. In place of | |
| 93373 | +** a legal notice, here is a blessing: | |
| 93374 | +** | |
| 93375 | +** May you do good and not evil. | |
| 93376 | +** May you find forgiveness for yourself and forgive others. | |
| 93377 | +** May you share freely, never taking more than you give. | |
| 93378 | +** | |
| 93379 | +****************************************************************************** | |
| 93380 | +** | |
| 93381 | +** This file contains inline asm code for retrieving "high-performance" | |
| 93382 | +** counters for x86 and x86_64 class CPUs. | |
| 93383 | +*/ | |
| 93384 | +#ifndef SQLITE_HWTIME_H | |
| 93385 | +#define SQLITE_HWTIME_H | |
| 93386 | + | |
| 93387 | +/* | |
| 93388 | +** The following routine only works on Pentium-class (or newer) processors. | |
| 93389 | +** It uses the RDTSC opcode to read the cycle count value out of the | |
| 93390 | +** processor and returns that value. This can be used for high-res | |
| 93391 | +** profiling. | |
| 93392 | +*/ | |
| 93393 | +#if !defined(__STRICT_ANSI__) && \ | |
| 93394 | + (defined(__GNUC__) || defined(_MSC_VER)) && \ | |
| 93395 | + (defined(i386) || defined(__i386__) || defined(_M_IX86)) | |
| 93396 | + | |
| 93397 | + #if defined(__GNUC__) | |
| 93398 | + | |
| 93399 | + __inline__ sqlite_uint64 sqlite3Hwtime(void){ | |
| 93400 | + unsigned int lo, hi; | |
| 93401 | + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | |
| 93402 | + return (sqlite_uint64)hi << 32 | lo; | |
| 93403 | + } | |
| 93404 | + | |
| 93405 | + #elif defined(_MSC_VER) | |
| 93406 | + | |
| 93407 | + __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ | |
| 93408 | + __asm { | |
| 93409 | + rdtsc | |
| 93410 | + ret ; return value at EDX:EAX | |
| 93411 | + } | |
| 93412 | + } | |
| 93413 | + | |
| 93414 | + #endif | |
| 93415 | + | |
| 93416 | +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) | |
| 93417 | + | |
| 93418 | + __inline__ sqlite_uint64 sqlite3Hwtime(void){ | |
| 93419 | + unsigned int lo, hi; | |
| 93420 | + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | |
| 93421 | + return (sqlite_uint64)hi << 32 | lo; | |
| 93422 | + } | |
| 93423 | + | |
| 93424 | +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) | |
| 93425 | + | |
| 93426 | + __inline__ sqlite_uint64 sqlite3Hwtime(void){ | |
| 93427 | + unsigned long long retval; | |
| 93428 | + unsigned long junk; | |
| 93429 | + __asm__ __volatile__ ("\n\ | |
| 93430 | + 1: mftbu %1\n\ | |
| 93431 | + mftb %L0\n\ | |
| 93432 | + mftbu %0\n\ | |
| 93433 | + cmpw %0,%1\n\ | |
| 93434 | + bne 1b" | |
| 93435 | + : "=r" (retval), "=r" (junk)); | |
| 93436 | + return retval; | |
| 93437 | + } | |
| 93438 | + | |
| 93439 | +#else | |
| 93440 | + | |
| 93441 | + /* | |
| 93442 | + ** asm() is needed for hardware timing support. Without asm(), | |
| 93443 | + ** disable the sqlite3Hwtime() routine. | |
| 93444 | + ** | |
| 93445 | + ** sqlite3Hwtime() is only used for some obscure debugging | |
| 93446 | + ** and analysis configurations, not in any deliverable, so this | |
| 93447 | + ** should not be a great loss. | |
| 93448 | + */ | |
| 93449 | +SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } | |
| 93450 | + | |
| 93451 | +#endif | |
| 93452 | + | |
| 93453 | +#endif /* !defined(SQLITE_HWTIME_H) */ | |
| 93454 | + | |
| 93455 | +/************** End of hwtime.h **********************************************/ | |
| 93456 | +/************** Continuing where we left off in vdbe.c ***********************/ | |
| 93457 | +#endif | |
| 93458 | + | |
| 93372 | 93459 | /* |
| 93373 | 93460 | ** Invoke this macro on memory cells just prior to changing the |
| 93374 | 93461 | ** value of the cell. This macro verifies that shallow copies are |
| 93375 | 93462 | ** not misused. A shallow copy of a string or blob just copies a |
| 93376 | 93463 | ** pointer to the string or blob, not the content. If the original |
| @@ -97875,12 +97962,17 @@ | ||
| 97875 | 97962 | 0, pCx->uc.pCursor); |
| 97876 | 97963 | pCx->isTable = 1; |
| 97877 | 97964 | } |
| 97878 | 97965 | } |
| 97879 | 97966 | pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); |
| 97967 | + assert( p->apCsr[pOp->p1]==pCx ); | |
| 97880 | 97968 | if( rc ){ |
| 97969 | + assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); | |
| 97881 | 97970 | sqlite3BtreeClose(pCx->ub.pBtx); |
| 97971 | + p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */ | |
| 97972 | + }else{ | |
| 97973 | + assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); | |
| 97882 | 97974 | } |
| 97883 | 97975 | } |
| 97884 | 97976 | } |
| 97885 | 97977 | if( rc ) goto abort_due_to_error; |
| 97886 | 97978 | pCx->nullRow = 1; |
| @@ -102394,11 +102486,11 @@ | ||
| 102394 | 102486 | ** element. |
| 102395 | 102487 | ** |
| 102396 | 102488 | ** As with all opcodes, the meanings of the parameters for OP_Explain |
| 102397 | 102489 | ** are subject to change from one release to the next. Applications |
| 102398 | 102490 | ** should not attempt to interpret or use any of the information |
| 102399 | -** contined in the OP_Explain opcode. The information provided by this | |
| 102491 | +** contained in the OP_Explain opcode. The information provided by this | |
| 102400 | 102492 | ** opcode is intended for testing and debugging use only. |
| 102401 | 102493 | */ |
| 102402 | 102494 | default: { /* This is really OP_Noop, OP_Explain */ |
| 102403 | 102495 | assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); |
| 102404 | 102496 | |
| @@ -107490,11 +107582,11 @@ | ||
| 107490 | 107582 | ** non-VIEW candidate plus multiple VIEW candidates. In other |
| 107491 | 107583 | ** words non-VIEW candidate terms take precedence over VIEWs. |
| 107492 | 107584 | */ |
| 107493 | 107585 | if( cntTab==0 |
| 107494 | 107586 | || (cntTab==1 |
| 107495 | - && ALWAYS(pMatch!=0) | |
| 107587 | + && pMatch!=0 | |
| 107496 | 107588 | && ALWAYS(pMatch->pSTab!=0) |
| 107497 | 107589 | && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 |
| 107498 | 107590 | && (pTab->tabFlags & TF_Ephemeral)==0) |
| 107499 | 107591 | ){ |
| 107500 | 107592 | cntTab = 1; |
| @@ -108123,12 +108215,12 @@ | ||
| 108123 | 108215 | } |
| 108124 | 108216 | |
| 108125 | 108217 | /* Resolve function names |
| 108126 | 108218 | */ |
| 108127 | 108219 | case TK_FUNCTION: { |
| 108128 | - ExprList *pList = pExpr->x.pList; /* The argument list */ | |
| 108129 | - int n = pList ? pList->nExpr : 0; /* Number of arguments */ | |
| 108220 | + ExprList *pList; /* The argument list */ | |
| 108221 | + int n; /* Number of arguments */ | |
| 108130 | 108222 | int no_such_func = 0; /* True if no such function exists */ |
| 108131 | 108223 | int wrong_num_args = 0; /* True if wrong number of arguments */ |
| 108132 | 108224 | int is_agg = 0; /* True if is an aggregate function */ |
| 108133 | 108225 | const char *zId; /* The function name. */ |
| 108134 | 108226 | FuncDef *pDef; /* Information about the function */ |
| @@ -108137,10 +108229,12 @@ | ||
| 108137 | 108229 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 108138 | 108230 | Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); |
| 108139 | 108231 | #endif |
| 108140 | 108232 | assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); |
| 108141 | 108233 | assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); |
| 108234 | + pList = pExpr->x.pList; | |
| 108235 | + n = pList ? pList->nExpr : 0; | |
| 108142 | 108236 | zId = pExpr->u.zToken; |
| 108143 | 108237 | pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); |
| 108144 | 108238 | if( pDef==0 ){ |
| 108145 | 108239 | pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0); |
| 108146 | 108240 | if( pDef==0 ){ |
| @@ -108185,10 +108279,28 @@ | ||
| 108185 | 108279 | pExpr->op = TK_NULL; |
| 108186 | 108280 | return WRC_Prune; |
| 108187 | 108281 | } |
| 108188 | 108282 | } |
| 108189 | 108283 | #endif |
| 108284 | + | |
| 108285 | + /* If the function may call sqlite3_value_subtype(), then set the | |
| 108286 | + ** EP_SubtArg flag on all of its argument expressions. This prevents | |
| 108287 | + ** where.c from replacing the expression with a value read from an | |
| 108288 | + ** index on the same expression, which will not have the correct | |
| 108289 | + ** subtype. Also set the flag if the function expression itself is | |
| 108290 | + ** an EP_SubtArg expression. In this case subtypes are required as | |
| 108291 | + ** the function may return a value with a subtype back to its | |
| 108292 | + ** caller using sqlite3_result_value(). */ | |
| 108293 | + if( (pDef->funcFlags & SQLITE_SUBTYPE) | |
| 108294 | + || ExprHasProperty(pExpr, EP_SubtArg) | |
| 108295 | + ){ | |
| 108296 | + int ii; | |
| 108297 | + for(ii=0; ii<n; ii++){ | |
| 108298 | + ExprSetProperty(pList->a[ii].pExpr, EP_SubtArg); | |
| 108299 | + } | |
| 108300 | + } | |
| 108301 | + | |
| 108190 | 108302 | if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ |
| 108191 | 108303 | /* For the purposes of the EP_ConstFunc flag, date and time |
| 108192 | 108304 | ** functions and other functions that change slowly are considered |
| 108193 | 108305 | ** constant because they are constant for the duration of one query. |
| 108194 | 108306 | ** This allows them to be factored out of inner loops. */ |
| @@ -111951,11 +112063,11 @@ | ||
| 111951 | 112063 | ** |
| 111952 | 112064 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 111953 | 112065 | ** (4a) pExpr must come from an ON clause.. |
| 111954 | 112066 | ** (4b) and specifically the ON clause associated with the LEFT JOIN. |
| 111955 | 112067 | ** |
| 111956 | -** (5) If pSrc is not the right operand of a LEFT JOIN or the left | |
| 112068 | +** (5) If pSrc is the right operand of a LEFT JOIN or the left | |
| 111957 | 112069 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 111958 | 112070 | ** clause, not an ON clause. |
| 111959 | 112071 | ** |
| 111960 | 112072 | ** (6) Either: |
| 111961 | 112073 | ** |
| @@ -113858,10 +113970,63 @@ | ||
| 113858 | 113970 | } |
| 113859 | 113971 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 113860 | 113972 | } |
| 113861 | 113973 | return target; |
| 113862 | 113974 | } |
| 113975 | + | |
| 113976 | +/* | |
| 113977 | +** Expression Node callback for sqlite3ExprCanReturnSubtype(). | |
| 113978 | +** | |
| 113979 | +** Only a function call is able to return a subtype. So if the node | |
| 113980 | +** is not a function call, return WRC_Prune immediately. | |
| 113981 | +** | |
| 113982 | +** A function call is able to return a subtype if it has the | |
| 113983 | +** SQLITE_RESULT_SUBTYPE property. | |
| 113984 | +** | |
| 113985 | +** Assume that every function is able to pass-through a subtype from | |
| 113986 | +** one of its argument (using sqlite3_result_value()). Most functions | |
| 113987 | +** are not this way, but we don't have a mechanism to distinguish those | |
| 113988 | +** that are from those that are not, so assume they all work this way. | |
| 113989 | +** That means that if one of its arguments is another function and that | |
| 113990 | +** other function is able to return a subtype, then this function is | |
| 113991 | +** able to return a subtype. | |
| 113992 | +*/ | |
| 113993 | +static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ | |
| 113994 | + int n; | |
| 113995 | + FuncDef *pDef; | |
| 113996 | + sqlite3 *db; | |
| 113997 | + if( pExpr->op!=TK_FUNCTION ){ | |
| 113998 | + return WRC_Prune; | |
| 113999 | + } | |
| 114000 | + assert( ExprUseXList(pExpr) ); | |
| 114001 | + db = pWalker->pParse->db; | |
| 114002 | + n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; | |
| 114003 | + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); | |
| 114004 | + if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ | |
| 114005 | + pWalker->eCode = 1; | |
| 114006 | + return WRC_Prune; | |
| 114007 | + } | |
| 114008 | + return WRC_Continue; | |
| 114009 | +} | |
| 114010 | + | |
| 114011 | +/* | |
| 114012 | +** Return TRUE if expression pExpr is able to return a subtype. | |
| 114013 | +** | |
| 114014 | +** A TRUE return does not guarantee that a subtype will be returned. | |
| 114015 | +** It only indicates that a subtype return is possible. False positives | |
| 114016 | +** are acceptable as they only disable an optimization. False negatives, | |
| 114017 | +** on the other hand, can lead to incorrect answers. | |
| 114018 | +*/ | |
| 114019 | +static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ | |
| 114020 | + Walker w; | |
| 114021 | + memset(&w, 0, sizeof(w)); | |
| 114022 | + w.pParse = pParse; | |
| 114023 | + w.xExprCallback = exprNodeCanReturnSubtype; | |
| 114024 | + sqlite3WalkExpr(&w, pExpr); | |
| 114025 | + return w.eCode; | |
| 114026 | +} | |
| 114027 | + | |
| 113863 | 114028 | |
| 113864 | 114029 | /* |
| 113865 | 114030 | ** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. |
| 113866 | 114031 | ** If it is, then resolve the expression by reading from the index and |
| 113867 | 114032 | ** return the register into which the value has been read. If pExpr is |
| @@ -113891,10 +114056,21 @@ | ||
| 113891 | 114056 | ){ |
| 113892 | 114057 | /* Affinity mismatch on a generated column */ |
| 113893 | 114058 | continue; |
| 113894 | 114059 | } |
| 113895 | 114060 | |
| 114061 | + | |
| 114062 | + /* Functions that might set a subtype should not be replaced by the | |
| 114063 | + ** value taken from an expression index if they are themselves an | |
| 114064 | + ** argument to another scalar function or aggregate. | |
| 114065 | + ** https://sqlite.org/forum/forumpost/68d284c86b082c3e */ | |
| 114066 | + if( ExprHasProperty(pExpr, EP_SubtArg) | |
| 114067 | + && sqlite3ExprCanReturnSubtype(pParse, pExpr) | |
| 114068 | + ){ | |
| 114069 | + continue; | |
| 114070 | + } | |
| 114071 | + | |
| 113896 | 114072 | v = pParse->pVdbe; |
| 113897 | 114073 | assert( v!=0 ); |
| 113898 | 114074 | if( p->bMaybeNullRow ){ |
| 113899 | 114075 | /* If the index is on a NULL row due to an outer join, then we |
| 113900 | 114076 | ** cannot extract the value from the index. The value must be |
| @@ -115421,35 +115597,41 @@ | ||
| 115421 | 115597 | ** |
| 115422 | 115598 | ** Additionally, if pExpr is a simple SQL value and the value is the |
| 115423 | 115599 | ** same as that currently bound to variable pVar, non-zero is returned. |
| 115424 | 115600 | ** Otherwise, if the values are not the same or if pExpr is not a simple |
| 115425 | 115601 | ** SQL value, zero is returned. |
| 115602 | +** | |
| 115603 | +** If the SQLITE_EnableQPSG flag is set on the database connection, then | |
| 115604 | +** this routine always returns false. | |
| 115426 | 115605 | */ |
| 115427 | -static int exprCompareVariable( | |
| 115606 | +static SQLITE_NOINLINE int exprCompareVariable( | |
| 115428 | 115607 | const Parse *pParse, |
| 115429 | 115608 | const Expr *pVar, |
| 115430 | 115609 | const Expr *pExpr |
| 115431 | 115610 | ){ |
| 115432 | - int res = 0; | |
| 115611 | + int res = 2; | |
| 115433 | 115612 | int iVar; |
| 115434 | 115613 | sqlite3_value *pL, *pR = 0; |
| 115435 | 115614 | |
| 115615 | + if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){ | |
| 115616 | + return 0; | |
| 115617 | + } | |
| 115618 | + if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2; | |
| 115436 | 115619 | sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); |
| 115437 | 115620 | if( pR ){ |
| 115438 | 115621 | iVar = pVar->iColumn; |
| 115439 | 115622 | sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); |
| 115440 | 115623 | pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB); |
| 115441 | 115624 | if( pL ){ |
| 115442 | 115625 | if( sqlite3_value_type(pL)==SQLITE_TEXT ){ |
| 115443 | 115626 | sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ |
| 115444 | 115627 | } |
| 115445 | - res = 0==sqlite3MemCompare(pL, pR, 0); | |
| 115628 | + res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0; | |
| 115446 | 115629 | } |
| 115447 | 115630 | sqlite3ValueFree(pR); |
| 115448 | 115631 | sqlite3ValueFree(pL); |
| 115449 | 115632 | } |
| 115450 | - | |
| 115451 | 115633 | return res; |
| 115452 | 115634 | } |
| 115453 | 115635 | |
| 115454 | 115636 | /* |
| 115455 | 115637 | ** Do a deep comparison of two expression trees. Return 0 if the two |
| @@ -115471,16 +115653,14 @@ | ||
| 115471 | 115653 | ** can be sure the expressions are the same. In the places where |
| 115472 | 115654 | ** this routine is used, it does not hurt to get an extra 2 - that |
| 115473 | 115655 | ** just might result in some slightly slower code. But returning |
| 115474 | 115656 | ** an incorrect 0 or 1 could lead to a malfunction. |
| 115475 | 115657 | ** |
| 115476 | -** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in | |
| 115477 | -** pParse->pReprepare can be matched against literals in pB. The | |
| 115478 | -** pParse->pVdbe->expmask bitmask is updated for each variable referenced. | |
| 115479 | -** If pParse is NULL (the normal case) then any TK_VARIABLE term in | |
| 115480 | -** Argument pParse should normally be NULL. If it is not NULL and pA or | |
| 115481 | -** pB causes a return value of 2. | |
| 115658 | +** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE | |
| 115659 | +** terms in pA with bindings in pParse->pReprepare can be matched against | |
| 115660 | +** literals in pB. The pParse->pVdbe->expmask bitmask is updated for | |
| 115661 | +** each variable referenced. | |
| 115482 | 115662 | */ |
| 115483 | 115663 | SQLITE_PRIVATE int sqlite3ExprCompare( |
| 115484 | 115664 | const Parse *pParse, |
| 115485 | 115665 | const Expr *pA, |
| 115486 | 115666 | const Expr *pB, |
| @@ -115488,12 +115668,12 @@ | ||
| 115488 | 115668 | ){ |
| 115489 | 115669 | u32 combinedFlags; |
| 115490 | 115670 | if( pA==0 || pB==0 ){ |
| 115491 | 115671 | return pB==pA ? 0 : 2; |
| 115492 | 115672 | } |
| 115493 | - if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){ | |
| 115494 | - return 0; | |
| 115673 | + if( pParse && pA->op==TK_VARIABLE ){ | |
| 115674 | + return exprCompareVariable(pParse, pA, pB); | |
| 115495 | 115675 | } |
| 115496 | 115676 | combinedFlags = pA->flags | pB->flags; |
| 115497 | 115677 | if( combinedFlags & EP_IntValue ){ |
| 115498 | 115678 | if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ |
| 115499 | 115679 | return 0; |
| @@ -115683,23 +115863,75 @@ | ||
| 115683 | 115863 | return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); |
| 115684 | 115864 | } |
| 115685 | 115865 | } |
| 115686 | 115866 | return 0; |
| 115687 | 115867 | } |
| 115868 | + | |
| 115869 | +/* | |
| 115870 | +** Return true if the boolean value of the expression is always either | |
| 115871 | +** FALSE or NULL. | |
| 115872 | +*/ | |
| 115873 | +static int sqlite3ExprIsNotTrue(Expr *pExpr){ | |
| 115874 | + int v; | |
| 115875 | + if( pExpr->op==TK_NULL ) return 1; | |
| 115876 | + if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1; | |
| 115877 | + v = 1; | |
| 115878 | + if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1; | |
| 115879 | + return 0; | |
| 115880 | +} | |
| 115881 | + | |
| 115882 | +/* | |
| 115883 | +** Return true if the expression is one of the following: | |
| 115884 | +** | |
| 115885 | +** CASE WHEN x THEN y END | |
| 115886 | +** CASE WHEN x THEN y ELSE NULL END | |
| 115887 | +** CASE WHEN x THEN y ELSE false END | |
| 115888 | +** iif(x,y) | |
| 115889 | +** iif(x,y,NULL) | |
| 115890 | +** iif(x,y,false) | |
| 115891 | +*/ | |
| 115892 | +static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){ | |
| 115893 | + ExprList *pList; | |
| 115894 | + if( pExpr->op==TK_FUNCTION ){ | |
| 115895 | + const char *z = pExpr->u.zToken; | |
| 115896 | + FuncDef *pDef; | |
| 115897 | + if( (z[0]!='i' && z[0]!='I') ) return 0; | |
| 115898 | + if( pExpr->x.pList==0 ) return 0; | |
| 115899 | + pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0); | |
| 115900 | +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION | |
| 115901 | + if( pDef==0 ) return 0; | |
| 115902 | +#else | |
| 115903 | + if( NEVER(pDef==0) ) return 0; | |
| 115904 | +#endif | |
| 115905 | + if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0; | |
| 115906 | + if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0; | |
| 115907 | + }else if( pExpr->op==TK_CASE ){ | |
| 115908 | + if( pExpr->pLeft!=0 ) return 0; | |
| 115909 | + }else{ | |
| 115910 | + return 0; | |
| 115911 | + } | |
| 115912 | + pList = pExpr->x.pList; | |
| 115913 | + assert( pList!=0 ); | |
| 115914 | + if( pList->nExpr==2 ) return 1; | |
| 115915 | + if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1; | |
| 115916 | + return 0; | |
| 115917 | +} | |
| 115688 | 115918 | |
| 115689 | 115919 | /* |
| 115690 | 115920 | ** Return true if we can prove the pE2 will always be true if pE1 is |
| 115691 | 115921 | ** true. Return false if we cannot complete the proof or if pE2 might |
| 115692 | 115922 | ** be false. Examples: |
| 115693 | 115923 | ** |
| 115694 | -** pE1: x==5 pE2: x==5 Result: true | |
| 115695 | -** pE1: x>0 pE2: x==5 Result: false | |
| 115696 | -** pE1: x=21 pE2: x=21 OR y=43 Result: true | |
| 115697 | -** pE1: x!=123 pE2: x IS NOT NULL Result: true | |
| 115698 | -** pE1: x!=?1 pE2: x IS NOT NULL Result: true | |
| 115699 | -** pE1: x IS NULL pE2: x IS NOT NULL Result: false | |
| 115700 | -** pE1: x IS ?2 pE2: x IS NOT NULL Result: false | |
| 115924 | +** pE1: x==5 pE2: x==5 Result: true | |
| 115925 | +** pE1: x>0 pE2: x==5 Result: false | |
| 115926 | +** pE1: x=21 pE2: x=21 OR y=43 Result: true | |
| 115927 | +** pE1: x!=123 pE2: x IS NOT NULL Result: true | |
| 115928 | +** pE1: x!=?1 pE2: x IS NOT NULL Result: true | |
| 115929 | +** pE1: x IS NULL pE2: x IS NOT NULL Result: false | |
| 115930 | +** pE1: x IS ?2 pE2: x IS NOT NULL Result: false | |
| 115931 | +** pE1: iif(x,y) pE2: x Result: true | |
| 115932 | +** PE1: iif(x,y,0) pE2: x Result: true | |
| 115701 | 115933 | ** |
| 115702 | 115934 | ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has |
| 115703 | 115935 | ** Expr.iTable<0 then assume a table number given by iTab. |
| 115704 | 115936 | ** |
| 115705 | 115937 | ** If pParse is not NULL, then the values of bound variables in pE1 are |
| @@ -115729,10 +115961,13 @@ | ||
| 115729 | 115961 | if( pE2->op==TK_NOTNULL |
| 115730 | 115962 | && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0) |
| 115731 | 115963 | ){ |
| 115732 | 115964 | return 1; |
| 115733 | 115965 | } |
| 115966 | + if( sqlite3ExprIsIIF(pParse->db, pE1) ){ | |
| 115967 | + return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab); | |
| 115968 | + } | |
| 115734 | 115969 | return 0; |
| 115735 | 115970 | } |
| 115736 | 115971 | |
| 115737 | 115972 | /* This is a helper function to impliesNotNullRow(). In this routine, |
| 115738 | 115973 | ** set pWalker->eCode to one only if *both* of the input expressions |
| @@ -120689,12 +120924,12 @@ | ||
| 120689 | 120924 | int nIdxCol = 1; /* Number of columns in stat4 records */ |
| 120690 | 120925 | |
| 120691 | 120926 | char *zIndex; /* Index name */ |
| 120692 | 120927 | Index *pIdx; /* Pointer to the index object */ |
| 120693 | 120928 | int nSample; /* Number of samples */ |
| 120694 | - int nByte; /* Bytes of space required */ | |
| 120695 | - int i; /* Bytes of space required */ | |
| 120929 | + i64 nByte; /* Bytes of space required */ | |
| 120930 | + i64 i; /* Bytes of space required */ | |
| 120696 | 120931 | tRowcnt *pSpace; /* Available allocated memory space */ |
| 120697 | 120932 | u8 *pPtr; /* Available memory as a u8 for easier manipulation */ |
| 120698 | 120933 | |
| 120699 | 120934 | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| 120700 | 120935 | if( zIndex==0 ) continue; |
| @@ -121140,19 +121375,10 @@ | ||
| 121140 | 121375 | rc = sqlite3Init(db, &zErrDyn); |
| 121141 | 121376 | } |
| 121142 | 121377 | sqlite3BtreeLeaveAll(db); |
| 121143 | 121378 | assert( zErrDyn==0 || rc!=SQLITE_OK ); |
| 121144 | 121379 | } |
| 121145 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 121146 | - if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ | |
| 121147 | - u8 newAuth = 0; | |
| 121148 | - rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); | |
| 121149 | - if( newAuth<db->auth.authLevel ){ | |
| 121150 | - rc = SQLITE_AUTH_USER; | |
| 121151 | - } | |
| 121152 | - } | |
| 121153 | -#endif | |
| 121154 | 121380 | if( rc ){ |
| 121155 | 121381 | if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){ |
| 121156 | 121382 | int iDb = db->nDb - 1; |
| 121157 | 121383 | assert( iDb>=2 ); |
| 121158 | 121384 | if( db->aDb[iDb].pBt ){ |
| @@ -121646,15 +121872,11 @@ | ||
| 121646 | 121872 | sqlite3 *db = pParse->db; /* Database handle */ |
| 121647 | 121873 | char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */ |
| 121648 | 121874 | int rc; /* Auth callback return code */ |
| 121649 | 121875 | |
| 121650 | 121876 | if( db->init.busy ) return SQLITE_OK; |
| 121651 | - rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext | |
| 121652 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 121653 | - ,db->auth.zAuthUser | |
| 121654 | -#endif | |
| 121655 | - ); | |
| 121877 | + rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); | |
| 121656 | 121878 | if( rc==SQLITE_DENY ){ |
| 121657 | 121879 | char *z = sqlite3_mprintf("%s.%s", zTab, zCol); |
| 121658 | 121880 | if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z); |
| 121659 | 121881 | sqlite3ErrorMsg(pParse, "access to %z is prohibited", z); |
| 121660 | 121882 | pParse->rc = SQLITE_AUTH; |
| @@ -121757,15 +121979,11 @@ | ||
| 121757 | 121979 | testcase( zArg1==0 ); |
| 121758 | 121980 | testcase( zArg2==0 ); |
| 121759 | 121981 | testcase( zArg3==0 ); |
| 121760 | 121982 | testcase( pParse->zAuthContext==0 ); |
| 121761 | 121983 | |
| 121762 | - rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext | |
| 121763 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 121764 | - ,db->auth.zAuthUser | |
| 121765 | -#endif | |
| 121766 | - ); | |
| 121984 | + rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext); | |
| 121767 | 121985 | if( rc==SQLITE_DENY ){ |
| 121768 | 121986 | sqlite3ErrorMsg(pParse, "not authorized"); |
| 121769 | 121987 | pParse->rc = SQLITE_AUTH; |
| 121770 | 121988 | }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ |
| 121771 | 121989 | rc = SQLITE_DENY; |
| @@ -121994,21 +122212,10 @@ | ||
| 121994 | 122212 | sqlite3VdbeJumpHere(v, addrRewind); |
| 121995 | 122213 | } |
| 121996 | 122214 | } |
| 121997 | 122215 | sqlite3VdbeAddOp0(v, OP_Halt); |
| 121998 | 122216 | |
| 121999 | -#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE) | |
| 122000 | - if( pParse->nTableLock>0 && db->init.busy==0 ){ | |
| 122001 | - sqlite3UserAuthInit(db); | |
| 122002 | - if( db->auth.authLevel<UAUTH_User ){ | |
| 122003 | - sqlite3ErrorMsg(pParse, "user not authenticated"); | |
| 122004 | - pParse->rc = SQLITE_AUTH_USER; | |
| 122005 | - return; | |
| 122006 | - } | |
| 122007 | - } | |
| 122008 | -#endif | |
| 122009 | - | |
| 122010 | 122217 | /* The cookie mask contains one bit for each database file open. |
| 122011 | 122218 | ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are |
| 122012 | 122219 | ** set for each database that is used. Generate code to start a |
| 122013 | 122220 | ** transaction on each used database and to verify the schema cookie |
| 122014 | 122221 | ** on each used database. |
| @@ -122133,20 +122340,10 @@ | ||
| 122133 | 122340 | sqlite3DbFree(db, zSql); |
| 122134 | 122341 | memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); |
| 122135 | 122342 | pParse->nested--; |
| 122136 | 122343 | } |
| 122137 | 122344 | |
| 122138 | -#if SQLITE_USER_AUTHENTICATION | |
| 122139 | -/* | |
| 122140 | -** Return TRUE if zTable is the name of the system table that stores the | |
| 122141 | -** list of users and their access credentials. | |
| 122142 | -*/ | |
| 122143 | -SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){ | |
| 122144 | - return sqlite3_stricmp(zTable, "sqlite_user")==0; | |
| 122145 | -} | |
| 122146 | -#endif | |
| 122147 | - | |
| 122148 | 122345 | /* |
| 122149 | 122346 | ** Locate the in-memory structure that describes a particular database |
| 122150 | 122347 | ** table given the name of that table and (optionally) the name of the |
| 122151 | 122348 | ** database containing the table. Return NULL if not found. |
| 122152 | 122349 | ** |
| @@ -122161,17 +122358,10 @@ | ||
| 122161 | 122358 | Table *p = 0; |
| 122162 | 122359 | int i; |
| 122163 | 122360 | |
| 122164 | 122361 | /* All mutexes are required for schema access. Make sure we hold them. */ |
| 122165 | 122362 | assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); |
| 122166 | -#if SQLITE_USER_AUTHENTICATION | |
| 122167 | - /* Only the admin user is allowed to know that the sqlite_user table | |
| 122168 | - ** exists */ | |
| 122169 | - if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ | |
| 122170 | - return 0; | |
| 122171 | - } | |
| 122172 | -#endif | |
| 122173 | 122363 | if( zDatabase ){ |
| 122174 | 122364 | for(i=0; i<db->nDb; i++){ |
| 122175 | 122365 | if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; |
| 122176 | 122366 | } |
| 122177 | 122367 | if( i>=db->nDb ){ |
| @@ -125826,13 +126016,10 @@ | ||
| 125826 | 126016 | |
| 125827 | 126017 | assert( pTab!=0 ); |
| 125828 | 126018 | if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 |
| 125829 | 126019 | && db->init.busy==0 |
| 125830 | 126020 | && pTblName!=0 |
| 125831 | -#if SQLITE_USER_AUTHENTICATION | |
| 125832 | - && sqlite3UserAuthTable(pTab->zName)==0 | |
| 125833 | -#endif | |
| 125834 | 126021 | ){ |
| 125835 | 126022 | sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); |
| 125836 | 126023 | goto exit_create_index; |
| 125837 | 126024 | } |
| 125838 | 126025 | #ifndef SQLITE_OMIT_VIEW |
| @@ -128224,10 +128411,11 @@ | ||
| 128224 | 128411 | ** 4) The table is a shadow table, the database connection is in |
| 128225 | 128412 | ** defensive mode, and the current sqlite3_prepare() |
| 128226 | 128413 | ** is for a top-level SQL statement. |
| 128227 | 128414 | */ |
| 128228 | 128415 | static int vtabIsReadOnly(Parse *pParse, Table *pTab){ |
| 128416 | + assert( IsVirtual(pTab) ); | |
| 128229 | 128417 | if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ |
| 128230 | 128418 | return 1; |
| 128231 | 128419 | } |
| 128232 | 128420 | |
| 128233 | 128421 | /* Within triggers: |
| @@ -131716,11 +131904,17 @@ | ||
| 131716 | 131904 | #ifdef SQLITE_DEBUG |
| 131717 | 131905 | /* |
| 131718 | 131906 | ** Implementation of fpdecode(x,y,z) function. |
| 131719 | 131907 | ** |
| 131720 | 131908 | ** x is a real number that is to be decoded. y is the precision. |
| 131721 | -** z is the maximum real precision. | |
| 131909 | +** z is the maximum real precision. Return a string that shows the | |
| 131910 | +** results of the sqlite3FpDecode() function. | |
| 131911 | +** | |
| 131912 | +** Used for testing and debugging only, specifically testing and debugging | |
| 131913 | +** of the sqlite3FpDecode() function. This SQL function does not appear | |
| 131914 | +** in production builds. This function is not an API and is subject to | |
| 131915 | +** modification or removal in future versions of SQLite. | |
| 131722 | 131916 | */ |
| 131723 | 131917 | static void fpdecodeFunc( |
| 131724 | 131918 | sqlite3_context *context, |
| 131725 | 131919 | int argc, |
| 131726 | 131920 | sqlite3_value **argv |
| @@ -131740,10 +131934,86 @@ | ||
| 131740 | 131934 | sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); |
| 131741 | 131935 | }else{ |
| 131742 | 131936 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); |
| 131743 | 131937 | } |
| 131744 | 131938 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| 131939 | +} | |
| 131940 | +#endif /* SQLITE_DEBUG */ | |
| 131941 | + | |
| 131942 | +#ifdef SQLITE_DEBUG | |
| 131943 | +/* | |
| 131944 | +** Implementation of parseuri(uri,flags) function. | |
| 131945 | +** | |
| 131946 | +** Required Arguments: | |
| 131947 | +** "uri" The URI to parse. | |
| 131948 | +** "flags" Bitmask of flags, as if to sqlite3_open_v2(). | |
| 131949 | +** | |
| 131950 | +** Additional arguments beyond the first two make calls to | |
| 131951 | +** sqlite3_uri_key() for integers and sqlite3_uri_parameter for | |
| 131952 | +** anything else. | |
| 131953 | +** | |
| 131954 | +** The result is a string showing the results of calling sqlite3ParseUri(). | |
| 131955 | +** | |
| 131956 | +** Used for testing and debugging only, specifically testing and debugging | |
| 131957 | +** of the sqlite3ParseUri() function. This SQL function does not appear | |
| 131958 | +** in production builds. This function is not an API and is subject to | |
| 131959 | +** modification or removal in future versions of SQLite. | |
| 131960 | +*/ | |
| 131961 | +static void parseuriFunc( | |
| 131962 | + sqlite3_context *ctx, | |
| 131963 | + int argc, | |
| 131964 | + sqlite3_value **argv | |
| 131965 | +){ | |
| 131966 | + sqlite3_str *pResult; | |
| 131967 | + const char *zVfs; | |
| 131968 | + const char *zUri; | |
| 131969 | + unsigned int flgs; | |
| 131970 | + int rc; | |
| 131971 | + sqlite3_vfs *pVfs = 0; | |
| 131972 | + char *zFile = 0; | |
| 131973 | + char *zErr = 0; | |
| 131974 | + | |
| 131975 | + if( argc<2 ) return; | |
| 131976 | + pVfs = sqlite3_vfs_find(0); | |
| 131977 | + assert( pVfs ); | |
| 131978 | + zVfs = pVfs->zName; | |
| 131979 | + zUri = (const char*)sqlite3_value_text(argv[0]); | |
| 131980 | + if( zUri==0 ) return; | |
| 131981 | + flgs = (unsigned int)sqlite3_value_int(argv[1]); | |
| 131982 | + rc = sqlite3ParseUri(zVfs, zUri, &flgs, &pVfs, &zFile, &zErr); | |
| 131983 | + pResult = sqlite3_str_new(0); | |
| 131984 | + if( pResult ){ | |
| 131985 | + int i; | |
| 131986 | + sqlite3_str_appendf(pResult, "rc=%d", rc); | |
| 131987 | + sqlite3_str_appendf(pResult, ", flags=0x%x", flgs); | |
| 131988 | + sqlite3_str_appendf(pResult, ", vfs=%Q", pVfs ? pVfs->zName: 0); | |
| 131989 | + sqlite3_str_appendf(pResult, ", err=%Q", zErr); | |
| 131990 | + sqlite3_str_appendf(pResult, ", file=%Q", zFile); | |
| 131991 | + if( zFile ){ | |
| 131992 | + const char *z = zFile; | |
| 131993 | + z += sqlite3Strlen30(z)+1; | |
| 131994 | + while( z[0] ){ | |
| 131995 | + sqlite3_str_appendf(pResult, ", %Q", z); | |
| 131996 | + z += sqlite3Strlen30(z)+1; | |
| 131997 | + } | |
| 131998 | + for(i=2; i<argc; i++){ | |
| 131999 | + const char *zArg; | |
| 132000 | + if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ | |
| 132001 | + int k = sqlite3_value_int(argv[i]); | |
| 132002 | + sqlite3_str_appendf(pResult, ", '%d:%q'",k,sqlite3_uri_key(zFile, k)); | |
| 132003 | + }else if( (zArg = (const char*)sqlite3_value_text(argv[i]))!=0 ){ | |
| 132004 | + sqlite3_str_appendf(pResult, ", '%q:%q'", | |
| 132005 | + zArg, sqlite3_uri_parameter(zFile,zArg)); | |
| 132006 | + }else{ | |
| 132007 | + sqlite3_str_appendf(pResult, ", NULL"); | |
| 132008 | + } | |
| 132009 | + } | |
| 132010 | + } | |
| 132011 | + sqlite3_result_text(ctx, sqlite3_str_finish(pResult), -1, sqlite3_free); | |
| 132012 | + } | |
| 132013 | + sqlite3_free_filename(zFile); | |
| 132014 | + sqlite3_free(zErr); | |
| 131745 | 132015 | } |
| 131746 | 132016 | #endif /* SQLITE_DEBUG */ |
| 131747 | 132017 | |
| 131748 | 132018 | /* |
| 131749 | 132019 | ** All of the FuncDef structures in the aBuiltinFunc[] array above |
| @@ -131777,13 +132047,10 @@ | ||
| 131777 | 132047 | #endif |
| 131778 | 132048 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 131779 | 132049 | SFUNCTION(load_extension, 1, 0, 0, loadExt ), |
| 131780 | 132050 | SFUNCTION(load_extension, 2, 0, 0, loadExt ), |
| 131781 | 132051 | #endif |
| 131782 | -#if SQLITE_USER_AUTHENTICATION | |
| 131783 | - FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ), | |
| 131784 | -#endif | |
| 131785 | 132052 | #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS |
| 131786 | 132053 | DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), |
| 131787 | 132054 | DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), |
| 131788 | 132055 | #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ |
| 131789 | 132056 | INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), |
| @@ -131805,11 +132072,12 @@ | ||
| 131805 | 132072 | FUNCTION(max, -1, 1, 1, minmaxFunc ), |
| 131806 | 132073 | FUNCTION(max, 0, 1, 1, 0 ), |
| 131807 | 132074 | WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, |
| 131808 | 132075 | SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), |
| 131809 | 132076 | FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), |
| 131810 | - FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), | |
| 132077 | + FUNCTION2(subtype, 1, 0, 0, subtypeFunc, | |
| 132078 | + SQLITE_FUNC_TYPEOF|SQLITE_SUBTYPE), | |
| 131811 | 132079 | FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), |
| 131812 | 132080 | FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN), |
| 131813 | 132081 | FUNCTION(instr, 2, 0, 0, instrFunc ), |
| 131814 | 132082 | FUNCTION(printf, -1, 0, 0, printfFunc ), |
| 131815 | 132083 | FUNCTION(format, -1, 0, 0, printfFunc ), |
| @@ -131816,10 +132084,11 @@ | ||
| 131816 | 132084 | FUNCTION(unicode, 1, 0, 0, unicodeFunc ), |
| 131817 | 132085 | FUNCTION(char, -1, 0, 0, charFunc ), |
| 131818 | 132086 | FUNCTION(abs, 1, 0, 0, absFunc ), |
| 131819 | 132087 | #ifdef SQLITE_DEBUG |
| 131820 | 132088 | FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ), |
| 132089 | + FUNCTION(parseuri, -1, 0, 0, parseuriFunc ), | |
| 131821 | 132090 | #endif |
| 131822 | 132091 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 131823 | 132092 | FUNCTION(round, 1, 0, 0, roundFunc ), |
| 131824 | 132093 | FUNCTION(round, 2, 0, 0, roundFunc ), |
| 131825 | 132094 | #endif |
| @@ -131910,15 +132179,18 @@ | ||
| 131910 | 132179 | MFUNCTION(atanh, 1, atanh, math1Func ), |
| 131911 | 132180 | #endif |
| 131912 | 132181 | MFUNCTION(sqrt, 1, sqrt, math1Func ), |
| 131913 | 132182 | MFUNCTION(radians, 1, degToRad, math1Func ), |
| 131914 | 132183 | MFUNCTION(degrees, 1, radToDeg, math1Func ), |
| 131915 | - FUNCTION(pi, 0, 0, 0, piFunc ), | |
| 132184 | + MFUNCTION(pi, 0, 0, piFunc ), | |
| 131916 | 132185 | #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ |
| 131917 | 132186 | FUNCTION(sign, 1, 0, 0, signFunc ), |
| 131918 | 132187 | INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), |
| 132188 | + INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ), | |
| 131919 | 132189 | INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), |
| 132190 | + INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ), | |
| 132191 | + INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ), | |
| 131920 | 132192 | }; |
| 131921 | 132193 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 131922 | 132194 | sqlite3AlterFunctions(); |
| 131923 | 132195 | #endif |
| 131924 | 132196 | sqlite3WindowFunctions(); |
| @@ -140427,16 +140699,10 @@ | ||
| 140427 | 140699 | if( db->autoCommit==0 ){ |
| 140428 | 140700 | /* Foreign key support may not be enabled or disabled while not |
| 140429 | 140701 | ** in auto-commit mode. */ |
| 140430 | 140702 | mask &= ~(SQLITE_ForeignKeys); |
| 140431 | 140703 | } |
| 140432 | -#if SQLITE_USER_AUTHENTICATION | |
| 140433 | - if( db->auth.authLevel==UAUTH_User ){ | |
| 140434 | - /* Do not allow non-admin users to modify the schema arbitrarily */ | |
| 140435 | - mask &= ~(SQLITE_WriteSchema); | |
| 140436 | - } | |
| 140437 | -#endif | |
| 140438 | 140704 | |
| 140439 | 140705 | if( sqlite3GetBoolean(zRight, 0) ){ |
| 140440 | 140706 | if( (mask & SQLITE_WriteSchema)==0 |
| 140441 | 140707 | || (db->flags & SQLITE_Defensive)==0 |
| 140442 | 140708 | ){ |
| @@ -140568,11 +140834,12 @@ | ||
| 140568 | 140834 | pTab = sqliteHashData(k); |
| 140569 | 140835 | if( pTab->nCol==0 ){ |
| 140570 | 140836 | char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); |
| 140571 | 140837 | if( zSql ){ |
| 140572 | 140838 | sqlite3_stmt *pDummy = 0; |
| 140573 | - (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0); | |
| 140839 | + (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG, | |
| 140840 | + &pDummy, 0); | |
| 140574 | 140841 | (void)sqlite3_finalize(pDummy); |
| 140575 | 140842 | sqlite3DbFree(db, zSql); |
| 140576 | 140843 | } |
| 140577 | 140844 | if( db->mallocFailed ){ |
| 140578 | 140845 | sqlite3ErrorMsg(db->pParse, "out of memory"); |
| @@ -141044,10 +141311,11 @@ | ||
| 141044 | 141311 | } |
| 141045 | 141312 | aRoot[0] = cnt; |
| 141046 | 141313 | |
| 141047 | 141314 | /* Make sure sufficient number of registers have been allocated */ |
| 141048 | 141315 | sqlite3TouchRegister(pParse, 8+cnt); |
| 141316 | + sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); | |
| 141049 | 141317 | sqlite3ClearTempRegCache(pParse); |
| 141050 | 141318 | |
| 141051 | 141319 | /* Do the b-tree integrity checks */ |
| 141052 | 141320 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141053 | 141321 | sqlite3VdbeChangeP5(v, (u8)i); |
| @@ -142668,18 +142936,11 @@ | ||
| 142668 | 142936 | encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; |
| 142669 | 142937 | if( encoding==0 ) encoding = SQLITE_UTF8; |
| 142670 | 142938 | #else |
| 142671 | 142939 | encoding = SQLITE_UTF8; |
| 142672 | 142940 | #endif |
| 142673 | - if( db->nVdbeActive>0 && encoding!=ENC(db) | |
| 142674 | - && (db->mDbFlags & DBFLAG_Vacuum)==0 | |
| 142675 | - ){ | |
| 142676 | - rc = SQLITE_LOCKED; | |
| 142677 | - goto initone_error_out; | |
| 142678 | - }else{ | |
| 142679 | - sqlite3SetTextEncoding(db, encoding); | |
| 142680 | - } | |
| 142941 | + sqlite3SetTextEncoding(db, encoding); | |
| 142681 | 142942 | }else{ |
| 142682 | 142943 | /* If opening an attached database, the encoding much match ENC(db) */ |
| 142683 | 142944 | if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){ |
| 142684 | 142945 | sqlite3SetString(pzErrMsg, db, "attached databases must use the same" |
| 142685 | 142946 | " text encoding as main database"); |
| @@ -143369,16 +143630,28 @@ | ||
| 143369 | 143630 | #endif |
| 143370 | 143631 | *ppStmt = 0; |
| 143371 | 143632 | if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ |
| 143372 | 143633 | return SQLITE_MISUSE_BKPT; |
| 143373 | 143634 | } |
| 143635 | + | |
| 143636 | + /* Make sure nBytes is non-negative and correct. It should be the | |
| 143637 | + ** number of bytes until the end of the input buffer or until the first | |
| 143638 | + ** U+0000 character. If the input nBytes is odd, convert it into | |
| 143639 | + ** an even number. If the input nBytes is negative, then the input | |
| 143640 | + ** must be terminated by at least one U+0000 character */ | |
| 143374 | 143641 | if( nBytes>=0 ){ |
| 143375 | 143642 | int sz; |
| 143376 | 143643 | const char *z = (const char*)zSql; |
| 143377 | 143644 | for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){} |
| 143378 | 143645 | nBytes = sz; |
| 143646 | + }else{ | |
| 143647 | + int sz; | |
| 143648 | + const char *z = (const char*)zSql; | |
| 143649 | + for(sz=0; z[sz]!=0 || z[sz+1]!=0; sz += 2){} | |
| 143650 | + nBytes = sz; | |
| 143379 | 143651 | } |
| 143652 | + | |
| 143380 | 143653 | sqlite3_mutex_enter(db->mutex); |
| 143381 | 143654 | zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); |
| 143382 | 143655 | if( zSql8 ){ |
| 143383 | 143656 | rc = sqlite3LockAndPrepare(db, zSql8, -1, prepFlags, 0, ppStmt, &zTail8); |
| 143384 | 143657 | } |
| @@ -143388,11 +143661,11 @@ | ||
| 143388 | 143661 | ** equivalent pointer into the UTF-16 string by counting the unicode |
| 143389 | 143662 | ** characters between zSql8 and zTail8, and then returning a pointer |
| 143390 | 143663 | ** the same number of characters into the UTF-16 string. |
| 143391 | 143664 | */ |
| 143392 | 143665 | int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); |
| 143393 | - *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed); | |
| 143666 | + *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, nBytes, chars_parsed); | |
| 143394 | 143667 | } |
| 143395 | 143668 | sqlite3DbFree(db, zSql8); |
| 143396 | 143669 | rc = sqlite3ApiExit(db, rc); |
| 143397 | 143670 | sqlite3_mutex_leave(db->mutex); |
| 143398 | 143671 | return rc; |
| @@ -147361,36 +147634,36 @@ | ||
| 147361 | 147634 | return pExpr; |
| 147362 | 147635 | } |
| 147363 | 147636 | if( pSubst->isOuterJoin ){ |
| 147364 | 147637 | ExprSetProperty(pNew, EP_CanBeNull); |
| 147365 | 147638 | } |
| 147639 | + if( pNew->op==TK_TRUEFALSE ){ | |
| 147640 | + pNew->u.iValue = sqlite3ExprTruthValue(pNew); | |
| 147641 | + pNew->op = TK_INTEGER; | |
| 147642 | + ExprSetProperty(pNew, EP_IntValue); | |
| 147643 | + } | |
| 147644 | + | |
| 147645 | + /* Ensure that the expression now has an implicit collation sequence, | |
| 147646 | + ** just as it did when it was a column of a view or sub-query. */ | |
| 147647 | + { | |
| 147648 | + CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew); | |
| 147649 | + CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, | |
| 147650 | + pSubst->pCList->a[iColumn].pExpr | |
| 147651 | + ); | |
| 147652 | + if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){ | |
| 147653 | + pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew, | |
| 147654 | + (pColl ? pColl->zName : "BINARY") | |
| 147655 | + ); | |
| 147656 | + } | |
| 147657 | + } | |
| 147658 | + ExprClearProperty(pNew, EP_Collate); | |
| 147366 | 147659 | if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ |
| 147367 | 147660 | sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, |
| 147368 | 147661 | pExpr->flags & (EP_OuterON|EP_InnerON)); |
| 147369 | 147662 | } |
| 147370 | 147663 | sqlite3ExprDelete(db, pExpr); |
| 147371 | 147664 | pExpr = pNew; |
| 147372 | - if( pExpr->op==TK_TRUEFALSE ){ | |
| 147373 | - pExpr->u.iValue = sqlite3ExprTruthValue(pExpr); | |
| 147374 | - pExpr->op = TK_INTEGER; | |
| 147375 | - ExprSetProperty(pExpr, EP_IntValue); | |
| 147376 | - } | |
| 147377 | - | |
| 147378 | - /* Ensure that the expression now has an implicit collation sequence, | |
| 147379 | - ** just as it did when it was a column of a view or sub-query. */ | |
| 147380 | - { | |
| 147381 | - CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr); | |
| 147382 | - CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, | |
| 147383 | - pSubst->pCList->a[iColumn].pExpr | |
| 147384 | - ); | |
| 147385 | - if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){ | |
| 147386 | - pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, | |
| 147387 | - (pColl ? pColl->zName : "BINARY") | |
| 147388 | - ); | |
| 147389 | - } | |
| 147390 | - } | |
| 147391 | - ExprClearProperty(pExpr, EP_Collate); | |
| 147392 | 147665 | } |
| 147393 | 147666 | } |
| 147394 | 147667 | }else{ |
| 147395 | 147668 | if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){ |
| 147396 | 147669 | pExpr->iTable = pSubst->iNewTable; |
| @@ -148123,20 +148396,20 @@ | ||
| 148123 | 148396 | } |
| 148124 | 148397 | |
| 148125 | 148398 | /* Transfer the FROM clause terms from the subquery into the |
| 148126 | 148399 | ** outer query. |
| 148127 | 148400 | */ |
| 148401 | + iNewParent = pSubSrc->a[0].iCursor; | |
| 148128 | 148402 | for(i=0; i<nSubSrc; i++){ |
| 148129 | 148403 | SrcItem *pItem = &pSrc->a[i+iFrom]; |
| 148130 | 148404 | assert( pItem->fg.isTabFunc==0 ); |
| 148131 | 148405 | assert( pItem->fg.isSubquery |
| 148132 | 148406 | || pItem->fg.fixedSchema |
| 148133 | 148407 | || pItem->u4.zDatabase==0 ); |
| 148134 | 148408 | if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); |
| 148135 | 148409 | *pItem = pSubSrc->a[i]; |
| 148136 | 148410 | pItem->fg.jointype |= ltorj; |
| 148137 | - iNewParent = pSubSrc->a[i].iCursor; | |
| 148138 | 148411 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 148139 | 148412 | } |
| 148140 | 148413 | pSrc->a[iFrom].fg.jointype &= JT_LTORJ; |
| 148141 | 148414 | pSrc->a[iFrom].fg.jointype |= jointype | ltorj; |
| 148142 | 148415 | |
| @@ -148172,10 +148445,11 @@ | ||
| 148172 | 148445 | pSub->pOrderBy = 0; |
| 148173 | 148446 | } |
| 148174 | 148447 | pWhere = pSub->pWhere; |
| 148175 | 148448 | pSub->pWhere = 0; |
| 148176 | 148449 | if( isOuterJoin>0 ){ |
| 148450 | + assert( pSubSrc->nSrc==1 ); | |
| 148177 | 148451 | sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); |
| 148178 | 148452 | } |
| 148179 | 148453 | if( pWhere ){ |
| 148180 | 148454 | if( pParent->pWhere ){ |
| 148181 | 148455 | pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere); |
| @@ -151271,11 +151545,11 @@ | ||
| 151271 | 151545 | sqlite3TreeViewSelect(0, p, 0); |
| 151272 | 151546 | } |
| 151273 | 151547 | #endif |
| 151274 | 151548 | assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); |
| 151275 | 151549 | }else{ |
| 151276 | - TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); | |
| 151550 | + TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n")); | |
| 151277 | 151551 | } |
| 151278 | 151552 | |
| 151279 | 151553 | /* Convert unused result columns of the subquery into simple NULL |
| 151280 | 151554 | ** expressions, to avoid unneeded searching and computation. |
| 151281 | 151555 | ** tag-select-0440 |
| @@ -156980,10 +157254,11 @@ | ||
| 156980 | 157254 | assert( sParse.zErrMsg==0 ); |
| 156981 | 157255 | if( !pTab->aCol ){ |
| 156982 | 157256 | Table *pNew = sParse.pNewTable; |
| 156983 | 157257 | Index *pIdx; |
| 156984 | 157258 | pTab->aCol = pNew->aCol; |
| 157259 | + assert( IsOrdinaryTable(pNew) ); | |
| 156985 | 157260 | sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); |
| 156986 | 157261 | pTab->nNVCol = pTab->nCol = pNew->nCol; |
| 156987 | 157262 | pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); |
| 156988 | 157263 | pNew->nCol = 0; |
| 156989 | 157264 | pNew->aCol = 0; |
| @@ -158044,13 +158319,21 @@ | ||
| 158044 | 158319 | SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( |
| 158045 | 158320 | const Parse *pParse, /* Parse context */ |
| 158046 | 158321 | const WhereInfo *pWInfo, /* WHERE clause */ |
| 158047 | 158322 | const WhereLevel *pLevel /* Bloom filter on this level */ |
| 158048 | 158323 | ); |
| 158324 | +SQLITE_PRIVATE void sqlite3WhereAddExplainText( | |
| 158325 | + Parse *pParse, /* Parse context */ | |
| 158326 | + int addr, | |
| 158327 | + SrcList *pTabList, /* Table list this loop refers to */ | |
| 158328 | + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ | |
| 158329 | + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ | |
| 158330 | +); | |
| 158049 | 158331 | #else |
| 158050 | 158332 | # define sqlite3WhereExplainOneScan(u,v,w,x) 0 |
| 158051 | 158333 | # define sqlite3WhereExplainBloomFilter(u,v,w) 0 |
| 158334 | +# define sqlite3WhereAddExplainText(u,v,w,x,y) | |
| 158052 | 158335 | #endif /* SQLITE_OMIT_EXPLAIN */ |
| 158053 | 158336 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 158054 | 158337 | SQLITE_PRIVATE void sqlite3WhereAddScanStatus( |
| 158055 | 158338 | Vdbe *v, /* Vdbe to add scanstatus entry to */ |
| 158056 | 158339 | SrcList *pSrclist, /* FROM clause pLvl reads data from */ |
| @@ -158248,42 +158531,42 @@ | ||
| 158248 | 158531 | } |
| 158249 | 158532 | sqlite3_str_append(pStr, ")", 1); |
| 158250 | 158533 | } |
| 158251 | 158534 | |
| 158252 | 158535 | /* |
| 158253 | -** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN | |
| 158254 | -** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG | |
| 158255 | -** was defined at compile-time. If it is not a no-op, a single OP_Explain | |
| 158256 | -** opcode is added to the output to describe the table scan strategy in pLevel. | |
| 158257 | -** | |
| 158258 | -** If an OP_Explain opcode is added to the VM, its address is returned. | |
| 158259 | -** Otherwise, if no OP_Explain is coded, zero is returned. | |
| 158536 | +** This function sets the P4 value of an existing OP_Explain opcode to | |
| 158537 | +** text describing the loop in pLevel. If the OP_Explain opcode already has | |
| 158538 | +** a P4 value, it is freed before it is overwritten. | |
| 158260 | 158539 | */ |
| 158261 | -SQLITE_PRIVATE int sqlite3WhereExplainOneScan( | |
| 158540 | +SQLITE_PRIVATE void sqlite3WhereAddExplainText( | |
| 158262 | 158541 | Parse *pParse, /* Parse context */ |
| 158542 | + int addr, /* Address of OP_Explain opcode */ | |
| 158263 | 158543 | SrcList *pTabList, /* Table list this loop refers to */ |
| 158264 | 158544 | WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ |
| 158265 | 158545 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ |
| 158266 | 158546 | ){ |
| 158267 | - int ret = 0; | |
| 158268 | 158547 | #if !defined(SQLITE_DEBUG) |
| 158269 | 158548 | if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) |
| 158270 | 158549 | #endif |
| 158271 | 158550 | { |
| 158551 | + VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr); | |
| 158552 | + | |
| 158272 | 158553 | SrcItem *pItem = &pTabList->a[pLevel->iFrom]; |
| 158273 | - Vdbe *v = pParse->pVdbe; /* VM being constructed */ | |
| 158274 | 158554 | sqlite3 *db = pParse->db; /* Database handle */ |
| 158275 | 158555 | int isSearch; /* True for a SEARCH. False for SCAN. */ |
| 158276 | 158556 | WhereLoop *pLoop; /* The controlling WhereLoop object */ |
| 158277 | 158557 | u32 flags; /* Flags that describe this loop */ |
| 158558 | +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) | |
| 158278 | 158559 | char *zMsg; /* Text to add to EQP output */ |
| 158560 | +#endif | |
| 158279 | 158561 | StrAccum str; /* EQP output string */ |
| 158280 | 158562 | char zBuf[100]; /* Initial space for EQP output string */ |
| 158281 | 158563 | |
| 158564 | + if( db->mallocFailed ) return; | |
| 158565 | + | |
| 158282 | 158566 | pLoop = pLevel->pWLoop; |
| 158283 | 158567 | flags = pLoop->wsFlags; |
| 158284 | - if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0; | |
| 158285 | 158568 | |
| 158286 | 158569 | isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 |
| 158287 | 158570 | || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) |
| 158288 | 158571 | || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); |
| 158289 | 158572 | |
| @@ -158303,11 +158586,11 @@ | ||
| 158303 | 158586 | } |
| 158304 | 158587 | }else if( flags & WHERE_PARTIALIDX ){ |
| 158305 | 158588 | zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; |
| 158306 | 158589 | }else if( flags & WHERE_AUTO_INDEX ){ |
| 158307 | 158590 | zFmt = "AUTOMATIC COVERING INDEX"; |
| 158308 | - }else if( flags & WHERE_IDX_ONLY ){ | |
| 158591 | + }else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ | |
| 158309 | 158592 | zFmt = "COVERING INDEX %s"; |
| 158310 | 158593 | }else{ |
| 158311 | 158594 | zFmt = "INDEX %s"; |
| 158312 | 158595 | } |
| 158313 | 158596 | if( zFmt ){ |
| @@ -158355,15 +158638,54 @@ | ||
| 158355 | 158638 | sqlite3LogEstToInt(pLoop->nOut)); |
| 158356 | 158639 | }else{ |
| 158357 | 158640 | sqlite3_str_append(&str, " (~1 row)", 9); |
| 158358 | 158641 | } |
| 158359 | 158642 | #endif |
| 158643 | +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) | |
| 158360 | 158644 | zMsg = sqlite3StrAccumFinish(&str); |
| 158361 | 158645 | sqlite3ExplainBreakpoint("",zMsg); |
| 158362 | - ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), | |
| 158363 | - pParse->addrExplain, pLoop->rRun, | |
| 158364 | - zMsg, P4_DYNAMIC); | |
| 158646 | +#endif | |
| 158647 | + | |
| 158648 | + assert( pOp->opcode==OP_Explain ); | |
| 158649 | + assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 ); | |
| 158650 | + sqlite3DbFree(db, pOp->p4.z); | |
| 158651 | + pOp->p4type = P4_DYNAMIC; | |
| 158652 | + pOp->p4.z = sqlite3StrAccumFinish(&str); | |
| 158653 | + } | |
| 158654 | +} | |
| 158655 | + | |
| 158656 | + | |
| 158657 | +/* | |
| 158658 | +** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN | |
| 158659 | +** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG | |
| 158660 | +** was defined at compile-time. If it is not a no-op, a single OP_Explain | |
| 158661 | +** opcode is added to the output to describe the table scan strategy in pLevel. | |
| 158662 | +** | |
| 158663 | +** If an OP_Explain opcode is added to the VM, its address is returned. | |
| 158664 | +** Otherwise, if no OP_Explain is coded, zero is returned. | |
| 158665 | +*/ | |
| 158666 | +SQLITE_PRIVATE int sqlite3WhereExplainOneScan( | |
| 158667 | + Parse *pParse, /* Parse context */ | |
| 158668 | + SrcList *pTabList, /* Table list this loop refers to */ | |
| 158669 | + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ | |
| 158670 | + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ | |
| 158671 | +){ | |
| 158672 | + int ret = 0; | |
| 158673 | +#if !defined(SQLITE_DEBUG) | |
| 158674 | + if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) | |
| 158675 | +#endif | |
| 158676 | + { | |
| 158677 | + if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0 | |
| 158678 | + && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 | |
| 158679 | + ){ | |
| 158680 | + Vdbe *v = pParse->pVdbe; | |
| 158681 | + int addr = sqlite3VdbeCurrentAddr(v); | |
| 158682 | + ret = sqlite3VdbeAddOp3( | |
| 158683 | + v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun | |
| 158684 | + ); | |
| 158685 | + sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags); | |
| 158686 | + } | |
| 158365 | 158687 | } |
| 158366 | 158688 | return ret; |
| 158367 | 158689 | } |
| 158368 | 158690 | |
| 158369 | 158691 | /* |
| @@ -158458,13 +158780,14 @@ | ||
| 158458 | 158780 | if( wsFlags & WHERE_INDEXED ){ |
| 158459 | 158781 | sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); |
| 158460 | 158782 | } |
| 158461 | 158783 | }else{ |
| 158462 | 158784 | int addr; |
| 158785 | + VdbeOp *pOp; | |
| 158463 | 158786 | assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); |
| 158464 | 158787 | addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; |
| 158465 | - VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1); | |
| 158788 | + pOp = sqlite3VdbeGetOp(v, addr-1); | |
| 158466 | 158789 | assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); |
| 158467 | 158790 | assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); |
| 158468 | 158791 | sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); |
| 158469 | 158792 | } |
| 158470 | 158793 | } |
| @@ -158713,10 +159036,11 @@ | ||
| 158713 | 159036 | if( pOrigLhs ){ |
| 158714 | 159037 | sqlite3ExprListDelete(db, pOrigLhs); |
| 158715 | 159038 | pNew->pLeft->x.pList = pLhs; |
| 158716 | 159039 | } |
| 158717 | 159040 | pSelect->pEList = pRhs; |
| 159041 | + pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */ | |
| 158718 | 159042 | if( pLhs && pLhs->nExpr==1 ){ |
| 158719 | 159043 | /* Take care here not to generate a TK_VECTOR containing only a |
| 158720 | 159044 | ** single value. Since the parser never creates such a vector, some |
| 158721 | 159045 | ** of the subroutines do not handle this case. */ |
| 158722 | 159046 | Expr *p = pLhs->a[0].pExpr; |
| @@ -161260,24 +161584,29 @@ | ||
| 161260 | 161584 | }else if( op==TK_STRING ){ |
| 161261 | 161585 | assert( !ExprHasProperty(pRight, EP_IntValue) ); |
| 161262 | 161586 | z = (u8*)pRight->u.zToken; |
| 161263 | 161587 | } |
| 161264 | 161588 | if( z ){ |
| 161265 | - | |
| 161266 | - /* Count the number of prefix characters prior to the first wildcard. | |
| 161267 | - ** If the underlying database has a UTF16LE encoding, then only consider | |
| 161268 | - ** ASCII characters. Note that the encoding of z[] is UTF8 - we are | |
| 161269 | - ** dealing with only UTF8 here in this code, but the database engine | |
| 161270 | - ** itself might be processing content using a different encoding. */ | |
| 161589 | + /* Count the number of prefix bytes prior to the first wildcard. | |
| 161590 | + ** or U+fffd character. If the underlying database has a UTF16LE | |
| 161591 | + ** encoding, then only consider ASCII characters. Note that the | |
| 161592 | + ** encoding of z[] is UTF8 - we are dealing with only UTF8 here in | |
| 161593 | + ** this code, but the database engine itself might be processing | |
| 161594 | + ** content using a different encoding. */ | |
| 161271 | 161595 | cnt = 0; |
| 161272 | 161596 | while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ |
| 161273 | 161597 | cnt++; |
| 161274 | - if( c==wc[3] && z[cnt]!=0 ){ | |
| 161598 | + if( c==wc[3] && z[cnt]>0 && z[cnt]<0x80 ){ | |
| 161275 | 161599 | cnt++; |
| 161276 | - }else if( c>=0x80 && ENC(db)==SQLITE_UTF16LE ){ | |
| 161277 | - cnt--; | |
| 161278 | - break; | |
| 161600 | + }else if( c>=0x80 ){ | |
| 161601 | + const u8 *z2 = z+cnt-1; | |
| 161602 | + if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){ | |
| 161603 | + cnt--; | |
| 161604 | + break; | |
| 161605 | + }else{ | |
| 161606 | + cnt = (int)(z2-z); | |
| 161607 | + } | |
| 161279 | 161608 | } |
| 161280 | 161609 | } |
| 161281 | 161610 | |
| 161282 | 161611 | /* The optimization is possible only if (1) the pattern does not begin |
| 161283 | 161612 | ** with a wildcard and if (2) the non-wildcard prefix does not end with |
| @@ -161285,11 +161614,11 @@ | ||
| 161285 | 161614 | ** a single escape character. The second condition is necessary so |
| 161286 | 161615 | ** that we can increment the prefix key to find an upper bound for the |
| 161287 | 161616 | ** range search. The third is because the caller assumes that the pattern |
| 161288 | 161617 | ** consists of at least one character after all escapes have been |
| 161289 | 161618 | ** removed. */ |
| 161290 | - if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && 255!=(u8)z[cnt-1] ){ | |
| 161619 | + if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && ALWAYS(255!=(u8)z[cnt-1]) ){ | |
| 161291 | 161620 | Expr *pPrefix; |
| 161292 | 161621 | |
| 161293 | 161622 | /* A "complete" match if the pattern ends with "*" or "%" */ |
| 161294 | 161623 | *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; |
| 161295 | 161624 | |
| @@ -163780,11 +164109,11 @@ | ||
| 163780 | 164109 | || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 163781 | 164110 | ){ |
| 163782 | 164111 | return 0; |
| 163783 | 164112 | } |
| 163784 | 164113 | if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 |
| 163785 | - && ExprHasProperty(pTerm->pExpr, EP_InnerON) | |
| 164114 | + && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON)) | |
| 163786 | 164115 | ){ |
| 163787 | 164116 | return 0; |
| 163788 | 164117 | } |
| 163789 | 164118 | return 1; |
| 163790 | 164119 | } |
| @@ -164577,13 +164906,15 @@ | ||
| 164577 | 164906 | ** Whether or not an error is returned, it is the responsibility of the |
| 164578 | 164907 | ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates |
| 164579 | 164908 | ** that this is required. |
| 164580 | 164909 | */ |
| 164581 | 164910 | static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ |
| 164582 | - sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; | |
| 164583 | 164911 | int rc; |
| 164912 | + sqlite3_vtab *pVtab; | |
| 164584 | 164913 | |
| 164914 | + assert( IsVirtual(pTab) ); | |
| 164915 | + pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; | |
| 164585 | 164916 | whereTraceIndexInfoInputs(p, pTab); |
| 164586 | 164917 | pParse->db->nSchemaLock++; |
| 164587 | 164918 | rc = pVtab->pModule->xBestIndex(pVtab, p); |
| 164588 | 164919 | pParse->db->nSchemaLock--; |
| 164589 | 164920 | whereTraceIndexInfoOutputs(p, pTab); |
| @@ -165271,11 +165602,11 @@ | ||
| 165271 | 165602 | return rc; |
| 165272 | 165603 | } |
| 165273 | 165604 | #endif /* SQLITE_ENABLE_STAT4 */ |
| 165274 | 165605 | |
| 165275 | 165606 | |
| 165276 | -#ifdef WHERETRACE_ENABLED | |
| 165607 | +#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG) | |
| 165277 | 165608 | /* |
| 165278 | 165609 | ** Print the content of a WhereTerm object |
| 165279 | 165610 | */ |
| 165280 | 165611 | SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ |
| 165281 | 165612 | if( pTerm==0 ){ |
| @@ -165314,10 +165645,13 @@ | ||
| 165314 | 165645 | sqlite3DebugPrintf(" iParent=%d", pTerm->iParent); |
| 165315 | 165646 | } |
| 165316 | 165647 | sqlite3DebugPrintf("\n"); |
| 165317 | 165648 | sqlite3TreeViewExpr(0, pTerm->pExpr, 0); |
| 165318 | 165649 | } |
| 165650 | +} | |
| 165651 | +SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){ | |
| 165652 | + sqlite3WhereTermPrint(pTerm, 0); | |
| 165319 | 165653 | } |
| 165320 | 165654 | #endif |
| 165321 | 165655 | |
| 165322 | 165656 | #ifdef WHERETRACE_ENABLED |
| 165323 | 165657 | /* |
| @@ -165522,11 +165856,11 @@ | ||
| 165522 | 165856 | ** Return TRUE if X is a proper subset of Y but is of equal or less cost. |
| 165523 | 165857 | ** In other words, return true if all constraints of X are also part of Y |
| 165524 | 165858 | ** and Y has additional constraints that might speed the search that X lacks |
| 165525 | 165859 | ** but the cost of running X is not more than the cost of running Y. |
| 165526 | 165860 | ** |
| 165527 | -** In other words, return true if the cost relationwship between X and Y | |
| 165861 | +** In other words, return true if the cost relationship between X and Y | |
| 165528 | 165862 | ** is inverted and needs to be adjusted. |
| 165529 | 165863 | ** |
| 165530 | 165864 | ** Case 1: |
| 165531 | 165865 | ** |
| 165532 | 165866 | ** (1a) X and Y use the same index. |
| @@ -166500,11 +166834,10 @@ | ||
| 166500 | 166834 | pParse = pWC->pWInfo->pParse; |
| 166501 | 166835 | while( pWhere->op==TK_AND ){ |
| 166502 | 166836 | if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; |
| 166503 | 166837 | pWhere = pWhere->pRight; |
| 166504 | 166838 | } |
| 166505 | - if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; | |
| 166506 | 166839 | for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 166507 | 166840 | Expr *pExpr; |
| 166508 | 166841 | pExpr = pTerm->pExpr; |
| 166509 | 166842 | if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) |
| 166510 | 166843 | && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) |
| @@ -169140,10 +169473,11 @@ | ||
| 169140 | 169473 | hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; |
| 169141 | 169474 | for(i=pWInfo->nLevel-1; i>=1; i--){ |
| 169142 | 169475 | WhereTerm *pTerm, *pEnd; |
| 169143 | 169476 | SrcItem *pItem; |
| 169144 | 169477 | WhereLoop *pLoop; |
| 169478 | + Bitmask m1; | |
| 169145 | 169479 | pLoop = pWInfo->a[i].pWLoop; |
| 169146 | 169480 | pItem = &pWInfo->pTabList->a[pLoop->iTab]; |
| 169147 | 169481 | if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; |
| 169148 | 169482 | if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 |
| 169149 | 169483 | && (pLoop->wsFlags & WHERE_ONEROW)==0 |
| @@ -169160,17 +169494,20 @@ | ||
| 169160 | 169494 | break; |
| 169161 | 169495 | } |
| 169162 | 169496 | } |
| 169163 | 169497 | if( hasRightJoin |
| 169164 | 169498 | && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 169165 | - && pTerm->pExpr->w.iJoin==pItem->iCursor | |
| 169499 | + && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor) | |
| 169166 | 169500 | ){ |
| 169167 | 169501 | break; /* restriction (5) */ |
| 169168 | 169502 | } |
| 169169 | 169503 | } |
| 169170 | 169504 | if( pTerm<pEnd ) continue; |
| 169171 | - WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId)); | |
| 169505 | + WHERETRACE(0xffffffff,("-> omit unused FROM-clause term %c\n",pLoop->cId)); | |
| 169506 | + m1 = MASKBIT(i)-1; | |
| 169507 | + testcase( ((pWInfo->revMask>>1) & ~m1)!=0 ); | |
| 169508 | + pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); | |
| 169172 | 169509 | notReady &= ~pLoop->maskSelf; |
| 169173 | 169510 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| 169174 | 169511 | if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ |
| 169175 | 169512 | pTerm->wtFlags |= TERM_CODED; |
| 169176 | 169513 | } |
| @@ -169237,62 +169574,10 @@ | ||
| 169237 | 169574 | nSearch += pLoop->nOut; |
| 169238 | 169575 | if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta; |
| 169239 | 169576 | } |
| 169240 | 169577 | } |
| 169241 | 169578 | |
| 169242 | -/* | |
| 169243 | -** Expression Node callback for sqlite3ExprCanReturnSubtype(). | |
| 169244 | -** | |
| 169245 | -** Only a function call is able to return a subtype. So if the node | |
| 169246 | -** is not a function call, return WRC_Prune immediately. | |
| 169247 | -** | |
| 169248 | -** A function call is able to return a subtype if it has the | |
| 169249 | -** SQLITE_RESULT_SUBTYPE property. | |
| 169250 | -** | |
| 169251 | -** Assume that every function is able to pass-through a subtype from | |
| 169252 | -** one of its argument (using sqlite3_result_value()). Most functions | |
| 169253 | -** are not this way, but we don't have a mechanism to distinguish those | |
| 169254 | -** that are from those that are not, so assume they all work this way. | |
| 169255 | -** That means that if one of its arguments is another function and that | |
| 169256 | -** other function is able to return a subtype, then this function is | |
| 169257 | -** able to return a subtype. | |
| 169258 | -*/ | |
| 169259 | -static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ | |
| 169260 | - int n; | |
| 169261 | - FuncDef *pDef; | |
| 169262 | - sqlite3 *db; | |
| 169263 | - if( pExpr->op!=TK_FUNCTION ){ | |
| 169264 | - return WRC_Prune; | |
| 169265 | - } | |
| 169266 | - assert( ExprUseXList(pExpr) ); | |
| 169267 | - db = pWalker->pParse->db; | |
| 169268 | - n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; | |
| 169269 | - pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); | |
| 169270 | - if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ | |
| 169271 | - pWalker->eCode = 1; | |
| 169272 | - return WRC_Prune; | |
| 169273 | - } | |
| 169274 | - return WRC_Continue; | |
| 169275 | -} | |
| 169276 | - | |
| 169277 | -/* | |
| 169278 | -** Return TRUE if expression pExpr is able to return a subtype. | |
| 169279 | -** | |
| 169280 | -** A TRUE return does not guarantee that a subtype will be returned. | |
| 169281 | -** It only indicates that a subtype return is possible. False positives | |
| 169282 | -** are acceptable as they only disable an optimization. False negatives, | |
| 169283 | -** on the other hand, can lead to incorrect answers. | |
| 169284 | -*/ | |
| 169285 | -static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ | |
| 169286 | - Walker w; | |
| 169287 | - memset(&w, 0, sizeof(w)); | |
| 169288 | - w.pParse = pParse; | |
| 169289 | - w.xExprCallback = exprNodeCanReturnSubtype; | |
| 169290 | - sqlite3WalkExpr(&w, pExpr); | |
| 169291 | - return w.eCode; | |
| 169292 | -} | |
| 169293 | - | |
| 169294 | 169579 | /* |
| 169295 | 169580 | ** The index pIdx is used by a query and contains one or more expressions. |
| 169296 | 169581 | ** In other words pIdx is an index on an expression. iIdxCur is the cursor |
| 169297 | 169582 | ** number for the index and iDataCur is the cursor number for the corresponding |
| 169298 | 169583 | ** table. |
| @@ -169322,16 +169607,10 @@ | ||
| 169322 | 169607 | pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); |
| 169323 | 169608 | }else{ |
| 169324 | 169609 | continue; |
| 169325 | 169610 | } |
| 169326 | 169611 | if( sqlite3ExprIsConstant(0,pExpr) ) continue; |
| 169327 | - if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ | |
| 169328 | - /* Functions that might set a subtype should not be replaced by the | |
| 169329 | - ** value taken from an expression index since the index omits the | |
| 169330 | - ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ | |
| 169331 | - continue; | |
| 169332 | - } | |
| 169333 | 169612 | p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); |
| 169334 | 169613 | if( p==0 ) break; |
| 169335 | 169614 | p->pIENext = pParse->pIdxEpr; |
| 169336 | 169615 | #ifdef WHERETRACE_ENABLED |
| 169337 | 169616 | if( sqlite3WhereTrace & 0x200 ){ |
| @@ -170434,18 +170713,32 @@ | ||
| 170434 | 170713 | x = sqlite3TableColumnToIndex(pIdx, x); |
| 170435 | 170714 | if( x>=0 ){ |
| 170436 | 170715 | pOp->p2 = x; |
| 170437 | 170716 | pOp->p1 = pLevel->iIdxCur; |
| 170438 | 170717 | OpcodeRewriteTrace(db, k, pOp); |
| 170439 | - }else{ | |
| 170440 | - /* Unable to translate the table reference into an index | |
| 170441 | - ** reference. Verify that this is harmless - that the | |
| 170442 | - ** table being referenced really is open. | |
| 170443 | - */ | |
| 170718 | + }else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ | |
| 170444 | 170719 | if( pLoop->wsFlags & WHERE_IDX_ONLY ){ |
| 170720 | + /* An error. pLoop is supposed to be a covering index loop, | |
| 170721 | + ** and yet the VM code refers to a column of the table that | |
| 170722 | + ** is not part of the index. */ | |
| 170445 | 170723 | sqlite3ErrorMsg(pParse, "internal query planner error"); |
| 170446 | 170724 | pParse->rc = SQLITE_INTERNAL; |
| 170725 | + }else{ | |
| 170726 | + /* The WHERE_EXPRIDX flag is set by the planner when it is likely | |
| 170727 | + ** that pLoop is a covering index loop, but it is not possible | |
| 170728 | + ** to be 100% sure. In this case, any OP_Explain opcode | |
| 170729 | + ** corresponding to this loop describes the index as a "COVERING | |
| 170730 | + ** INDEX". But, pOp proves that pLoop is not actually a covering | |
| 170731 | + ** index loop. So clear the WHERE_EXPRIDX flag and rewrite the | |
| 170732 | + ** text that accompanies the OP_Explain opcode, if any. */ | |
| 170733 | + pLoop->wsFlags &= ~WHERE_EXPRIDX; | |
| 170734 | + sqlite3WhereAddExplainText(pParse, | |
| 170735 | + pLevel->addrBody-1, | |
| 170736 | + pTabList, | |
| 170737 | + pLevel, | |
| 170738 | + pWInfo->wctrlFlags | |
| 170739 | + ); | |
| 170447 | 170740 | } |
| 170448 | 170741 | } |
| 170449 | 170742 | }else if( pOp->opcode==OP_Rowid ){ |
| 170450 | 170743 | pOp->p1 = pLevel->iIdxCur; |
| 170451 | 170744 | pOp->opcode = OP_IdxRowid; |
| @@ -172149,10 +172442,11 @@ | ||
| 172149 | 172442 | for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ |
| 172150 | 172443 | FuncDef *pFunc = pWin->pWFunc; |
| 172151 | 172444 | int regArg; |
| 172152 | 172445 | int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); |
| 172153 | 172446 | int i; |
| 172447 | + int addrIf = 0; | |
| 172154 | 172448 | |
| 172155 | 172449 | assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED ); |
| 172156 | 172450 | |
| 172157 | 172451 | /* All OVER clauses in the same window function aggregate step must |
| 172158 | 172452 | ** be the same. */ |
| @@ -172164,10 +172458,22 @@ | ||
| 172164 | 172458 | }else{ |
| 172165 | 172459 | sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i); |
| 172166 | 172460 | } |
| 172167 | 172461 | } |
| 172168 | 172462 | regArg = reg; |
| 172463 | + | |
| 172464 | + if( pWin->pFilter ){ | |
| 172465 | + int regTmp; | |
| 172466 | + assert( ExprUseXList(pWin->pOwner) ); | |
| 172467 | + assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); | |
| 172468 | + assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); | |
| 172469 | + regTmp = sqlite3GetTempReg(pParse); | |
| 172470 | + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); | |
| 172471 | + addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); | |
| 172472 | + VdbeCoverage(v); | |
| 172473 | + sqlite3ReleaseTempReg(pParse, regTmp); | |
| 172474 | + } | |
| 172169 | 172475 | |
| 172170 | 172476 | if( pMWin->regStartRowid==0 |
| 172171 | 172477 | && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) |
| 172172 | 172478 | && (pWin->eStart!=TK_UNBOUNDED) |
| 172173 | 172479 | ){ |
| @@ -172184,29 +172490,17 @@ | ||
| 172184 | 172490 | sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp); |
| 172185 | 172491 | sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); |
| 172186 | 172492 | } |
| 172187 | 172493 | sqlite3VdbeJumpHere(v, addrIsNull); |
| 172188 | 172494 | }else if( pWin->regApp ){ |
| 172495 | + assert( pWin->pFilter==0 ); | |
| 172189 | 172496 | assert( pFunc->zName==nth_valueName |
| 172190 | 172497 | || pFunc->zName==first_valueName |
| 172191 | 172498 | ); |
| 172192 | 172499 | assert( bInverse==0 || bInverse==1 ); |
| 172193 | 172500 | sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); |
| 172194 | 172501 | }else if( pFunc->xSFunc!=noopStepFunc ){ |
| 172195 | - int addrIf = 0; | |
| 172196 | - if( pWin->pFilter ){ | |
| 172197 | - int regTmp; | |
| 172198 | - assert( ExprUseXList(pWin->pOwner) ); | |
| 172199 | - assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); | |
| 172200 | - assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); | |
| 172201 | - regTmp = sqlite3GetTempReg(pParse); | |
| 172202 | - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); | |
| 172203 | - addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); | |
| 172204 | - VdbeCoverage(v); | |
| 172205 | - sqlite3ReleaseTempReg(pParse, regTmp); | |
| 172206 | - } | |
| 172207 | - | |
| 172208 | 172502 | if( pWin->bExprArgs ){ |
| 172209 | 172503 | int iOp = sqlite3VdbeCurrentAddr(v); |
| 172210 | 172504 | int iEnd; |
| 172211 | 172505 | |
| 172212 | 172506 | assert( ExprUseXList(pWin->pOwner) ); |
| @@ -172233,12 +172527,13 @@ | ||
| 172233 | 172527 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172234 | 172528 | sqlite3VdbeChangeP5(v, (u8)nArg); |
| 172235 | 172529 | if( pWin->bExprArgs ){ |
| 172236 | 172530 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172237 | 172531 | } |
| 172238 | - if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); | |
| 172239 | 172532 | } |
| 172533 | + | |
| 172534 | + if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); | |
| 172240 | 172535 | } |
| 172241 | 172536 | } |
| 172242 | 172537 | |
| 172243 | 172538 | /* |
| 172244 | 172539 | ** Values that may be passed as the second argument to windowCodeOp(). |
| @@ -173660,10 +173955,17 @@ | ||
| 173660 | 173955 | ** Then the "b" IdList records the list "a,b,c". |
| 173661 | 173956 | */ |
| 173662 | 173957 | struct TrigEvent { int a; IdList * b; }; |
| 173663 | 173958 | |
| 173664 | 173959 | struct FrameBound { int eType; Expr *pExpr; }; |
| 173960 | + | |
| 173961 | +/* | |
| 173962 | +** Generate a syntax error | |
| 173963 | +*/ | |
| 173964 | +static void parserSyntaxError(Parse *pParse, Token *p){ | |
| 173965 | + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p); | |
| 173966 | +} | |
| 173665 | 173967 | |
| 173666 | 173968 | /* |
| 173667 | 173969 | ** Disable lookaside memory allocation for objects that might be |
| 173668 | 173970 | ** shared across database connections. |
| 173669 | 173971 | */ |
| @@ -177553,11 +177855,15 @@ | ||
| 177553 | 177855 | } |
| 177554 | 177856 | break; |
| 177555 | 177857 | case 84: /* cmd ::= select */ |
| 177556 | 177858 | { |
| 177557 | 177859 | SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; |
| 177558 | - sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); | |
| 177860 | + if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0 | |
| 177861 | + || sqlite3ReadSchema(pParse)==SQLITE_OK | |
| 177862 | + ){ | |
| 177863 | + sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); | |
| 177864 | + } | |
| 177559 | 177865 | sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); |
| 177560 | 177866 | } |
| 177561 | 177867 | break; |
| 177562 | 177868 | case 85: /* select ::= WITH wqlist selectnowith */ |
| 177563 | 177869 | {yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} |
| @@ -178024,11 +178330,11 @@ | ||
| 178024 | 178330 | ** that look like this: #1 #2 ... These terms refer to registers |
| 178025 | 178331 | ** in the virtual machine. #N is the N-th register. */ |
| 178026 | 178332 | Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ |
| 178027 | 178333 | assert( t.n>=2 ); |
| 178028 | 178334 | if( pParse->nested==0 ){ |
| 178029 | - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); | |
| 178335 | + parserSyntaxError(pParse, &t); | |
| 178030 | 178336 | yymsp[0].minor.yy454 = 0; |
| 178031 | 178337 | }else{ |
| 178032 | 178338 | yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); |
| 178033 | 178339 | if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); |
| 178034 | 178340 | } |
| @@ -178872,11 +179178,11 @@ | ||
| 178872 | 179178 | #define TOKEN yyminor |
| 178873 | 179179 | /************ Begin %syntax_error code ****************************************/ |
| 178874 | 179180 | |
| 178875 | 179181 | UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ |
| 178876 | 179182 | if( TOKEN.z[0] ){ |
| 178877 | - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); | |
| 179183 | + parserSyntaxError(pParse, &TOKEN); | |
| 178878 | 179184 | }else{ |
| 178879 | 179185 | sqlite3ErrorMsg(pParse, "incomplete input"); |
| 178880 | 179186 | } |
| 178881 | 179187 | /************ End %syntax_error code ******************************************/ |
| 178882 | 179188 | sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| @@ -180363,11 +180669,13 @@ | ||
| 180363 | 180669 | } |
| 180364 | 180670 | if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ |
| 180365 | 180671 | if( pParse->zErrMsg==0 ){ |
| 180366 | 180672 | pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); |
| 180367 | 180673 | } |
| 180368 | - sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); | |
| 180674 | + if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){ | |
| 180675 | + sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); | |
| 180676 | + } | |
| 180369 | 180677 | nErr++; |
| 180370 | 180678 | } |
| 180371 | 180679 | pParse->zTail = zSql; |
| 180372 | 180680 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 180373 | 180681 | sqlite3_free(pParse->apVtabLock); |
| @@ -181071,36 +181379,10 @@ | ||
| 181071 | 181379 | ** all database files specified with a relative pathname. |
| 181072 | 181380 | ** |
| 181073 | 181381 | ** See also the "PRAGMA data_store_directory" SQL command. |
| 181074 | 181382 | */ |
| 181075 | 181383 | SQLITE_API char *sqlite3_data_directory = 0; |
| 181076 | - | |
| 181077 | -/* | |
| 181078 | -** Determine whether or not high-precision (long double) floating point | |
| 181079 | -** math works correctly on CPU currently running. | |
| 181080 | -*/ | |
| 181081 | -static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){ | |
| 181082 | - if( sizeof(LONGDOUBLE_TYPE)<=8 ){ | |
| 181083 | - /* If the size of "long double" is not more than 8, then | |
| 181084 | - ** high-precision math is not possible. */ | |
| 181085 | - return 0; | |
| 181086 | - }else{ | |
| 181087 | - /* Just because sizeof(long double)>8 does not mean that the underlying | |
| 181088 | - ** hardware actually supports high-precision floating point. For example, | |
| 181089 | - ** clearing the 0x100 bit in the floating-point control word on Intel | |
| 181090 | - ** processors will make long double work like double, even though long | |
| 181091 | - ** double takes up more space. The only way to determine if long double | |
| 181092 | - ** actually works is to run an experiment. */ | |
| 181093 | - LONGDOUBLE_TYPE a, b, c; | |
| 181094 | - rc++; | |
| 181095 | - a = 1.0+rc*0.1; | |
| 181096 | - b = 1.0e+18+rc*25.0; | |
| 181097 | - c = a+b; | |
| 181098 | - return b!=c; | |
| 181099 | - } | |
| 181100 | -} | |
| 181101 | - | |
| 181102 | 181384 | |
| 181103 | 181385 | /* |
| 181104 | 181386 | ** Initialize SQLite. |
| 181105 | 181387 | ** |
| 181106 | 181388 | ** This routine must be called to initialize the memory allocation, |
| @@ -181292,17 +181574,10 @@ | ||
| 181292 | 181574 | if( bRunExtraInit ){ |
| 181293 | 181575 | int SQLITE_EXTRA_INIT(const char*); |
| 181294 | 181576 | rc = SQLITE_EXTRA_INIT(0); |
| 181295 | 181577 | } |
| 181296 | 181578 | #endif |
| 181297 | - | |
| 181298 | - /* Experimentally determine if high-precision floating point is | |
| 181299 | - ** available. */ | |
| 181300 | -#ifndef SQLITE_OMIT_WSD | |
| 181301 | - sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc); | |
| 181302 | -#endif | |
| 181303 | - | |
| 181304 | 181579 | return rc; |
| 181305 | 181580 | } |
| 181306 | 181581 | |
| 181307 | 181582 | /* |
| 181308 | 181583 | ** Undo the effects of sqlite3_initialize(). Must not be called while |
| @@ -182369,14 +182644,10 @@ | ||
| 182369 | 182644 | #endif |
| 182370 | 182645 | |
| 182371 | 182646 | sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ |
| 182372 | 182647 | sqlite3ValueFree(db->pErr); |
| 182373 | 182648 | sqlite3CloseExtensions(db); |
| 182374 | -#if SQLITE_USER_AUTHENTICATION | |
| 182375 | - sqlite3_free(db->auth.zAuthUser); | |
| 182376 | - sqlite3_free(db->auth.zAuthPW); | |
| 182377 | -#endif | |
| 182378 | 182649 | |
| 182379 | 182650 | db->eOpenState = SQLITE_STATE_ERROR; |
| 182380 | 182651 | |
| 182381 | 182652 | /* The temp-database schema is allocated differently from the other schema |
| 182382 | 182653 | ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). |
| @@ -183875,12 +184146,12 @@ | ||
| 183875 | 184146 | } |
| 183876 | 184147 | oldLimit = db->aLimit[limitId]; |
| 183877 | 184148 | if( newLimit>=0 ){ /* IMP: R-52476-28732 */ |
| 183878 | 184149 | if( newLimit>aHardLimit[limitId] ){ |
| 183879 | 184150 | newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ |
| 183880 | - }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ | |
| 183881 | - newLimit = 1; | |
| 184151 | + }else if( newLimit<SQLITE_MIN_LENGTH && limitId==SQLITE_LIMIT_LENGTH ){ | |
| 184152 | + newLimit = SQLITE_MIN_LENGTH; | |
| 183882 | 184153 | } |
| 183883 | 184154 | db->aLimit[limitId] = newLimit; |
| 183884 | 184155 | } |
| 183885 | 184156 | return oldLimit; /* IMP: R-53341-35419 */ |
| 183886 | 184157 | } |
| @@ -184395,10 +184666,11 @@ | ||
| 184395 | 184666 | testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ |
| 184396 | 184667 | testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ |
| 184397 | 184668 | if( ((1<<(flags&7)) & 0x46)==0 ){ |
| 184398 | 184669 | rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ |
| 184399 | 184670 | }else{ |
| 184671 | + if( zFilename==0 ) zFilename = ":memory:"; | |
| 184400 | 184672 | rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); |
| 184401 | 184673 | } |
| 184402 | 184674 | if( rc!=SQLITE_OK ){ |
| 184403 | 184675 | if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); |
| 184404 | 184676 | sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg); |
| @@ -185237,10 +185509,11 @@ | ||
| 185237 | 185509 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185238 | 185510 | sqlite3ShowWindow(0); |
| 185239 | 185511 | sqlite3ShowWinFunc(0); |
| 185240 | 185512 | #endif |
| 185241 | 185513 | sqlite3ShowSelect(0); |
| 185514 | + sqlite3ShowWhereTerm(0); | |
| 185242 | 185515 | } |
| 185243 | 185516 | #endif |
| 185244 | 185517 | break; |
| 185245 | 185518 | } |
| 185246 | 185519 | |
| @@ -185549,28 +185822,10 @@ | ||
| 185549 | 185822 | *pI1 = rLogEst; |
| 185550 | 185823 | *pU64 = sqlite3LogEstToInt(rLogEst); |
| 185551 | 185824 | *pI2 = sqlite3LogEst(*pU64); |
| 185552 | 185825 | break; |
| 185553 | 185826 | } |
| 185554 | - | |
| 185555 | -#if !defined(SQLITE_OMIT_WSD) | |
| 185556 | - /* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X); | |
| 185557 | - ** | |
| 185558 | - ** X<0 Make no changes to the bUseLongDouble. Just report value. | |
| 185559 | - ** X==0 Disable bUseLongDouble | |
| 185560 | - ** X==1 Enable bUseLongDouble | |
| 185561 | - ** X>=2 Set bUseLongDouble to its default value for this platform | |
| 185562 | - */ | |
| 185563 | - case SQLITE_TESTCTRL_USELONGDOUBLE: { | |
| 185564 | - int b = va_arg(ap, int); | |
| 185565 | - if( b>=2 ) b = hasHighPrecisionDouble(b); | |
| 185566 | - if( b>=0 ) sqlite3Config.bUseLongDouble = b>0; | |
| 185567 | - rc = sqlite3Config.bUseLongDouble!=0; | |
| 185568 | - break; | |
| 185569 | - } | |
| 185570 | -#endif | |
| 185571 | - | |
| 185572 | 185827 | |
| 185573 | 185828 | #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) |
| 185574 | 185829 | /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) |
| 185575 | 185830 | ** |
| 185576 | 185831 | ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value |
| @@ -185875,11 +186130,15 @@ | ||
| 185875 | 186130 | if( db->autoCommit==0 ){ |
| 185876 | 186131 | int iDb = sqlite3FindDbName(db, zDb); |
| 185877 | 186132 | if( iDb==0 || iDb>1 ){ |
| 185878 | 186133 | Btree *pBt = db->aDb[iDb].pBt; |
| 185879 | 186134 | if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){ |
| 186135 | + Pager *pPager = sqlite3BtreePager(pBt); | |
| 186136 | + i64 dummy = 0; | |
| 186137 | + sqlite3PagerSnapshotOpen(pPager, (sqlite3_snapshot*)&dummy); | |
| 185880 | 186138 | rc = sqlite3BtreeBeginTrans(pBt, 0, 0); |
| 186139 | + sqlite3PagerSnapshotOpen(pPager, 0); | |
| 185881 | 186140 | if( rc==SQLITE_OK ){ |
| 185882 | 186141 | rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); |
| 185883 | 186142 | } |
| 185884 | 186143 | } |
| 185885 | 186144 | } |
| @@ -189665,14 +189924,19 @@ | ||
| 189665 | 189924 | |
| 189666 | 189925 | assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 ); |
| 189667 | 189926 | if( *p1==POS_COLUMN ){ |
| 189668 | 189927 | p1++; |
| 189669 | 189928 | p1 += fts3GetVarint32(p1, &iCol1); |
| 189929 | + /* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN | |
| 189930 | + ** entry, so this is actually end-of-doclist. */ | |
| 189931 | + if( iCol1==0 ) return 0; | |
| 189670 | 189932 | } |
| 189671 | 189933 | if( *p2==POS_COLUMN ){ |
| 189672 | 189934 | p2++; |
| 189673 | 189935 | p2 += fts3GetVarint32(p2, &iCol2); |
| 189936 | + /* As above, iCol2==0 indicates corruption. */ | |
| 189937 | + if( iCol2==0 ) return 0; | |
| 189674 | 189938 | } |
| 189675 | 189939 | |
| 189676 | 189940 | while( 1 ){ |
| 189677 | 189941 | if( iCol1==iCol2 ){ |
| 189678 | 189942 | char *pSave = p; |
| @@ -192839,11 +193103,11 @@ | ||
| 192839 | 193103 | for(p=pExpr; p->pLeft; p=p->pLeft){ |
| 192840 | 193104 | assert( p->pRight->pPhrase->doclist.nList>0 ); |
| 192841 | 193105 | nTmp += p->pRight->pPhrase->doclist.nList; |
| 192842 | 193106 | } |
| 192843 | 193107 | nTmp += p->pPhrase->doclist.nList; |
| 192844 | - aTmp = sqlite3_malloc64(nTmp*2); | |
| 193108 | + aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX); | |
| 192845 | 193109 | if( !aTmp ){ |
| 192846 | 193110 | *pRc = SQLITE_NOMEM; |
| 192847 | 193111 | res = 0; |
| 192848 | 193112 | }else{ |
| 192849 | 193113 | char *aPoslist = p->pPhrase->doclist.pList; |
| @@ -193490,11 +193754,11 @@ | ||
| 193490 | 193754 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ |
| 193491 | 193755 | return SQLITE_CORRUPT_VTAB; |
| 193492 | 193756 | } |
| 193493 | 193757 | #endif |
| 193494 | 193758 | |
| 193495 | -#if !SQLITE_CORE | |
| 193759 | +#if !defined(SQLITE_CORE) | |
| 193496 | 193760 | /* |
| 193497 | 193761 | ** Initialize API pointer table, if required. |
| 193498 | 193762 | */ |
| 193499 | 193763 | #ifdef _WIN32 |
| 193500 | 193764 | __declspec(dllexport) |
| @@ -194392,14 +194656,15 @@ | ||
| 194392 | 194656 | rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); |
| 194393 | 194657 | if( rc==SQLITE_OK ){ |
| 194394 | 194658 | Fts3PhraseToken *pToken; |
| 194395 | 194659 | |
| 194396 | 194660 | p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); |
| 194397 | - if( !p ) goto no_mem; | |
| 194398 | - | |
| 194399 | 194661 | zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); |
| 194400 | - if( !zTemp ) goto no_mem; | |
| 194662 | + if( !zTemp || !p ){ | |
| 194663 | + rc = SQLITE_NOMEM; | |
| 194664 | + goto getnextstring_out; | |
| 194665 | + } | |
| 194401 | 194666 | |
| 194402 | 194667 | assert( nToken==ii ); |
| 194403 | 194668 | pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; |
| 194404 | 194669 | memset(pToken, 0, sizeof(Fts3PhraseToken)); |
| 194405 | 194670 | |
| @@ -194410,53 +194675,51 @@ | ||
| 194410 | 194675 | pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*'); |
| 194411 | 194676 | pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^'); |
| 194412 | 194677 | nToken = ii+1; |
| 194413 | 194678 | } |
| 194414 | 194679 | } |
| 194415 | - | |
| 194416 | - pModule->xClose(pCursor); | |
| 194417 | - pCursor = 0; | |
| 194418 | 194680 | } |
| 194419 | 194681 | |
| 194420 | 194682 | if( rc==SQLITE_DONE ){ |
| 194421 | 194683 | int jj; |
| 194422 | 194684 | char *zBuf = 0; |
| 194423 | 194685 | |
| 194424 | 194686 | p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); |
| 194425 | - if( !p ) goto no_mem; | |
| 194687 | + if( !p ){ | |
| 194688 | + rc = SQLITE_NOMEM; | |
| 194689 | + goto getnextstring_out; | |
| 194690 | + } | |
| 194426 | 194691 | memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); |
| 194427 | 194692 | p->eType = FTSQUERY_PHRASE; |
| 194428 | 194693 | p->pPhrase = (Fts3Phrase *)&p[1]; |
| 194429 | 194694 | p->pPhrase->iColumn = pParse->iDefaultCol; |
| 194430 | 194695 | p->pPhrase->nToken = nToken; |
| 194431 | 194696 | |
| 194432 | 194697 | zBuf = (char *)&p->pPhrase->aToken[nToken]; |
| 194698 | + assert( nTemp==0 || zTemp ); | |
| 194433 | 194699 | if( zTemp ){ |
| 194434 | 194700 | memcpy(zBuf, zTemp, nTemp); |
| 194435 | - sqlite3_free(zTemp); | |
| 194436 | - }else{ | |
| 194437 | - assert( nTemp==0 ); | |
| 194438 | 194701 | } |
| 194439 | 194702 | |
| 194440 | 194703 | for(jj=0; jj<p->pPhrase->nToken; jj++){ |
| 194441 | 194704 | p->pPhrase->aToken[jj].z = zBuf; |
| 194442 | 194705 | zBuf += p->pPhrase->aToken[jj].n; |
| 194443 | 194706 | } |
| 194444 | 194707 | rc = SQLITE_OK; |
| 194445 | 194708 | } |
| 194446 | 194709 | |
| 194447 | - *ppExpr = p; | |
| 194448 | - return rc; | |
| 194449 | -no_mem: | |
| 194450 | - | |
| 194710 | + getnextstring_out: | |
| 194451 | 194711 | if( pCursor ){ |
| 194452 | 194712 | pModule->xClose(pCursor); |
| 194453 | 194713 | } |
| 194454 | 194714 | sqlite3_free(zTemp); |
| 194455 | - sqlite3_free(p); | |
| 194456 | - *ppExpr = 0; | |
| 194457 | - return SQLITE_NOMEM; | |
| 194715 | + if( rc!=SQLITE_OK ){ | |
| 194716 | + sqlite3_free(p); | |
| 194717 | + p = 0; | |
| 194718 | + } | |
| 194719 | + *ppExpr = p; | |
| 194720 | + return rc; | |
| 194458 | 194721 | } |
| 194459 | 194722 | |
| 194460 | 194723 | /* |
| 194461 | 194724 | ** The output variable *ppExpr is populated with an allocated Fts3Expr |
| 194462 | 194725 | ** structure, or set to 0 if the end of the input buffer is reached. |
| @@ -208867,11 +209130,13 @@ | ||
| 208867 | 209130 | int rawKey = 1; |
| 208868 | 209131 | x = pParse->aBlob[iRoot]; |
| 208869 | 209132 | zPath++; |
| 208870 | 209133 | if( zPath[0]=='"' ){ |
| 208871 | 209134 | zKey = zPath + 1; |
| 208872 | - for(i=1; zPath[i] && zPath[i]!='"'; i++){} | |
| 209135 | + for(i=1; zPath[i] && zPath[i]!='"'; i++){ | |
| 209136 | + if( zPath[i]=='\\' && zPath[i+1]!=0 ) i++; | |
| 209137 | + } | |
| 208873 | 209138 | nKey = i-1; |
| 208874 | 209139 | if( zPath[i] ){ |
| 208875 | 209140 | i++; |
| 208876 | 209141 | }else{ |
| 208877 | 209142 | return JSON_LOOKUP_PATHERROR; |
| @@ -217779,11 +218044,11 @@ | ||
| 217779 | 218044 | return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY, |
| 217780 | 218045 | (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback |
| 217781 | 218046 | ); |
| 217782 | 218047 | } |
| 217783 | 218048 | |
| 217784 | -#if !SQLITE_CORE | |
| 218049 | +#ifndef SQLITE_CORE | |
| 217785 | 218050 | #ifdef _WIN32 |
| 217786 | 218051 | __declspec(dllexport) |
| 217787 | 218052 | #endif |
| 217788 | 218053 | SQLITE_API int sqlite3_rtree_init( |
| 217789 | 218054 | sqlite3 *db, |
| @@ -218370,11 +218635,11 @@ | ||
| 218370 | 218635 | } |
| 218371 | 218636 | |
| 218372 | 218637 | return rc; |
| 218373 | 218638 | } |
| 218374 | 218639 | |
| 218375 | -#if !SQLITE_CORE | |
| 218640 | +#ifndef SQLITE_CORE | |
| 218376 | 218641 | #ifdef _WIN32 |
| 218377 | 218642 | __declspec(dllexport) |
| 218378 | 218643 | #endif |
| 218379 | 218644 | SQLITE_API int sqlite3_icu_init( |
| 218380 | 218645 | sqlite3 *db, |
| @@ -219628,10 +219893,31 @@ | ||
| 219628 | 219893 | struct RbuFrame { |
| 219629 | 219894 | u32 iDbPage; |
| 219630 | 219895 | u32 iWalFrame; |
| 219631 | 219896 | }; |
| 219632 | 219897 | |
| 219898 | +#ifndef UNUSED_PARAMETER | |
| 219899 | +/* | |
| 219900 | +** The following macros are used to suppress compiler warnings and to | |
| 219901 | +** make it clear to human readers when a function parameter is deliberately | |
| 219902 | +** left unused within the body of a function. This usually happens when | |
| 219903 | +** a function is called via a function pointer. For example the | |
| 219904 | +** implementation of an SQL aggregate step callback may not use the | |
| 219905 | +** parameter indicating the number of arguments passed to the aggregate, | |
| 219906 | +** if it knows that this is enforced elsewhere. | |
| 219907 | +** | |
| 219908 | +** When a function parameter is not used at all within the body of a function, | |
| 219909 | +** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. | |
| 219910 | +** However, these macros may also be used to suppress warnings related to | |
| 219911 | +** parameters that may or may not be used depending on compilation options. | |
| 219912 | +** For example those parameters only used in assert() statements. In these | |
| 219913 | +** cases the parameters are named as per the usual conventions. | |
| 219914 | +*/ | |
| 219915 | +#define UNUSED_PARAMETER(x) (void)(x) | |
| 219916 | +#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) | |
| 219917 | +#endif | |
| 219918 | + | |
| 219633 | 219919 | /* |
| 219634 | 219920 | ** RBU handle. |
| 219635 | 219921 | ** |
| 219636 | 219922 | ** nPhaseOneStep: |
| 219637 | 219923 | ** If the RBU database contains an rbu_count table, this value is set to |
| @@ -219679,11 +219965,11 @@ | ||
| 219679 | 219965 | char *zState; /* Path to state db (or NULL if zRbu) */ |
| 219680 | 219966 | char zStateDb[5]; /* Db name for state ("stat" or "main") */ |
| 219681 | 219967 | int rc; /* Value returned by last rbu_step() call */ |
| 219682 | 219968 | char *zErrmsg; /* Error message if rc!=SQLITE_OK */ |
| 219683 | 219969 | int nStep; /* Rows processed for current object */ |
| 219684 | - int nProgress; /* Rows processed for all objects */ | |
| 219970 | + sqlite3_int64 nProgress; /* Rows processed for all objects */ | |
| 219685 | 219971 | RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ |
| 219686 | 219972 | const char *zVfsName; /* Name of automatically created rbu vfs */ |
| 219687 | 219973 | rbu_file *pTargetFd; /* File handle open on target db */ |
| 219688 | 219974 | int nPagePerSector; /* Pages per sector for pTargetFd */ |
| 219689 | 219975 | i64 iOalSz; |
| @@ -219796,11 +220082,11 @@ | ||
| 219796 | 220082 | unsigned char *zStart = z; |
| 219797 | 220083 | while( (c = zValue[0x7f&*(z++)])>=0 ){ |
| 219798 | 220084 | v = (v<<6) + c; |
| 219799 | 220085 | } |
| 219800 | 220086 | z--; |
| 219801 | - *pLen -= z - zStart; | |
| 220087 | + *pLen -= (int)(z - zStart); | |
| 219802 | 220088 | *pz = (char*)z; |
| 219803 | 220089 | return v; |
| 219804 | 220090 | } |
| 219805 | 220091 | |
| 219806 | 220092 | #if RBU_ENABLE_DELTA_CKSUM |
| @@ -219981,10 +220267,11 @@ | ||
| 219981 | 220267 | int nOut; |
| 219982 | 220268 | int nOut2; |
| 219983 | 220269 | char *aOut; |
| 219984 | 220270 | |
| 219985 | 220271 | assert( argc==2 ); |
| 220272 | + UNUSED_PARAMETER(argc); | |
| 219986 | 220273 | |
| 219987 | 220274 | nOrig = sqlite3_value_bytes(argv[0]); |
| 219988 | 220275 | aOrig = (const char*)sqlite3_value_blob(argv[0]); |
| 219989 | 220276 | nDelta = sqlite3_value_bytes(argv[1]); |
| 219990 | 220277 | aDelta = (const char*)sqlite3_value_blob(argv[1]); |
| @@ -221560,17 +221847,17 @@ | ||
| 221560 | 221847 | nParen++; |
| 221561 | 221848 | } |
| 221562 | 221849 | else if( c==')' ){ |
| 221563 | 221850 | nParen--; |
| 221564 | 221851 | if( nParen==0 ){ |
| 221565 | - int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; | |
| 221852 | + int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); | |
| 221566 | 221853 | pIter->aIdxCol[iIdxCol++].nSpan = nSpan; |
| 221567 | 221854 | i++; |
| 221568 | 221855 | break; |
| 221569 | 221856 | } |
| 221570 | 221857 | }else if( c==',' && nParen==1 ){ |
| 221571 | - int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; | |
| 221858 | + int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); | |
| 221572 | 221859 | pIter->aIdxCol[iIdxCol++].nSpan = nSpan; |
| 221573 | 221860 | pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1]; |
| 221574 | 221861 | }else if( c=='"' || c=='\'' || c=='`' ){ |
| 221575 | 221862 | for(i++; 1; i++){ |
| 221576 | 221863 | if( zSql[i]==c ){ |
| @@ -222256,10 +222543,12 @@ | ||
| 222256 | 222543 | int i, sz; |
| 222257 | 222544 | sz = (int)strlen(z)&0xffffff; |
| 222258 | 222545 | for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} |
| 222259 | 222546 | if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); |
| 222260 | 222547 | } |
| 222548 | +#else | |
| 222549 | + UNUSED_PARAMETER2(zBase,z); | |
| 222261 | 222550 | #endif |
| 222262 | 222551 | } |
| 222263 | 222552 | |
| 222264 | 222553 | /* |
| 222265 | 222554 | ** Return the current wal-index header checksum for the target database |
| @@ -222840,11 +223129,11 @@ | ||
| 222840 | 223129 | "INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES " |
| 222841 | 223130 | "(%d, %d), " |
| 222842 | 223131 | "(%d, %Q), " |
| 222843 | 223132 | "(%d, %Q), " |
| 222844 | 223133 | "(%d, %d), " |
| 222845 | - "(%d, %d), " | |
| 223134 | + "(%d, %lld), " | |
| 222846 | 223135 | "(%d, %lld), " |
| 222847 | 223136 | "(%d, %lld), " |
| 222848 | 223137 | "(%d, %lld), " |
| 222849 | 223138 | "(%d, %lld), " |
| 222850 | 223139 | "(%d, %Q) ", |
| @@ -223198,10 +223487,11 @@ | ||
| 223198 | 223487 | char *zErrmsg = 0; |
| 223199 | 223488 | int rc; |
| 223200 | 223489 | sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain); |
| 223201 | 223490 | |
| 223202 | 223491 | assert( nVal==1 ); |
| 223492 | + UNUSED_PARAMETER(nVal); | |
| 223203 | 223493 | |
| 223204 | 223494 | rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, |
| 223205 | 223495 | sqlite3_mprintf("SELECT count(*) FROM sqlite_schema " |
| 223206 | 223496 | "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0])) |
| 223207 | 223497 | ); |
| @@ -223473,11 +223763,11 @@ | ||
| 223473 | 223763 | const char *zTarget, |
| 223474 | 223764 | const char *zState |
| 223475 | 223765 | ){ |
| 223476 | 223766 | if( zTarget==0 ){ return rbuMisuseError(); } |
| 223477 | 223767 | if( zState ){ |
| 223478 | - int n = strlen(zState); | |
| 223768 | + size_t n = strlen(zState); | |
| 223479 | 223769 | if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ |
| 223480 | 223770 | return rbuMisuseError(); |
| 223481 | 223771 | } |
| 223482 | 223772 | } |
| 223483 | 223773 | /* TODO: Check that both arguments are non-NULL */ |
| @@ -223690,10 +223980,11 @@ | ||
| 223690 | 223980 | /* |
| 223691 | 223981 | ** Default xRename callback for RBU. |
| 223692 | 223982 | */ |
| 223693 | 223983 | static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ |
| 223694 | 223984 | int rc = SQLITE_OK; |
| 223985 | + UNUSED_PARAMETER(pArg); | |
| 223695 | 223986 | #if defined(_WIN32_WCE) |
| 223696 | 223987 | { |
| 223697 | 223988 | LPWSTR zWideOld; |
| 223698 | 223989 | LPWSTR zWideNew; |
| 223699 | 223990 | |
| @@ -224594,10 +224885,13 @@ | ||
| 224594 | 224885 | |
| 224595 | 224886 | /* |
| 224596 | 224887 | ** No-op. |
| 224597 | 224888 | */ |
| 224598 | 224889 | static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){ |
| 224890 | + UNUSED_PARAMETER(pVfs); | |
| 224891 | + UNUSED_PARAMETER(a); | |
| 224892 | + UNUSED_PARAMETER(b); | |
| 224599 | 224893 | return 0; |
| 224600 | 224894 | } |
| 224601 | 224895 | |
| 224602 | 224896 | /* |
| 224603 | 224897 | ** Deregister and destroy an RBU vfs created by an earlier call to |
| @@ -225650,11 +225944,17 @@ | ||
| 225650 | 225944 | ** schema for the database file that is to be read. The default schema is |
| 225651 | 225945 | ** "main". |
| 225652 | 225946 | ** |
| 225653 | 225947 | ** The data field of sqlite_dbpage table can be updated. The new |
| 225654 | 225948 | ** value must be a BLOB which is the correct page size, otherwise the |
| 225655 | -** update fails. Rows may not be deleted or inserted. | |
| 225949 | +** update fails. INSERT operations also work, and operate as if they | |
| 225950 | +** where REPLACE. The size of the database can be extended by INSERT-ing | |
| 225951 | +** new pages on the end. | |
| 225952 | +** | |
| 225953 | +** Rows may not be deleted. However, doing an INSERT to page number N | |
| 225954 | +** with NULL page data causes the N-th page and all subsequent pages to be | |
| 225955 | +** deleted and the database to be truncated. | |
| 225656 | 225956 | */ |
| 225657 | 225957 | |
| 225658 | 225958 | /* #include "sqliteInt.h" ** Requires access to internal data structures ** */ |
| 225659 | 225959 | #if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ |
| 225660 | 225960 | && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| @@ -225673,17 +225973,18 @@ | ||
| 225673 | 225973 | }; |
| 225674 | 225974 | |
| 225675 | 225975 | struct DbpageTable { |
| 225676 | 225976 | sqlite3_vtab base; /* Base class. Must be first */ |
| 225677 | 225977 | sqlite3 *db; /* The database */ |
| 225978 | + int iDbTrunc; /* Database to truncate */ | |
| 225979 | + Pgno pgnoTrunc; /* Size to truncate to */ | |
| 225678 | 225980 | }; |
| 225679 | 225981 | |
| 225680 | 225982 | /* Columns */ |
| 225681 | 225983 | #define DBPAGE_COLUMN_PGNO 0 |
| 225682 | 225984 | #define DBPAGE_COLUMN_DATA 1 |
| 225683 | 225985 | #define DBPAGE_COLUMN_SCHEMA 2 |
| 225684 | - | |
| 225685 | 225986 | |
| 225686 | 225987 | |
| 225687 | 225988 | /* |
| 225688 | 225989 | ** Connect to or create a dbpagevfs virtual table. |
| 225689 | 225990 | */ |
| @@ -225943,15 +226244,15 @@ | ||
| 225943 | 226244 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 225944 | 226245 | Pgno pgno; |
| 225945 | 226246 | DbPage *pDbPage = 0; |
| 225946 | 226247 | int rc = SQLITE_OK; |
| 225947 | 226248 | char *zErr = 0; |
| 225948 | - const char *zSchema; | |
| 225949 | 226249 | int iDb; |
| 225950 | 226250 | Btree *pBt; |
| 225951 | 226251 | Pager *pPager; |
| 225952 | 226252 | int szPage; |
| 226253 | + int isInsert; | |
| 225953 | 226254 | |
| 225954 | 226255 | (void)pRowid; |
| 225955 | 226256 | if( pTab->db->flags & SQLITE_Defensive ){ |
| 225956 | 226257 | zErr = "read-only"; |
| 225957 | 226258 | goto update_fail; |
| @@ -225958,45 +226259,62 @@ | ||
| 225958 | 226259 | } |
| 225959 | 226260 | if( argc==1 ){ |
| 225960 | 226261 | zErr = "cannot delete"; |
| 225961 | 226262 | goto update_fail; |
| 225962 | 226263 | } |
| 225963 | - pgno = sqlite3_value_int(argv[0]); | |
| 225964 | - if( sqlite3_value_type(argv[0])==SQLITE_NULL | |
| 225965 | - || (Pgno)sqlite3_value_int(argv[1])!=pgno | |
| 225966 | - ){ | |
| 225967 | - zErr = "cannot insert"; | |
| 225968 | - goto update_fail; | |
| 225969 | - } | |
| 225970 | - zSchema = (const char*)sqlite3_value_text(argv[4]); | |
| 225971 | - iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1; | |
| 225972 | - if( NEVER(iDb<0) ){ | |
| 225973 | - zErr = "no such schema"; | |
| 225974 | - goto update_fail; | |
| 226264 | + if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ | |
| 226265 | + pgno = (Pgno)sqlite3_value_int(argv[2]); | |
| 226266 | + isInsert = 1; | |
| 226267 | + }else{ | |
| 226268 | + pgno = sqlite3_value_int(argv[0]); | |
| 226269 | + if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ | |
| 226270 | + zErr = "cannot insert"; | |
| 226271 | + goto update_fail; | |
| 226272 | + } | |
| 226273 | + isInsert = 0; | |
| 226274 | + } | |
| 226275 | + if( sqlite3_value_type(argv[4])==SQLITE_NULL ){ | |
| 226276 | + iDb = 0; | |
| 226277 | + }else{ | |
| 226278 | + const char *zSchema = (const char*)sqlite3_value_text(argv[4]); | |
| 226279 | + iDb = sqlite3FindDbName(pTab->db, zSchema); | |
| 226280 | + if( iDb<0 ){ | |
| 226281 | + zErr = "no such schema"; | |
| 226282 | + goto update_fail; | |
| 226283 | + } | |
| 225975 | 226284 | } |
| 225976 | 226285 | pBt = pTab->db->aDb[iDb].pBt; |
| 225977 | - if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){ | |
| 226286 | + if( pgno<1 || NEVER(pBt==0) ){ | |
| 225978 | 226287 | zErr = "bad page number"; |
| 225979 | 226288 | goto update_fail; |
| 225980 | 226289 | } |
| 225981 | 226290 | szPage = sqlite3BtreeGetPageSize(pBt); |
| 225982 | 226291 | if( sqlite3_value_type(argv[3])!=SQLITE_BLOB |
| 225983 | 226292 | || sqlite3_value_bytes(argv[3])!=szPage |
| 225984 | 226293 | ){ |
| 225985 | - zErr = "bad page value"; | |
| 225986 | - goto update_fail; | |
| 226294 | + if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){ | |
| 226295 | + /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and | |
| 226296 | + ** all subsequent pages to be deleted. */ | |
| 226297 | + pTab->iDbTrunc = iDb; | |
| 226298 | + pgno--; | |
| 226299 | + pTab->pgnoTrunc = pgno; | |
| 226300 | + }else{ | |
| 226301 | + zErr = "bad page value"; | |
| 226302 | + goto update_fail; | |
| 226303 | + } | |
| 225987 | 226304 | } |
| 225988 | 226305 | pPager = sqlite3BtreePager(pBt); |
| 225989 | 226306 | rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); |
| 225990 | 226307 | if( rc==SQLITE_OK ){ |
| 225991 | 226308 | const void *pData = sqlite3_value_blob(argv[3]); |
| 225992 | - assert( pData!=0 || pTab->db->mallocFailed ); | |
| 225993 | - if( pData | |
| 225994 | - && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK | |
| 225995 | - ){ | |
| 225996 | - memcpy(sqlite3PagerGetData(pDbPage), pData, szPage); | |
| 226309 | + if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){ | |
| 226310 | + unsigned char *aPage = sqlite3PagerGetData(pDbPage); | |
| 226311 | + memcpy(aPage, pData, szPage); | |
| 226312 | + pTab->pgnoTrunc = 0; | |
| 225997 | 226313 | } |
| 226314 | + }else{ | |
| 226315 | + pTab->pgnoTrunc = 0; | |
| 225998 | 226316 | } |
| 225999 | 226317 | sqlite3PagerUnref(pDbPage); |
| 226000 | 226318 | return rc; |
| 226001 | 226319 | |
| 226002 | 226320 | update_fail: |
| @@ -226015,13 +226333,35 @@ | ||
| 226015 | 226333 | int i; |
| 226016 | 226334 | for(i=0; i<db->nDb; i++){ |
| 226017 | 226335 | Btree *pBt = db->aDb[i].pBt; |
| 226018 | 226336 | if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0); |
| 226019 | 226337 | } |
| 226338 | + pTab->pgnoTrunc = 0; | |
| 226339 | + return SQLITE_OK; | |
| 226340 | +} | |
| 226341 | + | |
| 226342 | +/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT | |
| 226343 | +*/ | |
| 226344 | +static int dbpageSync(sqlite3_vtab *pVtab){ | |
| 226345 | + DbpageTable *pTab = (DbpageTable *)pVtab; | |
| 226346 | + if( pTab->pgnoTrunc>0 ){ | |
| 226347 | + Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; | |
| 226348 | + Pager *pPager = sqlite3BtreePager(pBt); | |
| 226349 | + sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); | |
| 226350 | + } | |
| 226351 | + pTab->pgnoTrunc = 0; | |
| 226352 | + return SQLITE_OK; | |
| 226353 | +} | |
| 226354 | + | |
| 226355 | +/* Cancel any pending truncate. | |
| 226356 | +*/ | |
| 226357 | +static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){ | |
| 226358 | + DbpageTable *pTab = (DbpageTable *)pVtab; | |
| 226359 | + pTab->pgnoTrunc = 0; | |
| 226360 | + (void)notUsed1; | |
| 226020 | 226361 | return SQLITE_OK; |
| 226021 | 226362 | } |
| 226022 | - | |
| 226023 | 226363 | |
| 226024 | 226364 | /* |
| 226025 | 226365 | ** Invoke this routine to register the "dbpage" virtual table module |
| 226026 | 226366 | */ |
| 226027 | 226367 | SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ |
| @@ -226039,18 +226379,18 @@ | ||
| 226039 | 226379 | dbpageEof, /* xEof - check for end of scan */ |
| 226040 | 226380 | dbpageColumn, /* xColumn - read data */ |
| 226041 | 226381 | dbpageRowid, /* xRowid - read data */ |
| 226042 | 226382 | dbpageUpdate, /* xUpdate */ |
| 226043 | 226383 | dbpageBegin, /* xBegin */ |
| 226044 | - 0, /* xSync */ | |
| 226384 | + dbpageSync, /* xSync */ | |
| 226045 | 226385 | 0, /* xCommit */ |
| 226046 | 226386 | 0, /* xRollback */ |
| 226047 | 226387 | 0, /* xFindMethod */ |
| 226048 | 226388 | 0, /* xRename */ |
| 226049 | 226389 | 0, /* xSavepoint */ |
| 226050 | 226390 | 0, /* xRelease */ |
| 226051 | - 0, /* xRollbackTo */ | |
| 226391 | + dbpageRollbackTo, /* xRollbackTo */ | |
| 226052 | 226392 | 0, /* xShadowName */ |
| 226053 | 226393 | 0 /* xIntegrity */ |
| 226054 | 226394 | }; |
| 226055 | 226395 | return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); |
| 226056 | 226396 | } |
| @@ -226134,10 +226474,14 @@ | ||
| 226134 | 226474 | /* |
| 226135 | 226475 | ** An object of this type is used internally as an abstraction for |
| 226136 | 226476 | ** input data. Input data may be supplied either as a single large buffer |
| 226137 | 226477 | ** (e.g. sqlite3changeset_start()) or using a stream function (e.g. |
| 226138 | 226478 | ** sqlite3changeset_start_strm()). |
| 226479 | +** | |
| 226480 | +** bNoDiscard: | |
| 226481 | +** If true, then the only time data is discarded is as a result of explicit | |
| 226482 | +** sessionDiscardData() calls. Not within every sessionInputBuffer() call. | |
| 226139 | 226483 | */ |
| 226140 | 226484 | struct SessionInput { |
| 226141 | 226485 | int bNoDiscard; /* If true, do not discard in InputBuffer() */ |
| 226142 | 226486 | int iCurrent; /* Offset in aData[] of current change */ |
| 226143 | 226487 | int iNext; /* Offset in aData[] of next change */ |
| @@ -227817,20 +228161,23 @@ | ||
| 227817 | 228161 | /* Figure out how large an allocation is required */ |
| 227818 | 228162 | nByte = sizeof(SessionChange); |
| 227819 | 228163 | for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ |
| 227820 | 228164 | sqlite3_value *p = 0; |
| 227821 | 228165 | if( op!=SQLITE_INSERT ){ |
| 227822 | - TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); | |
| 227823 | - assert( trc==SQLITE_OK ); | |
| 228166 | + /* This may fail if the column has a non-NULL default and was added | |
| 228167 | + ** using ALTER TABLE ADD COLUMN after this record was created. */ | |
| 228168 | + rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p); | |
| 227824 | 228169 | }else if( pTab->abPK[i] ){ |
| 227825 | 228170 | TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); |
| 227826 | 228171 | assert( trc==SQLITE_OK ); |
| 227827 | 228172 | } |
| 227828 | 228173 | |
| 227829 | - /* This may fail if SQLite value p contains a utf-16 string that must | |
| 227830 | - ** be converted to utf-8 and an OOM error occurs while doing so. */ | |
| 227831 | - rc = sessionSerializeValue(0, p, &nByte); | |
| 228174 | + if( rc==SQLITE_OK ){ | |
| 228175 | + /* This may fail if SQLite value p contains a utf-16 string that must | |
| 228176 | + ** be converted to utf-8 and an OOM error occurs while doing so. */ | |
| 228177 | + rc = sessionSerializeValue(0, p, &nByte); | |
| 228178 | + } | |
| 227832 | 228179 | if( rc!=SQLITE_OK ) goto error_out; |
| 227833 | 228180 | } |
| 227834 | 228181 | if( pTab->bRowid ){ |
| 227835 | 228182 | nByte += 9; /* Size of rowid field - an integer */ |
| 227836 | 228183 | } |
| @@ -231184,19 +231531,25 @@ | ||
| 231184 | 231531 | int rc = SQLITE_OK; /* Return code */ |
| 231185 | 231532 | const char *zTab = 0; /* Name of current table */ |
| 231186 | 231533 | int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ |
| 231187 | 231534 | SessionApplyCtx sApply; /* changeset_apply() context object */ |
| 231188 | 231535 | int bPatchset; |
| 231536 | + u64 savedFlag = db->flags & SQLITE_FkNoAction; | |
| 231189 | 231537 | |
| 231190 | 231538 | assert( xConflict!=0 ); |
| 231539 | + | |
| 231540 | + sqlite3_mutex_enter(sqlite3_db_mutex(db)); | |
| 231541 | + if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ | |
| 231542 | + db->flags |= ((u64)SQLITE_FkNoAction); | |
| 231543 | + db->aDb[0].pSchema->schema_cookie -= 32; | |
| 231544 | + } | |
| 231191 | 231545 | |
| 231192 | 231546 | pIter->in.bNoDiscard = 1; |
| 231193 | 231547 | memset(&sApply, 0, sizeof(sApply)); |
| 231194 | 231548 | sApply.bRebase = (ppRebase && pnRebase); |
| 231195 | 231549 | sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 231196 | 231550 | sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); |
| 231197 | - sqlite3_mutex_enter(sqlite3_db_mutex(db)); | |
| 231198 | 231551 | if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ |
| 231199 | 231552 | rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); |
| 231200 | 231553 | } |
| 231201 | 231554 | if( rc==SQLITE_OK ){ |
| 231202 | 231555 | rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0); |
| @@ -231354,10 +231707,16 @@ | ||
| 231354 | 231707 | sqlite3_finalize(sApply.pDelete); |
| 231355 | 231708 | sqlite3_finalize(sApply.pSelect); |
| 231356 | 231709 | sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ |
| 231357 | 231710 | sqlite3_free((char*)sApply.constraints.aBuf); |
| 231358 | 231711 | sqlite3_free((char*)sApply.rebase.aBuf); |
| 231712 | + | |
| 231713 | + if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ | |
| 231714 | + assert( db->flags & SQLITE_FkNoAction ); | |
| 231715 | + db->flags &= ~((u64)SQLITE_FkNoAction); | |
| 231716 | + db->aDb[0].pSchema->schema_cookie -= 32; | |
| 231717 | + } | |
| 231359 | 231718 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); |
| 231360 | 231719 | return rc; |
| 231361 | 231720 | } |
| 231362 | 231721 | |
| 231363 | 231722 | /* |
| @@ -231382,28 +231741,17 @@ | ||
| 231382 | 231741 | int flags |
| 231383 | 231742 | ){ |
| 231384 | 231743 | sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ |
| 231385 | 231744 | int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 231386 | 231745 | int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); |
| 231387 | - u64 savedFlag = db->flags & SQLITE_FkNoAction; | |
| 231388 | - | |
| 231389 | - if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ | |
| 231390 | - db->flags |= ((u64)SQLITE_FkNoAction); | |
| 231391 | - db->aDb[0].pSchema->schema_cookie -= 32; | |
| 231392 | - } | |
| 231393 | 231746 | |
| 231394 | 231747 | if( rc==SQLITE_OK ){ |
| 231395 | 231748 | rc = sessionChangesetApply( |
| 231396 | 231749 | db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags |
| 231397 | 231750 | ); |
| 231398 | 231751 | } |
| 231399 | 231752 | |
| 231400 | - if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ | |
| 231401 | - assert( db->flags & SQLITE_FkNoAction ); | |
| 231402 | - db->flags &= ~((u64)SQLITE_FkNoAction); | |
| 231403 | - db->aDb[0].pSchema->schema_cookie -= 32; | |
| 231404 | - } | |
| 231405 | 231753 | return rc; |
| 231406 | 231754 | } |
| 231407 | 231755 | |
| 231408 | 231756 | /* |
| 231409 | 231757 | ** Apply the changeset passed via pChangeset/nChangeset to the main database |
| @@ -231720,10 +232068,13 @@ | ||
| 231720 | 232068 | if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){ |
| 231721 | 232069 | /* Append the missing default column values to the record. */ |
| 231722 | 232070 | sessionAppendBlob(pOut, aRec, nRec, &rc); |
| 231723 | 232071 | if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ |
| 231724 | 232072 | rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); |
| 232073 | + if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){ | |
| 232074 | + rc = sqlite3_errcode(pGrp->db); | |
| 232075 | + } | |
| 231725 | 232076 | } |
| 231726 | 232077 | for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){ |
| 231727 | 232078 | int eType = sqlite3_column_type(pTab->pDfltStmt, ii); |
| 231728 | 232079 | sessionAppendByte(pOut, eType, &rc); |
| 231729 | 232080 | switch( eType ){ |
| @@ -231736,10 +232087,11 @@ | ||
| 231736 | 232087 | double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); |
| 231737 | 232088 | memcpy(&iVal, &rVal, sizeof(i64)); |
| 231738 | 232089 | } |
| 231739 | 232090 | if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ |
| 231740 | 232091 | sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); |
| 232092 | + pOut->nBuf += 8; | |
| 231741 | 232093 | } |
| 231742 | 232094 | break; |
| 231743 | 232095 | } |
| 231744 | 232096 | |
| 231745 | 232097 | case SQLITE_BLOB: |
| @@ -231874,10 +232226,12 @@ | ||
| 231874 | 232226 | SessionChange *pExist = 0; |
| 231875 | 232227 | SessionChange **pp = 0; |
| 231876 | 232228 | SessionTable *pTab = 0; |
| 231877 | 232229 | u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; |
| 231878 | 232230 | int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; |
| 232231 | + | |
| 232232 | + assert( nRec>0 ); | |
| 231879 | 232233 | |
| 231880 | 232234 | /* Ensure that only changesets, or only patchsets, but not a mixture |
| 231881 | 232235 | ** of both, are being combined. It is an error to try to combine a |
| 231882 | 232236 | ** changeset and a patchset. */ |
| 231883 | 232237 | if( pGrp->pList==0 ){ |
| @@ -231952,10 +232306,11 @@ | ||
| 231952 | 232306 | ){ |
| 231953 | 232307 | u8 *aRec; |
| 231954 | 232308 | int nRec; |
| 231955 | 232309 | int rc = SQLITE_OK; |
| 231956 | 232310 | |
| 232311 | + pIter->in.bNoDiscard = 1; | |
| 231957 | 232312 | while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ |
| 231958 | 232313 | rc = sessionOneChangeToHash(pGrp, pIter, bRebase); |
| 231959 | 232314 | if( rc!=SQLITE_OK ) break; |
| 231960 | 232315 | } |
| 231961 | 232316 | |
| @@ -232583,20 +232938,46 @@ | ||
| 232583 | 232938 | #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 232584 | 232939 | |
| 232585 | 232940 | /************** End of sqlite3session.c **************************************/ |
| 232586 | 232941 | /************** Begin file fts5.c ********************************************/ |
| 232587 | 232942 | |
| 232588 | - | |
| 232943 | +/* | |
| 232944 | +** This, the "fts5.c" source file, is a composite file that is itself | |
| 232945 | +** assembled from the following files: | |
| 232946 | +** | |
| 232947 | +** fts5.h | |
| 232948 | +** fts5Int.h | |
| 232949 | +** fts5parse.h <--- Generated from fts5parse.y by Lemon | |
| 232950 | +** fts5parse.c <--- Generated from fts5parse.y by Lemon | |
| 232951 | +** fts5_aux.c | |
| 232952 | +** fts5_buffer.c | |
| 232953 | +** fts5_config.c | |
| 232954 | +** fts5_expr.c | |
| 232955 | +** fts5_hash.c | |
| 232956 | +** fts5_index.c | |
| 232957 | +** fts5_main.c | |
| 232958 | +** fts5_storage.c | |
| 232959 | +** fts5_tokenize.c | |
| 232960 | +** fts5_unicode2.c | |
| 232961 | +** fts5_varint.c | |
| 232962 | +** fts5_vocab.c | |
| 232963 | +*/ | |
| 232589 | 232964 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) |
| 232590 | 232965 | |
| 232591 | 232966 | #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) |
| 232592 | 232967 | # define NDEBUG 1 |
| 232593 | 232968 | #endif |
| 232594 | 232969 | #if defined(NDEBUG) && defined(SQLITE_DEBUG) |
| 232595 | 232970 | # undef NDEBUG |
| 232596 | 232971 | #endif |
| 232597 | 232972 | |
| 232973 | +#ifdef HAVE_STDINT_H | |
| 232974 | +/* #include <stdint.h> */ | |
| 232975 | +#endif | |
| 232976 | +#ifdef HAVE_INTTYPES_H | |
| 232977 | +/* #include <inttypes.h> */ | |
| 232978 | +#endif | |
| 232598 | 232979 | /* |
| 232599 | 232980 | ** 2014 May 31 |
| 232600 | 232981 | ** |
| 232601 | 232982 | ** The author disclaims copyright to this source code. In place of |
| 232602 | 232983 | ** a legal notice, here is a blessing: |
| @@ -232893,17 +233274,32 @@ | ||
| 232893 | 233274 | ** This is used to access token iToken of phrase hit iIdx within the |
| 232894 | 233275 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 232895 | 233276 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 232896 | 233277 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 232897 | 233278 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 232898 | -** bytes. This API is not available if the specified token matches a | |
| 232899 | -** prefix query term. In that case both output variables are always set | |
| 232900 | -** to 0. | |
| 233279 | +** bytes. | |
| 232901 | 233280 | ** |
| 232902 | 233281 | ** The output text is not a copy of the document text that was tokenized. |
| 232903 | 233282 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 232904 | 233283 | ** includes any embedded 0x00 and trailing data. |
| 233284 | +** | |
| 233285 | +** This API may be slow in some cases if the token identified by parameters | |
| 233286 | +** iIdx and iToken matched a prefix token in the query. In most cases, the | |
| 233287 | +** first call to this API for each prefix token in the query is forced | |
| 233288 | +** to scan the portion of the full-text index that matches the prefix | |
| 233289 | +** token to collect the extra data required by this API. If the prefix | |
| 233290 | +** token matches a large number of token instances in the document set, | |
| 233291 | +** this may be a performance problem. | |
| 233292 | +** | |
| 233293 | +** If the user knows in advance that a query may use this API for a | |
| 233294 | +** prefix token, FTS5 may be configured to collect all required data as part | |
| 233295 | +** of the initial querying of the full-text index, avoiding the second scan | |
| 233296 | +** entirely. This also causes prefix queries that do not use this API to | |
| 233297 | +** run more slowly and use more memory. FTS5 may be configured in this way | |
| 233298 | +** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] | |
| 233299 | +** option, or on a per-query basis using the | |
| 233300 | +** [fts5_insttoken | fts5_insttoken()] user function. | |
| 232905 | 233301 | ** |
| 232906 | 233302 | ** This API can be quite slow if used with an FTS5 table created with the |
| 232907 | 233303 | ** "detail=none" or "detail=column" option. |
| 232908 | 233304 | ** |
| 232909 | 233305 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -232991,11 +233387,10 @@ | ||
| 232991 | 233387 | ** CUSTOM TOKENIZERS |
| 232992 | 233388 | ** |
| 232993 | 233389 | ** Applications may also register custom tokenizer types. A tokenizer |
| 232994 | 233390 | ** is registered by providing fts5 with a populated instance of the |
| 232995 | 233391 | ** following structure. All structure methods must be defined, setting |
| 232996 | -** | |
| 232997 | 233392 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 232998 | 233393 | ** behaviour. The structure methods are expected to function as follows: |
| 232999 | 233394 | ** |
| 233000 | 233395 | ** xCreate: |
| 233001 | 233396 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -233560,10 +233955,11 @@ | ||
| 233560 | 233955 | u8 *abUnindexed; /* True for unindexed columns */ |
| 233561 | 233956 | int nPrefix; /* Number of prefix indexes */ |
| 233562 | 233957 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ |
| 233563 | 233958 | int eContent; /* An FTS5_CONTENT value */ |
| 233564 | 233959 | int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ |
| 233960 | + int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ | |
| 233565 | 233961 | char *zContent; /* content table */ |
| 233566 | 233962 | char *zContentRowid; /* "content_rowid=" option value */ |
| 233567 | 233963 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ |
| 233568 | 233964 | int bTokendata; /* "tokendata=" option value (dflt==0) */ |
| 233569 | 233965 | int bLocale; /* "locale=" option value (dflt==0) */ |
| @@ -233582,11 +233978,12 @@ | ||
| 233582 | 233978 | int nUsermerge; /* 'usermerge' setting */ |
| 233583 | 233979 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 233584 | 233980 | char *zRank; /* Name of rank function */ |
| 233585 | 233981 | char *zRankArgs; /* Arguments to rank function */ |
| 233586 | 233982 | int bSecureDelete; /* 'secure-delete' */ |
| 233587 | - int nDeleteMerge; /* 'deletemerge' */ | |
| 233983 | + int nDeleteMerge; /* 'deletemerge' */ | |
| 233984 | + int bPrefixInsttoken; /* 'prefix-insttoken' */ | |
| 233588 | 233985 | |
| 233589 | 233986 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 233590 | 233987 | char **pzErrmsg; |
| 233591 | 233988 | |
| 233592 | 233989 | #ifdef SQLITE_DEBUG |
| @@ -233598,13 +233995,14 @@ | ||
| 233598 | 233995 | ** the expected version if the 'secure-delete' option has ever been |
| 233599 | 233996 | ** set on the table. */ |
| 233600 | 233997 | #define FTS5_CURRENT_VERSION 4 |
| 233601 | 233998 | #define FTS5_CURRENT_VERSION_SECUREDELETE 5 |
| 233602 | 233999 | |
| 233603 | -#define FTS5_CONTENT_NORMAL 0 | |
| 233604 | -#define FTS5_CONTENT_NONE 1 | |
| 233605 | -#define FTS5_CONTENT_EXTERNAL 2 | |
| 234000 | +#define FTS5_CONTENT_NORMAL 0 | |
| 234001 | +#define FTS5_CONTENT_NONE 1 | |
| 234002 | +#define FTS5_CONTENT_EXTERNAL 2 | |
| 234003 | +#define FTS5_CONTENT_UNINDEXED 3 | |
| 233606 | 234004 | |
| 233607 | 234005 | #define FTS5_DETAIL_FULL 0 |
| 233608 | 234006 | #define FTS5_DETAIL_NONE 1 |
| 233609 | 234007 | #define FTS5_DETAIL_COLUMNS 2 |
| 233610 | 234008 | |
| @@ -233838,11 +234236,18 @@ | ||
| 233838 | 234236 | static int sqlite3Fts5StructureTest(Fts5Index*, void*); |
| 233839 | 234237 | |
| 233840 | 234238 | /* |
| 233841 | 234239 | ** Used by xInstToken(): |
| 233842 | 234240 | */ |
| 233843 | -static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); | |
| 234241 | +static int sqlite3Fts5IterToken( | |
| 234242 | + Fts5IndexIter *pIndexIter, | |
| 234243 | + const char *pToken, int nToken, | |
| 234244 | + i64 iRowid, | |
| 234245 | + int iCol, | |
| 234246 | + int iOff, | |
| 234247 | + const char **ppOut, int *pnOut | |
| 234248 | +); | |
| 233844 | 234249 | |
| 233845 | 234250 | /* |
| 233846 | 234251 | ** Insert or remove data to or from the index. Each time a document is |
| 233847 | 234252 | ** added to or removed from the index, this function is called one or more |
| 233848 | 234253 | ** times. |
| @@ -233972,20 +234377,17 @@ | ||
| 233972 | 234377 | |
| 233973 | 234378 | static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); |
| 233974 | 234379 | |
| 233975 | 234380 | static int sqlite3Fts5FlushToDisk(Fts5Table*); |
| 233976 | 234381 | |
| 233977 | -static int sqlite3Fts5ExtractText( | |
| 233978 | - Fts5Config *pConfig, | |
| 233979 | - sqlite3_value *pVal, /* Value to extract text from */ | |
| 233980 | - int bContent, /* Loaded from content table */ | |
| 233981 | - int *pbResetTokenizer, /* OUT: True if ClearLocale() required */ | |
| 233982 | - const char **ppText, /* OUT: Pointer to text buffer */ | |
| 233983 | - int *pnText /* OUT: Size of (*ppText) in bytes */ | |
| 233984 | -); | |
| 233985 | - | |
| 233986 | 234382 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); |
| 234383 | +static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); | |
| 234384 | + | |
| 234385 | +static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); | |
| 234386 | +static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, | |
| 234387 | + const char **ppText, int *pnText, const char **ppLoc, int *pnLoc | |
| 234388 | +); | |
| 233987 | 234389 | |
| 233988 | 234390 | /* |
| 233989 | 234391 | ** End of interface to code in fts5.c. |
| 233990 | 234392 | **************************************************************************/ |
| 233991 | 234393 | |
| @@ -234063,11 +234465,11 @@ | ||
| 234063 | 234465 | |
| 234064 | 234466 | static int sqlite3Fts5DropAll(Fts5Config*); |
| 234065 | 234467 | static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); |
| 234066 | 234468 | |
| 234067 | 234469 | static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); |
| 234068 | -static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); | |
| 234470 | +static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); | |
| 234069 | 234471 | static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); |
| 234070 | 234472 | |
| 234071 | 234473 | static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); |
| 234072 | 234474 | |
| 234073 | 234475 | static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); |
| @@ -237270,10 +237672,11 @@ | ||
| 237270 | 237672 | const char *zArg, /* Argument to parse */ |
| 237271 | 237673 | char **pzErr /* OUT: Error message */ |
| 237272 | 237674 | ){ |
| 237273 | 237675 | int rc = SQLITE_OK; |
| 237274 | 237676 | int nCmd = (int)strlen(zCmd); |
| 237677 | + | |
| 237275 | 237678 | if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ |
| 237276 | 237679 | const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; |
| 237277 | 237680 | const char *p; |
| 237278 | 237681 | int bFirst = 1; |
| 237279 | 237682 | if( pConfig->aPrefix==0 ){ |
| @@ -237388,10 +237791,20 @@ | ||
| 237388 | 237791 | }else{ |
| 237389 | 237792 | pConfig->bContentlessDelete = (zArg[0]=='1'); |
| 237390 | 237793 | } |
| 237391 | 237794 | return rc; |
| 237392 | 237795 | } |
| 237796 | + | |
| 237797 | + if( sqlite3_strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){ | |
| 237798 | + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ | |
| 237799 | + *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); | |
| 237800 | + rc = SQLITE_ERROR; | |
| 237801 | + }else{ | |
| 237802 | + pConfig->bContentlessUnindexed = (zArg[0]=='1'); | |
| 237803 | + } | |
| 237804 | + return rc; | |
| 237805 | + } | |
| 237393 | 237806 | |
| 237394 | 237807 | if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ |
| 237395 | 237808 | if( pConfig->zContentRowid ){ |
| 237396 | 237809 | *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); |
| 237397 | 237810 | rc = SQLITE_ERROR; |
| @@ -237506,11 +237919,12 @@ | ||
| 237506 | 237919 | |
| 237507 | 237920 | static int fts5ConfigParseColumn( |
| 237508 | 237921 | Fts5Config *p, |
| 237509 | 237922 | char *zCol, |
| 237510 | 237923 | char *zArg, |
| 237511 | - char **pzErr | |
| 237924 | + char **pzErr, | |
| 237925 | + int *pbUnindexed | |
| 237512 | 237926 | ){ |
| 237513 | 237927 | int rc = SQLITE_OK; |
| 237514 | 237928 | if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) |
| 237515 | 237929 | || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME) |
| 237516 | 237930 | ){ |
| @@ -237517,10 +237931,11 @@ | ||
| 237517 | 237931 | *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol); |
| 237518 | 237932 | rc = SQLITE_ERROR; |
| 237519 | 237933 | }else if( zArg ){ |
| 237520 | 237934 | if( 0==sqlite3_stricmp(zArg, "unindexed") ){ |
| 237521 | 237935 | p->abUnindexed[p->nCol] = 1; |
| 237936 | + *pbUnindexed = 1; | |
| 237522 | 237937 | }else{ |
| 237523 | 237938 | *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg); |
| 237524 | 237939 | rc = SQLITE_ERROR; |
| 237525 | 237940 | } |
| 237526 | 237941 | } |
| @@ -237537,15 +237952,30 @@ | ||
| 237537 | 237952 | int rc = SQLITE_OK; |
| 237538 | 237953 | Fts5Buffer buf = {0, 0, 0}; |
| 237539 | 237954 | |
| 237540 | 237955 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); |
| 237541 | 237956 | if( p->eContent!=FTS5_CONTENT_NONE ){ |
| 237957 | + assert( p->eContent==FTS5_CONTENT_EXTERNAL | |
| 237958 | + || p->eContent==FTS5_CONTENT_NORMAL | |
| 237959 | + || p->eContent==FTS5_CONTENT_UNINDEXED | |
| 237960 | + ); | |
| 237542 | 237961 | for(i=0; i<p->nCol; i++){ |
| 237543 | 237962 | if( p->eContent==FTS5_CONTENT_EXTERNAL ){ |
| 237544 | 237963 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); |
| 237545 | - }else{ | |
| 237964 | + }else if( p->eContent==FTS5_CONTENT_NORMAL || p->abUnindexed[i] ){ | |
| 237546 | 237965 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); |
| 237966 | + }else{ | |
| 237967 | + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); | |
| 237968 | + } | |
| 237969 | + } | |
| 237970 | + } | |
| 237971 | + if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){ | |
| 237972 | + for(i=0; i<p->nCol; i++){ | |
| 237973 | + if( p->abUnindexed[i]==0 ){ | |
| 237974 | + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); | |
| 237975 | + }else{ | |
| 237976 | + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); | |
| 237547 | 237977 | } |
| 237548 | 237978 | } |
| 237549 | 237979 | } |
| 237550 | 237980 | |
| 237551 | 237981 | assert( p->zContentExprlist==0 ); |
| @@ -237575,10 +238005,11 @@ | ||
| 237575 | 238005 | ){ |
| 237576 | 238006 | int rc = SQLITE_OK; /* Return code */ |
| 237577 | 238007 | Fts5Config *pRet; /* New object to return */ |
| 237578 | 238008 | int i; |
| 237579 | 238009 | sqlite3_int64 nByte; |
| 238010 | + int bUnindexed = 0; /* True if there are one or more UNINDEXED */ | |
| 237580 | 238011 | |
| 237581 | 238012 | *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); |
| 237582 | 238013 | if( pRet==0 ) return SQLITE_NOMEM; |
| 237583 | 238014 | memset(pRet, 0, sizeof(Fts5Config)); |
| 237584 | 238015 | pRet->pGlobal = pGlobal; |
| @@ -237634,11 +238065,11 @@ | ||
| 237634 | 238065 | ALWAYS(zOne)?zOne:"", |
| 237635 | 238066 | zTwo?zTwo:"", |
| 237636 | 238067 | pzErr |
| 237637 | 238068 | ); |
| 237638 | 238069 | }else{ |
| 237639 | - rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr); | |
| 238070 | + rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr, &bUnindexed); | |
| 237640 | 238071 | zOne = 0; |
| 237641 | 238072 | } |
| 237642 | 238073 | } |
| 237643 | 238074 | } |
| 237644 | 238075 | |
| @@ -237665,18 +238096,34 @@ | ||
| 237665 | 238096 | *pzErr = sqlite3_mprintf( |
| 237666 | 238097 | "contentless_delete=1 is incompatible with columnsize=0" |
| 237667 | 238098 | ); |
| 237668 | 238099 | rc = SQLITE_ERROR; |
| 237669 | 238100 | } |
| 238101 | + | |
| 238102 | + /* We only allow contentless_unindexed=1 if the table is actually a | |
| 238103 | + ** contentless one. | |
| 238104 | + */ | |
| 238105 | + if( rc==SQLITE_OK | |
| 238106 | + && pRet->bContentlessUnindexed | |
| 238107 | + && pRet->eContent!=FTS5_CONTENT_NONE | |
| 238108 | + ){ | |
| 238109 | + *pzErr = sqlite3_mprintf( | |
| 238110 | + "contentless_unindexed=1 requires a contentless table" | |
| 238111 | + ); | |
| 238112 | + rc = SQLITE_ERROR; | |
| 238113 | + } | |
| 237670 | 238114 | |
| 237671 | 238115 | /* If no zContent option was specified, fill in the default values. */ |
| 237672 | 238116 | if( rc==SQLITE_OK && pRet->zContent==0 ){ |
| 237673 | 238117 | const char *zTail = 0; |
| 237674 | 238118 | assert( pRet->eContent==FTS5_CONTENT_NORMAL |
| 237675 | 238119 | || pRet->eContent==FTS5_CONTENT_NONE |
| 237676 | 238120 | ); |
| 237677 | 238121 | if( pRet->eContent==FTS5_CONTENT_NORMAL ){ |
| 238122 | + zTail = "content"; | |
| 238123 | + }else if( bUnindexed && pRet->bContentlessUnindexed ){ | |
| 238124 | + pRet->eContent = FTS5_CONTENT_UNINDEXED; | |
| 237678 | 238125 | zTail = "content"; |
| 237679 | 238126 | }else if( pRet->bColumnsize ){ |
| 237680 | 238127 | zTail = "docsize"; |
| 237681 | 238128 | } |
| 237682 | 238129 | |
| @@ -238010,10 +238457,23 @@ | ||
| 238010 | 238457 | if( bVal<0 ){ |
| 238011 | 238458 | *pbBadkey = 1; |
| 238012 | 238459 | }else{ |
| 238013 | 238460 | pConfig->bSecureDelete = (bVal ? 1 : 0); |
| 238014 | 238461 | } |
| 238462 | + } | |
| 238463 | + | |
| 238464 | + else if( 0==sqlite3_stricmp(zKey, "insttoken") ){ | |
| 238465 | + int bVal = -1; | |
| 238466 | + if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ | |
| 238467 | + bVal = sqlite3_value_int(pVal); | |
| 238468 | + } | |
| 238469 | + if( bVal<0 ){ | |
| 238470 | + *pbBadkey = 1; | |
| 238471 | + }else{ | |
| 238472 | + pConfig->bPrefixInsttoken = (bVal ? 1 : 0); | |
| 238473 | + } | |
| 238474 | + | |
| 238015 | 238475 | }else{ |
| 238016 | 238476 | *pbBadkey = 1; |
| 238017 | 238477 | } |
| 238018 | 238478 | return rc; |
| 238019 | 238479 | } |
| @@ -241145,11 +241605,11 @@ | ||
| 241145 | 241605 | && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 |
| 241146 | 241606 | ){ |
| 241147 | 241607 | int rc = sqlite3Fts5PoslistWriterAppend( |
| 241148 | 241608 | &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff |
| 241149 | 241609 | ); |
| 241150 | - if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){ | |
| 241610 | + if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){ | |
| 241151 | 241611 | int iCol = p->iOff>>32; |
| 241152 | 241612 | int iTokOff = p->iOff & 0x7FFFFFFF; |
| 241153 | 241613 | rc = sqlite3Fts5IndexIterWriteTokendata( |
| 241154 | 241614 | pT->pIter, pToken, nToken, iRowid, iCol, iTokOff |
| 241155 | 241615 | ); |
| @@ -241338,19 +241798,18 @@ | ||
| 241338 | 241798 | pPhrase = pExpr->apExprPhrase[iPhrase]; |
| 241339 | 241799 | if( iToken<0 || iToken>=pPhrase->nTerm ){ |
| 241340 | 241800 | return SQLITE_RANGE; |
| 241341 | 241801 | } |
| 241342 | 241802 | pTerm = &pPhrase->aTerm[iToken]; |
| 241343 | - if( pTerm->bPrefix==0 ){ | |
| 241344 | - if( pExpr->pConfig->bTokendata ){ | |
| 241345 | - rc = sqlite3Fts5IterToken( | |
| 241346 | - pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut | |
| 241347 | - ); | |
| 241348 | - }else{ | |
| 241349 | - *ppOut = pTerm->pTerm; | |
| 241350 | - *pnOut = pTerm->nFullTerm; | |
| 241351 | - } | |
| 241803 | + if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){ | |
| 241804 | + rc = sqlite3Fts5IterToken( | |
| 241805 | + pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm, | |
| 241806 | + iRowid, iCol, iOff+iToken, ppOut, pnOut | |
| 241807 | + ); | |
| 241808 | + }else{ | |
| 241809 | + *ppOut = pTerm->pTerm; | |
| 241810 | + *pnOut = pTerm->nFullTerm; | |
| 241352 | 241811 | } |
| 241353 | 241812 | return rc; |
| 241354 | 241813 | } |
| 241355 | 241814 | |
| 241356 | 241815 | /* |
| @@ -246847,10 +247306,15 @@ | ||
| 246847 | 247306 | if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ |
| 246848 | 247307 | iRet = ii; |
| 246849 | 247308 | nBest = nPercent; |
| 246850 | 247309 | } |
| 246851 | 247310 | } |
| 247311 | + | |
| 247312 | + /* If pLvl is already the input level to an ongoing merge, look no | |
| 247313 | + ** further for a merge candidate. The caller should be allowed to | |
| 247314 | + ** continue merging from pLvl first. */ | |
| 247315 | + if( pLvl->nMerge ) break; | |
| 246852 | 247316 | } |
| 246853 | 247317 | } |
| 246854 | 247318 | return iRet; |
| 246855 | 247319 | } |
| 246856 | 247320 | |
| @@ -248156,10 +248620,387 @@ | ||
| 248156 | 248620 | fts5BufferFree(&tmp); |
| 248157 | 248621 | memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); |
| 248158 | 248622 | *p1 = out; |
| 248159 | 248623 | } |
| 248160 | 248624 | |
| 248625 | + | |
| 248626 | +/* | |
| 248627 | +** Iterate through a range of entries in the FTS index, invoking the xVisit | |
| 248628 | +** callback for each of them. | |
| 248629 | +** | |
| 248630 | +** Parameter pToken points to an nToken buffer containing an FTS index term | |
| 248631 | +** (i.e. a document term with the preceding 1 byte index identifier - | |
| 248632 | +** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits | |
| 248633 | +** all entries for terms that have pToken/nToken as a prefix. If bPrefix | |
| 248634 | +** is false, then only entries with pToken/nToken as the entire key are | |
| 248635 | +** visited. | |
| 248636 | +** | |
| 248637 | +** If the current table is a tokendata=1 table, then if bPrefix is true then | |
| 248638 | +** each index term is treated separately. However, if bPrefix is false, then | |
| 248639 | +** all index terms corresponding to pToken/nToken are collapsed into a single | |
| 248640 | +** term before the callback is invoked. | |
| 248641 | +** | |
| 248642 | +** The callback invoked for each entry visited is specified by paramter xVisit. | |
| 248643 | +** Each time it is invoked, it is passed a pointer to the Fts5Index object, | |
| 248644 | +** a copy of the 7th paramter to this function (pCtx) and a pointer to the | |
| 248645 | +** iterator that indicates the current entry. If the current entry is the | |
| 248646 | +** first with a new term (i.e. different from that of the previous entry, | |
| 248647 | +** including the very first term), then the final two parameters are passed | |
| 248648 | +** a pointer to the term and its size in bytes, respectively. If the current | |
| 248649 | +** entry is not the first associated with its term, these two parameters | |
| 248650 | +** are passed 0. | |
| 248651 | +** | |
| 248652 | +** If parameter pColset is not NULL, then it is used to filter entries before | |
| 248653 | +** the callback is invoked. | |
| 248654 | +*/ | |
| 248655 | +static int fts5VisitEntries( | |
| 248656 | + Fts5Index *p, /* Fts5 index object */ | |
| 248657 | + Fts5Colset *pColset, /* Columns filter to apply, or NULL */ | |
| 248658 | + u8 *pToken, /* Buffer containing token */ | |
| 248659 | + int nToken, /* Size of buffer pToken in bytes */ | |
| 248660 | + int bPrefix, /* True for a prefix scan */ | |
| 248661 | + void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int), | |
| 248662 | + void *pCtx /* Passed as second argument to xVisit() */ | |
| 248663 | +){ | |
| 248664 | + const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0) | |
| 248665 | + | FTS5INDEX_QUERY_SKIPEMPTY | |
| 248666 | + | FTS5INDEX_QUERY_NOOUTPUT; | |
| 248667 | + Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ | |
| 248668 | + int bNewTerm = 1; | |
| 248669 | + Fts5Structure *pStruct = fts5StructureRead(p); | |
| 248670 | + | |
| 248671 | + fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); | |
| 248672 | + fts5IterSetOutputCb(&p->rc, p1); | |
| 248673 | + for( /* no-op */ ; | |
| 248674 | + fts5MultiIterEof(p, p1)==0; | |
| 248675 | + fts5MultiIterNext2(p, p1, &bNewTerm) | |
| 248676 | + ){ | |
| 248677 | + Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; | |
| 248678 | + int nNew = 0; | |
| 248679 | + const u8 *pNew = 0; | |
| 248680 | + | |
| 248681 | + p1->xSetOutputs(p1, pSeg); | |
| 248682 | + if( p->rc ) break; | |
| 248683 | + | |
| 248684 | + if( bNewTerm ){ | |
| 248685 | + nNew = pSeg->term.n; | |
| 248686 | + pNew = pSeg->term.p; | |
| 248687 | + if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break; | |
| 248688 | + } | |
| 248689 | + | |
| 248690 | + xVisit(p, pCtx, p1, pNew, nNew); | |
| 248691 | + } | |
| 248692 | + fts5MultiIterFree(p1); | |
| 248693 | + | |
| 248694 | + fts5StructureRelease(pStruct); | |
| 248695 | + return p->rc; | |
| 248696 | +} | |
| 248697 | + | |
| 248698 | + | |
| 248699 | +/* | |
| 248700 | +** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an | |
| 248701 | +** array of these for each row it visits (so all iRowid fields are the same). | |
| 248702 | +** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an | |
| 248703 | +** array of these for the entire query (in which case iRowid fields may take | |
| 248704 | +** a variety of values). | |
| 248705 | +** | |
| 248706 | +** Each instance in the array indicates the iterator (and therefore term) | |
| 248707 | +** associated with position iPos of rowid iRowid. This is used by the | |
| 248708 | +** xInstToken() API. | |
| 248709 | +** | |
| 248710 | +** iRowid: | |
| 248711 | +** Rowid for the current entry. | |
| 248712 | +** | |
| 248713 | +** iPos: | |
| 248714 | +** Position of current entry within row. In the usual ((iCol<<32)+iOff) | |
| 248715 | +** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()). | |
| 248716 | +** | |
| 248717 | +** iIter: | |
| 248718 | +** If the Fts5TokenDataIter iterator that the entry is part of is | |
| 248719 | +** actually an iterator (i.e. with nIter>0, not just a container for | |
| 248720 | +** Fts5TokenDataMap structures), then this variable is an index into | |
| 248721 | +** the apIter[] array. The corresponding term is that which the iterator | |
| 248722 | +** at apIter[iIter] currently points to. | |
| 248723 | +** | |
| 248724 | +** Or, if the Fts5TokenDataIter iterator is just a container object | |
| 248725 | +** (nIter==0), then iIter is an index into the term.p[] buffer where | |
| 248726 | +** the term is stored. | |
| 248727 | +** | |
| 248728 | +** nByte: | |
| 248729 | +** In the case where iIter is an index into term.p[], this variable | |
| 248730 | +** is the size of the term in bytes. If iIter is an index into apIter[], | |
| 248731 | +** this variable is unused. | |
| 248732 | +*/ | |
| 248733 | +struct Fts5TokenDataMap { | |
| 248734 | + i64 iRowid; /* Row this token is located in */ | |
| 248735 | + i64 iPos; /* Position of token */ | |
| 248736 | + int iIter; /* Iterator token was read from */ | |
| 248737 | + int nByte; /* Length of token in bytes (or 0) */ | |
| 248738 | +}; | |
| 248739 | + | |
| 248740 | +/* | |
| 248741 | +** An object used to supplement Fts5Iter for tokendata=1 iterators. | |
| 248742 | +** | |
| 248743 | +** This object serves two purposes. The first is as a container for an array | |
| 248744 | +** of Fts5TokenDataMap structures, which are used to find the token required | |
| 248745 | +** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and | |
| 248746 | +** aMap[] variables. | |
| 248747 | +*/ | |
| 248748 | +struct Fts5TokenDataIter { | |
| 248749 | + int nMapAlloc; /* Allocated size of aMap[] in entries */ | |
| 248750 | + int nMap; /* Number of valid entries in aMap[] */ | |
| 248751 | + Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ | |
| 248752 | + | |
| 248753 | + /* The following are used for prefix-queries only. */ | |
| 248754 | + Fts5Buffer terms; | |
| 248755 | + | |
| 248756 | + /* The following are used for other full-token tokendata queries only. */ | |
| 248757 | + int nIter; | |
| 248758 | + int nIterAlloc; | |
| 248759 | + Fts5PoslistReader *aPoslistReader; | |
| 248760 | + int *aPoslistToIter; | |
| 248761 | + Fts5Iter *apIter[1]; | |
| 248762 | +}; | |
| 248763 | + | |
| 248764 | +/* | |
| 248765 | +** The two input arrays - a1[] and a2[] - are in sorted order. This function | |
| 248766 | +** merges the two arrays together and writes the result to output array | |
| 248767 | +** aOut[]. aOut[] is guaranteed to be large enough to hold the result. | |
| 248768 | +** | |
| 248769 | +** Duplicate entries are copied into the output. So the size of the output | |
| 248770 | +** array is always (n1+n2) entries. | |
| 248771 | +*/ | |
| 248772 | +static void fts5TokendataMerge( | |
| 248773 | + Fts5TokenDataMap *a1, int n1, /* Input array 1 */ | |
| 248774 | + Fts5TokenDataMap *a2, int n2, /* Input array 2 */ | |
| 248775 | + Fts5TokenDataMap *aOut /* Output array */ | |
| 248776 | +){ | |
| 248777 | + int i1 = 0; | |
| 248778 | + int i2 = 0; | |
| 248779 | + | |
| 248780 | + assert( n1>=0 && n2>=0 ); | |
| 248781 | + while( i1<n1 || i2<n2 ){ | |
| 248782 | + Fts5TokenDataMap *pOut = &aOut[i1+i2]; | |
| 248783 | + if( i2>=n2 || (i1<n1 && ( | |
| 248784 | + a1[i1].iRowid<a2[i2].iRowid | |
| 248785 | + || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos) | |
| 248786 | + ))){ | |
| 248787 | + memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap)); | |
| 248788 | + i1++; | |
| 248789 | + }else{ | |
| 248790 | + memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap)); | |
| 248791 | + i2++; | |
| 248792 | + } | |
| 248793 | + } | |
| 248794 | +} | |
| 248795 | + | |
| 248796 | + | |
| 248797 | +/* | |
| 248798 | +** Append a mapping to the token-map belonging to object pT. | |
| 248799 | +*/ | |
| 248800 | +static void fts5TokendataIterAppendMap( | |
| 248801 | + Fts5Index *p, | |
| 248802 | + Fts5TokenDataIter *pT, | |
| 248803 | + int iIter, | |
| 248804 | + int nByte, | |
| 248805 | + i64 iRowid, | |
| 248806 | + i64 iPos | |
| 248807 | +){ | |
| 248808 | + if( p->rc==SQLITE_OK ){ | |
| 248809 | + if( pT->nMap==pT->nMapAlloc ){ | |
| 248810 | + int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; | |
| 248811 | + int nAlloc = nNew * sizeof(Fts5TokenDataMap); | |
| 248812 | + Fts5TokenDataMap *aNew; | |
| 248813 | + | |
| 248814 | + aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc); | |
| 248815 | + if( aNew==0 ){ | |
| 248816 | + p->rc = SQLITE_NOMEM; | |
| 248817 | + return; | |
| 248818 | + } | |
| 248819 | + | |
| 248820 | + pT->aMap = aNew; | |
| 248821 | + pT->nMapAlloc = nNew; | |
| 248822 | + } | |
| 248823 | + | |
| 248824 | + pT->aMap[pT->nMap].iRowid = iRowid; | |
| 248825 | + pT->aMap[pT->nMap].iPos = iPos; | |
| 248826 | + pT->aMap[pT->nMap].iIter = iIter; | |
| 248827 | + pT->aMap[pT->nMap].nByte = nByte; | |
| 248828 | + pT->nMap++; | |
| 248829 | + } | |
| 248830 | +} | |
| 248831 | + | |
| 248832 | +/* | |
| 248833 | +** Sort the contents of the pT->aMap[] array. | |
| 248834 | +** | |
| 248835 | +** The sorting algorithm requries a malloc(). If this fails, an error code | |
| 248836 | +** is left in Fts5Index.rc before returning. | |
| 248837 | +*/ | |
| 248838 | +static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ | |
| 248839 | + Fts5TokenDataMap *aTmp = 0; | |
| 248840 | + int nByte = pT->nMap * sizeof(Fts5TokenDataMap); | |
| 248841 | + | |
| 248842 | + aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); | |
| 248843 | + if( aTmp ){ | |
| 248844 | + Fts5TokenDataMap *a1 = pT->aMap; | |
| 248845 | + Fts5TokenDataMap *a2 = aTmp; | |
| 248846 | + i64 nHalf; | |
| 248847 | + | |
| 248848 | + for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){ | |
| 248849 | + int i1; | |
| 248850 | + for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){ | |
| 248851 | + int n1 = MIN(nHalf, pT->nMap-i1); | |
| 248852 | + int n2 = MIN(nHalf, pT->nMap-i1-n1); | |
| 248853 | + fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]); | |
| 248854 | + } | |
| 248855 | + SWAPVAL(Fts5TokenDataMap*, a1, a2); | |
| 248856 | + } | |
| 248857 | + | |
| 248858 | + if( a1!=pT->aMap ){ | |
| 248859 | + memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap)); | |
| 248860 | + } | |
| 248861 | + sqlite3_free(aTmp); | |
| 248862 | + | |
| 248863 | +#ifdef SQLITE_DEBUG | |
| 248864 | + { | |
| 248865 | + int ii; | |
| 248866 | + for(ii=1; ii<pT->nMap; ii++){ | |
| 248867 | + Fts5TokenDataMap *p1 = &pT->aMap[ii-1]; | |
| 248868 | + Fts5TokenDataMap *p2 = &pT->aMap[ii]; | |
| 248869 | + assert( p1->iRowid<p2->iRowid | |
| 248870 | + || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos) | |
| 248871 | + ); | |
| 248872 | + } | |
| 248873 | + } | |
| 248874 | +#endif | |
| 248875 | + } | |
| 248876 | +} | |
| 248877 | + | |
| 248878 | +/* | |
| 248879 | +** Delete an Fts5TokenDataIter structure and its contents. | |
| 248880 | +*/ | |
| 248881 | +static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ | |
| 248882 | + if( pSet ){ | |
| 248883 | + int ii; | |
| 248884 | + for(ii=0; ii<pSet->nIter; ii++){ | |
| 248885 | + fts5MultiIterFree(pSet->apIter[ii]); | |
| 248886 | + } | |
| 248887 | + fts5BufferFree(&pSet->terms); | |
| 248888 | + sqlite3_free(pSet->aPoslistReader); | |
| 248889 | + sqlite3_free(pSet->aMap); | |
| 248890 | + sqlite3_free(pSet); | |
| 248891 | + } | |
| 248892 | +} | |
| 248893 | + | |
| 248894 | + | |
| 248895 | +/* | |
| 248896 | +** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata() | |
| 248897 | +** to pass data to prefixIterSetupTokendataCb(). | |
| 248898 | +*/ | |
| 248899 | +typedef struct TokendataSetupCtx TokendataSetupCtx; | |
| 248900 | +struct TokendataSetupCtx { | |
| 248901 | + Fts5TokenDataIter *pT; /* Object being populated with mappings */ | |
| 248902 | + int iTermOff; /* Offset of current term in terms.p[] */ | |
| 248903 | + int nTermByte; /* Size of current term in bytes */ | |
| 248904 | +}; | |
| 248905 | + | |
| 248906 | +/* | |
| 248907 | +** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This | |
| 248908 | +** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each | |
| 248909 | +** position in the current position-list. It doesn't matter that some of | |
| 248910 | +** these may be out of order - they will be sorted later. | |
| 248911 | +*/ | |
| 248912 | +static void prefixIterSetupTokendataCb( | |
| 248913 | + Fts5Index *p, | |
| 248914 | + void *pCtx, | |
| 248915 | + Fts5Iter *p1, | |
| 248916 | + const u8 *pNew, | |
| 248917 | + int nNew | |
| 248918 | +){ | |
| 248919 | + TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx; | |
| 248920 | + int iPosOff = 0; | |
| 248921 | + i64 iPos = 0; | |
| 248922 | + | |
| 248923 | + if( pNew ){ | |
| 248924 | + pSetup->nTermByte = nNew-1; | |
| 248925 | + pSetup->iTermOff = pSetup->pT->terms.n; | |
| 248926 | + fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1); | |
| 248927 | + } | |
| 248928 | + | |
| 248929 | + while( 0==sqlite3Fts5PoslistNext64( | |
| 248930 | + p1->base.pData, p1->base.nData, &iPosOff, &iPos | |
| 248931 | + ) ){ | |
| 248932 | + fts5TokendataIterAppendMap(p, | |
| 248933 | + pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos | |
| 248934 | + ); | |
| 248935 | + } | |
| 248936 | +} | |
| 248937 | + | |
| 248938 | + | |
| 248939 | +/* | |
| 248940 | +** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries(). | |
| 248941 | +*/ | |
| 248942 | +typedef struct PrefixSetupCtx PrefixSetupCtx; | |
| 248943 | +struct PrefixSetupCtx { | |
| 248944 | + void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); | |
| 248945 | + void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); | |
| 248946 | + i64 iLastRowid; | |
| 248947 | + int nMerge; | |
| 248948 | + Fts5Buffer *aBuf; | |
| 248949 | + int nBuf; | |
| 248950 | + Fts5Buffer doclist; | |
| 248951 | + TokendataSetupCtx *pTokendata; | |
| 248952 | +}; | |
| 248953 | + | |
| 248954 | +/* | |
| 248955 | +** fts5VisitEntries() callback used by fts5SetupPrefixIter() | |
| 248956 | +*/ | |
| 248957 | +static void prefixIterSetupCb( | |
| 248958 | + Fts5Index *p, | |
| 248959 | + void *pCtx, | |
| 248960 | + Fts5Iter *p1, | |
| 248961 | + const u8 *pNew, | |
| 248962 | + int nNew | |
| 248963 | +){ | |
| 248964 | + PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx; | |
| 248965 | + const int nMerge = pSetup->nMerge; | |
| 248966 | + | |
| 248967 | + if( p1->base.nData>0 ){ | |
| 248968 | + if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){ | |
| 248969 | + int i; | |
| 248970 | + for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){ | |
| 248971 | + int i1 = i*nMerge; | |
| 248972 | + int iStore; | |
| 248973 | + assert( i1+nMerge<=pSetup->nBuf ); | |
| 248974 | + for(iStore=i1; iStore<i1+nMerge; iStore++){ | |
| 248975 | + if( pSetup->aBuf[iStore].n==0 ){ | |
| 248976 | + fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]); | |
| 248977 | + fts5BufferZero(&pSetup->doclist); | |
| 248978 | + break; | |
| 248979 | + } | |
| 248980 | + } | |
| 248981 | + if( iStore==i1+nMerge ){ | |
| 248982 | + pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]); | |
| 248983 | + for(iStore=i1; iStore<i1+nMerge; iStore++){ | |
| 248984 | + fts5BufferZero(&pSetup->aBuf[iStore]); | |
| 248985 | + } | |
| 248986 | + } | |
| 248987 | + } | |
| 248988 | + pSetup->iLastRowid = 0; | |
| 248989 | + } | |
| 248990 | + | |
| 248991 | + pSetup->xAppend( | |
| 248992 | + p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist | |
| 248993 | + ); | |
| 248994 | + pSetup->iLastRowid = p1->base.iRowid; | |
| 248995 | + } | |
| 248996 | + | |
| 248997 | + if( pSetup->pTokendata ){ | |
| 248998 | + prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew); | |
| 248999 | + } | |
| 249000 | +} | |
| 249001 | + | |
| 248161 | 249002 | static void fts5SetupPrefixIter( |
| 248162 | 249003 | Fts5Index *p, /* Index to read from */ |
| 248163 | 249004 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 248164 | 249005 | int iIdx, /* Index to scan for data */ |
| 248165 | 249006 | u8 *pToken, /* Buffer containing prefix to match */ |
| @@ -248166,137 +249007,89 @@ | ||
| 248166 | 249007 | int nToken, /* Size of buffer pToken in bytes */ |
| 248167 | 249008 | Fts5Colset *pColset, /* Restrict matches to these columns */ |
| 248168 | 249009 | Fts5Iter **ppIter /* OUT: New iterator */ |
| 248169 | 249010 | ){ |
| 248170 | 249011 | Fts5Structure *pStruct; |
| 248171 | - Fts5Buffer *aBuf; | |
| 248172 | - int nBuf = 32; | |
| 248173 | - int nMerge = 1; | |
| 249012 | + PrefixSetupCtx s; | |
| 249013 | + TokendataSetupCtx s2; | |
| 248174 | 249014 | |
| 248175 | - void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); | |
| 248176 | - void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); | |
| 249015 | + memset(&s, 0, sizeof(s)); | |
| 249016 | + memset(&s2, 0, sizeof(s2)); | |
| 249017 | + | |
| 249018 | + s.nMerge = 1; | |
| 249019 | + s.iLastRowid = 0; | |
| 249020 | + s.nBuf = 32; | |
| 249021 | + if( iIdx==0 | |
| 249022 | + && p->pConfig->eDetail==FTS5_DETAIL_FULL | |
| 249023 | + && p->pConfig->bPrefixInsttoken | |
| 249024 | + ){ | |
| 249025 | + s.pTokendata = &s2; | |
| 249026 | + s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); | |
| 249027 | + } | |
| 249028 | + | |
| 248177 | 249029 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 248178 | - xMerge = fts5MergeRowidLists; | |
| 248179 | - xAppend = fts5AppendRowid; | |
| 249030 | + s.xMerge = fts5MergeRowidLists; | |
| 249031 | + s.xAppend = fts5AppendRowid; | |
| 248180 | 249032 | }else{ |
| 248181 | - nMerge = FTS5_MERGE_NLIST-1; | |
| 248182 | - nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ | |
| 248183 | - xMerge = fts5MergePrefixLists; | |
| 248184 | - xAppend = fts5AppendPoslist; | |
| 249033 | + s.nMerge = FTS5_MERGE_NLIST-1; | |
| 249034 | + s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ | |
| 249035 | + s.xMerge = fts5MergePrefixLists; | |
| 249036 | + s.xAppend = fts5AppendPoslist; | |
| 248185 | 249037 | } |
| 248186 | 249038 | |
| 248187 | - aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); | |
| 249039 | + s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf); | |
| 248188 | 249040 | pStruct = fts5StructureRead(p); |
| 248189 | - assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); | |
| 249041 | + assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) ); | |
| 248190 | 249042 | |
| 248191 | 249043 | if( p->rc==SQLITE_OK ){ |
| 248192 | - const int flags = FTS5INDEX_QUERY_SCAN | |
| 248193 | - | FTS5INDEX_QUERY_SKIPEMPTY | |
| 248194 | - | FTS5INDEX_QUERY_NOOUTPUT; | |
| 249044 | + void *pCtx = (void*)&s; | |
| 248195 | 249045 | int i; |
| 248196 | - i64 iLastRowid = 0; | |
| 248197 | - Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ | |
| 248198 | 249046 | Fts5Data *pData; |
| 248199 | - Fts5Buffer doclist; | |
| 248200 | - int bNewTerm = 1; | |
| 248201 | - | |
| 248202 | - memset(&doclist, 0, sizeof(doclist)); | |
| 248203 | 249047 | |
| 248204 | 249048 | /* If iIdx is non-zero, then it is the number of a prefix-index for |
| 248205 | 249049 | ** prefixes 1 character longer than the prefix being queried for. That |
| 248206 | 249050 | ** index contains all the doclists required, except for the one |
| 248207 | 249051 | ** corresponding to the prefix itself. That one is extracted from the |
| 248208 | 249052 | ** main term index here. */ |
| 248209 | 249053 | if( iIdx!=0 ){ |
| 248210 | - int dummy = 0; | |
| 248211 | - const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; | |
| 248212 | 249054 | pToken[0] = FTS5_MAIN_PREFIX; |
| 248213 | - fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1); | |
| 248214 | - fts5IterSetOutputCb(&p->rc, p1); | |
| 248215 | - for(; | |
| 248216 | - fts5MultiIterEof(p, p1)==0; | |
| 248217 | - fts5MultiIterNext2(p, p1, &dummy) | |
| 248218 | - ){ | |
| 248219 | - Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; | |
| 248220 | - p1->xSetOutputs(p1, pSeg); | |
| 248221 | - if( p1->base.nData ){ | |
| 248222 | - xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); | |
| 248223 | - iLastRowid = p1->base.iRowid; | |
| 248224 | - } | |
| 248225 | - } | |
| 248226 | - fts5MultiIterFree(p1); | |
| 249055 | + fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx); | |
| 248227 | 249056 | } |
| 248228 | 249057 | |
| 248229 | 249058 | pToken[0] = FTS5_MAIN_PREFIX + iIdx; |
| 248230 | - fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); | |
| 248231 | - fts5IterSetOutputCb(&p->rc, p1); | |
| 248232 | - | |
| 248233 | - for( /* no-op */ ; | |
| 248234 | - fts5MultiIterEof(p, p1)==0; | |
| 248235 | - fts5MultiIterNext2(p, p1, &bNewTerm) | |
| 248236 | - ){ | |
| 248237 | - Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; | |
| 248238 | - int nTerm = pSeg->term.n; | |
| 248239 | - const u8 *pTerm = pSeg->term.p; | |
| 248240 | - p1->xSetOutputs(p1, pSeg); | |
| 248241 | - | |
| 248242 | - assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); | |
| 248243 | - if( bNewTerm ){ | |
| 248244 | - if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; | |
| 248245 | - } | |
| 248246 | - | |
| 248247 | - if( p1->base.nData==0 ) continue; | |
| 248248 | - if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ | |
| 248249 | - for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ | |
| 248250 | - int i1 = i*nMerge; | |
| 248251 | - int iStore; | |
| 248252 | - assert( i1+nMerge<=nBuf ); | |
| 248253 | - for(iStore=i1; iStore<i1+nMerge; iStore++){ | |
| 248254 | - if( aBuf[iStore].n==0 ){ | |
| 248255 | - fts5BufferSwap(&doclist, &aBuf[iStore]); | |
| 248256 | - fts5BufferZero(&doclist); | |
| 248257 | - break; | |
| 248258 | - } | |
| 248259 | - } | |
| 248260 | - if( iStore==i1+nMerge ){ | |
| 248261 | - xMerge(p, &doclist, nMerge, &aBuf[i1]); | |
| 248262 | - for(iStore=i1; iStore<i1+nMerge; iStore++){ | |
| 248263 | - fts5BufferZero(&aBuf[iStore]); | |
| 248264 | - } | |
| 248265 | - } | |
| 248266 | - } | |
| 248267 | - iLastRowid = 0; | |
| 248268 | - } | |
| 248269 | - | |
| 248270 | - xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); | |
| 248271 | - iLastRowid = p1->base.iRowid; | |
| 248272 | - } | |
| 248273 | - | |
| 248274 | - assert( (nBuf%nMerge)==0 ); | |
| 248275 | - for(i=0; i<nBuf; i+=nMerge){ | |
| 249059 | + fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx); | |
| 249060 | + | |
| 249061 | + assert( (s.nBuf%s.nMerge)==0 ); | |
| 249062 | + for(i=0; i<s.nBuf; i+=s.nMerge){ | |
| 248276 | 249063 | int iFree; |
| 248277 | 249064 | if( p->rc==SQLITE_OK ){ |
| 248278 | - xMerge(p, &doclist, nMerge, &aBuf[i]); | |
| 249065 | + s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]); | |
| 248279 | 249066 | } |
| 248280 | - for(iFree=i; iFree<i+nMerge; iFree++){ | |
| 248281 | - fts5BufferFree(&aBuf[iFree]); | |
| 249067 | + for(iFree=i; iFree<i+s.nMerge; iFree++){ | |
| 249068 | + fts5BufferFree(&s.aBuf[iFree]); | |
| 248282 | 249069 | } |
| 248283 | 249070 | } |
| 248284 | - fts5MultiIterFree(p1); | |
| 248285 | 249071 | |
| 248286 | - pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING); | |
| 249072 | + pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING); | |
| 248287 | 249073 | if( pData ){ |
| 248288 | 249074 | pData->p = (u8*)&pData[1]; |
| 248289 | - pData->nn = pData->szLeaf = doclist.n; | |
| 248290 | - if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); | |
| 249075 | + pData->nn = pData->szLeaf = s.doclist.n; | |
| 249076 | + if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n); | |
| 248291 | 249077 | fts5MultiIterNew2(p, pData, bDesc, ppIter); |
| 248292 | 249078 | } |
| 248293 | - fts5BufferFree(&doclist); | |
| 249079 | + | |
| 249080 | + if( p->rc==SQLITE_OK && s.pTokendata ){ | |
| 249081 | + fts5TokendataIterSortMap(p, s2.pT); | |
| 249082 | + (*ppIter)->pTokenDataIter = s2.pT; | |
| 249083 | + s2.pT = 0; | |
| 249084 | + } | |
| 248294 | 249085 | } |
| 248295 | 249086 | |
| 249087 | + fts5TokendataIterDelete(s2.pT); | |
| 249088 | + fts5BufferFree(&s.doclist); | |
| 248296 | 249089 | fts5StructureRelease(pStruct); |
| 248297 | - sqlite3_free(aBuf); | |
| 249090 | + sqlite3_free(s.aBuf); | |
| 248298 | 249091 | } |
| 248299 | 249092 | |
| 248300 | 249093 | |
| 248301 | 249094 | /* |
| 248302 | 249095 | ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain |
| @@ -248546,42 +249339,10 @@ | ||
| 248546 | 249339 | static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ |
| 248547 | 249340 | fts5DataRelease(pSeg->pLeaf); |
| 248548 | 249341 | pSeg->pLeaf = 0; |
| 248549 | 249342 | } |
| 248550 | 249343 | |
| 248551 | -/* | |
| 248552 | -** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an | |
| 248553 | -** array of these for each row it visits. Or, for an iterator used by an | |
| 248554 | -** "ORDER BY rank" query, it accumulates an array of these for the entire | |
| 248555 | -** query. | |
| 248556 | -** | |
| 248557 | -** Each instance in the array indicates the iterator (and therefore term) | |
| 248558 | -** associated with position iPos of rowid iRowid. This is used by the | |
| 248559 | -** xInstToken() API. | |
| 248560 | -*/ | |
| 248561 | -struct Fts5TokenDataMap { | |
| 248562 | - i64 iRowid; /* Row this token is located in */ | |
| 248563 | - i64 iPos; /* Position of token */ | |
| 248564 | - int iIter; /* Iterator token was read from */ | |
| 248565 | -}; | |
| 248566 | - | |
| 248567 | -/* | |
| 248568 | -** An object used to supplement Fts5Iter for tokendata=1 iterators. | |
| 248569 | -*/ | |
| 248570 | -struct Fts5TokenDataIter { | |
| 248571 | - int nIter; | |
| 248572 | - int nIterAlloc; | |
| 248573 | - | |
| 248574 | - int nMap; | |
| 248575 | - int nMapAlloc; | |
| 248576 | - Fts5TokenDataMap *aMap; | |
| 248577 | - | |
| 248578 | - Fts5PoslistReader *aPoslistReader; | |
| 248579 | - int *aPoslistToIter; | |
| 248580 | - Fts5Iter *apIter[1]; | |
| 248581 | -}; | |
| 248582 | - | |
| 248583 | 249344 | /* |
| 248584 | 249345 | ** This function appends iterator pAppend to Fts5TokenDataIter pIn and |
| 248585 | 249346 | ** returns the result. |
| 248586 | 249347 | */ |
| 248587 | 249348 | static Fts5TokenDataIter *fts5AppendTokendataIter( |
| @@ -248614,58 +249375,10 @@ | ||
| 248614 | 249375 | assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); |
| 248615 | 249376 | |
| 248616 | 249377 | return pRet; |
| 248617 | 249378 | } |
| 248618 | 249379 | |
| 248619 | -/* | |
| 248620 | -** Delete an Fts5TokenDataIter structure and its contents. | |
| 248621 | -*/ | |
| 248622 | -static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ | |
| 248623 | - if( pSet ){ | |
| 248624 | - int ii; | |
| 248625 | - for(ii=0; ii<pSet->nIter; ii++){ | |
| 248626 | - fts5MultiIterFree(pSet->apIter[ii]); | |
| 248627 | - } | |
| 248628 | - sqlite3_free(pSet->aPoslistReader); | |
| 248629 | - sqlite3_free(pSet->aMap); | |
| 248630 | - sqlite3_free(pSet); | |
| 248631 | - } | |
| 248632 | -} | |
| 248633 | - | |
| 248634 | -/* | |
| 248635 | -** Append a mapping to the token-map belonging to object pT. | |
| 248636 | -*/ | |
| 248637 | -static void fts5TokendataIterAppendMap( | |
| 248638 | - Fts5Index *p, | |
| 248639 | - Fts5TokenDataIter *pT, | |
| 248640 | - int iIter, | |
| 248641 | - i64 iRowid, | |
| 248642 | - i64 iPos | |
| 248643 | -){ | |
| 248644 | - if( p->rc==SQLITE_OK ){ | |
| 248645 | - if( pT->nMap==pT->nMapAlloc ){ | |
| 248646 | - int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; | |
| 248647 | - int nByte = nNew * sizeof(Fts5TokenDataMap); | |
| 248648 | - Fts5TokenDataMap *aNew; | |
| 248649 | - | |
| 248650 | - aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte); | |
| 248651 | - if( aNew==0 ){ | |
| 248652 | - p->rc = SQLITE_NOMEM; | |
| 248653 | - return; | |
| 248654 | - } | |
| 248655 | - | |
| 248656 | - pT->aMap = aNew; | |
| 248657 | - pT->nMapAlloc = nNew; | |
| 248658 | - } | |
| 248659 | - | |
| 248660 | - pT->aMap[pT->nMap].iRowid = iRowid; | |
| 248661 | - pT->aMap[pT->nMap].iPos = iPos; | |
| 248662 | - pT->aMap[pT->nMap].iIter = iIter; | |
| 248663 | - pT->nMap++; | |
| 248664 | - } | |
| 248665 | -} | |
| 248666 | - | |
| 248667 | 249380 | /* |
| 248668 | 249381 | ** The iterator passed as the only argument must be a tokendata=1 iterator |
| 248669 | 249382 | ** (pIter->pTokenDataIter!=0). This function sets the iterator output |
| 248670 | 249383 | ** variables (pIter->base.*) according to the contents of the current |
| 248671 | 249384 | ** row. |
| @@ -248702,11 +249415,11 @@ | ||
| 248702 | 249415 | int eDetail = pIter->pIndex->pConfig->eDetail; |
| 248703 | 249416 | pIter->base.bEof = 0; |
| 248704 | 249417 | pIter->base.iRowid = iRowid; |
| 248705 | 249418 | |
| 248706 | 249419 | if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ |
| 248707 | - fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1); | |
| 249420 | + fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1); | |
| 248708 | 249421 | }else |
| 248709 | 249422 | if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ |
| 248710 | 249423 | int nReader = 0; |
| 248711 | 249424 | int nByte = 0; |
| 248712 | 249425 | i64 iPrev = 0; |
| @@ -248955,10 +249668,11 @@ | ||
| 248955 | 249668 | |
| 248956 | 249669 | if( p->rc==SQLITE_OK ){ |
| 248957 | 249670 | pRet = fts5MultiIterAlloc(p, 0); |
| 248958 | 249671 | } |
| 248959 | 249672 | if( pRet ){ |
| 249673 | + pRet->nSeg = 0; | |
| 248960 | 249674 | pRet->pTokenDataIter = pSet; |
| 248961 | 249675 | if( pSet ){ |
| 248962 | 249676 | fts5IterSetOutputsTokendata(pRet); |
| 248963 | 249677 | }else{ |
| 248964 | 249678 | pRet->base.bEof = 1; |
| @@ -248969,11 +249683,10 @@ | ||
| 248969 | 249683 | |
| 248970 | 249684 | fts5StructureRelease(pStruct); |
| 248971 | 249685 | fts5BufferFree(&bSeek); |
| 248972 | 249686 | return pRet; |
| 248973 | 249687 | } |
| 248974 | - | |
| 248975 | 249688 | |
| 248976 | 249689 | /* |
| 248977 | 249690 | ** Open a new iterator to iterate though all rowid that match the |
| 248978 | 249691 | ** specified token or token prefix. |
| 248979 | 249692 | */ |
| @@ -248995,10 +249708,15 @@ | ||
| 248995 | 249708 | int iIdx = 0; /* Index to search */ |
| 248996 | 249709 | int iPrefixIdx = 0; /* +1 prefix index */ |
| 248997 | 249710 | int bTokendata = pConfig->bTokendata; |
| 248998 | 249711 | if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); |
| 248999 | 249712 | |
| 249713 | + /* The NOTOKENDATA flag is set when each token in a tokendata=1 table | |
| 249714 | + ** should be treated individually, instead of merging all those with | |
| 249715 | + ** a common prefix into a single entry. This is used, for example, by | |
| 249716 | + ** queries performed as part of an integrity-check, or by the fts5vocab | |
| 249717 | + ** module. */ | |
| 249000 | 249718 | if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ |
| 249001 | 249719 | bTokendata = 0; |
| 249002 | 249720 | } |
| 249003 | 249721 | |
| 249004 | 249722 | /* Figure out which index to search and set iIdx accordingly. If this |
| @@ -249025,11 +249743,11 @@ | ||
| 249025 | 249743 | if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; |
| 249026 | 249744 | } |
| 249027 | 249745 | } |
| 249028 | 249746 | |
| 249029 | 249747 | if( bTokendata && iIdx==0 ){ |
| 249030 | - buf.p[0] = '0'; | |
| 249748 | + buf.p[0] = FTS5_MAIN_PREFIX; | |
| 249031 | 249749 | pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); |
| 249032 | 249750 | }else if( iIdx<=pConfig->nPrefix ){ |
| 249033 | 249751 | /* Straight index lookup */ |
| 249034 | 249752 | Fts5Structure *pStruct = fts5StructureRead(p); |
| 249035 | 249753 | buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); |
| @@ -249038,11 +249756,11 @@ | ||
| 249038 | 249756 | pColset, buf.p, nToken+1, -1, 0, &pRet |
| 249039 | 249757 | ); |
| 249040 | 249758 | fts5StructureRelease(pStruct); |
| 249041 | 249759 | } |
| 249042 | 249760 | }else{ |
| 249043 | - /* Scan multiple terms in the main index */ | |
| 249761 | + /* Scan multiple terms in the main index for a prefix query. */ | |
| 249044 | 249762 | int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; |
| 249045 | 249763 | fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); |
| 249046 | 249764 | if( pRet==0 ){ |
| 249047 | 249765 | assert( p->rc!=SQLITE_OK ); |
| 249048 | 249766 | }else{ |
| @@ -249074,11 +249792,12 @@ | ||
| 249074 | 249792 | ** Move to the next matching rowid. |
| 249075 | 249793 | */ |
| 249076 | 249794 | static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ |
| 249077 | 249795 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249078 | 249796 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 249079 | - if( pIter->pTokenDataIter ){ | |
| 249797 | + if( pIter->nSeg==0 ){ | |
| 249798 | + assert( pIter->pTokenDataIter ); | |
| 249080 | 249799 | fts5TokendataIterNext(pIter, 0, 0); |
| 249081 | 249800 | }else{ |
| 249082 | 249801 | fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); |
| 249083 | 249802 | } |
| 249084 | 249803 | return fts5IndexReturn(pIter->pIndex); |
| @@ -249111,11 +249830,12 @@ | ||
| 249111 | 249830 | ** definition of "at or after" depends on whether this iterator iterates |
| 249112 | 249831 | ** in ascending or descending rowid order. |
| 249113 | 249832 | */ |
| 249114 | 249833 | static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ |
| 249115 | 249834 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249116 | - if( pIter->pTokenDataIter ){ | |
| 249835 | + if( pIter->nSeg==0 ){ | |
| 249836 | + assert( pIter->pTokenDataIter ); | |
| 249117 | 249837 | fts5TokendataIterNext(pIter, 1, iMatch); |
| 249118 | 249838 | }else{ |
| 249119 | 249839 | fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); |
| 249120 | 249840 | } |
| 249121 | 249841 | return fts5IndexReturn(pIter->pIndex); |
| @@ -249129,32 +249849,87 @@ | ||
| 249129 | 249849 | const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); |
| 249130 | 249850 | assert_nc( z || n<=1 ); |
| 249131 | 249851 | *pn = n-1; |
| 249132 | 249852 | return (z ? &z[1] : 0); |
| 249133 | 249853 | } |
| 249854 | + | |
| 249855 | +/* | |
| 249856 | +** pIter is a prefix query. This function populates pIter->pTokenDataIter | |
| 249857 | +** with an Fts5TokenDataIter object containing mappings for all rows | |
| 249858 | +** matched by the query. | |
| 249859 | +*/ | |
| 249860 | +static int fts5SetupPrefixIterTokendata( | |
| 249861 | + Fts5Iter *pIter, | |
| 249862 | + const char *pToken, /* Token prefix to search for */ | |
| 249863 | + int nToken /* Size of pToken in bytes */ | |
| 249864 | +){ | |
| 249865 | + Fts5Index *p = pIter->pIndex; | |
| 249866 | + Fts5Buffer token = {0, 0, 0}; | |
| 249867 | + TokendataSetupCtx ctx; | |
| 249868 | + | |
| 249869 | + memset(&ctx, 0, sizeof(ctx)); | |
| 249870 | + | |
| 249871 | + fts5BufferGrow(&p->rc, &token, nToken+1); | |
| 249872 | + ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); | |
| 249873 | + | |
| 249874 | + if( p->rc==SQLITE_OK ){ | |
| 249875 | + | |
| 249876 | + /* Fill in the token prefix to search for */ | |
| 249877 | + token.p[0] = FTS5_MAIN_PREFIX; | |
| 249878 | + memcpy(&token.p[1], pToken, nToken); | |
| 249879 | + token.n = nToken+1; | |
| 249880 | + | |
| 249881 | + fts5VisitEntries( | |
| 249882 | + p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx | |
| 249883 | + ); | |
| 249884 | + | |
| 249885 | + fts5TokendataIterSortMap(p, ctx.pT); | |
| 249886 | + } | |
| 249887 | + | |
| 249888 | + if( p->rc==SQLITE_OK ){ | |
| 249889 | + pIter->pTokenDataIter = ctx.pT; | |
| 249890 | + }else{ | |
| 249891 | + fts5TokendataIterDelete(ctx.pT); | |
| 249892 | + } | |
| 249893 | + fts5BufferFree(&token); | |
| 249894 | + | |
| 249895 | + return fts5IndexReturn(p); | |
| 249896 | +} | |
| 249134 | 249897 | |
| 249135 | 249898 | /* |
| 249136 | 249899 | ** This is used by xInstToken() to access the token at offset iOff, column |
| 249137 | 249900 | ** iCol of row iRowid. The token is returned via output variables *ppOut |
| 249138 | 249901 | ** and *pnOut. The iterator passed as the first argument must be a tokendata=1 |
| 249139 | 249902 | ** iterator (pIter->pTokenDataIter!=0). |
| 249903 | +** | |
| 249904 | +** pToken/nToken: | |
| 249140 | 249905 | */ |
| 249141 | 249906 | static int sqlite3Fts5IterToken( |
| 249142 | 249907 | Fts5IndexIter *pIndexIter, |
| 249908 | + const char *pToken, int nToken, | |
| 249143 | 249909 | i64 iRowid, |
| 249144 | 249910 | int iCol, |
| 249145 | 249911 | int iOff, |
| 249146 | 249912 | const char **ppOut, int *pnOut |
| 249147 | 249913 | ){ |
| 249148 | 249914 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249149 | 249915 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249150 | - Fts5TokenDataMap *aMap = pT->aMap; | |
| 249151 | 249916 | i64 iPos = (((i64)iCol)<<32) + iOff; |
| 249152 | - | |
| 249917 | + Fts5TokenDataMap *aMap = 0; | |
| 249153 | 249918 | int i1 = 0; |
| 249154 | - int i2 = pT->nMap; | |
| 249919 | + int i2 = 0; | |
| 249155 | 249920 | int iTest = 0; |
| 249921 | + | |
| 249922 | + assert( pT || (pToken && pIter->nSeg>0) ); | |
| 249923 | + if( pT==0 ){ | |
| 249924 | + int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken); | |
| 249925 | + if( rc!=SQLITE_OK ) return rc; | |
| 249926 | + pT = pIter->pTokenDataIter; | |
| 249927 | + } | |
| 249928 | + | |
| 249929 | + i2 = pT->nMap; | |
| 249930 | + aMap = pT->aMap; | |
| 249156 | 249931 | |
| 249157 | 249932 | while( i2>i1 ){ |
| 249158 | 249933 | iTest = (i1 + i2) / 2; |
| 249159 | 249934 | |
| 249160 | 249935 | if( aMap[iTest].iRowid<iRowid ){ |
| @@ -249174,13 +249949,19 @@ | ||
| 249174 | 249949 | } |
| 249175 | 249950 | } |
| 249176 | 249951 | } |
| 249177 | 249952 | |
| 249178 | 249953 | if( i2>i1 ){ |
| 249179 | - Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; | |
| 249180 | - *ppOut = (const char*)pMap->aSeg[0].term.p+1; | |
| 249181 | - *pnOut = pMap->aSeg[0].term.n-1; | |
| 249954 | + if( pIter->nSeg==0 ){ | |
| 249955 | + Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; | |
| 249956 | + *ppOut = (const char*)pMap->aSeg[0].term.p+1; | |
| 249957 | + *pnOut = pMap->aSeg[0].term.n-1; | |
| 249958 | + }else{ | |
| 249959 | + Fts5TokenDataMap *p = &aMap[iTest]; | |
| 249960 | + *ppOut = (const char*)&pT->terms.p[p->iIter]; | |
| 249961 | + *pnOut = aMap[iTest].nByte; | |
| 249962 | + } | |
| 249182 | 249963 | } |
| 249183 | 249964 | |
| 249184 | 249965 | return SQLITE_OK; |
| 249185 | 249966 | } |
| 249186 | 249967 | |
| @@ -249188,11 +249969,13 @@ | ||
| 249188 | 249969 | ** Clear any existing entries from the token-map associated with the |
| 249189 | 249970 | ** iterator passed as the only argument. |
| 249190 | 249971 | */ |
| 249191 | 249972 | static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ |
| 249192 | 249973 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249193 | - if( pIter && pIter->pTokenDataIter ){ | |
| 249974 | + if( pIter && pIter->pTokenDataIter | |
| 249975 | + && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL) | |
| 249976 | + ){ | |
| 249194 | 249977 | pIter->pTokenDataIter->nMap = 0; |
| 249195 | 249978 | } |
| 249196 | 249979 | } |
| 249197 | 249980 | |
| 249198 | 249981 | /* |
| @@ -249208,21 +249991,33 @@ | ||
| 249208 | 249991 | i64 iRowid, int iCol, int iOff |
| 249209 | 249992 | ){ |
| 249210 | 249993 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249211 | 249994 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249212 | 249995 | Fts5Index *p = pIter->pIndex; |
| 249213 | - int ii; | |
| 249996 | + i64 iPos = (((i64)iCol)<<32) + iOff; | |
| 249214 | 249997 | |
| 249215 | 249998 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 249216 | - assert( pIter->pTokenDataIter ); | |
| 249217 | - | |
| 249218 | - for(ii=0; ii<pT->nIter; ii++){ | |
| 249219 | - Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; | |
| 249220 | - if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; | |
| 249221 | - } | |
| 249222 | - if( ii<pT->nIter ){ | |
| 249223 | - fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); | |
| 249999 | + assert( pIter->pTokenDataIter || pIter->nSeg>0 ); | |
| 250000 | + if( pIter->nSeg>0 ){ | |
| 250001 | + /* This is a prefix term iterator. */ | |
| 250002 | + if( pT==0 ){ | |
| 250003 | + pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); | |
| 250004 | + pIter->pTokenDataIter = pT; | |
| 250005 | + } | |
| 250006 | + if( pT ){ | |
| 250007 | + fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); | |
| 250008 | + fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); | |
| 250009 | + } | |
| 250010 | + }else{ | |
| 250011 | + int ii; | |
| 250012 | + for(ii=0; ii<pT->nIter; ii++){ | |
| 250013 | + Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; | |
| 250014 | + if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; | |
| 250015 | + } | |
| 250016 | + if( ii<pT->nIter ){ | |
| 250017 | + fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos); | |
| 250018 | + } | |
| 249224 | 250019 | } |
| 249225 | 250020 | return fts5IndexReturn(p); |
| 249226 | 250021 | } |
| 249227 | 250022 | |
| 249228 | 250023 | /* |
| @@ -250771,11 +251566,11 @@ | ||
| 250771 | 251566 | return rc; |
| 250772 | 251567 | } |
| 250773 | 251568 | |
| 250774 | 251569 | /* |
| 250775 | 251570 | ** We must have a single struct=? constraint that will be passed through |
| 250776 | -** into the xFilter method. If there is no valid stmt=? constraint, | |
| 251571 | +** into the xFilter method. If there is no valid struct=? constraint, | |
| 250777 | 251572 | ** then return an SQLITE_CONSTRAINT error. |
| 250778 | 251573 | */ |
| 250779 | 251574 | static int fts5structBestIndexMethod( |
| 250780 | 251575 | sqlite3_vtab *tab, |
| 250781 | 251576 | sqlite3_index_info *pIdxInfo |
| @@ -251113,12 +251908,22 @@ | ||
| 251113 | 251908 | i64 iNextId; /* Used to allocate unique cursor ids */ |
| 251114 | 251909 | Fts5Auxiliary *pAux; /* First in list of all aux. functions */ |
| 251115 | 251910 | Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ |
| 251116 | 251911 | Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ |
| 251117 | 251912 | Fts5Cursor *pCsr; /* First in list of all open cursors */ |
| 251913 | + u32 aLocaleHdr[4]; | |
| 251118 | 251914 | }; |
| 251119 | 251915 | |
| 251916 | +/* | |
| 251917 | +** Size of header on fts5_locale() values. And macro to access a buffer | |
| 251918 | +** containing a copy of the header from an Fts5Config pointer. | |
| 251919 | +*/ | |
| 251920 | +#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) | |
| 251921 | +#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) | |
| 251922 | + | |
| 251923 | +#define FTS5_INSTTOKEN_SUBTYPE 73 | |
| 251924 | + | |
| 251120 | 251925 | /* |
| 251121 | 251926 | ** Each auxiliary function registered with the FTS5 module is represented |
| 251122 | 251927 | ** by an object of the following type. All such objects are stored as part |
| 251123 | 251928 | ** of the Fts5Global.pAux list. |
| 251124 | 251929 | */ |
| @@ -251277,16 +252082,10 @@ | ||
| 251277 | 252082 | #define FTS5CSR_REQUIRE_POSLIST 0x40 |
| 251278 | 252083 | |
| 251279 | 252084 | #define BitFlagAllTest(x,y) (((x) & (y))==(y)) |
| 251280 | 252085 | #define BitFlagTest(x,y) (((x) & (y))!=0) |
| 251281 | 252086 | |
| 251282 | -/* | |
| 251283 | -** The subtype value and header bytes used by fts5_locale(). | |
| 251284 | -*/ | |
| 251285 | -#define FTS5_LOCALE_SUBTYPE ((unsigned int)'L') | |
| 251286 | -#define FTS5_LOCALE_HEADER "\x00\xE0\xB2\xEB" | |
| 251287 | - | |
| 251288 | 252087 | |
| 251289 | 252088 | /* |
| 251290 | 252089 | ** Macros to Set(), Clear() and Test() cursor flags. |
| 251291 | 252090 | */ |
| 251292 | 252091 | #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) |
| @@ -251359,14 +252158,20 @@ | ||
| 251359 | 252158 | #else |
| 251360 | 252159 | # define fts5CheckTransactionState(x,y,z) |
| 251361 | 252160 | #endif |
| 251362 | 252161 | |
| 251363 | 252162 | /* |
| 251364 | -** Return true if pTab is a contentless table. | |
| 252163 | +** Return true if pTab is a contentless table. If parameter bIncludeUnindexed | |
| 252164 | +** is true, this includes contentless tables that store UNINDEXED columns | |
| 252165 | +** only. | |
| 251365 | 252166 | */ |
| 251366 | -static int fts5IsContentless(Fts5FullTable *pTab){ | |
| 251367 | - return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; | |
| 252167 | +static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){ | |
| 252168 | + int eContent = pTab->p.pConfig->eContent; | |
| 252169 | + return ( | |
| 252170 | + eContent==FTS5_CONTENT_NONE | |
| 252171 | + || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED) | |
| 252172 | + ); | |
| 251368 | 252173 | } |
| 251369 | 252174 | |
| 251370 | 252175 | /* |
| 251371 | 252176 | ** Delete a virtual table handle allocated by fts5InitVtab(). |
| 251372 | 252177 | */ |
| @@ -251653,10 +252458,11 @@ | ||
| 251653 | 252458 | ){ |
| 251654 | 252459 | /* A MATCH operator or equivalent */ |
| 251655 | 252460 | if( p->usable==0 || iCol<0 ){ |
| 251656 | 252461 | /* As there exists an unusable MATCH constraint this is an |
| 251657 | 252462 | ** unusable plan. Return SQLITE_CONSTRAINT. */ |
| 252463 | + idxStr[iIdxStr] = 0; | |
| 251658 | 252464 | return SQLITE_CONSTRAINT; |
| 251659 | 252465 | }else{ |
| 251660 | 252466 | if( iCol==nCol+1 ){ |
| 251661 | 252467 | if( bSeenRank ) continue; |
| 251662 | 252468 | idxStr[iIdxStr++] = 'r'; |
| @@ -252286,11 +253092,11 @@ | ||
| 252286 | 253092 | ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale |
| 252287 | 253093 | ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain |
| 252288 | 253094 | ** valid until after the final call to sqlite3Fts5Tokenize() that will use |
| 252289 | 253095 | ** the locale. |
| 252290 | 253096 | */ |
| 252291 | -static void fts5SetLocale( | |
| 253097 | +static void sqlite3Fts5SetLocale( | |
| 252292 | 253098 | Fts5Config *pConfig, |
| 252293 | 253099 | const char *zLocale, |
| 252294 | 253100 | int nLocale |
| 252295 | 253101 | ){ |
| 252296 | 253102 | Fts5TokenizerConfig *pT = &pConfig->t; |
| @@ -252297,141 +253103,88 @@ | ||
| 252297 | 253103 | pT->pLocale = zLocale; |
| 252298 | 253104 | pT->nLocale = nLocale; |
| 252299 | 253105 | } |
| 252300 | 253106 | |
| 252301 | 253107 | /* |
| 252302 | -** Clear any locale configured by an earlier call to fts5SetLocale() or | |
| 252303 | -** sqlite3Fts5ExtractText(). | |
| 253108 | +** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). | |
| 252304 | 253109 | */ |
| 252305 | 253110 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ |
| 252306 | - fts5SetLocale(pConfig, 0, 0); | |
| 253111 | + sqlite3Fts5SetLocale(pConfig, 0, 0); | |
| 252307 | 253112 | } |
| 252308 | 253113 | |
| 252309 | 253114 | /* |
| 252310 | -** This function is used to extract utf-8 text from an sqlite3_value. This | |
| 252311 | -** is usually done in order to tokenize it. For example, when: | |
| 252312 | -** | |
| 252313 | -** * a value is written to an fts5 table, | |
| 252314 | -** * a value is deleted from an FTS5_CONTENT_NORMAL table, | |
| 252315 | -** * a value containing a query expression is passed to xFilter() | |
| 252316 | -** | |
| 252317 | -** and so on. | |
| 252318 | -** | |
| 252319 | -** This function handles 2 cases: | |
| 252320 | -** | |
| 252321 | -** 1) Ordinary values. The text can be extracted from these using | |
| 252322 | -** sqlite3_value_text(). | |
| 252323 | -** | |
| 252324 | -** 2) Combination text/locale blobs created by fts5_locale(). There | |
| 252325 | -** are several cases for these: | |
| 252326 | -** | |
| 252327 | -** * Blobs tagged with FTS5_LOCALE_SUBTYPE. | |
| 252328 | -** * Blobs read from the content table of a locale=1 external-content | |
| 252329 | -** table, and | |
| 252330 | -** * Blobs read from the content table of a locale=1 regular | |
| 252331 | -** content table. | |
| 252332 | -** | |
| 252333 | -** The first two cases above should have the 4 byte FTS5_LOCALE_HEADER | |
| 252334 | -** header. It is an error if a blob with the subtype or a blob read | |
| 252335 | -** from the content table of an external content table does not have | |
| 252336 | -** the required header. A blob read from the content table of a regular | |
| 252337 | -** locale=1 table does not have the header. This is to save space. | |
| 252338 | -** | |
| 252339 | -** If successful, SQLITE_OK is returned and output parameters (*ppText) | |
| 252340 | -** and (*pnText) are set to point to a buffer containing the extracted utf-8 | |
| 252341 | -** text and its length in bytes, respectively. The buffer is not | |
| 252342 | -** nul-terminated. It has the same lifetime as the sqlite3_value object | |
| 252343 | -** from which it is extracted. | |
| 252344 | -** | |
| 252345 | -** Parameter bContent must be true if the value was read from an indexed | |
| 252346 | -** column (i.e. not UNINDEXED) of the on disk content. | |
| 252347 | -** | |
| 252348 | -** If pbResetTokenizer is not NULL and if case (2) is used, then | |
| 252349 | -** fts5SetLocale() is called to ensure subsequent sqlite3Fts5Tokenize() calls | |
| 252350 | -** use the locale. In this case (*pbResetTokenizer) is set to true before | |
| 252351 | -** returning, to indicate that the caller must call sqlite3Fts5ClearLocale() | |
| 252352 | -** to clear the locale after tokenizing the text. | |
| 253115 | +** Return true if the value passed as the only argument is an | |
| 253116 | +** fts5_locale() value. | |
| 252353 | 253117 | */ |
| 252354 | -static int sqlite3Fts5ExtractText( | |
| 252355 | - Fts5Config *pConfig, | |
| 252356 | - sqlite3_value *pVal, /* Value to extract text from */ | |
| 252357 | - int bContent, /* True if indexed table content */ | |
| 252358 | - int *pbResetTokenizer, /* OUT: True if xSetLocale(NULL) required */ | |
| 252359 | - const char **ppText, /* OUT: Pointer to text buffer */ | |
| 252360 | - int *pnText /* OUT: Size of (*ppText) in bytes */ | |
| 252361 | -){ | |
| 252362 | - const char *pText = 0; | |
| 252363 | - int nText = 0; | |
| 252364 | - int rc = SQLITE_OK; | |
| 252365 | - int bDecodeBlob = 0; | |
| 252366 | - | |
| 252367 | - assert( pbResetTokenizer==0 || *pbResetTokenizer==0 ); | |
| 252368 | - assert( bContent==0 || pConfig->eContent!=FTS5_CONTENT_NONE ); | |
| 252369 | - assert( bContent==0 || sqlite3_value_subtype(pVal)==0 ); | |
| 252370 | - | |
| 253118 | +static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ | |
| 253119 | + int ret = 0; | |
| 252371 | 253120 | if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ |
| 252372 | - if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE | |
| 252373 | - || (bContent && pConfig->bLocale) | |
| 252374 | - ){ | |
| 252375 | - bDecodeBlob = 1; | |
| 252376 | - } | |
| 252377 | - } | |
| 252378 | - | |
| 252379 | - if( bDecodeBlob ){ | |
| 252380 | - const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; | |
| 253121 | + /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case. | |
| 253122 | + ** If the blob was created using zeroblob(), then sqlite3_value_blob() | |
| 253123 | + ** may call malloc(). If this malloc() fails, then the values returned | |
| 253124 | + ** by both value_blob() and value_bytes() will be 0. If value_bytes() were | |
| 253125 | + ** called first, then the NULL pointer returned by value_blob() might | |
| 253126 | + ** be dereferenced. */ | |
| 252381 | 253127 | const u8 *pBlob = sqlite3_value_blob(pVal); |
| 252382 | 253128 | int nBlob = sqlite3_value_bytes(pVal); |
| 252383 | - | |
| 252384 | - /* Unless this blob was read from the %_content table of an | |
| 252385 | - ** FTS5_CONTENT_NORMAL table, it should have the 4 byte fts5_locale() | |
| 252386 | - ** header. Check for this. If it is not found, return an error. */ | |
| 252387 | - if( (!bContent || pConfig->eContent!=FTS5_CONTENT_NORMAL) ){ | |
| 252388 | - if( nBlob<SZHDR || memcmp(FTS5_LOCALE_HEADER, pBlob, SZHDR) ){ | |
| 252389 | - rc = SQLITE_ERROR; | |
| 252390 | - }else{ | |
| 252391 | - pBlob += 4; | |
| 252392 | - nBlob -= 4; | |
| 252393 | - } | |
| 252394 | - } | |
| 252395 | - | |
| 252396 | - if( rc==SQLITE_OK ){ | |
| 252397 | - int nLocale = 0; | |
| 252398 | - | |
| 252399 | - for(nLocale=0; nLocale<nBlob; nLocale++){ | |
| 252400 | - if( pBlob[nLocale]==0x00 ) break; | |
| 252401 | - } | |
| 252402 | - if( nLocale==nBlob || nLocale==0 ){ | |
| 252403 | - rc = SQLITE_ERROR; | |
| 252404 | - }else{ | |
| 252405 | - pText = (const char*)&pBlob[nLocale+1]; | |
| 252406 | - nText = nBlob-nLocale-1; | |
| 252407 | - | |
| 252408 | - if( pbResetTokenizer ){ | |
| 252409 | - fts5SetLocale(pConfig, (const char*)pBlob, nLocale); | |
| 252410 | - *pbResetTokenizer = 1; | |
| 252411 | - } | |
| 252412 | - } | |
| 252413 | - } | |
| 252414 | - | |
| 252415 | - }else{ | |
| 252416 | - pText = (const char*)sqlite3_value_text(pVal); | |
| 252417 | - nText = sqlite3_value_bytes(pVal); | |
| 252418 | - } | |
| 252419 | - | |
| 252420 | - *ppText = pText; | |
| 252421 | - *pnText = nText; | |
| 252422 | - return rc; | |
| 253129 | + if( nBlob>FTS5_LOCALE_HDR_SIZE | |
| 253130 | + && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE) | |
| 253131 | + ){ | |
| 253132 | + ret = 1; | |
| 253133 | + } | |
| 253134 | + } | |
| 253135 | + return ret; | |
| 253136 | +} | |
| 253137 | + | |
| 253138 | +/* | |
| 253139 | +** Value pVal is guaranteed to be an fts5_locale() value, according to | |
| 253140 | +** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale | |
| 253141 | +** from the value and returns them separately. | |
| 253142 | +** | |
| 253143 | +** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set | |
| 253144 | +** to point to buffers containing the text and locale, as utf-8, | |
| 253145 | +** respectively. In this case output parameters (*pnText) and (*pnLoc) are | |
| 253146 | +** set to the sizes in bytes of these two buffers. | |
| 253147 | +** | |
| 253148 | +** Or, if an error occurs, then an SQLite error code is returned. The final | |
| 253149 | +** value of the four output parameters is undefined in this case. | |
| 253150 | +*/ | |
| 253151 | +static int sqlite3Fts5DecodeLocaleValue( | |
| 253152 | + sqlite3_value *pVal, | |
| 253153 | + const char **ppText, | |
| 253154 | + int *pnText, | |
| 253155 | + const char **ppLoc, | |
| 253156 | + int *pnLoc | |
| 253157 | +){ | |
| 253158 | + const char *p = sqlite3_value_blob(pVal); | |
| 253159 | + int n = sqlite3_value_bytes(pVal); | |
| 253160 | + int nLoc = 0; | |
| 253161 | + | |
| 253162 | + assert( sqlite3_value_type(pVal)==SQLITE_BLOB ); | |
| 253163 | + assert( n>FTS5_LOCALE_HDR_SIZE ); | |
| 253164 | + | |
| 253165 | + for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){ | |
| 253166 | + if( nLoc==(n-1) ){ | |
| 253167 | + return SQLITE_MISMATCH; | |
| 253168 | + } | |
| 253169 | + } | |
| 253170 | + *ppLoc = &p[FTS5_LOCALE_HDR_SIZE]; | |
| 253171 | + *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE; | |
| 253172 | + | |
| 253173 | + *ppText = &p[nLoc+1]; | |
| 253174 | + *pnText = n - nLoc - 1; | |
| 253175 | + return SQLITE_OK; | |
| 252423 | 253176 | } |
| 252424 | 253177 | |
| 252425 | 253178 | /* |
| 252426 | 253179 | ** Argument pVal is the text of a full-text search expression. It may or |
| 252427 | 253180 | ** may not have been wrapped by fts5_locale(). This function extracts |
| 252428 | 253181 | ** the text of the expression, and sets output variable (*pzText) to |
| 252429 | 253182 | ** point to a nul-terminated buffer containing the expression. |
| 252430 | 253183 | ** |
| 252431 | -** If pVal was an fts5_locale() value, then fts5SetLocale() is called to | |
| 252432 | -** set the tokenizer to use the specified locale. | |
| 253184 | +** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called | |
| 253185 | +** to set the tokenizer to use the specified locale. | |
| 252433 | 253186 | ** |
| 252434 | 253187 | ** If output variable (*pbFreeAndReset) is set to true, then the caller |
| 252435 | 253188 | ** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer |
| 252436 | 253189 | ** locale, and (b) call sqlite3_free() to free (*pzText). |
| 252437 | 253190 | */ |
| @@ -252439,28 +253192,26 @@ | ||
| 252439 | 253192 | Fts5Config *pConfig, /* Fts5 configuration */ |
| 252440 | 253193 | sqlite3_value *pVal, /* Value to extract expression text from */ |
| 252441 | 253194 | char **pzText, /* OUT: nul-terminated buffer of text */ |
| 252442 | 253195 | int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ |
| 252443 | 253196 | ){ |
| 252444 | - const char *zText = 0; | |
| 252445 | - int nText = 0; | |
| 252446 | 253197 | int rc = SQLITE_OK; |
| 252447 | - int bReset = 0; | |
| 252448 | - | |
| 252449 | - *pbFreeAndReset = 0; | |
| 252450 | - rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, &bReset, &zText, &nText); | |
| 252451 | - if( rc==SQLITE_OK ){ | |
| 252452 | - if( bReset ){ | |
| 252453 | - *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, zText); | |
| 252454 | - if( rc!=SQLITE_OK ){ | |
| 252455 | - sqlite3Fts5ClearLocale(pConfig); | |
| 252456 | - }else{ | |
| 252457 | - *pbFreeAndReset = 1; | |
| 252458 | - } | |
| 252459 | - }else{ | |
| 252460 | - *pzText = (char*)zText; | |
| 252461 | - } | |
| 253198 | + | |
| 253199 | + if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |
| 253200 | + const char *pText = 0; | |
| 253201 | + int nText = 0; | |
| 253202 | + const char *pLoc = 0; | |
| 253203 | + int nLoc = 0; | |
| 253204 | + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |
| 253205 | + *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText); | |
| 253206 | + if( rc==SQLITE_OK ){ | |
| 253207 | + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |
| 253208 | + } | |
| 253209 | + *pbFreeAndReset = 1; | |
| 253210 | + }else{ | |
| 253211 | + *pzText = (char*)sqlite3_value_text(pVal); | |
| 253212 | + *pbFreeAndReset = 0; | |
| 252462 | 253213 | } |
| 252463 | 253214 | |
| 252464 | 253215 | return rc; |
| 252465 | 253216 | } |
| 252466 | 253217 | |
| @@ -252493,10 +253244,11 @@ | ||
| 252493 | 253244 | sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ |
| 252494 | 253245 | sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ |
| 252495 | 253246 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ |
| 252496 | 253247 | int iCol; /* Column on LHS of MATCH operator */ |
| 252497 | 253248 | char **pzErrmsg = pConfig->pzErrmsg; |
| 253249 | + int bPrefixInsttoken = pConfig->bPrefixInsttoken; | |
| 252498 | 253250 | int i; |
| 252499 | 253251 | int iIdxStr = 0; |
| 252500 | 253252 | Fts5Expr *pExpr = 0; |
| 252501 | 253253 | |
| 252502 | 253254 | assert( pConfig->bLock==0 ); |
| @@ -252528,10 +253280,13 @@ | ||
| 252528 | 253280 | int bInternal = 0; |
| 252529 | 253281 | |
| 252530 | 253282 | rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); |
| 252531 | 253283 | if( rc!=SQLITE_OK ) goto filter_out; |
| 252532 | 253284 | if( zText==0 ) zText = ""; |
| 253285 | + if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){ | |
| 253286 | + pConfig->bPrefixInsttoken = 1; | |
| 253287 | + } | |
| 252533 | 253288 | |
| 252534 | 253289 | iCol = 0; |
| 252535 | 253290 | do{ |
| 252536 | 253291 | iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 252537 | 253292 | iIdxStr++; |
| @@ -252668,10 +253423,11 @@ | ||
| 252668 | 253423 | } |
| 252669 | 253424 | |
| 252670 | 253425 | filter_out: |
| 252671 | 253426 | sqlite3Fts5ExprFree(pExpr); |
| 252672 | 253427 | pConfig->pzErrmsg = pzErrmsg; |
| 253428 | + pConfig->bPrefixInsttoken = bPrefixInsttoken; | |
| 252673 | 253429 | return rc; |
| 252674 | 253430 | } |
| 252675 | 253431 | |
| 252676 | 253432 | /* |
| 252677 | 253433 | ** This is the xEof method of the virtual table. SQLite calls this |
| @@ -252808,11 +253564,11 @@ | ||
| 252808 | 253564 | }else{ |
| 252809 | 253565 | rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); |
| 252810 | 253566 | } |
| 252811 | 253567 | bLoadConfig = 1; |
| 252812 | 253568 | }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ |
| 252813 | - if( pConfig->eContent==FTS5_CONTENT_NONE ){ | |
| 253569 | + if( fts5IsContentless(pTab, 1) ){ | |
| 252814 | 253570 | fts5SetVtabError(pTab, |
| 252815 | 253571 | "'rebuild' may not be used with a contentless fts5 table" |
| 252816 | 253572 | ); |
| 252817 | 253573 | rc = SQLITE_ERROR; |
| 252818 | 253574 | }else{ |
| @@ -252877,17 +253633,78 @@ | ||
| 252877 | 253633 | sqlite3_value **apVal, |
| 252878 | 253634 | i64 *piRowid |
| 252879 | 253635 | ){ |
| 252880 | 253636 | int rc = *pRc; |
| 252881 | 253637 | if( rc==SQLITE_OK ){ |
| 252882 | - rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); | |
| 253638 | + rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid); | |
| 252883 | 253639 | } |
| 252884 | 253640 | if( rc==SQLITE_OK ){ |
| 252885 | 253641 | rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); |
| 252886 | 253642 | } |
| 252887 | 253643 | *pRc = rc; |
| 252888 | 253644 | } |
| 253645 | + | |
| 253646 | +/* | |
| 253647 | +** | |
| 253648 | +** This function is called when the user attempts an UPDATE on a contentless | |
| 253649 | +** table. Parameter bRowidModified is true if the UPDATE statement modifies | |
| 253650 | +** the rowid value. Parameter apVal[] contains the new values for each user | |
| 253651 | +** defined column of the fts5 table. pConfig is the configuration object of the | |
| 253652 | +** table being updated (guaranteed to be contentless). The contentless_delete=1 | |
| 253653 | +** and contentless_unindexed=1 options may or may not be set. | |
| 253654 | +** | |
| 253655 | +** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite | |
| 253656 | +** error code if it cannot. In this case an error message is also loaded into | |
| 253657 | +** pConfig. Output parameter (*pbContent) is set to true if the caller should | |
| 253658 | +** update the %_content table only - not the FTS index or any other shadow | |
| 253659 | +** table. This occurs when an UPDATE modifies only UNINDEXED columns of the | |
| 253660 | +** table. | |
| 253661 | +** | |
| 253662 | +** An UPDATE may proceed if: | |
| 253663 | +** | |
| 253664 | +** * The only columns modified are UNINDEXED columns, or | |
| 253665 | +** | |
| 253666 | +** * The contentless_delete=1 option was specified and all of the indexed | |
| 253667 | +** columns (not a subset) have been modified. | |
| 253668 | +*/ | |
| 253669 | +static int fts5ContentlessUpdate( | |
| 253670 | + Fts5Config *pConfig, | |
| 253671 | + sqlite3_value **apVal, | |
| 253672 | + int bRowidModified, | |
| 253673 | + int *pbContent | |
| 253674 | +){ | |
| 253675 | + int ii; | |
| 253676 | + int bSeenIndex = 0; /* Have seen modified indexed column */ | |
| 253677 | + int bSeenIndexNC = 0; /* Have seen unmodified indexed column */ | |
| 253678 | + int rc = SQLITE_OK; | |
| 253679 | + | |
| 253680 | + for(ii=0; ii<pConfig->nCol; ii++){ | |
| 253681 | + if( pConfig->abUnindexed[ii]==0 ){ | |
| 253682 | + if( sqlite3_value_nochange(apVal[ii]) ){ | |
| 253683 | + bSeenIndexNC++; | |
| 253684 | + }else{ | |
| 253685 | + bSeenIndex++; | |
| 253686 | + } | |
| 253687 | + } | |
| 253688 | + } | |
| 253689 | + | |
| 253690 | + if( bSeenIndex==0 && bRowidModified==0 ){ | |
| 253691 | + *pbContent = 1; | |
| 253692 | + }else{ | |
| 253693 | + if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){ | |
| 253694 | + rc = SQLITE_ERROR; | |
| 253695 | + sqlite3Fts5ConfigErrmsg(pConfig, | |
| 253696 | + (pConfig->bContentlessDelete ? | |
| 253697 | + "%s a subset of columns on fts5 contentless-delete table: %s" : | |
| 253698 | + "%s contentless fts5 table: %s") | |
| 253699 | + , "cannot UPDATE", pConfig->zName | |
| 253700 | + ); | |
| 253701 | + } | |
| 253702 | + } | |
| 253703 | + | |
| 253704 | + return rc; | |
| 253705 | +} | |
| 252889 | 253706 | |
| 252890 | 253707 | /* |
| 252891 | 253708 | ** This function is the implementation of the xUpdate callback used by |
| 252892 | 253709 | ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be |
| 252893 | 253710 | ** inserted, updated or deleted. |
| @@ -252971,48 +253788,38 @@ | ||
| 252971 | 253788 | } |
| 252972 | 253789 | |
| 252973 | 253790 | assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); |
| 252974 | 253791 | assert( nArg!=1 || eType0==SQLITE_INTEGER ); |
| 252975 | 253792 | |
| 252976 | - /* Filter out attempts to run UPDATE or DELETE on contentless tables. | |
| 252977 | - ** This is not suported. Except - they are both supported if the CREATE | |
| 252978 | - ** VIRTUAL TABLE statement contained "contentless_delete=1". */ | |
| 252979 | - if( eType0==SQLITE_INTEGER | |
| 252980 | - && pConfig->eContent==FTS5_CONTENT_NONE | |
| 252981 | - && pConfig->bContentlessDelete==0 | |
| 252982 | - ){ | |
| 252983 | - pTab->p.base.zErrMsg = sqlite3_mprintf( | |
| 252984 | - "cannot %s contentless fts5 table: %s", | |
| 252985 | - (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName | |
| 252986 | - ); | |
| 252987 | - rc = SQLITE_ERROR; | |
| 252988 | - } | |
| 252989 | - | |
| 252990 | 253793 | /* DELETE */ |
| 252991 | - else if( nArg==1 ){ | |
| 252992 | - i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ | |
| 252993 | - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); | |
| 252994 | - bUpdateOrDelete = 1; | |
| 253794 | + if( nArg==1 ){ | |
| 253795 | + /* It is only possible to DELETE from a contentless table if the | |
| 253796 | + ** contentless_delete=1 flag is set. */ | |
| 253797 | + if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){ | |
| 253798 | + fts5SetVtabError(pTab, | |
| 253799 | + "cannot DELETE from contentless fts5 table: %s", pConfig->zName | |
| 253800 | + ); | |
| 253801 | + rc = SQLITE_ERROR; | |
| 253802 | + }else{ | |
| 253803 | + i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ | |
| 253804 | + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); | |
| 253805 | + bUpdateOrDelete = 1; | |
| 253806 | + } | |
| 252995 | 253807 | } |
| 252996 | 253808 | |
| 252997 | 253809 | /* INSERT or UPDATE */ |
| 252998 | 253810 | else{ |
| 252999 | 253811 | int eType1 = sqlite3_value_numeric_type(apVal[1]); |
| 253000 | 253812 | |
| 253001 | - /* Ensure that no fts5_locale() values are written to locale=0 tables. | |
| 253002 | - ** And that no blobs except fts5_locale() blobs are written to indexed | |
| 253003 | - ** (i.e. not UNINDEXED) columns of locale=1 tables. */ | |
| 253004 | - int ii; | |
| 253005 | - for(ii=0; ii<pConfig->nCol; ii++){ | |
| 253006 | - if( sqlite3_value_type(apVal[ii+2])==SQLITE_BLOB ){ | |
| 253007 | - int bSub = (sqlite3_value_subtype(apVal[ii+2])==FTS5_LOCALE_SUBTYPE); | |
| 253008 | - if( (pConfig->bLocale && !bSub && pConfig->abUnindexed[ii]==0) | |
| 253009 | - || (pConfig->bLocale==0 && bSub) | |
| 253010 | - ){ | |
| 253011 | - if( pConfig->bLocale==0 ){ | |
| 253012 | - fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); | |
| 253013 | - } | |
| 253813 | + /* It is an error to write an fts5_locale() value to a table without | |
| 253814 | + ** the locale=1 option. */ | |
| 253815 | + if( pConfig->bLocale==0 ){ | |
| 253816 | + int ii; | |
| 253817 | + for(ii=0; ii<pConfig->nCol; ii++){ | |
| 253818 | + sqlite3_value *pVal = apVal[ii+2]; | |
| 253819 | + if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |
| 253820 | + fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); | |
| 253014 | 253821 | rc = SQLITE_MISMATCH; |
| 253015 | 253822 | goto update_out; |
| 253016 | 253823 | } |
| 253017 | 253824 | } |
| 253018 | 253825 | } |
| @@ -253028,39 +253835,59 @@ | ||
| 253028 | 253835 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253029 | 253836 | } |
| 253030 | 253837 | |
| 253031 | 253838 | /* UPDATE */ |
| 253032 | 253839 | else{ |
| 253033 | - i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ | |
| 253034 | - i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ | |
| 253035 | - if( eType1!=SQLITE_INTEGER ){ | |
| 253036 | - rc = SQLITE_MISMATCH; | |
| 253037 | - }else if( iOld!=iNew ){ | |
| 253038 | - if( eConflict==SQLITE_REPLACE ){ | |
| 253039 | - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); | |
| 253040 | - if( rc==SQLITE_OK ){ | |
| 253041 | - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); | |
| 253042 | - } | |
| 253043 | - fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
| 253044 | - }else{ | |
| 253045 | - rc = sqlite3Fts5StorageFindDeleteRow(pTab->pStorage, iOld); | |
| 253046 | - if( rc==SQLITE_OK ){ | |
| 253047 | - rc = sqlite3Fts5StorageContentInsert(pTab->pStorage,apVal,pRowid); | |
| 253048 | - } | |
| 253049 | - if( rc==SQLITE_OK ){ | |
| 253050 | - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); | |
| 253051 | - } | |
| 253052 | - if( rc==SQLITE_OK ){ | |
| 253053 | - rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); | |
| 253054 | - } | |
| 253055 | - } | |
| 253056 | - }else{ | |
| 253057 | - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); | |
| 253058 | - fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
| 253059 | - } | |
| 253060 | - bUpdateOrDelete = 1; | |
| 253061 | - sqlite3Fts5StorageReleaseDeleteRow(pTab->pStorage); | |
| 253840 | + Fts5Storage *pStorage = pTab->pStorage; | |
| 253841 | + i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ | |
| 253842 | + i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ | |
| 253843 | + int bContent = 0; /* Content only update */ | |
| 253844 | + | |
| 253845 | + /* If this is a contentless table (including contentless_unindexed=1 | |
| 253846 | + ** tables), check if the UPDATE may proceed. */ | |
| 253847 | + if( fts5IsContentless(pTab, 1) ){ | |
| 253848 | + rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent); | |
| 253849 | + if( rc!=SQLITE_OK ) goto update_out; | |
| 253850 | + } | |
| 253851 | + | |
| 253852 | + if( eType1!=SQLITE_INTEGER ){ | |
| 253853 | + rc = SQLITE_MISMATCH; | |
| 253854 | + }else if( iOld!=iNew ){ | |
| 253855 | + assert( bContent==0 ); | |
| 253856 | + if( eConflict==SQLITE_REPLACE ){ | |
| 253857 | + rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); | |
| 253858 | + if( rc==SQLITE_OK ){ | |
| 253859 | + rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0); | |
| 253860 | + } | |
| 253861 | + fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
| 253862 | + }else{ | |
| 253863 | + rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); | |
| 253864 | + if( rc==SQLITE_OK ){ | |
| 253865 | + rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid); | |
| 253866 | + } | |
| 253867 | + if( rc==SQLITE_OK ){ | |
| 253868 | + rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0); | |
| 253869 | + } | |
| 253870 | + if( rc==SQLITE_OK ){ | |
| 253871 | + rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid); | |
| 253872 | + } | |
| 253873 | + } | |
| 253874 | + }else if( bContent ){ | |
| 253875 | + /* This occurs when an UPDATE on a contentless table affects *only* | |
| 253876 | + ** UNINDEXED columns. This is a no-op for contentless_unindexed=0 | |
| 253877 | + ** tables, or a write to the %_content table only for =1 tables. */ | |
| 253878 | + assert( fts5IsContentless(pTab, 1) ); | |
| 253879 | + rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); | |
| 253880 | + if( rc==SQLITE_OK ){ | |
| 253881 | + rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid); | |
| 253882 | + } | |
| 253883 | + }else{ | |
| 253884 | + rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); | |
| 253885 | + fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
| 253886 | + } | |
| 253887 | + bUpdateOrDelete = 1; | |
| 253888 | + sqlite3Fts5StorageReleaseDeleteRow(pStorage); | |
| 253062 | 253889 | } |
| 253063 | 253890 | |
| 253064 | 253891 | } |
| 253065 | 253892 | } |
| 253066 | 253893 | |
| @@ -253169,15 +253996,15 @@ | ||
| 253169 | 253996 | ){ |
| 253170 | 253997 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 253171 | 253998 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| 253172 | 253999 | int rc = SQLITE_OK; |
| 253173 | 254000 | |
| 253174 | - fts5SetLocale(pTab->pConfig, pLoc, nLoc); | |
| 254001 | + sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); | |
| 253175 | 254002 | rc = sqlite3Fts5Tokenize(pTab->pConfig, |
| 253176 | 254003 | FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken |
| 253177 | 254004 | ); |
| 253178 | - fts5SetLocale(pTab->pConfig, 0, 0); | |
| 254005 | + sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); | |
| 253179 | 254006 | |
| 253180 | 254007 | return rc; |
| 253181 | 254008 | } |
| 253182 | 254009 | |
| 253183 | 254010 | /* |
| @@ -253200,10 +254027,53 @@ | ||
| 253200 | 254027 | |
| 253201 | 254028 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ |
| 253202 | 254029 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 253203 | 254030 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); |
| 253204 | 254031 | } |
| 254032 | + | |
| 254033 | +/* | |
| 254034 | +** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This | |
| 254035 | +** function extracts the text value of column iCol of the current row. | |
| 254036 | +** Additionally, if there is an associated locale, it invokes | |
| 254037 | +** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller | |
| 254038 | +** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point | |
| 254039 | +** after this function returns. | |
| 254040 | +** | |
| 254041 | +** If successful, (*ppText) is set to point to a buffer containing the text | |
| 254042 | +** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that | |
| 254043 | +** buffer in bytes. It is not guaranteed to be nul-terminated. If an error | |
| 254044 | +** occurs, an SQLite error code is returned. The final values of the two | |
| 254045 | +** output parameters are undefined in this case. | |
| 254046 | +*/ | |
| 254047 | +static int fts5TextFromStmt( | |
| 254048 | + Fts5Config *pConfig, | |
| 254049 | + sqlite3_stmt *pStmt, | |
| 254050 | + int iCol, | |
| 254051 | + const char **ppText, | |
| 254052 | + int *pnText | |
| 254053 | +){ | |
| 254054 | + sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1); | |
| 254055 | + const char *pLoc = 0; | |
| 254056 | + int nLoc = 0; | |
| 254057 | + int rc = SQLITE_OK; | |
| 254058 | + | |
| 254059 | + if( pConfig->bLocale | |
| 254060 | + && pConfig->eContent==FTS5_CONTENT_EXTERNAL | |
| 254061 | + && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |
| 254062 | + ){ | |
| 254063 | + rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); | |
| 254064 | + }else{ | |
| 254065 | + *ppText = (const char*)sqlite3_value_text(pVal); | |
| 254066 | + *pnText = sqlite3_value_bytes(pVal); | |
| 254067 | + if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){ | |
| 254068 | + pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol); | |
| 254069 | + nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol); | |
| 254070 | + } | |
| 254071 | + } | |
| 254072 | + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |
| 254073 | + return rc; | |
| 254074 | +} | |
| 253205 | 254075 | |
| 253206 | 254076 | static int fts5ApiColumnText( |
| 253207 | 254077 | Fts5Context *pCtx, |
| 253208 | 254078 | int iCol, |
| 253209 | 254079 | const char **pz, |
| @@ -253214,20 +254084,18 @@ | ||
| 253214 | 254084 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| 253215 | 254085 | |
| 253216 | 254086 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); |
| 253217 | 254087 | if( iCol<0 || iCol>=pTab->pConfig->nCol ){ |
| 253218 | 254088 | rc = SQLITE_RANGE; |
| 253219 | - }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) ){ | |
| 254089 | + }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){ | |
| 253220 | 254090 | *pz = 0; |
| 253221 | 254091 | *pn = 0; |
| 253222 | 254092 | }else{ |
| 253223 | 254093 | rc = fts5SeekCursor(pCsr, 0); |
| 253224 | 254094 | if( rc==SQLITE_OK ){ |
| 253225 | - Fts5Config *pConfig = pTab->pConfig; | |
| 253226 | - int bContent = (pConfig->abUnindexed[iCol]==0); | |
| 253227 | - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); | |
| 253228 | - sqlite3Fts5ExtractText(pConfig, pVal, bContent, 0, pz, pn); | |
| 254095 | + rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); | |
| 254096 | + sqlite3Fts5ClearLocale(pTab->pConfig); | |
| 253229 | 254097 | } |
| 253230 | 254098 | } |
| 253231 | 254099 | return rc; |
| 253232 | 254100 | } |
| 253233 | 254101 | |
| @@ -253249,11 +254117,11 @@ | ||
| 253249 | 254117 | int bLive = (pCsr->pSorter==0); |
| 253250 | 254118 | |
| 253251 | 254119 | if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ |
| 253252 | 254120 | rc = SQLITE_RANGE; |
| 253253 | 254121 | }else if( pConfig->eDetail!=FTS5_DETAIL_FULL |
| 253254 | - && pConfig->eContent==FTS5_CONTENT_NONE | |
| 254122 | + && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) | |
| 253255 | 254123 | ){ |
| 253256 | 254124 | *pa = 0; |
| 253257 | 254125 | *pn = 0; |
| 253258 | 254126 | return SQLITE_OK; |
| 253259 | 254127 | }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ |
| @@ -253265,21 +254133,19 @@ | ||
| 253265 | 254133 | if( aPopulator==0 ) rc = SQLITE_NOMEM; |
| 253266 | 254134 | if( rc==SQLITE_OK ){ |
| 253267 | 254135 | rc = fts5SeekCursor(pCsr, 0); |
| 253268 | 254136 | } |
| 253269 | 254137 | for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ |
| 253270 | - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); | |
| 253271 | 254138 | const char *z = 0; |
| 253272 | 254139 | int n = 0; |
| 253273 | - int bReset = 0; | |
| 253274 | - rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); | |
| 254140 | + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); | |
| 253275 | 254141 | if( rc==SQLITE_OK ){ |
| 253276 | 254142 | rc = sqlite3Fts5ExprPopulatePoslists( |
| 253277 | 254143 | pConfig, pCsr->pExpr, aPopulator, i, z, n |
| 253278 | 254144 | ); |
| 253279 | 254145 | } |
| 253280 | - if( bReset ) sqlite3Fts5ClearLocale(pConfig); | |
| 254146 | + sqlite3Fts5ClearLocale(pConfig); | |
| 253281 | 254147 | } |
| 253282 | 254148 | sqlite3_free(aPopulator); |
| 253283 | 254149 | |
| 253284 | 254150 | if( pCsr->pSorter ){ |
| 253285 | 254151 | sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); |
| @@ -253447,11 +254313,11 @@ | ||
| 253447 | 254313 | |
| 253448 | 254314 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ |
| 253449 | 254315 | if( pConfig->bColumnsize ){ |
| 253450 | 254316 | i64 iRowid = fts5CursorRowid(pCsr); |
| 253451 | 254317 | rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); |
| 253452 | - }else if( pConfig->zContent==0 ){ | |
| 254318 | + }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ | |
| 253453 | 254319 | int i; |
| 253454 | 254320 | for(i=0; i<pConfig->nCol; i++){ |
| 253455 | 254321 | if( pConfig->abUnindexed[i]==0 ){ |
| 253456 | 254322 | pCsr->aColumnSize[i] = -1; |
| 253457 | 254323 | } |
| @@ -253461,21 +254327,18 @@ | ||
| 253461 | 254327 | rc = fts5SeekCursor(pCsr, 0); |
| 253462 | 254328 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 253463 | 254329 | if( pConfig->abUnindexed[i]==0 ){ |
| 253464 | 254330 | const char *z = 0; |
| 253465 | 254331 | int n = 0; |
| 253466 | - int bReset = 0; | |
| 253467 | - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); | |
| 253468 | - | |
| 253469 | 254332 | pCsr->aColumnSize[i] = 0; |
| 253470 | - rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); | |
| 254333 | + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); | |
| 253471 | 254334 | if( rc==SQLITE_OK ){ |
| 253472 | 254335 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, |
| 253473 | 254336 | z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb |
| 253474 | 254337 | ); |
| 253475 | - if( bReset ) sqlite3Fts5ClearLocale(pConfig); | |
| 253476 | 254338 | } |
| 254339 | + sqlite3Fts5ClearLocale(pConfig); | |
| 253477 | 254340 | } |
| 253478 | 254341 | } |
| 253479 | 254342 | } |
| 253480 | 254343 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE); |
| 253481 | 254344 | } |
| @@ -253738,46 +254601,23 @@ | ||
| 253738 | 254601 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); |
| 253739 | 254602 | if( iCol<0 || iCol>=pConfig->nCol ){ |
| 253740 | 254603 | rc = SQLITE_RANGE; |
| 253741 | 254604 | }else if( |
| 253742 | 254605 | pConfig->abUnindexed[iCol]==0 |
| 253743 | - && pConfig->eContent!=FTS5_CONTENT_NONE | |
| 254606 | + && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) | |
| 253744 | 254607 | && pConfig->bLocale |
| 253745 | 254608 | ){ |
| 253746 | 254609 | rc = fts5SeekCursor(pCsr, 0); |
| 253747 | 254610 | if( rc==SQLITE_OK ){ |
| 253748 | - /* Load the value into pVal. pVal is a locale/text pair iff: | |
| 253749 | - ** | |
| 253750 | - ** 1) It is an SQLITE_BLOB, and | |
| 253751 | - ** 2) Either the subtype is FTS5_LOCALE_SUBTYPE, or else the | |
| 253752 | - ** value was loaded from an FTS5_CONTENT_NORMAL table, and | |
| 253753 | - ** 3) It does not begin with an 0x00 byte. | |
| 253754 | - */ | |
| 253755 | - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); | |
| 253756 | - if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ | |
| 253757 | - const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); | |
| 253758 | - int nBlob = sqlite3_value_bytes(pVal); | |
| 253759 | - if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ | |
| 253760 | - const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; | |
| 253761 | - if( nBlob<SZHDR || memcmp(FTS5_LOCALE_HEADER, pBlob, SZHDR) ){ | |
| 253762 | - rc = SQLITE_ERROR; | |
| 253763 | - } | |
| 253764 | - pBlob += 4; | |
| 253765 | - nBlob -= 4; | |
| 253766 | - } | |
| 253767 | - if( rc==SQLITE_OK ){ | |
| 253768 | - int nLocale = 0; | |
| 253769 | - for(nLocale=0; nLocale<nBlob && pBlob[nLocale]!=0x00; nLocale++); | |
| 253770 | - if( nLocale==nBlob || nLocale==0 ){ | |
| 253771 | - rc = SQLITE_ERROR; | |
| 253772 | - }else{ | |
| 253773 | - /* A locale/text pair */ | |
| 253774 | - *pzLocale = (const char*)pBlob; | |
| 253775 | - *pnLocale = nLocale; | |
| 253776 | - } | |
| 253777 | - } | |
| 253778 | - } | |
| 254611 | + const char *zDummy = 0; | |
| 254612 | + int nDummy = 0; | |
| 254613 | + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy); | |
| 254614 | + if( rc==SQLITE_OK ){ | |
| 254615 | + *pzLocale = pConfig->t.pLocale; | |
| 254616 | + *pnLocale = pConfig->t.nLocale; | |
| 254617 | + } | |
| 254618 | + sqlite3Fts5ClearLocale(pConfig); | |
| 253779 | 254619 | } |
| 253780 | 254620 | } |
| 253781 | 254621 | |
| 253782 | 254622 | return rc; |
| 253783 | 254623 | } |
| @@ -253994,61 +254834,10 @@ | ||
| 253994 | 254834 | |
| 253995 | 254835 | sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); |
| 253996 | 254836 | return rc; |
| 253997 | 254837 | } |
| 253998 | 254838 | |
| 253999 | -/* | |
| 254000 | -** Value pVal was read from column iCol of the FTS5 table. This function | |
| 254001 | -** returns it to the owner of pCtx via a call to an sqlite3_result_xxx() | |
| 254002 | -** function. This function deals with the same cases as | |
| 254003 | -** sqlite3Fts5ExtractText(): | |
| 254004 | -** | |
| 254005 | -** 1) Ordinary values. These can be returned using sqlite3_result_value(). | |
| 254006 | -** | |
| 254007 | -** 2) Blobs from fts5_locale(). The text is extracted from these and | |
| 254008 | -** returned via sqlite3_result_text(). The locale is discarded. | |
| 254009 | -*/ | |
| 254010 | -static void fts5ExtractValueFromColumn( | |
| 254011 | - sqlite3_context *pCtx, | |
| 254012 | - Fts5Config *pConfig, | |
| 254013 | - int iCol, | |
| 254014 | - sqlite3_value *pVal | |
| 254015 | -){ | |
| 254016 | - assert( pConfig->eContent!=FTS5_CONTENT_NONE ); | |
| 254017 | - | |
| 254018 | - if( pConfig->bLocale | |
| 254019 | - && sqlite3_value_type(pVal)==SQLITE_BLOB | |
| 254020 | - && pConfig->abUnindexed[iCol]==0 | |
| 254021 | - ){ | |
| 254022 | - const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; | |
| 254023 | - const u8 *pBlob = sqlite3_value_blob(pVal); | |
| 254024 | - int nBlob = sqlite3_value_bytes(pVal); | |
| 254025 | - int ii; | |
| 254026 | - | |
| 254027 | - if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ | |
| 254028 | - if( nBlob<SZHDR || memcmp(pBlob, FTS5_LOCALE_HEADER, SZHDR) ){ | |
| 254029 | - sqlite3_result_error_code(pCtx, SQLITE_ERROR); | |
| 254030 | - return; | |
| 254031 | - }else{ | |
| 254032 | - pBlob += 4; | |
| 254033 | - nBlob -= 4; | |
| 254034 | - } | |
| 254035 | - } | |
| 254036 | - | |
| 254037 | - for(ii=0; ii<nBlob && pBlob[ii]; ii++); | |
| 254038 | - if( ii==0 || ii==nBlob ){ | |
| 254039 | - sqlite3_result_error_code(pCtx, SQLITE_ERROR); | |
| 254040 | - }else{ | |
| 254041 | - const char *pText = (const char*)&pBlob[ii+1]; | |
| 254042 | - sqlite3_result_text(pCtx, pText, nBlob-ii-1, SQLITE_TRANSIENT); | |
| 254043 | - } | |
| 254044 | - return; | |
| 254045 | - } | |
| 254046 | - | |
| 254047 | - sqlite3_result_value(pCtx, pVal); | |
| 254048 | -} | |
| 254049 | - | |
| 254050 | 254839 | /* |
| 254051 | 254840 | ** This is the xColumn method, called by SQLite to request a value from |
| 254052 | 254841 | ** the row that the supplied cursor currently points to. |
| 254053 | 254842 | */ |
| 254054 | 254843 | static int fts5ColumnMethod( |
| @@ -254087,26 +254876,31 @@ | ||
| 254087 | 254876 | if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){ |
| 254088 | 254877 | fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); |
| 254089 | 254878 | } |
| 254090 | 254879 | } |
| 254091 | 254880 | }else{ |
| 254092 | - /* A column created by the user containing values. */ | |
| 254093 | - int bNochange = sqlite3_vtab_nochange(pCtx); | |
| 254094 | - | |
| 254095 | - if( fts5IsContentless(pTab) ){ | |
| 254096 | - if( bNochange && pConfig->bContentlessDelete ){ | |
| 254097 | - fts5ResultError(pCtx, "cannot UPDATE a subset of " | |
| 254098 | - "columns on fts5 contentless-delete table: %s", pConfig->zName | |
| 254099 | - ); | |
| 254100 | - } | |
| 254101 | - }else if( bNochange==0 || pConfig->eContent!=FTS5_CONTENT_NORMAL ){ | |
| 254881 | + if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){ | |
| 254102 | 254882 | pConfig->pzErrmsg = &pTab->p.base.zErrMsg; |
| 254103 | 254883 | rc = fts5SeekCursor(pCsr, 1); |
| 254104 | 254884 | if( rc==SQLITE_OK ){ |
| 254105 | 254885 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); |
| 254106 | - fts5ExtractValueFromColumn(pCtx, pConfig, iCol, pVal); | |
| 254886 | + if( pConfig->bLocale | |
| 254887 | + && pConfig->eContent==FTS5_CONTENT_EXTERNAL | |
| 254888 | + && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |
| 254889 | + ){ | |
| 254890 | + const char *z = 0; | |
| 254891 | + int n = 0; | |
| 254892 | + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); | |
| 254893 | + if( rc==SQLITE_OK ){ | |
| 254894 | + sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT); | |
| 254895 | + } | |
| 254896 | + sqlite3Fts5ClearLocale(pConfig); | |
| 254897 | + }else{ | |
| 254898 | + sqlite3_result_value(pCtx, pVal); | |
| 254899 | + } | |
| 254107 | 254900 | } |
| 254901 | + | |
| 254108 | 254902 | pConfig->pzErrmsg = 0; |
| 254109 | 254903 | } |
| 254110 | 254904 | } |
| 254111 | 254905 | |
| 254112 | 254906 | return rc; |
| @@ -254625,11 +255419,11 @@ | ||
| 254625 | 255419 | int nArg, /* Number of args */ |
| 254626 | 255420 | sqlite3_value **apUnused /* Function arguments */ |
| 254627 | 255421 | ){ |
| 254628 | 255422 | assert( nArg==0 ); |
| 254629 | 255423 | UNUSED_PARAM2(nArg, apUnused); |
| 254630 | - sqlite3_result_text(pCtx, "fts5: 2024-09-02 18:41:59 e6bec37ea1ca51e1d048941ce4c5211d8fc5c5e3556a1441f9c79b036843f9e3", -1, SQLITE_TRANSIENT); | |
| 255424 | + sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT); | |
| 254631 | 255425 | } |
| 254632 | 255426 | |
| 254633 | 255427 | /* |
| 254634 | 255428 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 254635 | 255429 | ** |
| @@ -254664,36 +255458,48 @@ | ||
| 254664 | 255458 | nText = sqlite3_value_bytes(apArg[1]); |
| 254665 | 255459 | |
| 254666 | 255460 | if( zLocale==0 || zLocale[0]=='\0' ){ |
| 254667 | 255461 | sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); |
| 254668 | 255462 | }else{ |
| 255463 | + Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx); | |
| 254669 | 255464 | u8 *pBlob = 0; |
| 254670 | 255465 | u8 *pCsr = 0; |
| 254671 | 255466 | int nBlob = 0; |
| 254672 | - const int nHdr = 4; | |
| 254673 | - assert( sizeof(FTS5_LOCALE_HEADER)==nHdr+1 ); | |
| 254674 | 255467 | |
| 254675 | - nBlob = nHdr + nLocale + 1 + nText; | |
| 255468 | + nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText; | |
| 254676 | 255469 | pBlob = (u8*)sqlite3_malloc(nBlob); |
| 254677 | 255470 | if( pBlob==0 ){ |
| 254678 | 255471 | sqlite3_result_error_nomem(pCtx); |
| 254679 | 255472 | return; |
| 254680 | 255473 | } |
| 254681 | 255474 | |
| 254682 | 255475 | pCsr = pBlob; |
| 254683 | - memcpy(pCsr, FTS5_LOCALE_HEADER, nHdr); | |
| 254684 | - pCsr += nHdr; | |
| 255476 | + memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE); | |
| 255477 | + pCsr += FTS5_LOCALE_HDR_SIZE; | |
| 254685 | 255478 | memcpy(pCsr, zLocale, nLocale); |
| 254686 | 255479 | pCsr += nLocale; |
| 254687 | 255480 | (*pCsr++) = 0x00; |
| 254688 | 255481 | if( zText ) memcpy(pCsr, zText, nText); |
| 254689 | 255482 | assert( &pCsr[nText]==&pBlob[nBlob] ); |
| 254690 | 255483 | |
| 254691 | 255484 | sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); |
| 254692 | - sqlite3_result_subtype(pCtx, FTS5_LOCALE_SUBTYPE); | |
| 254693 | 255485 | } |
| 254694 | 255486 | } |
| 255487 | + | |
| 255488 | +/* | |
| 255489 | +** Implementation of fts5_insttoken() function. | |
| 255490 | +*/ | |
| 255491 | +static void fts5InsttokenFunc( | |
| 255492 | + sqlite3_context *pCtx, /* Function call context */ | |
| 255493 | + int nArg, /* Number of args */ | |
| 255494 | + sqlite3_value **apArg /* Function arguments */ | |
| 255495 | +){ | |
| 255496 | + assert( nArg==1 ); | |
| 255497 | + (void)nArg; | |
| 255498 | + sqlite3_result_value(pCtx, apArg[0]); | |
| 255499 | + sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE); | |
| 255500 | +} | |
| 254695 | 255501 | |
| 254696 | 255502 | /* |
| 254697 | 255503 | ** Return true if zName is the extension on one of the shadow tables used |
| 254698 | 255504 | ** by this module. |
| 254699 | 255505 | */ |
| @@ -254789,10 +255595,20 @@ | ||
| 254789 | 255595 | pGlobal->api.xCreateFunction = fts5CreateAux; |
| 254790 | 255596 | pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; |
| 254791 | 255597 | pGlobal->api.xFindTokenizer = fts5FindTokenizer; |
| 254792 | 255598 | pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; |
| 254793 | 255599 | pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; |
| 255600 | + | |
| 255601 | + /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector. | |
| 255602 | + ** The constants below were generated randomly. */ | |
| 255603 | + sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr); | |
| 255604 | + pGlobal->aLocaleHdr[0] ^= 0xF924976D; | |
| 255605 | + pGlobal->aLocaleHdr[1] ^= 0x16596E13; | |
| 255606 | + pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA; | |
| 255607 | + pGlobal->aLocaleHdr[3] ^= 0x9B03A67F; | |
| 255608 | + assert( sizeof(pGlobal->aLocaleHdr)==16 ); | |
| 255609 | + | |
| 254794 | 255610 | rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); |
| 254795 | 255611 | if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); |
| 254796 | 255612 | if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); |
| 254797 | 255613 | if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); |
| 254798 | 255614 | if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); |
| @@ -254810,13 +255626,20 @@ | ||
| 254810 | 255626 | ); |
| 254811 | 255627 | } |
| 254812 | 255628 | if( rc==SQLITE_OK ){ |
| 254813 | 255629 | rc = sqlite3_create_function( |
| 254814 | 255630 | db, "fts5_locale", 2, |
| 254815 | - SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, | |
| 255631 | + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE, | |
| 254816 | 255632 | p, fts5LocaleFunc, 0, 0 |
| 254817 | 255633 | ); |
| 255634 | + } | |
| 255635 | + if( rc==SQLITE_OK ){ | |
| 255636 | + rc = sqlite3_create_function( | |
| 255637 | + db, "fts5_insttoken", 1, | |
| 255638 | + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, | |
| 255639 | + p, fts5InsttokenFunc, 0, 0 | |
| 255640 | + ); | |
| 254818 | 255641 | } |
| 254819 | 255642 | } |
| 254820 | 255643 | |
| 254821 | 255644 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 254822 | 255645 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| @@ -255015,24 +255838,39 @@ | ||
| 255015 | 255838 | ); |
| 255016 | 255839 | break; |
| 255017 | 255840 | |
| 255018 | 255841 | case FTS5_STMT_INSERT_CONTENT: |
| 255019 | 255842 | case FTS5_STMT_REPLACE_CONTENT: { |
| 255020 | - int nCol = pC->nCol + 1; | |
| 255021 | - char *zBind; | |
| 255843 | + char *zBind = 0; | |
| 255022 | 255844 | int i; |
| 255023 | 255845 | |
| 255024 | - zBind = sqlite3_malloc64(1 + nCol*2); | |
| 255025 | - if( zBind ){ | |
| 255026 | - for(i=0; i<nCol; i++){ | |
| 255027 | - zBind[i*2] = '?'; | |
| 255028 | - zBind[i*2 + 1] = ','; | |
| 255029 | - } | |
| 255030 | - zBind[i*2-1] = '\0'; | |
| 255031 | - zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind); | |
| 255032 | - sqlite3_free(zBind); | |
| 255033 | - } | |
| 255846 | + assert( pC->eContent==FTS5_CONTENT_NORMAL | |
| 255847 | + || pC->eContent==FTS5_CONTENT_UNINDEXED | |
| 255848 | + ); | |
| 255849 | + | |
| 255850 | + /* Add bindings for the "c*" columns - those that store the actual | |
| 255851 | + ** table content. If eContent==NORMAL, then there is one binding | |
| 255852 | + ** for each column. Or, if eContent==UNINDEXED, then there are only | |
| 255853 | + ** bindings for the UNINDEXED columns. */ | |
| 255854 | + for(i=0; rc==SQLITE_OK && i<(pC->nCol+1); i++){ | |
| 255855 | + if( !i || pC->eContent==FTS5_CONTENT_NORMAL || pC->abUnindexed[i-1] ){ | |
| 255856 | + zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1); | |
| 255857 | + } | |
| 255858 | + } | |
| 255859 | + | |
| 255860 | + /* Add bindings for any "l*" columns. Only non-UNINDEXED columns | |
| 255861 | + ** require these. */ | |
| 255862 | + if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL ){ | |
| 255863 | + for(i=0; rc==SQLITE_OK && i<pC->nCol; i++){ | |
| 255864 | + if( pC->abUnindexed[i]==0 ){ | |
| 255865 | + zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2); | |
| 255866 | + } | |
| 255867 | + } | |
| 255868 | + } | |
| 255869 | + | |
| 255870 | + zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind); | |
| 255871 | + sqlite3_free(zBind); | |
| 255034 | 255872 | break; |
| 255035 | 255873 | } |
| 255036 | 255874 | |
| 255037 | 255875 | case FTS5_STMT_REPLACE_DOCSIZE: |
| 255038 | 255876 | zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, |
| @@ -255214,23 +256052,37 @@ | ||
| 255214 | 256052 | p->aTotalSize = (i64*)&p[1]; |
| 255215 | 256053 | p->pConfig = pConfig; |
| 255216 | 256054 | p->pIndex = pIndex; |
| 255217 | 256055 | |
| 255218 | 256056 | if( bCreate ){ |
| 255219 | - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ | |
| 256057 | + if( pConfig->eContent==FTS5_CONTENT_NORMAL | |
| 256058 | + || pConfig->eContent==FTS5_CONTENT_UNINDEXED | |
| 256059 | + ){ | |
| 255220 | 256060 | int nDefn = 32 + pConfig->nCol*10; |
| 255221 | - char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); | |
| 256061 | + char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); | |
| 255222 | 256062 | if( zDefn==0 ){ |
| 255223 | 256063 | rc = SQLITE_NOMEM; |
| 255224 | 256064 | }else{ |
| 255225 | 256065 | int i; |
| 255226 | 256066 | int iOff; |
| 255227 | 256067 | sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); |
| 255228 | 256068 | iOff = (int)strlen(zDefn); |
| 255229 | 256069 | for(i=0; i<pConfig->nCol; i++){ |
| 255230 | - sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); | |
| 255231 | - iOff += (int)strlen(&zDefn[iOff]); | |
| 256070 | + if( pConfig->eContent==FTS5_CONTENT_NORMAL | |
| 256071 | + || pConfig->abUnindexed[i] | |
| 256072 | + ){ | |
| 256073 | + sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); | |
| 256074 | + iOff += (int)strlen(&zDefn[iOff]); | |
| 256075 | + } | |
| 256076 | + } | |
| 256077 | + if( pConfig->bLocale ){ | |
| 256078 | + for(i=0; i<pConfig->nCol; i++){ | |
| 256079 | + if( pConfig->abUnindexed[i]==0 ){ | |
| 256080 | + sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); | |
| 256081 | + iOff += (int)strlen(&zDefn[iOff]); | |
| 256082 | + } | |
| 256083 | + } | |
| 255232 | 256084 | } |
| 255233 | 256085 | rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); |
| 255234 | 256086 | } |
| 255235 | 256087 | sqlite3_free(zDefn); |
| 255236 | 256088 | } |
| @@ -255379,33 +256231,43 @@ | ||
| 255379 | 256231 | for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ |
| 255380 | 256232 | if( pConfig->abUnindexed[iCol-1]==0 ){ |
| 255381 | 256233 | sqlite3_value *pVal = 0; |
| 255382 | 256234 | const char *pText = 0; |
| 255383 | 256235 | int nText = 0; |
| 255384 | - int bReset = 0; | |
| 256236 | + const char *pLoc = 0; | |
| 256237 | + int nLoc = 0; | |
| 255385 | 256238 | |
| 255386 | 256239 | assert( pSeek==0 || apVal==0 ); |
| 255387 | 256240 | assert( pSeek!=0 || apVal!=0 ); |
| 255388 | 256241 | if( pSeek ){ |
| 255389 | 256242 | pVal = sqlite3_column_value(pSeek, iCol); |
| 255390 | 256243 | }else{ |
| 255391 | 256244 | pVal = apVal[iCol-1]; |
| 255392 | 256245 | } |
| 255393 | 256246 | |
| 255394 | - rc = sqlite3Fts5ExtractText( | |
| 255395 | - pConfig, pVal, pSeek!=0, &bReset, &pText, &nText | |
| 255396 | - ); | |
| 256247 | + if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |
| 256248 | + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |
| 256249 | + }else{ | |
| 256250 | + pText = (const char*)sqlite3_value_text(pVal); | |
| 256251 | + nText = sqlite3_value_bytes(pVal); | |
| 256252 | + if( pConfig->bLocale && pSeek ){ | |
| 256253 | + pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol); | |
| 256254 | + nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol); | |
| 256255 | + } | |
| 256256 | + } | |
| 256257 | + | |
| 255397 | 256258 | if( rc==SQLITE_OK ){ |
| 256259 | + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |
| 255398 | 256260 | ctx.szCol = 0; |
| 255399 | 256261 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, |
| 255400 | 256262 | pText, nText, (void*)&ctx, fts5StorageInsertCallback |
| 255401 | 256263 | ); |
| 255402 | 256264 | p->aTotalSize[iCol-1] -= (i64)ctx.szCol; |
| 255403 | 256265 | if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ |
| 255404 | 256266 | rc = FTS5_CORRUPT; |
| 255405 | 256267 | } |
| 255406 | - if( bReset ) sqlite3Fts5ClearLocale(pConfig); | |
| 256268 | + sqlite3Fts5ClearLocale(pConfig); | |
| 255407 | 256269 | } |
| 255408 | 256270 | } |
| 255409 | 256271 | } |
| 255410 | 256272 | if( rc==SQLITE_OK && p->nTotalRow<1 ){ |
| 255411 | 256273 | rc = FTS5_CORRUPT; |
| @@ -255446,11 +256308,13 @@ | ||
| 255446 | 256308 | i64 iOrigin = 0; |
| 255447 | 256309 | sqlite3_stmt *pLookup = 0; |
| 255448 | 256310 | int rc = SQLITE_OK; |
| 255449 | 256311 | |
| 255450 | 256312 | assert( p->pConfig->bContentlessDelete ); |
| 255451 | - assert( p->pConfig->eContent==FTS5_CONTENT_NONE ); | |
| 256313 | + assert( p->pConfig->eContent==FTS5_CONTENT_NONE | |
| 256314 | + || p->pConfig->eContent==FTS5_CONTENT_UNINDEXED | |
| 256315 | + ); | |
| 255452 | 256316 | |
| 255453 | 256317 | /* Look up the origin of the document in the %_docsize table. Store |
| 255454 | 256318 | ** this in stack variable iOrigin. */ |
| 255455 | 256319 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); |
| 255456 | 256320 | if( rc==SQLITE_OK ){ |
| @@ -255570,10 +256434,16 @@ | ||
| 255570 | 256434 | } |
| 255571 | 256435 | |
| 255572 | 256436 | if( rc==SQLITE_OK ){ |
| 255573 | 256437 | if( p->pConfig->bContentlessDelete ){ |
| 255574 | 256438 | rc = fts5StorageContentlessDelete(p, iDel); |
| 256439 | + if( rc==SQLITE_OK | |
| 256440 | + && bSaveRow | |
| 256441 | + && p->pConfig->eContent==FTS5_CONTENT_UNINDEXED | |
| 256442 | + ){ | |
| 256443 | + rc = sqlite3Fts5StorageFindDeleteRow(p, iDel); | |
| 256444 | + } | |
| 255575 | 256445 | }else{ |
| 255576 | 256446 | rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); |
| 255577 | 256447 | } |
| 255578 | 256448 | } |
| 255579 | 256449 | |
| @@ -255586,11 +256456,13 @@ | ||
| 255586 | 256456 | rc = sqlite3_reset(pDel); |
| 255587 | 256457 | } |
| 255588 | 256458 | } |
| 255589 | 256459 | |
| 255590 | 256460 | /* Delete the %_content record */ |
| 255591 | - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ | |
| 256461 | + if( pConfig->eContent==FTS5_CONTENT_NORMAL | |
| 256462 | + || pConfig->eContent==FTS5_CONTENT_UNINDEXED | |
| 256463 | + ){ | |
| 255592 | 256464 | if( rc==SQLITE_OK ){ |
| 255593 | 256465 | rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); |
| 255594 | 256466 | } |
| 255595 | 256467 | if( rc==SQLITE_OK ){ |
| 255596 | 256468 | sqlite3_bind_int64(pDel, 1, iDel); |
| @@ -255618,12 +256490,17 @@ | ||
| 255618 | 256490 | pConfig->zDb, pConfig->zName, |
| 255619 | 256491 | pConfig->zDb, pConfig->zName |
| 255620 | 256492 | ); |
| 255621 | 256493 | if( rc==SQLITE_OK && pConfig->bColumnsize ){ |
| 255622 | 256494 | rc = fts5ExecPrintf(pConfig->db, 0, |
| 255623 | - "DELETE FROM %Q.'%q_docsize';", | |
| 255624 | - pConfig->zDb, pConfig->zName | |
| 256495 | + "DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName | |
| 256496 | + ); | |
| 256497 | + } | |
| 256498 | + | |
| 256499 | + if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ | |
| 256500 | + rc = fts5ExecPrintf(pConfig->db, 0, | |
| 256501 | + "DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName | |
| 255625 | 256502 | ); |
| 255626 | 256503 | } |
| 255627 | 256504 | |
| 255628 | 256505 | /* Reinitialize the %_data table. This call creates the initial structure |
| 255629 | 256506 | ** and averages records. */ |
| @@ -255660,24 +256537,39 @@ | ||
| 255660 | 256537 | sqlite3Fts5BufferZero(&buf); |
| 255661 | 256538 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
| 255662 | 256539 | for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ |
| 255663 | 256540 | ctx.szCol = 0; |
| 255664 | 256541 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ |
| 255665 | - int bReset = 0; /* True if tokenizer locale must be reset */ | |
| 255666 | 256542 | int nText = 0; /* Size of pText in bytes */ |
| 255667 | 256543 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 256544 | + int nLoc = 0; /* Size of pLoc in bytes */ | |
| 256545 | + const char *pLoc = 0; /* Pointer to buffer containing text value */ | |
| 256546 | + | |
| 255668 | 256547 | sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); |
| 256548 | + if( pConfig->eContent==FTS5_CONTENT_EXTERNAL | |
| 256549 | + && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |
| 256550 | + ){ | |
| 256551 | + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |
| 256552 | + }else{ | |
| 256553 | + pText = (const char*)sqlite3_value_text(pVal); | |
| 256554 | + nText = sqlite3_value_bytes(pVal); | |
| 256555 | + if( pConfig->bLocale ){ | |
| 256556 | + int iCol = ctx.iCol + 1 + pConfig->nCol; | |
| 256557 | + pLoc = (const char*)sqlite3_column_text(pScan, iCol); | |
| 256558 | + nLoc = sqlite3_column_bytes(pScan, iCol); | |
| 256559 | + } | |
| 256560 | + } | |
| 255669 | 256561 | |
| 255670 | - rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &pText, &nText); | |
| 255671 | 256562 | if( rc==SQLITE_OK ){ |
| 256563 | + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |
| 255672 | 256564 | rc = sqlite3Fts5Tokenize(pConfig, |
| 255673 | 256565 | FTS5_TOKENIZE_DOCUMENT, |
| 255674 | 256566 | pText, nText, |
| 255675 | 256567 | (void*)&ctx, |
| 255676 | 256568 | fts5StorageInsertCallback |
| 255677 | 256569 | ); |
| 255678 | - if( bReset ) sqlite3Fts5ClearLocale(pConfig); | |
| 256570 | + sqlite3Fts5ClearLocale(pConfig); | |
| 255679 | 256571 | } |
| 255680 | 256572 | } |
| 255681 | 256573 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); |
| 255682 | 256574 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; |
| 255683 | 256575 | } |
| @@ -255740,53 +256632,75 @@ | ||
| 255740 | 256632 | /* |
| 255741 | 256633 | ** Insert a new row into the FTS content table. |
| 255742 | 256634 | */ |
| 255743 | 256635 | static int sqlite3Fts5StorageContentInsert( |
| 255744 | 256636 | Fts5Storage *p, |
| 256637 | + int bReplace, /* True to use REPLACE instead of INSERT */ | |
| 255745 | 256638 | sqlite3_value **apVal, |
| 255746 | 256639 | i64 *piRowid |
| 255747 | 256640 | ){ |
| 255748 | 256641 | Fts5Config *pConfig = p->pConfig; |
| 255749 | 256642 | int rc = SQLITE_OK; |
| 255750 | 256643 | |
| 255751 | 256644 | /* Insert the new row into the %_content table. */ |
| 255752 | - if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ | |
| 256645 | + if( pConfig->eContent!=FTS5_CONTENT_NORMAL | |
| 256646 | + && pConfig->eContent!=FTS5_CONTENT_UNINDEXED | |
| 256647 | + ){ | |
| 255753 | 256648 | if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ |
| 255754 | 256649 | *piRowid = sqlite3_value_int64(apVal[1]); |
| 255755 | 256650 | }else{ |
| 255756 | 256651 | rc = fts5StorageNewRowid(p, piRowid); |
| 255757 | 256652 | } |
| 255758 | 256653 | }else{ |
| 255759 | 256654 | sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ |
| 255760 | 256655 | int i; /* Counter variable */ |
| 255761 | - rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); | |
| 255762 | - for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ | |
| 255763 | - sqlite3_value *pVal = apVal[i]; | |
| 255764 | - if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ | |
| 255765 | - /* This is an UPDATE statement, and column (i-2) was not modified. | |
| 255766 | - ** Retrieve the value from Fts5Storage.pSavedRow instead. */ | |
| 255767 | - pVal = sqlite3_column_value(p->pSavedRow, i-1); | |
| 255768 | - }else if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE ){ | |
| 255769 | - assert( pConfig->bLocale ); | |
| 255770 | - assert( i>1 ); | |
| 255771 | - if( pConfig->abUnindexed[i-2] ){ | |
| 255772 | - /* At attempt to insert an fts5_locale() value into an UNINDEXED | |
| 255773 | - ** column. Strip the locale away and just bind the text. */ | |
| 256656 | + | |
| 256657 | + assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT ); | |
| 256658 | + assert( bReplace==0 || bReplace==1 ); | |
| 256659 | + rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0); | |
| 256660 | + if( pInsert ) sqlite3_clear_bindings(pInsert); | |
| 256661 | + | |
| 256662 | + /* Bind the rowid value */ | |
| 256663 | + sqlite3_bind_value(pInsert, 1, apVal[1]); | |
| 256664 | + | |
| 256665 | + /* Loop through values for user-defined columns. i=2 is the leftmost | |
| 256666 | + ** user-defined column. As is column 1 of pSavedRow. */ | |
| 256667 | + for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ | |
| 256668 | + int bUnindexed = pConfig->abUnindexed[i-2]; | |
| 256669 | + if( pConfig->eContent==FTS5_CONTENT_NORMAL || bUnindexed ){ | |
| 256670 | + sqlite3_value *pVal = apVal[i]; | |
| 256671 | + | |
| 256672 | + if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ | |
| 256673 | + /* This is an UPDATE statement, and user-defined column (i-2) was not | |
| 256674 | + ** modified. Retrieve the value from Fts5Storage.pSavedRow. */ | |
| 256675 | + pVal = sqlite3_column_value(p->pSavedRow, i-1); | |
| 256676 | + if( pConfig->bLocale && bUnindexed==0 ){ | |
| 256677 | + sqlite3_bind_value(pInsert, pConfig->nCol + i, | |
| 256678 | + sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1) | |
| 256679 | + ); | |
| 256680 | + } | |
| 256681 | + }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |
| 255774 | 256682 | const char *pText = 0; |
| 256683 | + const char *pLoc = 0; | |
| 255775 | 256684 | int nText = 0; |
| 255776 | - rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, 0, &pText, &nText); | |
| 255777 | - sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); | |
| 255778 | - }else{ | |
| 255779 | - const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); | |
| 255780 | - int nBlob = sqlite3_value_bytes(pVal); | |
| 255781 | - assert( nBlob>4 ); | |
| 255782 | - sqlite3_bind_blob(pInsert, i, pBlob+4, nBlob-4, SQLITE_TRANSIENT); | |
| 255783 | - } | |
| 255784 | - continue; | |
| 255785 | - } | |
| 255786 | - | |
| 255787 | - rc = sqlite3_bind_value(pInsert, i, pVal); | |
| 256685 | + int nLoc = 0; | |
| 256686 | + assert( pConfig->bLocale ); | |
| 256687 | + | |
| 256688 | + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |
| 256689 | + if( rc==SQLITE_OK ){ | |
| 256690 | + sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); | |
| 256691 | + if( bUnindexed==0 ){ | |
| 256692 | + int iLoc = pConfig->nCol + i; | |
| 256693 | + sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT); | |
| 256694 | + } | |
| 256695 | + } | |
| 256696 | + | |
| 256697 | + continue; | |
| 256698 | + } | |
| 256699 | + | |
| 256700 | + rc = sqlite3_bind_value(pInsert, i, pVal); | |
| 256701 | + } | |
| 255788 | 256702 | } |
| 255789 | 256703 | if( rc==SQLITE_OK ){ |
| 255790 | 256704 | sqlite3_step(pInsert); |
| 255791 | 256705 | rc = sqlite3_reset(pInsert); |
| 255792 | 256706 | } |
| @@ -255817,27 +256731,41 @@ | ||
| 255817 | 256731 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
| 255818 | 256732 | } |
| 255819 | 256733 | for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ |
| 255820 | 256734 | ctx.szCol = 0; |
| 255821 | 256735 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ |
| 255822 | - int bReset = 0; /* True if tokenizer locale must be reset */ | |
| 255823 | 256736 | int nText = 0; /* Size of pText in bytes */ |
| 255824 | 256737 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 256738 | + int nLoc = 0; /* Size of pText in bytes */ | |
| 256739 | + const char *pLoc = 0; /* Pointer to buffer containing text value */ | |
| 256740 | + | |
| 255825 | 256741 | sqlite3_value *pVal = apVal[ctx.iCol+2]; |
| 255826 | - int bDisk = 0; | |
| 255827 | 256742 | if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ |
| 255828 | 256743 | pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); |
| 255829 | - bDisk = 1; | |
| 256744 | + if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ | |
| 256745 | + int iCol = ctx.iCol + 1 + pConfig->nCol; | |
| 256746 | + pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol); | |
| 256747 | + nLoc = sqlite3_column_bytes(p->pSavedRow, iCol); | |
| 256748 | + } | |
| 256749 | + }else{ | |
| 256750 | + pVal = apVal[ctx.iCol+2]; | |
| 256751 | + } | |
| 256752 | + | |
| 256753 | + if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |
| 256754 | + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |
| 256755 | + }else{ | |
| 256756 | + pText = (const char*)sqlite3_value_text(pVal); | |
| 256757 | + nText = sqlite3_value_bytes(pVal); | |
| 255830 | 256758 | } |
| 255831 | - rc = sqlite3Fts5ExtractText(pConfig, pVal, bDisk, &bReset, &pText,&nText); | |
| 256759 | + | |
| 255832 | 256760 | if( rc==SQLITE_OK ){ |
| 255833 | - assert( bReset==0 || pConfig->bLocale ); | |
| 256761 | + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |
| 255834 | 256762 | rc = sqlite3Fts5Tokenize(pConfig, |
| 255835 | 256763 | FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, |
| 255836 | 256764 | fts5StorageInsertCallback |
| 255837 | 256765 | ); |
| 255838 | - if( bReset ) sqlite3Fts5ClearLocale(pConfig); | |
| 256766 | + sqlite3Fts5ClearLocale(pConfig); | |
| 255839 | 256767 | } |
| 255840 | 256768 | } |
| 255841 | 256769 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); |
| 255842 | 256770 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; |
| 255843 | 256771 | } |
| @@ -255998,41 +256926,65 @@ | ||
| 255998 | 256926 | } |
| 255999 | 256927 | if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 256000 | 256928 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 256001 | 256929 | } |
| 256002 | 256930 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 256003 | - if( pConfig->abUnindexed[i] ) continue; | |
| 256004 | - ctx.iCol = i; | |
| 256005 | - ctx.szCol = 0; | |
| 256006 | - if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 256007 | - rc = sqlite3Fts5TermsetNew(&ctx.pTermset); | |
| 256008 | - } | |
| 256009 | - if( rc==SQLITE_OK ){ | |
| 256010 | - int bReset = 0; /* True if tokenizer locale must be reset */ | |
| 256011 | - int nText = 0; /* Size of pText in bytes */ | |
| 256012 | - const char *pText = 0; /* Pointer to buffer containing text value */ | |
| 256013 | - | |
| 256014 | - rc = sqlite3Fts5ExtractText(pConfig, | |
| 256015 | - sqlite3_column_value(pScan, i+1), 1, &bReset, &pText, &nText | |
| 256016 | - ); | |
| 256017 | - if( rc==SQLITE_OK ){ | |
| 256931 | + if( pConfig->abUnindexed[i]==0 ){ | |
| 256932 | + const char *pText = 0; | |
| 256933 | + int nText = 0; | |
| 256934 | + const char *pLoc = 0; | |
| 256935 | + int nLoc = 0; | |
| 256936 | + sqlite3_value *pVal = sqlite3_column_value(pScan, i+1); | |
| 256937 | + | |
| 256938 | + if( pConfig->eContent==FTS5_CONTENT_EXTERNAL | |
| 256939 | + && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |
| 256940 | + ){ | |
| 256941 | + rc = sqlite3Fts5DecodeLocaleValue( | |
| 256942 | + pVal, &pText, &nText, &pLoc, &nLoc | |
| 256943 | + ); | |
| 256944 | + }else{ | |
| 256945 | + if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ | |
| 256946 | + int iCol = i + 1 + pConfig->nCol; | |
| 256947 | + pLoc = (const char*)sqlite3_column_text(pScan, iCol); | |
| 256948 | + nLoc = sqlite3_column_bytes(pScan, iCol); | |
| 256949 | + } | |
| 256950 | + pText = (const char*)sqlite3_value_text(pVal); | |
| 256951 | + nText = sqlite3_value_bytes(pVal); | |
| 256952 | + } | |
| 256953 | + | |
| 256954 | + ctx.iCol = i; | |
| 256955 | + ctx.szCol = 0; | |
| 256956 | + | |
| 256957 | + if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 256958 | + rc = sqlite3Fts5TermsetNew(&ctx.pTermset); | |
| 256959 | + } | |
| 256960 | + | |
| 256961 | + if( rc==SQLITE_OK ){ | |
| 256962 | + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |
| 256018 | 256963 | rc = sqlite3Fts5Tokenize(pConfig, |
| 256019 | 256964 | FTS5_TOKENIZE_DOCUMENT, |
| 256020 | 256965 | pText, nText, |
| 256021 | 256966 | (void*)&ctx, |
| 256022 | 256967 | fts5StorageIntegrityCallback |
| 256023 | 256968 | ); |
| 256024 | - if( bReset ) sqlite3Fts5ClearLocale(pConfig); | |
| 256025 | - } | |
| 256026 | - } | |
| 256027 | - if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ | |
| 256028 | - rc = FTS5_CORRUPT; | |
| 256029 | - } | |
| 256030 | - aTotalSize[i] += ctx.szCol; | |
| 256031 | - if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 256032 | - sqlite3Fts5TermsetFree(ctx.pTermset); | |
| 256033 | - ctx.pTermset = 0; | |
| 256969 | + sqlite3Fts5ClearLocale(pConfig); | |
| 256970 | + } | |
| 256971 | + | |
| 256972 | + /* If this is not a columnsize=0 database, check that the number | |
| 256973 | + ** of tokens in the value matches the aColSize[] value read from | |
| 256974 | + ** the %_docsize table. */ | |
| 256975 | + if( rc==SQLITE_OK | |
| 256976 | + && pConfig->bColumnsize | |
| 256977 | + && ctx.szCol!=aColSize[i] | |
| 256978 | + ){ | |
| 256979 | + rc = FTS5_CORRUPT; | |
| 256980 | + } | |
| 256981 | + aTotalSize[i] += ctx.szCol; | |
| 256982 | + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 256983 | + sqlite3Fts5TermsetFree(ctx.pTermset); | |
| 256984 | + ctx.pTermset = 0; | |
| 256985 | + } | |
| 256034 | 256986 | } |
| 256035 | 256987 | } |
| 256036 | 256988 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 256037 | 256989 | ctx.pTermset = 0; |
| 256038 | 256990 | |
| @@ -256454,11 +257406,11 @@ | ||
| 256454 | 257406 | |
| 256455 | 257407 | #define READ_UTF8(zIn, zTerm, c) \ |
| 256456 | 257408 | c = *(zIn++); \ |
| 256457 | 257409 | if( c>=0xc0 ){ \ |
| 256458 | 257410 | c = sqlite3Utf8Trans1[c-0xc0]; \ |
| 256459 | - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ | |
| 257411 | + while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \ | |
| 256460 | 257412 | c = (c<<6) + (0x3f & *(zIn++)); \ |
| 256461 | 257413 | } \ |
| 256462 | 257414 | if( c<0x80 \ |
| 256463 | 257415 | || (c&0xFFFFF800)==0xD800 \ |
| 256464 | 257416 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| @@ -257609,22 +258561,22 @@ | ||
| 257609 | 258561 | int rc = SQLITE_OK; |
| 257610 | 258562 | char aBuf[32]; |
| 257611 | 258563 | char *zOut = aBuf; |
| 257612 | 258564 | int ii; |
| 257613 | 258565 | const unsigned char *zIn = (const unsigned char*)pText; |
| 257614 | - const unsigned char *zEof = &zIn[nText]; | |
| 257615 | - u32 iCode; | |
| 258566 | + const unsigned char *zEof = (zIn ? &zIn[nText] : 0); | |
| 258567 | + u32 iCode = 0; | |
| 257616 | 258568 | int aStart[3]; /* Input offset of each character in aBuf[] */ |
| 257617 | 258569 | |
| 257618 | 258570 | UNUSED_PARAM(unusedFlags); |
| 257619 | 258571 | |
| 257620 | 258572 | /* Populate aBuf[] with the characters for the first trigram. */ |
| 257621 | 258573 | for(ii=0; ii<3; ii++){ |
| 257622 | 258574 | do { |
| 257623 | 258575 | aStart[ii] = zIn - (const unsigned char*)pText; |
| 258576 | + if( zIn>=zEof ) return SQLITE_OK; | |
| 257624 | 258577 | READ_UTF8(zIn, zEof, iCode); |
| 257625 | - if( iCode==0 ) return SQLITE_OK; | |
| 257626 | 258578 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); |
| 257627 | 258579 | }while( iCode==0 ); |
| 257628 | 258580 | WRITE_UTF8(zOut, iCode); |
| 257629 | 258581 | } |
| 257630 | 258582 | |
| @@ -257641,12 +258593,15 @@ | ||
| 257641 | 258593 | const char *z1; |
| 257642 | 258594 | |
| 257643 | 258595 | /* Read characters from the input up until the first non-diacritic */ |
| 257644 | 258596 | do { |
| 257645 | 258597 | iNext = zIn - (const unsigned char*)pText; |
| 258598 | + if( zIn>=zEof ){ | |
| 258599 | + iCode = 0; | |
| 258600 | + break; | |
| 258601 | + } | |
| 257646 | 258602 | READ_UTF8(zIn, zEof, iCode); |
| 257647 | - if( iCode==0 ) break; | |
| 257648 | 258603 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); |
| 257649 | 258604 | }while( iCode==0 ); |
| 257650 | 258605 | |
| 257651 | 258606 | /* Pass the current trigram back to fts5 */ |
| 257652 | 258607 | rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); |
| @@ -259679,11 +260634,11 @@ | ||
| 259679 | 260634 | |
| 259680 | 260635 | return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); |
| 259681 | 260636 | } |
| 259682 | 260637 | |
| 259683 | 260638 | |
| 259684 | - | |
| 260639 | +/* Here ends the fts5.c composite file. */ | |
| 259685 | 260640 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ |
| 259686 | 260641 | |
| 259687 | 260642 | /************** End of fts5.c ************************************************/ |
| 259688 | 260643 | /************** Begin file stmt.c ********************************************/ |
| 259689 | 260644 | /* |
| @@ -260035,6 +260990,7 @@ | ||
| 260035 | 260990 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 260036 | 260991 | |
| 260037 | 260992 | /************** End of stmt.c ************************************************/ |
| 260038 | 260993 | /* Return the source-id for this library */ |
| 260039 | 260994 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 260995 | +#endif /* SQLITE_AMALGAMATION */ | |
| 260040 | 260996 | /************************** End of sqlite3.c ******************************/ |
| 260041 | 260997 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.47.0. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -16,12 +16,15 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** 7891a266c4425722ae8b9231397ef9e42e24. |
| 22 | */ |
| 23 | #define SQLITE_CORE 1 |
| 24 | #define SQLITE_AMALGAMATION 1 |
| 25 | #ifndef SQLITE_PRIVATE |
| 26 | # define SQLITE_PRIVATE static |
| 27 | #endif |
| @@ -460,13 +463,13 @@ | |
| 460 | ** |
| 461 | ** See also: [sqlite3_libversion()], |
| 462 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 463 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 464 | */ |
| 465 | #define SQLITE_VERSION "3.47.0" |
| 466 | #define SQLITE_VERSION_NUMBER 3047000 |
| 467 | #define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" |
| 468 | |
| 469 | /* |
| 470 | ** CAPI3REF: Run-Time Library Version Numbers |
| 471 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 472 | ** |
| @@ -966,10 +969,17 @@ | |
| 966 | ** |
| 967 | ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying |
| 968 | ** filesystem supports doing multiple write operations atomically when those |
| 969 | ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and |
| 970 | ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. |
| 971 | */ |
| 972 | #define SQLITE_IOCAP_ATOMIC 0x00000001 |
| 973 | #define SQLITE_IOCAP_ATOMIC512 0x00000002 |
| 974 | #define SQLITE_IOCAP_ATOMIC1K 0x00000004 |
| 975 | #define SQLITE_IOCAP_ATOMIC2K 0x00000008 |
| @@ -982,10 +992,11 @@ | |
| 982 | #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 |
| 983 | #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 |
| 984 | #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 |
| 985 | #define SQLITE_IOCAP_IMMUTABLE 0x00002000 |
| 986 | #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 |
| 987 | |
| 988 | /* |
| 989 | ** CAPI3REF: File Locking Levels |
| 990 | ** |
| 991 | ** SQLite uses one of these integer values as the second |
| @@ -1128,10 +1139,11 @@ | |
| 1128 | ** <li> [SQLITE_IOCAP_SEQUENTIAL] |
| 1129 | ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] |
| 1130 | ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] |
| 1131 | ** <li> [SQLITE_IOCAP_IMMUTABLE] |
| 1132 | ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] |
| 1133 | ** </ul> |
| 1134 | ** |
| 1135 | ** The SQLITE_IOCAP_ATOMIC property means that all writes of |
| 1136 | ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values |
| 1137 | ** mean that writes of blocks that are nnn bytes in size and |
| @@ -1405,10 +1417,15 @@ | |
| 1405 | ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This |
| 1406 | ** opcode causes the xFileControl method to swap the file handle with the one |
| 1407 | ** pointed to by the pArg argument. This capability is used during testing |
| 1408 | ** and only needs to be supported when SQLITE_TEST is defined. |
| 1409 | ** |
| 1410 | ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] |
| 1411 | ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might |
| 1412 | ** be advantageous to block on the next WAL lock if the lock is not immediately |
| 1413 | ** available. The WAL subsystem issues this signal during rare |
| 1414 | ** circumstances in order to fix a problem with priority inversion. |
| @@ -1558,10 +1575,11 @@ | |
| 1558 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1559 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1560 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1561 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1562 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1563 | |
| 1564 | /* deprecated names */ |
| 1565 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1566 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1567 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -2936,14 +2954,18 @@ | |
| 2936 | ** |
| 2937 | ** ^These functions return the number of rows modified, inserted or |
| 2938 | ** deleted by the most recently completed INSERT, UPDATE or DELETE |
| 2939 | ** statement on the database connection specified by the only parameter. |
| 2940 | ** The two functions are identical except for the type of the return value |
| 2941 | ** and that if the number of rows modified by the most recent INSERT, UPDATE |
| 2942 | ** or DELETE is greater than the maximum value supported by type "int", then |
| 2943 | ** the return value of sqlite3_changes() is undefined. ^Executing any other |
| 2944 | ** type of SQL statement does not modify the value returned by these functions. |
| 2945 | ** |
| 2946 | ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are |
| 2947 | ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], |
| 2948 | ** [foreign key actions] or [REPLACE] constraint resolution are not counted. |
| 2949 | ** |
| @@ -4499,15 +4521,26 @@ | |
| 4499 | ** |
| 4500 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4501 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4502 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4503 | ** any virtual tables. |
| 4504 | ** </dl> |
| 4505 | */ |
| 4506 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4507 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4508 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4509 | |
| 4510 | /* |
| 4511 | ** CAPI3REF: Compiling An SQL Statement |
| 4512 | ** KEYWORDS: {SQL statement compiler} |
| 4513 | ** METHOD: sqlite3 |
| @@ -4536,17 +4569,21 @@ | |
| 4536 | ** and sqlite3_prepare_v3() |
| 4537 | ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), |
| 4538 | ** and sqlite3_prepare16_v3() use UTF-16. |
| 4539 | ** |
| 4540 | ** ^If the nByte argument is negative, then zSql is read up to the |
| 4541 | ** first zero terminator. ^If nByte is positive, then it is the |
| 4542 | ** number of bytes read from zSql. ^If nByte is zero, then no prepared |
| 4543 | ** statement is generated. |
| 4544 | ** If the caller knows that the supplied string is nul-terminated, then |
| 4545 | ** there is a small performance advantage to passing an nByte parameter that |
| 4546 | ** is the number of bytes in the input string <i>including</i> |
| 4547 | ** the nul-terminator. |
| 4548 | ** |
| 4549 | ** ^If pzTail is not NULL then *pzTail is made to point to the first byte |
| 4550 | ** past the end of the first SQL statement in zSql. These routines only |
| 4551 | ** compile the first statement in zSql, so *pzTail is left pointing to |
| 4552 | ** what remains uncompiled. |
| @@ -5913,11 +5950,11 @@ | |
| 5913 | ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call |
| 5914 | ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. |
| 5915 | ** This flag instructs SQLite to omit some corner-case optimizations that |
| 5916 | ** might disrupt the operation of the [sqlite3_value_subtype()] function, |
| 5917 | ** causing it to return zero rather than the correct subtype(). |
| 5918 | ** SQL functions that invokes [sqlite3_value_subtype()] should have this |
| 5919 | ** property. If the SQLITE_SUBTYPE property is omitted, then the return |
| 5920 | ** value from [sqlite3_value_subtype()] might sometimes be zero even though |
| 5921 | ** a non-zero subtype was specified by the function argument expression. |
| 5922 | ** |
| 5923 | ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> |
| @@ -8678,11 +8715,11 @@ | |
| 8678 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8679 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8680 | #define SQLITE_TESTCTRL_TRACEFLAGS 31 |
| 8681 | #define SQLITE_TESTCTRL_TUNE 32 |
| 8682 | #define SQLITE_TESTCTRL_LOGEST 33 |
| 8683 | #define SQLITE_TESTCTRL_USELONGDOUBLE 34 |
| 8684 | #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ |
| 8685 | |
| 8686 | /* |
| 8687 | ** CAPI3REF: SQL Keyword Checking |
| 8688 | ** |
| @@ -9654,10 +9691,20 @@ | |
| 9654 | ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). |
| 9655 | ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() |
| 9656 | ** APIs are not strictly speaking threadsafe. If they are invoked at the |
| 9657 | ** same time as another thread is invoking sqlite3_backup_step() it is |
| 9658 | ** possible that they return invalid values. |
| 9659 | */ |
| 9660 | SQLITE_API sqlite3_backup *sqlite3_backup_init( |
| 9661 | sqlite3 *pDest, /* Destination database handle */ |
| 9662 | const char *zDestName, /* Destination database name */ |
| 9663 | sqlite3 *pSource, /* Source database handle */ |
| @@ -10852,10 +10899,18 @@ | |
| 10852 | ** schema S in database connection D. ^On success, the |
| 10853 | ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly |
| 10854 | ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. |
| 10855 | ** If there is not already a read-transaction open on schema S when |
| 10856 | ** this function is called, one is opened automatically. |
| 10857 | ** |
| 10858 | ** The following must be true for this function to succeed. If any of |
| 10859 | ** the following statements are false when sqlite3_snapshot_get() is |
| 10860 | ** called, SQLITE_ERROR is returned. The final value of *P is undefined |
| 10861 | ** in this case. |
| @@ -11172,11 +11227,11 @@ | |
| 11172 | #endif |
| 11173 | |
| 11174 | #if 0 |
| 11175 | } /* End of the 'extern "C"' block */ |
| 11176 | #endif |
| 11177 | #endif /* SQLITE3_H */ |
| 11178 | |
| 11179 | /******** Begin file sqlite3rtree.h *********/ |
| 11180 | /* |
| 11181 | ** 2010 August 30 |
| 11182 | ** |
| @@ -13423,17 +13478,32 @@ | |
| 13423 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13424 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13425 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13426 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13427 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13428 | ** bytes. This API is not available if the specified token matches a |
| 13429 | ** prefix query term. In that case both output variables are always set |
| 13430 | ** to 0. |
| 13431 | ** |
| 13432 | ** The output text is not a copy of the document text that was tokenized. |
| 13433 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13434 | ** includes any embedded 0x00 and trailing data. |
| 13435 | ** |
| 13436 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13437 | ** "detail=none" or "detail=column" option. |
| 13438 | ** |
| 13439 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -13521,11 +13591,10 @@ | |
| 13521 | ** CUSTOM TOKENIZERS |
| 13522 | ** |
| 13523 | ** Applications may also register custom tokenizer types. A tokenizer |
| 13524 | ** is registered by providing fts5 with a populated instance of the |
| 13525 | ** following structure. All structure methods must be defined, setting |
| 13526 | ** |
| 13527 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 13528 | ** behaviour. The structure methods are expected to function as follows: |
| 13529 | ** |
| 13530 | ** xCreate: |
| 13531 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -13865,10 +13934,11 @@ | |
| 13865 | #endif |
| 13866 | |
| 13867 | #endif /* _FTS5_H */ |
| 13868 | |
| 13869 | /******** End of fts5.h *********/ |
| 13870 | |
| 13871 | /************** End of sqlite3.h *********************************************/ |
| 13872 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 13873 | |
| 13874 | /* |
| @@ -13910,10 +13980,11 @@ | |
| 13910 | ** to count the size: 2^31-1 or 2147483647. |
| 13911 | */ |
| 13912 | #ifndef SQLITE_MAX_LENGTH |
| 13913 | # define SQLITE_MAX_LENGTH 1000000000 |
| 13914 | #endif |
| 13915 | |
| 13916 | /* |
| 13917 | ** This is the maximum number of |
| 13918 | ** |
| 13919 | ** * Columns in a table |
| @@ -14809,10 +14880,11 @@ | |
| 14809 | #include <stdio.h> |
| 14810 | #include <stdlib.h> |
| 14811 | #include <string.h> |
| 14812 | #include <assert.h> |
| 14813 | #include <stddef.h> |
| 14814 | |
| 14815 | /* |
| 14816 | ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. |
| 14817 | ** This allows better measurements of where memcpy() is used when running |
| 14818 | ** cachegrind. But this macro version of memcpy() is very slow so it |
| @@ -14831,11 +14903,10 @@ | |
| 14831 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 14832 | # define double sqlite_int64 |
| 14833 | # define float sqlite_int64 |
| 14834 | # define fabs(X) ((X)<0?-(X):(X)) |
| 14835 | # define sqlite3IsOverflow(X) 0 |
| 14836 | # define LONGDOUBLE_TYPE sqlite_int64 |
| 14837 | # ifndef SQLITE_BIG_DBL |
| 14838 | # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) |
| 14839 | # endif |
| 14840 | # define SQLITE_OMIT_DATETIME_FUNCS 1 |
| 14841 | # define SQLITE_OMIT_TRACE 1 |
| @@ -15006,13 +15077,10 @@ | |
| 15006 | # define INT8_TYPE int8_t |
| 15007 | # else |
| 15008 | # define INT8_TYPE signed char |
| 15009 | # endif |
| 15010 | #endif |
| 15011 | #ifndef LONGDOUBLE_TYPE |
| 15012 | # define LONGDOUBLE_TYPE long double |
| 15013 | #endif |
| 15014 | typedef sqlite_int64 i64; /* 8-byte signed integer */ |
| 15015 | typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ |
| 15016 | typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ |
| 15017 | typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ |
| 15018 | typedef INT16_TYPE i16; /* 2-byte signed integer */ |
| @@ -16391,10 +16459,13 @@ | |
| 16391 | struct KeyInfo*, /* First argument to compare function */ |
| 16392 | BtCursor *pCursor /* Space to write cursor structure */ |
| 16393 | ); |
| 16394 | SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); |
| 16395 | SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); |
| 16396 | SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); |
| 16397 | SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); |
| 16398 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 16399 | SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); |
| 16400 | #endif |
| @@ -17004,11 +17075,11 @@ | |
| 17004 | |
| 17005 | /* |
| 17006 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17007 | */ |
| 17008 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17009 | #define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */ |
| 17010 | |
| 17011 | /* |
| 17012 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17013 | ** for a description of what each of these routines does. |
| 17014 | */ |
| @@ -17719,51 +17790,15 @@ | |
| 17719 | struct FuncDefHash { |
| 17720 | FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ |
| 17721 | }; |
| 17722 | #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) |
| 17723 | |
| 17724 | #if defined(SQLITE_USER_AUTHENTICATION) |
| 17725 | # warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \ |
| 17726 | See ext/userauth/user-auth.txt for details." |
| 17727 | #endif |
| 17728 | #ifdef SQLITE_USER_AUTHENTICATION |
| 17729 | /* |
| 17730 | ** Information held in the "sqlite3" database connection object and used |
| 17731 | ** to manage user authentication. |
| 17732 | */ |
| 17733 | typedef struct sqlite3_userauth sqlite3_userauth; |
| 17734 | struct sqlite3_userauth { |
| 17735 | u8 authLevel; /* Current authentication level */ |
| 17736 | int nAuthPW; /* Size of the zAuthPW in bytes */ |
| 17737 | char *zAuthPW; /* Password used to authenticate */ |
| 17738 | char *zAuthUser; /* User name used to authenticate */ |
| 17739 | }; |
| 17740 | |
| 17741 | /* Allowed values for sqlite3_userauth.authLevel */ |
| 17742 | #define UAUTH_Unknown 0 /* Authentication not yet checked */ |
| 17743 | #define UAUTH_Fail 1 /* User authentication failed */ |
| 17744 | #define UAUTH_User 2 /* Authenticated as a normal user */ |
| 17745 | #define UAUTH_Admin 3 /* Authenticated as an administrator */ |
| 17746 | |
| 17747 | /* Functions used only by user authorization logic */ |
| 17748 | SQLITE_PRIVATE int sqlite3UserAuthTable(const char*); |
| 17749 | SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*); |
| 17750 | SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*); |
| 17751 | SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); |
| 17752 | |
| 17753 | #endif /* SQLITE_USER_AUTHENTICATION */ |
| 17754 | |
| 17755 | /* |
| 17756 | ** typedef for the authorization callback function. |
| 17757 | */ |
| 17758 | #ifdef SQLITE_USER_AUTHENTICATION |
| 17759 | typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, |
| 17760 | const char*, const char*); |
| 17761 | #else |
| 17762 | typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, |
| 17763 | const char*); |
| 17764 | #endif |
| 17765 | |
| 17766 | #ifndef SQLITE_OMIT_DEPRECATED |
| 17767 | /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing |
| 17768 | ** in the style of sqlite3_trace() |
| 17769 | */ |
| @@ -17920,13 +17955,10 @@ | |
| 17920 | sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ |
| 17921 | void *pUnlockArg; /* Argument to xUnlockNotify */ |
| 17922 | void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ |
| 17923 | sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ |
| 17924 | #endif |
| 17925 | #ifdef SQLITE_USER_AUTHENTICATION |
| 17926 | sqlite3_userauth auth; /* User authentication information */ |
| 17927 | #endif |
| 17928 | }; |
| 17929 | |
| 17930 | /* |
| 17931 | ** A macro to discover the encoding of a database. |
| 17932 | */ |
| @@ -19221,11 +19253,11 @@ | |
| 19221 | #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ |
| 19222 | #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ |
| 19223 | #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ |
| 19224 | #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ |
| 19225 | #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ |
| 19226 | /* 0x80000000 // Available */ |
| 19227 | |
| 19228 | /* The EP_Propagate mask is a set of properties that automatically propagate |
| 19229 | ** upwards into parent nodes. |
| 19230 | */ |
| 19231 | #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) |
| @@ -19777,11 +19809,11 @@ | |
| 19777 | ** |
| 19778 | ** SRT_Set The result must be a single column. Store each |
| 19779 | ** row of result as the key in table pDest->iSDParm. |
| 19780 | ** Apply the affinity pDest->affSdst before storing |
| 19781 | ** results. if pDest->iSDParm2 is positive, then it is |
| 19782 | ** a regsiter holding a Bloom filter for the IN operator |
| 19783 | ** that should be populated in addition to the |
| 19784 | ** pDest->iSDParm table. This SRT is used to |
| 19785 | ** implement "IN (SELECT ...)". |
| 19786 | ** |
| 19787 | ** SRT_EphemTab Create an temporary table pDest->iSDParm and store |
| @@ -20376,11 +20408,10 @@ | |
| 20376 | u8 bFullMutex; /* True to enable full mutexing */ |
| 20377 | u8 bOpenUri; /* True to interpret filenames as URIs */ |
| 20378 | u8 bUseCis; /* Use covering indices for full-scans */ |
| 20379 | u8 bSmallMalloc; /* Avoid large memory allocations if true */ |
| 20380 | u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ |
| 20381 | u8 bUseLongDouble; /* Make use of long double */ |
| 20382 | #ifdef SQLITE_DEBUG |
| 20383 | u8 bJsonSelfcheck; /* Double-check JSON parsing */ |
| 20384 | #endif |
| 20385 | int mxStrlen; /* Maximum string length */ |
| 20386 | int neverCorrupt; /* Database is always well-formed */ |
| @@ -20751,19 +20782,10 @@ | |
| 20751 | */ |
| 20752 | #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) |
| 20753 | # define SQLITE_ENABLE_FTS3 1 |
| 20754 | #endif |
| 20755 | |
| 20756 | /* |
| 20757 | ** The ctype.h header is needed for non-ASCII systems. It is also |
| 20758 | ** needed by FTS3 when FTS3 is included in the amalgamation. |
| 20759 | */ |
| 20760 | #if !defined(SQLITE_ASCII) || \ |
| 20761 | (defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_AMALGAMATION)) |
| 20762 | # include <ctype.h> |
| 20763 | #endif |
| 20764 | |
| 20765 | /* |
| 20766 | ** The following macros mimic the standard library functions toupper(), |
| 20767 | ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The |
| 20768 | ** sqlite versions only work for ASCII characters, regardless of locale. |
| 20769 | */ |
| @@ -21381,11 +21403,11 @@ | |
| 21381 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 21382 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 21383 | SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); |
| 21384 | SQLITE_PRIVATE int sqlite3Atoi(const char*); |
| 21385 | #ifndef SQLITE_OMIT_UTF16 |
| 21386 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 21387 | #endif |
| 21388 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| 21389 | SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); |
| 21390 | SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); |
| 21391 | SQLITE_PRIVATE LogEst sqlite3LogEst(u64); |
| @@ -22839,13 +22861,10 @@ | |
| 22839 | "UNLINK_AFTER_CLOSE", |
| 22840 | #endif |
| 22841 | #ifdef SQLITE_UNTESTABLE |
| 22842 | "UNTESTABLE", |
| 22843 | #endif |
| 22844 | #ifdef SQLITE_USER_AUTHENTICATION |
| 22845 | "USER_AUTHENTICATION", |
| 22846 | #endif |
| 22847 | #ifdef SQLITE_USE_ALLOCA |
| 22848 | "USE_ALLOCA", |
| 22849 | #endif |
| 22850 | #ifdef SQLITE_USE_FCNTL_TRACE |
| 22851 | "USE_FCNTL_TRACE", |
| @@ -23117,11 +23136,10 @@ | |
| 23117 | SQLITE_THREADSAFE==1, /* bFullMutex */ |
| 23118 | SQLITE_USE_URI, /* bOpenUri */ |
| 23119 | SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ |
| 23120 | 0, /* bSmallMalloc */ |
| 23121 | 1, /* bExtraSchemaChecks */ |
| 23122 | sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ |
| 23123 | #ifdef SQLITE_DEBUG |
| 23124 | 0, /* bJsonSelfcheck */ |
| 23125 | #endif |
| 23126 | 0x7ffffffe, /* mxStrlen */ |
| 23127 | 0, /* neverCorrupt */ |
| @@ -23837,13 +23855,15 @@ | |
| 23837 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 23838 | int iNewReg; /* Register for new.* values */ |
| 23839 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 23840 | i64 iKey1; /* First key value passed to hook */ |
| 23841 | i64 iKey2; /* Second key value passed to hook */ |
| 23842 | Mem *aNew; /* Array of new.* values */ |
| 23843 | Table *pTab; /* Schema object being updated */ |
| 23844 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 23845 | }; |
| 23846 | |
| 23847 | /* |
| 23848 | ** An instance of this object is used to pass an vector of values into |
| 23849 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -29285,20 +29305,33 @@ | |
| 29285 | |
| 29286 | #ifndef NDEBUG |
| 29287 | /* |
| 29288 | ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are |
| 29289 | ** intended for use inside assert() statements. |
| 29290 | */ |
| 29291 | SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ |
| 29292 | assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld ); |
| 29293 | return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); |
| 29294 | } |
| 29295 | SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ |
| 29296 | assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld ); |
| 29297 | return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); |
| 29298 | } |
| 29299 | #endif |
| 29300 | |
| 29301 | #endif /* !defined(SQLITE_MUTEX_OMIT) */ |
| 29302 | |
| 29303 | /************** End of mutex.c ***********************************************/ |
| 29304 | /************** Begin file mutex_noop.c **************************************/ |
| @@ -32272,10 +32305,11 @@ | |
| 32272 | && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) |
| 32273 | ){ |
| 32274 | pExpr = pExpr->pLeft; |
| 32275 | } |
| 32276 | if( pExpr==0 ) return; |
| 32277 | db->errByteOffset = pExpr->w.iOfst; |
| 32278 | } |
| 32279 | |
| 32280 | /* |
| 32281 | ** Enlarge the memory allocation on a StrAccum object so that it is |
| @@ -33001,11 +33035,11 @@ | |
| 33001 | } |
| 33002 | if( pItem->fg.isCte ){ |
| 33003 | sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); |
| 33004 | } |
| 33005 | if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ |
| 33006 | sqlite3_str_appendf(&x, " ON"); |
| 33007 | } |
| 33008 | if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); |
| 33009 | if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); |
| 33010 | if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); |
| 33011 | if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); |
| @@ -34085,10 +34119,14 @@ | |
| 34085 | ** |
| 34086 | ** This routines are given external linkage so that they will always be |
| 34087 | ** accessible to the debugging, and to avoid warnings about unused |
| 34088 | ** functions. But these routines only exist in debugging builds, so they |
| 34089 | ** do not contaminate the interface. |
| 34090 | */ |
| 34091 | SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } |
| 34092 | SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} |
| 34093 | SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } |
| 34094 | SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } |
| @@ -34687,11 +34725,11 @@ | |
| 34687 | */ |
| 34688 | #define READ_UTF8(zIn, zTerm, c) \ |
| 34689 | c = *(zIn++); \ |
| 34690 | if( c>=0xc0 ){ \ |
| 34691 | c = sqlite3Utf8Trans1[c-0xc0]; \ |
| 34692 | while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ |
| 34693 | c = (c<<6) + (0x3f & *(zIn++)); \ |
| 34694 | } \ |
| 34695 | if( c<0x80 \ |
| 34696 | || (c&0xFFFFF800)==0xD800 \ |
| 34697 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| @@ -35065,24 +35103,26 @@ | |
| 35065 | assert( m.z || db->mallocFailed ); |
| 35066 | return m.z; |
| 35067 | } |
| 35068 | |
| 35069 | /* |
| 35070 | ** zIn is a UTF-16 encoded unicode string at least nChar characters long. |
| 35071 | ** Return the number of bytes in the first nChar unicode characters |
| 35072 | ** in pZ. nChar must be non-negative. |
| 35073 | */ |
| 35074 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nChar){ |
| 35075 | int c; |
| 35076 | unsigned char const *z = zIn; |
| 35077 | int n = 0; |
| 35078 | |
| 35079 | if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++; |
| 35080 | while( n<nChar ){ |
| 35081 | c = z[0]; |
| 35082 | z += 2; |
| 35083 | if( c>=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; |
| 35084 | n++; |
| 35085 | } |
| 35086 | return (int)(z-(unsigned char const *)zIn) |
| 35087 | - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE); |
| 35088 | } |
| @@ -35659,10 +35699,12 @@ | |
| 35659 | int esign = 1; /* sign of exponent */ |
| 35660 | int e = 0; /* exponent */ |
| 35661 | int eValid = 1; /* True exponent is either not used or is well-formed */ |
| 35662 | int nDigit = 0; /* Number of digits processed */ |
| 35663 | int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ |
| 35664 | |
| 35665 | assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); |
| 35666 | *pResult = 0.0; /* Default return value, in case of an error */ |
| 35667 | if( length==0 ) return 0; |
| 35668 | |
| @@ -35761,81 +35803,65 @@ | |
| 35761 | |
| 35762 | /* adjust exponent by d, and update sign */ |
| 35763 | e = (e*esign) + d; |
| 35764 | |
| 35765 | /* Try to adjust the exponent to make it smaller */ |
| 35766 | while( e>0 && s<(LARGEST_UINT64/10) ){ |
| 35767 | s *= 10; |
| 35768 | e--; |
| 35769 | } |
| 35770 | while( e<0 && (s%10)==0 ){ |
| 35771 | s /= 10; |
| 35772 | e++; |
| 35773 | } |
| 35774 | |
| 35775 | if( e==0 ){ |
| 35776 | *pResult = s; |
| 35777 | }else if( sqlite3Config.bUseLongDouble ){ |
| 35778 | LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s; |
| 35779 | if( e>0 ){ |
| 35780 | while( e>=100 ){ e-=100; r *= 1.0e+100L; } |
| 35781 | while( e>=10 ){ e-=10; r *= 1.0e+10L; } |
| 35782 | while( e>=1 ){ e-=1; r *= 1.0e+01L; } |
| 35783 | }else{ |
| 35784 | while( e<=-100 ){ e+=100; r *= 1.0e-100L; } |
| 35785 | while( e<=-10 ){ e+=10; r *= 1.0e-10L; } |
| 35786 | while( e<=-1 ){ e+=1; r *= 1.0e-01L; } |
| 35787 | } |
| 35788 | assert( r>=0.0 ); |
| 35789 | if( r>+1.7976931348623157081452742373e+308L ){ |
| 35790 | #ifdef INFINITY |
| 35791 | *pResult = +INFINITY; |
| 35792 | #else |
| 35793 | *pResult = 1.0e308*10.0; |
| 35794 | #endif |
| 35795 | }else{ |
| 35796 | *pResult = (double)r; |
| 35797 | } |
| 35798 | }else{ |
| 35799 | double rr[2]; |
| 35800 | u64 s2; |
| 35801 | rr[0] = (double)s; |
| 35802 | s2 = (u64)rr[0]; |
| 35803 | #if defined(_MSC_VER) && _MSC_VER<1700 |
| 35804 | if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } |
| 35805 | #endif |
| 35806 | rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); |
| 35807 | if( e>0 ){ |
| 35808 | while( e>=100 ){ |
| 35809 | e -= 100; |
| 35810 | dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); |
| 35811 | } |
| 35812 | while( e>=10 ){ |
| 35813 | e -= 10; |
| 35814 | dekkerMul2(rr, 1.0e+10, 0.0); |
| 35815 | } |
| 35816 | while( e>=1 ){ |
| 35817 | e -= 1; |
| 35818 | dekkerMul2(rr, 1.0e+01, 0.0); |
| 35819 | } |
| 35820 | }else{ |
| 35821 | while( e<=-100 ){ |
| 35822 | e += 100; |
| 35823 | dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); |
| 35824 | } |
| 35825 | while( e<=-10 ){ |
| 35826 | e += 10; |
| 35827 | dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); |
| 35828 | } |
| 35829 | while( e<=-1 ){ |
| 35830 | e += 1; |
| 35831 | dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); |
| 35832 | } |
| 35833 | } |
| 35834 | *pResult = rr[0]+rr[1]; |
| 35835 | if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; |
| 35836 | } |
| 35837 | if( sign<0 ) *pResult = -*pResult; |
| 35838 | assert( !sqlite3IsNaN(*pResult) ); |
| 35839 | |
| 35840 | atof_return: |
| 35841 | /* return true if number and no extra non-whitespace characters after */ |
| @@ -36152,13 +36178,14 @@ | |
| 36152 | */ |
| 36153 | SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ |
| 36154 | int i; |
| 36155 | u64 v; |
| 36156 | int e, exp = 0; |
| 36157 | p->isSpecial = 0; |
| 36158 | p->z = p->zBuf; |
| 36159 | |
| 36160 | assert( mxRound>0 ); |
| 36161 | |
| 36162 | /* Convert negative numbers to positive. Deal with Infinity, 0.0, and |
| 36163 | ** NaN. */ |
| 36164 | if( r<0.0 ){ |
| @@ -36182,66 +36209,49 @@ | |
| 36182 | return; |
| 36183 | } |
| 36184 | |
| 36185 | /* Multiply r by powers of ten until it lands somewhere in between |
| 36186 | ** 1.0e+19 and 1.0e+17. |
| 36187 | */ |
| 36188 | if( sqlite3Config.bUseLongDouble ){ |
| 36189 | LONGDOUBLE_TYPE rr = r; |
| 36190 | if( rr>=1.0e+19 ){ |
| 36191 | while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } |
| 36192 | while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; } |
| 36193 | while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; } |
| 36194 | }else{ |
| 36195 | while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; } |
| 36196 | while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; } |
| 36197 | while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; } |
| 36198 | } |
| 36199 | v = (u64)rr; |
| 36200 | }else{ |
| 36201 | /* If high-precision floating point is not available using "long double", |
| 36202 | ** then use Dekker-style double-double computation to increase the |
| 36203 | ** precision. |
| 36204 | ** |
| 36205 | ** The error terms on constants like 1.0e+100 computed using the |
| 36206 | ** decimal extension, for example as follows: |
| 36207 | ** |
| 36208 | ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); |
| 36209 | */ |
| 36210 | double rr[2]; |
| 36211 | rr[0] = r; |
| 36212 | rr[1] = 0.0; |
| 36213 | if( rr[0]>9.223372036854774784e+18 ){ |
| 36214 | while( rr[0]>9.223372036854774784e+118 ){ |
| 36215 | exp += 100; |
| 36216 | dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); |
| 36217 | } |
| 36218 | while( rr[0]>9.223372036854774784e+28 ){ |
| 36219 | exp += 10; |
| 36220 | dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); |
| 36221 | } |
| 36222 | while( rr[0]>9.223372036854774784e+18 ){ |
| 36223 | exp += 1; |
| 36224 | dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); |
| 36225 | } |
| 36226 | }else{ |
| 36227 | while( rr[0]<9.223372036854774784e-83 ){ |
| 36228 | exp -= 100; |
| 36229 | dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); |
| 36230 | } |
| 36231 | while( rr[0]<9.223372036854774784e+07 ){ |
| 36232 | exp -= 10; |
| 36233 | dekkerMul2(rr, 1.0e+10, 0.0); |
| 36234 | } |
| 36235 | while( rr[0]<9.22337203685477478e+17 ){ |
| 36236 | exp -= 1; |
| 36237 | dekkerMul2(rr, 1.0e+01, 0.0); |
| 36238 | } |
| 36239 | } |
| 36240 | v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; |
| 36241 | } |
| 36242 | |
| 36243 | |
| 36244 | /* Extract significant digits. */ |
| 36245 | i = sizeof(p->zBuf)-1; |
| 36246 | assert( v>0 ); |
| 36247 | while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } |
| @@ -37008,108 +37018,10 @@ | |
| 37008 | i += pIn[i+1]; |
| 37009 | }while( i<mx ); |
| 37010 | return 0; |
| 37011 | } |
| 37012 | |
| 37013 | /* |
| 37014 | ** High-resolution hardware timer used for debugging and testing only. |
| 37015 | */ |
| 37016 | #if defined(VDBE_PROFILE) \ |
| 37017 | || defined(SQLITE_PERFORMANCE_TRACE) \ |
| 37018 | || defined(SQLITE_ENABLE_STMT_SCANSTATUS) |
| 37019 | /************** Include hwtime.h in the middle of util.c *********************/ |
| 37020 | /************** Begin file hwtime.h ******************************************/ |
| 37021 | /* |
| 37022 | ** 2008 May 27 |
| 37023 | ** |
| 37024 | ** The author disclaims copyright to this source code. In place of |
| 37025 | ** a legal notice, here is a blessing: |
| 37026 | ** |
| 37027 | ** May you do good and not evil. |
| 37028 | ** May you find forgiveness for yourself and forgive others. |
| 37029 | ** May you share freely, never taking more than you give. |
| 37030 | ** |
| 37031 | ****************************************************************************** |
| 37032 | ** |
| 37033 | ** This file contains inline asm code for retrieving "high-performance" |
| 37034 | ** counters for x86 and x86_64 class CPUs. |
| 37035 | */ |
| 37036 | #ifndef SQLITE_HWTIME_H |
| 37037 | #define SQLITE_HWTIME_H |
| 37038 | |
| 37039 | /* |
| 37040 | ** The following routine only works on Pentium-class (or newer) processors. |
| 37041 | ** It uses the RDTSC opcode to read the cycle count value out of the |
| 37042 | ** processor and returns that value. This can be used for high-res |
| 37043 | ** profiling. |
| 37044 | */ |
| 37045 | #if !defined(__STRICT_ANSI__) && \ |
| 37046 | (defined(__GNUC__) || defined(_MSC_VER)) && \ |
| 37047 | (defined(i386) || defined(__i386__) || defined(_M_IX86)) |
| 37048 | |
| 37049 | #if defined(__GNUC__) |
| 37050 | |
| 37051 | __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
| 37052 | unsigned int lo, hi; |
| 37053 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
| 37054 | return (sqlite_uint64)hi << 32 | lo; |
| 37055 | } |
| 37056 | |
| 37057 | #elif defined(_MSC_VER) |
| 37058 | |
| 37059 | __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ |
| 37060 | __asm { |
| 37061 | rdtsc |
| 37062 | ret ; return value at EDX:EAX |
| 37063 | } |
| 37064 | } |
| 37065 | |
| 37066 | #endif |
| 37067 | |
| 37068 | #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) |
| 37069 | |
| 37070 | __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
| 37071 | unsigned int lo, hi; |
| 37072 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
| 37073 | return (sqlite_uint64)hi << 32 | lo; |
| 37074 | } |
| 37075 | |
| 37076 | #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) |
| 37077 | |
| 37078 | __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
| 37079 | unsigned long long retval; |
| 37080 | unsigned long junk; |
| 37081 | __asm__ __volatile__ ("\n\ |
| 37082 | 1: mftbu %1\n\ |
| 37083 | mftb %L0\n\ |
| 37084 | mftbu %0\n\ |
| 37085 | cmpw %0,%1\n\ |
| 37086 | bne 1b" |
| 37087 | : "=r" (retval), "=r" (junk)); |
| 37088 | return retval; |
| 37089 | } |
| 37090 | |
| 37091 | #else |
| 37092 | |
| 37093 | /* |
| 37094 | ** asm() is needed for hardware timing support. Without asm(), |
| 37095 | ** disable the sqlite3Hwtime() routine. |
| 37096 | ** |
| 37097 | ** sqlite3Hwtime() is only used for some obscure debugging |
| 37098 | ** and analysis configurations, not in any deliverable, so this |
| 37099 | ** should not be a great loss. |
| 37100 | */ |
| 37101 | SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } |
| 37102 | |
| 37103 | #endif |
| 37104 | |
| 37105 | #endif /* !defined(SQLITE_HWTIME_H) */ |
| 37106 | |
| 37107 | /************** End of hwtime.h **********************************************/ |
| 37108 | /************** Continuing where we left off in util.c ***********************/ |
| 37109 | #endif |
| 37110 | |
| 37111 | /************** End of util.c ************************************************/ |
| 37112 | /************** Begin file hash.c ********************************************/ |
| 37113 | /* |
| 37114 | ** 2001 September 22 |
| 37115 | ** |
| @@ -38787,11 +38699,11 @@ | |
| 38787 | # define F_SETLKW 7 |
| 38788 | # endif |
| 38789 | # endif |
| 38790 | #else /* !SQLITE_WASI */ |
| 38791 | # ifndef HAVE_FCHMOD |
| 38792 | # define HAVE_FCHMOD |
| 38793 | # endif |
| 38794 | #endif /* SQLITE_WASI */ |
| 38795 | |
| 38796 | #ifdef SQLITE_WASI |
| 38797 | # define osGetpid(X) (pid_t)1 |
| @@ -41038,58 +40950,37 @@ | |
| 41038 | ** file by this or any other process. If such a lock is held, set *pResOut |
| 41039 | ** to a non-zero value otherwise *pResOut is set to zero. The return value |
| 41040 | ** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
| 41041 | */ |
| 41042 | static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ |
| 41043 | int rc = SQLITE_OK; |
| 41044 | int reserved = 0; |
| 41045 | unixFile *pFile = (unixFile*)id; |
| 41046 | |
| 41047 | SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
| 41048 | |
| 41049 | assert( pFile ); |
| 41050 | |
| 41051 | /* Check if a thread in this process holds such a lock */ |
| 41052 | if( pFile->eFileLock>SHARED_LOCK ){ |
| 41053 | reserved = 1; |
| 41054 | } |
| 41055 | |
| 41056 | /* Otherwise see if some other process holds it. */ |
| 41057 | if( !reserved ){ |
| 41058 | /* attempt to get the lock */ |
| 41059 | int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); |
| 41060 | if( !lrc ){ |
| 41061 | /* got the lock, unlock it */ |
| 41062 | lrc = robust_flock(pFile->h, LOCK_UN); |
| 41063 | if ( lrc ) { |
| 41064 | int tErrno = errno; |
| 41065 | /* unlock failed with an error */ |
| 41066 | lrc = SQLITE_IOERR_UNLOCK; |
| 41067 | storeLastErrno(pFile, tErrno); |
| 41068 | rc = lrc; |
| 41069 | } |
| 41070 | } else { |
| 41071 | int tErrno = errno; |
| 41072 | reserved = 1; |
| 41073 | /* someone else might have it reserved */ |
| 41074 | lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
| 41075 | if( IS_LOCK_ERROR(lrc) ){ |
| 41076 | storeLastErrno(pFile, tErrno); |
| 41077 | rc = lrc; |
| 41078 | } |
| 41079 | } |
| 41080 | } |
| 41081 | OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); |
| 41082 | |
| 41083 | #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS |
| 41084 | if( (rc & 0xff) == SQLITE_IOERR ){ |
| 41085 | rc = SQLITE_OK; |
| 41086 | reserved=1; |
| 41087 | } |
| 41088 | #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ |
| 41089 | *pResOut = reserved; |
| 41090 | return rc; |
| 41091 | } |
| 41092 | |
| 41093 | /* |
| 41094 | ** Lock the file with the lock specified by parameter eFileLock - one |
| 41095 | ** of the following: |
| @@ -42582,10 +42473,15 @@ | |
| 42582 | int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); |
| 42583 | return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; |
| 42584 | } |
| 42585 | #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ |
| 42586 | |
| 42587 | case SQLITE_FCNTL_LOCKSTATE: { |
| 42588 | *(int*)pArg = pFile->eFileLock; |
| 42589 | return SQLITE_OK; |
| 42590 | } |
| 42591 | case SQLITE_FCNTL_LAST_ERRNO: { |
| @@ -42723,10 +42619,11 @@ | |
| 42723 | |
| 42724 | /* Set the POWERSAFE_OVERWRITE flag if requested. */ |
| 42725 | if( pFd->ctrlFlags & UNIXFILE_PSOW ){ |
| 42726 | pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; |
| 42727 | } |
| 42728 | |
| 42729 | pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; |
| 42730 | } |
| 42731 | } |
| 42732 | #else |
| @@ -42773,19 +42670,19 @@ | |
| 42773 | 0; |
| 42774 | }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ |
| 42775 | pFile->sectorSize = fsInfo.f_bsize; |
| 42776 | pFile->deviceCharacteristics = |
| 42777 | /* full bitset of atomics from max sector size and smaller */ |
| 42778 | ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | |
| 42779 | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
| 42780 | ** so it is ordered */ |
| 42781 | 0; |
| 42782 | }else if( strstr(fsInfo.f_basetype, "dos") ){ |
| 42783 | pFile->sectorSize = fsInfo.f_bsize; |
| 42784 | pFile->deviceCharacteristics = |
| 42785 | /* full bitset of atomics from max sector size and smaller */ |
| 42786 | ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | |
| 42787 | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
| 42788 | ** so it is ordered */ |
| 42789 | 0; |
| 42790 | }else{ |
| 42791 | pFile->deviceCharacteristics = |
| @@ -50462,10 +50359,15 @@ | |
| 50462 | OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n", |
| 50463 | hOldFile, pFile->h)); |
| 50464 | return SQLITE_OK; |
| 50465 | } |
| 50466 | #endif |
| 50467 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 50468 | char *zTFile = 0; |
| 50469 | int rc = winGetTempname(pFile->pVfs, &zTFile); |
| 50470 | if( rc==SQLITE_OK ){ |
| 50471 | *(char**)pArg = zTFile; |
| @@ -50523,11 +50425,11 @@ | |
| 50523 | /* |
| 50524 | ** Return a vector of device characteristics. |
| 50525 | */ |
| 50526 | static int winDeviceCharacteristics(sqlite3_file *id){ |
| 50527 | winFile *p = (winFile*)id; |
| 50528 | return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | |
| 50529 | ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); |
| 50530 | } |
| 50531 | |
| 50532 | /* |
| 50533 | ** Windows will only let you create file view mappings |
| @@ -51911,11 +51813,11 @@ | |
| 51911 | */ |
| 51912 | char *zTmpname = 0; /* For temporary filename, if necessary. */ |
| 51913 | |
| 51914 | int rc = SQLITE_OK; /* Function Return Code */ |
| 51915 | #if !defined(NDEBUG) || SQLITE_OS_WINCE |
| 51916 | int eType = flags&0xFFFFFF00; /* Type of file to open */ |
| 51917 | #endif |
| 51918 | |
| 51919 | int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); |
| 51920 | int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); |
| 51921 | int isCreate = (flags & SQLITE_OPEN_CREATE); |
| @@ -58131,24 +58033,32 @@ | |
| 58131 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58132 | /* |
| 58133 | ** Return true if page pgno can be read directly from the database file |
| 58134 | ** by the b-tree layer. This is the case if: |
| 58135 | ** |
| 58136 | ** * the database file is open, |
| 58137 | ** * there are no dirty pages in the cache, and |
| 58138 | ** * the desired page is not currently in the wal file. |
| 58139 | */ |
| 58140 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ |
| 58141 | if( pPager->fd->pMethods==0 ) return 0; |
| 58142 | if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; |
| 58143 | #ifndef SQLITE_OMIT_WAL |
| 58144 | if( pPager->pWal ){ |
| 58145 | u32 iRead = 0; |
| 58146 | (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); |
| 58147 | return iRead==0; |
| 58148 | } |
| 58149 | #endif |
| 58150 | return 1; |
| 58151 | } |
| 58152 | #endif |
| 58153 | |
| 58154 | #ifndef SQLITE_OMIT_WAL |
| @@ -65171,11 +65081,11 @@ | |
| 65171 | ** 20: Salt-2, a different random integer changing with each ckpt |
| 65172 | ** 24: Checksum-1 (first part of checksum for first 24 bytes of header). |
| 65173 | ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). |
| 65174 | ** |
| 65175 | ** Immediately following the wal-header are zero or more frames. Each |
| 65176 | ** frame consists of a 24-byte frame-header followed by a <page-size> bytes |
| 65177 | ** of page data. The frame-header is six big-endian 32-bit unsigned |
| 65178 | ** integer values, as follows: |
| 65179 | ** |
| 65180 | ** 0: Page number. |
| 65181 | ** 4: For commit records, the size of the database image in pages |
| @@ -65668,10 +65578,11 @@ | |
| 65668 | int nSehTry; /* Number of nested SEH_TRY{} blocks */ |
| 65669 | u8 lockError; /* True if a locking error has occurred */ |
| 65670 | #endif |
| 65671 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 65672 | WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ |
| 65673 | #endif |
| 65674 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 65675 | sqlite3 *db; |
| 65676 | #endif |
| 65677 | }; |
| @@ -67560,11 +67471,11 @@ | |
| 67560 | return SQLITE_IOERR_IN_PAGE; |
| 67561 | } |
| 67562 | |
| 67563 | /* |
| 67564 | ** Assert that the Wal.lockMask mask, which indicates the locks held |
| 67565 | ** by the connenction, is consistent with the Wal.readLock, Wal.writeLock |
| 67566 | ** and Wal.ckptLock variables. To be used as: |
| 67567 | ** |
| 67568 | ** assert( walAssertLockmask(pWal) ); |
| 67569 | */ |
| 67570 | static int walAssertLockmask(Wal *pWal){ |
| @@ -68224,11 +68135,11 @@ | |
| 68224 | assert( pWal->apWiData[0]!=0 ); |
| 68225 | pInfo = walCkptInfo(pWal); |
| 68226 | SEH_INJECT_FAULT; |
| 68227 | if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame |
| 68228 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68229 | && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) |
| 68230 | #endif |
| 68231 | ){ |
| 68232 | /* The WAL has been completely backfilled (or it is empty). |
| 68233 | ** and can be safely ignored. |
| 68234 | */ |
| @@ -69624,11 +69535,24 @@ | |
| 69624 | */ |
| 69625 | SQLITE_PRIVATE void sqlite3WalSnapshotOpen( |
| 69626 | Wal *pWal, |
| 69627 | sqlite3_snapshot *pSnapshot |
| 69628 | ){ |
| 69629 | pWal->pSnapshot = (WalIndexHdr*)pSnapshot; |
| 69630 | } |
| 69631 | |
| 69632 | /* |
| 69633 | ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if |
| 69634 | ** p1 is older than p2 and zero if p1 and p2 are the same snapshot. |
| @@ -75504,10 +75428,29 @@ | |
| 75504 | ** this routine. |
| 75505 | */ |
| 75506 | SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ |
| 75507 | return ROUND8(sizeof(BtCursor)); |
| 75508 | } |
| 75509 | |
| 75510 | /* |
| 75511 | ** Initialize memory that will be converted into a BtCursor object. |
| 75512 | ** |
| 75513 | ** The simple approach here would be to memset() the entire object |
| @@ -84538,11 +84481,12 @@ | |
| 84538 | if( apVal==0 ){ |
| 84539 | rc = SQLITE_NOMEM_BKPT; |
| 84540 | goto value_from_function_out; |
| 84541 | } |
| 84542 | for(i=0; i<nVal; i++){ |
| 84543 | rc = sqlite3ValueFromExpr(db, pList->a[i].pExpr, enc, aff, &apVal[i]); |
| 84544 | if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; |
| 84545 | } |
| 84546 | } |
| 84547 | |
| 84548 | pVal = valueNew(db, pCtx); |
| @@ -89571,11 +89515,11 @@ | |
| 89571 | /* The following two functions are used only within testcase() to prove |
| 89572 | ** test coverage. These functions do no exist for production builds. |
| 89573 | ** We must use separate SQLITE_NOINLINE functions here, since otherwise |
| 89574 | ** optimizer code movement causes gcov to become very confused. |
| 89575 | */ |
| 89576 | #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) |
| 89577 | static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; } |
| 89578 | static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; } |
| 89579 | #endif |
| 89580 | |
| 89581 | /* |
| @@ -89586,17 +89530,10 @@ | |
| 89586 | SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ |
| 89587 | if( sqlite3IsNaN(r) ){ |
| 89588 | /* SQLite considers NaN to be a NULL. And all integer values are greater |
| 89589 | ** than NULL */ |
| 89590 | return 1; |
| 89591 | } |
| 89592 | if( sqlite3Config.bUseLongDouble ){ |
| 89593 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 89594 | testcase( x<r ); |
| 89595 | testcase( x>r ); |
| 89596 | testcase( x==r ); |
| 89597 | return (x<r) ? -1 : (x>r); |
| 89598 | }else{ |
| 89599 | i64 y; |
| 89600 | if( r<-9223372036854775808.0 ) return +1; |
| 89601 | if( r>=9223372036854775808.0 ) return -1; |
| 89602 | y = (i64)r; |
| @@ -90595,17 +90532,25 @@ | |
| 90595 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 90596 | db->pPreUpdate = 0; |
| 90597 | sqlite3DbFree(db, preupdate.aRecord); |
| 90598 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); |
| 90599 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); |
| 90600 | if( preupdate.aNew ){ |
| 90601 | int i; |
| 90602 | for(i=0; i<pCsr->nField; i++){ |
| 90603 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| 90604 | } |
| 90605 | sqlite3DbNNFreeNN(db, preupdate.aNew); |
| 90606 | } |
| 90607 | } |
| 90608 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 90609 | |
| 90610 | /************** End of vdbeaux.c *********************************************/ |
| 90611 | /************** Begin file vdbeapi.c *****************************************/ |
| @@ -92230,10 +92175,21 @@ | |
| 92230 | ** A successful evaluation of this routine acquires the mutex on p. |
| 92231 | ** the mutex is released if any kind of error occurs. |
| 92232 | ** |
| 92233 | ** The error code stored in database p->db is overwritten with the return |
| 92234 | ** value in any case. |
| 92235 | */ |
| 92236 | static int vdbeUnbind(Vdbe *p, unsigned int i){ |
| 92237 | Mem *pVar; |
| 92238 | if( vdbeSafetyNotNull(p) ){ |
| 92239 | return SQLITE_MISUSE_BKPT; |
| @@ -92287,10 +92243,11 @@ | |
| 92287 | Mem *pVar; |
| 92288 | int rc; |
| 92289 | |
| 92290 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92291 | if( rc==SQLITE_OK ){ |
| 92292 | if( zData!=0 ){ |
| 92293 | pVar = &p->aVar[i-1]; |
| 92294 | rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); |
| 92295 | if( rc==SQLITE_OK && encoding!=0 ){ |
| 92296 | rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); |
| @@ -92336,10 +92293,11 @@ | |
| 92336 | SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ |
| 92337 | int rc; |
| 92338 | Vdbe *p = (Vdbe *)pStmt; |
| 92339 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92340 | if( rc==SQLITE_OK ){ |
| 92341 | sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); |
| 92342 | sqlite3_mutex_leave(p->db->mutex); |
| 92343 | } |
| 92344 | return rc; |
| 92345 | } |
| @@ -92349,10 +92307,11 @@ | |
| 92349 | SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ |
| 92350 | int rc; |
| 92351 | Vdbe *p = (Vdbe *)pStmt; |
| 92352 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92353 | if( rc==SQLITE_OK ){ |
| 92354 | sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); |
| 92355 | sqlite3_mutex_leave(p->db->mutex); |
| 92356 | } |
| 92357 | return rc; |
| 92358 | } |
| @@ -92359,10 +92318,11 @@ | |
| 92359 | SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ |
| 92360 | int rc; |
| 92361 | Vdbe *p = (Vdbe*)pStmt; |
| 92362 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92363 | if( rc==SQLITE_OK ){ |
| 92364 | sqlite3_mutex_leave(p->db->mutex); |
| 92365 | } |
| 92366 | return rc; |
| 92367 | } |
| 92368 | SQLITE_API int sqlite3_bind_pointer( |
| @@ -92374,10 +92334,11 @@ | |
| 92374 | ){ |
| 92375 | int rc; |
| 92376 | Vdbe *p = (Vdbe*)pStmt; |
| 92377 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92378 | if( rc==SQLITE_OK ){ |
| 92379 | sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); |
| 92380 | sqlite3_mutex_leave(p->db->mutex); |
| 92381 | }else if( xDestructor ){ |
| 92382 | xDestructor(pPtr); |
| 92383 | } |
| @@ -92455,10 +92416,11 @@ | |
| 92455 | SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ |
| 92456 | int rc; |
| 92457 | Vdbe *p = (Vdbe *)pStmt; |
| 92458 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92459 | if( rc==SQLITE_OK ){ |
| 92460 | #ifndef SQLITE_OMIT_INCRBLOB |
| 92461 | sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); |
| 92462 | #else |
| 92463 | rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); |
| 92464 | #endif |
| @@ -92789,41 +92751,68 @@ | |
| 92789 | if( iIdx>=p->pCsr->nField || iIdx<0 ){ |
| 92790 | rc = SQLITE_RANGE; |
| 92791 | goto preupdate_old_out; |
| 92792 | } |
| 92793 | |
| 92794 | /* If the old.* record has not yet been loaded into memory, do so now. */ |
| 92795 | if( p->pUnpacked==0 ){ |
| 92796 | u32 nRec; |
| 92797 | u8 *aRec; |
| 92798 | |
| 92799 | assert( p->pCsr->eCurType==CURTYPE_BTREE ); |
| 92800 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 92801 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 92802 | if( !aRec ) goto preupdate_old_out; |
| 92803 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 92804 | if( rc==SQLITE_OK ){ |
| 92805 | p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); |
| 92806 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 92807 | } |
| 92808 | if( rc!=SQLITE_OK ){ |
| 92809 | sqlite3DbFree(db, aRec); |
| 92810 | goto preupdate_old_out; |
| 92811 | } |
| 92812 | p->aRecord = aRec; |
| 92813 | } |
| 92814 | |
| 92815 | pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; |
| 92816 | if( iIdx==p->pTab->iPKey ){ |
| 92817 | sqlite3VdbeMemSetInt64(pMem, p->iKey1); |
| 92818 | }else if( iIdx>=p->pUnpacked->nField ){ |
| 92819 | *ppValue = (sqlite3_value *)columnNullValue(); |
| 92820 | }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ |
| 92821 | if( pMem->flags & (MEM_Int|MEM_IntReal) ){ |
| 92822 | testcase( pMem->flags & MEM_Int ); |
| 92823 | testcase( pMem->flags & MEM_IntReal ); |
| 92824 | sqlite3VdbeMemRealify(pMem); |
| 92825 | } |
| 92826 | } |
| 92827 | |
| 92828 | preupdate_old_out: |
| 92829 | sqlite3Error(db, rc); |
| @@ -93367,10 +93356,108 @@ | |
| 93367 | ** commenting and indentation practices when changing or adding code. |
| 93368 | */ |
| 93369 | /* #include "sqliteInt.h" */ |
| 93370 | /* #include "vdbeInt.h" */ |
| 93371 | |
| 93372 | /* |
| 93373 | ** Invoke this macro on memory cells just prior to changing the |
| 93374 | ** value of the cell. This macro verifies that shallow copies are |
| 93375 | ** not misused. A shallow copy of a string or blob just copies a |
| 93376 | ** pointer to the string or blob, not the content. If the original |
| @@ -97875,12 +97962,17 @@ | |
| 97875 | 0, pCx->uc.pCursor); |
| 97876 | pCx->isTable = 1; |
| 97877 | } |
| 97878 | } |
| 97879 | pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); |
| 97880 | if( rc ){ |
| 97881 | sqlite3BtreeClose(pCx->ub.pBtx); |
| 97882 | } |
| 97883 | } |
| 97884 | } |
| 97885 | if( rc ) goto abort_due_to_error; |
| 97886 | pCx->nullRow = 1; |
| @@ -102394,11 +102486,11 @@ | |
| 102394 | ** element. |
| 102395 | ** |
| 102396 | ** As with all opcodes, the meanings of the parameters for OP_Explain |
| 102397 | ** are subject to change from one release to the next. Applications |
| 102398 | ** should not attempt to interpret or use any of the information |
| 102399 | ** contined in the OP_Explain opcode. The information provided by this |
| 102400 | ** opcode is intended for testing and debugging use only. |
| 102401 | */ |
| 102402 | default: { /* This is really OP_Noop, OP_Explain */ |
| 102403 | assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); |
| 102404 | |
| @@ -107490,11 +107582,11 @@ | |
| 107490 | ** non-VIEW candidate plus multiple VIEW candidates. In other |
| 107491 | ** words non-VIEW candidate terms take precedence over VIEWs. |
| 107492 | */ |
| 107493 | if( cntTab==0 |
| 107494 | || (cntTab==1 |
| 107495 | && ALWAYS(pMatch!=0) |
| 107496 | && ALWAYS(pMatch->pSTab!=0) |
| 107497 | && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 |
| 107498 | && (pTab->tabFlags & TF_Ephemeral)==0) |
| 107499 | ){ |
| 107500 | cntTab = 1; |
| @@ -108123,12 +108215,12 @@ | |
| 108123 | } |
| 108124 | |
| 108125 | /* Resolve function names |
| 108126 | */ |
| 108127 | case TK_FUNCTION: { |
| 108128 | ExprList *pList = pExpr->x.pList; /* The argument list */ |
| 108129 | int n = pList ? pList->nExpr : 0; /* Number of arguments */ |
| 108130 | int no_such_func = 0; /* True if no such function exists */ |
| 108131 | int wrong_num_args = 0; /* True if wrong number of arguments */ |
| 108132 | int is_agg = 0; /* True if is an aggregate function */ |
| 108133 | const char *zId; /* The function name. */ |
| 108134 | FuncDef *pDef; /* Information about the function */ |
| @@ -108137,10 +108229,12 @@ | |
| 108137 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 108138 | Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); |
| 108139 | #endif |
| 108140 | assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); |
| 108141 | assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); |
| 108142 | zId = pExpr->u.zToken; |
| 108143 | pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); |
| 108144 | if( pDef==0 ){ |
| 108145 | pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0); |
| 108146 | if( pDef==0 ){ |
| @@ -108185,10 +108279,28 @@ | |
| 108185 | pExpr->op = TK_NULL; |
| 108186 | return WRC_Prune; |
| 108187 | } |
| 108188 | } |
| 108189 | #endif |
| 108190 | if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ |
| 108191 | /* For the purposes of the EP_ConstFunc flag, date and time |
| 108192 | ** functions and other functions that change slowly are considered |
| 108193 | ** constant because they are constant for the duration of one query. |
| 108194 | ** This allows them to be factored out of inner loops. */ |
| @@ -111951,11 +112063,11 @@ | |
| 111951 | ** |
| 111952 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 111953 | ** (4a) pExpr must come from an ON clause.. |
| 111954 | ** (4b) and specifically the ON clause associated with the LEFT JOIN. |
| 111955 | ** |
| 111956 | ** (5) If pSrc is not the right operand of a LEFT JOIN or the left |
| 111957 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 111958 | ** clause, not an ON clause. |
| 111959 | ** |
| 111960 | ** (6) Either: |
| 111961 | ** |
| @@ -113858,10 +113970,63 @@ | |
| 113858 | } |
| 113859 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 113860 | } |
| 113861 | return target; |
| 113862 | } |
| 113863 | |
| 113864 | /* |
| 113865 | ** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. |
| 113866 | ** If it is, then resolve the expression by reading from the index and |
| 113867 | ** return the register into which the value has been read. If pExpr is |
| @@ -113891,10 +114056,21 @@ | |
| 113891 | ){ |
| 113892 | /* Affinity mismatch on a generated column */ |
| 113893 | continue; |
| 113894 | } |
| 113895 | |
| 113896 | v = pParse->pVdbe; |
| 113897 | assert( v!=0 ); |
| 113898 | if( p->bMaybeNullRow ){ |
| 113899 | /* If the index is on a NULL row due to an outer join, then we |
| 113900 | ** cannot extract the value from the index. The value must be |
| @@ -115421,35 +115597,41 @@ | |
| 115421 | ** |
| 115422 | ** Additionally, if pExpr is a simple SQL value and the value is the |
| 115423 | ** same as that currently bound to variable pVar, non-zero is returned. |
| 115424 | ** Otherwise, if the values are not the same or if pExpr is not a simple |
| 115425 | ** SQL value, zero is returned. |
| 115426 | */ |
| 115427 | static int exprCompareVariable( |
| 115428 | const Parse *pParse, |
| 115429 | const Expr *pVar, |
| 115430 | const Expr *pExpr |
| 115431 | ){ |
| 115432 | int res = 0; |
| 115433 | int iVar; |
| 115434 | sqlite3_value *pL, *pR = 0; |
| 115435 | |
| 115436 | sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); |
| 115437 | if( pR ){ |
| 115438 | iVar = pVar->iColumn; |
| 115439 | sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); |
| 115440 | pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB); |
| 115441 | if( pL ){ |
| 115442 | if( sqlite3_value_type(pL)==SQLITE_TEXT ){ |
| 115443 | sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ |
| 115444 | } |
| 115445 | res = 0==sqlite3MemCompare(pL, pR, 0); |
| 115446 | } |
| 115447 | sqlite3ValueFree(pR); |
| 115448 | sqlite3ValueFree(pL); |
| 115449 | } |
| 115450 | |
| 115451 | return res; |
| 115452 | } |
| 115453 | |
| 115454 | /* |
| 115455 | ** Do a deep comparison of two expression trees. Return 0 if the two |
| @@ -115471,16 +115653,14 @@ | |
| 115471 | ** can be sure the expressions are the same. In the places where |
| 115472 | ** this routine is used, it does not hurt to get an extra 2 - that |
| 115473 | ** just might result in some slightly slower code. But returning |
| 115474 | ** an incorrect 0 or 1 could lead to a malfunction. |
| 115475 | ** |
| 115476 | ** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in |
| 115477 | ** pParse->pReprepare can be matched against literals in pB. The |
| 115478 | ** pParse->pVdbe->expmask bitmask is updated for each variable referenced. |
| 115479 | ** If pParse is NULL (the normal case) then any TK_VARIABLE term in |
| 115480 | ** Argument pParse should normally be NULL. If it is not NULL and pA or |
| 115481 | ** pB causes a return value of 2. |
| 115482 | */ |
| 115483 | SQLITE_PRIVATE int sqlite3ExprCompare( |
| 115484 | const Parse *pParse, |
| 115485 | const Expr *pA, |
| 115486 | const Expr *pB, |
| @@ -115488,12 +115668,12 @@ | |
| 115488 | ){ |
| 115489 | u32 combinedFlags; |
| 115490 | if( pA==0 || pB==0 ){ |
| 115491 | return pB==pA ? 0 : 2; |
| 115492 | } |
| 115493 | if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){ |
| 115494 | return 0; |
| 115495 | } |
| 115496 | combinedFlags = pA->flags | pB->flags; |
| 115497 | if( combinedFlags & EP_IntValue ){ |
| 115498 | if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ |
| 115499 | return 0; |
| @@ -115683,23 +115863,75 @@ | |
| 115683 | return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); |
| 115684 | } |
| 115685 | } |
| 115686 | return 0; |
| 115687 | } |
| 115688 | |
| 115689 | /* |
| 115690 | ** Return true if we can prove the pE2 will always be true if pE1 is |
| 115691 | ** true. Return false if we cannot complete the proof or if pE2 might |
| 115692 | ** be false. Examples: |
| 115693 | ** |
| 115694 | ** pE1: x==5 pE2: x==5 Result: true |
| 115695 | ** pE1: x>0 pE2: x==5 Result: false |
| 115696 | ** pE1: x=21 pE2: x=21 OR y=43 Result: true |
| 115697 | ** pE1: x!=123 pE2: x IS NOT NULL Result: true |
| 115698 | ** pE1: x!=?1 pE2: x IS NOT NULL Result: true |
| 115699 | ** pE1: x IS NULL pE2: x IS NOT NULL Result: false |
| 115700 | ** pE1: x IS ?2 pE2: x IS NOT NULL Result: false |
| 115701 | ** |
| 115702 | ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has |
| 115703 | ** Expr.iTable<0 then assume a table number given by iTab. |
| 115704 | ** |
| 115705 | ** If pParse is not NULL, then the values of bound variables in pE1 are |
| @@ -115729,10 +115961,13 @@ | |
| 115729 | if( pE2->op==TK_NOTNULL |
| 115730 | && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0) |
| 115731 | ){ |
| 115732 | return 1; |
| 115733 | } |
| 115734 | return 0; |
| 115735 | } |
| 115736 | |
| 115737 | /* This is a helper function to impliesNotNullRow(). In this routine, |
| 115738 | ** set pWalker->eCode to one only if *both* of the input expressions |
| @@ -120689,12 +120924,12 @@ | |
| 120689 | int nIdxCol = 1; /* Number of columns in stat4 records */ |
| 120690 | |
| 120691 | char *zIndex; /* Index name */ |
| 120692 | Index *pIdx; /* Pointer to the index object */ |
| 120693 | int nSample; /* Number of samples */ |
| 120694 | int nByte; /* Bytes of space required */ |
| 120695 | int i; /* Bytes of space required */ |
| 120696 | tRowcnt *pSpace; /* Available allocated memory space */ |
| 120697 | u8 *pPtr; /* Available memory as a u8 for easier manipulation */ |
| 120698 | |
| 120699 | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| 120700 | if( zIndex==0 ) continue; |
| @@ -121140,19 +121375,10 @@ | |
| 121140 | rc = sqlite3Init(db, &zErrDyn); |
| 121141 | } |
| 121142 | sqlite3BtreeLeaveAll(db); |
| 121143 | assert( zErrDyn==0 || rc!=SQLITE_OK ); |
| 121144 | } |
| 121145 | #ifdef SQLITE_USER_AUTHENTICATION |
| 121146 | if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ |
| 121147 | u8 newAuth = 0; |
| 121148 | rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); |
| 121149 | if( newAuth<db->auth.authLevel ){ |
| 121150 | rc = SQLITE_AUTH_USER; |
| 121151 | } |
| 121152 | } |
| 121153 | #endif |
| 121154 | if( rc ){ |
| 121155 | if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){ |
| 121156 | int iDb = db->nDb - 1; |
| 121157 | assert( iDb>=2 ); |
| 121158 | if( db->aDb[iDb].pBt ){ |
| @@ -121646,15 +121872,11 @@ | |
| 121646 | sqlite3 *db = pParse->db; /* Database handle */ |
| 121647 | char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */ |
| 121648 | int rc; /* Auth callback return code */ |
| 121649 | |
| 121650 | if( db->init.busy ) return SQLITE_OK; |
| 121651 | rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext |
| 121652 | #ifdef SQLITE_USER_AUTHENTICATION |
| 121653 | ,db->auth.zAuthUser |
| 121654 | #endif |
| 121655 | ); |
| 121656 | if( rc==SQLITE_DENY ){ |
| 121657 | char *z = sqlite3_mprintf("%s.%s", zTab, zCol); |
| 121658 | if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z); |
| 121659 | sqlite3ErrorMsg(pParse, "access to %z is prohibited", z); |
| 121660 | pParse->rc = SQLITE_AUTH; |
| @@ -121757,15 +121979,11 @@ | |
| 121757 | testcase( zArg1==0 ); |
| 121758 | testcase( zArg2==0 ); |
| 121759 | testcase( zArg3==0 ); |
| 121760 | testcase( pParse->zAuthContext==0 ); |
| 121761 | |
| 121762 | rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext |
| 121763 | #ifdef SQLITE_USER_AUTHENTICATION |
| 121764 | ,db->auth.zAuthUser |
| 121765 | #endif |
| 121766 | ); |
| 121767 | if( rc==SQLITE_DENY ){ |
| 121768 | sqlite3ErrorMsg(pParse, "not authorized"); |
| 121769 | pParse->rc = SQLITE_AUTH; |
| 121770 | }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ |
| 121771 | rc = SQLITE_DENY; |
| @@ -121994,21 +122212,10 @@ | |
| 121994 | sqlite3VdbeJumpHere(v, addrRewind); |
| 121995 | } |
| 121996 | } |
| 121997 | sqlite3VdbeAddOp0(v, OP_Halt); |
| 121998 | |
| 121999 | #if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE) |
| 122000 | if( pParse->nTableLock>0 && db->init.busy==0 ){ |
| 122001 | sqlite3UserAuthInit(db); |
| 122002 | if( db->auth.authLevel<UAUTH_User ){ |
| 122003 | sqlite3ErrorMsg(pParse, "user not authenticated"); |
| 122004 | pParse->rc = SQLITE_AUTH_USER; |
| 122005 | return; |
| 122006 | } |
| 122007 | } |
| 122008 | #endif |
| 122009 | |
| 122010 | /* The cookie mask contains one bit for each database file open. |
| 122011 | ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are |
| 122012 | ** set for each database that is used. Generate code to start a |
| 122013 | ** transaction on each used database and to verify the schema cookie |
| 122014 | ** on each used database. |
| @@ -122133,20 +122340,10 @@ | |
| 122133 | sqlite3DbFree(db, zSql); |
| 122134 | memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); |
| 122135 | pParse->nested--; |
| 122136 | } |
| 122137 | |
| 122138 | #if SQLITE_USER_AUTHENTICATION |
| 122139 | /* |
| 122140 | ** Return TRUE if zTable is the name of the system table that stores the |
| 122141 | ** list of users and their access credentials. |
| 122142 | */ |
| 122143 | SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){ |
| 122144 | return sqlite3_stricmp(zTable, "sqlite_user")==0; |
| 122145 | } |
| 122146 | #endif |
| 122147 | |
| 122148 | /* |
| 122149 | ** Locate the in-memory structure that describes a particular database |
| 122150 | ** table given the name of that table and (optionally) the name of the |
| 122151 | ** database containing the table. Return NULL if not found. |
| 122152 | ** |
| @@ -122161,17 +122358,10 @@ | |
| 122161 | Table *p = 0; |
| 122162 | int i; |
| 122163 | |
| 122164 | /* All mutexes are required for schema access. Make sure we hold them. */ |
| 122165 | assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); |
| 122166 | #if SQLITE_USER_AUTHENTICATION |
| 122167 | /* Only the admin user is allowed to know that the sqlite_user table |
| 122168 | ** exists */ |
| 122169 | if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ |
| 122170 | return 0; |
| 122171 | } |
| 122172 | #endif |
| 122173 | if( zDatabase ){ |
| 122174 | for(i=0; i<db->nDb; i++){ |
| 122175 | if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; |
| 122176 | } |
| 122177 | if( i>=db->nDb ){ |
| @@ -125826,13 +126016,10 @@ | |
| 125826 | |
| 125827 | assert( pTab!=0 ); |
| 125828 | if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 |
| 125829 | && db->init.busy==0 |
| 125830 | && pTblName!=0 |
| 125831 | #if SQLITE_USER_AUTHENTICATION |
| 125832 | && sqlite3UserAuthTable(pTab->zName)==0 |
| 125833 | #endif |
| 125834 | ){ |
| 125835 | sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); |
| 125836 | goto exit_create_index; |
| 125837 | } |
| 125838 | #ifndef SQLITE_OMIT_VIEW |
| @@ -128224,10 +128411,11 @@ | |
| 128224 | ** 4) The table is a shadow table, the database connection is in |
| 128225 | ** defensive mode, and the current sqlite3_prepare() |
| 128226 | ** is for a top-level SQL statement. |
| 128227 | */ |
| 128228 | static int vtabIsReadOnly(Parse *pParse, Table *pTab){ |
| 128229 | if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ |
| 128230 | return 1; |
| 128231 | } |
| 128232 | |
| 128233 | /* Within triggers: |
| @@ -131716,11 +131904,17 @@ | |
| 131716 | #ifdef SQLITE_DEBUG |
| 131717 | /* |
| 131718 | ** Implementation of fpdecode(x,y,z) function. |
| 131719 | ** |
| 131720 | ** x is a real number that is to be decoded. y is the precision. |
| 131721 | ** z is the maximum real precision. |
| 131722 | */ |
| 131723 | static void fpdecodeFunc( |
| 131724 | sqlite3_context *context, |
| 131725 | int argc, |
| 131726 | sqlite3_value **argv |
| @@ -131740,10 +131934,86 @@ | |
| 131740 | sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); |
| 131741 | }else{ |
| 131742 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); |
| 131743 | } |
| 131744 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| 131745 | } |
| 131746 | #endif /* SQLITE_DEBUG */ |
| 131747 | |
| 131748 | /* |
| 131749 | ** All of the FuncDef structures in the aBuiltinFunc[] array above |
| @@ -131777,13 +132047,10 @@ | |
| 131777 | #endif |
| 131778 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 131779 | SFUNCTION(load_extension, 1, 0, 0, loadExt ), |
| 131780 | SFUNCTION(load_extension, 2, 0, 0, loadExt ), |
| 131781 | #endif |
| 131782 | #if SQLITE_USER_AUTHENTICATION |
| 131783 | FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ), |
| 131784 | #endif |
| 131785 | #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS |
| 131786 | DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), |
| 131787 | DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), |
| 131788 | #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ |
| 131789 | INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), |
| @@ -131805,11 +132072,12 @@ | |
| 131805 | FUNCTION(max, -1, 1, 1, minmaxFunc ), |
| 131806 | FUNCTION(max, 0, 1, 1, 0 ), |
| 131807 | WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, |
| 131808 | SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), |
| 131809 | FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), |
| 131810 | FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), |
| 131811 | FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), |
| 131812 | FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN), |
| 131813 | FUNCTION(instr, 2, 0, 0, instrFunc ), |
| 131814 | FUNCTION(printf, -1, 0, 0, printfFunc ), |
| 131815 | FUNCTION(format, -1, 0, 0, printfFunc ), |
| @@ -131816,10 +132084,11 @@ | |
| 131816 | FUNCTION(unicode, 1, 0, 0, unicodeFunc ), |
| 131817 | FUNCTION(char, -1, 0, 0, charFunc ), |
| 131818 | FUNCTION(abs, 1, 0, 0, absFunc ), |
| 131819 | #ifdef SQLITE_DEBUG |
| 131820 | FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ), |
| 131821 | #endif |
| 131822 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 131823 | FUNCTION(round, 1, 0, 0, roundFunc ), |
| 131824 | FUNCTION(round, 2, 0, 0, roundFunc ), |
| 131825 | #endif |
| @@ -131910,15 +132179,18 @@ | |
| 131910 | MFUNCTION(atanh, 1, atanh, math1Func ), |
| 131911 | #endif |
| 131912 | MFUNCTION(sqrt, 1, sqrt, math1Func ), |
| 131913 | MFUNCTION(radians, 1, degToRad, math1Func ), |
| 131914 | MFUNCTION(degrees, 1, radToDeg, math1Func ), |
| 131915 | FUNCTION(pi, 0, 0, 0, piFunc ), |
| 131916 | #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ |
| 131917 | FUNCTION(sign, 1, 0, 0, signFunc ), |
| 131918 | INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), |
| 131919 | INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), |
| 131920 | }; |
| 131921 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 131922 | sqlite3AlterFunctions(); |
| 131923 | #endif |
| 131924 | sqlite3WindowFunctions(); |
| @@ -140427,16 +140699,10 @@ | |
| 140427 | if( db->autoCommit==0 ){ |
| 140428 | /* Foreign key support may not be enabled or disabled while not |
| 140429 | ** in auto-commit mode. */ |
| 140430 | mask &= ~(SQLITE_ForeignKeys); |
| 140431 | } |
| 140432 | #if SQLITE_USER_AUTHENTICATION |
| 140433 | if( db->auth.authLevel==UAUTH_User ){ |
| 140434 | /* Do not allow non-admin users to modify the schema arbitrarily */ |
| 140435 | mask &= ~(SQLITE_WriteSchema); |
| 140436 | } |
| 140437 | #endif |
| 140438 | |
| 140439 | if( sqlite3GetBoolean(zRight, 0) ){ |
| 140440 | if( (mask & SQLITE_WriteSchema)==0 |
| 140441 | || (db->flags & SQLITE_Defensive)==0 |
| 140442 | ){ |
| @@ -140568,11 +140834,12 @@ | |
| 140568 | pTab = sqliteHashData(k); |
| 140569 | if( pTab->nCol==0 ){ |
| 140570 | char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); |
| 140571 | if( zSql ){ |
| 140572 | sqlite3_stmt *pDummy = 0; |
| 140573 | (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0); |
| 140574 | (void)sqlite3_finalize(pDummy); |
| 140575 | sqlite3DbFree(db, zSql); |
| 140576 | } |
| 140577 | if( db->mallocFailed ){ |
| 140578 | sqlite3ErrorMsg(db->pParse, "out of memory"); |
| @@ -141044,10 +141311,11 @@ | |
| 141044 | } |
| 141045 | aRoot[0] = cnt; |
| 141046 | |
| 141047 | /* Make sure sufficient number of registers have been allocated */ |
| 141048 | sqlite3TouchRegister(pParse, 8+cnt); |
| 141049 | sqlite3ClearTempRegCache(pParse); |
| 141050 | |
| 141051 | /* Do the b-tree integrity checks */ |
| 141052 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141053 | sqlite3VdbeChangeP5(v, (u8)i); |
| @@ -142668,18 +142936,11 @@ | |
| 142668 | encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; |
| 142669 | if( encoding==0 ) encoding = SQLITE_UTF8; |
| 142670 | #else |
| 142671 | encoding = SQLITE_UTF8; |
| 142672 | #endif |
| 142673 | if( db->nVdbeActive>0 && encoding!=ENC(db) |
| 142674 | && (db->mDbFlags & DBFLAG_Vacuum)==0 |
| 142675 | ){ |
| 142676 | rc = SQLITE_LOCKED; |
| 142677 | goto initone_error_out; |
| 142678 | }else{ |
| 142679 | sqlite3SetTextEncoding(db, encoding); |
| 142680 | } |
| 142681 | }else{ |
| 142682 | /* If opening an attached database, the encoding much match ENC(db) */ |
| 142683 | if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){ |
| 142684 | sqlite3SetString(pzErrMsg, db, "attached databases must use the same" |
| 142685 | " text encoding as main database"); |
| @@ -143369,16 +143630,28 @@ | |
| 143369 | #endif |
| 143370 | *ppStmt = 0; |
| 143371 | if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ |
| 143372 | return SQLITE_MISUSE_BKPT; |
| 143373 | } |
| 143374 | if( nBytes>=0 ){ |
| 143375 | int sz; |
| 143376 | const char *z = (const char*)zSql; |
| 143377 | for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){} |
| 143378 | nBytes = sz; |
| 143379 | } |
| 143380 | sqlite3_mutex_enter(db->mutex); |
| 143381 | zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); |
| 143382 | if( zSql8 ){ |
| 143383 | rc = sqlite3LockAndPrepare(db, zSql8, -1, prepFlags, 0, ppStmt, &zTail8); |
| 143384 | } |
| @@ -143388,11 +143661,11 @@ | |
| 143388 | ** equivalent pointer into the UTF-16 string by counting the unicode |
| 143389 | ** characters between zSql8 and zTail8, and then returning a pointer |
| 143390 | ** the same number of characters into the UTF-16 string. |
| 143391 | */ |
| 143392 | int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); |
| 143393 | *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed); |
| 143394 | } |
| 143395 | sqlite3DbFree(db, zSql8); |
| 143396 | rc = sqlite3ApiExit(db, rc); |
| 143397 | sqlite3_mutex_leave(db->mutex); |
| 143398 | return rc; |
| @@ -147361,36 +147634,36 @@ | |
| 147361 | return pExpr; |
| 147362 | } |
| 147363 | if( pSubst->isOuterJoin ){ |
| 147364 | ExprSetProperty(pNew, EP_CanBeNull); |
| 147365 | } |
| 147366 | if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ |
| 147367 | sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, |
| 147368 | pExpr->flags & (EP_OuterON|EP_InnerON)); |
| 147369 | } |
| 147370 | sqlite3ExprDelete(db, pExpr); |
| 147371 | pExpr = pNew; |
| 147372 | if( pExpr->op==TK_TRUEFALSE ){ |
| 147373 | pExpr->u.iValue = sqlite3ExprTruthValue(pExpr); |
| 147374 | pExpr->op = TK_INTEGER; |
| 147375 | ExprSetProperty(pExpr, EP_IntValue); |
| 147376 | } |
| 147377 | |
| 147378 | /* Ensure that the expression now has an implicit collation sequence, |
| 147379 | ** just as it did when it was a column of a view or sub-query. */ |
| 147380 | { |
| 147381 | CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr); |
| 147382 | CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, |
| 147383 | pSubst->pCList->a[iColumn].pExpr |
| 147384 | ); |
| 147385 | if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){ |
| 147386 | pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, |
| 147387 | (pColl ? pColl->zName : "BINARY") |
| 147388 | ); |
| 147389 | } |
| 147390 | } |
| 147391 | ExprClearProperty(pExpr, EP_Collate); |
| 147392 | } |
| 147393 | } |
| 147394 | }else{ |
| 147395 | if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){ |
| 147396 | pExpr->iTable = pSubst->iNewTable; |
| @@ -148123,20 +148396,20 @@ | |
| 148123 | } |
| 148124 | |
| 148125 | /* Transfer the FROM clause terms from the subquery into the |
| 148126 | ** outer query. |
| 148127 | */ |
| 148128 | for(i=0; i<nSubSrc; i++){ |
| 148129 | SrcItem *pItem = &pSrc->a[i+iFrom]; |
| 148130 | assert( pItem->fg.isTabFunc==0 ); |
| 148131 | assert( pItem->fg.isSubquery |
| 148132 | || pItem->fg.fixedSchema |
| 148133 | || pItem->u4.zDatabase==0 ); |
| 148134 | if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); |
| 148135 | *pItem = pSubSrc->a[i]; |
| 148136 | pItem->fg.jointype |= ltorj; |
| 148137 | iNewParent = pSubSrc->a[i].iCursor; |
| 148138 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 148139 | } |
| 148140 | pSrc->a[iFrom].fg.jointype &= JT_LTORJ; |
| 148141 | pSrc->a[iFrom].fg.jointype |= jointype | ltorj; |
| 148142 | |
| @@ -148172,10 +148445,11 @@ | |
| 148172 | pSub->pOrderBy = 0; |
| 148173 | } |
| 148174 | pWhere = pSub->pWhere; |
| 148175 | pSub->pWhere = 0; |
| 148176 | if( isOuterJoin>0 ){ |
| 148177 | sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); |
| 148178 | } |
| 148179 | if( pWhere ){ |
| 148180 | if( pParent->pWhere ){ |
| 148181 | pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere); |
| @@ -151271,11 +151545,11 @@ | |
| 151271 | sqlite3TreeViewSelect(0, p, 0); |
| 151272 | } |
| 151273 | #endif |
| 151274 | assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); |
| 151275 | }else{ |
| 151276 | TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); |
| 151277 | } |
| 151278 | |
| 151279 | /* Convert unused result columns of the subquery into simple NULL |
| 151280 | ** expressions, to avoid unneeded searching and computation. |
| 151281 | ** tag-select-0440 |
| @@ -156980,10 +157254,11 @@ | |
| 156980 | assert( sParse.zErrMsg==0 ); |
| 156981 | if( !pTab->aCol ){ |
| 156982 | Table *pNew = sParse.pNewTable; |
| 156983 | Index *pIdx; |
| 156984 | pTab->aCol = pNew->aCol; |
| 156985 | sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); |
| 156986 | pTab->nNVCol = pTab->nCol = pNew->nCol; |
| 156987 | pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); |
| 156988 | pNew->nCol = 0; |
| 156989 | pNew->aCol = 0; |
| @@ -158044,13 +158319,21 @@ | |
| 158044 | SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( |
| 158045 | const Parse *pParse, /* Parse context */ |
| 158046 | const WhereInfo *pWInfo, /* WHERE clause */ |
| 158047 | const WhereLevel *pLevel /* Bloom filter on this level */ |
| 158048 | ); |
| 158049 | #else |
| 158050 | # define sqlite3WhereExplainOneScan(u,v,w,x) 0 |
| 158051 | # define sqlite3WhereExplainBloomFilter(u,v,w) 0 |
| 158052 | #endif /* SQLITE_OMIT_EXPLAIN */ |
| 158053 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 158054 | SQLITE_PRIVATE void sqlite3WhereAddScanStatus( |
| 158055 | Vdbe *v, /* Vdbe to add scanstatus entry to */ |
| 158056 | SrcList *pSrclist, /* FROM clause pLvl reads data from */ |
| @@ -158248,42 +158531,42 @@ | |
| 158248 | } |
| 158249 | sqlite3_str_append(pStr, ")", 1); |
| 158250 | } |
| 158251 | |
| 158252 | /* |
| 158253 | ** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN |
| 158254 | ** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG |
| 158255 | ** was defined at compile-time. If it is not a no-op, a single OP_Explain |
| 158256 | ** opcode is added to the output to describe the table scan strategy in pLevel. |
| 158257 | ** |
| 158258 | ** If an OP_Explain opcode is added to the VM, its address is returned. |
| 158259 | ** Otherwise, if no OP_Explain is coded, zero is returned. |
| 158260 | */ |
| 158261 | SQLITE_PRIVATE int sqlite3WhereExplainOneScan( |
| 158262 | Parse *pParse, /* Parse context */ |
| 158263 | SrcList *pTabList, /* Table list this loop refers to */ |
| 158264 | WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ |
| 158265 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ |
| 158266 | ){ |
| 158267 | int ret = 0; |
| 158268 | #if !defined(SQLITE_DEBUG) |
| 158269 | if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) |
| 158270 | #endif |
| 158271 | { |
| 158272 | SrcItem *pItem = &pTabList->a[pLevel->iFrom]; |
| 158273 | Vdbe *v = pParse->pVdbe; /* VM being constructed */ |
| 158274 | sqlite3 *db = pParse->db; /* Database handle */ |
| 158275 | int isSearch; /* True for a SEARCH. False for SCAN. */ |
| 158276 | WhereLoop *pLoop; /* The controlling WhereLoop object */ |
| 158277 | u32 flags; /* Flags that describe this loop */ |
| 158278 | char *zMsg; /* Text to add to EQP output */ |
| 158279 | StrAccum str; /* EQP output string */ |
| 158280 | char zBuf[100]; /* Initial space for EQP output string */ |
| 158281 | |
| 158282 | pLoop = pLevel->pWLoop; |
| 158283 | flags = pLoop->wsFlags; |
| 158284 | if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0; |
| 158285 | |
| 158286 | isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 |
| 158287 | || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) |
| 158288 | || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); |
| 158289 | |
| @@ -158303,11 +158586,11 @@ | |
| 158303 | } |
| 158304 | }else if( flags & WHERE_PARTIALIDX ){ |
| 158305 | zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; |
| 158306 | }else if( flags & WHERE_AUTO_INDEX ){ |
| 158307 | zFmt = "AUTOMATIC COVERING INDEX"; |
| 158308 | }else if( flags & WHERE_IDX_ONLY ){ |
| 158309 | zFmt = "COVERING INDEX %s"; |
| 158310 | }else{ |
| 158311 | zFmt = "INDEX %s"; |
| 158312 | } |
| 158313 | if( zFmt ){ |
| @@ -158355,15 +158638,54 @@ | |
| 158355 | sqlite3LogEstToInt(pLoop->nOut)); |
| 158356 | }else{ |
| 158357 | sqlite3_str_append(&str, " (~1 row)", 9); |
| 158358 | } |
| 158359 | #endif |
| 158360 | zMsg = sqlite3StrAccumFinish(&str); |
| 158361 | sqlite3ExplainBreakpoint("",zMsg); |
| 158362 | ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), |
| 158363 | pParse->addrExplain, pLoop->rRun, |
| 158364 | zMsg, P4_DYNAMIC); |
| 158365 | } |
| 158366 | return ret; |
| 158367 | } |
| 158368 | |
| 158369 | /* |
| @@ -158458,13 +158780,14 @@ | |
| 158458 | if( wsFlags & WHERE_INDEXED ){ |
| 158459 | sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); |
| 158460 | } |
| 158461 | }else{ |
| 158462 | int addr; |
| 158463 | assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); |
| 158464 | addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; |
| 158465 | VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1); |
| 158466 | assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); |
| 158467 | assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); |
| 158468 | sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); |
| 158469 | } |
| 158470 | } |
| @@ -158713,10 +159036,11 @@ | |
| 158713 | if( pOrigLhs ){ |
| 158714 | sqlite3ExprListDelete(db, pOrigLhs); |
| 158715 | pNew->pLeft->x.pList = pLhs; |
| 158716 | } |
| 158717 | pSelect->pEList = pRhs; |
| 158718 | if( pLhs && pLhs->nExpr==1 ){ |
| 158719 | /* Take care here not to generate a TK_VECTOR containing only a |
| 158720 | ** single value. Since the parser never creates such a vector, some |
| 158721 | ** of the subroutines do not handle this case. */ |
| 158722 | Expr *p = pLhs->a[0].pExpr; |
| @@ -161260,24 +161584,29 @@ | |
| 161260 | }else if( op==TK_STRING ){ |
| 161261 | assert( !ExprHasProperty(pRight, EP_IntValue) ); |
| 161262 | z = (u8*)pRight->u.zToken; |
| 161263 | } |
| 161264 | if( z ){ |
| 161265 | |
| 161266 | /* Count the number of prefix characters prior to the first wildcard. |
| 161267 | ** If the underlying database has a UTF16LE encoding, then only consider |
| 161268 | ** ASCII characters. Note that the encoding of z[] is UTF8 - we are |
| 161269 | ** dealing with only UTF8 here in this code, but the database engine |
| 161270 | ** itself might be processing content using a different encoding. */ |
| 161271 | cnt = 0; |
| 161272 | while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ |
| 161273 | cnt++; |
| 161274 | if( c==wc[3] && z[cnt]!=0 ){ |
| 161275 | cnt++; |
| 161276 | }else if( c>=0x80 && ENC(db)==SQLITE_UTF16LE ){ |
| 161277 | cnt--; |
| 161278 | break; |
| 161279 | } |
| 161280 | } |
| 161281 | |
| 161282 | /* The optimization is possible only if (1) the pattern does not begin |
| 161283 | ** with a wildcard and if (2) the non-wildcard prefix does not end with |
| @@ -161285,11 +161614,11 @@ | |
| 161285 | ** a single escape character. The second condition is necessary so |
| 161286 | ** that we can increment the prefix key to find an upper bound for the |
| 161287 | ** range search. The third is because the caller assumes that the pattern |
| 161288 | ** consists of at least one character after all escapes have been |
| 161289 | ** removed. */ |
| 161290 | if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && 255!=(u8)z[cnt-1] ){ |
| 161291 | Expr *pPrefix; |
| 161292 | |
| 161293 | /* A "complete" match if the pattern ends with "*" or "%" */ |
| 161294 | *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; |
| 161295 | |
| @@ -163780,11 +164109,11 @@ | |
| 163780 | || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 163781 | ){ |
| 163782 | return 0; |
| 163783 | } |
| 163784 | if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 |
| 163785 | && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 163786 | ){ |
| 163787 | return 0; |
| 163788 | } |
| 163789 | return 1; |
| 163790 | } |
| @@ -164577,13 +164906,15 @@ | |
| 164577 | ** Whether or not an error is returned, it is the responsibility of the |
| 164578 | ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates |
| 164579 | ** that this is required. |
| 164580 | */ |
| 164581 | static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ |
| 164582 | sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; |
| 164583 | int rc; |
| 164584 | |
| 164585 | whereTraceIndexInfoInputs(p, pTab); |
| 164586 | pParse->db->nSchemaLock++; |
| 164587 | rc = pVtab->pModule->xBestIndex(pVtab, p); |
| 164588 | pParse->db->nSchemaLock--; |
| 164589 | whereTraceIndexInfoOutputs(p, pTab); |
| @@ -165271,11 +165602,11 @@ | |
| 165271 | return rc; |
| 165272 | } |
| 165273 | #endif /* SQLITE_ENABLE_STAT4 */ |
| 165274 | |
| 165275 | |
| 165276 | #ifdef WHERETRACE_ENABLED |
| 165277 | /* |
| 165278 | ** Print the content of a WhereTerm object |
| 165279 | */ |
| 165280 | SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ |
| 165281 | if( pTerm==0 ){ |
| @@ -165314,10 +165645,13 @@ | |
| 165314 | sqlite3DebugPrintf(" iParent=%d", pTerm->iParent); |
| 165315 | } |
| 165316 | sqlite3DebugPrintf("\n"); |
| 165317 | sqlite3TreeViewExpr(0, pTerm->pExpr, 0); |
| 165318 | } |
| 165319 | } |
| 165320 | #endif |
| 165321 | |
| 165322 | #ifdef WHERETRACE_ENABLED |
| 165323 | /* |
| @@ -165522,11 +165856,11 @@ | |
| 165522 | ** Return TRUE if X is a proper subset of Y but is of equal or less cost. |
| 165523 | ** In other words, return true if all constraints of X are also part of Y |
| 165524 | ** and Y has additional constraints that might speed the search that X lacks |
| 165525 | ** but the cost of running X is not more than the cost of running Y. |
| 165526 | ** |
| 165527 | ** In other words, return true if the cost relationwship between X and Y |
| 165528 | ** is inverted and needs to be adjusted. |
| 165529 | ** |
| 165530 | ** Case 1: |
| 165531 | ** |
| 165532 | ** (1a) X and Y use the same index. |
| @@ -166500,11 +166834,10 @@ | |
| 166500 | pParse = pWC->pWInfo->pParse; |
| 166501 | while( pWhere->op==TK_AND ){ |
| 166502 | if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; |
| 166503 | pWhere = pWhere->pRight; |
| 166504 | } |
| 166505 | if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; |
| 166506 | for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 166507 | Expr *pExpr; |
| 166508 | pExpr = pTerm->pExpr; |
| 166509 | if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) |
| 166510 | && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) |
| @@ -169140,10 +169473,11 @@ | |
| 169140 | hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; |
| 169141 | for(i=pWInfo->nLevel-1; i>=1; i--){ |
| 169142 | WhereTerm *pTerm, *pEnd; |
| 169143 | SrcItem *pItem; |
| 169144 | WhereLoop *pLoop; |
| 169145 | pLoop = pWInfo->a[i].pWLoop; |
| 169146 | pItem = &pWInfo->pTabList->a[pLoop->iTab]; |
| 169147 | if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; |
| 169148 | if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 |
| 169149 | && (pLoop->wsFlags & WHERE_ONEROW)==0 |
| @@ -169160,17 +169494,20 @@ | |
| 169160 | break; |
| 169161 | } |
| 169162 | } |
| 169163 | if( hasRightJoin |
| 169164 | && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 169165 | && pTerm->pExpr->w.iJoin==pItem->iCursor |
| 169166 | ){ |
| 169167 | break; /* restriction (5) */ |
| 169168 | } |
| 169169 | } |
| 169170 | if( pTerm<pEnd ) continue; |
| 169171 | WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId)); |
| 169172 | notReady &= ~pLoop->maskSelf; |
| 169173 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| 169174 | if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ |
| 169175 | pTerm->wtFlags |= TERM_CODED; |
| 169176 | } |
| @@ -169237,62 +169574,10 @@ | |
| 169237 | nSearch += pLoop->nOut; |
| 169238 | if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta; |
| 169239 | } |
| 169240 | } |
| 169241 | |
| 169242 | /* |
| 169243 | ** Expression Node callback for sqlite3ExprCanReturnSubtype(). |
| 169244 | ** |
| 169245 | ** Only a function call is able to return a subtype. So if the node |
| 169246 | ** is not a function call, return WRC_Prune immediately. |
| 169247 | ** |
| 169248 | ** A function call is able to return a subtype if it has the |
| 169249 | ** SQLITE_RESULT_SUBTYPE property. |
| 169250 | ** |
| 169251 | ** Assume that every function is able to pass-through a subtype from |
| 169252 | ** one of its argument (using sqlite3_result_value()). Most functions |
| 169253 | ** are not this way, but we don't have a mechanism to distinguish those |
| 169254 | ** that are from those that are not, so assume they all work this way. |
| 169255 | ** That means that if one of its arguments is another function and that |
| 169256 | ** other function is able to return a subtype, then this function is |
| 169257 | ** able to return a subtype. |
| 169258 | */ |
| 169259 | static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ |
| 169260 | int n; |
| 169261 | FuncDef *pDef; |
| 169262 | sqlite3 *db; |
| 169263 | if( pExpr->op!=TK_FUNCTION ){ |
| 169264 | return WRC_Prune; |
| 169265 | } |
| 169266 | assert( ExprUseXList(pExpr) ); |
| 169267 | db = pWalker->pParse->db; |
| 169268 | n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; |
| 169269 | pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); |
| 169270 | if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ |
| 169271 | pWalker->eCode = 1; |
| 169272 | return WRC_Prune; |
| 169273 | } |
| 169274 | return WRC_Continue; |
| 169275 | } |
| 169276 | |
| 169277 | /* |
| 169278 | ** Return TRUE if expression pExpr is able to return a subtype. |
| 169279 | ** |
| 169280 | ** A TRUE return does not guarantee that a subtype will be returned. |
| 169281 | ** It only indicates that a subtype return is possible. False positives |
| 169282 | ** are acceptable as they only disable an optimization. False negatives, |
| 169283 | ** on the other hand, can lead to incorrect answers. |
| 169284 | */ |
| 169285 | static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ |
| 169286 | Walker w; |
| 169287 | memset(&w, 0, sizeof(w)); |
| 169288 | w.pParse = pParse; |
| 169289 | w.xExprCallback = exprNodeCanReturnSubtype; |
| 169290 | sqlite3WalkExpr(&w, pExpr); |
| 169291 | return w.eCode; |
| 169292 | } |
| 169293 | |
| 169294 | /* |
| 169295 | ** The index pIdx is used by a query and contains one or more expressions. |
| 169296 | ** In other words pIdx is an index on an expression. iIdxCur is the cursor |
| 169297 | ** number for the index and iDataCur is the cursor number for the corresponding |
| 169298 | ** table. |
| @@ -169322,16 +169607,10 @@ | |
| 169322 | pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); |
| 169323 | }else{ |
| 169324 | continue; |
| 169325 | } |
| 169326 | if( sqlite3ExprIsConstant(0,pExpr) ) continue; |
| 169327 | if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ |
| 169328 | /* Functions that might set a subtype should not be replaced by the |
| 169329 | ** value taken from an expression index since the index omits the |
| 169330 | ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ |
| 169331 | continue; |
| 169332 | } |
| 169333 | p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); |
| 169334 | if( p==0 ) break; |
| 169335 | p->pIENext = pParse->pIdxEpr; |
| 169336 | #ifdef WHERETRACE_ENABLED |
| 169337 | if( sqlite3WhereTrace & 0x200 ){ |
| @@ -170434,18 +170713,32 @@ | |
| 170434 | x = sqlite3TableColumnToIndex(pIdx, x); |
| 170435 | if( x>=0 ){ |
| 170436 | pOp->p2 = x; |
| 170437 | pOp->p1 = pLevel->iIdxCur; |
| 170438 | OpcodeRewriteTrace(db, k, pOp); |
| 170439 | }else{ |
| 170440 | /* Unable to translate the table reference into an index |
| 170441 | ** reference. Verify that this is harmless - that the |
| 170442 | ** table being referenced really is open. |
| 170443 | */ |
| 170444 | if( pLoop->wsFlags & WHERE_IDX_ONLY ){ |
| 170445 | sqlite3ErrorMsg(pParse, "internal query planner error"); |
| 170446 | pParse->rc = SQLITE_INTERNAL; |
| 170447 | } |
| 170448 | } |
| 170449 | }else if( pOp->opcode==OP_Rowid ){ |
| 170450 | pOp->p1 = pLevel->iIdxCur; |
| 170451 | pOp->opcode = OP_IdxRowid; |
| @@ -172149,10 +172442,11 @@ | |
| 172149 | for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ |
| 172150 | FuncDef *pFunc = pWin->pWFunc; |
| 172151 | int regArg; |
| 172152 | int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); |
| 172153 | int i; |
| 172154 | |
| 172155 | assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED ); |
| 172156 | |
| 172157 | /* All OVER clauses in the same window function aggregate step must |
| 172158 | ** be the same. */ |
| @@ -172164,10 +172458,22 @@ | |
| 172164 | }else{ |
| 172165 | sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i); |
| 172166 | } |
| 172167 | } |
| 172168 | regArg = reg; |
| 172169 | |
| 172170 | if( pMWin->regStartRowid==0 |
| 172171 | && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) |
| 172172 | && (pWin->eStart!=TK_UNBOUNDED) |
| 172173 | ){ |
| @@ -172184,29 +172490,17 @@ | |
| 172184 | sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp); |
| 172185 | sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); |
| 172186 | } |
| 172187 | sqlite3VdbeJumpHere(v, addrIsNull); |
| 172188 | }else if( pWin->regApp ){ |
| 172189 | assert( pFunc->zName==nth_valueName |
| 172190 | || pFunc->zName==first_valueName |
| 172191 | ); |
| 172192 | assert( bInverse==0 || bInverse==1 ); |
| 172193 | sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); |
| 172194 | }else if( pFunc->xSFunc!=noopStepFunc ){ |
| 172195 | int addrIf = 0; |
| 172196 | if( pWin->pFilter ){ |
| 172197 | int regTmp; |
| 172198 | assert( ExprUseXList(pWin->pOwner) ); |
| 172199 | assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); |
| 172200 | assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); |
| 172201 | regTmp = sqlite3GetTempReg(pParse); |
| 172202 | sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); |
| 172203 | addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); |
| 172204 | VdbeCoverage(v); |
| 172205 | sqlite3ReleaseTempReg(pParse, regTmp); |
| 172206 | } |
| 172207 | |
| 172208 | if( pWin->bExprArgs ){ |
| 172209 | int iOp = sqlite3VdbeCurrentAddr(v); |
| 172210 | int iEnd; |
| 172211 | |
| 172212 | assert( ExprUseXList(pWin->pOwner) ); |
| @@ -172233,12 +172527,13 @@ | |
| 172233 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172234 | sqlite3VdbeChangeP5(v, (u8)nArg); |
| 172235 | if( pWin->bExprArgs ){ |
| 172236 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172237 | } |
| 172238 | if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); |
| 172239 | } |
| 172240 | } |
| 172241 | } |
| 172242 | |
| 172243 | /* |
| 172244 | ** Values that may be passed as the second argument to windowCodeOp(). |
| @@ -173660,10 +173955,17 @@ | |
| 173660 | ** Then the "b" IdList records the list "a,b,c". |
| 173661 | */ |
| 173662 | struct TrigEvent { int a; IdList * b; }; |
| 173663 | |
| 173664 | struct FrameBound { int eType; Expr *pExpr; }; |
| 173665 | |
| 173666 | /* |
| 173667 | ** Disable lookaside memory allocation for objects that might be |
| 173668 | ** shared across database connections. |
| 173669 | */ |
| @@ -177553,11 +177855,15 @@ | |
| 177553 | } |
| 177554 | break; |
| 177555 | case 84: /* cmd ::= select */ |
| 177556 | { |
| 177557 | SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; |
| 177558 | sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); |
| 177559 | sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); |
| 177560 | } |
| 177561 | break; |
| 177562 | case 85: /* select ::= WITH wqlist selectnowith */ |
| 177563 | {yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} |
| @@ -178024,11 +178330,11 @@ | |
| 178024 | ** that look like this: #1 #2 ... These terms refer to registers |
| 178025 | ** in the virtual machine. #N is the N-th register. */ |
| 178026 | Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ |
| 178027 | assert( t.n>=2 ); |
| 178028 | if( pParse->nested==0 ){ |
| 178029 | sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); |
| 178030 | yymsp[0].minor.yy454 = 0; |
| 178031 | }else{ |
| 178032 | yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); |
| 178033 | if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); |
| 178034 | } |
| @@ -178872,11 +179178,11 @@ | |
| 178872 | #define TOKEN yyminor |
| 178873 | /************ Begin %syntax_error code ****************************************/ |
| 178874 | |
| 178875 | UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ |
| 178876 | if( TOKEN.z[0] ){ |
| 178877 | sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); |
| 178878 | }else{ |
| 178879 | sqlite3ErrorMsg(pParse, "incomplete input"); |
| 178880 | } |
| 178881 | /************ End %syntax_error code ******************************************/ |
| 178882 | sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| @@ -180363,11 +180669,13 @@ | |
| 180363 | } |
| 180364 | if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ |
| 180365 | if( pParse->zErrMsg==0 ){ |
| 180366 | pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); |
| 180367 | } |
| 180368 | sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); |
| 180369 | nErr++; |
| 180370 | } |
| 180371 | pParse->zTail = zSql; |
| 180372 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 180373 | sqlite3_free(pParse->apVtabLock); |
| @@ -181071,36 +181379,10 @@ | |
| 181071 | ** all database files specified with a relative pathname. |
| 181072 | ** |
| 181073 | ** See also the "PRAGMA data_store_directory" SQL command. |
| 181074 | */ |
| 181075 | SQLITE_API char *sqlite3_data_directory = 0; |
| 181076 | |
| 181077 | /* |
| 181078 | ** Determine whether or not high-precision (long double) floating point |
| 181079 | ** math works correctly on CPU currently running. |
| 181080 | */ |
| 181081 | static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){ |
| 181082 | if( sizeof(LONGDOUBLE_TYPE)<=8 ){ |
| 181083 | /* If the size of "long double" is not more than 8, then |
| 181084 | ** high-precision math is not possible. */ |
| 181085 | return 0; |
| 181086 | }else{ |
| 181087 | /* Just because sizeof(long double)>8 does not mean that the underlying |
| 181088 | ** hardware actually supports high-precision floating point. For example, |
| 181089 | ** clearing the 0x100 bit in the floating-point control word on Intel |
| 181090 | ** processors will make long double work like double, even though long |
| 181091 | ** double takes up more space. The only way to determine if long double |
| 181092 | ** actually works is to run an experiment. */ |
| 181093 | LONGDOUBLE_TYPE a, b, c; |
| 181094 | rc++; |
| 181095 | a = 1.0+rc*0.1; |
| 181096 | b = 1.0e+18+rc*25.0; |
| 181097 | c = a+b; |
| 181098 | return b!=c; |
| 181099 | } |
| 181100 | } |
| 181101 | |
| 181102 | |
| 181103 | /* |
| 181104 | ** Initialize SQLite. |
| 181105 | ** |
| 181106 | ** This routine must be called to initialize the memory allocation, |
| @@ -181292,17 +181574,10 @@ | |
| 181292 | if( bRunExtraInit ){ |
| 181293 | int SQLITE_EXTRA_INIT(const char*); |
| 181294 | rc = SQLITE_EXTRA_INIT(0); |
| 181295 | } |
| 181296 | #endif |
| 181297 | |
| 181298 | /* Experimentally determine if high-precision floating point is |
| 181299 | ** available. */ |
| 181300 | #ifndef SQLITE_OMIT_WSD |
| 181301 | sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc); |
| 181302 | #endif |
| 181303 | |
| 181304 | return rc; |
| 181305 | } |
| 181306 | |
| 181307 | /* |
| 181308 | ** Undo the effects of sqlite3_initialize(). Must not be called while |
| @@ -182369,14 +182644,10 @@ | |
| 182369 | #endif |
| 182370 | |
| 182371 | sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ |
| 182372 | sqlite3ValueFree(db->pErr); |
| 182373 | sqlite3CloseExtensions(db); |
| 182374 | #if SQLITE_USER_AUTHENTICATION |
| 182375 | sqlite3_free(db->auth.zAuthUser); |
| 182376 | sqlite3_free(db->auth.zAuthPW); |
| 182377 | #endif |
| 182378 | |
| 182379 | db->eOpenState = SQLITE_STATE_ERROR; |
| 182380 | |
| 182381 | /* The temp-database schema is allocated differently from the other schema |
| 182382 | ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). |
| @@ -183875,12 +184146,12 @@ | |
| 183875 | } |
| 183876 | oldLimit = db->aLimit[limitId]; |
| 183877 | if( newLimit>=0 ){ /* IMP: R-52476-28732 */ |
| 183878 | if( newLimit>aHardLimit[limitId] ){ |
| 183879 | newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ |
| 183880 | }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ |
| 183881 | newLimit = 1; |
| 183882 | } |
| 183883 | db->aLimit[limitId] = newLimit; |
| 183884 | } |
| 183885 | return oldLimit; /* IMP: R-53341-35419 */ |
| 183886 | } |
| @@ -184395,10 +184666,11 @@ | |
| 184395 | testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ |
| 184396 | testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ |
| 184397 | if( ((1<<(flags&7)) & 0x46)==0 ){ |
| 184398 | rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ |
| 184399 | }else{ |
| 184400 | rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); |
| 184401 | } |
| 184402 | if( rc!=SQLITE_OK ){ |
| 184403 | if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); |
| 184404 | sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg); |
| @@ -185237,10 +185509,11 @@ | |
| 185237 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185238 | sqlite3ShowWindow(0); |
| 185239 | sqlite3ShowWinFunc(0); |
| 185240 | #endif |
| 185241 | sqlite3ShowSelect(0); |
| 185242 | } |
| 185243 | #endif |
| 185244 | break; |
| 185245 | } |
| 185246 | |
| @@ -185549,28 +185822,10 @@ | |
| 185549 | *pI1 = rLogEst; |
| 185550 | *pU64 = sqlite3LogEstToInt(rLogEst); |
| 185551 | *pI2 = sqlite3LogEst(*pU64); |
| 185552 | break; |
| 185553 | } |
| 185554 | |
| 185555 | #if !defined(SQLITE_OMIT_WSD) |
| 185556 | /* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X); |
| 185557 | ** |
| 185558 | ** X<0 Make no changes to the bUseLongDouble. Just report value. |
| 185559 | ** X==0 Disable bUseLongDouble |
| 185560 | ** X==1 Enable bUseLongDouble |
| 185561 | ** X>=2 Set bUseLongDouble to its default value for this platform |
| 185562 | */ |
| 185563 | case SQLITE_TESTCTRL_USELONGDOUBLE: { |
| 185564 | int b = va_arg(ap, int); |
| 185565 | if( b>=2 ) b = hasHighPrecisionDouble(b); |
| 185566 | if( b>=0 ) sqlite3Config.bUseLongDouble = b>0; |
| 185567 | rc = sqlite3Config.bUseLongDouble!=0; |
| 185568 | break; |
| 185569 | } |
| 185570 | #endif |
| 185571 | |
| 185572 | |
| 185573 | #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) |
| 185574 | /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) |
| 185575 | ** |
| 185576 | ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value |
| @@ -185875,11 +186130,15 @@ | |
| 185875 | if( db->autoCommit==0 ){ |
| 185876 | int iDb = sqlite3FindDbName(db, zDb); |
| 185877 | if( iDb==0 || iDb>1 ){ |
| 185878 | Btree *pBt = db->aDb[iDb].pBt; |
| 185879 | if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){ |
| 185880 | rc = sqlite3BtreeBeginTrans(pBt, 0, 0); |
| 185881 | if( rc==SQLITE_OK ){ |
| 185882 | rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); |
| 185883 | } |
| 185884 | } |
| 185885 | } |
| @@ -189665,14 +189924,19 @@ | |
| 189665 | |
| 189666 | assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 ); |
| 189667 | if( *p1==POS_COLUMN ){ |
| 189668 | p1++; |
| 189669 | p1 += fts3GetVarint32(p1, &iCol1); |
| 189670 | } |
| 189671 | if( *p2==POS_COLUMN ){ |
| 189672 | p2++; |
| 189673 | p2 += fts3GetVarint32(p2, &iCol2); |
| 189674 | } |
| 189675 | |
| 189676 | while( 1 ){ |
| 189677 | if( iCol1==iCol2 ){ |
| 189678 | char *pSave = p; |
| @@ -192839,11 +193103,11 @@ | |
| 192839 | for(p=pExpr; p->pLeft; p=p->pLeft){ |
| 192840 | assert( p->pRight->pPhrase->doclist.nList>0 ); |
| 192841 | nTmp += p->pRight->pPhrase->doclist.nList; |
| 192842 | } |
| 192843 | nTmp += p->pPhrase->doclist.nList; |
| 192844 | aTmp = sqlite3_malloc64(nTmp*2); |
| 192845 | if( !aTmp ){ |
| 192846 | *pRc = SQLITE_NOMEM; |
| 192847 | res = 0; |
| 192848 | }else{ |
| 192849 | char *aPoslist = p->pPhrase->doclist.pList; |
| @@ -193490,11 +193754,11 @@ | |
| 193490 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ |
| 193491 | return SQLITE_CORRUPT_VTAB; |
| 193492 | } |
| 193493 | #endif |
| 193494 | |
| 193495 | #if !SQLITE_CORE |
| 193496 | /* |
| 193497 | ** Initialize API pointer table, if required. |
| 193498 | */ |
| 193499 | #ifdef _WIN32 |
| 193500 | __declspec(dllexport) |
| @@ -194392,14 +194656,15 @@ | |
| 194392 | rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); |
| 194393 | if( rc==SQLITE_OK ){ |
| 194394 | Fts3PhraseToken *pToken; |
| 194395 | |
| 194396 | p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); |
| 194397 | if( !p ) goto no_mem; |
| 194398 | |
| 194399 | zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); |
| 194400 | if( !zTemp ) goto no_mem; |
| 194401 | |
| 194402 | assert( nToken==ii ); |
| 194403 | pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; |
| 194404 | memset(pToken, 0, sizeof(Fts3PhraseToken)); |
| 194405 | |
| @@ -194410,53 +194675,51 @@ | |
| 194410 | pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*'); |
| 194411 | pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^'); |
| 194412 | nToken = ii+1; |
| 194413 | } |
| 194414 | } |
| 194415 | |
| 194416 | pModule->xClose(pCursor); |
| 194417 | pCursor = 0; |
| 194418 | } |
| 194419 | |
| 194420 | if( rc==SQLITE_DONE ){ |
| 194421 | int jj; |
| 194422 | char *zBuf = 0; |
| 194423 | |
| 194424 | p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); |
| 194425 | if( !p ) goto no_mem; |
| 194426 | memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); |
| 194427 | p->eType = FTSQUERY_PHRASE; |
| 194428 | p->pPhrase = (Fts3Phrase *)&p[1]; |
| 194429 | p->pPhrase->iColumn = pParse->iDefaultCol; |
| 194430 | p->pPhrase->nToken = nToken; |
| 194431 | |
| 194432 | zBuf = (char *)&p->pPhrase->aToken[nToken]; |
| 194433 | if( zTemp ){ |
| 194434 | memcpy(zBuf, zTemp, nTemp); |
| 194435 | sqlite3_free(zTemp); |
| 194436 | }else{ |
| 194437 | assert( nTemp==0 ); |
| 194438 | } |
| 194439 | |
| 194440 | for(jj=0; jj<p->pPhrase->nToken; jj++){ |
| 194441 | p->pPhrase->aToken[jj].z = zBuf; |
| 194442 | zBuf += p->pPhrase->aToken[jj].n; |
| 194443 | } |
| 194444 | rc = SQLITE_OK; |
| 194445 | } |
| 194446 | |
| 194447 | *ppExpr = p; |
| 194448 | return rc; |
| 194449 | no_mem: |
| 194450 | |
| 194451 | if( pCursor ){ |
| 194452 | pModule->xClose(pCursor); |
| 194453 | } |
| 194454 | sqlite3_free(zTemp); |
| 194455 | sqlite3_free(p); |
| 194456 | *ppExpr = 0; |
| 194457 | return SQLITE_NOMEM; |
| 194458 | } |
| 194459 | |
| 194460 | /* |
| 194461 | ** The output variable *ppExpr is populated with an allocated Fts3Expr |
| 194462 | ** structure, or set to 0 if the end of the input buffer is reached. |
| @@ -208867,11 +209130,13 @@ | |
| 208867 | int rawKey = 1; |
| 208868 | x = pParse->aBlob[iRoot]; |
| 208869 | zPath++; |
| 208870 | if( zPath[0]=='"' ){ |
| 208871 | zKey = zPath + 1; |
| 208872 | for(i=1; zPath[i] && zPath[i]!='"'; i++){} |
| 208873 | nKey = i-1; |
| 208874 | if( zPath[i] ){ |
| 208875 | i++; |
| 208876 | }else{ |
| 208877 | return JSON_LOOKUP_PATHERROR; |
| @@ -217779,11 +218044,11 @@ | |
| 217779 | return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY, |
| 217780 | (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback |
| 217781 | ); |
| 217782 | } |
| 217783 | |
| 217784 | #if !SQLITE_CORE |
| 217785 | #ifdef _WIN32 |
| 217786 | __declspec(dllexport) |
| 217787 | #endif |
| 217788 | SQLITE_API int sqlite3_rtree_init( |
| 217789 | sqlite3 *db, |
| @@ -218370,11 +218635,11 @@ | |
| 218370 | } |
| 218371 | |
| 218372 | return rc; |
| 218373 | } |
| 218374 | |
| 218375 | #if !SQLITE_CORE |
| 218376 | #ifdef _WIN32 |
| 218377 | __declspec(dllexport) |
| 218378 | #endif |
| 218379 | SQLITE_API int sqlite3_icu_init( |
| 218380 | sqlite3 *db, |
| @@ -219628,10 +219893,31 @@ | |
| 219628 | struct RbuFrame { |
| 219629 | u32 iDbPage; |
| 219630 | u32 iWalFrame; |
| 219631 | }; |
| 219632 | |
| 219633 | /* |
| 219634 | ** RBU handle. |
| 219635 | ** |
| 219636 | ** nPhaseOneStep: |
| 219637 | ** If the RBU database contains an rbu_count table, this value is set to |
| @@ -219679,11 +219965,11 @@ | |
| 219679 | char *zState; /* Path to state db (or NULL if zRbu) */ |
| 219680 | char zStateDb[5]; /* Db name for state ("stat" or "main") */ |
| 219681 | int rc; /* Value returned by last rbu_step() call */ |
| 219682 | char *zErrmsg; /* Error message if rc!=SQLITE_OK */ |
| 219683 | int nStep; /* Rows processed for current object */ |
| 219684 | int nProgress; /* Rows processed for all objects */ |
| 219685 | RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ |
| 219686 | const char *zVfsName; /* Name of automatically created rbu vfs */ |
| 219687 | rbu_file *pTargetFd; /* File handle open on target db */ |
| 219688 | int nPagePerSector; /* Pages per sector for pTargetFd */ |
| 219689 | i64 iOalSz; |
| @@ -219796,11 +220082,11 @@ | |
| 219796 | unsigned char *zStart = z; |
| 219797 | while( (c = zValue[0x7f&*(z++)])>=0 ){ |
| 219798 | v = (v<<6) + c; |
| 219799 | } |
| 219800 | z--; |
| 219801 | *pLen -= z - zStart; |
| 219802 | *pz = (char*)z; |
| 219803 | return v; |
| 219804 | } |
| 219805 | |
| 219806 | #if RBU_ENABLE_DELTA_CKSUM |
| @@ -219981,10 +220267,11 @@ | |
| 219981 | int nOut; |
| 219982 | int nOut2; |
| 219983 | char *aOut; |
| 219984 | |
| 219985 | assert( argc==2 ); |
| 219986 | |
| 219987 | nOrig = sqlite3_value_bytes(argv[0]); |
| 219988 | aOrig = (const char*)sqlite3_value_blob(argv[0]); |
| 219989 | nDelta = sqlite3_value_bytes(argv[1]); |
| 219990 | aDelta = (const char*)sqlite3_value_blob(argv[1]); |
| @@ -221560,17 +221847,17 @@ | |
| 221560 | nParen++; |
| 221561 | } |
| 221562 | else if( c==')' ){ |
| 221563 | nParen--; |
| 221564 | if( nParen==0 ){ |
| 221565 | int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; |
| 221566 | pIter->aIdxCol[iIdxCol++].nSpan = nSpan; |
| 221567 | i++; |
| 221568 | break; |
| 221569 | } |
| 221570 | }else if( c==',' && nParen==1 ){ |
| 221571 | int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; |
| 221572 | pIter->aIdxCol[iIdxCol++].nSpan = nSpan; |
| 221573 | pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1]; |
| 221574 | }else if( c=='"' || c=='\'' || c=='`' ){ |
| 221575 | for(i++; 1; i++){ |
| 221576 | if( zSql[i]==c ){ |
| @@ -222256,10 +222543,12 @@ | |
| 222256 | int i, sz; |
| 222257 | sz = (int)strlen(z)&0xffffff; |
| 222258 | for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} |
| 222259 | if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); |
| 222260 | } |
| 222261 | #endif |
| 222262 | } |
| 222263 | |
| 222264 | /* |
| 222265 | ** Return the current wal-index header checksum for the target database |
| @@ -222840,11 +223129,11 @@ | |
| 222840 | "INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES " |
| 222841 | "(%d, %d), " |
| 222842 | "(%d, %Q), " |
| 222843 | "(%d, %Q), " |
| 222844 | "(%d, %d), " |
| 222845 | "(%d, %d), " |
| 222846 | "(%d, %lld), " |
| 222847 | "(%d, %lld), " |
| 222848 | "(%d, %lld), " |
| 222849 | "(%d, %lld), " |
| 222850 | "(%d, %Q) ", |
| @@ -223198,10 +223487,11 @@ | |
| 223198 | char *zErrmsg = 0; |
| 223199 | int rc; |
| 223200 | sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain); |
| 223201 | |
| 223202 | assert( nVal==1 ); |
| 223203 | |
| 223204 | rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, |
| 223205 | sqlite3_mprintf("SELECT count(*) FROM sqlite_schema " |
| 223206 | "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0])) |
| 223207 | ); |
| @@ -223473,11 +223763,11 @@ | |
| 223473 | const char *zTarget, |
| 223474 | const char *zState |
| 223475 | ){ |
| 223476 | if( zTarget==0 ){ return rbuMisuseError(); } |
| 223477 | if( zState ){ |
| 223478 | int n = strlen(zState); |
| 223479 | if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ |
| 223480 | return rbuMisuseError(); |
| 223481 | } |
| 223482 | } |
| 223483 | /* TODO: Check that both arguments are non-NULL */ |
| @@ -223690,10 +223980,11 @@ | |
| 223690 | /* |
| 223691 | ** Default xRename callback for RBU. |
| 223692 | */ |
| 223693 | static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ |
| 223694 | int rc = SQLITE_OK; |
| 223695 | #if defined(_WIN32_WCE) |
| 223696 | { |
| 223697 | LPWSTR zWideOld; |
| 223698 | LPWSTR zWideNew; |
| 223699 | |
| @@ -224594,10 +224885,13 @@ | |
| 224594 | |
| 224595 | /* |
| 224596 | ** No-op. |
| 224597 | */ |
| 224598 | static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){ |
| 224599 | return 0; |
| 224600 | } |
| 224601 | |
| 224602 | /* |
| 224603 | ** Deregister and destroy an RBU vfs created by an earlier call to |
| @@ -225650,11 +225944,17 @@ | |
| 225650 | ** schema for the database file that is to be read. The default schema is |
| 225651 | ** "main". |
| 225652 | ** |
| 225653 | ** The data field of sqlite_dbpage table can be updated. The new |
| 225654 | ** value must be a BLOB which is the correct page size, otherwise the |
| 225655 | ** update fails. Rows may not be deleted or inserted. |
| 225656 | */ |
| 225657 | |
| 225658 | /* #include "sqliteInt.h" ** Requires access to internal data structures ** */ |
| 225659 | #if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ |
| 225660 | && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| @@ -225673,17 +225973,18 @@ | |
| 225673 | }; |
| 225674 | |
| 225675 | struct DbpageTable { |
| 225676 | sqlite3_vtab base; /* Base class. Must be first */ |
| 225677 | sqlite3 *db; /* The database */ |
| 225678 | }; |
| 225679 | |
| 225680 | /* Columns */ |
| 225681 | #define DBPAGE_COLUMN_PGNO 0 |
| 225682 | #define DBPAGE_COLUMN_DATA 1 |
| 225683 | #define DBPAGE_COLUMN_SCHEMA 2 |
| 225684 | |
| 225685 | |
| 225686 | |
| 225687 | /* |
| 225688 | ** Connect to or create a dbpagevfs virtual table. |
| 225689 | */ |
| @@ -225943,15 +226244,15 @@ | |
| 225943 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 225944 | Pgno pgno; |
| 225945 | DbPage *pDbPage = 0; |
| 225946 | int rc = SQLITE_OK; |
| 225947 | char *zErr = 0; |
| 225948 | const char *zSchema; |
| 225949 | int iDb; |
| 225950 | Btree *pBt; |
| 225951 | Pager *pPager; |
| 225952 | int szPage; |
| 225953 | |
| 225954 | (void)pRowid; |
| 225955 | if( pTab->db->flags & SQLITE_Defensive ){ |
| 225956 | zErr = "read-only"; |
| 225957 | goto update_fail; |
| @@ -225958,45 +226259,62 @@ | |
| 225958 | } |
| 225959 | if( argc==1 ){ |
| 225960 | zErr = "cannot delete"; |
| 225961 | goto update_fail; |
| 225962 | } |
| 225963 | pgno = sqlite3_value_int(argv[0]); |
| 225964 | if( sqlite3_value_type(argv[0])==SQLITE_NULL |
| 225965 | || (Pgno)sqlite3_value_int(argv[1])!=pgno |
| 225966 | ){ |
| 225967 | zErr = "cannot insert"; |
| 225968 | goto update_fail; |
| 225969 | } |
| 225970 | zSchema = (const char*)sqlite3_value_text(argv[4]); |
| 225971 | iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1; |
| 225972 | if( NEVER(iDb<0) ){ |
| 225973 | zErr = "no such schema"; |
| 225974 | goto update_fail; |
| 225975 | } |
| 225976 | pBt = pTab->db->aDb[iDb].pBt; |
| 225977 | if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){ |
| 225978 | zErr = "bad page number"; |
| 225979 | goto update_fail; |
| 225980 | } |
| 225981 | szPage = sqlite3BtreeGetPageSize(pBt); |
| 225982 | if( sqlite3_value_type(argv[3])!=SQLITE_BLOB |
| 225983 | || sqlite3_value_bytes(argv[3])!=szPage |
| 225984 | ){ |
| 225985 | zErr = "bad page value"; |
| 225986 | goto update_fail; |
| 225987 | } |
| 225988 | pPager = sqlite3BtreePager(pBt); |
| 225989 | rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); |
| 225990 | if( rc==SQLITE_OK ){ |
| 225991 | const void *pData = sqlite3_value_blob(argv[3]); |
| 225992 | assert( pData!=0 || pTab->db->mallocFailed ); |
| 225993 | if( pData |
| 225994 | && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK |
| 225995 | ){ |
| 225996 | memcpy(sqlite3PagerGetData(pDbPage), pData, szPage); |
| 225997 | } |
| 225998 | } |
| 225999 | sqlite3PagerUnref(pDbPage); |
| 226000 | return rc; |
| 226001 | |
| 226002 | update_fail: |
| @@ -226015,13 +226333,35 @@ | |
| 226015 | int i; |
| 226016 | for(i=0; i<db->nDb; i++){ |
| 226017 | Btree *pBt = db->aDb[i].pBt; |
| 226018 | if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0); |
| 226019 | } |
| 226020 | return SQLITE_OK; |
| 226021 | } |
| 226022 | |
| 226023 | |
| 226024 | /* |
| 226025 | ** Invoke this routine to register the "dbpage" virtual table module |
| 226026 | */ |
| 226027 | SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ |
| @@ -226039,18 +226379,18 @@ | |
| 226039 | dbpageEof, /* xEof - check for end of scan */ |
| 226040 | dbpageColumn, /* xColumn - read data */ |
| 226041 | dbpageRowid, /* xRowid - read data */ |
| 226042 | dbpageUpdate, /* xUpdate */ |
| 226043 | dbpageBegin, /* xBegin */ |
| 226044 | 0, /* xSync */ |
| 226045 | 0, /* xCommit */ |
| 226046 | 0, /* xRollback */ |
| 226047 | 0, /* xFindMethod */ |
| 226048 | 0, /* xRename */ |
| 226049 | 0, /* xSavepoint */ |
| 226050 | 0, /* xRelease */ |
| 226051 | 0, /* xRollbackTo */ |
| 226052 | 0, /* xShadowName */ |
| 226053 | 0 /* xIntegrity */ |
| 226054 | }; |
| 226055 | return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); |
| 226056 | } |
| @@ -226134,10 +226474,14 @@ | |
| 226134 | /* |
| 226135 | ** An object of this type is used internally as an abstraction for |
| 226136 | ** input data. Input data may be supplied either as a single large buffer |
| 226137 | ** (e.g. sqlite3changeset_start()) or using a stream function (e.g. |
| 226138 | ** sqlite3changeset_start_strm()). |
| 226139 | */ |
| 226140 | struct SessionInput { |
| 226141 | int bNoDiscard; /* If true, do not discard in InputBuffer() */ |
| 226142 | int iCurrent; /* Offset in aData[] of current change */ |
| 226143 | int iNext; /* Offset in aData[] of next change */ |
| @@ -227817,20 +228161,23 @@ | |
| 227817 | /* Figure out how large an allocation is required */ |
| 227818 | nByte = sizeof(SessionChange); |
| 227819 | for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ |
| 227820 | sqlite3_value *p = 0; |
| 227821 | if( op!=SQLITE_INSERT ){ |
| 227822 | TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 227823 | assert( trc==SQLITE_OK ); |
| 227824 | }else if( pTab->abPK[i] ){ |
| 227825 | TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); |
| 227826 | assert( trc==SQLITE_OK ); |
| 227827 | } |
| 227828 | |
| 227829 | /* This may fail if SQLite value p contains a utf-16 string that must |
| 227830 | ** be converted to utf-8 and an OOM error occurs while doing so. */ |
| 227831 | rc = sessionSerializeValue(0, p, &nByte); |
| 227832 | if( rc!=SQLITE_OK ) goto error_out; |
| 227833 | } |
| 227834 | if( pTab->bRowid ){ |
| 227835 | nByte += 9; /* Size of rowid field - an integer */ |
| 227836 | } |
| @@ -231184,19 +231531,25 @@ | |
| 231184 | int rc = SQLITE_OK; /* Return code */ |
| 231185 | const char *zTab = 0; /* Name of current table */ |
| 231186 | int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ |
| 231187 | SessionApplyCtx sApply; /* changeset_apply() context object */ |
| 231188 | int bPatchset; |
| 231189 | |
| 231190 | assert( xConflict!=0 ); |
| 231191 | |
| 231192 | pIter->in.bNoDiscard = 1; |
| 231193 | memset(&sApply, 0, sizeof(sApply)); |
| 231194 | sApply.bRebase = (ppRebase && pnRebase); |
| 231195 | sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 231196 | sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); |
| 231197 | sqlite3_mutex_enter(sqlite3_db_mutex(db)); |
| 231198 | if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ |
| 231199 | rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); |
| 231200 | } |
| 231201 | if( rc==SQLITE_OK ){ |
| 231202 | rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0); |
| @@ -231354,10 +231707,16 @@ | |
| 231354 | sqlite3_finalize(sApply.pDelete); |
| 231355 | sqlite3_finalize(sApply.pSelect); |
| 231356 | sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ |
| 231357 | sqlite3_free((char*)sApply.constraints.aBuf); |
| 231358 | sqlite3_free((char*)sApply.rebase.aBuf); |
| 231359 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); |
| 231360 | return rc; |
| 231361 | } |
| 231362 | |
| 231363 | /* |
| @@ -231382,28 +231741,17 @@ | |
| 231382 | int flags |
| 231383 | ){ |
| 231384 | sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ |
| 231385 | int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 231386 | int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); |
| 231387 | u64 savedFlag = db->flags & SQLITE_FkNoAction; |
| 231388 | |
| 231389 | if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ |
| 231390 | db->flags |= ((u64)SQLITE_FkNoAction); |
| 231391 | db->aDb[0].pSchema->schema_cookie -= 32; |
| 231392 | } |
| 231393 | |
| 231394 | if( rc==SQLITE_OK ){ |
| 231395 | rc = sessionChangesetApply( |
| 231396 | db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags |
| 231397 | ); |
| 231398 | } |
| 231399 | |
| 231400 | if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ |
| 231401 | assert( db->flags & SQLITE_FkNoAction ); |
| 231402 | db->flags &= ~((u64)SQLITE_FkNoAction); |
| 231403 | db->aDb[0].pSchema->schema_cookie -= 32; |
| 231404 | } |
| 231405 | return rc; |
| 231406 | } |
| 231407 | |
| 231408 | /* |
| 231409 | ** Apply the changeset passed via pChangeset/nChangeset to the main database |
| @@ -231720,10 +232068,13 @@ | |
| 231720 | if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){ |
| 231721 | /* Append the missing default column values to the record. */ |
| 231722 | sessionAppendBlob(pOut, aRec, nRec, &rc); |
| 231723 | if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ |
| 231724 | rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); |
| 231725 | } |
| 231726 | for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){ |
| 231727 | int eType = sqlite3_column_type(pTab->pDfltStmt, ii); |
| 231728 | sessionAppendByte(pOut, eType, &rc); |
| 231729 | switch( eType ){ |
| @@ -231736,10 +232087,11 @@ | |
| 231736 | double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); |
| 231737 | memcpy(&iVal, &rVal, sizeof(i64)); |
| 231738 | } |
| 231739 | if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ |
| 231740 | sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); |
| 231741 | } |
| 231742 | break; |
| 231743 | } |
| 231744 | |
| 231745 | case SQLITE_BLOB: |
| @@ -231874,10 +232226,12 @@ | |
| 231874 | SessionChange *pExist = 0; |
| 231875 | SessionChange **pp = 0; |
| 231876 | SessionTable *pTab = 0; |
| 231877 | u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; |
| 231878 | int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; |
| 231879 | |
| 231880 | /* Ensure that only changesets, or only patchsets, but not a mixture |
| 231881 | ** of both, are being combined. It is an error to try to combine a |
| 231882 | ** changeset and a patchset. */ |
| 231883 | if( pGrp->pList==0 ){ |
| @@ -231952,10 +232306,11 @@ | |
| 231952 | ){ |
| 231953 | u8 *aRec; |
| 231954 | int nRec; |
| 231955 | int rc = SQLITE_OK; |
| 231956 | |
| 231957 | while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ |
| 231958 | rc = sessionOneChangeToHash(pGrp, pIter, bRebase); |
| 231959 | if( rc!=SQLITE_OK ) break; |
| 231960 | } |
| 231961 | |
| @@ -232583,20 +232938,46 @@ | |
| 232583 | #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 232584 | |
| 232585 | /************** End of sqlite3session.c **************************************/ |
| 232586 | /************** Begin file fts5.c ********************************************/ |
| 232587 | |
| 232588 | |
| 232589 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) |
| 232590 | |
| 232591 | #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) |
| 232592 | # define NDEBUG 1 |
| 232593 | #endif |
| 232594 | #if defined(NDEBUG) && defined(SQLITE_DEBUG) |
| 232595 | # undef NDEBUG |
| 232596 | #endif |
| 232597 | |
| 232598 | /* |
| 232599 | ** 2014 May 31 |
| 232600 | ** |
| 232601 | ** The author disclaims copyright to this source code. In place of |
| 232602 | ** a legal notice, here is a blessing: |
| @@ -232893,17 +233274,32 @@ | |
| 232893 | ** This is used to access token iToken of phrase hit iIdx within the |
| 232894 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 232895 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 232896 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 232897 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 232898 | ** bytes. This API is not available if the specified token matches a |
| 232899 | ** prefix query term. In that case both output variables are always set |
| 232900 | ** to 0. |
| 232901 | ** |
| 232902 | ** The output text is not a copy of the document text that was tokenized. |
| 232903 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 232904 | ** includes any embedded 0x00 and trailing data. |
| 232905 | ** |
| 232906 | ** This API can be quite slow if used with an FTS5 table created with the |
| 232907 | ** "detail=none" or "detail=column" option. |
| 232908 | ** |
| 232909 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -232991,11 +233387,10 @@ | |
| 232991 | ** CUSTOM TOKENIZERS |
| 232992 | ** |
| 232993 | ** Applications may also register custom tokenizer types. A tokenizer |
| 232994 | ** is registered by providing fts5 with a populated instance of the |
| 232995 | ** following structure. All structure methods must be defined, setting |
| 232996 | ** |
| 232997 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 232998 | ** behaviour. The structure methods are expected to function as follows: |
| 232999 | ** |
| 233000 | ** xCreate: |
| 233001 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -233560,10 +233955,11 @@ | |
| 233560 | u8 *abUnindexed; /* True for unindexed columns */ |
| 233561 | int nPrefix; /* Number of prefix indexes */ |
| 233562 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ |
| 233563 | int eContent; /* An FTS5_CONTENT value */ |
| 233564 | int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ |
| 233565 | char *zContent; /* content table */ |
| 233566 | char *zContentRowid; /* "content_rowid=" option value */ |
| 233567 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ |
| 233568 | int bTokendata; /* "tokendata=" option value (dflt==0) */ |
| 233569 | int bLocale; /* "locale=" option value (dflt==0) */ |
| @@ -233582,11 +233978,12 @@ | |
| 233582 | int nUsermerge; /* 'usermerge' setting */ |
| 233583 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 233584 | char *zRank; /* Name of rank function */ |
| 233585 | char *zRankArgs; /* Arguments to rank function */ |
| 233586 | int bSecureDelete; /* 'secure-delete' */ |
| 233587 | int nDeleteMerge; /* 'deletemerge' */ |
| 233588 | |
| 233589 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 233590 | char **pzErrmsg; |
| 233591 | |
| 233592 | #ifdef SQLITE_DEBUG |
| @@ -233598,13 +233995,14 @@ | |
| 233598 | ** the expected version if the 'secure-delete' option has ever been |
| 233599 | ** set on the table. */ |
| 233600 | #define FTS5_CURRENT_VERSION 4 |
| 233601 | #define FTS5_CURRENT_VERSION_SECUREDELETE 5 |
| 233602 | |
| 233603 | #define FTS5_CONTENT_NORMAL 0 |
| 233604 | #define FTS5_CONTENT_NONE 1 |
| 233605 | #define FTS5_CONTENT_EXTERNAL 2 |
| 233606 | |
| 233607 | #define FTS5_DETAIL_FULL 0 |
| 233608 | #define FTS5_DETAIL_NONE 1 |
| 233609 | #define FTS5_DETAIL_COLUMNS 2 |
| 233610 | |
| @@ -233838,11 +234236,18 @@ | |
| 233838 | static int sqlite3Fts5StructureTest(Fts5Index*, void*); |
| 233839 | |
| 233840 | /* |
| 233841 | ** Used by xInstToken(): |
| 233842 | */ |
| 233843 | static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); |
| 233844 | |
| 233845 | /* |
| 233846 | ** Insert or remove data to or from the index. Each time a document is |
| 233847 | ** added to or removed from the index, this function is called one or more |
| 233848 | ** times. |
| @@ -233972,20 +234377,17 @@ | |
| 233972 | |
| 233973 | static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); |
| 233974 | |
| 233975 | static int sqlite3Fts5FlushToDisk(Fts5Table*); |
| 233976 | |
| 233977 | static int sqlite3Fts5ExtractText( |
| 233978 | Fts5Config *pConfig, |
| 233979 | sqlite3_value *pVal, /* Value to extract text from */ |
| 233980 | int bContent, /* Loaded from content table */ |
| 233981 | int *pbResetTokenizer, /* OUT: True if ClearLocale() required */ |
| 233982 | const char **ppText, /* OUT: Pointer to text buffer */ |
| 233983 | int *pnText /* OUT: Size of (*ppText) in bytes */ |
| 233984 | ); |
| 233985 | |
| 233986 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); |
| 233987 | |
| 233988 | /* |
| 233989 | ** End of interface to code in fts5.c. |
| 233990 | **************************************************************************/ |
| 233991 | |
| @@ -234063,11 +234465,11 @@ | |
| 234063 | |
| 234064 | static int sqlite3Fts5DropAll(Fts5Config*); |
| 234065 | static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); |
| 234066 | |
| 234067 | static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); |
| 234068 | static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); |
| 234069 | static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); |
| 234070 | |
| 234071 | static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); |
| 234072 | |
| 234073 | static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); |
| @@ -237270,10 +237672,11 @@ | |
| 237270 | const char *zArg, /* Argument to parse */ |
| 237271 | char **pzErr /* OUT: Error message */ |
| 237272 | ){ |
| 237273 | int rc = SQLITE_OK; |
| 237274 | int nCmd = (int)strlen(zCmd); |
| 237275 | if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ |
| 237276 | const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; |
| 237277 | const char *p; |
| 237278 | int bFirst = 1; |
| 237279 | if( pConfig->aPrefix==0 ){ |
| @@ -237388,10 +237791,20 @@ | |
| 237388 | }else{ |
| 237389 | pConfig->bContentlessDelete = (zArg[0]=='1'); |
| 237390 | } |
| 237391 | return rc; |
| 237392 | } |
| 237393 | |
| 237394 | if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ |
| 237395 | if( pConfig->zContentRowid ){ |
| 237396 | *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); |
| 237397 | rc = SQLITE_ERROR; |
| @@ -237506,11 +237919,12 @@ | |
| 237506 | |
| 237507 | static int fts5ConfigParseColumn( |
| 237508 | Fts5Config *p, |
| 237509 | char *zCol, |
| 237510 | char *zArg, |
| 237511 | char **pzErr |
| 237512 | ){ |
| 237513 | int rc = SQLITE_OK; |
| 237514 | if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) |
| 237515 | || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME) |
| 237516 | ){ |
| @@ -237517,10 +237931,11 @@ | |
| 237517 | *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol); |
| 237518 | rc = SQLITE_ERROR; |
| 237519 | }else if( zArg ){ |
| 237520 | if( 0==sqlite3_stricmp(zArg, "unindexed") ){ |
| 237521 | p->abUnindexed[p->nCol] = 1; |
| 237522 | }else{ |
| 237523 | *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg); |
| 237524 | rc = SQLITE_ERROR; |
| 237525 | } |
| 237526 | } |
| @@ -237537,15 +237952,30 @@ | |
| 237537 | int rc = SQLITE_OK; |
| 237538 | Fts5Buffer buf = {0, 0, 0}; |
| 237539 | |
| 237540 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); |
| 237541 | if( p->eContent!=FTS5_CONTENT_NONE ){ |
| 237542 | for(i=0; i<p->nCol; i++){ |
| 237543 | if( p->eContent==FTS5_CONTENT_EXTERNAL ){ |
| 237544 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); |
| 237545 | }else{ |
| 237546 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); |
| 237547 | } |
| 237548 | } |
| 237549 | } |
| 237550 | |
| 237551 | assert( p->zContentExprlist==0 ); |
| @@ -237575,10 +238005,11 @@ | |
| 237575 | ){ |
| 237576 | int rc = SQLITE_OK; /* Return code */ |
| 237577 | Fts5Config *pRet; /* New object to return */ |
| 237578 | int i; |
| 237579 | sqlite3_int64 nByte; |
| 237580 | |
| 237581 | *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); |
| 237582 | if( pRet==0 ) return SQLITE_NOMEM; |
| 237583 | memset(pRet, 0, sizeof(Fts5Config)); |
| 237584 | pRet->pGlobal = pGlobal; |
| @@ -237634,11 +238065,11 @@ | |
| 237634 | ALWAYS(zOne)?zOne:"", |
| 237635 | zTwo?zTwo:"", |
| 237636 | pzErr |
| 237637 | ); |
| 237638 | }else{ |
| 237639 | rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr); |
| 237640 | zOne = 0; |
| 237641 | } |
| 237642 | } |
| 237643 | } |
| 237644 | |
| @@ -237665,18 +238096,34 @@ | |
| 237665 | *pzErr = sqlite3_mprintf( |
| 237666 | "contentless_delete=1 is incompatible with columnsize=0" |
| 237667 | ); |
| 237668 | rc = SQLITE_ERROR; |
| 237669 | } |
| 237670 | |
| 237671 | /* If no zContent option was specified, fill in the default values. */ |
| 237672 | if( rc==SQLITE_OK && pRet->zContent==0 ){ |
| 237673 | const char *zTail = 0; |
| 237674 | assert( pRet->eContent==FTS5_CONTENT_NORMAL |
| 237675 | || pRet->eContent==FTS5_CONTENT_NONE |
| 237676 | ); |
| 237677 | if( pRet->eContent==FTS5_CONTENT_NORMAL ){ |
| 237678 | zTail = "content"; |
| 237679 | }else if( pRet->bColumnsize ){ |
| 237680 | zTail = "docsize"; |
| 237681 | } |
| 237682 | |
| @@ -238010,10 +238457,23 @@ | |
| 238010 | if( bVal<0 ){ |
| 238011 | *pbBadkey = 1; |
| 238012 | }else{ |
| 238013 | pConfig->bSecureDelete = (bVal ? 1 : 0); |
| 238014 | } |
| 238015 | }else{ |
| 238016 | *pbBadkey = 1; |
| 238017 | } |
| 238018 | return rc; |
| 238019 | } |
| @@ -241145,11 +241605,11 @@ | |
| 241145 | && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 |
| 241146 | ){ |
| 241147 | int rc = sqlite3Fts5PoslistWriterAppend( |
| 241148 | &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff |
| 241149 | ); |
| 241150 | if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){ |
| 241151 | int iCol = p->iOff>>32; |
| 241152 | int iTokOff = p->iOff & 0x7FFFFFFF; |
| 241153 | rc = sqlite3Fts5IndexIterWriteTokendata( |
| 241154 | pT->pIter, pToken, nToken, iRowid, iCol, iTokOff |
| 241155 | ); |
| @@ -241338,19 +241798,18 @@ | |
| 241338 | pPhrase = pExpr->apExprPhrase[iPhrase]; |
| 241339 | if( iToken<0 || iToken>=pPhrase->nTerm ){ |
| 241340 | return SQLITE_RANGE; |
| 241341 | } |
| 241342 | pTerm = &pPhrase->aTerm[iToken]; |
| 241343 | if( pTerm->bPrefix==0 ){ |
| 241344 | if( pExpr->pConfig->bTokendata ){ |
| 241345 | rc = sqlite3Fts5IterToken( |
| 241346 | pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut |
| 241347 | ); |
| 241348 | }else{ |
| 241349 | *ppOut = pTerm->pTerm; |
| 241350 | *pnOut = pTerm->nFullTerm; |
| 241351 | } |
| 241352 | } |
| 241353 | return rc; |
| 241354 | } |
| 241355 | |
| 241356 | /* |
| @@ -246847,10 +247306,15 @@ | |
| 246847 | if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ |
| 246848 | iRet = ii; |
| 246849 | nBest = nPercent; |
| 246850 | } |
| 246851 | } |
| 246852 | } |
| 246853 | } |
| 246854 | return iRet; |
| 246855 | } |
| 246856 | |
| @@ -248156,10 +248620,387 @@ | |
| 248156 | fts5BufferFree(&tmp); |
| 248157 | memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); |
| 248158 | *p1 = out; |
| 248159 | } |
| 248160 | |
| 248161 | static void fts5SetupPrefixIter( |
| 248162 | Fts5Index *p, /* Index to read from */ |
| 248163 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 248164 | int iIdx, /* Index to scan for data */ |
| 248165 | u8 *pToken, /* Buffer containing prefix to match */ |
| @@ -248166,137 +249007,89 @@ | |
| 248166 | int nToken, /* Size of buffer pToken in bytes */ |
| 248167 | Fts5Colset *pColset, /* Restrict matches to these columns */ |
| 248168 | Fts5Iter **ppIter /* OUT: New iterator */ |
| 248169 | ){ |
| 248170 | Fts5Structure *pStruct; |
| 248171 | Fts5Buffer *aBuf; |
| 248172 | int nBuf = 32; |
| 248173 | int nMerge = 1; |
| 248174 | |
| 248175 | void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); |
| 248176 | void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); |
| 248177 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 248178 | xMerge = fts5MergeRowidLists; |
| 248179 | xAppend = fts5AppendRowid; |
| 248180 | }else{ |
| 248181 | nMerge = FTS5_MERGE_NLIST-1; |
| 248182 | nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ |
| 248183 | xMerge = fts5MergePrefixLists; |
| 248184 | xAppend = fts5AppendPoslist; |
| 248185 | } |
| 248186 | |
| 248187 | aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); |
| 248188 | pStruct = fts5StructureRead(p); |
| 248189 | assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); |
| 248190 | |
| 248191 | if( p->rc==SQLITE_OK ){ |
| 248192 | const int flags = FTS5INDEX_QUERY_SCAN |
| 248193 | | FTS5INDEX_QUERY_SKIPEMPTY |
| 248194 | | FTS5INDEX_QUERY_NOOUTPUT; |
| 248195 | int i; |
| 248196 | i64 iLastRowid = 0; |
| 248197 | Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ |
| 248198 | Fts5Data *pData; |
| 248199 | Fts5Buffer doclist; |
| 248200 | int bNewTerm = 1; |
| 248201 | |
| 248202 | memset(&doclist, 0, sizeof(doclist)); |
| 248203 | |
| 248204 | /* If iIdx is non-zero, then it is the number of a prefix-index for |
| 248205 | ** prefixes 1 character longer than the prefix being queried for. That |
| 248206 | ** index contains all the doclists required, except for the one |
| 248207 | ** corresponding to the prefix itself. That one is extracted from the |
| 248208 | ** main term index here. */ |
| 248209 | if( iIdx!=0 ){ |
| 248210 | int dummy = 0; |
| 248211 | const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; |
| 248212 | pToken[0] = FTS5_MAIN_PREFIX; |
| 248213 | fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1); |
| 248214 | fts5IterSetOutputCb(&p->rc, p1); |
| 248215 | for(; |
| 248216 | fts5MultiIterEof(p, p1)==0; |
| 248217 | fts5MultiIterNext2(p, p1, &dummy) |
| 248218 | ){ |
| 248219 | Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; |
| 248220 | p1->xSetOutputs(p1, pSeg); |
| 248221 | if( p1->base.nData ){ |
| 248222 | xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); |
| 248223 | iLastRowid = p1->base.iRowid; |
| 248224 | } |
| 248225 | } |
| 248226 | fts5MultiIterFree(p1); |
| 248227 | } |
| 248228 | |
| 248229 | pToken[0] = FTS5_MAIN_PREFIX + iIdx; |
| 248230 | fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); |
| 248231 | fts5IterSetOutputCb(&p->rc, p1); |
| 248232 | |
| 248233 | for( /* no-op */ ; |
| 248234 | fts5MultiIterEof(p, p1)==0; |
| 248235 | fts5MultiIterNext2(p, p1, &bNewTerm) |
| 248236 | ){ |
| 248237 | Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; |
| 248238 | int nTerm = pSeg->term.n; |
| 248239 | const u8 *pTerm = pSeg->term.p; |
| 248240 | p1->xSetOutputs(p1, pSeg); |
| 248241 | |
| 248242 | assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); |
| 248243 | if( bNewTerm ){ |
| 248244 | if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; |
| 248245 | } |
| 248246 | |
| 248247 | if( p1->base.nData==0 ) continue; |
| 248248 | if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ |
| 248249 | for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ |
| 248250 | int i1 = i*nMerge; |
| 248251 | int iStore; |
| 248252 | assert( i1+nMerge<=nBuf ); |
| 248253 | for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248254 | if( aBuf[iStore].n==0 ){ |
| 248255 | fts5BufferSwap(&doclist, &aBuf[iStore]); |
| 248256 | fts5BufferZero(&doclist); |
| 248257 | break; |
| 248258 | } |
| 248259 | } |
| 248260 | if( iStore==i1+nMerge ){ |
| 248261 | xMerge(p, &doclist, nMerge, &aBuf[i1]); |
| 248262 | for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248263 | fts5BufferZero(&aBuf[iStore]); |
| 248264 | } |
| 248265 | } |
| 248266 | } |
| 248267 | iLastRowid = 0; |
| 248268 | } |
| 248269 | |
| 248270 | xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); |
| 248271 | iLastRowid = p1->base.iRowid; |
| 248272 | } |
| 248273 | |
| 248274 | assert( (nBuf%nMerge)==0 ); |
| 248275 | for(i=0; i<nBuf; i+=nMerge){ |
| 248276 | int iFree; |
| 248277 | if( p->rc==SQLITE_OK ){ |
| 248278 | xMerge(p, &doclist, nMerge, &aBuf[i]); |
| 248279 | } |
| 248280 | for(iFree=i; iFree<i+nMerge; iFree++){ |
| 248281 | fts5BufferFree(&aBuf[iFree]); |
| 248282 | } |
| 248283 | } |
| 248284 | fts5MultiIterFree(p1); |
| 248285 | |
| 248286 | pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING); |
| 248287 | if( pData ){ |
| 248288 | pData->p = (u8*)&pData[1]; |
| 248289 | pData->nn = pData->szLeaf = doclist.n; |
| 248290 | if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); |
| 248291 | fts5MultiIterNew2(p, pData, bDesc, ppIter); |
| 248292 | } |
| 248293 | fts5BufferFree(&doclist); |
| 248294 | } |
| 248295 | |
| 248296 | fts5StructureRelease(pStruct); |
| 248297 | sqlite3_free(aBuf); |
| 248298 | } |
| 248299 | |
| 248300 | |
| 248301 | /* |
| 248302 | ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain |
| @@ -248546,42 +249339,10 @@ | |
| 248546 | static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ |
| 248547 | fts5DataRelease(pSeg->pLeaf); |
| 248548 | pSeg->pLeaf = 0; |
| 248549 | } |
| 248550 | |
| 248551 | /* |
| 248552 | ** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an |
| 248553 | ** array of these for each row it visits. Or, for an iterator used by an |
| 248554 | ** "ORDER BY rank" query, it accumulates an array of these for the entire |
| 248555 | ** query. |
| 248556 | ** |
| 248557 | ** Each instance in the array indicates the iterator (and therefore term) |
| 248558 | ** associated with position iPos of rowid iRowid. This is used by the |
| 248559 | ** xInstToken() API. |
| 248560 | */ |
| 248561 | struct Fts5TokenDataMap { |
| 248562 | i64 iRowid; /* Row this token is located in */ |
| 248563 | i64 iPos; /* Position of token */ |
| 248564 | int iIter; /* Iterator token was read from */ |
| 248565 | }; |
| 248566 | |
| 248567 | /* |
| 248568 | ** An object used to supplement Fts5Iter for tokendata=1 iterators. |
| 248569 | */ |
| 248570 | struct Fts5TokenDataIter { |
| 248571 | int nIter; |
| 248572 | int nIterAlloc; |
| 248573 | |
| 248574 | int nMap; |
| 248575 | int nMapAlloc; |
| 248576 | Fts5TokenDataMap *aMap; |
| 248577 | |
| 248578 | Fts5PoslistReader *aPoslistReader; |
| 248579 | int *aPoslistToIter; |
| 248580 | Fts5Iter *apIter[1]; |
| 248581 | }; |
| 248582 | |
| 248583 | /* |
| 248584 | ** This function appends iterator pAppend to Fts5TokenDataIter pIn and |
| 248585 | ** returns the result. |
| 248586 | */ |
| 248587 | static Fts5TokenDataIter *fts5AppendTokendataIter( |
| @@ -248614,58 +249375,10 @@ | |
| 248614 | assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); |
| 248615 | |
| 248616 | return pRet; |
| 248617 | } |
| 248618 | |
| 248619 | /* |
| 248620 | ** Delete an Fts5TokenDataIter structure and its contents. |
| 248621 | */ |
| 248622 | static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ |
| 248623 | if( pSet ){ |
| 248624 | int ii; |
| 248625 | for(ii=0; ii<pSet->nIter; ii++){ |
| 248626 | fts5MultiIterFree(pSet->apIter[ii]); |
| 248627 | } |
| 248628 | sqlite3_free(pSet->aPoslistReader); |
| 248629 | sqlite3_free(pSet->aMap); |
| 248630 | sqlite3_free(pSet); |
| 248631 | } |
| 248632 | } |
| 248633 | |
| 248634 | /* |
| 248635 | ** Append a mapping to the token-map belonging to object pT. |
| 248636 | */ |
| 248637 | static void fts5TokendataIterAppendMap( |
| 248638 | Fts5Index *p, |
| 248639 | Fts5TokenDataIter *pT, |
| 248640 | int iIter, |
| 248641 | i64 iRowid, |
| 248642 | i64 iPos |
| 248643 | ){ |
| 248644 | if( p->rc==SQLITE_OK ){ |
| 248645 | if( pT->nMap==pT->nMapAlloc ){ |
| 248646 | int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; |
| 248647 | int nByte = nNew * sizeof(Fts5TokenDataMap); |
| 248648 | Fts5TokenDataMap *aNew; |
| 248649 | |
| 248650 | aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte); |
| 248651 | if( aNew==0 ){ |
| 248652 | p->rc = SQLITE_NOMEM; |
| 248653 | return; |
| 248654 | } |
| 248655 | |
| 248656 | pT->aMap = aNew; |
| 248657 | pT->nMapAlloc = nNew; |
| 248658 | } |
| 248659 | |
| 248660 | pT->aMap[pT->nMap].iRowid = iRowid; |
| 248661 | pT->aMap[pT->nMap].iPos = iPos; |
| 248662 | pT->aMap[pT->nMap].iIter = iIter; |
| 248663 | pT->nMap++; |
| 248664 | } |
| 248665 | } |
| 248666 | |
| 248667 | /* |
| 248668 | ** The iterator passed as the only argument must be a tokendata=1 iterator |
| 248669 | ** (pIter->pTokenDataIter!=0). This function sets the iterator output |
| 248670 | ** variables (pIter->base.*) according to the contents of the current |
| 248671 | ** row. |
| @@ -248702,11 +249415,11 @@ | |
| 248702 | int eDetail = pIter->pIndex->pConfig->eDetail; |
| 248703 | pIter->base.bEof = 0; |
| 248704 | pIter->base.iRowid = iRowid; |
| 248705 | |
| 248706 | if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ |
| 248707 | fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1); |
| 248708 | }else |
| 248709 | if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ |
| 248710 | int nReader = 0; |
| 248711 | int nByte = 0; |
| 248712 | i64 iPrev = 0; |
| @@ -248955,10 +249668,11 @@ | |
| 248955 | |
| 248956 | if( p->rc==SQLITE_OK ){ |
| 248957 | pRet = fts5MultiIterAlloc(p, 0); |
| 248958 | } |
| 248959 | if( pRet ){ |
| 248960 | pRet->pTokenDataIter = pSet; |
| 248961 | if( pSet ){ |
| 248962 | fts5IterSetOutputsTokendata(pRet); |
| 248963 | }else{ |
| 248964 | pRet->base.bEof = 1; |
| @@ -248969,11 +249683,10 @@ | |
| 248969 | |
| 248970 | fts5StructureRelease(pStruct); |
| 248971 | fts5BufferFree(&bSeek); |
| 248972 | return pRet; |
| 248973 | } |
| 248974 | |
| 248975 | |
| 248976 | /* |
| 248977 | ** Open a new iterator to iterate though all rowid that match the |
| 248978 | ** specified token or token prefix. |
| 248979 | */ |
| @@ -248995,10 +249708,15 @@ | |
| 248995 | int iIdx = 0; /* Index to search */ |
| 248996 | int iPrefixIdx = 0; /* +1 prefix index */ |
| 248997 | int bTokendata = pConfig->bTokendata; |
| 248998 | if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); |
| 248999 | |
| 249000 | if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ |
| 249001 | bTokendata = 0; |
| 249002 | } |
| 249003 | |
| 249004 | /* Figure out which index to search and set iIdx accordingly. If this |
| @@ -249025,11 +249743,11 @@ | |
| 249025 | if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; |
| 249026 | } |
| 249027 | } |
| 249028 | |
| 249029 | if( bTokendata && iIdx==0 ){ |
| 249030 | buf.p[0] = '0'; |
| 249031 | pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); |
| 249032 | }else if( iIdx<=pConfig->nPrefix ){ |
| 249033 | /* Straight index lookup */ |
| 249034 | Fts5Structure *pStruct = fts5StructureRead(p); |
| 249035 | buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); |
| @@ -249038,11 +249756,11 @@ | |
| 249038 | pColset, buf.p, nToken+1, -1, 0, &pRet |
| 249039 | ); |
| 249040 | fts5StructureRelease(pStruct); |
| 249041 | } |
| 249042 | }else{ |
| 249043 | /* Scan multiple terms in the main index */ |
| 249044 | int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; |
| 249045 | fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); |
| 249046 | if( pRet==0 ){ |
| 249047 | assert( p->rc!=SQLITE_OK ); |
| 249048 | }else{ |
| @@ -249074,11 +249792,12 @@ | |
| 249074 | ** Move to the next matching rowid. |
| 249075 | */ |
| 249076 | static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ |
| 249077 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249078 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 249079 | if( pIter->pTokenDataIter ){ |
| 249080 | fts5TokendataIterNext(pIter, 0, 0); |
| 249081 | }else{ |
| 249082 | fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); |
| 249083 | } |
| 249084 | return fts5IndexReturn(pIter->pIndex); |
| @@ -249111,11 +249830,12 @@ | |
| 249111 | ** definition of "at or after" depends on whether this iterator iterates |
| 249112 | ** in ascending or descending rowid order. |
| 249113 | */ |
| 249114 | static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ |
| 249115 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249116 | if( pIter->pTokenDataIter ){ |
| 249117 | fts5TokendataIterNext(pIter, 1, iMatch); |
| 249118 | }else{ |
| 249119 | fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); |
| 249120 | } |
| 249121 | return fts5IndexReturn(pIter->pIndex); |
| @@ -249129,32 +249849,87 @@ | |
| 249129 | const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); |
| 249130 | assert_nc( z || n<=1 ); |
| 249131 | *pn = n-1; |
| 249132 | return (z ? &z[1] : 0); |
| 249133 | } |
| 249134 | |
| 249135 | /* |
| 249136 | ** This is used by xInstToken() to access the token at offset iOff, column |
| 249137 | ** iCol of row iRowid. The token is returned via output variables *ppOut |
| 249138 | ** and *pnOut. The iterator passed as the first argument must be a tokendata=1 |
| 249139 | ** iterator (pIter->pTokenDataIter!=0). |
| 249140 | */ |
| 249141 | static int sqlite3Fts5IterToken( |
| 249142 | Fts5IndexIter *pIndexIter, |
| 249143 | i64 iRowid, |
| 249144 | int iCol, |
| 249145 | int iOff, |
| 249146 | const char **ppOut, int *pnOut |
| 249147 | ){ |
| 249148 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249149 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249150 | Fts5TokenDataMap *aMap = pT->aMap; |
| 249151 | i64 iPos = (((i64)iCol)<<32) + iOff; |
| 249152 | |
| 249153 | int i1 = 0; |
| 249154 | int i2 = pT->nMap; |
| 249155 | int iTest = 0; |
| 249156 | |
| 249157 | while( i2>i1 ){ |
| 249158 | iTest = (i1 + i2) / 2; |
| 249159 | |
| 249160 | if( aMap[iTest].iRowid<iRowid ){ |
| @@ -249174,13 +249949,19 @@ | |
| 249174 | } |
| 249175 | } |
| 249176 | } |
| 249177 | |
| 249178 | if( i2>i1 ){ |
| 249179 | Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; |
| 249180 | *ppOut = (const char*)pMap->aSeg[0].term.p+1; |
| 249181 | *pnOut = pMap->aSeg[0].term.n-1; |
| 249182 | } |
| 249183 | |
| 249184 | return SQLITE_OK; |
| 249185 | } |
| 249186 | |
| @@ -249188,11 +249969,13 @@ | |
| 249188 | ** Clear any existing entries from the token-map associated with the |
| 249189 | ** iterator passed as the only argument. |
| 249190 | */ |
| 249191 | static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ |
| 249192 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249193 | if( pIter && pIter->pTokenDataIter ){ |
| 249194 | pIter->pTokenDataIter->nMap = 0; |
| 249195 | } |
| 249196 | } |
| 249197 | |
| 249198 | /* |
| @@ -249208,21 +249991,33 @@ | |
| 249208 | i64 iRowid, int iCol, int iOff |
| 249209 | ){ |
| 249210 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249211 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249212 | Fts5Index *p = pIter->pIndex; |
| 249213 | int ii; |
| 249214 | |
| 249215 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 249216 | assert( pIter->pTokenDataIter ); |
| 249217 | |
| 249218 | for(ii=0; ii<pT->nIter; ii++){ |
| 249219 | Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; |
| 249220 | if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; |
| 249221 | } |
| 249222 | if( ii<pT->nIter ){ |
| 249223 | fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); |
| 249224 | } |
| 249225 | return fts5IndexReturn(p); |
| 249226 | } |
| 249227 | |
| 249228 | /* |
| @@ -250771,11 +251566,11 @@ | |
| 250771 | return rc; |
| 250772 | } |
| 250773 | |
| 250774 | /* |
| 250775 | ** We must have a single struct=? constraint that will be passed through |
| 250776 | ** into the xFilter method. If there is no valid stmt=? constraint, |
| 250777 | ** then return an SQLITE_CONSTRAINT error. |
| 250778 | */ |
| 250779 | static int fts5structBestIndexMethod( |
| 250780 | sqlite3_vtab *tab, |
| 250781 | sqlite3_index_info *pIdxInfo |
| @@ -251113,12 +251908,22 @@ | |
| 251113 | i64 iNextId; /* Used to allocate unique cursor ids */ |
| 251114 | Fts5Auxiliary *pAux; /* First in list of all aux. functions */ |
| 251115 | Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ |
| 251116 | Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ |
| 251117 | Fts5Cursor *pCsr; /* First in list of all open cursors */ |
| 251118 | }; |
| 251119 | |
| 251120 | /* |
| 251121 | ** Each auxiliary function registered with the FTS5 module is represented |
| 251122 | ** by an object of the following type. All such objects are stored as part |
| 251123 | ** of the Fts5Global.pAux list. |
| 251124 | */ |
| @@ -251277,16 +252082,10 @@ | |
| 251277 | #define FTS5CSR_REQUIRE_POSLIST 0x40 |
| 251278 | |
| 251279 | #define BitFlagAllTest(x,y) (((x) & (y))==(y)) |
| 251280 | #define BitFlagTest(x,y) (((x) & (y))!=0) |
| 251281 | |
| 251282 | /* |
| 251283 | ** The subtype value and header bytes used by fts5_locale(). |
| 251284 | */ |
| 251285 | #define FTS5_LOCALE_SUBTYPE ((unsigned int)'L') |
| 251286 | #define FTS5_LOCALE_HEADER "\x00\xE0\xB2\xEB" |
| 251287 | |
| 251288 | |
| 251289 | /* |
| 251290 | ** Macros to Set(), Clear() and Test() cursor flags. |
| 251291 | */ |
| 251292 | #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) |
| @@ -251359,14 +252158,20 @@ | |
| 251359 | #else |
| 251360 | # define fts5CheckTransactionState(x,y,z) |
| 251361 | #endif |
| 251362 | |
| 251363 | /* |
| 251364 | ** Return true if pTab is a contentless table. |
| 251365 | */ |
| 251366 | static int fts5IsContentless(Fts5FullTable *pTab){ |
| 251367 | return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; |
| 251368 | } |
| 251369 | |
| 251370 | /* |
| 251371 | ** Delete a virtual table handle allocated by fts5InitVtab(). |
| 251372 | */ |
| @@ -251653,10 +252458,11 @@ | |
| 251653 | ){ |
| 251654 | /* A MATCH operator or equivalent */ |
| 251655 | if( p->usable==0 || iCol<0 ){ |
| 251656 | /* As there exists an unusable MATCH constraint this is an |
| 251657 | ** unusable plan. Return SQLITE_CONSTRAINT. */ |
| 251658 | return SQLITE_CONSTRAINT; |
| 251659 | }else{ |
| 251660 | if( iCol==nCol+1 ){ |
| 251661 | if( bSeenRank ) continue; |
| 251662 | idxStr[iIdxStr++] = 'r'; |
| @@ -252286,11 +253092,11 @@ | |
| 252286 | ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale |
| 252287 | ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain |
| 252288 | ** valid until after the final call to sqlite3Fts5Tokenize() that will use |
| 252289 | ** the locale. |
| 252290 | */ |
| 252291 | static void fts5SetLocale( |
| 252292 | Fts5Config *pConfig, |
| 252293 | const char *zLocale, |
| 252294 | int nLocale |
| 252295 | ){ |
| 252296 | Fts5TokenizerConfig *pT = &pConfig->t; |
| @@ -252297,141 +253103,88 @@ | |
| 252297 | pT->pLocale = zLocale; |
| 252298 | pT->nLocale = nLocale; |
| 252299 | } |
| 252300 | |
| 252301 | /* |
| 252302 | ** Clear any locale configured by an earlier call to fts5SetLocale() or |
| 252303 | ** sqlite3Fts5ExtractText(). |
| 252304 | */ |
| 252305 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ |
| 252306 | fts5SetLocale(pConfig, 0, 0); |
| 252307 | } |
| 252308 | |
| 252309 | /* |
| 252310 | ** This function is used to extract utf-8 text from an sqlite3_value. This |
| 252311 | ** is usually done in order to tokenize it. For example, when: |
| 252312 | ** |
| 252313 | ** * a value is written to an fts5 table, |
| 252314 | ** * a value is deleted from an FTS5_CONTENT_NORMAL table, |
| 252315 | ** * a value containing a query expression is passed to xFilter() |
| 252316 | ** |
| 252317 | ** and so on. |
| 252318 | ** |
| 252319 | ** This function handles 2 cases: |
| 252320 | ** |
| 252321 | ** 1) Ordinary values. The text can be extracted from these using |
| 252322 | ** sqlite3_value_text(). |
| 252323 | ** |
| 252324 | ** 2) Combination text/locale blobs created by fts5_locale(). There |
| 252325 | ** are several cases for these: |
| 252326 | ** |
| 252327 | ** * Blobs tagged with FTS5_LOCALE_SUBTYPE. |
| 252328 | ** * Blobs read from the content table of a locale=1 external-content |
| 252329 | ** table, and |
| 252330 | ** * Blobs read from the content table of a locale=1 regular |
| 252331 | ** content table. |
| 252332 | ** |
| 252333 | ** The first two cases above should have the 4 byte FTS5_LOCALE_HEADER |
| 252334 | ** header. It is an error if a blob with the subtype or a blob read |
| 252335 | ** from the content table of an external content table does not have |
| 252336 | ** the required header. A blob read from the content table of a regular |
| 252337 | ** locale=1 table does not have the header. This is to save space. |
| 252338 | ** |
| 252339 | ** If successful, SQLITE_OK is returned and output parameters (*ppText) |
| 252340 | ** and (*pnText) are set to point to a buffer containing the extracted utf-8 |
| 252341 | ** text and its length in bytes, respectively. The buffer is not |
| 252342 | ** nul-terminated. It has the same lifetime as the sqlite3_value object |
| 252343 | ** from which it is extracted. |
| 252344 | ** |
| 252345 | ** Parameter bContent must be true if the value was read from an indexed |
| 252346 | ** column (i.e. not UNINDEXED) of the on disk content. |
| 252347 | ** |
| 252348 | ** If pbResetTokenizer is not NULL and if case (2) is used, then |
| 252349 | ** fts5SetLocale() is called to ensure subsequent sqlite3Fts5Tokenize() calls |
| 252350 | ** use the locale. In this case (*pbResetTokenizer) is set to true before |
| 252351 | ** returning, to indicate that the caller must call sqlite3Fts5ClearLocale() |
| 252352 | ** to clear the locale after tokenizing the text. |
| 252353 | */ |
| 252354 | static int sqlite3Fts5ExtractText( |
| 252355 | Fts5Config *pConfig, |
| 252356 | sqlite3_value *pVal, /* Value to extract text from */ |
| 252357 | int bContent, /* True if indexed table content */ |
| 252358 | int *pbResetTokenizer, /* OUT: True if xSetLocale(NULL) required */ |
| 252359 | const char **ppText, /* OUT: Pointer to text buffer */ |
| 252360 | int *pnText /* OUT: Size of (*ppText) in bytes */ |
| 252361 | ){ |
| 252362 | const char *pText = 0; |
| 252363 | int nText = 0; |
| 252364 | int rc = SQLITE_OK; |
| 252365 | int bDecodeBlob = 0; |
| 252366 | |
| 252367 | assert( pbResetTokenizer==0 || *pbResetTokenizer==0 ); |
| 252368 | assert( bContent==0 || pConfig->eContent!=FTS5_CONTENT_NONE ); |
| 252369 | assert( bContent==0 || sqlite3_value_subtype(pVal)==0 ); |
| 252370 | |
| 252371 | if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ |
| 252372 | if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE |
| 252373 | || (bContent && pConfig->bLocale) |
| 252374 | ){ |
| 252375 | bDecodeBlob = 1; |
| 252376 | } |
| 252377 | } |
| 252378 | |
| 252379 | if( bDecodeBlob ){ |
| 252380 | const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; |
| 252381 | const u8 *pBlob = sqlite3_value_blob(pVal); |
| 252382 | int nBlob = sqlite3_value_bytes(pVal); |
| 252383 | |
| 252384 | /* Unless this blob was read from the %_content table of an |
| 252385 | ** FTS5_CONTENT_NORMAL table, it should have the 4 byte fts5_locale() |
| 252386 | ** header. Check for this. If it is not found, return an error. */ |
| 252387 | if( (!bContent || pConfig->eContent!=FTS5_CONTENT_NORMAL) ){ |
| 252388 | if( nBlob<SZHDR || memcmp(FTS5_LOCALE_HEADER, pBlob, SZHDR) ){ |
| 252389 | rc = SQLITE_ERROR; |
| 252390 | }else{ |
| 252391 | pBlob += 4; |
| 252392 | nBlob -= 4; |
| 252393 | } |
| 252394 | } |
| 252395 | |
| 252396 | if( rc==SQLITE_OK ){ |
| 252397 | int nLocale = 0; |
| 252398 | |
| 252399 | for(nLocale=0; nLocale<nBlob; nLocale++){ |
| 252400 | if( pBlob[nLocale]==0x00 ) break; |
| 252401 | } |
| 252402 | if( nLocale==nBlob || nLocale==0 ){ |
| 252403 | rc = SQLITE_ERROR; |
| 252404 | }else{ |
| 252405 | pText = (const char*)&pBlob[nLocale+1]; |
| 252406 | nText = nBlob-nLocale-1; |
| 252407 | |
| 252408 | if( pbResetTokenizer ){ |
| 252409 | fts5SetLocale(pConfig, (const char*)pBlob, nLocale); |
| 252410 | *pbResetTokenizer = 1; |
| 252411 | } |
| 252412 | } |
| 252413 | } |
| 252414 | |
| 252415 | }else{ |
| 252416 | pText = (const char*)sqlite3_value_text(pVal); |
| 252417 | nText = sqlite3_value_bytes(pVal); |
| 252418 | } |
| 252419 | |
| 252420 | *ppText = pText; |
| 252421 | *pnText = nText; |
| 252422 | return rc; |
| 252423 | } |
| 252424 | |
| 252425 | /* |
| 252426 | ** Argument pVal is the text of a full-text search expression. It may or |
| 252427 | ** may not have been wrapped by fts5_locale(). This function extracts |
| 252428 | ** the text of the expression, and sets output variable (*pzText) to |
| 252429 | ** point to a nul-terminated buffer containing the expression. |
| 252430 | ** |
| 252431 | ** If pVal was an fts5_locale() value, then fts5SetLocale() is called to |
| 252432 | ** set the tokenizer to use the specified locale. |
| 252433 | ** |
| 252434 | ** If output variable (*pbFreeAndReset) is set to true, then the caller |
| 252435 | ** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer |
| 252436 | ** locale, and (b) call sqlite3_free() to free (*pzText). |
| 252437 | */ |
| @@ -252439,28 +253192,26 @@ | |
| 252439 | Fts5Config *pConfig, /* Fts5 configuration */ |
| 252440 | sqlite3_value *pVal, /* Value to extract expression text from */ |
| 252441 | char **pzText, /* OUT: nul-terminated buffer of text */ |
| 252442 | int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ |
| 252443 | ){ |
| 252444 | const char *zText = 0; |
| 252445 | int nText = 0; |
| 252446 | int rc = SQLITE_OK; |
| 252447 | int bReset = 0; |
| 252448 | |
| 252449 | *pbFreeAndReset = 0; |
| 252450 | rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, &bReset, &zText, &nText); |
| 252451 | if( rc==SQLITE_OK ){ |
| 252452 | if( bReset ){ |
| 252453 | *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, zText); |
| 252454 | if( rc!=SQLITE_OK ){ |
| 252455 | sqlite3Fts5ClearLocale(pConfig); |
| 252456 | }else{ |
| 252457 | *pbFreeAndReset = 1; |
| 252458 | } |
| 252459 | }else{ |
| 252460 | *pzText = (char*)zText; |
| 252461 | } |
| 252462 | } |
| 252463 | |
| 252464 | return rc; |
| 252465 | } |
| 252466 | |
| @@ -252493,10 +253244,11 @@ | |
| 252493 | sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ |
| 252494 | sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ |
| 252495 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ |
| 252496 | int iCol; /* Column on LHS of MATCH operator */ |
| 252497 | char **pzErrmsg = pConfig->pzErrmsg; |
| 252498 | int i; |
| 252499 | int iIdxStr = 0; |
| 252500 | Fts5Expr *pExpr = 0; |
| 252501 | |
| 252502 | assert( pConfig->bLock==0 ); |
| @@ -252528,10 +253280,13 @@ | |
| 252528 | int bInternal = 0; |
| 252529 | |
| 252530 | rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); |
| 252531 | if( rc!=SQLITE_OK ) goto filter_out; |
| 252532 | if( zText==0 ) zText = ""; |
| 252533 | |
| 252534 | iCol = 0; |
| 252535 | do{ |
| 252536 | iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 252537 | iIdxStr++; |
| @@ -252668,10 +253423,11 @@ | |
| 252668 | } |
| 252669 | |
| 252670 | filter_out: |
| 252671 | sqlite3Fts5ExprFree(pExpr); |
| 252672 | pConfig->pzErrmsg = pzErrmsg; |
| 252673 | return rc; |
| 252674 | } |
| 252675 | |
| 252676 | /* |
| 252677 | ** This is the xEof method of the virtual table. SQLite calls this |
| @@ -252808,11 +253564,11 @@ | |
| 252808 | }else{ |
| 252809 | rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); |
| 252810 | } |
| 252811 | bLoadConfig = 1; |
| 252812 | }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ |
| 252813 | if( pConfig->eContent==FTS5_CONTENT_NONE ){ |
| 252814 | fts5SetVtabError(pTab, |
| 252815 | "'rebuild' may not be used with a contentless fts5 table" |
| 252816 | ); |
| 252817 | rc = SQLITE_ERROR; |
| 252818 | }else{ |
| @@ -252877,17 +253633,78 @@ | |
| 252877 | sqlite3_value **apVal, |
| 252878 | i64 *piRowid |
| 252879 | ){ |
| 252880 | int rc = *pRc; |
| 252881 | if( rc==SQLITE_OK ){ |
| 252882 | rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); |
| 252883 | } |
| 252884 | if( rc==SQLITE_OK ){ |
| 252885 | rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); |
| 252886 | } |
| 252887 | *pRc = rc; |
| 252888 | } |
| 252889 | |
| 252890 | /* |
| 252891 | ** This function is the implementation of the xUpdate callback used by |
| 252892 | ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be |
| 252893 | ** inserted, updated or deleted. |
| @@ -252971,48 +253788,38 @@ | |
| 252971 | } |
| 252972 | |
| 252973 | assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); |
| 252974 | assert( nArg!=1 || eType0==SQLITE_INTEGER ); |
| 252975 | |
| 252976 | /* Filter out attempts to run UPDATE or DELETE on contentless tables. |
| 252977 | ** This is not suported. Except - they are both supported if the CREATE |
| 252978 | ** VIRTUAL TABLE statement contained "contentless_delete=1". */ |
| 252979 | if( eType0==SQLITE_INTEGER |
| 252980 | && pConfig->eContent==FTS5_CONTENT_NONE |
| 252981 | && pConfig->bContentlessDelete==0 |
| 252982 | ){ |
| 252983 | pTab->p.base.zErrMsg = sqlite3_mprintf( |
| 252984 | "cannot %s contentless fts5 table: %s", |
| 252985 | (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName |
| 252986 | ); |
| 252987 | rc = SQLITE_ERROR; |
| 252988 | } |
| 252989 | |
| 252990 | /* DELETE */ |
| 252991 | else if( nArg==1 ){ |
| 252992 | i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ |
| 252993 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); |
| 252994 | bUpdateOrDelete = 1; |
| 252995 | } |
| 252996 | |
| 252997 | /* INSERT or UPDATE */ |
| 252998 | else{ |
| 252999 | int eType1 = sqlite3_value_numeric_type(apVal[1]); |
| 253000 | |
| 253001 | /* Ensure that no fts5_locale() values are written to locale=0 tables. |
| 253002 | ** And that no blobs except fts5_locale() blobs are written to indexed |
| 253003 | ** (i.e. not UNINDEXED) columns of locale=1 tables. */ |
| 253004 | int ii; |
| 253005 | for(ii=0; ii<pConfig->nCol; ii++){ |
| 253006 | if( sqlite3_value_type(apVal[ii+2])==SQLITE_BLOB ){ |
| 253007 | int bSub = (sqlite3_value_subtype(apVal[ii+2])==FTS5_LOCALE_SUBTYPE); |
| 253008 | if( (pConfig->bLocale && !bSub && pConfig->abUnindexed[ii]==0) |
| 253009 | || (pConfig->bLocale==0 && bSub) |
| 253010 | ){ |
| 253011 | if( pConfig->bLocale==0 ){ |
| 253012 | fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); |
| 253013 | } |
| 253014 | rc = SQLITE_MISMATCH; |
| 253015 | goto update_out; |
| 253016 | } |
| 253017 | } |
| 253018 | } |
| @@ -253028,39 +253835,59 @@ | |
| 253028 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253029 | } |
| 253030 | |
| 253031 | /* UPDATE */ |
| 253032 | else{ |
| 253033 | i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ |
| 253034 | i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ |
| 253035 | if( eType1!=SQLITE_INTEGER ){ |
| 253036 | rc = SQLITE_MISMATCH; |
| 253037 | }else if( iOld!=iNew ){ |
| 253038 | if( eConflict==SQLITE_REPLACE ){ |
| 253039 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); |
| 253040 | if( rc==SQLITE_OK ){ |
| 253041 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); |
| 253042 | } |
| 253043 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253044 | }else{ |
| 253045 | rc = sqlite3Fts5StorageFindDeleteRow(pTab->pStorage, iOld); |
| 253046 | if( rc==SQLITE_OK ){ |
| 253047 | rc = sqlite3Fts5StorageContentInsert(pTab->pStorage,apVal,pRowid); |
| 253048 | } |
| 253049 | if( rc==SQLITE_OK ){ |
| 253050 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); |
| 253051 | } |
| 253052 | if( rc==SQLITE_OK ){ |
| 253053 | rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); |
| 253054 | } |
| 253055 | } |
| 253056 | }else{ |
| 253057 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); |
| 253058 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253059 | } |
| 253060 | bUpdateOrDelete = 1; |
| 253061 | sqlite3Fts5StorageReleaseDeleteRow(pTab->pStorage); |
| 253062 | } |
| 253063 | |
| 253064 | } |
| 253065 | } |
| 253066 | |
| @@ -253169,15 +253996,15 @@ | |
| 253169 | ){ |
| 253170 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 253171 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| 253172 | int rc = SQLITE_OK; |
| 253173 | |
| 253174 | fts5SetLocale(pTab->pConfig, pLoc, nLoc); |
| 253175 | rc = sqlite3Fts5Tokenize(pTab->pConfig, |
| 253176 | FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken |
| 253177 | ); |
| 253178 | fts5SetLocale(pTab->pConfig, 0, 0); |
| 253179 | |
| 253180 | return rc; |
| 253181 | } |
| 253182 | |
| 253183 | /* |
| @@ -253200,10 +254027,53 @@ | |
| 253200 | |
| 253201 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ |
| 253202 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 253203 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); |
| 253204 | } |
| 253205 | |
| 253206 | static int fts5ApiColumnText( |
| 253207 | Fts5Context *pCtx, |
| 253208 | int iCol, |
| 253209 | const char **pz, |
| @@ -253214,20 +254084,18 @@ | |
| 253214 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| 253215 | |
| 253216 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); |
| 253217 | if( iCol<0 || iCol>=pTab->pConfig->nCol ){ |
| 253218 | rc = SQLITE_RANGE; |
| 253219 | }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) ){ |
| 253220 | *pz = 0; |
| 253221 | *pn = 0; |
| 253222 | }else{ |
| 253223 | rc = fts5SeekCursor(pCsr, 0); |
| 253224 | if( rc==SQLITE_OK ){ |
| 253225 | Fts5Config *pConfig = pTab->pConfig; |
| 253226 | int bContent = (pConfig->abUnindexed[iCol]==0); |
| 253227 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); |
| 253228 | sqlite3Fts5ExtractText(pConfig, pVal, bContent, 0, pz, pn); |
| 253229 | } |
| 253230 | } |
| 253231 | return rc; |
| 253232 | } |
| 253233 | |
| @@ -253249,11 +254117,11 @@ | |
| 253249 | int bLive = (pCsr->pSorter==0); |
| 253250 | |
| 253251 | if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ |
| 253252 | rc = SQLITE_RANGE; |
| 253253 | }else if( pConfig->eDetail!=FTS5_DETAIL_FULL |
| 253254 | && pConfig->eContent==FTS5_CONTENT_NONE |
| 253255 | ){ |
| 253256 | *pa = 0; |
| 253257 | *pn = 0; |
| 253258 | return SQLITE_OK; |
| 253259 | }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ |
| @@ -253265,21 +254133,19 @@ | |
| 253265 | if( aPopulator==0 ) rc = SQLITE_NOMEM; |
| 253266 | if( rc==SQLITE_OK ){ |
| 253267 | rc = fts5SeekCursor(pCsr, 0); |
| 253268 | } |
| 253269 | for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ |
| 253270 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); |
| 253271 | const char *z = 0; |
| 253272 | int n = 0; |
| 253273 | int bReset = 0; |
| 253274 | rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); |
| 253275 | if( rc==SQLITE_OK ){ |
| 253276 | rc = sqlite3Fts5ExprPopulatePoslists( |
| 253277 | pConfig, pCsr->pExpr, aPopulator, i, z, n |
| 253278 | ); |
| 253279 | } |
| 253280 | if( bReset ) sqlite3Fts5ClearLocale(pConfig); |
| 253281 | } |
| 253282 | sqlite3_free(aPopulator); |
| 253283 | |
| 253284 | if( pCsr->pSorter ){ |
| 253285 | sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); |
| @@ -253447,11 +254313,11 @@ | |
| 253447 | |
| 253448 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ |
| 253449 | if( pConfig->bColumnsize ){ |
| 253450 | i64 iRowid = fts5CursorRowid(pCsr); |
| 253451 | rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); |
| 253452 | }else if( pConfig->zContent==0 ){ |
| 253453 | int i; |
| 253454 | for(i=0; i<pConfig->nCol; i++){ |
| 253455 | if( pConfig->abUnindexed[i]==0 ){ |
| 253456 | pCsr->aColumnSize[i] = -1; |
| 253457 | } |
| @@ -253461,21 +254327,18 @@ | |
| 253461 | rc = fts5SeekCursor(pCsr, 0); |
| 253462 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 253463 | if( pConfig->abUnindexed[i]==0 ){ |
| 253464 | const char *z = 0; |
| 253465 | int n = 0; |
| 253466 | int bReset = 0; |
| 253467 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); |
| 253468 | |
| 253469 | pCsr->aColumnSize[i] = 0; |
| 253470 | rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); |
| 253471 | if( rc==SQLITE_OK ){ |
| 253472 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, |
| 253473 | z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb |
| 253474 | ); |
| 253475 | if( bReset ) sqlite3Fts5ClearLocale(pConfig); |
| 253476 | } |
| 253477 | } |
| 253478 | } |
| 253479 | } |
| 253480 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE); |
| 253481 | } |
| @@ -253738,46 +254601,23 @@ | |
| 253738 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); |
| 253739 | if( iCol<0 || iCol>=pConfig->nCol ){ |
| 253740 | rc = SQLITE_RANGE; |
| 253741 | }else if( |
| 253742 | pConfig->abUnindexed[iCol]==0 |
| 253743 | && pConfig->eContent!=FTS5_CONTENT_NONE |
| 253744 | && pConfig->bLocale |
| 253745 | ){ |
| 253746 | rc = fts5SeekCursor(pCsr, 0); |
| 253747 | if( rc==SQLITE_OK ){ |
| 253748 | /* Load the value into pVal. pVal is a locale/text pair iff: |
| 253749 | ** |
| 253750 | ** 1) It is an SQLITE_BLOB, and |
| 253751 | ** 2) Either the subtype is FTS5_LOCALE_SUBTYPE, or else the |
| 253752 | ** value was loaded from an FTS5_CONTENT_NORMAL table, and |
| 253753 | ** 3) It does not begin with an 0x00 byte. |
| 253754 | */ |
| 253755 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); |
| 253756 | if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ |
| 253757 | const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); |
| 253758 | int nBlob = sqlite3_value_bytes(pVal); |
| 253759 | if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ |
| 253760 | const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; |
| 253761 | if( nBlob<SZHDR || memcmp(FTS5_LOCALE_HEADER, pBlob, SZHDR) ){ |
| 253762 | rc = SQLITE_ERROR; |
| 253763 | } |
| 253764 | pBlob += 4; |
| 253765 | nBlob -= 4; |
| 253766 | } |
| 253767 | if( rc==SQLITE_OK ){ |
| 253768 | int nLocale = 0; |
| 253769 | for(nLocale=0; nLocale<nBlob && pBlob[nLocale]!=0x00; nLocale++); |
| 253770 | if( nLocale==nBlob || nLocale==0 ){ |
| 253771 | rc = SQLITE_ERROR; |
| 253772 | }else{ |
| 253773 | /* A locale/text pair */ |
| 253774 | *pzLocale = (const char*)pBlob; |
| 253775 | *pnLocale = nLocale; |
| 253776 | } |
| 253777 | } |
| 253778 | } |
| 253779 | } |
| 253780 | } |
| 253781 | |
| 253782 | return rc; |
| 253783 | } |
| @@ -253994,61 +254834,10 @@ | |
| 253994 | |
| 253995 | sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); |
| 253996 | return rc; |
| 253997 | } |
| 253998 | |
| 253999 | /* |
| 254000 | ** Value pVal was read from column iCol of the FTS5 table. This function |
| 254001 | ** returns it to the owner of pCtx via a call to an sqlite3_result_xxx() |
| 254002 | ** function. This function deals with the same cases as |
| 254003 | ** sqlite3Fts5ExtractText(): |
| 254004 | ** |
| 254005 | ** 1) Ordinary values. These can be returned using sqlite3_result_value(). |
| 254006 | ** |
| 254007 | ** 2) Blobs from fts5_locale(). The text is extracted from these and |
| 254008 | ** returned via sqlite3_result_text(). The locale is discarded. |
| 254009 | */ |
| 254010 | static void fts5ExtractValueFromColumn( |
| 254011 | sqlite3_context *pCtx, |
| 254012 | Fts5Config *pConfig, |
| 254013 | int iCol, |
| 254014 | sqlite3_value *pVal |
| 254015 | ){ |
| 254016 | assert( pConfig->eContent!=FTS5_CONTENT_NONE ); |
| 254017 | |
| 254018 | if( pConfig->bLocale |
| 254019 | && sqlite3_value_type(pVal)==SQLITE_BLOB |
| 254020 | && pConfig->abUnindexed[iCol]==0 |
| 254021 | ){ |
| 254022 | const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; |
| 254023 | const u8 *pBlob = sqlite3_value_blob(pVal); |
| 254024 | int nBlob = sqlite3_value_bytes(pVal); |
| 254025 | int ii; |
| 254026 | |
| 254027 | if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ |
| 254028 | if( nBlob<SZHDR || memcmp(pBlob, FTS5_LOCALE_HEADER, SZHDR) ){ |
| 254029 | sqlite3_result_error_code(pCtx, SQLITE_ERROR); |
| 254030 | return; |
| 254031 | }else{ |
| 254032 | pBlob += 4; |
| 254033 | nBlob -= 4; |
| 254034 | } |
| 254035 | } |
| 254036 | |
| 254037 | for(ii=0; ii<nBlob && pBlob[ii]; ii++); |
| 254038 | if( ii==0 || ii==nBlob ){ |
| 254039 | sqlite3_result_error_code(pCtx, SQLITE_ERROR); |
| 254040 | }else{ |
| 254041 | const char *pText = (const char*)&pBlob[ii+1]; |
| 254042 | sqlite3_result_text(pCtx, pText, nBlob-ii-1, SQLITE_TRANSIENT); |
| 254043 | } |
| 254044 | return; |
| 254045 | } |
| 254046 | |
| 254047 | sqlite3_result_value(pCtx, pVal); |
| 254048 | } |
| 254049 | |
| 254050 | /* |
| 254051 | ** This is the xColumn method, called by SQLite to request a value from |
| 254052 | ** the row that the supplied cursor currently points to. |
| 254053 | */ |
| 254054 | static int fts5ColumnMethod( |
| @@ -254087,26 +254876,31 @@ | |
| 254087 | if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){ |
| 254088 | fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); |
| 254089 | } |
| 254090 | } |
| 254091 | }else{ |
| 254092 | /* A column created by the user containing values. */ |
| 254093 | int bNochange = sqlite3_vtab_nochange(pCtx); |
| 254094 | |
| 254095 | if( fts5IsContentless(pTab) ){ |
| 254096 | if( bNochange && pConfig->bContentlessDelete ){ |
| 254097 | fts5ResultError(pCtx, "cannot UPDATE a subset of " |
| 254098 | "columns on fts5 contentless-delete table: %s", pConfig->zName |
| 254099 | ); |
| 254100 | } |
| 254101 | }else if( bNochange==0 || pConfig->eContent!=FTS5_CONTENT_NORMAL ){ |
| 254102 | pConfig->pzErrmsg = &pTab->p.base.zErrMsg; |
| 254103 | rc = fts5SeekCursor(pCsr, 1); |
| 254104 | if( rc==SQLITE_OK ){ |
| 254105 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); |
| 254106 | fts5ExtractValueFromColumn(pCtx, pConfig, iCol, pVal); |
| 254107 | } |
| 254108 | pConfig->pzErrmsg = 0; |
| 254109 | } |
| 254110 | } |
| 254111 | |
| 254112 | return rc; |
| @@ -254625,11 +255419,11 @@ | |
| 254625 | int nArg, /* Number of args */ |
| 254626 | sqlite3_value **apUnused /* Function arguments */ |
| 254627 | ){ |
| 254628 | assert( nArg==0 ); |
| 254629 | UNUSED_PARAM2(nArg, apUnused); |
| 254630 | sqlite3_result_text(pCtx, "fts5: 2024-09-02 18:41:59 e6bec37ea1ca51e1d048941ce4c5211d8fc5c5e3556a1441f9c79b036843f9e3", -1, SQLITE_TRANSIENT); |
| 254631 | } |
| 254632 | |
| 254633 | /* |
| 254634 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 254635 | ** |
| @@ -254664,36 +255458,48 @@ | |
| 254664 | nText = sqlite3_value_bytes(apArg[1]); |
| 254665 | |
| 254666 | if( zLocale==0 || zLocale[0]=='\0' ){ |
| 254667 | sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); |
| 254668 | }else{ |
| 254669 | u8 *pBlob = 0; |
| 254670 | u8 *pCsr = 0; |
| 254671 | int nBlob = 0; |
| 254672 | const int nHdr = 4; |
| 254673 | assert( sizeof(FTS5_LOCALE_HEADER)==nHdr+1 ); |
| 254674 | |
| 254675 | nBlob = nHdr + nLocale + 1 + nText; |
| 254676 | pBlob = (u8*)sqlite3_malloc(nBlob); |
| 254677 | if( pBlob==0 ){ |
| 254678 | sqlite3_result_error_nomem(pCtx); |
| 254679 | return; |
| 254680 | } |
| 254681 | |
| 254682 | pCsr = pBlob; |
| 254683 | memcpy(pCsr, FTS5_LOCALE_HEADER, nHdr); |
| 254684 | pCsr += nHdr; |
| 254685 | memcpy(pCsr, zLocale, nLocale); |
| 254686 | pCsr += nLocale; |
| 254687 | (*pCsr++) = 0x00; |
| 254688 | if( zText ) memcpy(pCsr, zText, nText); |
| 254689 | assert( &pCsr[nText]==&pBlob[nBlob] ); |
| 254690 | |
| 254691 | sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); |
| 254692 | sqlite3_result_subtype(pCtx, FTS5_LOCALE_SUBTYPE); |
| 254693 | } |
| 254694 | } |
| 254695 | |
| 254696 | /* |
| 254697 | ** Return true if zName is the extension on one of the shadow tables used |
| 254698 | ** by this module. |
| 254699 | */ |
| @@ -254789,10 +255595,20 @@ | |
| 254789 | pGlobal->api.xCreateFunction = fts5CreateAux; |
| 254790 | pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; |
| 254791 | pGlobal->api.xFindTokenizer = fts5FindTokenizer; |
| 254792 | pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; |
| 254793 | pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; |
| 254794 | rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); |
| 254795 | if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); |
| 254796 | if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); |
| 254797 | if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); |
| 254798 | if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); |
| @@ -254810,13 +255626,20 @@ | |
| 254810 | ); |
| 254811 | } |
| 254812 | if( rc==SQLITE_OK ){ |
| 254813 | rc = sqlite3_create_function( |
| 254814 | db, "fts5_locale", 2, |
| 254815 | SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, |
| 254816 | p, fts5LocaleFunc, 0, 0 |
| 254817 | ); |
| 254818 | } |
| 254819 | } |
| 254820 | |
| 254821 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 254822 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| @@ -255015,24 +255838,39 @@ | |
| 255015 | ); |
| 255016 | break; |
| 255017 | |
| 255018 | case FTS5_STMT_INSERT_CONTENT: |
| 255019 | case FTS5_STMT_REPLACE_CONTENT: { |
| 255020 | int nCol = pC->nCol + 1; |
| 255021 | char *zBind; |
| 255022 | int i; |
| 255023 | |
| 255024 | zBind = sqlite3_malloc64(1 + nCol*2); |
| 255025 | if( zBind ){ |
| 255026 | for(i=0; i<nCol; i++){ |
| 255027 | zBind[i*2] = '?'; |
| 255028 | zBind[i*2 + 1] = ','; |
| 255029 | } |
| 255030 | zBind[i*2-1] = '\0'; |
| 255031 | zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind); |
| 255032 | sqlite3_free(zBind); |
| 255033 | } |
| 255034 | break; |
| 255035 | } |
| 255036 | |
| 255037 | case FTS5_STMT_REPLACE_DOCSIZE: |
| 255038 | zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, |
| @@ -255214,23 +256052,37 @@ | |
| 255214 | p->aTotalSize = (i64*)&p[1]; |
| 255215 | p->pConfig = pConfig; |
| 255216 | p->pIndex = pIndex; |
| 255217 | |
| 255218 | if( bCreate ){ |
| 255219 | if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ |
| 255220 | int nDefn = 32 + pConfig->nCol*10; |
| 255221 | char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); |
| 255222 | if( zDefn==0 ){ |
| 255223 | rc = SQLITE_NOMEM; |
| 255224 | }else{ |
| 255225 | int i; |
| 255226 | int iOff; |
| 255227 | sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); |
| 255228 | iOff = (int)strlen(zDefn); |
| 255229 | for(i=0; i<pConfig->nCol; i++){ |
| 255230 | sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); |
| 255231 | iOff += (int)strlen(&zDefn[iOff]); |
| 255232 | } |
| 255233 | rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); |
| 255234 | } |
| 255235 | sqlite3_free(zDefn); |
| 255236 | } |
| @@ -255379,33 +256231,43 @@ | |
| 255379 | for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ |
| 255380 | if( pConfig->abUnindexed[iCol-1]==0 ){ |
| 255381 | sqlite3_value *pVal = 0; |
| 255382 | const char *pText = 0; |
| 255383 | int nText = 0; |
| 255384 | int bReset = 0; |
| 255385 | |
| 255386 | assert( pSeek==0 || apVal==0 ); |
| 255387 | assert( pSeek!=0 || apVal!=0 ); |
| 255388 | if( pSeek ){ |
| 255389 | pVal = sqlite3_column_value(pSeek, iCol); |
| 255390 | }else{ |
| 255391 | pVal = apVal[iCol-1]; |
| 255392 | } |
| 255393 | |
| 255394 | rc = sqlite3Fts5ExtractText( |
| 255395 | pConfig, pVal, pSeek!=0, &bReset, &pText, &nText |
| 255396 | ); |
| 255397 | if( rc==SQLITE_OK ){ |
| 255398 | ctx.szCol = 0; |
| 255399 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, |
| 255400 | pText, nText, (void*)&ctx, fts5StorageInsertCallback |
| 255401 | ); |
| 255402 | p->aTotalSize[iCol-1] -= (i64)ctx.szCol; |
| 255403 | if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ |
| 255404 | rc = FTS5_CORRUPT; |
| 255405 | } |
| 255406 | if( bReset ) sqlite3Fts5ClearLocale(pConfig); |
| 255407 | } |
| 255408 | } |
| 255409 | } |
| 255410 | if( rc==SQLITE_OK && p->nTotalRow<1 ){ |
| 255411 | rc = FTS5_CORRUPT; |
| @@ -255446,11 +256308,13 @@ | |
| 255446 | i64 iOrigin = 0; |
| 255447 | sqlite3_stmt *pLookup = 0; |
| 255448 | int rc = SQLITE_OK; |
| 255449 | |
| 255450 | assert( p->pConfig->bContentlessDelete ); |
| 255451 | assert( p->pConfig->eContent==FTS5_CONTENT_NONE ); |
| 255452 | |
| 255453 | /* Look up the origin of the document in the %_docsize table. Store |
| 255454 | ** this in stack variable iOrigin. */ |
| 255455 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); |
| 255456 | if( rc==SQLITE_OK ){ |
| @@ -255570,10 +256434,16 @@ | |
| 255570 | } |
| 255571 | |
| 255572 | if( rc==SQLITE_OK ){ |
| 255573 | if( p->pConfig->bContentlessDelete ){ |
| 255574 | rc = fts5StorageContentlessDelete(p, iDel); |
| 255575 | }else{ |
| 255576 | rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); |
| 255577 | } |
| 255578 | } |
| 255579 | |
| @@ -255586,11 +256456,13 @@ | |
| 255586 | rc = sqlite3_reset(pDel); |
| 255587 | } |
| 255588 | } |
| 255589 | |
| 255590 | /* Delete the %_content record */ |
| 255591 | if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ |
| 255592 | if( rc==SQLITE_OK ){ |
| 255593 | rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); |
| 255594 | } |
| 255595 | if( rc==SQLITE_OK ){ |
| 255596 | sqlite3_bind_int64(pDel, 1, iDel); |
| @@ -255618,12 +256490,17 @@ | |
| 255618 | pConfig->zDb, pConfig->zName, |
| 255619 | pConfig->zDb, pConfig->zName |
| 255620 | ); |
| 255621 | if( rc==SQLITE_OK && pConfig->bColumnsize ){ |
| 255622 | rc = fts5ExecPrintf(pConfig->db, 0, |
| 255623 | "DELETE FROM %Q.'%q_docsize';", |
| 255624 | pConfig->zDb, pConfig->zName |
| 255625 | ); |
| 255626 | } |
| 255627 | |
| 255628 | /* Reinitialize the %_data table. This call creates the initial structure |
| 255629 | ** and averages records. */ |
| @@ -255660,24 +256537,39 @@ | |
| 255660 | sqlite3Fts5BufferZero(&buf); |
| 255661 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
| 255662 | for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ |
| 255663 | ctx.szCol = 0; |
| 255664 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ |
| 255665 | int bReset = 0; /* True if tokenizer locale must be reset */ |
| 255666 | int nText = 0; /* Size of pText in bytes */ |
| 255667 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 255668 | sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); |
| 255669 | |
| 255670 | rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &pText, &nText); |
| 255671 | if( rc==SQLITE_OK ){ |
| 255672 | rc = sqlite3Fts5Tokenize(pConfig, |
| 255673 | FTS5_TOKENIZE_DOCUMENT, |
| 255674 | pText, nText, |
| 255675 | (void*)&ctx, |
| 255676 | fts5StorageInsertCallback |
| 255677 | ); |
| 255678 | if( bReset ) sqlite3Fts5ClearLocale(pConfig); |
| 255679 | } |
| 255680 | } |
| 255681 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); |
| 255682 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; |
| 255683 | } |
| @@ -255740,53 +256632,75 @@ | |
| 255740 | /* |
| 255741 | ** Insert a new row into the FTS content table. |
| 255742 | */ |
| 255743 | static int sqlite3Fts5StorageContentInsert( |
| 255744 | Fts5Storage *p, |
| 255745 | sqlite3_value **apVal, |
| 255746 | i64 *piRowid |
| 255747 | ){ |
| 255748 | Fts5Config *pConfig = p->pConfig; |
| 255749 | int rc = SQLITE_OK; |
| 255750 | |
| 255751 | /* Insert the new row into the %_content table. */ |
| 255752 | if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ |
| 255753 | if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ |
| 255754 | *piRowid = sqlite3_value_int64(apVal[1]); |
| 255755 | }else{ |
| 255756 | rc = fts5StorageNewRowid(p, piRowid); |
| 255757 | } |
| 255758 | }else{ |
| 255759 | sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ |
| 255760 | int i; /* Counter variable */ |
| 255761 | rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); |
| 255762 | for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ |
| 255763 | sqlite3_value *pVal = apVal[i]; |
| 255764 | if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ |
| 255765 | /* This is an UPDATE statement, and column (i-2) was not modified. |
| 255766 | ** Retrieve the value from Fts5Storage.pSavedRow instead. */ |
| 255767 | pVal = sqlite3_column_value(p->pSavedRow, i-1); |
| 255768 | }else if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE ){ |
| 255769 | assert( pConfig->bLocale ); |
| 255770 | assert( i>1 ); |
| 255771 | if( pConfig->abUnindexed[i-2] ){ |
| 255772 | /* At attempt to insert an fts5_locale() value into an UNINDEXED |
| 255773 | ** column. Strip the locale away and just bind the text. */ |
| 255774 | const char *pText = 0; |
| 255775 | int nText = 0; |
| 255776 | rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, 0, &pText, &nText); |
| 255777 | sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); |
| 255778 | }else{ |
| 255779 | const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); |
| 255780 | int nBlob = sqlite3_value_bytes(pVal); |
| 255781 | assert( nBlob>4 ); |
| 255782 | sqlite3_bind_blob(pInsert, i, pBlob+4, nBlob-4, SQLITE_TRANSIENT); |
| 255783 | } |
| 255784 | continue; |
| 255785 | } |
| 255786 | |
| 255787 | rc = sqlite3_bind_value(pInsert, i, pVal); |
| 255788 | } |
| 255789 | if( rc==SQLITE_OK ){ |
| 255790 | sqlite3_step(pInsert); |
| 255791 | rc = sqlite3_reset(pInsert); |
| 255792 | } |
| @@ -255817,27 +256731,41 @@ | |
| 255817 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
| 255818 | } |
| 255819 | for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ |
| 255820 | ctx.szCol = 0; |
| 255821 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ |
| 255822 | int bReset = 0; /* True if tokenizer locale must be reset */ |
| 255823 | int nText = 0; /* Size of pText in bytes */ |
| 255824 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 255825 | sqlite3_value *pVal = apVal[ctx.iCol+2]; |
| 255826 | int bDisk = 0; |
| 255827 | if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ |
| 255828 | pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); |
| 255829 | bDisk = 1; |
| 255830 | } |
| 255831 | rc = sqlite3Fts5ExtractText(pConfig, pVal, bDisk, &bReset, &pText,&nText); |
| 255832 | if( rc==SQLITE_OK ){ |
| 255833 | assert( bReset==0 || pConfig->bLocale ); |
| 255834 | rc = sqlite3Fts5Tokenize(pConfig, |
| 255835 | FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, |
| 255836 | fts5StorageInsertCallback |
| 255837 | ); |
| 255838 | if( bReset ) sqlite3Fts5ClearLocale(pConfig); |
| 255839 | } |
| 255840 | } |
| 255841 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); |
| 255842 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; |
| 255843 | } |
| @@ -255998,41 +256926,65 @@ | |
| 255998 | } |
| 255999 | if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 256000 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 256001 | } |
| 256002 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 256003 | if( pConfig->abUnindexed[i] ) continue; |
| 256004 | ctx.iCol = i; |
| 256005 | ctx.szCol = 0; |
| 256006 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 256007 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 256008 | } |
| 256009 | if( rc==SQLITE_OK ){ |
| 256010 | int bReset = 0; /* True if tokenizer locale must be reset */ |
| 256011 | int nText = 0; /* Size of pText in bytes */ |
| 256012 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 256013 | |
| 256014 | rc = sqlite3Fts5ExtractText(pConfig, |
| 256015 | sqlite3_column_value(pScan, i+1), 1, &bReset, &pText, &nText |
| 256016 | ); |
| 256017 | if( rc==SQLITE_OK ){ |
| 256018 | rc = sqlite3Fts5Tokenize(pConfig, |
| 256019 | FTS5_TOKENIZE_DOCUMENT, |
| 256020 | pText, nText, |
| 256021 | (void*)&ctx, |
| 256022 | fts5StorageIntegrityCallback |
| 256023 | ); |
| 256024 | if( bReset ) sqlite3Fts5ClearLocale(pConfig); |
| 256025 | } |
| 256026 | } |
| 256027 | if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ |
| 256028 | rc = FTS5_CORRUPT; |
| 256029 | } |
| 256030 | aTotalSize[i] += ctx.szCol; |
| 256031 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 256032 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 256033 | ctx.pTermset = 0; |
| 256034 | } |
| 256035 | } |
| 256036 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 256037 | ctx.pTermset = 0; |
| 256038 | |
| @@ -256454,11 +257406,11 @@ | |
| 256454 | |
| 256455 | #define READ_UTF8(zIn, zTerm, c) \ |
| 256456 | c = *(zIn++); \ |
| 256457 | if( c>=0xc0 ){ \ |
| 256458 | c = sqlite3Utf8Trans1[c-0xc0]; \ |
| 256459 | while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ |
| 256460 | c = (c<<6) + (0x3f & *(zIn++)); \ |
| 256461 | } \ |
| 256462 | if( c<0x80 \ |
| 256463 | || (c&0xFFFFF800)==0xD800 \ |
| 256464 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| @@ -257609,22 +258561,22 @@ | |
| 257609 | int rc = SQLITE_OK; |
| 257610 | char aBuf[32]; |
| 257611 | char *zOut = aBuf; |
| 257612 | int ii; |
| 257613 | const unsigned char *zIn = (const unsigned char*)pText; |
| 257614 | const unsigned char *zEof = &zIn[nText]; |
| 257615 | u32 iCode; |
| 257616 | int aStart[3]; /* Input offset of each character in aBuf[] */ |
| 257617 | |
| 257618 | UNUSED_PARAM(unusedFlags); |
| 257619 | |
| 257620 | /* Populate aBuf[] with the characters for the first trigram. */ |
| 257621 | for(ii=0; ii<3; ii++){ |
| 257622 | do { |
| 257623 | aStart[ii] = zIn - (const unsigned char*)pText; |
| 257624 | READ_UTF8(zIn, zEof, iCode); |
| 257625 | if( iCode==0 ) return SQLITE_OK; |
| 257626 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); |
| 257627 | }while( iCode==0 ); |
| 257628 | WRITE_UTF8(zOut, iCode); |
| 257629 | } |
| 257630 | |
| @@ -257641,12 +258593,15 @@ | |
| 257641 | const char *z1; |
| 257642 | |
| 257643 | /* Read characters from the input up until the first non-diacritic */ |
| 257644 | do { |
| 257645 | iNext = zIn - (const unsigned char*)pText; |
| 257646 | READ_UTF8(zIn, zEof, iCode); |
| 257647 | if( iCode==0 ) break; |
| 257648 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); |
| 257649 | }while( iCode==0 ); |
| 257650 | |
| 257651 | /* Pass the current trigram back to fts5 */ |
| 257652 | rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); |
| @@ -259679,11 +260634,11 @@ | |
| 259679 | |
| 259680 | return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); |
| 259681 | } |
| 259682 | |
| 259683 | |
| 259684 | |
| 259685 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ |
| 259686 | |
| 259687 | /************** End of fts5.c ************************************************/ |
| 259688 | /************** Begin file stmt.c ********************************************/ |
| 259689 | /* |
| @@ -260035,6 +260990,7 @@ | |
| 260035 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 260036 | |
| 260037 | /************** End of stmt.c ************************************************/ |
| 260038 | /* Return the source-id for this library */ |
| 260039 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 260040 | /************************** End of sqlite3.c ******************************/ |
| 260041 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.48.0. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -16,12 +16,15 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| 27 | #define SQLITE_AMALGAMATION 1 |
| 28 | #ifndef SQLITE_PRIVATE |
| 29 | # define SQLITE_PRIVATE static |
| 30 | #endif |
| @@ -460,13 +463,13 @@ | |
| 463 | ** |
| 464 | ** See also: [sqlite3_libversion()], |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.48.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3048000 |
| 470 | #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -966,10 +969,17 @@ | |
| 969 | ** |
| 970 | ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying |
| 971 | ** filesystem supports doing multiple write operations atomically when those |
| 972 | ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and |
| 973 | ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. |
| 974 | ** |
| 975 | ** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read |
| 976 | ** from the database file in amounts that are not a multiple of the |
| 977 | ** page size and that do not begin at a page boundary. Without this |
| 978 | ** property, SQLite is careful to only do full-page reads and write |
| 979 | ** on aligned pages, with the one exception that it will do a sub-page |
| 980 | ** read of the first page to access the database header. |
| 981 | */ |
| 982 | #define SQLITE_IOCAP_ATOMIC 0x00000001 |
| 983 | #define SQLITE_IOCAP_ATOMIC512 0x00000002 |
| 984 | #define SQLITE_IOCAP_ATOMIC1K 0x00000004 |
| 985 | #define SQLITE_IOCAP_ATOMIC2K 0x00000008 |
| @@ -982,10 +992,11 @@ | |
| 992 | #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 |
| 993 | #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 |
| 994 | #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 |
| 995 | #define SQLITE_IOCAP_IMMUTABLE 0x00002000 |
| 996 | #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 |
| 997 | #define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 |
| 998 | |
| 999 | /* |
| 1000 | ** CAPI3REF: File Locking Levels |
| 1001 | ** |
| 1002 | ** SQLite uses one of these integer values as the second |
| @@ -1128,10 +1139,11 @@ | |
| 1139 | ** <li> [SQLITE_IOCAP_SEQUENTIAL] |
| 1140 | ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] |
| 1141 | ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] |
| 1142 | ** <li> [SQLITE_IOCAP_IMMUTABLE] |
| 1143 | ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] |
| 1144 | ** <li> [SQLITE_IOCAP_SUBPAGE_READ] |
| 1145 | ** </ul> |
| 1146 | ** |
| 1147 | ** The SQLITE_IOCAP_ATOMIC property means that all writes of |
| 1148 | ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values |
| 1149 | ** mean that writes of blocks that are nnn bytes in size and |
| @@ -1405,10 +1417,15 @@ | |
| 1417 | ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This |
| 1418 | ** opcode causes the xFileControl method to swap the file handle with the one |
| 1419 | ** pointed to by the pArg argument. This capability is used during testing |
| 1420 | ** and only needs to be supported when SQLITE_TEST is defined. |
| 1421 | ** |
| 1422 | ** <li>[[SQLITE_FCNTL_NULL_IO]] |
| 1423 | ** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor |
| 1424 | ** or file handle for the [sqlite3_file] object such that it will no longer |
| 1425 | ** read or write to the database file. |
| 1426 | ** |
| 1427 | ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] |
| 1428 | ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might |
| 1429 | ** be advantageous to block on the next WAL lock if the lock is not immediately |
| 1430 | ** available. The WAL subsystem issues this signal during rare |
| 1431 | ** circumstances in order to fix a problem with priority inversion. |
| @@ -1558,10 +1575,11 @@ | |
| 1575 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1576 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1577 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1578 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1579 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1580 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1581 | |
| 1582 | /* deprecated names */ |
| 1583 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1584 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1585 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -2936,14 +2954,18 @@ | |
| 2954 | ** |
| 2955 | ** ^These functions return the number of rows modified, inserted or |
| 2956 | ** deleted by the most recently completed INSERT, UPDATE or DELETE |
| 2957 | ** statement on the database connection specified by the only parameter. |
| 2958 | ** The two functions are identical except for the type of the return value |
| 2959 | ** and that if the number of rows modified by the most recent INSERT, UPDATE, |
| 2960 | ** or DELETE is greater than the maximum value supported by type "int", then |
| 2961 | ** the return value of sqlite3_changes() is undefined. ^Executing any other |
| 2962 | ** type of SQL statement does not modify the value returned by these functions. |
| 2963 | ** For the purposes of this interface, a CREATE TABLE AS SELECT statement |
| 2964 | ** does not count as an INSERT, UPDATE or DELETE statement and hence the rows |
| 2965 | ** added to the new table by the CREATE TABLE AS SELECT statement are not |
| 2966 | ** counted. |
| 2967 | ** |
| 2968 | ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are |
| 2969 | ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], |
| 2970 | ** [foreign key actions] or [REPLACE] constraint resolution are not counted. |
| 2971 | ** |
| @@ -4499,15 +4521,26 @@ | |
| 4521 | ** |
| 4522 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4523 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4524 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4525 | ** any virtual tables. |
| 4526 | ** |
| 4527 | ** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt> |
| 4528 | ** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler |
| 4529 | ** errors from being sent to the error log defined by |
| 4530 | ** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test |
| 4531 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4532 | ** messages on the global error log when it is not. If the test compile |
| 4533 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4534 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4535 | ** logs the error. |
| 4536 | ** </dl> |
| 4537 | */ |
| 4538 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4539 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4540 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4541 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4542 | |
| 4543 | /* |
| 4544 | ** CAPI3REF: Compiling An SQL Statement |
| 4545 | ** KEYWORDS: {SQL statement compiler} |
| 4546 | ** METHOD: sqlite3 |
| @@ -4536,17 +4569,21 @@ | |
| 4569 | ** and sqlite3_prepare_v3() |
| 4570 | ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), |
| 4571 | ** and sqlite3_prepare16_v3() use UTF-16. |
| 4572 | ** |
| 4573 | ** ^If the nByte argument is negative, then zSql is read up to the |
| 4574 | ** first zero terminator. ^If nByte is positive, then it is the maximum |
| 4575 | ** number of bytes read from zSql. When nByte is positive, zSql is read |
| 4576 | ** up to the first zero terminator or until the nByte bytes have been read, |
| 4577 | ** whichever comes first. ^If nByte is zero, then no prepared |
| 4578 | ** statement is generated. |
| 4579 | ** If the caller knows that the supplied string is nul-terminated, then |
| 4580 | ** there is a small performance advantage to passing an nByte parameter that |
| 4581 | ** is the number of bytes in the input string <i>including</i> |
| 4582 | ** the nul-terminator. |
| 4583 | ** Note that nByte measure the length of the input in bytes, not |
| 4584 | ** characters, even for the UTF-16 interfaces. |
| 4585 | ** |
| 4586 | ** ^If pzTail is not NULL then *pzTail is made to point to the first byte |
| 4587 | ** past the end of the first SQL statement in zSql. These routines only |
| 4588 | ** compile the first statement in zSql, so *pzTail is left pointing to |
| 4589 | ** what remains uncompiled. |
| @@ -5913,11 +5950,11 @@ | |
| 5950 | ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call |
| 5951 | ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. |
| 5952 | ** This flag instructs SQLite to omit some corner-case optimizations that |
| 5953 | ** might disrupt the operation of the [sqlite3_value_subtype()] function, |
| 5954 | ** causing it to return zero rather than the correct subtype(). |
| 5955 | ** All SQL functions that invoke [sqlite3_value_subtype()] should have this |
| 5956 | ** property. If the SQLITE_SUBTYPE property is omitted, then the return |
| 5957 | ** value from [sqlite3_value_subtype()] might sometimes be zero even though |
| 5958 | ** a non-zero subtype was specified by the function argument expression. |
| 5959 | ** |
| 5960 | ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> |
| @@ -8678,11 +8715,11 @@ | |
| 8715 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8716 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8717 | #define SQLITE_TESTCTRL_TRACEFLAGS 31 |
| 8718 | #define SQLITE_TESTCTRL_TUNE 32 |
| 8719 | #define SQLITE_TESTCTRL_LOGEST 33 |
| 8720 | #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ |
| 8721 | #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ |
| 8722 | |
| 8723 | /* |
| 8724 | ** CAPI3REF: SQL Keyword Checking |
| 8725 | ** |
| @@ -9654,10 +9691,20 @@ | |
| 9691 | ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). |
| 9692 | ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() |
| 9693 | ** APIs are not strictly speaking threadsafe. If they are invoked at the |
| 9694 | ** same time as another thread is invoking sqlite3_backup_step() it is |
| 9695 | ** possible that they return invalid values. |
| 9696 | ** |
| 9697 | ** <b>Alternatives To Using The Backup API</b> |
| 9698 | ** |
| 9699 | ** Other techniques for safely creating a consistent backup of an SQLite |
| 9700 | ** database include: |
| 9701 | ** |
| 9702 | ** <ul> |
| 9703 | ** <li> The [VACUUM INTO] command. |
| 9704 | ** <li> The [sqlite3_rsync] utility program. |
| 9705 | ** </ul> |
| 9706 | */ |
| 9707 | SQLITE_API sqlite3_backup *sqlite3_backup_init( |
| 9708 | sqlite3 *pDest, /* Destination database handle */ |
| 9709 | const char *zDestName, /* Destination database name */ |
| 9710 | sqlite3 *pSource, /* Source database handle */ |
| @@ -10852,10 +10899,18 @@ | |
| 10899 | ** schema S in database connection D. ^On success, the |
| 10900 | ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly |
| 10901 | ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. |
| 10902 | ** If there is not already a read-transaction open on schema S when |
| 10903 | ** this function is called, one is opened automatically. |
| 10904 | ** |
| 10905 | ** If a read-transaction is opened by this function, then it is guaranteed |
| 10906 | ** that the returned snapshot object may not be invalidated by a database |
| 10907 | ** writer or checkpointer until after the read-transaction is closed. This |
| 10908 | ** is not guaranteed if a read-transaction is already open when this |
| 10909 | ** function is called. In that case, any subsequent write or checkpoint |
| 10910 | ** operation on the database may invalidate the returned snapshot handle, |
| 10911 | ** even while the read-transaction remains open. |
| 10912 | ** |
| 10913 | ** The following must be true for this function to succeed. If any of |
| 10914 | ** the following statements are false when sqlite3_snapshot_get() is |
| 10915 | ** called, SQLITE_ERROR is returned. The final value of *P is undefined |
| 10916 | ** in this case. |
| @@ -11172,11 +11227,11 @@ | |
| 11227 | #endif |
| 11228 | |
| 11229 | #if 0 |
| 11230 | } /* End of the 'extern "C"' block */ |
| 11231 | #endif |
| 11232 | /* #endif for SQLITE3_H will be added by mksqlite3.tcl */ |
| 11233 | |
| 11234 | /******** Begin file sqlite3rtree.h *********/ |
| 11235 | /* |
| 11236 | ** 2010 August 30 |
| 11237 | ** |
| @@ -13423,17 +13478,32 @@ | |
| 13478 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13479 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13480 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13481 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13482 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13483 | ** bytes. |
| 13484 | ** |
| 13485 | ** The output text is not a copy of the document text that was tokenized. |
| 13486 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13487 | ** includes any embedded 0x00 and trailing data. |
| 13488 | ** |
| 13489 | ** This API may be slow in some cases if the token identified by parameters |
| 13490 | ** iIdx and iToken matched a prefix token in the query. In most cases, the |
| 13491 | ** first call to this API for each prefix token in the query is forced |
| 13492 | ** to scan the portion of the full-text index that matches the prefix |
| 13493 | ** token to collect the extra data required by this API. If the prefix |
| 13494 | ** token matches a large number of token instances in the document set, |
| 13495 | ** this may be a performance problem. |
| 13496 | ** |
| 13497 | ** If the user knows in advance that a query may use this API for a |
| 13498 | ** prefix token, FTS5 may be configured to collect all required data as part |
| 13499 | ** of the initial querying of the full-text index, avoiding the second scan |
| 13500 | ** entirely. This also causes prefix queries that do not use this API to |
| 13501 | ** run more slowly and use more memory. FTS5 may be configured in this way |
| 13502 | ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] |
| 13503 | ** option, or on a per-query basis using the |
| 13504 | ** [fts5_insttoken | fts5_insttoken()] user function. |
| 13505 | ** |
| 13506 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13507 | ** "detail=none" or "detail=column" option. |
| 13508 | ** |
| 13509 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -13521,11 +13591,10 @@ | |
| 13591 | ** CUSTOM TOKENIZERS |
| 13592 | ** |
| 13593 | ** Applications may also register custom tokenizer types. A tokenizer |
| 13594 | ** is registered by providing fts5 with a populated instance of the |
| 13595 | ** following structure. All structure methods must be defined, setting |
| 13596 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 13597 | ** behaviour. The structure methods are expected to function as follows: |
| 13598 | ** |
| 13599 | ** xCreate: |
| 13600 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -13865,10 +13934,11 @@ | |
| 13934 | #endif |
| 13935 | |
| 13936 | #endif /* _FTS5_H */ |
| 13937 | |
| 13938 | /******** End of fts5.h *********/ |
| 13939 | #endif /* SQLITE3_H */ |
| 13940 | |
| 13941 | /************** End of sqlite3.h *********************************************/ |
| 13942 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 13943 | |
| 13944 | /* |
| @@ -13910,10 +13980,11 @@ | |
| 13980 | ** to count the size: 2^31-1 or 2147483647. |
| 13981 | */ |
| 13982 | #ifndef SQLITE_MAX_LENGTH |
| 13983 | # define SQLITE_MAX_LENGTH 1000000000 |
| 13984 | #endif |
| 13985 | #define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ |
| 13986 | |
| 13987 | /* |
| 13988 | ** This is the maximum number of |
| 13989 | ** |
| 13990 | ** * Columns in a table |
| @@ -14809,10 +14880,11 @@ | |
| 14880 | #include <stdio.h> |
| 14881 | #include <stdlib.h> |
| 14882 | #include <string.h> |
| 14883 | #include <assert.h> |
| 14884 | #include <stddef.h> |
| 14885 | #include <ctype.h> |
| 14886 | |
| 14887 | /* |
| 14888 | ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. |
| 14889 | ** This allows better measurements of where memcpy() is used when running |
| 14890 | ** cachegrind. But this macro version of memcpy() is very slow so it |
| @@ -14831,11 +14903,10 @@ | |
| 14903 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 14904 | # define double sqlite_int64 |
| 14905 | # define float sqlite_int64 |
| 14906 | # define fabs(X) ((X)<0?-(X):(X)) |
| 14907 | # define sqlite3IsOverflow(X) 0 |
| 14908 | # ifndef SQLITE_BIG_DBL |
| 14909 | # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) |
| 14910 | # endif |
| 14911 | # define SQLITE_OMIT_DATETIME_FUNCS 1 |
| 14912 | # define SQLITE_OMIT_TRACE 1 |
| @@ -15006,13 +15077,10 @@ | |
| 15077 | # define INT8_TYPE int8_t |
| 15078 | # else |
| 15079 | # define INT8_TYPE signed char |
| 15080 | # endif |
| 15081 | #endif |
| 15082 | typedef sqlite_int64 i64; /* 8-byte signed integer */ |
| 15083 | typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ |
| 15084 | typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ |
| 15085 | typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ |
| 15086 | typedef INT16_TYPE i16; /* 2-byte signed integer */ |
| @@ -16391,10 +16459,13 @@ | |
| 16459 | struct KeyInfo*, /* First argument to compare function */ |
| 16460 | BtCursor *pCursor /* Space to write cursor structure */ |
| 16461 | ); |
| 16462 | SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); |
| 16463 | SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); |
| 16464 | #ifdef SQLITE_DEBUG |
| 16465 | SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*); |
| 16466 | #endif |
| 16467 | SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); |
| 16468 | SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); |
| 16469 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 16470 | SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); |
| 16471 | #endif |
| @@ -17004,11 +17075,11 @@ | |
| 17075 | |
| 17076 | /* |
| 17077 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17078 | */ |
| 17079 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17080 | #define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ |
| 17081 | |
| 17082 | /* |
| 17083 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17084 | ** for a description of what each of these routines does. |
| 17085 | */ |
| @@ -17719,51 +17790,15 @@ | |
| 17790 | struct FuncDefHash { |
| 17791 | FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ |
| 17792 | }; |
| 17793 | #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) |
| 17794 | |
| 17795 | /* |
| 17796 | ** typedef for the authorization callback function. |
| 17797 | */ |
| 17798 | typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, |
| 17799 | const char*); |
| 17800 | |
| 17801 | #ifndef SQLITE_OMIT_DEPRECATED |
| 17802 | /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing |
| 17803 | ** in the style of sqlite3_trace() |
| 17804 | */ |
| @@ -17920,13 +17955,10 @@ | |
| 17955 | sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ |
| 17956 | void *pUnlockArg; /* Argument to xUnlockNotify */ |
| 17957 | void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ |
| 17958 | sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ |
| 17959 | #endif |
| 17960 | }; |
| 17961 | |
| 17962 | /* |
| 17963 | ** A macro to discover the encoding of a database. |
| 17964 | */ |
| @@ -19221,11 +19253,11 @@ | |
| 19253 | #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ |
| 19254 | #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ |
| 19255 | #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ |
| 19256 | #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ |
| 19257 | #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ |
| 19258 | #define EP_SubtArg 0x80000000 /* Is argument to SQLITE_SUBTYPE function */ |
| 19259 | |
| 19260 | /* The EP_Propagate mask is a set of properties that automatically propagate |
| 19261 | ** upwards into parent nodes. |
| 19262 | */ |
| 19263 | #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) |
| @@ -19777,11 +19809,11 @@ | |
| 19809 | ** |
| 19810 | ** SRT_Set The result must be a single column. Store each |
| 19811 | ** row of result as the key in table pDest->iSDParm. |
| 19812 | ** Apply the affinity pDest->affSdst before storing |
| 19813 | ** results. if pDest->iSDParm2 is positive, then it is |
| 19814 | ** a register holding a Bloom filter for the IN operator |
| 19815 | ** that should be populated in addition to the |
| 19816 | ** pDest->iSDParm table. This SRT is used to |
| 19817 | ** implement "IN (SELECT ...)". |
| 19818 | ** |
| 19819 | ** SRT_EphemTab Create an temporary table pDest->iSDParm and store |
| @@ -20376,11 +20408,10 @@ | |
| 20408 | u8 bFullMutex; /* True to enable full mutexing */ |
| 20409 | u8 bOpenUri; /* True to interpret filenames as URIs */ |
| 20410 | u8 bUseCis; /* Use covering indices for full-scans */ |
| 20411 | u8 bSmallMalloc; /* Avoid large memory allocations if true */ |
| 20412 | u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ |
| 20413 | #ifdef SQLITE_DEBUG |
| 20414 | u8 bJsonSelfcheck; /* Double-check JSON parsing */ |
| 20415 | #endif |
| 20416 | int mxStrlen; /* Maximum string length */ |
| 20417 | int neverCorrupt; /* Database is always well-formed */ |
| @@ -20751,19 +20782,10 @@ | |
| 20782 | */ |
| 20783 | #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) |
| 20784 | # define SQLITE_ENABLE_FTS3 1 |
| 20785 | #endif |
| 20786 | |
| 20787 | /* |
| 20788 | ** The following macros mimic the standard library functions toupper(), |
| 20789 | ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The |
| 20790 | ** sqlite versions only work for ASCII characters, regardless of locale. |
| 20791 | */ |
| @@ -21381,11 +21403,11 @@ | |
| 21403 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 21404 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 21405 | SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); |
| 21406 | SQLITE_PRIVATE int sqlite3Atoi(const char*); |
| 21407 | #ifndef SQLITE_OMIT_UTF16 |
| 21408 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nByte, int nChar); |
| 21409 | #endif |
| 21410 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| 21411 | SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); |
| 21412 | SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); |
| 21413 | SQLITE_PRIVATE LogEst sqlite3LogEst(u64); |
| @@ -22839,13 +22861,10 @@ | |
| 22861 | "UNLINK_AFTER_CLOSE", |
| 22862 | #endif |
| 22863 | #ifdef SQLITE_UNTESTABLE |
| 22864 | "UNTESTABLE", |
| 22865 | #endif |
| 22866 | #ifdef SQLITE_USE_ALLOCA |
| 22867 | "USE_ALLOCA", |
| 22868 | #endif |
| 22869 | #ifdef SQLITE_USE_FCNTL_TRACE |
| 22870 | "USE_FCNTL_TRACE", |
| @@ -23117,11 +23136,10 @@ | |
| 23136 | SQLITE_THREADSAFE==1, /* bFullMutex */ |
| 23137 | SQLITE_USE_URI, /* bOpenUri */ |
| 23138 | SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ |
| 23139 | 0, /* bSmallMalloc */ |
| 23140 | 1, /* bExtraSchemaChecks */ |
| 23141 | #ifdef SQLITE_DEBUG |
| 23142 | 0, /* bJsonSelfcheck */ |
| 23143 | #endif |
| 23144 | 0x7ffffffe, /* mxStrlen */ |
| 23145 | 0, /* neverCorrupt */ |
| @@ -23837,13 +23855,15 @@ | |
| 23855 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 23856 | int iNewReg; /* Register for new.* values */ |
| 23857 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 23858 | i64 iKey1; /* First key value passed to hook */ |
| 23859 | i64 iKey2; /* Second key value passed to hook */ |
| 23860 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 23861 | Mem *aNew; /* Array of new.* values */ |
| 23862 | Table *pTab; /* Schema object being updated */ |
| 23863 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 23864 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 23865 | }; |
| 23866 | |
| 23867 | /* |
| 23868 | ** An instance of this object is used to pass an vector of values into |
| 23869 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -29285,20 +29305,33 @@ | |
| 29305 | |
| 29306 | #ifndef NDEBUG |
| 29307 | /* |
| 29308 | ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are |
| 29309 | ** intended for use inside assert() statements. |
| 29310 | ** |
| 29311 | ** Because these routines raise false-positive alerts in TSAN, disable |
| 29312 | ** them (make them always return 1) when compiling with TSAN. |
| 29313 | */ |
| 29314 | SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ |
| 29315 | # if defined(__has_feature) |
| 29316 | # if __has_feature(thread_sanitizer) |
| 29317 | p = 0; |
| 29318 | # endif |
| 29319 | # endif |
| 29320 | assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld ); |
| 29321 | return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); |
| 29322 | } |
| 29323 | SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ |
| 29324 | # if defined(__has_feature) |
| 29325 | # if __has_feature(thread_sanitizer) |
| 29326 | p = 0; |
| 29327 | # endif |
| 29328 | # endif |
| 29329 | assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld ); |
| 29330 | return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); |
| 29331 | } |
| 29332 | #endif /* NDEBUG */ |
| 29333 | |
| 29334 | #endif /* !defined(SQLITE_MUTEX_OMIT) */ |
| 29335 | |
| 29336 | /************** End of mutex.c ***********************************************/ |
| 29337 | /************** Begin file mutex_noop.c **************************************/ |
| @@ -32272,10 +32305,11 @@ | |
| 32305 | && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) |
| 32306 | ){ |
| 32307 | pExpr = pExpr->pLeft; |
| 32308 | } |
| 32309 | if( pExpr==0 ) return; |
| 32310 | if( ExprHasProperty(pExpr, EP_FromDDL) ) return; |
| 32311 | db->errByteOffset = pExpr->w.iOfst; |
| 32312 | } |
| 32313 | |
| 32314 | /* |
| 32315 | ** Enlarge the memory allocation on a StrAccum object so that it is |
| @@ -33001,11 +33035,11 @@ | |
| 33035 | } |
| 33036 | if( pItem->fg.isCte ){ |
| 33037 | sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); |
| 33038 | } |
| 33039 | if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ |
| 33040 | sqlite3_str_appendf(&x, " isOn"); |
| 33041 | } |
| 33042 | if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); |
| 33043 | if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); |
| 33044 | if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); |
| 33045 | if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); |
| @@ -34085,10 +34119,14 @@ | |
| 34119 | ** |
| 34120 | ** This routines are given external linkage so that they will always be |
| 34121 | ** accessible to the debugging, and to avoid warnings about unused |
| 34122 | ** functions. But these routines only exist in debugging builds, so they |
| 34123 | ** do not contaminate the interface. |
| 34124 | ** |
| 34125 | ** See Also: |
| 34126 | ** |
| 34127 | ** sqlite3ShowWhereTerm() in where.c |
| 34128 | */ |
| 34129 | SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } |
| 34130 | SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} |
| 34131 | SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } |
| 34132 | SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } |
| @@ -34687,11 +34725,11 @@ | |
| 34725 | */ |
| 34726 | #define READ_UTF8(zIn, zTerm, c) \ |
| 34727 | c = *(zIn++); \ |
| 34728 | if( c>=0xc0 ){ \ |
| 34729 | c = sqlite3Utf8Trans1[c-0xc0]; \ |
| 34730 | while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \ |
| 34731 | c = (c<<6) + (0x3f & *(zIn++)); \ |
| 34732 | } \ |
| 34733 | if( c<0x80 \ |
| 34734 | || (c&0xFFFFF800)==0xD800 \ |
| 34735 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| @@ -35065,24 +35103,26 @@ | |
| 35103 | assert( m.z || db->mallocFailed ); |
| 35104 | return m.z; |
| 35105 | } |
| 35106 | |
| 35107 | /* |
| 35108 | ** zIn is a UTF-16 encoded unicode string at least nByte bytes long. |
| 35109 | ** Return the number of bytes in the first nChar unicode characters |
| 35110 | ** in pZ. nChar must be non-negative. Surrogate pairs count as a single |
| 35111 | ** character. |
| 35112 | */ |
| 35113 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nByte, int nChar){ |
| 35114 | int c; |
| 35115 | unsigned char const *z = zIn; |
| 35116 | unsigned char const *zEnd = &z[nByte-1]; |
| 35117 | int n = 0; |
| 35118 | |
| 35119 | if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++; |
| 35120 | while( n<nChar && ALWAYS(z<=zEnd) ){ |
| 35121 | c = z[0]; |
| 35122 | z += 2; |
| 35123 | if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2; |
| 35124 | n++; |
| 35125 | } |
| 35126 | return (int)(z-(unsigned char const *)zIn) |
| 35127 | - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE); |
| 35128 | } |
| @@ -35659,10 +35699,12 @@ | |
| 35699 | int esign = 1; /* sign of exponent */ |
| 35700 | int e = 0; /* exponent */ |
| 35701 | int eValid = 1; /* True exponent is either not used or is well-formed */ |
| 35702 | int nDigit = 0; /* Number of digits processed */ |
| 35703 | int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ |
| 35704 | u64 s2; /* round-tripped significand */ |
| 35705 | double rr[2]; |
| 35706 | |
| 35707 | assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); |
| 35708 | *pResult = 0.0; /* Default return value, in case of an error */ |
| 35709 | if( length==0 ) return 0; |
| 35710 | |
| @@ -35761,81 +35803,65 @@ | |
| 35803 | |
| 35804 | /* adjust exponent by d, and update sign */ |
| 35805 | e = (e*esign) + d; |
| 35806 | |
| 35807 | /* Try to adjust the exponent to make it smaller */ |
| 35808 | while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){ |
| 35809 | s *= 10; |
| 35810 | e--; |
| 35811 | } |
| 35812 | while( e<0 && (s%10)==0 ){ |
| 35813 | s /= 10; |
| 35814 | e++; |
| 35815 | } |
| 35816 | |
| 35817 | rr[0] = (double)s; |
| 35818 | assert( sizeof(s2)==sizeof(rr[0]) ); |
| 35819 | #ifdef SQLITE_DEBUG |
| 35820 | rr[1] = 18446744073709549568.0; |
| 35821 | memcpy(&s2, &rr[1], sizeof(s2)); |
| 35822 | assert( s2==0x43efffffffffffffLL ); |
| 35823 | #endif |
| 35824 | /* Largest double that can be safely converted to u64 |
| 35825 | ** vvvvvvvvvvvvvvvvvvvvvv */ |
| 35826 | if( rr[0]<=18446744073709549568.0 ){ |
| 35827 | s2 = (u64)rr[0]; |
| 35828 | rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); |
| 35829 | }else{ |
| 35830 | rr[1] = 0.0; |
| 35831 | } |
| 35832 | assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */ |
| 35833 | |
| 35834 | if( e>0 ){ |
| 35835 | while( e>=100 ){ |
| 35836 | e -= 100; |
| 35837 | dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); |
| 35838 | } |
| 35839 | while( e>=10 ){ |
| 35840 | e -= 10; |
| 35841 | dekkerMul2(rr, 1.0e+10, 0.0); |
| 35842 | } |
| 35843 | while( e>=1 ){ |
| 35844 | e -= 1; |
| 35845 | dekkerMul2(rr, 1.0e+01, 0.0); |
| 35846 | } |
| 35847 | }else{ |
| 35848 | while( e<=-100 ){ |
| 35849 | e += 100; |
| 35850 | dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); |
| 35851 | } |
| 35852 | while( e<=-10 ){ |
| 35853 | e += 10; |
| 35854 | dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); |
| 35855 | } |
| 35856 | while( e<=-1 ){ |
| 35857 | e += 1; |
| 35858 | dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); |
| 35859 | } |
| 35860 | } |
| 35861 | *pResult = rr[0]+rr[1]; |
| 35862 | if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; |
| 35863 | if( sign<0 ) *pResult = -*pResult; |
| 35864 | assert( !sqlite3IsNaN(*pResult) ); |
| 35865 | |
| 35866 | atof_return: |
| 35867 | /* return true if number and no extra non-whitespace characters after */ |
| @@ -36152,13 +36178,14 @@ | |
| 36178 | */ |
| 36179 | SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ |
| 36180 | int i; |
| 36181 | u64 v; |
| 36182 | int e, exp = 0; |
| 36183 | double rr[2]; |
| 36184 | |
| 36185 | p->isSpecial = 0; |
| 36186 | p->z = p->zBuf; |
| 36187 | assert( mxRound>0 ); |
| 36188 | |
| 36189 | /* Convert negative numbers to positive. Deal with Infinity, 0.0, and |
| 36190 | ** NaN. */ |
| 36191 | if( r<0.0 ){ |
| @@ -36182,66 +36209,49 @@ | |
| 36209 | return; |
| 36210 | } |
| 36211 | |
| 36212 | /* Multiply r by powers of ten until it lands somewhere in between |
| 36213 | ** 1.0e+19 and 1.0e+17. |
| 36214 | ** |
| 36215 | ** Use Dekker-style double-double computation to increase the |
| 36216 | ** precision. |
| 36217 | ** |
| 36218 | ** The error terms on constants like 1.0e+100 computed using the |
| 36219 | ** decimal extension, for example as follows: |
| 36220 | ** |
| 36221 | ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); |
| 36222 | */ |
| 36223 | rr[0] = r; |
| 36224 | rr[1] = 0.0; |
| 36225 | if( rr[0]>9.223372036854774784e+18 ){ |
| 36226 | while( rr[0]>9.223372036854774784e+118 ){ |
| 36227 | exp += 100; |
| 36228 | dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); |
| 36229 | } |
| 36230 | while( rr[0]>9.223372036854774784e+28 ){ |
| 36231 | exp += 10; |
| 36232 | dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); |
| 36233 | } |
| 36234 | while( rr[0]>9.223372036854774784e+18 ){ |
| 36235 | exp += 1; |
| 36236 | dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); |
| 36237 | } |
| 36238 | }else{ |
| 36239 | while( rr[0]<9.223372036854774784e-83 ){ |
| 36240 | exp -= 100; |
| 36241 | dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); |
| 36242 | } |
| 36243 | while( rr[0]<9.223372036854774784e+07 ){ |
| 36244 | exp -= 10; |
| 36245 | dekkerMul2(rr, 1.0e+10, 0.0); |
| 36246 | } |
| 36247 | while( rr[0]<9.22337203685477478e+17 ){ |
| 36248 | exp -= 1; |
| 36249 | dekkerMul2(rr, 1.0e+01, 0.0); |
| 36250 | } |
| 36251 | } |
| 36252 | v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; |
| 36253 | |
| 36254 | /* Extract significant digits. */ |
| 36255 | i = sizeof(p->zBuf)-1; |
| 36256 | assert( v>0 ); |
| 36257 | while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } |
| @@ -37008,108 +37018,10 @@ | |
| 37018 | i += pIn[i+1]; |
| 37019 | }while( i<mx ); |
| 37020 | return 0; |
| 37021 | } |
| 37022 | |
| 37023 | /************** End of util.c ************************************************/ |
| 37024 | /************** Begin file hash.c ********************************************/ |
| 37025 | /* |
| 37026 | ** 2001 September 22 |
| 37027 | ** |
| @@ -38787,11 +38699,11 @@ | |
| 38699 | # define F_SETLKW 7 |
| 38700 | # endif |
| 38701 | # endif |
| 38702 | #else /* !SQLITE_WASI */ |
| 38703 | # ifndef HAVE_FCHMOD |
| 38704 | # define HAVE_FCHMOD 1 |
| 38705 | # endif |
| 38706 | #endif /* SQLITE_WASI */ |
| 38707 | |
| 38708 | #ifdef SQLITE_WASI |
| 38709 | # define osGetpid(X) (pid_t)1 |
| @@ -41038,58 +40950,37 @@ | |
| 40950 | ** file by this or any other process. If such a lock is held, set *pResOut |
| 40951 | ** to a non-zero value otherwise *pResOut is set to zero. The return value |
| 40952 | ** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
| 40953 | */ |
| 40954 | static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ |
| 40955 | #ifdef SQLITE_DEBUG |
| 40956 | unixFile *pFile = (unixFile*)id; |
| 40957 | #else |
| 40958 | UNUSED_PARAMETER(id); |
| 40959 | #endif |
| 40960 | |
| 40961 | SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
| 40962 | |
| 40963 | assert( pFile ); |
| 40964 | assert( pFile->eFileLock<=SHARED_LOCK ); |
| 40965 | |
| 40966 | /* The flock VFS only ever takes exclusive locks (see function flockLock). |
| 40967 | ** Therefore, if this connection is holding any lock at all, no other |
| 40968 | ** connection may be holding a RESERVED lock. So set *pResOut to 0 |
| 40969 | ** in this case. |
| 40970 | ** |
| 40971 | ** Or, this connection may be holding no lock. In that case, set *pResOut to |
| 40972 | ** 0 as well. The caller will then attempt to take an EXCLUSIVE lock on the |
| 40973 | ** db in order to roll the hot journal back. If there is another connection |
| 40974 | ** holding a lock, that attempt will fail and an SQLITE_BUSY returned to |
| 40975 | ** the user. With other VFS, we try to avoid this, in order to allow a reader |
| 40976 | ** to proceed while a writer is preparing its transaction. But that won't |
| 40977 | ** work with the flock VFS - as it always takes EXCLUSIVE locks - so it is |
| 40978 | ** not a problem in this case. */ |
| 40979 | *pResOut = 0; |
| 40980 | |
| 40981 | return SQLITE_OK; |
| 40982 | } |
| 40983 | |
| 40984 | /* |
| 40985 | ** Lock the file with the lock specified by parameter eFileLock - one |
| 40986 | ** of the following: |
| @@ -42582,10 +42473,15 @@ | |
| 42473 | int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); |
| 42474 | return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; |
| 42475 | } |
| 42476 | #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ |
| 42477 | |
| 42478 | case SQLITE_FCNTL_NULL_IO: { |
| 42479 | osClose(pFile->h); |
| 42480 | pFile->h = -1; |
| 42481 | return SQLITE_OK; |
| 42482 | } |
| 42483 | case SQLITE_FCNTL_LOCKSTATE: { |
| 42484 | *(int*)pArg = pFile->eFileLock; |
| 42485 | return SQLITE_OK; |
| 42486 | } |
| 42487 | case SQLITE_FCNTL_LAST_ERRNO: { |
| @@ -42723,10 +42619,11 @@ | |
| 42619 | |
| 42620 | /* Set the POWERSAFE_OVERWRITE flag if requested. */ |
| 42621 | if( pFd->ctrlFlags & UNIXFILE_PSOW ){ |
| 42622 | pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; |
| 42623 | } |
| 42624 | pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ; |
| 42625 | |
| 42626 | pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; |
| 42627 | } |
| 42628 | } |
| 42629 | #else |
| @@ -42773,19 +42670,19 @@ | |
| 42670 | 0; |
| 42671 | }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ |
| 42672 | pFile->sectorSize = fsInfo.f_bsize; |
| 42673 | pFile->deviceCharacteristics = |
| 42674 | /* full bitset of atomics from max sector size and smaller */ |
| 42675 | (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | |
| 42676 | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
| 42677 | ** so it is ordered */ |
| 42678 | 0; |
| 42679 | }else if( strstr(fsInfo.f_basetype, "dos") ){ |
| 42680 | pFile->sectorSize = fsInfo.f_bsize; |
| 42681 | pFile->deviceCharacteristics = |
| 42682 | /* full bitset of atomics from max sector size and smaller */ |
| 42683 | (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | |
| 42684 | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
| 42685 | ** so it is ordered */ |
| 42686 | 0; |
| 42687 | }else{ |
| 42688 | pFile->deviceCharacteristics = |
| @@ -50462,10 +50359,15 @@ | |
| 50359 | OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n", |
| 50360 | hOldFile, pFile->h)); |
| 50361 | return SQLITE_OK; |
| 50362 | } |
| 50363 | #endif |
| 50364 | case SQLITE_FCNTL_NULL_IO: { |
| 50365 | (void)osCloseHandle(pFile->h); |
| 50366 | pFile->h = NULL; |
| 50367 | return SQLITE_OK; |
| 50368 | } |
| 50369 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 50370 | char *zTFile = 0; |
| 50371 | int rc = winGetTempname(pFile->pVfs, &zTFile); |
| 50372 | if( rc==SQLITE_OK ){ |
| 50373 | *(char**)pArg = zTFile; |
| @@ -50523,11 +50425,11 @@ | |
| 50425 | /* |
| 50426 | ** Return a vector of device characteristics. |
| 50427 | */ |
| 50428 | static int winDeviceCharacteristics(sqlite3_file *id){ |
| 50429 | winFile *p = (winFile*)id; |
| 50430 | return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ | |
| 50431 | ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); |
| 50432 | } |
| 50433 | |
| 50434 | /* |
| 50435 | ** Windows will only let you create file view mappings |
| @@ -51911,11 +51813,11 @@ | |
| 51813 | */ |
| 51814 | char *zTmpname = 0; /* For temporary filename, if necessary. */ |
| 51815 | |
| 51816 | int rc = SQLITE_OK; /* Function Return Code */ |
| 51817 | #if !defined(NDEBUG) || SQLITE_OS_WINCE |
| 51818 | int eType = flags&0x0FFF00; /* Type of file to open */ |
| 51819 | #endif |
| 51820 | |
| 51821 | int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); |
| 51822 | int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); |
| 51823 | int isCreate = (flags & SQLITE_OPEN_CREATE); |
| @@ -58131,24 +58033,32 @@ | |
| 58033 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58034 | /* |
| 58035 | ** Return true if page pgno can be read directly from the database file |
| 58036 | ** by the b-tree layer. This is the case if: |
| 58037 | ** |
| 58038 | ** (1) the database file is open |
| 58039 | ** (2) the VFS for the database is able to do unaligned sub-page reads |
| 58040 | ** (3) there are no dirty pages in the cache, and |
| 58041 | ** (4) the desired page is not currently in the wal file. |
| 58042 | */ |
| 58043 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ |
| 58044 | assert( pPager!=0 ); |
| 58045 | assert( pPager->fd!=0 ); |
| 58046 | if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */ |
| 58047 | if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */ |
| 58048 | #ifndef SQLITE_OMIT_WAL |
| 58049 | if( pPager->pWal ){ |
| 58050 | u32 iRead = 0; |
| 58051 | (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); |
| 58052 | return iRead==0; /* Condition (4) */ |
| 58053 | } |
| 58054 | #endif |
| 58055 | assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); |
| 58056 | if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) |
| 58057 | & SQLITE_IOCAP_SUBPAGE_READ)==0 ){ |
| 58058 | return 0; /* Case (2) */ |
| 58059 | } |
| 58060 | return 1; |
| 58061 | } |
| 58062 | #endif |
| 58063 | |
| 58064 | #ifndef SQLITE_OMIT_WAL |
| @@ -65171,11 +65081,11 @@ | |
| 65081 | ** 20: Salt-2, a different random integer changing with each ckpt |
| 65082 | ** 24: Checksum-1 (first part of checksum for first 24 bytes of header). |
| 65083 | ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). |
| 65084 | ** |
| 65085 | ** Immediately following the wal-header are zero or more frames. Each |
| 65086 | ** frame consists of a 24-byte frame-header followed by <page-size> bytes |
| 65087 | ** of page data. The frame-header is six big-endian 32-bit unsigned |
| 65088 | ** integer values, as follows: |
| 65089 | ** |
| 65090 | ** 0: Page number. |
| 65091 | ** 4: For commit records, the size of the database image in pages |
| @@ -65668,10 +65578,11 @@ | |
| 65578 | int nSehTry; /* Number of nested SEH_TRY{} blocks */ |
| 65579 | u8 lockError; /* True if a locking error has occurred */ |
| 65580 | #endif |
| 65581 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 65582 | WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ |
| 65583 | int bGetSnapshot; /* Transaction opened for sqlite3_get_snapshot() */ |
| 65584 | #endif |
| 65585 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 65586 | sqlite3 *db; |
| 65587 | #endif |
| 65588 | }; |
| @@ -67560,11 +67471,11 @@ | |
| 67471 | return SQLITE_IOERR_IN_PAGE; |
| 67472 | } |
| 67473 | |
| 67474 | /* |
| 67475 | ** Assert that the Wal.lockMask mask, which indicates the locks held |
| 67476 | ** by the connection, is consistent with the Wal.readLock, Wal.writeLock |
| 67477 | ** and Wal.ckptLock variables. To be used as: |
| 67478 | ** |
| 67479 | ** assert( walAssertLockmask(pWal) ); |
| 67480 | */ |
| 67481 | static int walAssertLockmask(Wal *pWal){ |
| @@ -68224,11 +68135,11 @@ | |
| 68135 | assert( pWal->apWiData[0]!=0 ); |
| 68136 | pInfo = walCkptInfo(pWal); |
| 68137 | SEH_INJECT_FAULT; |
| 68138 | if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame |
| 68139 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68140 | && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) |
| 68141 | #endif |
| 68142 | ){ |
| 68143 | /* The WAL has been completely backfilled (or it is empty). |
| 68144 | ** and can be safely ignored. |
| 68145 | */ |
| @@ -69624,11 +69535,24 @@ | |
| 69535 | */ |
| 69536 | SQLITE_PRIVATE void sqlite3WalSnapshotOpen( |
| 69537 | Wal *pWal, |
| 69538 | sqlite3_snapshot *pSnapshot |
| 69539 | ){ |
| 69540 | if( pSnapshot && ((WalIndexHdr*)pSnapshot)->iVersion==0 ){ |
| 69541 | /* iVersion==0 means that this is a call to sqlite3_snapshot_get(). In |
| 69542 | ** this case set the bGetSnapshot flag so that if the call to |
| 69543 | ** sqlite3_snapshot_get() is about to read transaction on this wal |
| 69544 | ** file, it does not take read-lock 0 if the wal file has been completely |
| 69545 | ** checkpointed. Taking read-lock 0 would work, but then it would be |
| 69546 | ** possible for a subsequent writer to destroy the snapshot even while |
| 69547 | ** this connection is holding its read-transaction open. This is contrary |
| 69548 | ** to user expectations, so we avoid it by not taking read-lock 0. */ |
| 69549 | pWal->bGetSnapshot = 1; |
| 69550 | }else{ |
| 69551 | pWal->pSnapshot = (WalIndexHdr*)pSnapshot; |
| 69552 | pWal->bGetSnapshot = 0; |
| 69553 | } |
| 69554 | } |
| 69555 | |
| 69556 | /* |
| 69557 | ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if |
| 69558 | ** p1 is older than p2 and zero if p1 and p2 are the same snapshot. |
| @@ -75504,10 +75428,29 @@ | |
| 75428 | ** this routine. |
| 75429 | */ |
| 75430 | SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ |
| 75431 | return ROUND8(sizeof(BtCursor)); |
| 75432 | } |
| 75433 | |
| 75434 | #ifdef SQLITE_DEBUG |
| 75435 | /* |
| 75436 | ** Return true if and only if the Btree object will be automatically |
| 75437 | ** closed with the BtCursor closes. This is used within assert() statements |
| 75438 | ** only. |
| 75439 | */ |
| 75440 | SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor( |
| 75441 | Btree *pBtree, /* the btree object */ |
| 75442 | BtCursor *pCur /* Corresponding cursor */ |
| 75443 | ){ |
| 75444 | BtShared *pBt = pBtree->pBt; |
| 75445 | if( (pBt->openFlags & BTREE_SINGLE)==0 ) return 0; |
| 75446 | if( pBt->pCursor!=pCur ) return 0; |
| 75447 | if( pCur->pNext!=0 ) return 0; |
| 75448 | if( pCur->pBtree!=pBtree ) return 0; |
| 75449 | return 1; |
| 75450 | } |
| 75451 | #endif |
| 75452 | |
| 75453 | /* |
| 75454 | ** Initialize memory that will be converted into a BtCursor object. |
| 75455 | ** |
| 75456 | ** The simple approach here would be to memset() the entire object |
| @@ -84538,11 +84481,12 @@ | |
| 84481 | if( apVal==0 ){ |
| 84482 | rc = SQLITE_NOMEM_BKPT; |
| 84483 | goto value_from_function_out; |
| 84484 | } |
| 84485 | for(i=0; i<nVal; i++){ |
| 84486 | rc = sqlite3Stat4ValueFromExpr(pCtx->pParse, pList->a[i].pExpr, aff, |
| 84487 | &apVal[i]); |
| 84488 | if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; |
| 84489 | } |
| 84490 | } |
| 84491 | |
| 84492 | pVal = valueNew(db, pCtx); |
| @@ -89571,11 +89515,11 @@ | |
| 89515 | /* The following two functions are used only within testcase() to prove |
| 89516 | ** test coverage. These functions do no exist for production builds. |
| 89517 | ** We must use separate SQLITE_NOINLINE functions here, since otherwise |
| 89518 | ** optimizer code movement causes gcov to become very confused. |
| 89519 | */ |
| 89520 | #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) |
| 89521 | static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; } |
| 89522 | static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; } |
| 89523 | #endif |
| 89524 | |
| 89525 | /* |
| @@ -89586,17 +89530,10 @@ | |
| 89530 | SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ |
| 89531 | if( sqlite3IsNaN(r) ){ |
| 89532 | /* SQLite considers NaN to be a NULL. And all integer values are greater |
| 89533 | ** than NULL */ |
| 89534 | return 1; |
| 89535 | }else{ |
| 89536 | i64 y; |
| 89537 | if( r<-9223372036854775808.0 ) return +1; |
| 89538 | if( r>=9223372036854775808.0 ) return -1; |
| 89539 | y = (i64)r; |
| @@ -90595,17 +90532,25 @@ | |
| 90532 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 90533 | db->pPreUpdate = 0; |
| 90534 | sqlite3DbFree(db, preupdate.aRecord); |
| 90535 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); |
| 90536 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); |
| 90537 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 90538 | if( preupdate.aNew ){ |
| 90539 | int i; |
| 90540 | for(i=0; i<pCsr->nField; i++){ |
| 90541 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| 90542 | } |
| 90543 | sqlite3DbNNFreeNN(db, preupdate.aNew); |
| 90544 | } |
| 90545 | if( preupdate.apDflt ){ |
| 90546 | int i; |
| 90547 | for(i=0; i<pTab->nCol; i++){ |
| 90548 | sqlite3ValueFree(preupdate.apDflt[i]); |
| 90549 | } |
| 90550 | sqlite3DbFree(db, preupdate.apDflt); |
| 90551 | } |
| 90552 | } |
| 90553 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 90554 | |
| 90555 | /************** End of vdbeaux.c *********************************************/ |
| 90556 | /************** Begin file vdbeapi.c *****************************************/ |
| @@ -92230,10 +92175,21 @@ | |
| 92175 | ** A successful evaluation of this routine acquires the mutex on p. |
| 92176 | ** the mutex is released if any kind of error occurs. |
| 92177 | ** |
| 92178 | ** The error code stored in database p->db is overwritten with the return |
| 92179 | ** value in any case. |
| 92180 | ** |
| 92181 | ** (tag-20240917-01) If vdbeUnbind(p,(u32)(i-1)) returns SQLITE_OK, |
| 92182 | ** that means all of the the following will be true: |
| 92183 | ** |
| 92184 | ** p!=0 |
| 92185 | ** p->pVar!=0 |
| 92186 | ** i>0 |
| 92187 | ** i<=p->nVar |
| 92188 | ** |
| 92189 | ** An assert() is normally added after vdbeUnbind() to help static analyzers |
| 92190 | ** realize this. |
| 92191 | */ |
| 92192 | static int vdbeUnbind(Vdbe *p, unsigned int i){ |
| 92193 | Mem *pVar; |
| 92194 | if( vdbeSafetyNotNull(p) ){ |
| 92195 | return SQLITE_MISUSE_BKPT; |
| @@ -92287,10 +92243,11 @@ | |
| 92243 | Mem *pVar; |
| 92244 | int rc; |
| 92245 | |
| 92246 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92247 | if( rc==SQLITE_OK ){ |
| 92248 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 92249 | if( zData!=0 ){ |
| 92250 | pVar = &p->aVar[i-1]; |
| 92251 | rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); |
| 92252 | if( rc==SQLITE_OK && encoding!=0 ){ |
| 92253 | rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); |
| @@ -92336,10 +92293,11 @@ | |
| 92293 | SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ |
| 92294 | int rc; |
| 92295 | Vdbe *p = (Vdbe *)pStmt; |
| 92296 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92297 | if( rc==SQLITE_OK ){ |
| 92298 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 92299 | sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); |
| 92300 | sqlite3_mutex_leave(p->db->mutex); |
| 92301 | } |
| 92302 | return rc; |
| 92303 | } |
| @@ -92349,10 +92307,11 @@ | |
| 92307 | SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ |
| 92308 | int rc; |
| 92309 | Vdbe *p = (Vdbe *)pStmt; |
| 92310 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92311 | if( rc==SQLITE_OK ){ |
| 92312 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 92313 | sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); |
| 92314 | sqlite3_mutex_leave(p->db->mutex); |
| 92315 | } |
| 92316 | return rc; |
| 92317 | } |
| @@ -92359,10 +92318,11 @@ | |
| 92318 | SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ |
| 92319 | int rc; |
| 92320 | Vdbe *p = (Vdbe*)pStmt; |
| 92321 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92322 | if( rc==SQLITE_OK ){ |
| 92323 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 92324 | sqlite3_mutex_leave(p->db->mutex); |
| 92325 | } |
| 92326 | return rc; |
| 92327 | } |
| 92328 | SQLITE_API int sqlite3_bind_pointer( |
| @@ -92374,10 +92334,11 @@ | |
| 92334 | ){ |
| 92335 | int rc; |
| 92336 | Vdbe *p = (Vdbe*)pStmt; |
| 92337 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92338 | if( rc==SQLITE_OK ){ |
| 92339 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 92340 | sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); |
| 92341 | sqlite3_mutex_leave(p->db->mutex); |
| 92342 | }else if( xDestructor ){ |
| 92343 | xDestructor(pPtr); |
| 92344 | } |
| @@ -92455,10 +92416,11 @@ | |
| 92416 | SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ |
| 92417 | int rc; |
| 92418 | Vdbe *p = (Vdbe *)pStmt; |
| 92419 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 92420 | if( rc==SQLITE_OK ){ |
| 92421 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 92422 | #ifndef SQLITE_OMIT_INCRBLOB |
| 92423 | sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); |
| 92424 | #else |
| 92425 | rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); |
| 92426 | #endif |
| @@ -92789,41 +92751,68 @@ | |
| 92751 | if( iIdx>=p->pCsr->nField || iIdx<0 ){ |
| 92752 | rc = SQLITE_RANGE; |
| 92753 | goto preupdate_old_out; |
| 92754 | } |
| 92755 | |
| 92756 | if( iIdx==p->pTab->iPKey ){ |
| 92757 | *ppValue = pMem = &p->oldipk; |
| 92758 | sqlite3VdbeMemSetInt64(pMem, p->iKey1); |
| 92759 | }else{ |
| 92760 | |
| 92761 | /* If the old.* record has not yet been loaded into memory, do so now. */ |
| 92762 | if( p->pUnpacked==0 ){ |
| 92763 | u32 nRec; |
| 92764 | u8 *aRec; |
| 92765 | |
| 92766 | assert( p->pCsr->eCurType==CURTYPE_BTREE ); |
| 92767 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 92768 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 92769 | if( !aRec ) goto preupdate_old_out; |
| 92770 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 92771 | if( rc==SQLITE_OK ){ |
| 92772 | p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); |
| 92773 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 92774 | } |
| 92775 | if( rc!=SQLITE_OK ){ |
| 92776 | sqlite3DbFree(db, aRec); |
| 92777 | goto preupdate_old_out; |
| 92778 | } |
| 92779 | p->aRecord = aRec; |
| 92780 | } |
| 92781 | |
| 92782 | pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; |
| 92783 | if( iIdx>=p->pUnpacked->nField ){ |
| 92784 | /* This occurs when the table has been extended using ALTER TABLE |
| 92785 | ** ADD COLUMN. The value to return is the default value of the column. */ |
| 92786 | Column *pCol = &p->pTab->aCol[iIdx]; |
| 92787 | if( pCol->iDflt>0 ){ |
| 92788 | if( p->apDflt==0 ){ |
| 92789 | int nByte = sizeof(sqlite3_value*)*p->pTab->nCol; |
| 92790 | p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); |
| 92791 | if( p->apDflt==0 ) goto preupdate_old_out; |
| 92792 | } |
| 92793 | if( p->apDflt[iIdx]==0 ){ |
| 92794 | sqlite3_value *pVal = 0; |
| 92795 | Expr *pDflt; |
| 92796 | assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); |
| 92797 | pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; |
| 92798 | rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); |
| 92799 | if( rc==SQLITE_OK && pVal==0 ){ |
| 92800 | rc = SQLITE_CORRUPT_BKPT; |
| 92801 | } |
| 92802 | p->apDflt[iIdx] = pVal; |
| 92803 | } |
| 92804 | *ppValue = p->apDflt[iIdx]; |
| 92805 | }else{ |
| 92806 | *ppValue = (sqlite3_value *)columnNullValue(); |
| 92807 | } |
| 92808 | }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ |
| 92809 | if( pMem->flags & (MEM_Int|MEM_IntReal) ){ |
| 92810 | testcase( pMem->flags & MEM_Int ); |
| 92811 | testcase( pMem->flags & MEM_IntReal ); |
| 92812 | sqlite3VdbeMemRealify(pMem); |
| 92813 | } |
| 92814 | } |
| 92815 | } |
| 92816 | |
| 92817 | preupdate_old_out: |
| 92818 | sqlite3Error(db, rc); |
| @@ -93367,10 +93356,108 @@ | |
| 93356 | ** commenting and indentation practices when changing or adding code. |
| 93357 | */ |
| 93358 | /* #include "sqliteInt.h" */ |
| 93359 | /* #include "vdbeInt.h" */ |
| 93360 | |
| 93361 | /* |
| 93362 | ** High-resolution hardware timer used for debugging and testing only. |
| 93363 | */ |
| 93364 | #if defined(VDBE_PROFILE) \ |
| 93365 | || defined(SQLITE_PERFORMANCE_TRACE) \ |
| 93366 | || defined(SQLITE_ENABLE_STMT_SCANSTATUS) |
| 93367 | /************** Include hwtime.h in the middle of vdbe.c *********************/ |
| 93368 | /************** Begin file hwtime.h ******************************************/ |
| 93369 | /* |
| 93370 | ** 2008 May 27 |
| 93371 | ** |
| 93372 | ** The author disclaims copyright to this source code. In place of |
| 93373 | ** a legal notice, here is a blessing: |
| 93374 | ** |
| 93375 | ** May you do good and not evil. |
| 93376 | ** May you find forgiveness for yourself and forgive others. |
| 93377 | ** May you share freely, never taking more than you give. |
| 93378 | ** |
| 93379 | ****************************************************************************** |
| 93380 | ** |
| 93381 | ** This file contains inline asm code for retrieving "high-performance" |
| 93382 | ** counters for x86 and x86_64 class CPUs. |
| 93383 | */ |
| 93384 | #ifndef SQLITE_HWTIME_H |
| 93385 | #define SQLITE_HWTIME_H |
| 93386 | |
| 93387 | /* |
| 93388 | ** The following routine only works on Pentium-class (or newer) processors. |
| 93389 | ** It uses the RDTSC opcode to read the cycle count value out of the |
| 93390 | ** processor and returns that value. This can be used for high-res |
| 93391 | ** profiling. |
| 93392 | */ |
| 93393 | #if !defined(__STRICT_ANSI__) && \ |
| 93394 | (defined(__GNUC__) || defined(_MSC_VER)) && \ |
| 93395 | (defined(i386) || defined(__i386__) || defined(_M_IX86)) |
| 93396 | |
| 93397 | #if defined(__GNUC__) |
| 93398 | |
| 93399 | __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
| 93400 | unsigned int lo, hi; |
| 93401 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
| 93402 | return (sqlite_uint64)hi << 32 | lo; |
| 93403 | } |
| 93404 | |
| 93405 | #elif defined(_MSC_VER) |
| 93406 | |
| 93407 | __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ |
| 93408 | __asm { |
| 93409 | rdtsc |
| 93410 | ret ; return value at EDX:EAX |
| 93411 | } |
| 93412 | } |
| 93413 | |
| 93414 | #endif |
| 93415 | |
| 93416 | #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) |
| 93417 | |
| 93418 | __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
| 93419 | unsigned int lo, hi; |
| 93420 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
| 93421 | return (sqlite_uint64)hi << 32 | lo; |
| 93422 | } |
| 93423 | |
| 93424 | #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) |
| 93425 | |
| 93426 | __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
| 93427 | unsigned long long retval; |
| 93428 | unsigned long junk; |
| 93429 | __asm__ __volatile__ ("\n\ |
| 93430 | 1: mftbu %1\n\ |
| 93431 | mftb %L0\n\ |
| 93432 | mftbu %0\n\ |
| 93433 | cmpw %0,%1\n\ |
| 93434 | bne 1b" |
| 93435 | : "=r" (retval), "=r" (junk)); |
| 93436 | return retval; |
| 93437 | } |
| 93438 | |
| 93439 | #else |
| 93440 | |
| 93441 | /* |
| 93442 | ** asm() is needed for hardware timing support. Without asm(), |
| 93443 | ** disable the sqlite3Hwtime() routine. |
| 93444 | ** |
| 93445 | ** sqlite3Hwtime() is only used for some obscure debugging |
| 93446 | ** and analysis configurations, not in any deliverable, so this |
| 93447 | ** should not be a great loss. |
| 93448 | */ |
| 93449 | SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } |
| 93450 | |
| 93451 | #endif |
| 93452 | |
| 93453 | #endif /* !defined(SQLITE_HWTIME_H) */ |
| 93454 | |
| 93455 | /************** End of hwtime.h **********************************************/ |
| 93456 | /************** Continuing where we left off in vdbe.c ***********************/ |
| 93457 | #endif |
| 93458 | |
| 93459 | /* |
| 93460 | ** Invoke this macro on memory cells just prior to changing the |
| 93461 | ** value of the cell. This macro verifies that shallow copies are |
| 93462 | ** not misused. A shallow copy of a string or blob just copies a |
| 93463 | ** pointer to the string or blob, not the content. If the original |
| @@ -97875,12 +97962,17 @@ | |
| 97962 | 0, pCx->uc.pCursor); |
| 97963 | pCx->isTable = 1; |
| 97964 | } |
| 97965 | } |
| 97966 | pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); |
| 97967 | assert( p->apCsr[pOp->p1]==pCx ); |
| 97968 | if( rc ){ |
| 97969 | assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); |
| 97970 | sqlite3BtreeClose(pCx->ub.pBtx); |
| 97971 | p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */ |
| 97972 | }else{ |
| 97973 | assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); |
| 97974 | } |
| 97975 | } |
| 97976 | } |
| 97977 | if( rc ) goto abort_due_to_error; |
| 97978 | pCx->nullRow = 1; |
| @@ -102394,11 +102486,11 @@ | |
| 102486 | ** element. |
| 102487 | ** |
| 102488 | ** As with all opcodes, the meanings of the parameters for OP_Explain |
| 102489 | ** are subject to change from one release to the next. Applications |
| 102490 | ** should not attempt to interpret or use any of the information |
| 102491 | ** contained in the OP_Explain opcode. The information provided by this |
| 102492 | ** opcode is intended for testing and debugging use only. |
| 102493 | */ |
| 102494 | default: { /* This is really OP_Noop, OP_Explain */ |
| 102495 | assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); |
| 102496 | |
| @@ -107490,11 +107582,11 @@ | |
| 107582 | ** non-VIEW candidate plus multiple VIEW candidates. In other |
| 107583 | ** words non-VIEW candidate terms take precedence over VIEWs. |
| 107584 | */ |
| 107585 | if( cntTab==0 |
| 107586 | || (cntTab==1 |
| 107587 | && pMatch!=0 |
| 107588 | && ALWAYS(pMatch->pSTab!=0) |
| 107589 | && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 |
| 107590 | && (pTab->tabFlags & TF_Ephemeral)==0) |
| 107591 | ){ |
| 107592 | cntTab = 1; |
| @@ -108123,12 +108215,12 @@ | |
| 108215 | } |
| 108216 | |
| 108217 | /* Resolve function names |
| 108218 | */ |
| 108219 | case TK_FUNCTION: { |
| 108220 | ExprList *pList; /* The argument list */ |
| 108221 | int n; /* Number of arguments */ |
| 108222 | int no_such_func = 0; /* True if no such function exists */ |
| 108223 | int wrong_num_args = 0; /* True if wrong number of arguments */ |
| 108224 | int is_agg = 0; /* True if is an aggregate function */ |
| 108225 | const char *zId; /* The function name. */ |
| 108226 | FuncDef *pDef; /* Information about the function */ |
| @@ -108137,10 +108229,12 @@ | |
| 108229 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 108230 | Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); |
| 108231 | #endif |
| 108232 | assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); |
| 108233 | assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); |
| 108234 | pList = pExpr->x.pList; |
| 108235 | n = pList ? pList->nExpr : 0; |
| 108236 | zId = pExpr->u.zToken; |
| 108237 | pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); |
| 108238 | if( pDef==0 ){ |
| 108239 | pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0); |
| 108240 | if( pDef==0 ){ |
| @@ -108185,10 +108279,28 @@ | |
| 108279 | pExpr->op = TK_NULL; |
| 108280 | return WRC_Prune; |
| 108281 | } |
| 108282 | } |
| 108283 | #endif |
| 108284 | |
| 108285 | /* If the function may call sqlite3_value_subtype(), then set the |
| 108286 | ** EP_SubtArg flag on all of its argument expressions. This prevents |
| 108287 | ** where.c from replacing the expression with a value read from an |
| 108288 | ** index on the same expression, which will not have the correct |
| 108289 | ** subtype. Also set the flag if the function expression itself is |
| 108290 | ** an EP_SubtArg expression. In this case subtypes are required as |
| 108291 | ** the function may return a value with a subtype back to its |
| 108292 | ** caller using sqlite3_result_value(). */ |
| 108293 | if( (pDef->funcFlags & SQLITE_SUBTYPE) |
| 108294 | || ExprHasProperty(pExpr, EP_SubtArg) |
| 108295 | ){ |
| 108296 | int ii; |
| 108297 | for(ii=0; ii<n; ii++){ |
| 108298 | ExprSetProperty(pList->a[ii].pExpr, EP_SubtArg); |
| 108299 | } |
| 108300 | } |
| 108301 | |
| 108302 | if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ |
| 108303 | /* For the purposes of the EP_ConstFunc flag, date and time |
| 108304 | ** functions and other functions that change slowly are considered |
| 108305 | ** constant because they are constant for the duration of one query. |
| 108306 | ** This allows them to be factored out of inner loops. */ |
| @@ -111951,11 +112063,11 @@ | |
| 112063 | ** |
| 112064 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 112065 | ** (4a) pExpr must come from an ON clause.. |
| 112066 | ** (4b) and specifically the ON clause associated with the LEFT JOIN. |
| 112067 | ** |
| 112068 | ** (5) If pSrc is the right operand of a LEFT JOIN or the left |
| 112069 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 112070 | ** clause, not an ON clause. |
| 112071 | ** |
| 112072 | ** (6) Either: |
| 112073 | ** |
| @@ -113858,10 +113970,63 @@ | |
| 113970 | } |
| 113971 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 113972 | } |
| 113973 | return target; |
| 113974 | } |
| 113975 | |
| 113976 | /* |
| 113977 | ** Expression Node callback for sqlite3ExprCanReturnSubtype(). |
| 113978 | ** |
| 113979 | ** Only a function call is able to return a subtype. So if the node |
| 113980 | ** is not a function call, return WRC_Prune immediately. |
| 113981 | ** |
| 113982 | ** A function call is able to return a subtype if it has the |
| 113983 | ** SQLITE_RESULT_SUBTYPE property. |
| 113984 | ** |
| 113985 | ** Assume that every function is able to pass-through a subtype from |
| 113986 | ** one of its argument (using sqlite3_result_value()). Most functions |
| 113987 | ** are not this way, but we don't have a mechanism to distinguish those |
| 113988 | ** that are from those that are not, so assume they all work this way. |
| 113989 | ** That means that if one of its arguments is another function and that |
| 113990 | ** other function is able to return a subtype, then this function is |
| 113991 | ** able to return a subtype. |
| 113992 | */ |
| 113993 | static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ |
| 113994 | int n; |
| 113995 | FuncDef *pDef; |
| 113996 | sqlite3 *db; |
| 113997 | if( pExpr->op!=TK_FUNCTION ){ |
| 113998 | return WRC_Prune; |
| 113999 | } |
| 114000 | assert( ExprUseXList(pExpr) ); |
| 114001 | db = pWalker->pParse->db; |
| 114002 | n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; |
| 114003 | pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); |
| 114004 | if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ |
| 114005 | pWalker->eCode = 1; |
| 114006 | return WRC_Prune; |
| 114007 | } |
| 114008 | return WRC_Continue; |
| 114009 | } |
| 114010 | |
| 114011 | /* |
| 114012 | ** Return TRUE if expression pExpr is able to return a subtype. |
| 114013 | ** |
| 114014 | ** A TRUE return does not guarantee that a subtype will be returned. |
| 114015 | ** It only indicates that a subtype return is possible. False positives |
| 114016 | ** are acceptable as they only disable an optimization. False negatives, |
| 114017 | ** on the other hand, can lead to incorrect answers. |
| 114018 | */ |
| 114019 | static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ |
| 114020 | Walker w; |
| 114021 | memset(&w, 0, sizeof(w)); |
| 114022 | w.pParse = pParse; |
| 114023 | w.xExprCallback = exprNodeCanReturnSubtype; |
| 114024 | sqlite3WalkExpr(&w, pExpr); |
| 114025 | return w.eCode; |
| 114026 | } |
| 114027 | |
| 114028 | |
| 114029 | /* |
| 114030 | ** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. |
| 114031 | ** If it is, then resolve the expression by reading from the index and |
| 114032 | ** return the register into which the value has been read. If pExpr is |
| @@ -113891,10 +114056,21 @@ | |
| 114056 | ){ |
| 114057 | /* Affinity mismatch on a generated column */ |
| 114058 | continue; |
| 114059 | } |
| 114060 | |
| 114061 | |
| 114062 | /* Functions that might set a subtype should not be replaced by the |
| 114063 | ** value taken from an expression index if they are themselves an |
| 114064 | ** argument to another scalar function or aggregate. |
| 114065 | ** https://sqlite.org/forum/forumpost/68d284c86b082c3e */ |
| 114066 | if( ExprHasProperty(pExpr, EP_SubtArg) |
| 114067 | && sqlite3ExprCanReturnSubtype(pParse, pExpr) |
| 114068 | ){ |
| 114069 | continue; |
| 114070 | } |
| 114071 | |
| 114072 | v = pParse->pVdbe; |
| 114073 | assert( v!=0 ); |
| 114074 | if( p->bMaybeNullRow ){ |
| 114075 | /* If the index is on a NULL row due to an outer join, then we |
| 114076 | ** cannot extract the value from the index. The value must be |
| @@ -115421,35 +115597,41 @@ | |
| 115597 | ** |
| 115598 | ** Additionally, if pExpr is a simple SQL value and the value is the |
| 115599 | ** same as that currently bound to variable pVar, non-zero is returned. |
| 115600 | ** Otherwise, if the values are not the same or if pExpr is not a simple |
| 115601 | ** SQL value, zero is returned. |
| 115602 | ** |
| 115603 | ** If the SQLITE_EnableQPSG flag is set on the database connection, then |
| 115604 | ** this routine always returns false. |
| 115605 | */ |
| 115606 | static SQLITE_NOINLINE int exprCompareVariable( |
| 115607 | const Parse *pParse, |
| 115608 | const Expr *pVar, |
| 115609 | const Expr *pExpr |
| 115610 | ){ |
| 115611 | int res = 2; |
| 115612 | int iVar; |
| 115613 | sqlite3_value *pL, *pR = 0; |
| 115614 | |
| 115615 | if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){ |
| 115616 | return 0; |
| 115617 | } |
| 115618 | if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2; |
| 115619 | sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); |
| 115620 | if( pR ){ |
| 115621 | iVar = pVar->iColumn; |
| 115622 | sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); |
| 115623 | pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB); |
| 115624 | if( pL ){ |
| 115625 | if( sqlite3_value_type(pL)==SQLITE_TEXT ){ |
| 115626 | sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ |
| 115627 | } |
| 115628 | res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0; |
| 115629 | } |
| 115630 | sqlite3ValueFree(pR); |
| 115631 | sqlite3ValueFree(pL); |
| 115632 | } |
| 115633 | return res; |
| 115634 | } |
| 115635 | |
| 115636 | /* |
| 115637 | ** Do a deep comparison of two expression trees. Return 0 if the two |
| @@ -115471,16 +115653,14 @@ | |
| 115653 | ** can be sure the expressions are the same. In the places where |
| 115654 | ** this routine is used, it does not hurt to get an extra 2 - that |
| 115655 | ** just might result in some slightly slower code. But returning |
| 115656 | ** an incorrect 0 or 1 could lead to a malfunction. |
| 115657 | ** |
| 115658 | ** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE |
| 115659 | ** terms in pA with bindings in pParse->pReprepare can be matched against |
| 115660 | ** literals in pB. The pParse->pVdbe->expmask bitmask is updated for |
| 115661 | ** each variable referenced. |
| 115662 | */ |
| 115663 | SQLITE_PRIVATE int sqlite3ExprCompare( |
| 115664 | const Parse *pParse, |
| 115665 | const Expr *pA, |
| 115666 | const Expr *pB, |
| @@ -115488,12 +115668,12 @@ | |
| 115668 | ){ |
| 115669 | u32 combinedFlags; |
| 115670 | if( pA==0 || pB==0 ){ |
| 115671 | return pB==pA ? 0 : 2; |
| 115672 | } |
| 115673 | if( pParse && pA->op==TK_VARIABLE ){ |
| 115674 | return exprCompareVariable(pParse, pA, pB); |
| 115675 | } |
| 115676 | combinedFlags = pA->flags | pB->flags; |
| 115677 | if( combinedFlags & EP_IntValue ){ |
| 115678 | if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ |
| 115679 | return 0; |
| @@ -115683,23 +115863,75 @@ | |
| 115863 | return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); |
| 115864 | } |
| 115865 | } |
| 115866 | return 0; |
| 115867 | } |
| 115868 | |
| 115869 | /* |
| 115870 | ** Return true if the boolean value of the expression is always either |
| 115871 | ** FALSE or NULL. |
| 115872 | */ |
| 115873 | static int sqlite3ExprIsNotTrue(Expr *pExpr){ |
| 115874 | int v; |
| 115875 | if( pExpr->op==TK_NULL ) return 1; |
| 115876 | if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1; |
| 115877 | v = 1; |
| 115878 | if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1; |
| 115879 | return 0; |
| 115880 | } |
| 115881 | |
| 115882 | /* |
| 115883 | ** Return true if the expression is one of the following: |
| 115884 | ** |
| 115885 | ** CASE WHEN x THEN y END |
| 115886 | ** CASE WHEN x THEN y ELSE NULL END |
| 115887 | ** CASE WHEN x THEN y ELSE false END |
| 115888 | ** iif(x,y) |
| 115889 | ** iif(x,y,NULL) |
| 115890 | ** iif(x,y,false) |
| 115891 | */ |
| 115892 | static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){ |
| 115893 | ExprList *pList; |
| 115894 | if( pExpr->op==TK_FUNCTION ){ |
| 115895 | const char *z = pExpr->u.zToken; |
| 115896 | FuncDef *pDef; |
| 115897 | if( (z[0]!='i' && z[0]!='I') ) return 0; |
| 115898 | if( pExpr->x.pList==0 ) return 0; |
| 115899 | pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0); |
| 115900 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 115901 | if( pDef==0 ) return 0; |
| 115902 | #else |
| 115903 | if( NEVER(pDef==0) ) return 0; |
| 115904 | #endif |
| 115905 | if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0; |
| 115906 | if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0; |
| 115907 | }else if( pExpr->op==TK_CASE ){ |
| 115908 | if( pExpr->pLeft!=0 ) return 0; |
| 115909 | }else{ |
| 115910 | return 0; |
| 115911 | } |
| 115912 | pList = pExpr->x.pList; |
| 115913 | assert( pList!=0 ); |
| 115914 | if( pList->nExpr==2 ) return 1; |
| 115915 | if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1; |
| 115916 | return 0; |
| 115917 | } |
| 115918 | |
| 115919 | /* |
| 115920 | ** Return true if we can prove the pE2 will always be true if pE1 is |
| 115921 | ** true. Return false if we cannot complete the proof or if pE2 might |
| 115922 | ** be false. Examples: |
| 115923 | ** |
| 115924 | ** pE1: x==5 pE2: x==5 Result: true |
| 115925 | ** pE1: x>0 pE2: x==5 Result: false |
| 115926 | ** pE1: x=21 pE2: x=21 OR y=43 Result: true |
| 115927 | ** pE1: x!=123 pE2: x IS NOT NULL Result: true |
| 115928 | ** pE1: x!=?1 pE2: x IS NOT NULL Result: true |
| 115929 | ** pE1: x IS NULL pE2: x IS NOT NULL Result: false |
| 115930 | ** pE1: x IS ?2 pE2: x IS NOT NULL Result: false |
| 115931 | ** pE1: iif(x,y) pE2: x Result: true |
| 115932 | ** PE1: iif(x,y,0) pE2: x Result: true |
| 115933 | ** |
| 115934 | ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has |
| 115935 | ** Expr.iTable<0 then assume a table number given by iTab. |
| 115936 | ** |
| 115937 | ** If pParse is not NULL, then the values of bound variables in pE1 are |
| @@ -115729,10 +115961,13 @@ | |
| 115961 | if( pE2->op==TK_NOTNULL |
| 115962 | && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0) |
| 115963 | ){ |
| 115964 | return 1; |
| 115965 | } |
| 115966 | if( sqlite3ExprIsIIF(pParse->db, pE1) ){ |
| 115967 | return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab); |
| 115968 | } |
| 115969 | return 0; |
| 115970 | } |
| 115971 | |
| 115972 | /* This is a helper function to impliesNotNullRow(). In this routine, |
| 115973 | ** set pWalker->eCode to one only if *both* of the input expressions |
| @@ -120689,12 +120924,12 @@ | |
| 120924 | int nIdxCol = 1; /* Number of columns in stat4 records */ |
| 120925 | |
| 120926 | char *zIndex; /* Index name */ |
| 120927 | Index *pIdx; /* Pointer to the index object */ |
| 120928 | int nSample; /* Number of samples */ |
| 120929 | i64 nByte; /* Bytes of space required */ |
| 120930 | i64 i; /* Bytes of space required */ |
| 120931 | tRowcnt *pSpace; /* Available allocated memory space */ |
| 120932 | u8 *pPtr; /* Available memory as a u8 for easier manipulation */ |
| 120933 | |
| 120934 | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| 120935 | if( zIndex==0 ) continue; |
| @@ -121140,19 +121375,10 @@ | |
| 121375 | rc = sqlite3Init(db, &zErrDyn); |
| 121376 | } |
| 121377 | sqlite3BtreeLeaveAll(db); |
| 121378 | assert( zErrDyn==0 || rc!=SQLITE_OK ); |
| 121379 | } |
| 121380 | if( rc ){ |
| 121381 | if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){ |
| 121382 | int iDb = db->nDb - 1; |
| 121383 | assert( iDb>=2 ); |
| 121384 | if( db->aDb[iDb].pBt ){ |
| @@ -121646,15 +121872,11 @@ | |
| 121872 | sqlite3 *db = pParse->db; /* Database handle */ |
| 121873 | char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */ |
| 121874 | int rc; /* Auth callback return code */ |
| 121875 | |
| 121876 | if( db->init.busy ) return SQLITE_OK; |
| 121877 | rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); |
| 121878 | if( rc==SQLITE_DENY ){ |
| 121879 | char *z = sqlite3_mprintf("%s.%s", zTab, zCol); |
| 121880 | if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z); |
| 121881 | sqlite3ErrorMsg(pParse, "access to %z is prohibited", z); |
| 121882 | pParse->rc = SQLITE_AUTH; |
| @@ -121757,15 +121979,11 @@ | |
| 121979 | testcase( zArg1==0 ); |
| 121980 | testcase( zArg2==0 ); |
| 121981 | testcase( zArg3==0 ); |
| 121982 | testcase( pParse->zAuthContext==0 ); |
| 121983 | |
| 121984 | rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext); |
| 121985 | if( rc==SQLITE_DENY ){ |
| 121986 | sqlite3ErrorMsg(pParse, "not authorized"); |
| 121987 | pParse->rc = SQLITE_AUTH; |
| 121988 | }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ |
| 121989 | rc = SQLITE_DENY; |
| @@ -121994,21 +122212,10 @@ | |
| 122212 | sqlite3VdbeJumpHere(v, addrRewind); |
| 122213 | } |
| 122214 | } |
| 122215 | sqlite3VdbeAddOp0(v, OP_Halt); |
| 122216 | |
| 122217 | /* The cookie mask contains one bit for each database file open. |
| 122218 | ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are |
| 122219 | ** set for each database that is used. Generate code to start a |
| 122220 | ** transaction on each used database and to verify the schema cookie |
| 122221 | ** on each used database. |
| @@ -122133,20 +122340,10 @@ | |
| 122340 | sqlite3DbFree(db, zSql); |
| 122341 | memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); |
| 122342 | pParse->nested--; |
| 122343 | } |
| 122344 | |
| 122345 | /* |
| 122346 | ** Locate the in-memory structure that describes a particular database |
| 122347 | ** table given the name of that table and (optionally) the name of the |
| 122348 | ** database containing the table. Return NULL if not found. |
| 122349 | ** |
| @@ -122161,17 +122358,10 @@ | |
| 122358 | Table *p = 0; |
| 122359 | int i; |
| 122360 | |
| 122361 | /* All mutexes are required for schema access. Make sure we hold them. */ |
| 122362 | assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); |
| 122363 | if( zDatabase ){ |
| 122364 | for(i=0; i<db->nDb; i++){ |
| 122365 | if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; |
| 122366 | } |
| 122367 | if( i>=db->nDb ){ |
| @@ -125826,13 +126016,10 @@ | |
| 126016 | |
| 126017 | assert( pTab!=0 ); |
| 126018 | if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 |
| 126019 | && db->init.busy==0 |
| 126020 | && pTblName!=0 |
| 126021 | ){ |
| 126022 | sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); |
| 126023 | goto exit_create_index; |
| 126024 | } |
| 126025 | #ifndef SQLITE_OMIT_VIEW |
| @@ -128224,10 +128411,11 @@ | |
| 128411 | ** 4) The table is a shadow table, the database connection is in |
| 128412 | ** defensive mode, and the current sqlite3_prepare() |
| 128413 | ** is for a top-level SQL statement. |
| 128414 | */ |
| 128415 | static int vtabIsReadOnly(Parse *pParse, Table *pTab){ |
| 128416 | assert( IsVirtual(pTab) ); |
| 128417 | if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ |
| 128418 | return 1; |
| 128419 | } |
| 128420 | |
| 128421 | /* Within triggers: |
| @@ -131716,11 +131904,17 @@ | |
| 131904 | #ifdef SQLITE_DEBUG |
| 131905 | /* |
| 131906 | ** Implementation of fpdecode(x,y,z) function. |
| 131907 | ** |
| 131908 | ** x is a real number that is to be decoded. y is the precision. |
| 131909 | ** z is the maximum real precision. Return a string that shows the |
| 131910 | ** results of the sqlite3FpDecode() function. |
| 131911 | ** |
| 131912 | ** Used for testing and debugging only, specifically testing and debugging |
| 131913 | ** of the sqlite3FpDecode() function. This SQL function does not appear |
| 131914 | ** in production builds. This function is not an API and is subject to |
| 131915 | ** modification or removal in future versions of SQLite. |
| 131916 | */ |
| 131917 | static void fpdecodeFunc( |
| 131918 | sqlite3_context *context, |
| 131919 | int argc, |
| 131920 | sqlite3_value **argv |
| @@ -131740,10 +131934,86 @@ | |
| 131934 | sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); |
| 131935 | }else{ |
| 131936 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); |
| 131937 | } |
| 131938 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| 131939 | } |
| 131940 | #endif /* SQLITE_DEBUG */ |
| 131941 | |
| 131942 | #ifdef SQLITE_DEBUG |
| 131943 | /* |
| 131944 | ** Implementation of parseuri(uri,flags) function. |
| 131945 | ** |
| 131946 | ** Required Arguments: |
| 131947 | ** "uri" The URI to parse. |
| 131948 | ** "flags" Bitmask of flags, as if to sqlite3_open_v2(). |
| 131949 | ** |
| 131950 | ** Additional arguments beyond the first two make calls to |
| 131951 | ** sqlite3_uri_key() for integers and sqlite3_uri_parameter for |
| 131952 | ** anything else. |
| 131953 | ** |
| 131954 | ** The result is a string showing the results of calling sqlite3ParseUri(). |
| 131955 | ** |
| 131956 | ** Used for testing and debugging only, specifically testing and debugging |
| 131957 | ** of the sqlite3ParseUri() function. This SQL function does not appear |
| 131958 | ** in production builds. This function is not an API and is subject to |
| 131959 | ** modification or removal in future versions of SQLite. |
| 131960 | */ |
| 131961 | static void parseuriFunc( |
| 131962 | sqlite3_context *ctx, |
| 131963 | int argc, |
| 131964 | sqlite3_value **argv |
| 131965 | ){ |
| 131966 | sqlite3_str *pResult; |
| 131967 | const char *zVfs; |
| 131968 | const char *zUri; |
| 131969 | unsigned int flgs; |
| 131970 | int rc; |
| 131971 | sqlite3_vfs *pVfs = 0; |
| 131972 | char *zFile = 0; |
| 131973 | char *zErr = 0; |
| 131974 | |
| 131975 | if( argc<2 ) return; |
| 131976 | pVfs = sqlite3_vfs_find(0); |
| 131977 | assert( pVfs ); |
| 131978 | zVfs = pVfs->zName; |
| 131979 | zUri = (const char*)sqlite3_value_text(argv[0]); |
| 131980 | if( zUri==0 ) return; |
| 131981 | flgs = (unsigned int)sqlite3_value_int(argv[1]); |
| 131982 | rc = sqlite3ParseUri(zVfs, zUri, &flgs, &pVfs, &zFile, &zErr); |
| 131983 | pResult = sqlite3_str_new(0); |
| 131984 | if( pResult ){ |
| 131985 | int i; |
| 131986 | sqlite3_str_appendf(pResult, "rc=%d", rc); |
| 131987 | sqlite3_str_appendf(pResult, ", flags=0x%x", flgs); |
| 131988 | sqlite3_str_appendf(pResult, ", vfs=%Q", pVfs ? pVfs->zName: 0); |
| 131989 | sqlite3_str_appendf(pResult, ", err=%Q", zErr); |
| 131990 | sqlite3_str_appendf(pResult, ", file=%Q", zFile); |
| 131991 | if( zFile ){ |
| 131992 | const char *z = zFile; |
| 131993 | z += sqlite3Strlen30(z)+1; |
| 131994 | while( z[0] ){ |
| 131995 | sqlite3_str_appendf(pResult, ", %Q", z); |
| 131996 | z += sqlite3Strlen30(z)+1; |
| 131997 | } |
| 131998 | for(i=2; i<argc; i++){ |
| 131999 | const char *zArg; |
| 132000 | if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ |
| 132001 | int k = sqlite3_value_int(argv[i]); |
| 132002 | sqlite3_str_appendf(pResult, ", '%d:%q'",k,sqlite3_uri_key(zFile, k)); |
| 132003 | }else if( (zArg = (const char*)sqlite3_value_text(argv[i]))!=0 ){ |
| 132004 | sqlite3_str_appendf(pResult, ", '%q:%q'", |
| 132005 | zArg, sqlite3_uri_parameter(zFile,zArg)); |
| 132006 | }else{ |
| 132007 | sqlite3_str_appendf(pResult, ", NULL"); |
| 132008 | } |
| 132009 | } |
| 132010 | } |
| 132011 | sqlite3_result_text(ctx, sqlite3_str_finish(pResult), -1, sqlite3_free); |
| 132012 | } |
| 132013 | sqlite3_free_filename(zFile); |
| 132014 | sqlite3_free(zErr); |
| 132015 | } |
| 132016 | #endif /* SQLITE_DEBUG */ |
| 132017 | |
| 132018 | /* |
| 132019 | ** All of the FuncDef structures in the aBuiltinFunc[] array above |
| @@ -131777,13 +132047,10 @@ | |
| 132047 | #endif |
| 132048 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 132049 | SFUNCTION(load_extension, 1, 0, 0, loadExt ), |
| 132050 | SFUNCTION(load_extension, 2, 0, 0, loadExt ), |
| 132051 | #endif |
| 132052 | #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS |
| 132053 | DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), |
| 132054 | DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), |
| 132055 | #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ |
| 132056 | INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), |
| @@ -131805,11 +132072,12 @@ | |
| 132072 | FUNCTION(max, -1, 1, 1, minmaxFunc ), |
| 132073 | FUNCTION(max, 0, 1, 1, 0 ), |
| 132074 | WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, |
| 132075 | SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), |
| 132076 | FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), |
| 132077 | FUNCTION2(subtype, 1, 0, 0, subtypeFunc, |
| 132078 | SQLITE_FUNC_TYPEOF|SQLITE_SUBTYPE), |
| 132079 | FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), |
| 132080 | FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN), |
| 132081 | FUNCTION(instr, 2, 0, 0, instrFunc ), |
| 132082 | FUNCTION(printf, -1, 0, 0, printfFunc ), |
| 132083 | FUNCTION(format, -1, 0, 0, printfFunc ), |
| @@ -131816,10 +132084,11 @@ | |
| 132084 | FUNCTION(unicode, 1, 0, 0, unicodeFunc ), |
| 132085 | FUNCTION(char, -1, 0, 0, charFunc ), |
| 132086 | FUNCTION(abs, 1, 0, 0, absFunc ), |
| 132087 | #ifdef SQLITE_DEBUG |
| 132088 | FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ), |
| 132089 | FUNCTION(parseuri, -1, 0, 0, parseuriFunc ), |
| 132090 | #endif |
| 132091 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 132092 | FUNCTION(round, 1, 0, 0, roundFunc ), |
| 132093 | FUNCTION(round, 2, 0, 0, roundFunc ), |
| 132094 | #endif |
| @@ -131910,15 +132179,18 @@ | |
| 132179 | MFUNCTION(atanh, 1, atanh, math1Func ), |
| 132180 | #endif |
| 132181 | MFUNCTION(sqrt, 1, sqrt, math1Func ), |
| 132182 | MFUNCTION(radians, 1, degToRad, math1Func ), |
| 132183 | MFUNCTION(degrees, 1, radToDeg, math1Func ), |
| 132184 | MFUNCTION(pi, 0, 0, piFunc ), |
| 132185 | #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ |
| 132186 | FUNCTION(sign, 1, 0, 0, signFunc ), |
| 132187 | INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), |
| 132188 | INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ), |
| 132189 | INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), |
| 132190 | INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ), |
| 132191 | INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ), |
| 132192 | }; |
| 132193 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 132194 | sqlite3AlterFunctions(); |
| 132195 | #endif |
| 132196 | sqlite3WindowFunctions(); |
| @@ -140427,16 +140699,10 @@ | |
| 140699 | if( db->autoCommit==0 ){ |
| 140700 | /* Foreign key support may not be enabled or disabled while not |
| 140701 | ** in auto-commit mode. */ |
| 140702 | mask &= ~(SQLITE_ForeignKeys); |
| 140703 | } |
| 140704 | |
| 140705 | if( sqlite3GetBoolean(zRight, 0) ){ |
| 140706 | if( (mask & SQLITE_WriteSchema)==0 |
| 140707 | || (db->flags & SQLITE_Defensive)==0 |
| 140708 | ){ |
| @@ -140568,11 +140834,12 @@ | |
| 140834 | pTab = sqliteHashData(k); |
| 140835 | if( pTab->nCol==0 ){ |
| 140836 | char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); |
| 140837 | if( zSql ){ |
| 140838 | sqlite3_stmt *pDummy = 0; |
| 140839 | (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG, |
| 140840 | &pDummy, 0); |
| 140841 | (void)sqlite3_finalize(pDummy); |
| 140842 | sqlite3DbFree(db, zSql); |
| 140843 | } |
| 140844 | if( db->mallocFailed ){ |
| 140845 | sqlite3ErrorMsg(db->pParse, "out of memory"); |
| @@ -141044,10 +141311,11 @@ | |
| 141311 | } |
| 141312 | aRoot[0] = cnt; |
| 141313 | |
| 141314 | /* Make sure sufficient number of registers have been allocated */ |
| 141315 | sqlite3TouchRegister(pParse, 8+cnt); |
| 141316 | sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); |
| 141317 | sqlite3ClearTempRegCache(pParse); |
| 141318 | |
| 141319 | /* Do the b-tree integrity checks */ |
| 141320 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141321 | sqlite3VdbeChangeP5(v, (u8)i); |
| @@ -142668,18 +142936,11 @@ | |
| 142936 | encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; |
| 142937 | if( encoding==0 ) encoding = SQLITE_UTF8; |
| 142938 | #else |
| 142939 | encoding = SQLITE_UTF8; |
| 142940 | #endif |
| 142941 | sqlite3SetTextEncoding(db, encoding); |
| 142942 | }else{ |
| 142943 | /* If opening an attached database, the encoding much match ENC(db) */ |
| 142944 | if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){ |
| 142945 | sqlite3SetString(pzErrMsg, db, "attached databases must use the same" |
| 142946 | " text encoding as main database"); |
| @@ -143369,16 +143630,28 @@ | |
| 143630 | #endif |
| 143631 | *ppStmt = 0; |
| 143632 | if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ |
| 143633 | return SQLITE_MISUSE_BKPT; |
| 143634 | } |
| 143635 | |
| 143636 | /* Make sure nBytes is non-negative and correct. It should be the |
| 143637 | ** number of bytes until the end of the input buffer or until the first |
| 143638 | ** U+0000 character. If the input nBytes is odd, convert it into |
| 143639 | ** an even number. If the input nBytes is negative, then the input |
| 143640 | ** must be terminated by at least one U+0000 character */ |
| 143641 | if( nBytes>=0 ){ |
| 143642 | int sz; |
| 143643 | const char *z = (const char*)zSql; |
| 143644 | for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){} |
| 143645 | nBytes = sz; |
| 143646 | }else{ |
| 143647 | int sz; |
| 143648 | const char *z = (const char*)zSql; |
| 143649 | for(sz=0; z[sz]!=0 || z[sz+1]!=0; sz += 2){} |
| 143650 | nBytes = sz; |
| 143651 | } |
| 143652 | |
| 143653 | sqlite3_mutex_enter(db->mutex); |
| 143654 | zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); |
| 143655 | if( zSql8 ){ |
| 143656 | rc = sqlite3LockAndPrepare(db, zSql8, -1, prepFlags, 0, ppStmt, &zTail8); |
| 143657 | } |
| @@ -143388,11 +143661,11 @@ | |
| 143661 | ** equivalent pointer into the UTF-16 string by counting the unicode |
| 143662 | ** characters between zSql8 and zTail8, and then returning a pointer |
| 143663 | ** the same number of characters into the UTF-16 string. |
| 143664 | */ |
| 143665 | int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); |
| 143666 | *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, nBytes, chars_parsed); |
| 143667 | } |
| 143668 | sqlite3DbFree(db, zSql8); |
| 143669 | rc = sqlite3ApiExit(db, rc); |
| 143670 | sqlite3_mutex_leave(db->mutex); |
| 143671 | return rc; |
| @@ -147361,36 +147634,36 @@ | |
| 147634 | return pExpr; |
| 147635 | } |
| 147636 | if( pSubst->isOuterJoin ){ |
| 147637 | ExprSetProperty(pNew, EP_CanBeNull); |
| 147638 | } |
| 147639 | if( pNew->op==TK_TRUEFALSE ){ |
| 147640 | pNew->u.iValue = sqlite3ExprTruthValue(pNew); |
| 147641 | pNew->op = TK_INTEGER; |
| 147642 | ExprSetProperty(pNew, EP_IntValue); |
| 147643 | } |
| 147644 | |
| 147645 | /* Ensure that the expression now has an implicit collation sequence, |
| 147646 | ** just as it did when it was a column of a view or sub-query. */ |
| 147647 | { |
| 147648 | CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew); |
| 147649 | CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, |
| 147650 | pSubst->pCList->a[iColumn].pExpr |
| 147651 | ); |
| 147652 | if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){ |
| 147653 | pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew, |
| 147654 | (pColl ? pColl->zName : "BINARY") |
| 147655 | ); |
| 147656 | } |
| 147657 | } |
| 147658 | ExprClearProperty(pNew, EP_Collate); |
| 147659 | if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ |
| 147660 | sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, |
| 147661 | pExpr->flags & (EP_OuterON|EP_InnerON)); |
| 147662 | } |
| 147663 | sqlite3ExprDelete(db, pExpr); |
| 147664 | pExpr = pNew; |
| 147665 | } |
| 147666 | } |
| 147667 | }else{ |
| 147668 | if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){ |
| 147669 | pExpr->iTable = pSubst->iNewTable; |
| @@ -148123,20 +148396,20 @@ | |
| 148396 | } |
| 148397 | |
| 148398 | /* Transfer the FROM clause terms from the subquery into the |
| 148399 | ** outer query. |
| 148400 | */ |
| 148401 | iNewParent = pSubSrc->a[0].iCursor; |
| 148402 | for(i=0; i<nSubSrc; i++){ |
| 148403 | SrcItem *pItem = &pSrc->a[i+iFrom]; |
| 148404 | assert( pItem->fg.isTabFunc==0 ); |
| 148405 | assert( pItem->fg.isSubquery |
| 148406 | || pItem->fg.fixedSchema |
| 148407 | || pItem->u4.zDatabase==0 ); |
| 148408 | if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); |
| 148409 | *pItem = pSubSrc->a[i]; |
| 148410 | pItem->fg.jointype |= ltorj; |
| 148411 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 148412 | } |
| 148413 | pSrc->a[iFrom].fg.jointype &= JT_LTORJ; |
| 148414 | pSrc->a[iFrom].fg.jointype |= jointype | ltorj; |
| 148415 | |
| @@ -148172,10 +148445,11 @@ | |
| 148445 | pSub->pOrderBy = 0; |
| 148446 | } |
| 148447 | pWhere = pSub->pWhere; |
| 148448 | pSub->pWhere = 0; |
| 148449 | if( isOuterJoin>0 ){ |
| 148450 | assert( pSubSrc->nSrc==1 ); |
| 148451 | sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); |
| 148452 | } |
| 148453 | if( pWhere ){ |
| 148454 | if( pParent->pWhere ){ |
| 148455 | pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere); |
| @@ -151271,11 +151545,11 @@ | |
| 151545 | sqlite3TreeViewSelect(0, p, 0); |
| 151546 | } |
| 151547 | #endif |
| 151548 | assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); |
| 151549 | }else{ |
| 151550 | TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n")); |
| 151551 | } |
| 151552 | |
| 151553 | /* Convert unused result columns of the subquery into simple NULL |
| 151554 | ** expressions, to avoid unneeded searching and computation. |
| 151555 | ** tag-select-0440 |
| @@ -156980,10 +157254,11 @@ | |
| 157254 | assert( sParse.zErrMsg==0 ); |
| 157255 | if( !pTab->aCol ){ |
| 157256 | Table *pNew = sParse.pNewTable; |
| 157257 | Index *pIdx; |
| 157258 | pTab->aCol = pNew->aCol; |
| 157259 | assert( IsOrdinaryTable(pNew) ); |
| 157260 | sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); |
| 157261 | pTab->nNVCol = pTab->nCol = pNew->nCol; |
| 157262 | pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); |
| 157263 | pNew->nCol = 0; |
| 157264 | pNew->aCol = 0; |
| @@ -158044,13 +158319,21 @@ | |
| 158319 | SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( |
| 158320 | const Parse *pParse, /* Parse context */ |
| 158321 | const WhereInfo *pWInfo, /* WHERE clause */ |
| 158322 | const WhereLevel *pLevel /* Bloom filter on this level */ |
| 158323 | ); |
| 158324 | SQLITE_PRIVATE void sqlite3WhereAddExplainText( |
| 158325 | Parse *pParse, /* Parse context */ |
| 158326 | int addr, |
| 158327 | SrcList *pTabList, /* Table list this loop refers to */ |
| 158328 | WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ |
| 158329 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ |
| 158330 | ); |
| 158331 | #else |
| 158332 | # define sqlite3WhereExplainOneScan(u,v,w,x) 0 |
| 158333 | # define sqlite3WhereExplainBloomFilter(u,v,w) 0 |
| 158334 | # define sqlite3WhereAddExplainText(u,v,w,x,y) |
| 158335 | #endif /* SQLITE_OMIT_EXPLAIN */ |
| 158336 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 158337 | SQLITE_PRIVATE void sqlite3WhereAddScanStatus( |
| 158338 | Vdbe *v, /* Vdbe to add scanstatus entry to */ |
| 158339 | SrcList *pSrclist, /* FROM clause pLvl reads data from */ |
| @@ -158248,42 +158531,42 @@ | |
| 158531 | } |
| 158532 | sqlite3_str_append(pStr, ")", 1); |
| 158533 | } |
| 158534 | |
| 158535 | /* |
| 158536 | ** This function sets the P4 value of an existing OP_Explain opcode to |
| 158537 | ** text describing the loop in pLevel. If the OP_Explain opcode already has |
| 158538 | ** a P4 value, it is freed before it is overwritten. |
| 158539 | */ |
| 158540 | SQLITE_PRIVATE void sqlite3WhereAddExplainText( |
| 158541 | Parse *pParse, /* Parse context */ |
| 158542 | int addr, /* Address of OP_Explain opcode */ |
| 158543 | SrcList *pTabList, /* Table list this loop refers to */ |
| 158544 | WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ |
| 158545 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ |
| 158546 | ){ |
| 158547 | #if !defined(SQLITE_DEBUG) |
| 158548 | if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) |
| 158549 | #endif |
| 158550 | { |
| 158551 | VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr); |
| 158552 | |
| 158553 | SrcItem *pItem = &pTabList->a[pLevel->iFrom]; |
| 158554 | sqlite3 *db = pParse->db; /* Database handle */ |
| 158555 | int isSearch; /* True for a SEARCH. False for SCAN. */ |
| 158556 | WhereLoop *pLoop; /* The controlling WhereLoop object */ |
| 158557 | u32 flags; /* Flags that describe this loop */ |
| 158558 | #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) |
| 158559 | char *zMsg; /* Text to add to EQP output */ |
| 158560 | #endif |
| 158561 | StrAccum str; /* EQP output string */ |
| 158562 | char zBuf[100]; /* Initial space for EQP output string */ |
| 158563 | |
| 158564 | if( db->mallocFailed ) return; |
| 158565 | |
| 158566 | pLoop = pLevel->pWLoop; |
| 158567 | flags = pLoop->wsFlags; |
| 158568 | |
| 158569 | isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 |
| 158570 | || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) |
| 158571 | || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); |
| 158572 | |
| @@ -158303,11 +158586,11 @@ | |
| 158586 | } |
| 158587 | }else if( flags & WHERE_PARTIALIDX ){ |
| 158588 | zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; |
| 158589 | }else if( flags & WHERE_AUTO_INDEX ){ |
| 158590 | zFmt = "AUTOMATIC COVERING INDEX"; |
| 158591 | }else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ |
| 158592 | zFmt = "COVERING INDEX %s"; |
| 158593 | }else{ |
| 158594 | zFmt = "INDEX %s"; |
| 158595 | } |
| 158596 | if( zFmt ){ |
| @@ -158355,15 +158638,54 @@ | |
| 158638 | sqlite3LogEstToInt(pLoop->nOut)); |
| 158639 | }else{ |
| 158640 | sqlite3_str_append(&str, " (~1 row)", 9); |
| 158641 | } |
| 158642 | #endif |
| 158643 | #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) |
| 158644 | zMsg = sqlite3StrAccumFinish(&str); |
| 158645 | sqlite3ExplainBreakpoint("",zMsg); |
| 158646 | #endif |
| 158647 | |
| 158648 | assert( pOp->opcode==OP_Explain ); |
| 158649 | assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 ); |
| 158650 | sqlite3DbFree(db, pOp->p4.z); |
| 158651 | pOp->p4type = P4_DYNAMIC; |
| 158652 | pOp->p4.z = sqlite3StrAccumFinish(&str); |
| 158653 | } |
| 158654 | } |
| 158655 | |
| 158656 | |
| 158657 | /* |
| 158658 | ** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN |
| 158659 | ** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG |
| 158660 | ** was defined at compile-time. If it is not a no-op, a single OP_Explain |
| 158661 | ** opcode is added to the output to describe the table scan strategy in pLevel. |
| 158662 | ** |
| 158663 | ** If an OP_Explain opcode is added to the VM, its address is returned. |
| 158664 | ** Otherwise, if no OP_Explain is coded, zero is returned. |
| 158665 | */ |
| 158666 | SQLITE_PRIVATE int sqlite3WhereExplainOneScan( |
| 158667 | Parse *pParse, /* Parse context */ |
| 158668 | SrcList *pTabList, /* Table list this loop refers to */ |
| 158669 | WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ |
| 158670 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ |
| 158671 | ){ |
| 158672 | int ret = 0; |
| 158673 | #if !defined(SQLITE_DEBUG) |
| 158674 | if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) |
| 158675 | #endif |
| 158676 | { |
| 158677 | if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0 |
| 158678 | && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 |
| 158679 | ){ |
| 158680 | Vdbe *v = pParse->pVdbe; |
| 158681 | int addr = sqlite3VdbeCurrentAddr(v); |
| 158682 | ret = sqlite3VdbeAddOp3( |
| 158683 | v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun |
| 158684 | ); |
| 158685 | sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags); |
| 158686 | } |
| 158687 | } |
| 158688 | return ret; |
| 158689 | } |
| 158690 | |
| 158691 | /* |
| @@ -158458,13 +158780,14 @@ | |
| 158780 | if( wsFlags & WHERE_INDEXED ){ |
| 158781 | sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); |
| 158782 | } |
| 158783 | }else{ |
| 158784 | int addr; |
| 158785 | VdbeOp *pOp; |
| 158786 | assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); |
| 158787 | addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; |
| 158788 | pOp = sqlite3VdbeGetOp(v, addr-1); |
| 158789 | assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); |
| 158790 | assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); |
| 158791 | sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); |
| 158792 | } |
| 158793 | } |
| @@ -158713,10 +159036,11 @@ | |
| 159036 | if( pOrigLhs ){ |
| 159037 | sqlite3ExprListDelete(db, pOrigLhs); |
| 159038 | pNew->pLeft->x.pList = pLhs; |
| 159039 | } |
| 159040 | pSelect->pEList = pRhs; |
| 159041 | pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */ |
| 159042 | if( pLhs && pLhs->nExpr==1 ){ |
| 159043 | /* Take care here not to generate a TK_VECTOR containing only a |
| 159044 | ** single value. Since the parser never creates such a vector, some |
| 159045 | ** of the subroutines do not handle this case. */ |
| 159046 | Expr *p = pLhs->a[0].pExpr; |
| @@ -161260,24 +161584,29 @@ | |
| 161584 | }else if( op==TK_STRING ){ |
| 161585 | assert( !ExprHasProperty(pRight, EP_IntValue) ); |
| 161586 | z = (u8*)pRight->u.zToken; |
| 161587 | } |
| 161588 | if( z ){ |
| 161589 | /* Count the number of prefix bytes prior to the first wildcard. |
| 161590 | ** or U+fffd character. If the underlying database has a UTF16LE |
| 161591 | ** encoding, then only consider ASCII characters. Note that the |
| 161592 | ** encoding of z[] is UTF8 - we are dealing with only UTF8 here in |
| 161593 | ** this code, but the database engine itself might be processing |
| 161594 | ** content using a different encoding. */ |
| 161595 | cnt = 0; |
| 161596 | while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ |
| 161597 | cnt++; |
| 161598 | if( c==wc[3] && z[cnt]>0 && z[cnt]<0x80 ){ |
| 161599 | cnt++; |
| 161600 | }else if( c>=0x80 ){ |
| 161601 | const u8 *z2 = z+cnt-1; |
| 161602 | if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){ |
| 161603 | cnt--; |
| 161604 | break; |
| 161605 | }else{ |
| 161606 | cnt = (int)(z2-z); |
| 161607 | } |
| 161608 | } |
| 161609 | } |
| 161610 | |
| 161611 | /* The optimization is possible only if (1) the pattern does not begin |
| 161612 | ** with a wildcard and if (2) the non-wildcard prefix does not end with |
| @@ -161285,11 +161614,11 @@ | |
| 161614 | ** a single escape character. The second condition is necessary so |
| 161615 | ** that we can increment the prefix key to find an upper bound for the |
| 161616 | ** range search. The third is because the caller assumes that the pattern |
| 161617 | ** consists of at least one character after all escapes have been |
| 161618 | ** removed. */ |
| 161619 | if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && ALWAYS(255!=(u8)z[cnt-1]) ){ |
| 161620 | Expr *pPrefix; |
| 161621 | |
| 161622 | /* A "complete" match if the pattern ends with "*" or "%" */ |
| 161623 | *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; |
| 161624 | |
| @@ -163780,11 +164109,11 @@ | |
| 164109 | || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 164110 | ){ |
| 164111 | return 0; |
| 164112 | } |
| 164113 | if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 |
| 164114 | && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON)) |
| 164115 | ){ |
| 164116 | return 0; |
| 164117 | } |
| 164118 | return 1; |
| 164119 | } |
| @@ -164577,13 +164906,15 @@ | |
| 164906 | ** Whether or not an error is returned, it is the responsibility of the |
| 164907 | ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates |
| 164908 | ** that this is required. |
| 164909 | */ |
| 164910 | static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ |
| 164911 | int rc; |
| 164912 | sqlite3_vtab *pVtab; |
| 164913 | |
| 164914 | assert( IsVirtual(pTab) ); |
| 164915 | pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; |
| 164916 | whereTraceIndexInfoInputs(p, pTab); |
| 164917 | pParse->db->nSchemaLock++; |
| 164918 | rc = pVtab->pModule->xBestIndex(pVtab, p); |
| 164919 | pParse->db->nSchemaLock--; |
| 164920 | whereTraceIndexInfoOutputs(p, pTab); |
| @@ -165271,11 +165602,11 @@ | |
| 165602 | return rc; |
| 165603 | } |
| 165604 | #endif /* SQLITE_ENABLE_STAT4 */ |
| 165605 | |
| 165606 | |
| 165607 | #if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG) |
| 165608 | /* |
| 165609 | ** Print the content of a WhereTerm object |
| 165610 | */ |
| 165611 | SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ |
| 165612 | if( pTerm==0 ){ |
| @@ -165314,10 +165645,13 @@ | |
| 165645 | sqlite3DebugPrintf(" iParent=%d", pTerm->iParent); |
| 165646 | } |
| 165647 | sqlite3DebugPrintf("\n"); |
| 165648 | sqlite3TreeViewExpr(0, pTerm->pExpr, 0); |
| 165649 | } |
| 165650 | } |
| 165651 | SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){ |
| 165652 | sqlite3WhereTermPrint(pTerm, 0); |
| 165653 | } |
| 165654 | #endif |
| 165655 | |
| 165656 | #ifdef WHERETRACE_ENABLED |
| 165657 | /* |
| @@ -165522,11 +165856,11 @@ | |
| 165856 | ** Return TRUE if X is a proper subset of Y but is of equal or less cost. |
| 165857 | ** In other words, return true if all constraints of X are also part of Y |
| 165858 | ** and Y has additional constraints that might speed the search that X lacks |
| 165859 | ** but the cost of running X is not more than the cost of running Y. |
| 165860 | ** |
| 165861 | ** In other words, return true if the cost relationship between X and Y |
| 165862 | ** is inverted and needs to be adjusted. |
| 165863 | ** |
| 165864 | ** Case 1: |
| 165865 | ** |
| 165866 | ** (1a) X and Y use the same index. |
| @@ -166500,11 +166834,10 @@ | |
| 166834 | pParse = pWC->pWInfo->pParse; |
| 166835 | while( pWhere->op==TK_AND ){ |
| 166836 | if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; |
| 166837 | pWhere = pWhere->pRight; |
| 166838 | } |
| 166839 | for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 166840 | Expr *pExpr; |
| 166841 | pExpr = pTerm->pExpr; |
| 166842 | if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) |
| 166843 | && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) |
| @@ -169140,10 +169473,11 @@ | |
| 169473 | hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; |
| 169474 | for(i=pWInfo->nLevel-1; i>=1; i--){ |
| 169475 | WhereTerm *pTerm, *pEnd; |
| 169476 | SrcItem *pItem; |
| 169477 | WhereLoop *pLoop; |
| 169478 | Bitmask m1; |
| 169479 | pLoop = pWInfo->a[i].pWLoop; |
| 169480 | pItem = &pWInfo->pTabList->a[pLoop->iTab]; |
| 169481 | if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; |
| 169482 | if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 |
| 169483 | && (pLoop->wsFlags & WHERE_ONEROW)==0 |
| @@ -169160,17 +169494,20 @@ | |
| 169494 | break; |
| 169495 | } |
| 169496 | } |
| 169497 | if( hasRightJoin |
| 169498 | && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 169499 | && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor) |
| 169500 | ){ |
| 169501 | break; /* restriction (5) */ |
| 169502 | } |
| 169503 | } |
| 169504 | if( pTerm<pEnd ) continue; |
| 169505 | WHERETRACE(0xffffffff,("-> omit unused FROM-clause term %c\n",pLoop->cId)); |
| 169506 | m1 = MASKBIT(i)-1; |
| 169507 | testcase( ((pWInfo->revMask>>1) & ~m1)!=0 ); |
| 169508 | pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); |
| 169509 | notReady &= ~pLoop->maskSelf; |
| 169510 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| 169511 | if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ |
| 169512 | pTerm->wtFlags |= TERM_CODED; |
| 169513 | } |
| @@ -169237,62 +169574,10 @@ | |
| 169574 | nSearch += pLoop->nOut; |
| 169575 | if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta; |
| 169576 | } |
| 169577 | } |
| 169578 | |
| 169579 | /* |
| 169580 | ** The index pIdx is used by a query and contains one or more expressions. |
| 169581 | ** In other words pIdx is an index on an expression. iIdxCur is the cursor |
| 169582 | ** number for the index and iDataCur is the cursor number for the corresponding |
| 169583 | ** table. |
| @@ -169322,16 +169607,10 @@ | |
| 169607 | pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); |
| 169608 | }else{ |
| 169609 | continue; |
| 169610 | } |
| 169611 | if( sqlite3ExprIsConstant(0,pExpr) ) continue; |
| 169612 | p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); |
| 169613 | if( p==0 ) break; |
| 169614 | p->pIENext = pParse->pIdxEpr; |
| 169615 | #ifdef WHERETRACE_ENABLED |
| 169616 | if( sqlite3WhereTrace & 0x200 ){ |
| @@ -170434,18 +170713,32 @@ | |
| 170713 | x = sqlite3TableColumnToIndex(pIdx, x); |
| 170714 | if( x>=0 ){ |
| 170715 | pOp->p2 = x; |
| 170716 | pOp->p1 = pLevel->iIdxCur; |
| 170717 | OpcodeRewriteTrace(db, k, pOp); |
| 170718 | }else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ |
| 170719 | if( pLoop->wsFlags & WHERE_IDX_ONLY ){ |
| 170720 | /* An error. pLoop is supposed to be a covering index loop, |
| 170721 | ** and yet the VM code refers to a column of the table that |
| 170722 | ** is not part of the index. */ |
| 170723 | sqlite3ErrorMsg(pParse, "internal query planner error"); |
| 170724 | pParse->rc = SQLITE_INTERNAL; |
| 170725 | }else{ |
| 170726 | /* The WHERE_EXPRIDX flag is set by the planner when it is likely |
| 170727 | ** that pLoop is a covering index loop, but it is not possible |
| 170728 | ** to be 100% sure. In this case, any OP_Explain opcode |
| 170729 | ** corresponding to this loop describes the index as a "COVERING |
| 170730 | ** INDEX". But, pOp proves that pLoop is not actually a covering |
| 170731 | ** index loop. So clear the WHERE_EXPRIDX flag and rewrite the |
| 170732 | ** text that accompanies the OP_Explain opcode, if any. */ |
| 170733 | pLoop->wsFlags &= ~WHERE_EXPRIDX; |
| 170734 | sqlite3WhereAddExplainText(pParse, |
| 170735 | pLevel->addrBody-1, |
| 170736 | pTabList, |
| 170737 | pLevel, |
| 170738 | pWInfo->wctrlFlags |
| 170739 | ); |
| 170740 | } |
| 170741 | } |
| 170742 | }else if( pOp->opcode==OP_Rowid ){ |
| 170743 | pOp->p1 = pLevel->iIdxCur; |
| 170744 | pOp->opcode = OP_IdxRowid; |
| @@ -172149,10 +172442,11 @@ | |
| 172442 | for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ |
| 172443 | FuncDef *pFunc = pWin->pWFunc; |
| 172444 | int regArg; |
| 172445 | int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); |
| 172446 | int i; |
| 172447 | int addrIf = 0; |
| 172448 | |
| 172449 | assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED ); |
| 172450 | |
| 172451 | /* All OVER clauses in the same window function aggregate step must |
| 172452 | ** be the same. */ |
| @@ -172164,10 +172458,22 @@ | |
| 172458 | }else{ |
| 172459 | sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i); |
| 172460 | } |
| 172461 | } |
| 172462 | regArg = reg; |
| 172463 | |
| 172464 | if( pWin->pFilter ){ |
| 172465 | int regTmp; |
| 172466 | assert( ExprUseXList(pWin->pOwner) ); |
| 172467 | assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); |
| 172468 | assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); |
| 172469 | regTmp = sqlite3GetTempReg(pParse); |
| 172470 | sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); |
| 172471 | addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); |
| 172472 | VdbeCoverage(v); |
| 172473 | sqlite3ReleaseTempReg(pParse, regTmp); |
| 172474 | } |
| 172475 | |
| 172476 | if( pMWin->regStartRowid==0 |
| 172477 | && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) |
| 172478 | && (pWin->eStart!=TK_UNBOUNDED) |
| 172479 | ){ |
| @@ -172184,29 +172490,17 @@ | |
| 172490 | sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp); |
| 172491 | sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); |
| 172492 | } |
| 172493 | sqlite3VdbeJumpHere(v, addrIsNull); |
| 172494 | }else if( pWin->regApp ){ |
| 172495 | assert( pWin->pFilter==0 ); |
| 172496 | assert( pFunc->zName==nth_valueName |
| 172497 | || pFunc->zName==first_valueName |
| 172498 | ); |
| 172499 | assert( bInverse==0 || bInverse==1 ); |
| 172500 | sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); |
| 172501 | }else if( pFunc->xSFunc!=noopStepFunc ){ |
| 172502 | if( pWin->bExprArgs ){ |
| 172503 | int iOp = sqlite3VdbeCurrentAddr(v); |
| 172504 | int iEnd; |
| 172505 | |
| 172506 | assert( ExprUseXList(pWin->pOwner) ); |
| @@ -172233,12 +172527,13 @@ | |
| 172527 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172528 | sqlite3VdbeChangeP5(v, (u8)nArg); |
| 172529 | if( pWin->bExprArgs ){ |
| 172530 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172531 | } |
| 172532 | } |
| 172533 | |
| 172534 | if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); |
| 172535 | } |
| 172536 | } |
| 172537 | |
| 172538 | /* |
| 172539 | ** Values that may be passed as the second argument to windowCodeOp(). |
| @@ -173660,10 +173955,17 @@ | |
| 173955 | ** Then the "b" IdList records the list "a,b,c". |
| 173956 | */ |
| 173957 | struct TrigEvent { int a; IdList * b; }; |
| 173958 | |
| 173959 | struct FrameBound { int eType; Expr *pExpr; }; |
| 173960 | |
| 173961 | /* |
| 173962 | ** Generate a syntax error |
| 173963 | */ |
| 173964 | static void parserSyntaxError(Parse *pParse, Token *p){ |
| 173965 | sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p); |
| 173966 | } |
| 173967 | |
| 173968 | /* |
| 173969 | ** Disable lookaside memory allocation for objects that might be |
| 173970 | ** shared across database connections. |
| 173971 | */ |
| @@ -177553,11 +177855,15 @@ | |
| 177855 | } |
| 177856 | break; |
| 177857 | case 84: /* cmd ::= select */ |
| 177858 | { |
| 177859 | SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; |
| 177860 | if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0 |
| 177861 | || sqlite3ReadSchema(pParse)==SQLITE_OK |
| 177862 | ){ |
| 177863 | sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); |
| 177864 | } |
| 177865 | sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); |
| 177866 | } |
| 177867 | break; |
| 177868 | case 85: /* select ::= WITH wqlist selectnowith */ |
| 177869 | {yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} |
| @@ -178024,11 +178330,11 @@ | |
| 178330 | ** that look like this: #1 #2 ... These terms refer to registers |
| 178331 | ** in the virtual machine. #N is the N-th register. */ |
| 178332 | Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ |
| 178333 | assert( t.n>=2 ); |
| 178334 | if( pParse->nested==0 ){ |
| 178335 | parserSyntaxError(pParse, &t); |
| 178336 | yymsp[0].minor.yy454 = 0; |
| 178337 | }else{ |
| 178338 | yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); |
| 178339 | if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); |
| 178340 | } |
| @@ -178872,11 +179178,11 @@ | |
| 179178 | #define TOKEN yyminor |
| 179179 | /************ Begin %syntax_error code ****************************************/ |
| 179180 | |
| 179181 | UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ |
| 179182 | if( TOKEN.z[0] ){ |
| 179183 | parserSyntaxError(pParse, &TOKEN); |
| 179184 | }else{ |
| 179185 | sqlite3ErrorMsg(pParse, "incomplete input"); |
| 179186 | } |
| 179187 | /************ End %syntax_error code ******************************************/ |
| 179188 | sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| @@ -180363,11 +180669,13 @@ | |
| 180669 | } |
| 180670 | if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ |
| 180671 | if( pParse->zErrMsg==0 ){ |
| 180672 | pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); |
| 180673 | } |
| 180674 | if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){ |
| 180675 | sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); |
| 180676 | } |
| 180677 | nErr++; |
| 180678 | } |
| 180679 | pParse->zTail = zSql; |
| 180680 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 180681 | sqlite3_free(pParse->apVtabLock); |
| @@ -181071,36 +181379,10 @@ | |
| 181379 | ** all database files specified with a relative pathname. |
| 181380 | ** |
| 181381 | ** See also the "PRAGMA data_store_directory" SQL command. |
| 181382 | */ |
| 181383 | SQLITE_API char *sqlite3_data_directory = 0; |
| 181384 | |
| 181385 | /* |
| 181386 | ** Initialize SQLite. |
| 181387 | ** |
| 181388 | ** This routine must be called to initialize the memory allocation, |
| @@ -181292,17 +181574,10 @@ | |
| 181574 | if( bRunExtraInit ){ |
| 181575 | int SQLITE_EXTRA_INIT(const char*); |
| 181576 | rc = SQLITE_EXTRA_INIT(0); |
| 181577 | } |
| 181578 | #endif |
| 181579 | return rc; |
| 181580 | } |
| 181581 | |
| 181582 | /* |
| 181583 | ** Undo the effects of sqlite3_initialize(). Must not be called while |
| @@ -182369,14 +182644,10 @@ | |
| 182644 | #endif |
| 182645 | |
| 182646 | sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ |
| 182647 | sqlite3ValueFree(db->pErr); |
| 182648 | sqlite3CloseExtensions(db); |
| 182649 | |
| 182650 | db->eOpenState = SQLITE_STATE_ERROR; |
| 182651 | |
| 182652 | /* The temp-database schema is allocated differently from the other schema |
| 182653 | ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). |
| @@ -183875,12 +184146,12 @@ | |
| 184146 | } |
| 184147 | oldLimit = db->aLimit[limitId]; |
| 184148 | if( newLimit>=0 ){ /* IMP: R-52476-28732 */ |
| 184149 | if( newLimit>aHardLimit[limitId] ){ |
| 184150 | newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ |
| 184151 | }else if( newLimit<SQLITE_MIN_LENGTH && limitId==SQLITE_LIMIT_LENGTH ){ |
| 184152 | newLimit = SQLITE_MIN_LENGTH; |
| 184153 | } |
| 184154 | db->aLimit[limitId] = newLimit; |
| 184155 | } |
| 184156 | return oldLimit; /* IMP: R-53341-35419 */ |
| 184157 | } |
| @@ -184395,10 +184666,11 @@ | |
| 184666 | testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ |
| 184667 | testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ |
| 184668 | if( ((1<<(flags&7)) & 0x46)==0 ){ |
| 184669 | rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ |
| 184670 | }else{ |
| 184671 | if( zFilename==0 ) zFilename = ":memory:"; |
| 184672 | rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); |
| 184673 | } |
| 184674 | if( rc!=SQLITE_OK ){ |
| 184675 | if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); |
| 184676 | sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg); |
| @@ -185237,10 +185509,11 @@ | |
| 185509 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185510 | sqlite3ShowWindow(0); |
| 185511 | sqlite3ShowWinFunc(0); |
| 185512 | #endif |
| 185513 | sqlite3ShowSelect(0); |
| 185514 | sqlite3ShowWhereTerm(0); |
| 185515 | } |
| 185516 | #endif |
| 185517 | break; |
| 185518 | } |
| 185519 | |
| @@ -185549,28 +185822,10 @@ | |
| 185822 | *pI1 = rLogEst; |
| 185823 | *pU64 = sqlite3LogEstToInt(rLogEst); |
| 185824 | *pI2 = sqlite3LogEst(*pU64); |
| 185825 | break; |
| 185826 | } |
| 185827 | |
| 185828 | #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) |
| 185829 | /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) |
| 185830 | ** |
| 185831 | ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value |
| @@ -185875,11 +186130,15 @@ | |
| 186130 | if( db->autoCommit==0 ){ |
| 186131 | int iDb = sqlite3FindDbName(db, zDb); |
| 186132 | if( iDb==0 || iDb>1 ){ |
| 186133 | Btree *pBt = db->aDb[iDb].pBt; |
| 186134 | if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){ |
| 186135 | Pager *pPager = sqlite3BtreePager(pBt); |
| 186136 | i64 dummy = 0; |
| 186137 | sqlite3PagerSnapshotOpen(pPager, (sqlite3_snapshot*)&dummy); |
| 186138 | rc = sqlite3BtreeBeginTrans(pBt, 0, 0); |
| 186139 | sqlite3PagerSnapshotOpen(pPager, 0); |
| 186140 | if( rc==SQLITE_OK ){ |
| 186141 | rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); |
| 186142 | } |
| 186143 | } |
| 186144 | } |
| @@ -189665,14 +189924,19 @@ | |
| 189924 | |
| 189925 | assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 ); |
| 189926 | if( *p1==POS_COLUMN ){ |
| 189927 | p1++; |
| 189928 | p1 += fts3GetVarint32(p1, &iCol1); |
| 189929 | /* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN |
| 189930 | ** entry, so this is actually end-of-doclist. */ |
| 189931 | if( iCol1==0 ) return 0; |
| 189932 | } |
| 189933 | if( *p2==POS_COLUMN ){ |
| 189934 | p2++; |
| 189935 | p2 += fts3GetVarint32(p2, &iCol2); |
| 189936 | /* As above, iCol2==0 indicates corruption. */ |
| 189937 | if( iCol2==0 ) return 0; |
| 189938 | } |
| 189939 | |
| 189940 | while( 1 ){ |
| 189941 | if( iCol1==iCol2 ){ |
| 189942 | char *pSave = p; |
| @@ -192839,11 +193103,11 @@ | |
| 193103 | for(p=pExpr; p->pLeft; p=p->pLeft){ |
| 193104 | assert( p->pRight->pPhrase->doclist.nList>0 ); |
| 193105 | nTmp += p->pRight->pPhrase->doclist.nList; |
| 193106 | } |
| 193107 | nTmp += p->pPhrase->doclist.nList; |
| 193108 | aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX); |
| 193109 | if( !aTmp ){ |
| 193110 | *pRc = SQLITE_NOMEM; |
| 193111 | res = 0; |
| 193112 | }else{ |
| 193113 | char *aPoslist = p->pPhrase->doclist.pList; |
| @@ -193490,11 +193754,11 @@ | |
| 193754 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ |
| 193755 | return SQLITE_CORRUPT_VTAB; |
| 193756 | } |
| 193757 | #endif |
| 193758 | |
| 193759 | #if !defined(SQLITE_CORE) |
| 193760 | /* |
| 193761 | ** Initialize API pointer table, if required. |
| 193762 | */ |
| 193763 | #ifdef _WIN32 |
| 193764 | __declspec(dllexport) |
| @@ -194392,14 +194656,15 @@ | |
| 194656 | rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); |
| 194657 | if( rc==SQLITE_OK ){ |
| 194658 | Fts3PhraseToken *pToken; |
| 194659 | |
| 194660 | p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); |
| 194661 | zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); |
| 194662 | if( !zTemp || !p ){ |
| 194663 | rc = SQLITE_NOMEM; |
| 194664 | goto getnextstring_out; |
| 194665 | } |
| 194666 | |
| 194667 | assert( nToken==ii ); |
| 194668 | pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; |
| 194669 | memset(pToken, 0, sizeof(Fts3PhraseToken)); |
| 194670 | |
| @@ -194410,53 +194675,51 @@ | |
| 194675 | pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*'); |
| 194676 | pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^'); |
| 194677 | nToken = ii+1; |
| 194678 | } |
| 194679 | } |
| 194680 | } |
| 194681 | |
| 194682 | if( rc==SQLITE_DONE ){ |
| 194683 | int jj; |
| 194684 | char *zBuf = 0; |
| 194685 | |
| 194686 | p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); |
| 194687 | if( !p ){ |
| 194688 | rc = SQLITE_NOMEM; |
| 194689 | goto getnextstring_out; |
| 194690 | } |
| 194691 | memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); |
| 194692 | p->eType = FTSQUERY_PHRASE; |
| 194693 | p->pPhrase = (Fts3Phrase *)&p[1]; |
| 194694 | p->pPhrase->iColumn = pParse->iDefaultCol; |
| 194695 | p->pPhrase->nToken = nToken; |
| 194696 | |
| 194697 | zBuf = (char *)&p->pPhrase->aToken[nToken]; |
| 194698 | assert( nTemp==0 || zTemp ); |
| 194699 | if( zTemp ){ |
| 194700 | memcpy(zBuf, zTemp, nTemp); |
| 194701 | } |
| 194702 | |
| 194703 | for(jj=0; jj<p->pPhrase->nToken; jj++){ |
| 194704 | p->pPhrase->aToken[jj].z = zBuf; |
| 194705 | zBuf += p->pPhrase->aToken[jj].n; |
| 194706 | } |
| 194707 | rc = SQLITE_OK; |
| 194708 | } |
| 194709 | |
| 194710 | getnextstring_out: |
| 194711 | if( pCursor ){ |
| 194712 | pModule->xClose(pCursor); |
| 194713 | } |
| 194714 | sqlite3_free(zTemp); |
| 194715 | if( rc!=SQLITE_OK ){ |
| 194716 | sqlite3_free(p); |
| 194717 | p = 0; |
| 194718 | } |
| 194719 | *ppExpr = p; |
| 194720 | return rc; |
| 194721 | } |
| 194722 | |
| 194723 | /* |
| 194724 | ** The output variable *ppExpr is populated with an allocated Fts3Expr |
| 194725 | ** structure, or set to 0 if the end of the input buffer is reached. |
| @@ -208867,11 +209130,13 @@ | |
| 209130 | int rawKey = 1; |
| 209131 | x = pParse->aBlob[iRoot]; |
| 209132 | zPath++; |
| 209133 | if( zPath[0]=='"' ){ |
| 209134 | zKey = zPath + 1; |
| 209135 | for(i=1; zPath[i] && zPath[i]!='"'; i++){ |
| 209136 | if( zPath[i]=='\\' && zPath[i+1]!=0 ) i++; |
| 209137 | } |
| 209138 | nKey = i-1; |
| 209139 | if( zPath[i] ){ |
| 209140 | i++; |
| 209141 | }else{ |
| 209142 | return JSON_LOOKUP_PATHERROR; |
| @@ -217779,11 +218044,11 @@ | |
| 218044 | return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY, |
| 218045 | (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback |
| 218046 | ); |
| 218047 | } |
| 218048 | |
| 218049 | #ifndef SQLITE_CORE |
| 218050 | #ifdef _WIN32 |
| 218051 | __declspec(dllexport) |
| 218052 | #endif |
| 218053 | SQLITE_API int sqlite3_rtree_init( |
| 218054 | sqlite3 *db, |
| @@ -218370,11 +218635,11 @@ | |
| 218635 | } |
| 218636 | |
| 218637 | return rc; |
| 218638 | } |
| 218639 | |
| 218640 | #ifndef SQLITE_CORE |
| 218641 | #ifdef _WIN32 |
| 218642 | __declspec(dllexport) |
| 218643 | #endif |
| 218644 | SQLITE_API int sqlite3_icu_init( |
| 218645 | sqlite3 *db, |
| @@ -219628,10 +219893,31 @@ | |
| 219893 | struct RbuFrame { |
| 219894 | u32 iDbPage; |
| 219895 | u32 iWalFrame; |
| 219896 | }; |
| 219897 | |
| 219898 | #ifndef UNUSED_PARAMETER |
| 219899 | /* |
| 219900 | ** The following macros are used to suppress compiler warnings and to |
| 219901 | ** make it clear to human readers when a function parameter is deliberately |
| 219902 | ** left unused within the body of a function. This usually happens when |
| 219903 | ** a function is called via a function pointer. For example the |
| 219904 | ** implementation of an SQL aggregate step callback may not use the |
| 219905 | ** parameter indicating the number of arguments passed to the aggregate, |
| 219906 | ** if it knows that this is enforced elsewhere. |
| 219907 | ** |
| 219908 | ** When a function parameter is not used at all within the body of a function, |
| 219909 | ** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. |
| 219910 | ** However, these macros may also be used to suppress warnings related to |
| 219911 | ** parameters that may or may not be used depending on compilation options. |
| 219912 | ** For example those parameters only used in assert() statements. In these |
| 219913 | ** cases the parameters are named as per the usual conventions. |
| 219914 | */ |
| 219915 | #define UNUSED_PARAMETER(x) (void)(x) |
| 219916 | #define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) |
| 219917 | #endif |
| 219918 | |
| 219919 | /* |
| 219920 | ** RBU handle. |
| 219921 | ** |
| 219922 | ** nPhaseOneStep: |
| 219923 | ** If the RBU database contains an rbu_count table, this value is set to |
| @@ -219679,11 +219965,11 @@ | |
| 219965 | char *zState; /* Path to state db (or NULL if zRbu) */ |
| 219966 | char zStateDb[5]; /* Db name for state ("stat" or "main") */ |
| 219967 | int rc; /* Value returned by last rbu_step() call */ |
| 219968 | char *zErrmsg; /* Error message if rc!=SQLITE_OK */ |
| 219969 | int nStep; /* Rows processed for current object */ |
| 219970 | sqlite3_int64 nProgress; /* Rows processed for all objects */ |
| 219971 | RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ |
| 219972 | const char *zVfsName; /* Name of automatically created rbu vfs */ |
| 219973 | rbu_file *pTargetFd; /* File handle open on target db */ |
| 219974 | int nPagePerSector; /* Pages per sector for pTargetFd */ |
| 219975 | i64 iOalSz; |
| @@ -219796,11 +220082,11 @@ | |
| 220082 | unsigned char *zStart = z; |
| 220083 | while( (c = zValue[0x7f&*(z++)])>=0 ){ |
| 220084 | v = (v<<6) + c; |
| 220085 | } |
| 220086 | z--; |
| 220087 | *pLen -= (int)(z - zStart); |
| 220088 | *pz = (char*)z; |
| 220089 | return v; |
| 220090 | } |
| 220091 | |
| 220092 | #if RBU_ENABLE_DELTA_CKSUM |
| @@ -219981,10 +220267,11 @@ | |
| 220267 | int nOut; |
| 220268 | int nOut2; |
| 220269 | char *aOut; |
| 220270 | |
| 220271 | assert( argc==2 ); |
| 220272 | UNUSED_PARAMETER(argc); |
| 220273 | |
| 220274 | nOrig = sqlite3_value_bytes(argv[0]); |
| 220275 | aOrig = (const char*)sqlite3_value_blob(argv[0]); |
| 220276 | nDelta = sqlite3_value_bytes(argv[1]); |
| 220277 | aDelta = (const char*)sqlite3_value_blob(argv[1]); |
| @@ -221560,17 +221847,17 @@ | |
| 221847 | nParen++; |
| 221848 | } |
| 221849 | else if( c==')' ){ |
| 221850 | nParen--; |
| 221851 | if( nParen==0 ){ |
| 221852 | int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); |
| 221853 | pIter->aIdxCol[iIdxCol++].nSpan = nSpan; |
| 221854 | i++; |
| 221855 | break; |
| 221856 | } |
| 221857 | }else if( c==',' && nParen==1 ){ |
| 221858 | int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); |
| 221859 | pIter->aIdxCol[iIdxCol++].nSpan = nSpan; |
| 221860 | pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1]; |
| 221861 | }else if( c=='"' || c=='\'' || c=='`' ){ |
| 221862 | for(i++; 1; i++){ |
| 221863 | if( zSql[i]==c ){ |
| @@ -222256,10 +222543,12 @@ | |
| 222543 | int i, sz; |
| 222544 | sz = (int)strlen(z)&0xffffff; |
| 222545 | for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} |
| 222546 | if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); |
| 222547 | } |
| 222548 | #else |
| 222549 | UNUSED_PARAMETER2(zBase,z); |
| 222550 | #endif |
| 222551 | } |
| 222552 | |
| 222553 | /* |
| 222554 | ** Return the current wal-index header checksum for the target database |
| @@ -222840,11 +223129,11 @@ | |
| 223129 | "INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES " |
| 223130 | "(%d, %d), " |
| 223131 | "(%d, %Q), " |
| 223132 | "(%d, %Q), " |
| 223133 | "(%d, %d), " |
| 223134 | "(%d, %lld), " |
| 223135 | "(%d, %lld), " |
| 223136 | "(%d, %lld), " |
| 223137 | "(%d, %lld), " |
| 223138 | "(%d, %lld), " |
| 223139 | "(%d, %Q) ", |
| @@ -223198,10 +223487,11 @@ | |
| 223487 | char *zErrmsg = 0; |
| 223488 | int rc; |
| 223489 | sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain); |
| 223490 | |
| 223491 | assert( nVal==1 ); |
| 223492 | UNUSED_PARAMETER(nVal); |
| 223493 | |
| 223494 | rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, |
| 223495 | sqlite3_mprintf("SELECT count(*) FROM sqlite_schema " |
| 223496 | "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0])) |
| 223497 | ); |
| @@ -223473,11 +223763,11 @@ | |
| 223763 | const char *zTarget, |
| 223764 | const char *zState |
| 223765 | ){ |
| 223766 | if( zTarget==0 ){ return rbuMisuseError(); } |
| 223767 | if( zState ){ |
| 223768 | size_t n = strlen(zState); |
| 223769 | if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ |
| 223770 | return rbuMisuseError(); |
| 223771 | } |
| 223772 | } |
| 223773 | /* TODO: Check that both arguments are non-NULL */ |
| @@ -223690,10 +223980,11 @@ | |
| 223980 | /* |
| 223981 | ** Default xRename callback for RBU. |
| 223982 | */ |
| 223983 | static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ |
| 223984 | int rc = SQLITE_OK; |
| 223985 | UNUSED_PARAMETER(pArg); |
| 223986 | #if defined(_WIN32_WCE) |
| 223987 | { |
| 223988 | LPWSTR zWideOld; |
| 223989 | LPWSTR zWideNew; |
| 223990 | |
| @@ -224594,10 +224885,13 @@ | |
| 224885 | |
| 224886 | /* |
| 224887 | ** No-op. |
| 224888 | */ |
| 224889 | static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){ |
| 224890 | UNUSED_PARAMETER(pVfs); |
| 224891 | UNUSED_PARAMETER(a); |
| 224892 | UNUSED_PARAMETER(b); |
| 224893 | return 0; |
| 224894 | } |
| 224895 | |
| 224896 | /* |
| 224897 | ** Deregister and destroy an RBU vfs created by an earlier call to |
| @@ -225650,11 +225944,17 @@ | |
| 225944 | ** schema for the database file that is to be read. The default schema is |
| 225945 | ** "main". |
| 225946 | ** |
| 225947 | ** The data field of sqlite_dbpage table can be updated. The new |
| 225948 | ** value must be a BLOB which is the correct page size, otherwise the |
| 225949 | ** update fails. INSERT operations also work, and operate as if they |
| 225950 | ** where REPLACE. The size of the database can be extended by INSERT-ing |
| 225951 | ** new pages on the end. |
| 225952 | ** |
| 225953 | ** Rows may not be deleted. However, doing an INSERT to page number N |
| 225954 | ** with NULL page data causes the N-th page and all subsequent pages to be |
| 225955 | ** deleted and the database to be truncated. |
| 225956 | */ |
| 225957 | |
| 225958 | /* #include "sqliteInt.h" ** Requires access to internal data structures ** */ |
| 225959 | #if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ |
| 225960 | && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| @@ -225673,17 +225973,18 @@ | |
| 225973 | }; |
| 225974 | |
| 225975 | struct DbpageTable { |
| 225976 | sqlite3_vtab base; /* Base class. Must be first */ |
| 225977 | sqlite3 *db; /* The database */ |
| 225978 | int iDbTrunc; /* Database to truncate */ |
| 225979 | Pgno pgnoTrunc; /* Size to truncate to */ |
| 225980 | }; |
| 225981 | |
| 225982 | /* Columns */ |
| 225983 | #define DBPAGE_COLUMN_PGNO 0 |
| 225984 | #define DBPAGE_COLUMN_DATA 1 |
| 225985 | #define DBPAGE_COLUMN_SCHEMA 2 |
| 225986 | |
| 225987 | |
| 225988 | /* |
| 225989 | ** Connect to or create a dbpagevfs virtual table. |
| 225990 | */ |
| @@ -225943,15 +226244,15 @@ | |
| 226244 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226245 | Pgno pgno; |
| 226246 | DbPage *pDbPage = 0; |
| 226247 | int rc = SQLITE_OK; |
| 226248 | char *zErr = 0; |
| 226249 | int iDb; |
| 226250 | Btree *pBt; |
| 226251 | Pager *pPager; |
| 226252 | int szPage; |
| 226253 | int isInsert; |
| 226254 | |
| 226255 | (void)pRowid; |
| 226256 | if( pTab->db->flags & SQLITE_Defensive ){ |
| 226257 | zErr = "read-only"; |
| 226258 | goto update_fail; |
| @@ -225958,45 +226259,62 @@ | |
| 226259 | } |
| 226260 | if( argc==1 ){ |
| 226261 | zErr = "cannot delete"; |
| 226262 | goto update_fail; |
| 226263 | } |
| 226264 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ |
| 226265 | pgno = (Pgno)sqlite3_value_int(argv[2]); |
| 226266 | isInsert = 1; |
| 226267 | }else{ |
| 226268 | pgno = sqlite3_value_int(argv[0]); |
| 226269 | if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ |
| 226270 | zErr = "cannot insert"; |
| 226271 | goto update_fail; |
| 226272 | } |
| 226273 | isInsert = 0; |
| 226274 | } |
| 226275 | if( sqlite3_value_type(argv[4])==SQLITE_NULL ){ |
| 226276 | iDb = 0; |
| 226277 | }else{ |
| 226278 | const char *zSchema = (const char*)sqlite3_value_text(argv[4]); |
| 226279 | iDb = sqlite3FindDbName(pTab->db, zSchema); |
| 226280 | if( iDb<0 ){ |
| 226281 | zErr = "no such schema"; |
| 226282 | goto update_fail; |
| 226283 | } |
| 226284 | } |
| 226285 | pBt = pTab->db->aDb[iDb].pBt; |
| 226286 | if( pgno<1 || NEVER(pBt==0) ){ |
| 226287 | zErr = "bad page number"; |
| 226288 | goto update_fail; |
| 226289 | } |
| 226290 | szPage = sqlite3BtreeGetPageSize(pBt); |
| 226291 | if( sqlite3_value_type(argv[3])!=SQLITE_BLOB |
| 226292 | || sqlite3_value_bytes(argv[3])!=szPage |
| 226293 | ){ |
| 226294 | if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){ |
| 226295 | /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and |
| 226296 | ** all subsequent pages to be deleted. */ |
| 226297 | pTab->iDbTrunc = iDb; |
| 226298 | pgno--; |
| 226299 | pTab->pgnoTrunc = pgno; |
| 226300 | }else{ |
| 226301 | zErr = "bad page value"; |
| 226302 | goto update_fail; |
| 226303 | } |
| 226304 | } |
| 226305 | pPager = sqlite3BtreePager(pBt); |
| 226306 | rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); |
| 226307 | if( rc==SQLITE_OK ){ |
| 226308 | const void *pData = sqlite3_value_blob(argv[3]); |
| 226309 | if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){ |
| 226310 | unsigned char *aPage = sqlite3PagerGetData(pDbPage); |
| 226311 | memcpy(aPage, pData, szPage); |
| 226312 | pTab->pgnoTrunc = 0; |
| 226313 | } |
| 226314 | }else{ |
| 226315 | pTab->pgnoTrunc = 0; |
| 226316 | } |
| 226317 | sqlite3PagerUnref(pDbPage); |
| 226318 | return rc; |
| 226319 | |
| 226320 | update_fail: |
| @@ -226015,13 +226333,35 @@ | |
| 226333 | int i; |
| 226334 | for(i=0; i<db->nDb; i++){ |
| 226335 | Btree *pBt = db->aDb[i].pBt; |
| 226336 | if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0); |
| 226337 | } |
| 226338 | pTab->pgnoTrunc = 0; |
| 226339 | return SQLITE_OK; |
| 226340 | } |
| 226341 | |
| 226342 | /* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT |
| 226343 | */ |
| 226344 | static int dbpageSync(sqlite3_vtab *pVtab){ |
| 226345 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226346 | if( pTab->pgnoTrunc>0 ){ |
| 226347 | Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; |
| 226348 | Pager *pPager = sqlite3BtreePager(pBt); |
| 226349 | sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); |
| 226350 | } |
| 226351 | pTab->pgnoTrunc = 0; |
| 226352 | return SQLITE_OK; |
| 226353 | } |
| 226354 | |
| 226355 | /* Cancel any pending truncate. |
| 226356 | */ |
| 226357 | static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){ |
| 226358 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226359 | pTab->pgnoTrunc = 0; |
| 226360 | (void)notUsed1; |
| 226361 | return SQLITE_OK; |
| 226362 | } |
| 226363 | |
| 226364 | /* |
| 226365 | ** Invoke this routine to register the "dbpage" virtual table module |
| 226366 | */ |
| 226367 | SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ |
| @@ -226039,18 +226379,18 @@ | |
| 226379 | dbpageEof, /* xEof - check for end of scan */ |
| 226380 | dbpageColumn, /* xColumn - read data */ |
| 226381 | dbpageRowid, /* xRowid - read data */ |
| 226382 | dbpageUpdate, /* xUpdate */ |
| 226383 | dbpageBegin, /* xBegin */ |
| 226384 | dbpageSync, /* xSync */ |
| 226385 | 0, /* xCommit */ |
| 226386 | 0, /* xRollback */ |
| 226387 | 0, /* xFindMethod */ |
| 226388 | 0, /* xRename */ |
| 226389 | 0, /* xSavepoint */ |
| 226390 | 0, /* xRelease */ |
| 226391 | dbpageRollbackTo, /* xRollbackTo */ |
| 226392 | 0, /* xShadowName */ |
| 226393 | 0 /* xIntegrity */ |
| 226394 | }; |
| 226395 | return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); |
| 226396 | } |
| @@ -226134,10 +226474,14 @@ | |
| 226474 | /* |
| 226475 | ** An object of this type is used internally as an abstraction for |
| 226476 | ** input data. Input data may be supplied either as a single large buffer |
| 226477 | ** (e.g. sqlite3changeset_start()) or using a stream function (e.g. |
| 226478 | ** sqlite3changeset_start_strm()). |
| 226479 | ** |
| 226480 | ** bNoDiscard: |
| 226481 | ** If true, then the only time data is discarded is as a result of explicit |
| 226482 | ** sessionDiscardData() calls. Not within every sessionInputBuffer() call. |
| 226483 | */ |
| 226484 | struct SessionInput { |
| 226485 | int bNoDiscard; /* If true, do not discard in InputBuffer() */ |
| 226486 | int iCurrent; /* Offset in aData[] of current change */ |
| 226487 | int iNext; /* Offset in aData[] of next change */ |
| @@ -227817,20 +228161,23 @@ | |
| 228161 | /* Figure out how large an allocation is required */ |
| 228162 | nByte = sizeof(SessionChange); |
| 228163 | for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ |
| 228164 | sqlite3_value *p = 0; |
| 228165 | if( op!=SQLITE_INSERT ){ |
| 228166 | /* This may fail if the column has a non-NULL default and was added |
| 228167 | ** using ALTER TABLE ADD COLUMN after this record was created. */ |
| 228168 | rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 228169 | }else if( pTab->abPK[i] ){ |
| 228170 | TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); |
| 228171 | assert( trc==SQLITE_OK ); |
| 228172 | } |
| 228173 | |
| 228174 | if( rc==SQLITE_OK ){ |
| 228175 | /* This may fail if SQLite value p contains a utf-16 string that must |
| 228176 | ** be converted to utf-8 and an OOM error occurs while doing so. */ |
| 228177 | rc = sessionSerializeValue(0, p, &nByte); |
| 228178 | } |
| 228179 | if( rc!=SQLITE_OK ) goto error_out; |
| 228180 | } |
| 228181 | if( pTab->bRowid ){ |
| 228182 | nByte += 9; /* Size of rowid field - an integer */ |
| 228183 | } |
| @@ -231184,19 +231531,25 @@ | |
| 231531 | int rc = SQLITE_OK; /* Return code */ |
| 231532 | const char *zTab = 0; /* Name of current table */ |
| 231533 | int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ |
| 231534 | SessionApplyCtx sApply; /* changeset_apply() context object */ |
| 231535 | int bPatchset; |
| 231536 | u64 savedFlag = db->flags & SQLITE_FkNoAction; |
| 231537 | |
| 231538 | assert( xConflict!=0 ); |
| 231539 | |
| 231540 | sqlite3_mutex_enter(sqlite3_db_mutex(db)); |
| 231541 | if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ |
| 231542 | db->flags |= ((u64)SQLITE_FkNoAction); |
| 231543 | db->aDb[0].pSchema->schema_cookie -= 32; |
| 231544 | } |
| 231545 | |
| 231546 | pIter->in.bNoDiscard = 1; |
| 231547 | memset(&sApply, 0, sizeof(sApply)); |
| 231548 | sApply.bRebase = (ppRebase && pnRebase); |
| 231549 | sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 231550 | sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); |
| 231551 | if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ |
| 231552 | rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); |
| 231553 | } |
| 231554 | if( rc==SQLITE_OK ){ |
| 231555 | rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0); |
| @@ -231354,10 +231707,16 @@ | |
| 231707 | sqlite3_finalize(sApply.pDelete); |
| 231708 | sqlite3_finalize(sApply.pSelect); |
| 231709 | sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ |
| 231710 | sqlite3_free((char*)sApply.constraints.aBuf); |
| 231711 | sqlite3_free((char*)sApply.rebase.aBuf); |
| 231712 | |
| 231713 | if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ |
| 231714 | assert( db->flags & SQLITE_FkNoAction ); |
| 231715 | db->flags &= ~((u64)SQLITE_FkNoAction); |
| 231716 | db->aDb[0].pSchema->schema_cookie -= 32; |
| 231717 | } |
| 231718 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); |
| 231719 | return rc; |
| 231720 | } |
| 231721 | |
| 231722 | /* |
| @@ -231382,28 +231741,17 @@ | |
| 231741 | int flags |
| 231742 | ){ |
| 231743 | sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ |
| 231744 | int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 231745 | int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); |
| 231746 | |
| 231747 | if( rc==SQLITE_OK ){ |
| 231748 | rc = sessionChangesetApply( |
| 231749 | db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags |
| 231750 | ); |
| 231751 | } |
| 231752 | |
| 231753 | return rc; |
| 231754 | } |
| 231755 | |
| 231756 | /* |
| 231757 | ** Apply the changeset passed via pChangeset/nChangeset to the main database |
| @@ -231720,10 +232068,13 @@ | |
| 232068 | if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){ |
| 232069 | /* Append the missing default column values to the record. */ |
| 232070 | sessionAppendBlob(pOut, aRec, nRec, &rc); |
| 232071 | if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ |
| 232072 | rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); |
| 232073 | if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){ |
| 232074 | rc = sqlite3_errcode(pGrp->db); |
| 232075 | } |
| 232076 | } |
| 232077 | for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){ |
| 232078 | int eType = sqlite3_column_type(pTab->pDfltStmt, ii); |
| 232079 | sessionAppendByte(pOut, eType, &rc); |
| 232080 | switch( eType ){ |
| @@ -231736,10 +232087,11 @@ | |
| 232087 | double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); |
| 232088 | memcpy(&iVal, &rVal, sizeof(i64)); |
| 232089 | } |
| 232090 | if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ |
| 232091 | sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); |
| 232092 | pOut->nBuf += 8; |
| 232093 | } |
| 232094 | break; |
| 232095 | } |
| 232096 | |
| 232097 | case SQLITE_BLOB: |
| @@ -231874,10 +232226,12 @@ | |
| 232226 | SessionChange *pExist = 0; |
| 232227 | SessionChange **pp = 0; |
| 232228 | SessionTable *pTab = 0; |
| 232229 | u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; |
| 232230 | int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; |
| 232231 | |
| 232232 | assert( nRec>0 ); |
| 232233 | |
| 232234 | /* Ensure that only changesets, or only patchsets, but not a mixture |
| 232235 | ** of both, are being combined. It is an error to try to combine a |
| 232236 | ** changeset and a patchset. */ |
| 232237 | if( pGrp->pList==0 ){ |
| @@ -231952,10 +232306,11 @@ | |
| 232306 | ){ |
| 232307 | u8 *aRec; |
| 232308 | int nRec; |
| 232309 | int rc = SQLITE_OK; |
| 232310 | |
| 232311 | pIter->in.bNoDiscard = 1; |
| 232312 | while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ |
| 232313 | rc = sessionOneChangeToHash(pGrp, pIter, bRebase); |
| 232314 | if( rc!=SQLITE_OK ) break; |
| 232315 | } |
| 232316 | |
| @@ -232583,20 +232938,46 @@ | |
| 232938 | #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 232939 | |
| 232940 | /************** End of sqlite3session.c **************************************/ |
| 232941 | /************** Begin file fts5.c ********************************************/ |
| 232942 | |
| 232943 | /* |
| 232944 | ** This, the "fts5.c" source file, is a composite file that is itself |
| 232945 | ** assembled from the following files: |
| 232946 | ** |
| 232947 | ** fts5.h |
| 232948 | ** fts5Int.h |
| 232949 | ** fts5parse.h <--- Generated from fts5parse.y by Lemon |
| 232950 | ** fts5parse.c <--- Generated from fts5parse.y by Lemon |
| 232951 | ** fts5_aux.c |
| 232952 | ** fts5_buffer.c |
| 232953 | ** fts5_config.c |
| 232954 | ** fts5_expr.c |
| 232955 | ** fts5_hash.c |
| 232956 | ** fts5_index.c |
| 232957 | ** fts5_main.c |
| 232958 | ** fts5_storage.c |
| 232959 | ** fts5_tokenize.c |
| 232960 | ** fts5_unicode2.c |
| 232961 | ** fts5_varint.c |
| 232962 | ** fts5_vocab.c |
| 232963 | */ |
| 232964 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) |
| 232965 | |
| 232966 | #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) |
| 232967 | # define NDEBUG 1 |
| 232968 | #endif |
| 232969 | #if defined(NDEBUG) && defined(SQLITE_DEBUG) |
| 232970 | # undef NDEBUG |
| 232971 | #endif |
| 232972 | |
| 232973 | #ifdef HAVE_STDINT_H |
| 232974 | /* #include <stdint.h> */ |
| 232975 | #endif |
| 232976 | #ifdef HAVE_INTTYPES_H |
| 232977 | /* #include <inttypes.h> */ |
| 232978 | #endif |
| 232979 | /* |
| 232980 | ** 2014 May 31 |
| 232981 | ** |
| 232982 | ** The author disclaims copyright to this source code. In place of |
| 232983 | ** a legal notice, here is a blessing: |
| @@ -232893,17 +233274,32 @@ | |
| 233274 | ** This is used to access token iToken of phrase hit iIdx within the |
| 233275 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 233276 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 233277 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 233278 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 233279 | ** bytes. |
| 233280 | ** |
| 233281 | ** The output text is not a copy of the document text that was tokenized. |
| 233282 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 233283 | ** includes any embedded 0x00 and trailing data. |
| 233284 | ** |
| 233285 | ** This API may be slow in some cases if the token identified by parameters |
| 233286 | ** iIdx and iToken matched a prefix token in the query. In most cases, the |
| 233287 | ** first call to this API for each prefix token in the query is forced |
| 233288 | ** to scan the portion of the full-text index that matches the prefix |
| 233289 | ** token to collect the extra data required by this API. If the prefix |
| 233290 | ** token matches a large number of token instances in the document set, |
| 233291 | ** this may be a performance problem. |
| 233292 | ** |
| 233293 | ** If the user knows in advance that a query may use this API for a |
| 233294 | ** prefix token, FTS5 may be configured to collect all required data as part |
| 233295 | ** of the initial querying of the full-text index, avoiding the second scan |
| 233296 | ** entirely. This also causes prefix queries that do not use this API to |
| 233297 | ** run more slowly and use more memory. FTS5 may be configured in this way |
| 233298 | ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] |
| 233299 | ** option, or on a per-query basis using the |
| 233300 | ** [fts5_insttoken | fts5_insttoken()] user function. |
| 233301 | ** |
| 233302 | ** This API can be quite slow if used with an FTS5 table created with the |
| 233303 | ** "detail=none" or "detail=column" option. |
| 233304 | ** |
| 233305 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -232991,11 +233387,10 @@ | |
| 233387 | ** CUSTOM TOKENIZERS |
| 233388 | ** |
| 233389 | ** Applications may also register custom tokenizer types. A tokenizer |
| 233390 | ** is registered by providing fts5 with a populated instance of the |
| 233391 | ** following structure. All structure methods must be defined, setting |
| 233392 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 233393 | ** behaviour. The structure methods are expected to function as follows: |
| 233394 | ** |
| 233395 | ** xCreate: |
| 233396 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -233560,10 +233955,11 @@ | |
| 233955 | u8 *abUnindexed; /* True for unindexed columns */ |
| 233956 | int nPrefix; /* Number of prefix indexes */ |
| 233957 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ |
| 233958 | int eContent; /* An FTS5_CONTENT value */ |
| 233959 | int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ |
| 233960 | int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ |
| 233961 | char *zContent; /* content table */ |
| 233962 | char *zContentRowid; /* "content_rowid=" option value */ |
| 233963 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ |
| 233964 | int bTokendata; /* "tokendata=" option value (dflt==0) */ |
| 233965 | int bLocale; /* "locale=" option value (dflt==0) */ |
| @@ -233582,11 +233978,12 @@ | |
| 233978 | int nUsermerge; /* 'usermerge' setting */ |
| 233979 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 233980 | char *zRank; /* Name of rank function */ |
| 233981 | char *zRankArgs; /* Arguments to rank function */ |
| 233982 | int bSecureDelete; /* 'secure-delete' */ |
| 233983 | int nDeleteMerge; /* 'deletemerge' */ |
| 233984 | int bPrefixInsttoken; /* 'prefix-insttoken' */ |
| 233985 | |
| 233986 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 233987 | char **pzErrmsg; |
| 233988 | |
| 233989 | #ifdef SQLITE_DEBUG |
| @@ -233598,13 +233995,14 @@ | |
| 233995 | ** the expected version if the 'secure-delete' option has ever been |
| 233996 | ** set on the table. */ |
| 233997 | #define FTS5_CURRENT_VERSION 4 |
| 233998 | #define FTS5_CURRENT_VERSION_SECUREDELETE 5 |
| 233999 | |
| 234000 | #define FTS5_CONTENT_NORMAL 0 |
| 234001 | #define FTS5_CONTENT_NONE 1 |
| 234002 | #define FTS5_CONTENT_EXTERNAL 2 |
| 234003 | #define FTS5_CONTENT_UNINDEXED 3 |
| 234004 | |
| 234005 | #define FTS5_DETAIL_FULL 0 |
| 234006 | #define FTS5_DETAIL_NONE 1 |
| 234007 | #define FTS5_DETAIL_COLUMNS 2 |
| 234008 | |
| @@ -233838,11 +234236,18 @@ | |
| 234236 | static int sqlite3Fts5StructureTest(Fts5Index*, void*); |
| 234237 | |
| 234238 | /* |
| 234239 | ** Used by xInstToken(): |
| 234240 | */ |
| 234241 | static int sqlite3Fts5IterToken( |
| 234242 | Fts5IndexIter *pIndexIter, |
| 234243 | const char *pToken, int nToken, |
| 234244 | i64 iRowid, |
| 234245 | int iCol, |
| 234246 | int iOff, |
| 234247 | const char **ppOut, int *pnOut |
| 234248 | ); |
| 234249 | |
| 234250 | /* |
| 234251 | ** Insert or remove data to or from the index. Each time a document is |
| 234252 | ** added to or removed from the index, this function is called one or more |
| 234253 | ** times. |
| @@ -233972,20 +234377,17 @@ | |
| 234377 | |
| 234378 | static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); |
| 234379 | |
| 234380 | static int sqlite3Fts5FlushToDisk(Fts5Table*); |
| 234381 | |
| 234382 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); |
| 234383 | static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); |
| 234384 | |
| 234385 | static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); |
| 234386 | static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, |
| 234387 | const char **ppText, int *pnText, const char **ppLoc, int *pnLoc |
| 234388 | ); |
| 234389 | |
| 234390 | /* |
| 234391 | ** End of interface to code in fts5.c. |
| 234392 | **************************************************************************/ |
| 234393 | |
| @@ -234063,11 +234465,11 @@ | |
| 234465 | |
| 234466 | static int sqlite3Fts5DropAll(Fts5Config*); |
| 234467 | static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); |
| 234468 | |
| 234469 | static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); |
| 234470 | static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); |
| 234471 | static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); |
| 234472 | |
| 234473 | static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); |
| 234474 | |
| 234475 | static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); |
| @@ -237270,10 +237672,11 @@ | |
| 237672 | const char *zArg, /* Argument to parse */ |
| 237673 | char **pzErr /* OUT: Error message */ |
| 237674 | ){ |
| 237675 | int rc = SQLITE_OK; |
| 237676 | int nCmd = (int)strlen(zCmd); |
| 237677 | |
| 237678 | if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ |
| 237679 | const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; |
| 237680 | const char *p; |
| 237681 | int bFirst = 1; |
| 237682 | if( pConfig->aPrefix==0 ){ |
| @@ -237388,10 +237791,20 @@ | |
| 237791 | }else{ |
| 237792 | pConfig->bContentlessDelete = (zArg[0]=='1'); |
| 237793 | } |
| 237794 | return rc; |
| 237795 | } |
| 237796 | |
| 237797 | if( sqlite3_strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){ |
| 237798 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ |
| 237799 | *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); |
| 237800 | rc = SQLITE_ERROR; |
| 237801 | }else{ |
| 237802 | pConfig->bContentlessUnindexed = (zArg[0]=='1'); |
| 237803 | } |
| 237804 | return rc; |
| 237805 | } |
| 237806 | |
| 237807 | if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ |
| 237808 | if( pConfig->zContentRowid ){ |
| 237809 | *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); |
| 237810 | rc = SQLITE_ERROR; |
| @@ -237506,11 +237919,12 @@ | |
| 237919 | |
| 237920 | static int fts5ConfigParseColumn( |
| 237921 | Fts5Config *p, |
| 237922 | char *zCol, |
| 237923 | char *zArg, |
| 237924 | char **pzErr, |
| 237925 | int *pbUnindexed |
| 237926 | ){ |
| 237927 | int rc = SQLITE_OK; |
| 237928 | if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) |
| 237929 | || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME) |
| 237930 | ){ |
| @@ -237517,10 +237931,11 @@ | |
| 237931 | *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol); |
| 237932 | rc = SQLITE_ERROR; |
| 237933 | }else if( zArg ){ |
| 237934 | if( 0==sqlite3_stricmp(zArg, "unindexed") ){ |
| 237935 | p->abUnindexed[p->nCol] = 1; |
| 237936 | *pbUnindexed = 1; |
| 237937 | }else{ |
| 237938 | *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg); |
| 237939 | rc = SQLITE_ERROR; |
| 237940 | } |
| 237941 | } |
| @@ -237537,15 +237952,30 @@ | |
| 237952 | int rc = SQLITE_OK; |
| 237953 | Fts5Buffer buf = {0, 0, 0}; |
| 237954 | |
| 237955 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); |
| 237956 | if( p->eContent!=FTS5_CONTENT_NONE ){ |
| 237957 | assert( p->eContent==FTS5_CONTENT_EXTERNAL |
| 237958 | || p->eContent==FTS5_CONTENT_NORMAL |
| 237959 | || p->eContent==FTS5_CONTENT_UNINDEXED |
| 237960 | ); |
| 237961 | for(i=0; i<p->nCol; i++){ |
| 237962 | if( p->eContent==FTS5_CONTENT_EXTERNAL ){ |
| 237963 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); |
| 237964 | }else if( p->eContent==FTS5_CONTENT_NORMAL || p->abUnindexed[i] ){ |
| 237965 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); |
| 237966 | }else{ |
| 237967 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); |
| 237968 | } |
| 237969 | } |
| 237970 | } |
| 237971 | if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){ |
| 237972 | for(i=0; i<p->nCol; i++){ |
| 237973 | if( p->abUnindexed[i]==0 ){ |
| 237974 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); |
| 237975 | }else{ |
| 237976 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); |
| 237977 | } |
| 237978 | } |
| 237979 | } |
| 237980 | |
| 237981 | assert( p->zContentExprlist==0 ); |
| @@ -237575,10 +238005,11 @@ | |
| 238005 | ){ |
| 238006 | int rc = SQLITE_OK; /* Return code */ |
| 238007 | Fts5Config *pRet; /* New object to return */ |
| 238008 | int i; |
| 238009 | sqlite3_int64 nByte; |
| 238010 | int bUnindexed = 0; /* True if there are one or more UNINDEXED */ |
| 238011 | |
| 238012 | *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); |
| 238013 | if( pRet==0 ) return SQLITE_NOMEM; |
| 238014 | memset(pRet, 0, sizeof(Fts5Config)); |
| 238015 | pRet->pGlobal = pGlobal; |
| @@ -237634,11 +238065,11 @@ | |
| 238065 | ALWAYS(zOne)?zOne:"", |
| 238066 | zTwo?zTwo:"", |
| 238067 | pzErr |
| 238068 | ); |
| 238069 | }else{ |
| 238070 | rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr, &bUnindexed); |
| 238071 | zOne = 0; |
| 238072 | } |
| 238073 | } |
| 238074 | } |
| 238075 | |
| @@ -237665,18 +238096,34 @@ | |
| 238096 | *pzErr = sqlite3_mprintf( |
| 238097 | "contentless_delete=1 is incompatible with columnsize=0" |
| 238098 | ); |
| 238099 | rc = SQLITE_ERROR; |
| 238100 | } |
| 238101 | |
| 238102 | /* We only allow contentless_unindexed=1 if the table is actually a |
| 238103 | ** contentless one. |
| 238104 | */ |
| 238105 | if( rc==SQLITE_OK |
| 238106 | && pRet->bContentlessUnindexed |
| 238107 | && pRet->eContent!=FTS5_CONTENT_NONE |
| 238108 | ){ |
| 238109 | *pzErr = sqlite3_mprintf( |
| 238110 | "contentless_unindexed=1 requires a contentless table" |
| 238111 | ); |
| 238112 | rc = SQLITE_ERROR; |
| 238113 | } |
| 238114 | |
| 238115 | /* If no zContent option was specified, fill in the default values. */ |
| 238116 | if( rc==SQLITE_OK && pRet->zContent==0 ){ |
| 238117 | const char *zTail = 0; |
| 238118 | assert( pRet->eContent==FTS5_CONTENT_NORMAL |
| 238119 | || pRet->eContent==FTS5_CONTENT_NONE |
| 238120 | ); |
| 238121 | if( pRet->eContent==FTS5_CONTENT_NORMAL ){ |
| 238122 | zTail = "content"; |
| 238123 | }else if( bUnindexed && pRet->bContentlessUnindexed ){ |
| 238124 | pRet->eContent = FTS5_CONTENT_UNINDEXED; |
| 238125 | zTail = "content"; |
| 238126 | }else if( pRet->bColumnsize ){ |
| 238127 | zTail = "docsize"; |
| 238128 | } |
| 238129 | |
| @@ -238010,10 +238457,23 @@ | |
| 238457 | if( bVal<0 ){ |
| 238458 | *pbBadkey = 1; |
| 238459 | }else{ |
| 238460 | pConfig->bSecureDelete = (bVal ? 1 : 0); |
| 238461 | } |
| 238462 | } |
| 238463 | |
| 238464 | else if( 0==sqlite3_stricmp(zKey, "insttoken") ){ |
| 238465 | int bVal = -1; |
| 238466 | if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ |
| 238467 | bVal = sqlite3_value_int(pVal); |
| 238468 | } |
| 238469 | if( bVal<0 ){ |
| 238470 | *pbBadkey = 1; |
| 238471 | }else{ |
| 238472 | pConfig->bPrefixInsttoken = (bVal ? 1 : 0); |
| 238473 | } |
| 238474 | |
| 238475 | }else{ |
| 238476 | *pbBadkey = 1; |
| 238477 | } |
| 238478 | return rc; |
| 238479 | } |
| @@ -241145,11 +241605,11 @@ | |
| 241605 | && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 |
| 241606 | ){ |
| 241607 | int rc = sqlite3Fts5PoslistWriterAppend( |
| 241608 | &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff |
| 241609 | ); |
| 241610 | if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){ |
| 241611 | int iCol = p->iOff>>32; |
| 241612 | int iTokOff = p->iOff & 0x7FFFFFFF; |
| 241613 | rc = sqlite3Fts5IndexIterWriteTokendata( |
| 241614 | pT->pIter, pToken, nToken, iRowid, iCol, iTokOff |
| 241615 | ); |
| @@ -241338,19 +241798,18 @@ | |
| 241798 | pPhrase = pExpr->apExprPhrase[iPhrase]; |
| 241799 | if( iToken<0 || iToken>=pPhrase->nTerm ){ |
| 241800 | return SQLITE_RANGE; |
| 241801 | } |
| 241802 | pTerm = &pPhrase->aTerm[iToken]; |
| 241803 | if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){ |
| 241804 | rc = sqlite3Fts5IterToken( |
| 241805 | pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm, |
| 241806 | iRowid, iCol, iOff+iToken, ppOut, pnOut |
| 241807 | ); |
| 241808 | }else{ |
| 241809 | *ppOut = pTerm->pTerm; |
| 241810 | *pnOut = pTerm->nFullTerm; |
| 241811 | } |
| 241812 | return rc; |
| 241813 | } |
| 241814 | |
| 241815 | /* |
| @@ -246847,10 +247306,15 @@ | |
| 247306 | if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ |
| 247307 | iRet = ii; |
| 247308 | nBest = nPercent; |
| 247309 | } |
| 247310 | } |
| 247311 | |
| 247312 | /* If pLvl is already the input level to an ongoing merge, look no |
| 247313 | ** further for a merge candidate. The caller should be allowed to |
| 247314 | ** continue merging from pLvl first. */ |
| 247315 | if( pLvl->nMerge ) break; |
| 247316 | } |
| 247317 | } |
| 247318 | return iRet; |
| 247319 | } |
| 247320 | |
| @@ -248156,10 +248620,387 @@ | |
| 248620 | fts5BufferFree(&tmp); |
| 248621 | memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); |
| 248622 | *p1 = out; |
| 248623 | } |
| 248624 | |
| 248625 | |
| 248626 | /* |
| 248627 | ** Iterate through a range of entries in the FTS index, invoking the xVisit |
| 248628 | ** callback for each of them. |
| 248629 | ** |
| 248630 | ** Parameter pToken points to an nToken buffer containing an FTS index term |
| 248631 | ** (i.e. a document term with the preceding 1 byte index identifier - |
| 248632 | ** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits |
| 248633 | ** all entries for terms that have pToken/nToken as a prefix. If bPrefix |
| 248634 | ** is false, then only entries with pToken/nToken as the entire key are |
| 248635 | ** visited. |
| 248636 | ** |
| 248637 | ** If the current table is a tokendata=1 table, then if bPrefix is true then |
| 248638 | ** each index term is treated separately. However, if bPrefix is false, then |
| 248639 | ** all index terms corresponding to pToken/nToken are collapsed into a single |
| 248640 | ** term before the callback is invoked. |
| 248641 | ** |
| 248642 | ** The callback invoked for each entry visited is specified by paramter xVisit. |
| 248643 | ** Each time it is invoked, it is passed a pointer to the Fts5Index object, |
| 248644 | ** a copy of the 7th paramter to this function (pCtx) and a pointer to the |
| 248645 | ** iterator that indicates the current entry. If the current entry is the |
| 248646 | ** first with a new term (i.e. different from that of the previous entry, |
| 248647 | ** including the very first term), then the final two parameters are passed |
| 248648 | ** a pointer to the term and its size in bytes, respectively. If the current |
| 248649 | ** entry is not the first associated with its term, these two parameters |
| 248650 | ** are passed 0. |
| 248651 | ** |
| 248652 | ** If parameter pColset is not NULL, then it is used to filter entries before |
| 248653 | ** the callback is invoked. |
| 248654 | */ |
| 248655 | static int fts5VisitEntries( |
| 248656 | Fts5Index *p, /* Fts5 index object */ |
| 248657 | Fts5Colset *pColset, /* Columns filter to apply, or NULL */ |
| 248658 | u8 *pToken, /* Buffer containing token */ |
| 248659 | int nToken, /* Size of buffer pToken in bytes */ |
| 248660 | int bPrefix, /* True for a prefix scan */ |
| 248661 | void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int), |
| 248662 | void *pCtx /* Passed as second argument to xVisit() */ |
| 248663 | ){ |
| 248664 | const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0) |
| 248665 | | FTS5INDEX_QUERY_SKIPEMPTY |
| 248666 | | FTS5INDEX_QUERY_NOOUTPUT; |
| 248667 | Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ |
| 248668 | int bNewTerm = 1; |
| 248669 | Fts5Structure *pStruct = fts5StructureRead(p); |
| 248670 | |
| 248671 | fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); |
| 248672 | fts5IterSetOutputCb(&p->rc, p1); |
| 248673 | for( /* no-op */ ; |
| 248674 | fts5MultiIterEof(p, p1)==0; |
| 248675 | fts5MultiIterNext2(p, p1, &bNewTerm) |
| 248676 | ){ |
| 248677 | Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; |
| 248678 | int nNew = 0; |
| 248679 | const u8 *pNew = 0; |
| 248680 | |
| 248681 | p1->xSetOutputs(p1, pSeg); |
| 248682 | if( p->rc ) break; |
| 248683 | |
| 248684 | if( bNewTerm ){ |
| 248685 | nNew = pSeg->term.n; |
| 248686 | pNew = pSeg->term.p; |
| 248687 | if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break; |
| 248688 | } |
| 248689 | |
| 248690 | xVisit(p, pCtx, p1, pNew, nNew); |
| 248691 | } |
| 248692 | fts5MultiIterFree(p1); |
| 248693 | |
| 248694 | fts5StructureRelease(pStruct); |
| 248695 | return p->rc; |
| 248696 | } |
| 248697 | |
| 248698 | |
| 248699 | /* |
| 248700 | ** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an |
| 248701 | ** array of these for each row it visits (so all iRowid fields are the same). |
| 248702 | ** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an |
| 248703 | ** array of these for the entire query (in which case iRowid fields may take |
| 248704 | ** a variety of values). |
| 248705 | ** |
| 248706 | ** Each instance in the array indicates the iterator (and therefore term) |
| 248707 | ** associated with position iPos of rowid iRowid. This is used by the |
| 248708 | ** xInstToken() API. |
| 248709 | ** |
| 248710 | ** iRowid: |
| 248711 | ** Rowid for the current entry. |
| 248712 | ** |
| 248713 | ** iPos: |
| 248714 | ** Position of current entry within row. In the usual ((iCol<<32)+iOff) |
| 248715 | ** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()). |
| 248716 | ** |
| 248717 | ** iIter: |
| 248718 | ** If the Fts5TokenDataIter iterator that the entry is part of is |
| 248719 | ** actually an iterator (i.e. with nIter>0, not just a container for |
| 248720 | ** Fts5TokenDataMap structures), then this variable is an index into |
| 248721 | ** the apIter[] array. The corresponding term is that which the iterator |
| 248722 | ** at apIter[iIter] currently points to. |
| 248723 | ** |
| 248724 | ** Or, if the Fts5TokenDataIter iterator is just a container object |
| 248725 | ** (nIter==0), then iIter is an index into the term.p[] buffer where |
| 248726 | ** the term is stored. |
| 248727 | ** |
| 248728 | ** nByte: |
| 248729 | ** In the case where iIter is an index into term.p[], this variable |
| 248730 | ** is the size of the term in bytes. If iIter is an index into apIter[], |
| 248731 | ** this variable is unused. |
| 248732 | */ |
| 248733 | struct Fts5TokenDataMap { |
| 248734 | i64 iRowid; /* Row this token is located in */ |
| 248735 | i64 iPos; /* Position of token */ |
| 248736 | int iIter; /* Iterator token was read from */ |
| 248737 | int nByte; /* Length of token in bytes (or 0) */ |
| 248738 | }; |
| 248739 | |
| 248740 | /* |
| 248741 | ** An object used to supplement Fts5Iter for tokendata=1 iterators. |
| 248742 | ** |
| 248743 | ** This object serves two purposes. The first is as a container for an array |
| 248744 | ** of Fts5TokenDataMap structures, which are used to find the token required |
| 248745 | ** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and |
| 248746 | ** aMap[] variables. |
| 248747 | */ |
| 248748 | struct Fts5TokenDataIter { |
| 248749 | int nMapAlloc; /* Allocated size of aMap[] in entries */ |
| 248750 | int nMap; /* Number of valid entries in aMap[] */ |
| 248751 | Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ |
| 248752 | |
| 248753 | /* The following are used for prefix-queries only. */ |
| 248754 | Fts5Buffer terms; |
| 248755 | |
| 248756 | /* The following are used for other full-token tokendata queries only. */ |
| 248757 | int nIter; |
| 248758 | int nIterAlloc; |
| 248759 | Fts5PoslistReader *aPoslistReader; |
| 248760 | int *aPoslistToIter; |
| 248761 | Fts5Iter *apIter[1]; |
| 248762 | }; |
| 248763 | |
| 248764 | /* |
| 248765 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 248766 | ** merges the two arrays together and writes the result to output array |
| 248767 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| 248768 | ** |
| 248769 | ** Duplicate entries are copied into the output. So the size of the output |
| 248770 | ** array is always (n1+n2) entries. |
| 248771 | */ |
| 248772 | static void fts5TokendataMerge( |
| 248773 | Fts5TokenDataMap *a1, int n1, /* Input array 1 */ |
| 248774 | Fts5TokenDataMap *a2, int n2, /* Input array 2 */ |
| 248775 | Fts5TokenDataMap *aOut /* Output array */ |
| 248776 | ){ |
| 248777 | int i1 = 0; |
| 248778 | int i2 = 0; |
| 248779 | |
| 248780 | assert( n1>=0 && n2>=0 ); |
| 248781 | while( i1<n1 || i2<n2 ){ |
| 248782 | Fts5TokenDataMap *pOut = &aOut[i1+i2]; |
| 248783 | if( i2>=n2 || (i1<n1 && ( |
| 248784 | a1[i1].iRowid<a2[i2].iRowid |
| 248785 | || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos) |
| 248786 | ))){ |
| 248787 | memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap)); |
| 248788 | i1++; |
| 248789 | }else{ |
| 248790 | memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap)); |
| 248791 | i2++; |
| 248792 | } |
| 248793 | } |
| 248794 | } |
| 248795 | |
| 248796 | |
| 248797 | /* |
| 248798 | ** Append a mapping to the token-map belonging to object pT. |
| 248799 | */ |
| 248800 | static void fts5TokendataIterAppendMap( |
| 248801 | Fts5Index *p, |
| 248802 | Fts5TokenDataIter *pT, |
| 248803 | int iIter, |
| 248804 | int nByte, |
| 248805 | i64 iRowid, |
| 248806 | i64 iPos |
| 248807 | ){ |
| 248808 | if( p->rc==SQLITE_OK ){ |
| 248809 | if( pT->nMap==pT->nMapAlloc ){ |
| 248810 | int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; |
| 248811 | int nAlloc = nNew * sizeof(Fts5TokenDataMap); |
| 248812 | Fts5TokenDataMap *aNew; |
| 248813 | |
| 248814 | aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc); |
| 248815 | if( aNew==0 ){ |
| 248816 | p->rc = SQLITE_NOMEM; |
| 248817 | return; |
| 248818 | } |
| 248819 | |
| 248820 | pT->aMap = aNew; |
| 248821 | pT->nMapAlloc = nNew; |
| 248822 | } |
| 248823 | |
| 248824 | pT->aMap[pT->nMap].iRowid = iRowid; |
| 248825 | pT->aMap[pT->nMap].iPos = iPos; |
| 248826 | pT->aMap[pT->nMap].iIter = iIter; |
| 248827 | pT->aMap[pT->nMap].nByte = nByte; |
| 248828 | pT->nMap++; |
| 248829 | } |
| 248830 | } |
| 248831 | |
| 248832 | /* |
| 248833 | ** Sort the contents of the pT->aMap[] array. |
| 248834 | ** |
| 248835 | ** The sorting algorithm requries a malloc(). If this fails, an error code |
| 248836 | ** is left in Fts5Index.rc before returning. |
| 248837 | */ |
| 248838 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 248839 | Fts5TokenDataMap *aTmp = 0; |
| 248840 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| 248841 | |
| 248842 | aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 248843 | if( aTmp ){ |
| 248844 | Fts5TokenDataMap *a1 = pT->aMap; |
| 248845 | Fts5TokenDataMap *a2 = aTmp; |
| 248846 | i64 nHalf; |
| 248847 | |
| 248848 | for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){ |
| 248849 | int i1; |
| 248850 | for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){ |
| 248851 | int n1 = MIN(nHalf, pT->nMap-i1); |
| 248852 | int n2 = MIN(nHalf, pT->nMap-i1-n1); |
| 248853 | fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]); |
| 248854 | } |
| 248855 | SWAPVAL(Fts5TokenDataMap*, a1, a2); |
| 248856 | } |
| 248857 | |
| 248858 | if( a1!=pT->aMap ){ |
| 248859 | memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap)); |
| 248860 | } |
| 248861 | sqlite3_free(aTmp); |
| 248862 | |
| 248863 | #ifdef SQLITE_DEBUG |
| 248864 | { |
| 248865 | int ii; |
| 248866 | for(ii=1; ii<pT->nMap; ii++){ |
| 248867 | Fts5TokenDataMap *p1 = &pT->aMap[ii-1]; |
| 248868 | Fts5TokenDataMap *p2 = &pT->aMap[ii]; |
| 248869 | assert( p1->iRowid<p2->iRowid |
| 248870 | || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos) |
| 248871 | ); |
| 248872 | } |
| 248873 | } |
| 248874 | #endif |
| 248875 | } |
| 248876 | } |
| 248877 | |
| 248878 | /* |
| 248879 | ** Delete an Fts5TokenDataIter structure and its contents. |
| 248880 | */ |
| 248881 | static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ |
| 248882 | if( pSet ){ |
| 248883 | int ii; |
| 248884 | for(ii=0; ii<pSet->nIter; ii++){ |
| 248885 | fts5MultiIterFree(pSet->apIter[ii]); |
| 248886 | } |
| 248887 | fts5BufferFree(&pSet->terms); |
| 248888 | sqlite3_free(pSet->aPoslistReader); |
| 248889 | sqlite3_free(pSet->aMap); |
| 248890 | sqlite3_free(pSet); |
| 248891 | } |
| 248892 | } |
| 248893 | |
| 248894 | |
| 248895 | /* |
| 248896 | ** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata() |
| 248897 | ** to pass data to prefixIterSetupTokendataCb(). |
| 248898 | */ |
| 248899 | typedef struct TokendataSetupCtx TokendataSetupCtx; |
| 248900 | struct TokendataSetupCtx { |
| 248901 | Fts5TokenDataIter *pT; /* Object being populated with mappings */ |
| 248902 | int iTermOff; /* Offset of current term in terms.p[] */ |
| 248903 | int nTermByte; /* Size of current term in bytes */ |
| 248904 | }; |
| 248905 | |
| 248906 | /* |
| 248907 | ** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This |
| 248908 | ** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each |
| 248909 | ** position in the current position-list. It doesn't matter that some of |
| 248910 | ** these may be out of order - they will be sorted later. |
| 248911 | */ |
| 248912 | static void prefixIterSetupTokendataCb( |
| 248913 | Fts5Index *p, |
| 248914 | void *pCtx, |
| 248915 | Fts5Iter *p1, |
| 248916 | const u8 *pNew, |
| 248917 | int nNew |
| 248918 | ){ |
| 248919 | TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx; |
| 248920 | int iPosOff = 0; |
| 248921 | i64 iPos = 0; |
| 248922 | |
| 248923 | if( pNew ){ |
| 248924 | pSetup->nTermByte = nNew-1; |
| 248925 | pSetup->iTermOff = pSetup->pT->terms.n; |
| 248926 | fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1); |
| 248927 | } |
| 248928 | |
| 248929 | while( 0==sqlite3Fts5PoslistNext64( |
| 248930 | p1->base.pData, p1->base.nData, &iPosOff, &iPos |
| 248931 | ) ){ |
| 248932 | fts5TokendataIterAppendMap(p, |
| 248933 | pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos |
| 248934 | ); |
| 248935 | } |
| 248936 | } |
| 248937 | |
| 248938 | |
| 248939 | /* |
| 248940 | ** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries(). |
| 248941 | */ |
| 248942 | typedef struct PrefixSetupCtx PrefixSetupCtx; |
| 248943 | struct PrefixSetupCtx { |
| 248944 | void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); |
| 248945 | void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); |
| 248946 | i64 iLastRowid; |
| 248947 | int nMerge; |
| 248948 | Fts5Buffer *aBuf; |
| 248949 | int nBuf; |
| 248950 | Fts5Buffer doclist; |
| 248951 | TokendataSetupCtx *pTokendata; |
| 248952 | }; |
| 248953 | |
| 248954 | /* |
| 248955 | ** fts5VisitEntries() callback used by fts5SetupPrefixIter() |
| 248956 | */ |
| 248957 | static void prefixIterSetupCb( |
| 248958 | Fts5Index *p, |
| 248959 | void *pCtx, |
| 248960 | Fts5Iter *p1, |
| 248961 | const u8 *pNew, |
| 248962 | int nNew |
| 248963 | ){ |
| 248964 | PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx; |
| 248965 | const int nMerge = pSetup->nMerge; |
| 248966 | |
| 248967 | if( p1->base.nData>0 ){ |
| 248968 | if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){ |
| 248969 | int i; |
| 248970 | for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){ |
| 248971 | int i1 = i*nMerge; |
| 248972 | int iStore; |
| 248973 | assert( i1+nMerge<=pSetup->nBuf ); |
| 248974 | for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248975 | if( pSetup->aBuf[iStore].n==0 ){ |
| 248976 | fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]); |
| 248977 | fts5BufferZero(&pSetup->doclist); |
| 248978 | break; |
| 248979 | } |
| 248980 | } |
| 248981 | if( iStore==i1+nMerge ){ |
| 248982 | pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]); |
| 248983 | for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248984 | fts5BufferZero(&pSetup->aBuf[iStore]); |
| 248985 | } |
| 248986 | } |
| 248987 | } |
| 248988 | pSetup->iLastRowid = 0; |
| 248989 | } |
| 248990 | |
| 248991 | pSetup->xAppend( |
| 248992 | p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist |
| 248993 | ); |
| 248994 | pSetup->iLastRowid = p1->base.iRowid; |
| 248995 | } |
| 248996 | |
| 248997 | if( pSetup->pTokendata ){ |
| 248998 | prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew); |
| 248999 | } |
| 249000 | } |
| 249001 | |
| 249002 | static void fts5SetupPrefixIter( |
| 249003 | Fts5Index *p, /* Index to read from */ |
| 249004 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 249005 | int iIdx, /* Index to scan for data */ |
| 249006 | u8 *pToken, /* Buffer containing prefix to match */ |
| @@ -248166,137 +249007,89 @@ | |
| 249007 | int nToken, /* Size of buffer pToken in bytes */ |
| 249008 | Fts5Colset *pColset, /* Restrict matches to these columns */ |
| 249009 | Fts5Iter **ppIter /* OUT: New iterator */ |
| 249010 | ){ |
| 249011 | Fts5Structure *pStruct; |
| 249012 | PrefixSetupCtx s; |
| 249013 | TokendataSetupCtx s2; |
| 249014 | |
| 249015 | memset(&s, 0, sizeof(s)); |
| 249016 | memset(&s2, 0, sizeof(s2)); |
| 249017 | |
| 249018 | s.nMerge = 1; |
| 249019 | s.iLastRowid = 0; |
| 249020 | s.nBuf = 32; |
| 249021 | if( iIdx==0 |
| 249022 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 249023 | && p->pConfig->bPrefixInsttoken |
| 249024 | ){ |
| 249025 | s.pTokendata = &s2; |
| 249026 | s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); |
| 249027 | } |
| 249028 | |
| 249029 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 249030 | s.xMerge = fts5MergeRowidLists; |
| 249031 | s.xAppend = fts5AppendRowid; |
| 249032 | }else{ |
| 249033 | s.nMerge = FTS5_MERGE_NLIST-1; |
| 249034 | s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ |
| 249035 | s.xMerge = fts5MergePrefixLists; |
| 249036 | s.xAppend = fts5AppendPoslist; |
| 249037 | } |
| 249038 | |
| 249039 | s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf); |
| 249040 | pStruct = fts5StructureRead(p); |
| 249041 | assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) ); |
| 249042 | |
| 249043 | if( p->rc==SQLITE_OK ){ |
| 249044 | void *pCtx = (void*)&s; |
| 249045 | int i; |
| 249046 | Fts5Data *pData; |
| 249047 | |
| 249048 | /* If iIdx is non-zero, then it is the number of a prefix-index for |
| 249049 | ** prefixes 1 character longer than the prefix being queried for. That |
| 249050 | ** index contains all the doclists required, except for the one |
| 249051 | ** corresponding to the prefix itself. That one is extracted from the |
| 249052 | ** main term index here. */ |
| 249053 | if( iIdx!=0 ){ |
| 249054 | pToken[0] = FTS5_MAIN_PREFIX; |
| 249055 | fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx); |
| 249056 | } |
| 249057 | |
| 249058 | pToken[0] = FTS5_MAIN_PREFIX + iIdx; |
| 249059 | fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx); |
| 249060 | |
| 249061 | assert( (s.nBuf%s.nMerge)==0 ); |
| 249062 | for(i=0; i<s.nBuf; i+=s.nMerge){ |
| 249063 | int iFree; |
| 249064 | if( p->rc==SQLITE_OK ){ |
| 249065 | s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]); |
| 249066 | } |
| 249067 | for(iFree=i; iFree<i+s.nMerge; iFree++){ |
| 249068 | fts5BufferFree(&s.aBuf[iFree]); |
| 249069 | } |
| 249070 | } |
| 249071 | |
| 249072 | pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING); |
| 249073 | if( pData ){ |
| 249074 | pData->p = (u8*)&pData[1]; |
| 249075 | pData->nn = pData->szLeaf = s.doclist.n; |
| 249076 | if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n); |
| 249077 | fts5MultiIterNew2(p, pData, bDesc, ppIter); |
| 249078 | } |
| 249079 | |
| 249080 | if( p->rc==SQLITE_OK && s.pTokendata ){ |
| 249081 | fts5TokendataIterSortMap(p, s2.pT); |
| 249082 | (*ppIter)->pTokenDataIter = s2.pT; |
| 249083 | s2.pT = 0; |
| 249084 | } |
| 249085 | } |
| 249086 | |
| 249087 | fts5TokendataIterDelete(s2.pT); |
| 249088 | fts5BufferFree(&s.doclist); |
| 249089 | fts5StructureRelease(pStruct); |
| 249090 | sqlite3_free(s.aBuf); |
| 249091 | } |
| 249092 | |
| 249093 | |
| 249094 | /* |
| 249095 | ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain |
| @@ -248546,42 +249339,10 @@ | |
| 249339 | static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ |
| 249340 | fts5DataRelease(pSeg->pLeaf); |
| 249341 | pSeg->pLeaf = 0; |
| 249342 | } |
| 249343 | |
| 249344 | /* |
| 249345 | ** This function appends iterator pAppend to Fts5TokenDataIter pIn and |
| 249346 | ** returns the result. |
| 249347 | */ |
| 249348 | static Fts5TokenDataIter *fts5AppendTokendataIter( |
| @@ -248614,58 +249375,10 @@ | |
| 249375 | assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); |
| 249376 | |
| 249377 | return pRet; |
| 249378 | } |
| 249379 | |
| 249380 | /* |
| 249381 | ** The iterator passed as the only argument must be a tokendata=1 iterator |
| 249382 | ** (pIter->pTokenDataIter!=0). This function sets the iterator output |
| 249383 | ** variables (pIter->base.*) according to the contents of the current |
| 249384 | ** row. |
| @@ -248702,11 +249415,11 @@ | |
| 249415 | int eDetail = pIter->pIndex->pConfig->eDetail; |
| 249416 | pIter->base.bEof = 0; |
| 249417 | pIter->base.iRowid = iRowid; |
| 249418 | |
| 249419 | if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ |
| 249420 | fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1); |
| 249421 | }else |
| 249422 | if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ |
| 249423 | int nReader = 0; |
| 249424 | int nByte = 0; |
| 249425 | i64 iPrev = 0; |
| @@ -248955,10 +249668,11 @@ | |
| 249668 | |
| 249669 | if( p->rc==SQLITE_OK ){ |
| 249670 | pRet = fts5MultiIterAlloc(p, 0); |
| 249671 | } |
| 249672 | if( pRet ){ |
| 249673 | pRet->nSeg = 0; |
| 249674 | pRet->pTokenDataIter = pSet; |
| 249675 | if( pSet ){ |
| 249676 | fts5IterSetOutputsTokendata(pRet); |
| 249677 | }else{ |
| 249678 | pRet->base.bEof = 1; |
| @@ -248969,11 +249683,10 @@ | |
| 249683 | |
| 249684 | fts5StructureRelease(pStruct); |
| 249685 | fts5BufferFree(&bSeek); |
| 249686 | return pRet; |
| 249687 | } |
| 249688 | |
| 249689 | /* |
| 249690 | ** Open a new iterator to iterate though all rowid that match the |
| 249691 | ** specified token or token prefix. |
| 249692 | */ |
| @@ -248995,10 +249708,15 @@ | |
| 249708 | int iIdx = 0; /* Index to search */ |
| 249709 | int iPrefixIdx = 0; /* +1 prefix index */ |
| 249710 | int bTokendata = pConfig->bTokendata; |
| 249711 | if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); |
| 249712 | |
| 249713 | /* The NOTOKENDATA flag is set when each token in a tokendata=1 table |
| 249714 | ** should be treated individually, instead of merging all those with |
| 249715 | ** a common prefix into a single entry. This is used, for example, by |
| 249716 | ** queries performed as part of an integrity-check, or by the fts5vocab |
| 249717 | ** module. */ |
| 249718 | if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ |
| 249719 | bTokendata = 0; |
| 249720 | } |
| 249721 | |
| 249722 | /* Figure out which index to search and set iIdx accordingly. If this |
| @@ -249025,11 +249743,11 @@ | |
| 249743 | if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; |
| 249744 | } |
| 249745 | } |
| 249746 | |
| 249747 | if( bTokendata && iIdx==0 ){ |
| 249748 | buf.p[0] = FTS5_MAIN_PREFIX; |
| 249749 | pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); |
| 249750 | }else if( iIdx<=pConfig->nPrefix ){ |
| 249751 | /* Straight index lookup */ |
| 249752 | Fts5Structure *pStruct = fts5StructureRead(p); |
| 249753 | buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); |
| @@ -249038,11 +249756,11 @@ | |
| 249756 | pColset, buf.p, nToken+1, -1, 0, &pRet |
| 249757 | ); |
| 249758 | fts5StructureRelease(pStruct); |
| 249759 | } |
| 249760 | }else{ |
| 249761 | /* Scan multiple terms in the main index for a prefix query. */ |
| 249762 | int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; |
| 249763 | fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); |
| 249764 | if( pRet==0 ){ |
| 249765 | assert( p->rc!=SQLITE_OK ); |
| 249766 | }else{ |
| @@ -249074,11 +249792,12 @@ | |
| 249792 | ** Move to the next matching rowid. |
| 249793 | */ |
| 249794 | static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ |
| 249795 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249796 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 249797 | if( pIter->nSeg==0 ){ |
| 249798 | assert( pIter->pTokenDataIter ); |
| 249799 | fts5TokendataIterNext(pIter, 0, 0); |
| 249800 | }else{ |
| 249801 | fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); |
| 249802 | } |
| 249803 | return fts5IndexReturn(pIter->pIndex); |
| @@ -249111,11 +249830,12 @@ | |
| 249830 | ** definition of "at or after" depends on whether this iterator iterates |
| 249831 | ** in ascending or descending rowid order. |
| 249832 | */ |
| 249833 | static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ |
| 249834 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249835 | if( pIter->nSeg==0 ){ |
| 249836 | assert( pIter->pTokenDataIter ); |
| 249837 | fts5TokendataIterNext(pIter, 1, iMatch); |
| 249838 | }else{ |
| 249839 | fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); |
| 249840 | } |
| 249841 | return fts5IndexReturn(pIter->pIndex); |
| @@ -249129,32 +249849,87 @@ | |
| 249849 | const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); |
| 249850 | assert_nc( z || n<=1 ); |
| 249851 | *pn = n-1; |
| 249852 | return (z ? &z[1] : 0); |
| 249853 | } |
| 249854 | |
| 249855 | /* |
| 249856 | ** pIter is a prefix query. This function populates pIter->pTokenDataIter |
| 249857 | ** with an Fts5TokenDataIter object containing mappings for all rows |
| 249858 | ** matched by the query. |
| 249859 | */ |
| 249860 | static int fts5SetupPrefixIterTokendata( |
| 249861 | Fts5Iter *pIter, |
| 249862 | const char *pToken, /* Token prefix to search for */ |
| 249863 | int nToken /* Size of pToken in bytes */ |
| 249864 | ){ |
| 249865 | Fts5Index *p = pIter->pIndex; |
| 249866 | Fts5Buffer token = {0, 0, 0}; |
| 249867 | TokendataSetupCtx ctx; |
| 249868 | |
| 249869 | memset(&ctx, 0, sizeof(ctx)); |
| 249870 | |
| 249871 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 249872 | ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); |
| 249873 | |
| 249874 | if( p->rc==SQLITE_OK ){ |
| 249875 | |
| 249876 | /* Fill in the token prefix to search for */ |
| 249877 | token.p[0] = FTS5_MAIN_PREFIX; |
| 249878 | memcpy(&token.p[1], pToken, nToken); |
| 249879 | token.n = nToken+1; |
| 249880 | |
| 249881 | fts5VisitEntries( |
| 249882 | p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx |
| 249883 | ); |
| 249884 | |
| 249885 | fts5TokendataIterSortMap(p, ctx.pT); |
| 249886 | } |
| 249887 | |
| 249888 | if( p->rc==SQLITE_OK ){ |
| 249889 | pIter->pTokenDataIter = ctx.pT; |
| 249890 | }else{ |
| 249891 | fts5TokendataIterDelete(ctx.pT); |
| 249892 | } |
| 249893 | fts5BufferFree(&token); |
| 249894 | |
| 249895 | return fts5IndexReturn(p); |
| 249896 | } |
| 249897 | |
| 249898 | /* |
| 249899 | ** This is used by xInstToken() to access the token at offset iOff, column |
| 249900 | ** iCol of row iRowid. The token is returned via output variables *ppOut |
| 249901 | ** and *pnOut. The iterator passed as the first argument must be a tokendata=1 |
| 249902 | ** iterator (pIter->pTokenDataIter!=0). |
| 249903 | ** |
| 249904 | ** pToken/nToken: |
| 249905 | */ |
| 249906 | static int sqlite3Fts5IterToken( |
| 249907 | Fts5IndexIter *pIndexIter, |
| 249908 | const char *pToken, int nToken, |
| 249909 | i64 iRowid, |
| 249910 | int iCol, |
| 249911 | int iOff, |
| 249912 | const char **ppOut, int *pnOut |
| 249913 | ){ |
| 249914 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249915 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249916 | i64 iPos = (((i64)iCol)<<32) + iOff; |
| 249917 | Fts5TokenDataMap *aMap = 0; |
| 249918 | int i1 = 0; |
| 249919 | int i2 = 0; |
| 249920 | int iTest = 0; |
| 249921 | |
| 249922 | assert( pT || (pToken && pIter->nSeg>0) ); |
| 249923 | if( pT==0 ){ |
| 249924 | int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken); |
| 249925 | if( rc!=SQLITE_OK ) return rc; |
| 249926 | pT = pIter->pTokenDataIter; |
| 249927 | } |
| 249928 | |
| 249929 | i2 = pT->nMap; |
| 249930 | aMap = pT->aMap; |
| 249931 | |
| 249932 | while( i2>i1 ){ |
| 249933 | iTest = (i1 + i2) / 2; |
| 249934 | |
| 249935 | if( aMap[iTest].iRowid<iRowid ){ |
| @@ -249174,13 +249949,19 @@ | |
| 249949 | } |
| 249950 | } |
| 249951 | } |
| 249952 | |
| 249953 | if( i2>i1 ){ |
| 249954 | if( pIter->nSeg==0 ){ |
| 249955 | Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; |
| 249956 | *ppOut = (const char*)pMap->aSeg[0].term.p+1; |
| 249957 | *pnOut = pMap->aSeg[0].term.n-1; |
| 249958 | }else{ |
| 249959 | Fts5TokenDataMap *p = &aMap[iTest]; |
| 249960 | *ppOut = (const char*)&pT->terms.p[p->iIter]; |
| 249961 | *pnOut = aMap[iTest].nByte; |
| 249962 | } |
| 249963 | } |
| 249964 | |
| 249965 | return SQLITE_OK; |
| 249966 | } |
| 249967 | |
| @@ -249188,11 +249969,13 @@ | |
| 249969 | ** Clear any existing entries from the token-map associated with the |
| 249970 | ** iterator passed as the only argument. |
| 249971 | */ |
| 249972 | static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ |
| 249973 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249974 | if( pIter && pIter->pTokenDataIter |
| 249975 | && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL) |
| 249976 | ){ |
| 249977 | pIter->pTokenDataIter->nMap = 0; |
| 249978 | } |
| 249979 | } |
| 249980 | |
| 249981 | /* |
| @@ -249208,21 +249991,33 @@ | |
| 249991 | i64 iRowid, int iCol, int iOff |
| 249992 | ){ |
| 249993 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249994 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249995 | Fts5Index *p = pIter->pIndex; |
| 249996 | i64 iPos = (((i64)iCol)<<32) + iOff; |
| 249997 | |
| 249998 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 249999 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 250000 | if( pIter->nSeg>0 ){ |
| 250001 | /* This is a prefix term iterator. */ |
| 250002 | if( pT==0 ){ |
| 250003 | pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); |
| 250004 | pIter->pTokenDataIter = pT; |
| 250005 | } |
| 250006 | if( pT ){ |
| 250007 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 250008 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| 250009 | } |
| 250010 | }else{ |
| 250011 | int ii; |
| 250012 | for(ii=0; ii<pT->nIter; ii++){ |
| 250013 | Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; |
| 250014 | if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; |
| 250015 | } |
| 250016 | if( ii<pT->nIter ){ |
| 250017 | fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos); |
| 250018 | } |
| 250019 | } |
| 250020 | return fts5IndexReturn(p); |
| 250021 | } |
| 250022 | |
| 250023 | /* |
| @@ -250771,11 +251566,11 @@ | |
| 251566 | return rc; |
| 251567 | } |
| 251568 | |
| 251569 | /* |
| 251570 | ** We must have a single struct=? constraint that will be passed through |
| 251571 | ** into the xFilter method. If there is no valid struct=? constraint, |
| 251572 | ** then return an SQLITE_CONSTRAINT error. |
| 251573 | */ |
| 251574 | static int fts5structBestIndexMethod( |
| 251575 | sqlite3_vtab *tab, |
| 251576 | sqlite3_index_info *pIdxInfo |
| @@ -251113,12 +251908,22 @@ | |
| 251908 | i64 iNextId; /* Used to allocate unique cursor ids */ |
| 251909 | Fts5Auxiliary *pAux; /* First in list of all aux. functions */ |
| 251910 | Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ |
| 251911 | Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ |
| 251912 | Fts5Cursor *pCsr; /* First in list of all open cursors */ |
| 251913 | u32 aLocaleHdr[4]; |
| 251914 | }; |
| 251915 | |
| 251916 | /* |
| 251917 | ** Size of header on fts5_locale() values. And macro to access a buffer |
| 251918 | ** containing a copy of the header from an Fts5Config pointer. |
| 251919 | */ |
| 251920 | #define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) |
| 251921 | #define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) |
| 251922 | |
| 251923 | #define FTS5_INSTTOKEN_SUBTYPE 73 |
| 251924 | |
| 251925 | /* |
| 251926 | ** Each auxiliary function registered with the FTS5 module is represented |
| 251927 | ** by an object of the following type. All such objects are stored as part |
| 251928 | ** of the Fts5Global.pAux list. |
| 251929 | */ |
| @@ -251277,16 +252082,10 @@ | |
| 252082 | #define FTS5CSR_REQUIRE_POSLIST 0x40 |
| 252083 | |
| 252084 | #define BitFlagAllTest(x,y) (((x) & (y))==(y)) |
| 252085 | #define BitFlagTest(x,y) (((x) & (y))!=0) |
| 252086 | |
| 252087 | |
| 252088 | /* |
| 252089 | ** Macros to Set(), Clear() and Test() cursor flags. |
| 252090 | */ |
| 252091 | #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) |
| @@ -251359,14 +252158,20 @@ | |
| 252158 | #else |
| 252159 | # define fts5CheckTransactionState(x,y,z) |
| 252160 | #endif |
| 252161 | |
| 252162 | /* |
| 252163 | ** Return true if pTab is a contentless table. If parameter bIncludeUnindexed |
| 252164 | ** is true, this includes contentless tables that store UNINDEXED columns |
| 252165 | ** only. |
| 252166 | */ |
| 252167 | static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){ |
| 252168 | int eContent = pTab->p.pConfig->eContent; |
| 252169 | return ( |
| 252170 | eContent==FTS5_CONTENT_NONE |
| 252171 | || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED) |
| 252172 | ); |
| 252173 | } |
| 252174 | |
| 252175 | /* |
| 252176 | ** Delete a virtual table handle allocated by fts5InitVtab(). |
| 252177 | */ |
| @@ -251653,10 +252458,11 @@ | |
| 252458 | ){ |
| 252459 | /* A MATCH operator or equivalent */ |
| 252460 | if( p->usable==0 || iCol<0 ){ |
| 252461 | /* As there exists an unusable MATCH constraint this is an |
| 252462 | ** unusable plan. Return SQLITE_CONSTRAINT. */ |
| 252463 | idxStr[iIdxStr] = 0; |
| 252464 | return SQLITE_CONSTRAINT; |
| 252465 | }else{ |
| 252466 | if( iCol==nCol+1 ){ |
| 252467 | if( bSeenRank ) continue; |
| 252468 | idxStr[iIdxStr++] = 'r'; |
| @@ -252286,11 +253092,11 @@ | |
| 253092 | ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale |
| 253093 | ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain |
| 253094 | ** valid until after the final call to sqlite3Fts5Tokenize() that will use |
| 253095 | ** the locale. |
| 253096 | */ |
| 253097 | static void sqlite3Fts5SetLocale( |
| 253098 | Fts5Config *pConfig, |
| 253099 | const char *zLocale, |
| 253100 | int nLocale |
| 253101 | ){ |
| 253102 | Fts5TokenizerConfig *pT = &pConfig->t; |
| @@ -252297,141 +253103,88 @@ | |
| 253103 | pT->pLocale = zLocale; |
| 253104 | pT->nLocale = nLocale; |
| 253105 | } |
| 253106 | |
| 253107 | /* |
| 253108 | ** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). |
| 253109 | */ |
| 253110 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ |
| 253111 | sqlite3Fts5SetLocale(pConfig, 0, 0); |
| 253112 | } |
| 253113 | |
| 253114 | /* |
| 253115 | ** Return true if the value passed as the only argument is an |
| 253116 | ** fts5_locale() value. |
| 253117 | */ |
| 253118 | static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ |
| 253119 | int ret = 0; |
| 253120 | if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ |
| 253121 | /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case. |
| 253122 | ** If the blob was created using zeroblob(), then sqlite3_value_blob() |
| 253123 | ** may call malloc(). If this malloc() fails, then the values returned |
| 253124 | ** by both value_blob() and value_bytes() will be 0. If value_bytes() were |
| 253125 | ** called first, then the NULL pointer returned by value_blob() might |
| 253126 | ** be dereferenced. */ |
| 253127 | const u8 *pBlob = sqlite3_value_blob(pVal); |
| 253128 | int nBlob = sqlite3_value_bytes(pVal); |
| 253129 | if( nBlob>FTS5_LOCALE_HDR_SIZE |
| 253130 | && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE) |
| 253131 | ){ |
| 253132 | ret = 1; |
| 253133 | } |
| 253134 | } |
| 253135 | return ret; |
| 253136 | } |
| 253137 | |
| 253138 | /* |
| 253139 | ** Value pVal is guaranteed to be an fts5_locale() value, according to |
| 253140 | ** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale |
| 253141 | ** from the value and returns them separately. |
| 253142 | ** |
| 253143 | ** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set |
| 253144 | ** to point to buffers containing the text and locale, as utf-8, |
| 253145 | ** respectively. In this case output parameters (*pnText) and (*pnLoc) are |
| 253146 | ** set to the sizes in bytes of these two buffers. |
| 253147 | ** |
| 253148 | ** Or, if an error occurs, then an SQLite error code is returned. The final |
| 253149 | ** value of the four output parameters is undefined in this case. |
| 253150 | */ |
| 253151 | static int sqlite3Fts5DecodeLocaleValue( |
| 253152 | sqlite3_value *pVal, |
| 253153 | const char **ppText, |
| 253154 | int *pnText, |
| 253155 | const char **ppLoc, |
| 253156 | int *pnLoc |
| 253157 | ){ |
| 253158 | const char *p = sqlite3_value_blob(pVal); |
| 253159 | int n = sqlite3_value_bytes(pVal); |
| 253160 | int nLoc = 0; |
| 253161 | |
| 253162 | assert( sqlite3_value_type(pVal)==SQLITE_BLOB ); |
| 253163 | assert( n>FTS5_LOCALE_HDR_SIZE ); |
| 253164 | |
| 253165 | for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){ |
| 253166 | if( nLoc==(n-1) ){ |
| 253167 | return SQLITE_MISMATCH; |
| 253168 | } |
| 253169 | } |
| 253170 | *ppLoc = &p[FTS5_LOCALE_HDR_SIZE]; |
| 253171 | *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE; |
| 253172 | |
| 253173 | *ppText = &p[nLoc+1]; |
| 253174 | *pnText = n - nLoc - 1; |
| 253175 | return SQLITE_OK; |
| 253176 | } |
| 253177 | |
| 253178 | /* |
| 253179 | ** Argument pVal is the text of a full-text search expression. It may or |
| 253180 | ** may not have been wrapped by fts5_locale(). This function extracts |
| 253181 | ** the text of the expression, and sets output variable (*pzText) to |
| 253182 | ** point to a nul-terminated buffer containing the expression. |
| 253183 | ** |
| 253184 | ** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called |
| 253185 | ** to set the tokenizer to use the specified locale. |
| 253186 | ** |
| 253187 | ** If output variable (*pbFreeAndReset) is set to true, then the caller |
| 253188 | ** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer |
| 253189 | ** locale, and (b) call sqlite3_free() to free (*pzText). |
| 253190 | */ |
| @@ -252439,28 +253192,26 @@ | |
| 253192 | Fts5Config *pConfig, /* Fts5 configuration */ |
| 253193 | sqlite3_value *pVal, /* Value to extract expression text from */ |
| 253194 | char **pzText, /* OUT: nul-terminated buffer of text */ |
| 253195 | int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ |
| 253196 | ){ |
| 253197 | int rc = SQLITE_OK; |
| 253198 | |
| 253199 | if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ |
| 253200 | const char *pText = 0; |
| 253201 | int nText = 0; |
| 253202 | const char *pLoc = 0; |
| 253203 | int nLoc = 0; |
| 253204 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); |
| 253205 | *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText); |
| 253206 | if( rc==SQLITE_OK ){ |
| 253207 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); |
| 253208 | } |
| 253209 | *pbFreeAndReset = 1; |
| 253210 | }else{ |
| 253211 | *pzText = (char*)sqlite3_value_text(pVal); |
| 253212 | *pbFreeAndReset = 0; |
| 253213 | } |
| 253214 | |
| 253215 | return rc; |
| 253216 | } |
| 253217 | |
| @@ -252493,10 +253244,11 @@ | |
| 253244 | sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ |
| 253245 | sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ |
| 253246 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ |
| 253247 | int iCol; /* Column on LHS of MATCH operator */ |
| 253248 | char **pzErrmsg = pConfig->pzErrmsg; |
| 253249 | int bPrefixInsttoken = pConfig->bPrefixInsttoken; |
| 253250 | int i; |
| 253251 | int iIdxStr = 0; |
| 253252 | Fts5Expr *pExpr = 0; |
| 253253 | |
| 253254 | assert( pConfig->bLock==0 ); |
| @@ -252528,10 +253280,13 @@ | |
| 253280 | int bInternal = 0; |
| 253281 | |
| 253282 | rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); |
| 253283 | if( rc!=SQLITE_OK ) goto filter_out; |
| 253284 | if( zText==0 ) zText = ""; |
| 253285 | if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){ |
| 253286 | pConfig->bPrefixInsttoken = 1; |
| 253287 | } |
| 253288 | |
| 253289 | iCol = 0; |
| 253290 | do{ |
| 253291 | iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 253292 | iIdxStr++; |
| @@ -252668,10 +253423,11 @@ | |
| 253423 | } |
| 253424 | |
| 253425 | filter_out: |
| 253426 | sqlite3Fts5ExprFree(pExpr); |
| 253427 | pConfig->pzErrmsg = pzErrmsg; |
| 253428 | pConfig->bPrefixInsttoken = bPrefixInsttoken; |
| 253429 | return rc; |
| 253430 | } |
| 253431 | |
| 253432 | /* |
| 253433 | ** This is the xEof method of the virtual table. SQLite calls this |
| @@ -252808,11 +253564,11 @@ | |
| 253564 | }else{ |
| 253565 | rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); |
| 253566 | } |
| 253567 | bLoadConfig = 1; |
| 253568 | }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ |
| 253569 | if( fts5IsContentless(pTab, 1) ){ |
| 253570 | fts5SetVtabError(pTab, |
| 253571 | "'rebuild' may not be used with a contentless fts5 table" |
| 253572 | ); |
| 253573 | rc = SQLITE_ERROR; |
| 253574 | }else{ |
| @@ -252877,17 +253633,78 @@ | |
| 253633 | sqlite3_value **apVal, |
| 253634 | i64 *piRowid |
| 253635 | ){ |
| 253636 | int rc = *pRc; |
| 253637 | if( rc==SQLITE_OK ){ |
| 253638 | rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid); |
| 253639 | } |
| 253640 | if( rc==SQLITE_OK ){ |
| 253641 | rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); |
| 253642 | } |
| 253643 | *pRc = rc; |
| 253644 | } |
| 253645 | |
| 253646 | /* |
| 253647 | ** |
| 253648 | ** This function is called when the user attempts an UPDATE on a contentless |
| 253649 | ** table. Parameter bRowidModified is true if the UPDATE statement modifies |
| 253650 | ** the rowid value. Parameter apVal[] contains the new values for each user |
| 253651 | ** defined column of the fts5 table. pConfig is the configuration object of the |
| 253652 | ** table being updated (guaranteed to be contentless). The contentless_delete=1 |
| 253653 | ** and contentless_unindexed=1 options may or may not be set. |
| 253654 | ** |
| 253655 | ** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite |
| 253656 | ** error code if it cannot. In this case an error message is also loaded into |
| 253657 | ** pConfig. Output parameter (*pbContent) is set to true if the caller should |
| 253658 | ** update the %_content table only - not the FTS index or any other shadow |
| 253659 | ** table. This occurs when an UPDATE modifies only UNINDEXED columns of the |
| 253660 | ** table. |
| 253661 | ** |
| 253662 | ** An UPDATE may proceed if: |
| 253663 | ** |
| 253664 | ** * The only columns modified are UNINDEXED columns, or |
| 253665 | ** |
| 253666 | ** * The contentless_delete=1 option was specified and all of the indexed |
| 253667 | ** columns (not a subset) have been modified. |
| 253668 | */ |
| 253669 | static int fts5ContentlessUpdate( |
| 253670 | Fts5Config *pConfig, |
| 253671 | sqlite3_value **apVal, |
| 253672 | int bRowidModified, |
| 253673 | int *pbContent |
| 253674 | ){ |
| 253675 | int ii; |
| 253676 | int bSeenIndex = 0; /* Have seen modified indexed column */ |
| 253677 | int bSeenIndexNC = 0; /* Have seen unmodified indexed column */ |
| 253678 | int rc = SQLITE_OK; |
| 253679 | |
| 253680 | for(ii=0; ii<pConfig->nCol; ii++){ |
| 253681 | if( pConfig->abUnindexed[ii]==0 ){ |
| 253682 | if( sqlite3_value_nochange(apVal[ii]) ){ |
| 253683 | bSeenIndexNC++; |
| 253684 | }else{ |
| 253685 | bSeenIndex++; |
| 253686 | } |
| 253687 | } |
| 253688 | } |
| 253689 | |
| 253690 | if( bSeenIndex==0 && bRowidModified==0 ){ |
| 253691 | *pbContent = 1; |
| 253692 | }else{ |
| 253693 | if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){ |
| 253694 | rc = SQLITE_ERROR; |
| 253695 | sqlite3Fts5ConfigErrmsg(pConfig, |
| 253696 | (pConfig->bContentlessDelete ? |
| 253697 | "%s a subset of columns on fts5 contentless-delete table: %s" : |
| 253698 | "%s contentless fts5 table: %s") |
| 253699 | , "cannot UPDATE", pConfig->zName |
| 253700 | ); |
| 253701 | } |
| 253702 | } |
| 253703 | |
| 253704 | return rc; |
| 253705 | } |
| 253706 | |
| 253707 | /* |
| 253708 | ** This function is the implementation of the xUpdate callback used by |
| 253709 | ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be |
| 253710 | ** inserted, updated or deleted. |
| @@ -252971,48 +253788,38 @@ | |
| 253788 | } |
| 253789 | |
| 253790 | assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); |
| 253791 | assert( nArg!=1 || eType0==SQLITE_INTEGER ); |
| 253792 | |
| 253793 | /* DELETE */ |
| 253794 | if( nArg==1 ){ |
| 253795 | /* It is only possible to DELETE from a contentless table if the |
| 253796 | ** contentless_delete=1 flag is set. */ |
| 253797 | if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){ |
| 253798 | fts5SetVtabError(pTab, |
| 253799 | "cannot DELETE from contentless fts5 table: %s", pConfig->zName |
| 253800 | ); |
| 253801 | rc = SQLITE_ERROR; |
| 253802 | }else{ |
| 253803 | i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ |
| 253804 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); |
| 253805 | bUpdateOrDelete = 1; |
| 253806 | } |
| 253807 | } |
| 253808 | |
| 253809 | /* INSERT or UPDATE */ |
| 253810 | else{ |
| 253811 | int eType1 = sqlite3_value_numeric_type(apVal[1]); |
| 253812 | |
| 253813 | /* It is an error to write an fts5_locale() value to a table without |
| 253814 | ** the locale=1 option. */ |
| 253815 | if( pConfig->bLocale==0 ){ |
| 253816 | int ii; |
| 253817 | for(ii=0; ii<pConfig->nCol; ii++){ |
| 253818 | sqlite3_value *pVal = apVal[ii+2]; |
| 253819 | if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ |
| 253820 | fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); |
| 253821 | rc = SQLITE_MISMATCH; |
| 253822 | goto update_out; |
| 253823 | } |
| 253824 | } |
| 253825 | } |
| @@ -253028,39 +253835,59 @@ | |
| 253835 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253836 | } |
| 253837 | |
| 253838 | /* UPDATE */ |
| 253839 | else{ |
| 253840 | Fts5Storage *pStorage = pTab->pStorage; |
| 253841 | i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ |
| 253842 | i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ |
| 253843 | int bContent = 0; /* Content only update */ |
| 253844 | |
| 253845 | /* If this is a contentless table (including contentless_unindexed=1 |
| 253846 | ** tables), check if the UPDATE may proceed. */ |
| 253847 | if( fts5IsContentless(pTab, 1) ){ |
| 253848 | rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent); |
| 253849 | if( rc!=SQLITE_OK ) goto update_out; |
| 253850 | } |
| 253851 | |
| 253852 | if( eType1!=SQLITE_INTEGER ){ |
| 253853 | rc = SQLITE_MISMATCH; |
| 253854 | }else if( iOld!=iNew ){ |
| 253855 | assert( bContent==0 ); |
| 253856 | if( eConflict==SQLITE_REPLACE ){ |
| 253857 | rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); |
| 253858 | if( rc==SQLITE_OK ){ |
| 253859 | rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0); |
| 253860 | } |
| 253861 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253862 | }else{ |
| 253863 | rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); |
| 253864 | if( rc==SQLITE_OK ){ |
| 253865 | rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid); |
| 253866 | } |
| 253867 | if( rc==SQLITE_OK ){ |
| 253868 | rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0); |
| 253869 | } |
| 253870 | if( rc==SQLITE_OK ){ |
| 253871 | rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid); |
| 253872 | } |
| 253873 | } |
| 253874 | }else if( bContent ){ |
| 253875 | /* This occurs when an UPDATE on a contentless table affects *only* |
| 253876 | ** UNINDEXED columns. This is a no-op for contentless_unindexed=0 |
| 253877 | ** tables, or a write to the %_content table only for =1 tables. */ |
| 253878 | assert( fts5IsContentless(pTab, 1) ); |
| 253879 | rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); |
| 253880 | if( rc==SQLITE_OK ){ |
| 253881 | rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid); |
| 253882 | } |
| 253883 | }else{ |
| 253884 | rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); |
| 253885 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 253886 | } |
| 253887 | bUpdateOrDelete = 1; |
| 253888 | sqlite3Fts5StorageReleaseDeleteRow(pStorage); |
| 253889 | } |
| 253890 | |
| 253891 | } |
| 253892 | } |
| 253893 | |
| @@ -253169,15 +253996,15 @@ | |
| 253996 | ){ |
| 253997 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 253998 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| 253999 | int rc = SQLITE_OK; |
| 254000 | |
| 254001 | sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); |
| 254002 | rc = sqlite3Fts5Tokenize(pTab->pConfig, |
| 254003 | FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken |
| 254004 | ); |
| 254005 | sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); |
| 254006 | |
| 254007 | return rc; |
| 254008 | } |
| 254009 | |
| 254010 | /* |
| @@ -253200,10 +254027,53 @@ | |
| 254027 | |
| 254028 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ |
| 254029 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 254030 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); |
| 254031 | } |
| 254032 | |
| 254033 | /* |
| 254034 | ** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This |
| 254035 | ** function extracts the text value of column iCol of the current row. |
| 254036 | ** Additionally, if there is an associated locale, it invokes |
| 254037 | ** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller |
| 254038 | ** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point |
| 254039 | ** after this function returns. |
| 254040 | ** |
| 254041 | ** If successful, (*ppText) is set to point to a buffer containing the text |
| 254042 | ** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that |
| 254043 | ** buffer in bytes. It is not guaranteed to be nul-terminated. If an error |
| 254044 | ** occurs, an SQLite error code is returned. The final values of the two |
| 254045 | ** output parameters are undefined in this case. |
| 254046 | */ |
| 254047 | static int fts5TextFromStmt( |
| 254048 | Fts5Config *pConfig, |
| 254049 | sqlite3_stmt *pStmt, |
| 254050 | int iCol, |
| 254051 | const char **ppText, |
| 254052 | int *pnText |
| 254053 | ){ |
| 254054 | sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1); |
| 254055 | const char *pLoc = 0; |
| 254056 | int nLoc = 0; |
| 254057 | int rc = SQLITE_OK; |
| 254058 | |
| 254059 | if( pConfig->bLocale |
| 254060 | && pConfig->eContent==FTS5_CONTENT_EXTERNAL |
| 254061 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) |
| 254062 | ){ |
| 254063 | rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); |
| 254064 | }else{ |
| 254065 | *ppText = (const char*)sqlite3_value_text(pVal); |
| 254066 | *pnText = sqlite3_value_bytes(pVal); |
| 254067 | if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){ |
| 254068 | pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol); |
| 254069 | nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol); |
| 254070 | } |
| 254071 | } |
| 254072 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); |
| 254073 | return rc; |
| 254074 | } |
| 254075 | |
| 254076 | static int fts5ApiColumnText( |
| 254077 | Fts5Context *pCtx, |
| 254078 | int iCol, |
| 254079 | const char **pz, |
| @@ -253214,20 +254084,18 @@ | |
| 254084 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| 254085 | |
| 254086 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); |
| 254087 | if( iCol<0 || iCol>=pTab->pConfig->nCol ){ |
| 254088 | rc = SQLITE_RANGE; |
| 254089 | }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){ |
| 254090 | *pz = 0; |
| 254091 | *pn = 0; |
| 254092 | }else{ |
| 254093 | rc = fts5SeekCursor(pCsr, 0); |
| 254094 | if( rc==SQLITE_OK ){ |
| 254095 | rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); |
| 254096 | sqlite3Fts5ClearLocale(pTab->pConfig); |
| 254097 | } |
| 254098 | } |
| 254099 | return rc; |
| 254100 | } |
| 254101 | |
| @@ -253249,11 +254117,11 @@ | |
| 254117 | int bLive = (pCsr->pSorter==0); |
| 254118 | |
| 254119 | if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ |
| 254120 | rc = SQLITE_RANGE; |
| 254121 | }else if( pConfig->eDetail!=FTS5_DETAIL_FULL |
| 254122 | && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) |
| 254123 | ){ |
| 254124 | *pa = 0; |
| 254125 | *pn = 0; |
| 254126 | return SQLITE_OK; |
| 254127 | }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ |
| @@ -253265,21 +254133,19 @@ | |
| 254133 | if( aPopulator==0 ) rc = SQLITE_NOMEM; |
| 254134 | if( rc==SQLITE_OK ){ |
| 254135 | rc = fts5SeekCursor(pCsr, 0); |
| 254136 | } |
| 254137 | for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ |
| 254138 | const char *z = 0; |
| 254139 | int n = 0; |
| 254140 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); |
| 254141 | if( rc==SQLITE_OK ){ |
| 254142 | rc = sqlite3Fts5ExprPopulatePoslists( |
| 254143 | pConfig, pCsr->pExpr, aPopulator, i, z, n |
| 254144 | ); |
| 254145 | } |
| 254146 | sqlite3Fts5ClearLocale(pConfig); |
| 254147 | } |
| 254148 | sqlite3_free(aPopulator); |
| 254149 | |
| 254150 | if( pCsr->pSorter ){ |
| 254151 | sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); |
| @@ -253447,11 +254313,11 @@ | |
| 254313 | |
| 254314 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ |
| 254315 | if( pConfig->bColumnsize ){ |
| 254316 | i64 iRowid = fts5CursorRowid(pCsr); |
| 254317 | rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); |
| 254318 | }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ |
| 254319 | int i; |
| 254320 | for(i=0; i<pConfig->nCol; i++){ |
| 254321 | if( pConfig->abUnindexed[i]==0 ){ |
| 254322 | pCsr->aColumnSize[i] = -1; |
| 254323 | } |
| @@ -253461,21 +254327,18 @@ | |
| 254327 | rc = fts5SeekCursor(pCsr, 0); |
| 254328 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 254329 | if( pConfig->abUnindexed[i]==0 ){ |
| 254330 | const char *z = 0; |
| 254331 | int n = 0; |
| 254332 | pCsr->aColumnSize[i] = 0; |
| 254333 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); |
| 254334 | if( rc==SQLITE_OK ){ |
| 254335 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, |
| 254336 | z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb |
| 254337 | ); |
| 254338 | } |
| 254339 | sqlite3Fts5ClearLocale(pConfig); |
| 254340 | } |
| 254341 | } |
| 254342 | } |
| 254343 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE); |
| 254344 | } |
| @@ -253738,46 +254601,23 @@ | |
| 254601 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); |
| 254602 | if( iCol<0 || iCol>=pConfig->nCol ){ |
| 254603 | rc = SQLITE_RANGE; |
| 254604 | }else if( |
| 254605 | pConfig->abUnindexed[iCol]==0 |
| 254606 | && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) |
| 254607 | && pConfig->bLocale |
| 254608 | ){ |
| 254609 | rc = fts5SeekCursor(pCsr, 0); |
| 254610 | if( rc==SQLITE_OK ){ |
| 254611 | const char *zDummy = 0; |
| 254612 | int nDummy = 0; |
| 254613 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy); |
| 254614 | if( rc==SQLITE_OK ){ |
| 254615 | *pzLocale = pConfig->t.pLocale; |
| 254616 | *pnLocale = pConfig->t.nLocale; |
| 254617 | } |
| 254618 | sqlite3Fts5ClearLocale(pConfig); |
| 254619 | } |
| 254620 | } |
| 254621 | |
| 254622 | return rc; |
| 254623 | } |
| @@ -253994,61 +254834,10 @@ | |
| 254834 | |
| 254835 | sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); |
| 254836 | return rc; |
| 254837 | } |
| 254838 | |
| 254839 | /* |
| 254840 | ** This is the xColumn method, called by SQLite to request a value from |
| 254841 | ** the row that the supplied cursor currently points to. |
| 254842 | */ |
| 254843 | static int fts5ColumnMethod( |
| @@ -254087,26 +254876,31 @@ | |
| 254876 | if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){ |
| 254877 | fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); |
| 254878 | } |
| 254879 | } |
| 254880 | }else{ |
| 254881 | if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){ |
| 254882 | pConfig->pzErrmsg = &pTab->p.base.zErrMsg; |
| 254883 | rc = fts5SeekCursor(pCsr, 1); |
| 254884 | if( rc==SQLITE_OK ){ |
| 254885 | sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); |
| 254886 | if( pConfig->bLocale |
| 254887 | && pConfig->eContent==FTS5_CONTENT_EXTERNAL |
| 254888 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) |
| 254889 | ){ |
| 254890 | const char *z = 0; |
| 254891 | int n = 0; |
| 254892 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); |
| 254893 | if( rc==SQLITE_OK ){ |
| 254894 | sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT); |
| 254895 | } |
| 254896 | sqlite3Fts5ClearLocale(pConfig); |
| 254897 | }else{ |
| 254898 | sqlite3_result_value(pCtx, pVal); |
| 254899 | } |
| 254900 | } |
| 254901 | |
| 254902 | pConfig->pzErrmsg = 0; |
| 254903 | } |
| 254904 | } |
| 254905 | |
| 254906 | return rc; |
| @@ -254625,11 +255419,11 @@ | |
| 255419 | int nArg, /* Number of args */ |
| 255420 | sqlite3_value **apUnused /* Function arguments */ |
| 255421 | ){ |
| 255422 | assert( nArg==0 ); |
| 255423 | UNUSED_PARAM2(nArg, apUnused); |
| 255424 | sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT); |
| 255425 | } |
| 255426 | |
| 255427 | /* |
| 255428 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255429 | ** |
| @@ -254664,36 +255458,48 @@ | |
| 255458 | nText = sqlite3_value_bytes(apArg[1]); |
| 255459 | |
| 255460 | if( zLocale==0 || zLocale[0]=='\0' ){ |
| 255461 | sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); |
| 255462 | }else{ |
| 255463 | Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx); |
| 255464 | u8 *pBlob = 0; |
| 255465 | u8 *pCsr = 0; |
| 255466 | int nBlob = 0; |
| 255467 | |
| 255468 | nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText; |
| 255469 | pBlob = (u8*)sqlite3_malloc(nBlob); |
| 255470 | if( pBlob==0 ){ |
| 255471 | sqlite3_result_error_nomem(pCtx); |
| 255472 | return; |
| 255473 | } |
| 255474 | |
| 255475 | pCsr = pBlob; |
| 255476 | memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE); |
| 255477 | pCsr += FTS5_LOCALE_HDR_SIZE; |
| 255478 | memcpy(pCsr, zLocale, nLocale); |
| 255479 | pCsr += nLocale; |
| 255480 | (*pCsr++) = 0x00; |
| 255481 | if( zText ) memcpy(pCsr, zText, nText); |
| 255482 | assert( &pCsr[nText]==&pBlob[nBlob] ); |
| 255483 | |
| 255484 | sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); |
| 255485 | } |
| 255486 | } |
| 255487 | |
| 255488 | /* |
| 255489 | ** Implementation of fts5_insttoken() function. |
| 255490 | */ |
| 255491 | static void fts5InsttokenFunc( |
| 255492 | sqlite3_context *pCtx, /* Function call context */ |
| 255493 | int nArg, /* Number of args */ |
| 255494 | sqlite3_value **apArg /* Function arguments */ |
| 255495 | ){ |
| 255496 | assert( nArg==1 ); |
| 255497 | (void)nArg; |
| 255498 | sqlite3_result_value(pCtx, apArg[0]); |
| 255499 | sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE); |
| 255500 | } |
| 255501 | |
| 255502 | /* |
| 255503 | ** Return true if zName is the extension on one of the shadow tables used |
| 255504 | ** by this module. |
| 255505 | */ |
| @@ -254789,10 +255595,20 @@ | |
| 255595 | pGlobal->api.xCreateFunction = fts5CreateAux; |
| 255596 | pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; |
| 255597 | pGlobal->api.xFindTokenizer = fts5FindTokenizer; |
| 255598 | pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; |
| 255599 | pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; |
| 255600 | |
| 255601 | /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector. |
| 255602 | ** The constants below were generated randomly. */ |
| 255603 | sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr); |
| 255604 | pGlobal->aLocaleHdr[0] ^= 0xF924976D; |
| 255605 | pGlobal->aLocaleHdr[1] ^= 0x16596E13; |
| 255606 | pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA; |
| 255607 | pGlobal->aLocaleHdr[3] ^= 0x9B03A67F; |
| 255608 | assert( sizeof(pGlobal->aLocaleHdr)==16 ); |
| 255609 | |
| 255610 | rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); |
| 255611 | if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); |
| 255612 | if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); |
| 255613 | if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); |
| 255614 | if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); |
| @@ -254810,13 +255626,20 @@ | |
| 255626 | ); |
| 255627 | } |
| 255628 | if( rc==SQLITE_OK ){ |
| 255629 | rc = sqlite3_create_function( |
| 255630 | db, "fts5_locale", 2, |
| 255631 | SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE, |
| 255632 | p, fts5LocaleFunc, 0, 0 |
| 255633 | ); |
| 255634 | } |
| 255635 | if( rc==SQLITE_OK ){ |
| 255636 | rc = sqlite3_create_function( |
| 255637 | db, "fts5_insttoken", 1, |
| 255638 | SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, |
| 255639 | p, fts5InsttokenFunc, 0, 0 |
| 255640 | ); |
| 255641 | } |
| 255642 | } |
| 255643 | |
| 255644 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 255645 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| @@ -255015,24 +255838,39 @@ | |
| 255838 | ); |
| 255839 | break; |
| 255840 | |
| 255841 | case FTS5_STMT_INSERT_CONTENT: |
| 255842 | case FTS5_STMT_REPLACE_CONTENT: { |
| 255843 | char *zBind = 0; |
| 255844 | int i; |
| 255845 | |
| 255846 | assert( pC->eContent==FTS5_CONTENT_NORMAL |
| 255847 | || pC->eContent==FTS5_CONTENT_UNINDEXED |
| 255848 | ); |
| 255849 | |
| 255850 | /* Add bindings for the "c*" columns - those that store the actual |
| 255851 | ** table content. If eContent==NORMAL, then there is one binding |
| 255852 | ** for each column. Or, if eContent==UNINDEXED, then there are only |
| 255853 | ** bindings for the UNINDEXED columns. */ |
| 255854 | for(i=0; rc==SQLITE_OK && i<(pC->nCol+1); i++){ |
| 255855 | if( !i || pC->eContent==FTS5_CONTENT_NORMAL || pC->abUnindexed[i-1] ){ |
| 255856 | zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1); |
| 255857 | } |
| 255858 | } |
| 255859 | |
| 255860 | /* Add bindings for any "l*" columns. Only non-UNINDEXED columns |
| 255861 | ** require these. */ |
| 255862 | if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL ){ |
| 255863 | for(i=0; rc==SQLITE_OK && i<pC->nCol; i++){ |
| 255864 | if( pC->abUnindexed[i]==0 ){ |
| 255865 | zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2); |
| 255866 | } |
| 255867 | } |
| 255868 | } |
| 255869 | |
| 255870 | zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind); |
| 255871 | sqlite3_free(zBind); |
| 255872 | break; |
| 255873 | } |
| 255874 | |
| 255875 | case FTS5_STMT_REPLACE_DOCSIZE: |
| 255876 | zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, |
| @@ -255214,23 +256052,37 @@ | |
| 256052 | p->aTotalSize = (i64*)&p[1]; |
| 256053 | p->pConfig = pConfig; |
| 256054 | p->pIndex = pIndex; |
| 256055 | |
| 256056 | if( bCreate ){ |
| 256057 | if( pConfig->eContent==FTS5_CONTENT_NORMAL |
| 256058 | || pConfig->eContent==FTS5_CONTENT_UNINDEXED |
| 256059 | ){ |
| 256060 | int nDefn = 32 + pConfig->nCol*10; |
| 256061 | char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); |
| 256062 | if( zDefn==0 ){ |
| 256063 | rc = SQLITE_NOMEM; |
| 256064 | }else{ |
| 256065 | int i; |
| 256066 | int iOff; |
| 256067 | sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); |
| 256068 | iOff = (int)strlen(zDefn); |
| 256069 | for(i=0; i<pConfig->nCol; i++){ |
| 256070 | if( pConfig->eContent==FTS5_CONTENT_NORMAL |
| 256071 | || pConfig->abUnindexed[i] |
| 256072 | ){ |
| 256073 | sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); |
| 256074 | iOff += (int)strlen(&zDefn[iOff]); |
| 256075 | } |
| 256076 | } |
| 256077 | if( pConfig->bLocale ){ |
| 256078 | for(i=0; i<pConfig->nCol; i++){ |
| 256079 | if( pConfig->abUnindexed[i]==0 ){ |
| 256080 | sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); |
| 256081 | iOff += (int)strlen(&zDefn[iOff]); |
| 256082 | } |
| 256083 | } |
| 256084 | } |
| 256085 | rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); |
| 256086 | } |
| 256087 | sqlite3_free(zDefn); |
| 256088 | } |
| @@ -255379,33 +256231,43 @@ | |
| 256231 | for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ |
| 256232 | if( pConfig->abUnindexed[iCol-1]==0 ){ |
| 256233 | sqlite3_value *pVal = 0; |
| 256234 | const char *pText = 0; |
| 256235 | int nText = 0; |
| 256236 | const char *pLoc = 0; |
| 256237 | int nLoc = 0; |
| 256238 | |
| 256239 | assert( pSeek==0 || apVal==0 ); |
| 256240 | assert( pSeek!=0 || apVal!=0 ); |
| 256241 | if( pSeek ){ |
| 256242 | pVal = sqlite3_column_value(pSeek, iCol); |
| 256243 | }else{ |
| 256244 | pVal = apVal[iCol-1]; |
| 256245 | } |
| 256246 | |
| 256247 | if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ |
| 256248 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); |
| 256249 | }else{ |
| 256250 | pText = (const char*)sqlite3_value_text(pVal); |
| 256251 | nText = sqlite3_value_bytes(pVal); |
| 256252 | if( pConfig->bLocale && pSeek ){ |
| 256253 | pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol); |
| 256254 | nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol); |
| 256255 | } |
| 256256 | } |
| 256257 | |
| 256258 | if( rc==SQLITE_OK ){ |
| 256259 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); |
| 256260 | ctx.szCol = 0; |
| 256261 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, |
| 256262 | pText, nText, (void*)&ctx, fts5StorageInsertCallback |
| 256263 | ); |
| 256264 | p->aTotalSize[iCol-1] -= (i64)ctx.szCol; |
| 256265 | if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ |
| 256266 | rc = FTS5_CORRUPT; |
| 256267 | } |
| 256268 | sqlite3Fts5ClearLocale(pConfig); |
| 256269 | } |
| 256270 | } |
| 256271 | } |
| 256272 | if( rc==SQLITE_OK && p->nTotalRow<1 ){ |
| 256273 | rc = FTS5_CORRUPT; |
| @@ -255446,11 +256308,13 @@ | |
| 256308 | i64 iOrigin = 0; |
| 256309 | sqlite3_stmt *pLookup = 0; |
| 256310 | int rc = SQLITE_OK; |
| 256311 | |
| 256312 | assert( p->pConfig->bContentlessDelete ); |
| 256313 | assert( p->pConfig->eContent==FTS5_CONTENT_NONE |
| 256314 | || p->pConfig->eContent==FTS5_CONTENT_UNINDEXED |
| 256315 | ); |
| 256316 | |
| 256317 | /* Look up the origin of the document in the %_docsize table. Store |
| 256318 | ** this in stack variable iOrigin. */ |
| 256319 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); |
| 256320 | if( rc==SQLITE_OK ){ |
| @@ -255570,10 +256434,16 @@ | |
| 256434 | } |
| 256435 | |
| 256436 | if( rc==SQLITE_OK ){ |
| 256437 | if( p->pConfig->bContentlessDelete ){ |
| 256438 | rc = fts5StorageContentlessDelete(p, iDel); |
| 256439 | if( rc==SQLITE_OK |
| 256440 | && bSaveRow |
| 256441 | && p->pConfig->eContent==FTS5_CONTENT_UNINDEXED |
| 256442 | ){ |
| 256443 | rc = sqlite3Fts5StorageFindDeleteRow(p, iDel); |
| 256444 | } |
| 256445 | }else{ |
| 256446 | rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); |
| 256447 | } |
| 256448 | } |
| 256449 | |
| @@ -255586,11 +256456,13 @@ | |
| 256456 | rc = sqlite3_reset(pDel); |
| 256457 | } |
| 256458 | } |
| 256459 | |
| 256460 | /* Delete the %_content record */ |
| 256461 | if( pConfig->eContent==FTS5_CONTENT_NORMAL |
| 256462 | || pConfig->eContent==FTS5_CONTENT_UNINDEXED |
| 256463 | ){ |
| 256464 | if( rc==SQLITE_OK ){ |
| 256465 | rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); |
| 256466 | } |
| 256467 | if( rc==SQLITE_OK ){ |
| 256468 | sqlite3_bind_int64(pDel, 1, iDel); |
| @@ -255618,12 +256490,17 @@ | |
| 256490 | pConfig->zDb, pConfig->zName, |
| 256491 | pConfig->zDb, pConfig->zName |
| 256492 | ); |
| 256493 | if( rc==SQLITE_OK && pConfig->bColumnsize ){ |
| 256494 | rc = fts5ExecPrintf(pConfig->db, 0, |
| 256495 | "DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName |
| 256496 | ); |
| 256497 | } |
| 256498 | |
| 256499 | if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ |
| 256500 | rc = fts5ExecPrintf(pConfig->db, 0, |
| 256501 | "DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName |
| 256502 | ); |
| 256503 | } |
| 256504 | |
| 256505 | /* Reinitialize the %_data table. This call creates the initial structure |
| 256506 | ** and averages records. */ |
| @@ -255660,24 +256537,39 @@ | |
| 256537 | sqlite3Fts5BufferZero(&buf); |
| 256538 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
| 256539 | for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ |
| 256540 | ctx.szCol = 0; |
| 256541 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ |
| 256542 | int nText = 0; /* Size of pText in bytes */ |
| 256543 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 256544 | int nLoc = 0; /* Size of pLoc in bytes */ |
| 256545 | const char *pLoc = 0; /* Pointer to buffer containing text value */ |
| 256546 | |
| 256547 | sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); |
| 256548 | if( pConfig->eContent==FTS5_CONTENT_EXTERNAL |
| 256549 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) |
| 256550 | ){ |
| 256551 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); |
| 256552 | }else{ |
| 256553 | pText = (const char*)sqlite3_value_text(pVal); |
| 256554 | nText = sqlite3_value_bytes(pVal); |
| 256555 | if( pConfig->bLocale ){ |
| 256556 | int iCol = ctx.iCol + 1 + pConfig->nCol; |
| 256557 | pLoc = (const char*)sqlite3_column_text(pScan, iCol); |
| 256558 | nLoc = sqlite3_column_bytes(pScan, iCol); |
| 256559 | } |
| 256560 | } |
| 256561 | |
| 256562 | if( rc==SQLITE_OK ){ |
| 256563 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); |
| 256564 | rc = sqlite3Fts5Tokenize(pConfig, |
| 256565 | FTS5_TOKENIZE_DOCUMENT, |
| 256566 | pText, nText, |
| 256567 | (void*)&ctx, |
| 256568 | fts5StorageInsertCallback |
| 256569 | ); |
| 256570 | sqlite3Fts5ClearLocale(pConfig); |
| 256571 | } |
| 256572 | } |
| 256573 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); |
| 256574 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; |
| 256575 | } |
| @@ -255740,53 +256632,75 @@ | |
| 256632 | /* |
| 256633 | ** Insert a new row into the FTS content table. |
| 256634 | */ |
| 256635 | static int sqlite3Fts5StorageContentInsert( |
| 256636 | Fts5Storage *p, |
| 256637 | int bReplace, /* True to use REPLACE instead of INSERT */ |
| 256638 | sqlite3_value **apVal, |
| 256639 | i64 *piRowid |
| 256640 | ){ |
| 256641 | Fts5Config *pConfig = p->pConfig; |
| 256642 | int rc = SQLITE_OK; |
| 256643 | |
| 256644 | /* Insert the new row into the %_content table. */ |
| 256645 | if( pConfig->eContent!=FTS5_CONTENT_NORMAL |
| 256646 | && pConfig->eContent!=FTS5_CONTENT_UNINDEXED |
| 256647 | ){ |
| 256648 | if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ |
| 256649 | *piRowid = sqlite3_value_int64(apVal[1]); |
| 256650 | }else{ |
| 256651 | rc = fts5StorageNewRowid(p, piRowid); |
| 256652 | } |
| 256653 | }else{ |
| 256654 | sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ |
| 256655 | int i; /* Counter variable */ |
| 256656 | |
| 256657 | assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT ); |
| 256658 | assert( bReplace==0 || bReplace==1 ); |
| 256659 | rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0); |
| 256660 | if( pInsert ) sqlite3_clear_bindings(pInsert); |
| 256661 | |
| 256662 | /* Bind the rowid value */ |
| 256663 | sqlite3_bind_value(pInsert, 1, apVal[1]); |
| 256664 | |
| 256665 | /* Loop through values for user-defined columns. i=2 is the leftmost |
| 256666 | ** user-defined column. As is column 1 of pSavedRow. */ |
| 256667 | for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ |
| 256668 | int bUnindexed = pConfig->abUnindexed[i-2]; |
| 256669 | if( pConfig->eContent==FTS5_CONTENT_NORMAL || bUnindexed ){ |
| 256670 | sqlite3_value *pVal = apVal[i]; |
| 256671 | |
| 256672 | if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ |
| 256673 | /* This is an UPDATE statement, and user-defined column (i-2) was not |
| 256674 | ** modified. Retrieve the value from Fts5Storage.pSavedRow. */ |
| 256675 | pVal = sqlite3_column_value(p->pSavedRow, i-1); |
| 256676 | if( pConfig->bLocale && bUnindexed==0 ){ |
| 256677 | sqlite3_bind_value(pInsert, pConfig->nCol + i, |
| 256678 | sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1) |
| 256679 | ); |
| 256680 | } |
| 256681 | }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ |
| 256682 | const char *pText = 0; |
| 256683 | const char *pLoc = 0; |
| 256684 | int nText = 0; |
| 256685 | int nLoc = 0; |
| 256686 | assert( pConfig->bLocale ); |
| 256687 | |
| 256688 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); |
| 256689 | if( rc==SQLITE_OK ){ |
| 256690 | sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); |
| 256691 | if( bUnindexed==0 ){ |
| 256692 | int iLoc = pConfig->nCol + i; |
| 256693 | sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT); |
| 256694 | } |
| 256695 | } |
| 256696 | |
| 256697 | continue; |
| 256698 | } |
| 256699 | |
| 256700 | rc = sqlite3_bind_value(pInsert, i, pVal); |
| 256701 | } |
| 256702 | } |
| 256703 | if( rc==SQLITE_OK ){ |
| 256704 | sqlite3_step(pInsert); |
| 256705 | rc = sqlite3_reset(pInsert); |
| 256706 | } |
| @@ -255817,27 +256731,41 @@ | |
| 256731 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
| 256732 | } |
| 256733 | for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ |
| 256734 | ctx.szCol = 0; |
| 256735 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ |
| 256736 | int nText = 0; /* Size of pText in bytes */ |
| 256737 | const char *pText = 0; /* Pointer to buffer containing text value */ |
| 256738 | int nLoc = 0; /* Size of pText in bytes */ |
| 256739 | const char *pLoc = 0; /* Pointer to buffer containing text value */ |
| 256740 | |
| 256741 | sqlite3_value *pVal = apVal[ctx.iCol+2]; |
| 256742 | if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ |
| 256743 | pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); |
| 256744 | if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ |
| 256745 | int iCol = ctx.iCol + 1 + pConfig->nCol; |
| 256746 | pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol); |
| 256747 | nLoc = sqlite3_column_bytes(p->pSavedRow, iCol); |
| 256748 | } |
| 256749 | }else{ |
| 256750 | pVal = apVal[ctx.iCol+2]; |
| 256751 | } |
| 256752 | |
| 256753 | if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ |
| 256754 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); |
| 256755 | }else{ |
| 256756 | pText = (const char*)sqlite3_value_text(pVal); |
| 256757 | nText = sqlite3_value_bytes(pVal); |
| 256758 | } |
| 256759 | |
| 256760 | if( rc==SQLITE_OK ){ |
| 256761 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); |
| 256762 | rc = sqlite3Fts5Tokenize(pConfig, |
| 256763 | FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, |
| 256764 | fts5StorageInsertCallback |
| 256765 | ); |
| 256766 | sqlite3Fts5ClearLocale(pConfig); |
| 256767 | } |
| 256768 | } |
| 256769 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); |
| 256770 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; |
| 256771 | } |
| @@ -255998,41 +256926,65 @@ | |
| 256926 | } |
| 256927 | if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 256928 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 256929 | } |
| 256930 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 256931 | if( pConfig->abUnindexed[i]==0 ){ |
| 256932 | const char *pText = 0; |
| 256933 | int nText = 0; |
| 256934 | const char *pLoc = 0; |
| 256935 | int nLoc = 0; |
| 256936 | sqlite3_value *pVal = sqlite3_column_value(pScan, i+1); |
| 256937 | |
| 256938 | if( pConfig->eContent==FTS5_CONTENT_EXTERNAL |
| 256939 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) |
| 256940 | ){ |
| 256941 | rc = sqlite3Fts5DecodeLocaleValue( |
| 256942 | pVal, &pText, &nText, &pLoc, &nLoc |
| 256943 | ); |
| 256944 | }else{ |
| 256945 | if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ |
| 256946 | int iCol = i + 1 + pConfig->nCol; |
| 256947 | pLoc = (const char*)sqlite3_column_text(pScan, iCol); |
| 256948 | nLoc = sqlite3_column_bytes(pScan, iCol); |
| 256949 | } |
| 256950 | pText = (const char*)sqlite3_value_text(pVal); |
| 256951 | nText = sqlite3_value_bytes(pVal); |
| 256952 | } |
| 256953 | |
| 256954 | ctx.iCol = i; |
| 256955 | ctx.szCol = 0; |
| 256956 | |
| 256957 | if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 256958 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 256959 | } |
| 256960 | |
| 256961 | if( rc==SQLITE_OK ){ |
| 256962 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); |
| 256963 | rc = sqlite3Fts5Tokenize(pConfig, |
| 256964 | FTS5_TOKENIZE_DOCUMENT, |
| 256965 | pText, nText, |
| 256966 | (void*)&ctx, |
| 256967 | fts5StorageIntegrityCallback |
| 256968 | ); |
| 256969 | sqlite3Fts5ClearLocale(pConfig); |
| 256970 | } |
| 256971 | |
| 256972 | /* If this is not a columnsize=0 database, check that the number |
| 256973 | ** of tokens in the value matches the aColSize[] value read from |
| 256974 | ** the %_docsize table. */ |
| 256975 | if( rc==SQLITE_OK |
| 256976 | && pConfig->bColumnsize |
| 256977 | && ctx.szCol!=aColSize[i] |
| 256978 | ){ |
| 256979 | rc = FTS5_CORRUPT; |
| 256980 | } |
| 256981 | aTotalSize[i] += ctx.szCol; |
| 256982 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 256983 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 256984 | ctx.pTermset = 0; |
| 256985 | } |
| 256986 | } |
| 256987 | } |
| 256988 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 256989 | ctx.pTermset = 0; |
| 256990 | |
| @@ -256454,11 +257406,11 @@ | |
| 257406 | |
| 257407 | #define READ_UTF8(zIn, zTerm, c) \ |
| 257408 | c = *(zIn++); \ |
| 257409 | if( c>=0xc0 ){ \ |
| 257410 | c = sqlite3Utf8Trans1[c-0xc0]; \ |
| 257411 | while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \ |
| 257412 | c = (c<<6) + (0x3f & *(zIn++)); \ |
| 257413 | } \ |
| 257414 | if( c<0x80 \ |
| 257415 | || (c&0xFFFFF800)==0xD800 \ |
| 257416 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
| @@ -257609,22 +258561,22 @@ | |
| 258561 | int rc = SQLITE_OK; |
| 258562 | char aBuf[32]; |
| 258563 | char *zOut = aBuf; |
| 258564 | int ii; |
| 258565 | const unsigned char *zIn = (const unsigned char*)pText; |
| 258566 | const unsigned char *zEof = (zIn ? &zIn[nText] : 0); |
| 258567 | u32 iCode = 0; |
| 258568 | int aStart[3]; /* Input offset of each character in aBuf[] */ |
| 258569 | |
| 258570 | UNUSED_PARAM(unusedFlags); |
| 258571 | |
| 258572 | /* Populate aBuf[] with the characters for the first trigram. */ |
| 258573 | for(ii=0; ii<3; ii++){ |
| 258574 | do { |
| 258575 | aStart[ii] = zIn - (const unsigned char*)pText; |
| 258576 | if( zIn>=zEof ) return SQLITE_OK; |
| 258577 | READ_UTF8(zIn, zEof, iCode); |
| 258578 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); |
| 258579 | }while( iCode==0 ); |
| 258580 | WRITE_UTF8(zOut, iCode); |
| 258581 | } |
| 258582 | |
| @@ -257641,12 +258593,15 @@ | |
| 258593 | const char *z1; |
| 258594 | |
| 258595 | /* Read characters from the input up until the first non-diacritic */ |
| 258596 | do { |
| 258597 | iNext = zIn - (const unsigned char*)pText; |
| 258598 | if( zIn>=zEof ){ |
| 258599 | iCode = 0; |
| 258600 | break; |
| 258601 | } |
| 258602 | READ_UTF8(zIn, zEof, iCode); |
| 258603 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); |
| 258604 | }while( iCode==0 ); |
| 258605 | |
| 258606 | /* Pass the current trigram back to fts5 */ |
| 258607 | rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); |
| @@ -259679,11 +260634,11 @@ | |
| 260634 | |
| 260635 | return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); |
| 260636 | } |
| 260637 | |
| 260638 | |
| 260639 | /* Here ends the fts5.c composite file. */ |
| 260640 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ |
| 260641 | |
| 260642 | /************** End of fts5.c ************************************************/ |
| 260643 | /************** Begin file stmt.c ********************************************/ |
| 260644 | /* |
| @@ -260035,6 +260990,7 @@ | |
| 260990 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 260991 | |
| 260992 | /************** End of stmt.c ************************************************/ |
| 260993 | /* Return the source-id for this library */ |
| 260994 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 260995 | #endif /* SQLITE_AMALGAMATION */ |
| 260996 | /************************** End of sqlite3.c ******************************/ |
| 260997 |
+80
-13
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -144,13 +144,13 @@ | ||
| 144 | 144 | ** |
| 145 | 145 | ** See also: [sqlite3_libversion()], |
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | -#define SQLITE_VERSION "3.47.0" | |
| 150 | -#define SQLITE_VERSION_NUMBER 3047000 | |
| 151 | -#define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" | |
| 149 | +#define SQLITE_VERSION "3.48.0" | |
| 150 | +#define SQLITE_VERSION_NUMBER 3048000 | |
| 151 | +#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| @@ -650,10 +650,17 @@ | ||
| 650 | 650 | ** |
| 651 | 651 | ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying |
| 652 | 652 | ** filesystem supports doing multiple write operations atomically when those |
| 653 | 653 | ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and |
| 654 | 654 | ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. |
| 655 | +** | |
| 656 | +** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read | |
| 657 | +** from the database file in amounts that are not a multiple of the | |
| 658 | +** page size and that do not begin at a page boundary. Without this | |
| 659 | +** property, SQLite is careful to only do full-page reads and write | |
| 660 | +** on aligned pages, with the one exception that it will do a sub-page | |
| 661 | +** read of the first page to access the database header. | |
| 655 | 662 | */ |
| 656 | 663 | #define SQLITE_IOCAP_ATOMIC 0x00000001 |
| 657 | 664 | #define SQLITE_IOCAP_ATOMIC512 0x00000002 |
| 658 | 665 | #define SQLITE_IOCAP_ATOMIC1K 0x00000004 |
| 659 | 666 | #define SQLITE_IOCAP_ATOMIC2K 0x00000008 |
| @@ -666,10 +673,11 @@ | ||
| 666 | 673 | #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 |
| 667 | 674 | #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 |
| 668 | 675 | #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 |
| 669 | 676 | #define SQLITE_IOCAP_IMMUTABLE 0x00002000 |
| 670 | 677 | #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 |
| 678 | +#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 | |
| 671 | 679 | |
| 672 | 680 | /* |
| 673 | 681 | ** CAPI3REF: File Locking Levels |
| 674 | 682 | ** |
| 675 | 683 | ** SQLite uses one of these integer values as the second |
| @@ -812,10 +820,11 @@ | ||
| 812 | 820 | ** <li> [SQLITE_IOCAP_SEQUENTIAL] |
| 813 | 821 | ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] |
| 814 | 822 | ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] |
| 815 | 823 | ** <li> [SQLITE_IOCAP_IMMUTABLE] |
| 816 | 824 | ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] |
| 825 | +** <li> [SQLITE_IOCAP_SUBPAGE_READ] | |
| 817 | 826 | ** </ul> |
| 818 | 827 | ** |
| 819 | 828 | ** The SQLITE_IOCAP_ATOMIC property means that all writes of |
| 820 | 829 | ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values |
| 821 | 830 | ** mean that writes of blocks that are nnn bytes in size and |
| @@ -1089,10 +1098,15 @@ | ||
| 1089 | 1098 | ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This |
| 1090 | 1099 | ** opcode causes the xFileControl method to swap the file handle with the one |
| 1091 | 1100 | ** pointed to by the pArg argument. This capability is used during testing |
| 1092 | 1101 | ** and only needs to be supported when SQLITE_TEST is defined. |
| 1093 | 1102 | ** |
| 1103 | +** <li>[[SQLITE_FCNTL_NULL_IO]] | |
| 1104 | +** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor | |
| 1105 | +** or file handle for the [sqlite3_file] object such that it will no longer | |
| 1106 | +** read or write to the database file. | |
| 1107 | +** | |
| 1094 | 1108 | ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] |
| 1095 | 1109 | ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might |
| 1096 | 1110 | ** be advantageous to block on the next WAL lock if the lock is not immediately |
| 1097 | 1111 | ** available. The WAL subsystem issues this signal during rare |
| 1098 | 1112 | ** circumstances in order to fix a problem with priority inversion. |
| @@ -1242,10 +1256,11 @@ | ||
| 1242 | 1256 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1243 | 1257 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1244 | 1258 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1245 | 1259 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1246 | 1260 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1261 | +#define SQLITE_FCNTL_NULL_IO 43 | |
| 1247 | 1262 | |
| 1248 | 1263 | /* deprecated names */ |
| 1249 | 1264 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1250 | 1265 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1251 | 1266 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -2620,14 +2635,18 @@ | ||
| 2620 | 2635 | ** |
| 2621 | 2636 | ** ^These functions return the number of rows modified, inserted or |
| 2622 | 2637 | ** deleted by the most recently completed INSERT, UPDATE or DELETE |
| 2623 | 2638 | ** statement on the database connection specified by the only parameter. |
| 2624 | 2639 | ** The two functions are identical except for the type of the return value |
| 2625 | -** and that if the number of rows modified by the most recent INSERT, UPDATE | |
| 2640 | +** and that if the number of rows modified by the most recent INSERT, UPDATE, | |
| 2626 | 2641 | ** or DELETE is greater than the maximum value supported by type "int", then |
| 2627 | 2642 | ** the return value of sqlite3_changes() is undefined. ^Executing any other |
| 2628 | 2643 | ** type of SQL statement does not modify the value returned by these functions. |
| 2644 | +** For the purposes of this interface, a CREATE TABLE AS SELECT statement | |
| 2645 | +** does not count as an INSERT, UPDATE or DELETE statement and hence the rows | |
| 2646 | +** added to the new table by the CREATE TABLE AS SELECT statement are not | |
| 2647 | +** counted. | |
| 2629 | 2648 | ** |
| 2630 | 2649 | ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are |
| 2631 | 2650 | ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], |
| 2632 | 2651 | ** [foreign key actions] or [REPLACE] constraint resolution are not counted. |
| 2633 | 2652 | ** |
| @@ -4183,15 +4202,26 @@ | ||
| 4183 | 4202 | ** |
| 4184 | 4203 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4185 | 4204 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4186 | 4205 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4187 | 4206 | ** any virtual tables. |
| 4207 | +** | |
| 4208 | +** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt> | |
| 4209 | +** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler | |
| 4210 | +** errors from being sent to the error log defined by | |
| 4211 | +** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test | |
| 4212 | +** compiles to see if some SQL syntax is well-formed, without generating | |
| 4213 | +** messages on the global error log when it is not. If the test compile | |
| 4214 | +** fails, the sqlite3_prepare_v3() call returns the same error indications | |
| 4215 | +** with or without this flag; it just omits the call to [sqlite3_log()] that | |
| 4216 | +** logs the error. | |
| 4188 | 4217 | ** </dl> |
| 4189 | 4218 | */ |
| 4190 | 4219 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4191 | 4220 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4192 | 4221 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4222 | +#define SQLITE_PREPARE_DONT_LOG 0x10 | |
| 4193 | 4223 | |
| 4194 | 4224 | /* |
| 4195 | 4225 | ** CAPI3REF: Compiling An SQL Statement |
| 4196 | 4226 | ** KEYWORDS: {SQL statement compiler} |
| 4197 | 4227 | ** METHOD: sqlite3 |
| @@ -4220,17 +4250,21 @@ | ||
| 4220 | 4250 | ** and sqlite3_prepare_v3() |
| 4221 | 4251 | ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), |
| 4222 | 4252 | ** and sqlite3_prepare16_v3() use UTF-16. |
| 4223 | 4253 | ** |
| 4224 | 4254 | ** ^If the nByte argument is negative, then zSql is read up to the |
| 4225 | -** first zero terminator. ^If nByte is positive, then it is the | |
| 4226 | -** number of bytes read from zSql. ^If nByte is zero, then no prepared | |
| 4255 | +** first zero terminator. ^If nByte is positive, then it is the maximum | |
| 4256 | +** number of bytes read from zSql. When nByte is positive, zSql is read | |
| 4257 | +** up to the first zero terminator or until the nByte bytes have been read, | |
| 4258 | +** whichever comes first. ^If nByte is zero, then no prepared | |
| 4227 | 4259 | ** statement is generated. |
| 4228 | 4260 | ** If the caller knows that the supplied string is nul-terminated, then |
| 4229 | 4261 | ** there is a small performance advantage to passing an nByte parameter that |
| 4230 | 4262 | ** is the number of bytes in the input string <i>including</i> |
| 4231 | 4263 | ** the nul-terminator. |
| 4264 | +** Note that nByte measure the length of the input in bytes, not | |
| 4265 | +** characters, even for the UTF-16 interfaces. | |
| 4232 | 4266 | ** |
| 4233 | 4267 | ** ^If pzTail is not NULL then *pzTail is made to point to the first byte |
| 4234 | 4268 | ** past the end of the first SQL statement in zSql. These routines only |
| 4235 | 4269 | ** compile the first statement in zSql, so *pzTail is left pointing to |
| 4236 | 4270 | ** what remains uncompiled. |
| @@ -5597,11 +5631,11 @@ | ||
| 5597 | 5631 | ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call |
| 5598 | 5632 | ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. |
| 5599 | 5633 | ** This flag instructs SQLite to omit some corner-case optimizations that |
| 5600 | 5634 | ** might disrupt the operation of the [sqlite3_value_subtype()] function, |
| 5601 | 5635 | ** causing it to return zero rather than the correct subtype(). |
| 5602 | -** SQL functions that invokes [sqlite3_value_subtype()] should have this | |
| 5636 | +** All SQL functions that invoke [sqlite3_value_subtype()] should have this | |
| 5603 | 5637 | ** property. If the SQLITE_SUBTYPE property is omitted, then the return |
| 5604 | 5638 | ** value from [sqlite3_value_subtype()] might sometimes be zero even though |
| 5605 | 5639 | ** a non-zero subtype was specified by the function argument expression. |
| 5606 | 5640 | ** |
| 5607 | 5641 | ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> |
| @@ -8362,11 +8396,11 @@ | ||
| 8362 | 8396 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8363 | 8397 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8364 | 8398 | #define SQLITE_TESTCTRL_TRACEFLAGS 31 |
| 8365 | 8399 | #define SQLITE_TESTCTRL_TUNE 32 |
| 8366 | 8400 | #define SQLITE_TESTCTRL_LOGEST 33 |
| 8367 | -#define SQLITE_TESTCTRL_USELONGDOUBLE 34 | |
| 8401 | +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ | |
| 8368 | 8402 | #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ |
| 8369 | 8403 | |
| 8370 | 8404 | /* |
| 8371 | 8405 | ** CAPI3REF: SQL Keyword Checking |
| 8372 | 8406 | ** |
| @@ -9338,10 +9372,20 @@ | ||
| 9338 | 9372 | ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). |
| 9339 | 9373 | ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() |
| 9340 | 9374 | ** APIs are not strictly speaking threadsafe. If they are invoked at the |
| 9341 | 9375 | ** same time as another thread is invoking sqlite3_backup_step() it is |
| 9342 | 9376 | ** possible that they return invalid values. |
| 9377 | +** | |
| 9378 | +** <b>Alternatives To Using The Backup API</b> | |
| 9379 | +** | |
| 9380 | +** Other techniques for safely creating a consistent backup of an SQLite | |
| 9381 | +** database include: | |
| 9382 | +** | |
| 9383 | +** <ul> | |
| 9384 | +** <li> The [VACUUM INTO] command. | |
| 9385 | +** <li> The [sqlite3_rsync] utility program. | |
| 9386 | +** </ul> | |
| 9343 | 9387 | */ |
| 9344 | 9388 | SQLITE_API sqlite3_backup *sqlite3_backup_init( |
| 9345 | 9389 | sqlite3 *pDest, /* Destination database handle */ |
| 9346 | 9390 | const char *zDestName, /* Destination database name */ |
| 9347 | 9391 | sqlite3 *pSource, /* Source database handle */ |
| @@ -10536,10 +10580,18 @@ | ||
| 10536 | 10580 | ** schema S in database connection D. ^On success, the |
| 10537 | 10581 | ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly |
| 10538 | 10582 | ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. |
| 10539 | 10583 | ** If there is not already a read-transaction open on schema S when |
| 10540 | 10584 | ** this function is called, one is opened automatically. |
| 10585 | +** | |
| 10586 | +** If a read-transaction is opened by this function, then it is guaranteed | |
| 10587 | +** that the returned snapshot object may not be invalidated by a database | |
| 10588 | +** writer or checkpointer until after the read-transaction is closed. This | |
| 10589 | +** is not guaranteed if a read-transaction is already open when this | |
| 10590 | +** function is called. In that case, any subsequent write or checkpoint | |
| 10591 | +** operation on the database may invalidate the returned snapshot handle, | |
| 10592 | +** even while the read-transaction remains open. | |
| 10541 | 10593 | ** |
| 10542 | 10594 | ** The following must be true for this function to succeed. If any of |
| 10543 | 10595 | ** the following statements are false when sqlite3_snapshot_get() is |
| 10544 | 10596 | ** called, SQLITE_ERROR is returned. The final value of *P is undefined |
| 10545 | 10597 | ** in this case. |
| @@ -10856,11 +10908,11 @@ | ||
| 10856 | 10908 | #endif |
| 10857 | 10909 | |
| 10858 | 10910 | #ifdef __cplusplus |
| 10859 | 10911 | } /* End of the 'extern "C"' block */ |
| 10860 | 10912 | #endif |
| 10861 | -#endif /* SQLITE3_H */ | |
| 10913 | +/* #endif for SQLITE3_H will be added by mksqlite3.tcl */ | |
| 10862 | 10914 | |
| 10863 | 10915 | /******** Begin file sqlite3rtree.h *********/ |
| 10864 | 10916 | /* |
| 10865 | 10917 | ** 2010 August 30 |
| 10866 | 10918 | ** |
| @@ -13107,17 +13159,32 @@ | ||
| 13107 | 13159 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13108 | 13160 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13109 | 13161 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13110 | 13162 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13111 | 13163 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13112 | -** bytes. This API is not available if the specified token matches a | |
| 13113 | -** prefix query term. In that case both output variables are always set | |
| 13114 | -** to 0. | |
| 13164 | +** bytes. | |
| 13115 | 13165 | ** |
| 13116 | 13166 | ** The output text is not a copy of the document text that was tokenized. |
| 13117 | 13167 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13118 | 13168 | ** includes any embedded 0x00 and trailing data. |
| 13169 | +** | |
| 13170 | +** This API may be slow in some cases if the token identified by parameters | |
| 13171 | +** iIdx and iToken matched a prefix token in the query. In most cases, the | |
| 13172 | +** first call to this API for each prefix token in the query is forced | |
| 13173 | +** to scan the portion of the full-text index that matches the prefix | |
| 13174 | +** token to collect the extra data required by this API. If the prefix | |
| 13175 | +** token matches a large number of token instances in the document set, | |
| 13176 | +** this may be a performance problem. | |
| 13177 | +** | |
| 13178 | +** If the user knows in advance that a query may use this API for a | |
| 13179 | +** prefix token, FTS5 may be configured to collect all required data as part | |
| 13180 | +** of the initial querying of the full-text index, avoiding the second scan | |
| 13181 | +** entirely. This also causes prefix queries that do not use this API to | |
| 13182 | +** run more slowly and use more memory. FTS5 may be configured in this way | |
| 13183 | +** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] | |
| 13184 | +** option, or on a per-query basis using the | |
| 13185 | +** [fts5_insttoken | fts5_insttoken()] user function. | |
| 13119 | 13186 | ** |
| 13120 | 13187 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13121 | 13188 | ** "detail=none" or "detail=column" option. |
| 13122 | 13189 | ** |
| 13123 | 13190 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -13205,11 +13272,10 @@ | ||
| 13205 | 13272 | ** CUSTOM TOKENIZERS |
| 13206 | 13273 | ** |
| 13207 | 13274 | ** Applications may also register custom tokenizer types. A tokenizer |
| 13208 | 13275 | ** is registered by providing fts5 with a populated instance of the |
| 13209 | 13276 | ** following structure. All structure methods must be defined, setting |
| 13210 | -** | |
| 13211 | 13277 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 13212 | 13278 | ** behaviour. The structure methods are expected to function as follows: |
| 13213 | 13279 | ** |
| 13214 | 13280 | ** xCreate: |
| 13215 | 13281 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -13549,5 +13615,6 @@ | ||
| 13549 | 13615 | #endif |
| 13550 | 13616 | |
| 13551 | 13617 | #endif /* _FTS5_H */ |
| 13552 | 13618 | |
| 13553 | 13619 | /******** End of fts5.h *********/ |
| 13620 | +#endif /* SQLITE3_H */ | |
| 13554 | 13621 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -144,13 +144,13 @@ | |
| 144 | ** |
| 145 | ** See also: [sqlite3_libversion()], |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.47.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3047000 |
| 151 | #define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -650,10 +650,17 @@ | |
| 650 | ** |
| 651 | ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying |
| 652 | ** filesystem supports doing multiple write operations atomically when those |
| 653 | ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and |
| 654 | ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. |
| 655 | */ |
| 656 | #define SQLITE_IOCAP_ATOMIC 0x00000001 |
| 657 | #define SQLITE_IOCAP_ATOMIC512 0x00000002 |
| 658 | #define SQLITE_IOCAP_ATOMIC1K 0x00000004 |
| 659 | #define SQLITE_IOCAP_ATOMIC2K 0x00000008 |
| @@ -666,10 +673,11 @@ | |
| 666 | #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 |
| 667 | #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 |
| 668 | #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 |
| 669 | #define SQLITE_IOCAP_IMMUTABLE 0x00002000 |
| 670 | #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 |
| 671 | |
| 672 | /* |
| 673 | ** CAPI3REF: File Locking Levels |
| 674 | ** |
| 675 | ** SQLite uses one of these integer values as the second |
| @@ -812,10 +820,11 @@ | |
| 812 | ** <li> [SQLITE_IOCAP_SEQUENTIAL] |
| 813 | ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] |
| 814 | ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] |
| 815 | ** <li> [SQLITE_IOCAP_IMMUTABLE] |
| 816 | ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] |
| 817 | ** </ul> |
| 818 | ** |
| 819 | ** The SQLITE_IOCAP_ATOMIC property means that all writes of |
| 820 | ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values |
| 821 | ** mean that writes of blocks that are nnn bytes in size and |
| @@ -1089,10 +1098,15 @@ | |
| 1089 | ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This |
| 1090 | ** opcode causes the xFileControl method to swap the file handle with the one |
| 1091 | ** pointed to by the pArg argument. This capability is used during testing |
| 1092 | ** and only needs to be supported when SQLITE_TEST is defined. |
| 1093 | ** |
| 1094 | ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] |
| 1095 | ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might |
| 1096 | ** be advantageous to block on the next WAL lock if the lock is not immediately |
| 1097 | ** available. The WAL subsystem issues this signal during rare |
| 1098 | ** circumstances in order to fix a problem with priority inversion. |
| @@ -1242,10 +1256,11 @@ | |
| 1242 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1243 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1244 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1245 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1246 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1247 | |
| 1248 | /* deprecated names */ |
| 1249 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1250 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1251 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -2620,14 +2635,18 @@ | |
| 2620 | ** |
| 2621 | ** ^These functions return the number of rows modified, inserted or |
| 2622 | ** deleted by the most recently completed INSERT, UPDATE or DELETE |
| 2623 | ** statement on the database connection specified by the only parameter. |
| 2624 | ** The two functions are identical except for the type of the return value |
| 2625 | ** and that if the number of rows modified by the most recent INSERT, UPDATE |
| 2626 | ** or DELETE is greater than the maximum value supported by type "int", then |
| 2627 | ** the return value of sqlite3_changes() is undefined. ^Executing any other |
| 2628 | ** type of SQL statement does not modify the value returned by these functions. |
| 2629 | ** |
| 2630 | ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are |
| 2631 | ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], |
| 2632 | ** [foreign key actions] or [REPLACE] constraint resolution are not counted. |
| 2633 | ** |
| @@ -4183,15 +4202,26 @@ | |
| 4183 | ** |
| 4184 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4185 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4186 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4187 | ** any virtual tables. |
| 4188 | ** </dl> |
| 4189 | */ |
| 4190 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4191 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4192 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4193 | |
| 4194 | /* |
| 4195 | ** CAPI3REF: Compiling An SQL Statement |
| 4196 | ** KEYWORDS: {SQL statement compiler} |
| 4197 | ** METHOD: sqlite3 |
| @@ -4220,17 +4250,21 @@ | |
| 4220 | ** and sqlite3_prepare_v3() |
| 4221 | ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), |
| 4222 | ** and sqlite3_prepare16_v3() use UTF-16. |
| 4223 | ** |
| 4224 | ** ^If the nByte argument is negative, then zSql is read up to the |
| 4225 | ** first zero terminator. ^If nByte is positive, then it is the |
| 4226 | ** number of bytes read from zSql. ^If nByte is zero, then no prepared |
| 4227 | ** statement is generated. |
| 4228 | ** If the caller knows that the supplied string is nul-terminated, then |
| 4229 | ** there is a small performance advantage to passing an nByte parameter that |
| 4230 | ** is the number of bytes in the input string <i>including</i> |
| 4231 | ** the nul-terminator. |
| 4232 | ** |
| 4233 | ** ^If pzTail is not NULL then *pzTail is made to point to the first byte |
| 4234 | ** past the end of the first SQL statement in zSql. These routines only |
| 4235 | ** compile the first statement in zSql, so *pzTail is left pointing to |
| 4236 | ** what remains uncompiled. |
| @@ -5597,11 +5631,11 @@ | |
| 5597 | ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call |
| 5598 | ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. |
| 5599 | ** This flag instructs SQLite to omit some corner-case optimizations that |
| 5600 | ** might disrupt the operation of the [sqlite3_value_subtype()] function, |
| 5601 | ** causing it to return zero rather than the correct subtype(). |
| 5602 | ** SQL functions that invokes [sqlite3_value_subtype()] should have this |
| 5603 | ** property. If the SQLITE_SUBTYPE property is omitted, then the return |
| 5604 | ** value from [sqlite3_value_subtype()] might sometimes be zero even though |
| 5605 | ** a non-zero subtype was specified by the function argument expression. |
| 5606 | ** |
| 5607 | ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> |
| @@ -8362,11 +8396,11 @@ | |
| 8362 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8363 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8364 | #define SQLITE_TESTCTRL_TRACEFLAGS 31 |
| 8365 | #define SQLITE_TESTCTRL_TUNE 32 |
| 8366 | #define SQLITE_TESTCTRL_LOGEST 33 |
| 8367 | #define SQLITE_TESTCTRL_USELONGDOUBLE 34 |
| 8368 | #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ |
| 8369 | |
| 8370 | /* |
| 8371 | ** CAPI3REF: SQL Keyword Checking |
| 8372 | ** |
| @@ -9338,10 +9372,20 @@ | |
| 9338 | ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). |
| 9339 | ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() |
| 9340 | ** APIs are not strictly speaking threadsafe. If they are invoked at the |
| 9341 | ** same time as another thread is invoking sqlite3_backup_step() it is |
| 9342 | ** possible that they return invalid values. |
| 9343 | */ |
| 9344 | SQLITE_API sqlite3_backup *sqlite3_backup_init( |
| 9345 | sqlite3 *pDest, /* Destination database handle */ |
| 9346 | const char *zDestName, /* Destination database name */ |
| 9347 | sqlite3 *pSource, /* Source database handle */ |
| @@ -10536,10 +10580,18 @@ | |
| 10536 | ** schema S in database connection D. ^On success, the |
| 10537 | ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly |
| 10538 | ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. |
| 10539 | ** If there is not already a read-transaction open on schema S when |
| 10540 | ** this function is called, one is opened automatically. |
| 10541 | ** |
| 10542 | ** The following must be true for this function to succeed. If any of |
| 10543 | ** the following statements are false when sqlite3_snapshot_get() is |
| 10544 | ** called, SQLITE_ERROR is returned. The final value of *P is undefined |
| 10545 | ** in this case. |
| @@ -10856,11 +10908,11 @@ | |
| 10856 | #endif |
| 10857 | |
| 10858 | #ifdef __cplusplus |
| 10859 | } /* End of the 'extern "C"' block */ |
| 10860 | #endif |
| 10861 | #endif /* SQLITE3_H */ |
| 10862 | |
| 10863 | /******** Begin file sqlite3rtree.h *********/ |
| 10864 | /* |
| 10865 | ** 2010 August 30 |
| 10866 | ** |
| @@ -13107,17 +13159,32 @@ | |
| 13107 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13108 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13109 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13110 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13111 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13112 | ** bytes. This API is not available if the specified token matches a |
| 13113 | ** prefix query term. In that case both output variables are always set |
| 13114 | ** to 0. |
| 13115 | ** |
| 13116 | ** The output text is not a copy of the document text that was tokenized. |
| 13117 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13118 | ** includes any embedded 0x00 and trailing data. |
| 13119 | ** |
| 13120 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13121 | ** "detail=none" or "detail=column" option. |
| 13122 | ** |
| 13123 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -13205,11 +13272,10 @@ | |
| 13205 | ** CUSTOM TOKENIZERS |
| 13206 | ** |
| 13207 | ** Applications may also register custom tokenizer types. A tokenizer |
| 13208 | ** is registered by providing fts5 with a populated instance of the |
| 13209 | ** following structure. All structure methods must be defined, setting |
| 13210 | ** |
| 13211 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 13212 | ** behaviour. The structure methods are expected to function as follows: |
| 13213 | ** |
| 13214 | ** xCreate: |
| 13215 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -13549,5 +13615,6 @@ | |
| 13549 | #endif |
| 13550 | |
| 13551 | #endif /* _FTS5_H */ |
| 13552 | |
| 13553 | /******** End of fts5.h *********/ |
| 13554 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -144,13 +144,13 @@ | |
| 144 | ** |
| 145 | ** See also: [sqlite3_libversion()], |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.48.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3048000 |
| 151 | #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -650,10 +650,17 @@ | |
| 650 | ** |
| 651 | ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying |
| 652 | ** filesystem supports doing multiple write operations atomically when those |
| 653 | ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and |
| 654 | ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. |
| 655 | ** |
| 656 | ** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read |
| 657 | ** from the database file in amounts that are not a multiple of the |
| 658 | ** page size and that do not begin at a page boundary. Without this |
| 659 | ** property, SQLite is careful to only do full-page reads and write |
| 660 | ** on aligned pages, with the one exception that it will do a sub-page |
| 661 | ** read of the first page to access the database header. |
| 662 | */ |
| 663 | #define SQLITE_IOCAP_ATOMIC 0x00000001 |
| 664 | #define SQLITE_IOCAP_ATOMIC512 0x00000002 |
| 665 | #define SQLITE_IOCAP_ATOMIC1K 0x00000004 |
| 666 | #define SQLITE_IOCAP_ATOMIC2K 0x00000008 |
| @@ -666,10 +673,11 @@ | |
| 673 | #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 |
| 674 | #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 |
| 675 | #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 |
| 676 | #define SQLITE_IOCAP_IMMUTABLE 0x00002000 |
| 677 | #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 |
| 678 | #define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: File Locking Levels |
| 682 | ** |
| 683 | ** SQLite uses one of these integer values as the second |
| @@ -812,10 +820,11 @@ | |
| 820 | ** <li> [SQLITE_IOCAP_SEQUENTIAL] |
| 821 | ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] |
| 822 | ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] |
| 823 | ** <li> [SQLITE_IOCAP_IMMUTABLE] |
| 824 | ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] |
| 825 | ** <li> [SQLITE_IOCAP_SUBPAGE_READ] |
| 826 | ** </ul> |
| 827 | ** |
| 828 | ** The SQLITE_IOCAP_ATOMIC property means that all writes of |
| 829 | ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values |
| 830 | ** mean that writes of blocks that are nnn bytes in size and |
| @@ -1089,10 +1098,15 @@ | |
| 1098 | ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This |
| 1099 | ** opcode causes the xFileControl method to swap the file handle with the one |
| 1100 | ** pointed to by the pArg argument. This capability is used during testing |
| 1101 | ** and only needs to be supported when SQLITE_TEST is defined. |
| 1102 | ** |
| 1103 | ** <li>[[SQLITE_FCNTL_NULL_IO]] |
| 1104 | ** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor |
| 1105 | ** or file handle for the [sqlite3_file] object such that it will no longer |
| 1106 | ** read or write to the database file. |
| 1107 | ** |
| 1108 | ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] |
| 1109 | ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might |
| 1110 | ** be advantageous to block on the next WAL lock if the lock is not immediately |
| 1111 | ** available. The WAL subsystem issues this signal during rare |
| 1112 | ** circumstances in order to fix a problem with priority inversion. |
| @@ -1242,10 +1256,11 @@ | |
| 1256 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1257 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1258 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1259 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1260 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1261 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1262 | |
| 1263 | /* deprecated names */ |
| 1264 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1265 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1266 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -2620,14 +2635,18 @@ | |
| 2635 | ** |
| 2636 | ** ^These functions return the number of rows modified, inserted or |
| 2637 | ** deleted by the most recently completed INSERT, UPDATE or DELETE |
| 2638 | ** statement on the database connection specified by the only parameter. |
| 2639 | ** The two functions are identical except for the type of the return value |
| 2640 | ** and that if the number of rows modified by the most recent INSERT, UPDATE, |
| 2641 | ** or DELETE is greater than the maximum value supported by type "int", then |
| 2642 | ** the return value of sqlite3_changes() is undefined. ^Executing any other |
| 2643 | ** type of SQL statement does not modify the value returned by these functions. |
| 2644 | ** For the purposes of this interface, a CREATE TABLE AS SELECT statement |
| 2645 | ** does not count as an INSERT, UPDATE or DELETE statement and hence the rows |
| 2646 | ** added to the new table by the CREATE TABLE AS SELECT statement are not |
| 2647 | ** counted. |
| 2648 | ** |
| 2649 | ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are |
| 2650 | ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], |
| 2651 | ** [foreign key actions] or [REPLACE] constraint resolution are not counted. |
| 2652 | ** |
| @@ -4183,15 +4202,26 @@ | |
| 4202 | ** |
| 4203 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4204 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4205 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4206 | ** any virtual tables. |
| 4207 | ** |
| 4208 | ** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt> |
| 4209 | ** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler |
| 4210 | ** errors from being sent to the error log defined by |
| 4211 | ** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test |
| 4212 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4213 | ** messages on the global error log when it is not. If the test compile |
| 4214 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4215 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4216 | ** logs the error. |
| 4217 | ** </dl> |
| 4218 | */ |
| 4219 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4220 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4221 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4222 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4223 | |
| 4224 | /* |
| 4225 | ** CAPI3REF: Compiling An SQL Statement |
| 4226 | ** KEYWORDS: {SQL statement compiler} |
| 4227 | ** METHOD: sqlite3 |
| @@ -4220,17 +4250,21 @@ | |
| 4250 | ** and sqlite3_prepare_v3() |
| 4251 | ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), |
| 4252 | ** and sqlite3_prepare16_v3() use UTF-16. |
| 4253 | ** |
| 4254 | ** ^If the nByte argument is negative, then zSql is read up to the |
| 4255 | ** first zero terminator. ^If nByte is positive, then it is the maximum |
| 4256 | ** number of bytes read from zSql. When nByte is positive, zSql is read |
| 4257 | ** up to the first zero terminator or until the nByte bytes have been read, |
| 4258 | ** whichever comes first. ^If nByte is zero, then no prepared |
| 4259 | ** statement is generated. |
| 4260 | ** If the caller knows that the supplied string is nul-terminated, then |
| 4261 | ** there is a small performance advantage to passing an nByte parameter that |
| 4262 | ** is the number of bytes in the input string <i>including</i> |
| 4263 | ** the nul-terminator. |
| 4264 | ** Note that nByte measure the length of the input in bytes, not |
| 4265 | ** characters, even for the UTF-16 interfaces. |
| 4266 | ** |
| 4267 | ** ^If pzTail is not NULL then *pzTail is made to point to the first byte |
| 4268 | ** past the end of the first SQL statement in zSql. These routines only |
| 4269 | ** compile the first statement in zSql, so *pzTail is left pointing to |
| 4270 | ** what remains uncompiled. |
| @@ -5597,11 +5631,11 @@ | |
| 5631 | ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call |
| 5632 | ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. |
| 5633 | ** This flag instructs SQLite to omit some corner-case optimizations that |
| 5634 | ** might disrupt the operation of the [sqlite3_value_subtype()] function, |
| 5635 | ** causing it to return zero rather than the correct subtype(). |
| 5636 | ** All SQL functions that invoke [sqlite3_value_subtype()] should have this |
| 5637 | ** property. If the SQLITE_SUBTYPE property is omitted, then the return |
| 5638 | ** value from [sqlite3_value_subtype()] might sometimes be zero even though |
| 5639 | ** a non-zero subtype was specified by the function argument expression. |
| 5640 | ** |
| 5641 | ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> |
| @@ -8362,11 +8396,11 @@ | |
| 8396 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8397 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8398 | #define SQLITE_TESTCTRL_TRACEFLAGS 31 |
| 8399 | #define SQLITE_TESTCTRL_TUNE 32 |
| 8400 | #define SQLITE_TESTCTRL_LOGEST 33 |
| 8401 | #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ |
| 8402 | #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ |
| 8403 | |
| 8404 | /* |
| 8405 | ** CAPI3REF: SQL Keyword Checking |
| 8406 | ** |
| @@ -9338,10 +9372,20 @@ | |
| 9372 | ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). |
| 9373 | ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() |
| 9374 | ** APIs are not strictly speaking threadsafe. If they are invoked at the |
| 9375 | ** same time as another thread is invoking sqlite3_backup_step() it is |
| 9376 | ** possible that they return invalid values. |
| 9377 | ** |
| 9378 | ** <b>Alternatives To Using The Backup API</b> |
| 9379 | ** |
| 9380 | ** Other techniques for safely creating a consistent backup of an SQLite |
| 9381 | ** database include: |
| 9382 | ** |
| 9383 | ** <ul> |
| 9384 | ** <li> The [VACUUM INTO] command. |
| 9385 | ** <li> The [sqlite3_rsync] utility program. |
| 9386 | ** </ul> |
| 9387 | */ |
| 9388 | SQLITE_API sqlite3_backup *sqlite3_backup_init( |
| 9389 | sqlite3 *pDest, /* Destination database handle */ |
| 9390 | const char *zDestName, /* Destination database name */ |
| 9391 | sqlite3 *pSource, /* Source database handle */ |
| @@ -10536,10 +10580,18 @@ | |
| 10580 | ** schema S in database connection D. ^On success, the |
| 10581 | ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly |
| 10582 | ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. |
| 10583 | ** If there is not already a read-transaction open on schema S when |
| 10584 | ** this function is called, one is opened automatically. |
| 10585 | ** |
| 10586 | ** If a read-transaction is opened by this function, then it is guaranteed |
| 10587 | ** that the returned snapshot object may not be invalidated by a database |
| 10588 | ** writer or checkpointer until after the read-transaction is closed. This |
| 10589 | ** is not guaranteed if a read-transaction is already open when this |
| 10590 | ** function is called. In that case, any subsequent write or checkpoint |
| 10591 | ** operation on the database may invalidate the returned snapshot handle, |
| 10592 | ** even while the read-transaction remains open. |
| 10593 | ** |
| 10594 | ** The following must be true for this function to succeed. If any of |
| 10595 | ** the following statements are false when sqlite3_snapshot_get() is |
| 10596 | ** called, SQLITE_ERROR is returned. The final value of *P is undefined |
| 10597 | ** in this case. |
| @@ -10856,11 +10908,11 @@ | |
| 10908 | #endif |
| 10909 | |
| 10910 | #ifdef __cplusplus |
| 10911 | } /* End of the 'extern "C"' block */ |
| 10912 | #endif |
| 10913 | /* #endif for SQLITE3_H will be added by mksqlite3.tcl */ |
| 10914 | |
| 10915 | /******** Begin file sqlite3rtree.h *********/ |
| 10916 | /* |
| 10917 | ** 2010 August 30 |
| 10918 | ** |
| @@ -13107,17 +13159,32 @@ | |
| 13159 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13160 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13161 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13162 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13163 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13164 | ** bytes. |
| 13165 | ** |
| 13166 | ** The output text is not a copy of the document text that was tokenized. |
| 13167 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13168 | ** includes any embedded 0x00 and trailing data. |
| 13169 | ** |
| 13170 | ** This API may be slow in some cases if the token identified by parameters |
| 13171 | ** iIdx and iToken matched a prefix token in the query. In most cases, the |
| 13172 | ** first call to this API for each prefix token in the query is forced |
| 13173 | ** to scan the portion of the full-text index that matches the prefix |
| 13174 | ** token to collect the extra data required by this API. If the prefix |
| 13175 | ** token matches a large number of token instances in the document set, |
| 13176 | ** this may be a performance problem. |
| 13177 | ** |
| 13178 | ** If the user knows in advance that a query may use this API for a |
| 13179 | ** prefix token, FTS5 may be configured to collect all required data as part |
| 13180 | ** of the initial querying of the full-text index, avoiding the second scan |
| 13181 | ** entirely. This also causes prefix queries that do not use this API to |
| 13182 | ** run more slowly and use more memory. FTS5 may be configured in this way |
| 13183 | ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] |
| 13184 | ** option, or on a per-query basis using the |
| 13185 | ** [fts5_insttoken | fts5_insttoken()] user function. |
| 13186 | ** |
| 13187 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13188 | ** "detail=none" or "detail=column" option. |
| 13189 | ** |
| 13190 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| @@ -13205,11 +13272,10 @@ | |
| 13272 | ** CUSTOM TOKENIZERS |
| 13273 | ** |
| 13274 | ** Applications may also register custom tokenizer types. A tokenizer |
| 13275 | ** is registered by providing fts5 with a populated instance of the |
| 13276 | ** following structure. All structure methods must be defined, setting |
| 13277 | ** any member of the fts5_tokenizer struct to NULL leads to undefined |
| 13278 | ** behaviour. The structure methods are expected to function as follows: |
| 13279 | ** |
| 13280 | ** xCreate: |
| 13281 | ** This function is used to allocate and initialize a tokenizer instance. |
| @@ -13549,5 +13615,6 @@ | |
| 13615 | #endif |
| 13616 | |
| 13617 | #endif /* _FTS5_H */ |
| 13618 | |
| 13619 | /******** End of fts5.h *********/ |
| 13620 | #endif /* SQLITE3_H */ |
| 13621 |
M
fossil.1
+22
-17
| --- fossil.1 | ||
| +++ fossil.1 | ||
| @@ -1,6 +1,6 @@ | ||
| 1 | -.TH FOSSIL "1" "July 2021" "https://fossil-scm.org" "User Commands" | |
| 1 | +.TH FOSSIL "1" "Oct 2024" "https://fossil-scm.org" "User Commands" | |
| 2 | 2 | .SH NAME |
| 3 | 3 | fossil \- Distributed Version Control System |
| 4 | 4 | .SH SYNOPSIS |
| 5 | 5 | .B fossil |
| 6 | 6 | \fIhelp\fR |
| @@ -14,26 +14,31 @@ | ||
| 14 | 14 | Fossil is a distributed version control system (DVCS) with built-in |
| 15 | 15 | forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server. |
| 16 | 16 | |
| 17 | 17 | .SH Common COMMANDs: |
| 18 | 18 | |
| 19 | -add cat diff ls revert timeline | |
| 20 | -.br | |
| 21 | -addremove changes extras merge rm ui | |
| 22 | -.br | |
| 23 | -all chat finfo mv settings undo | |
| 24 | -.br | |
| 25 | -amend clean gdiff open sql unversioned | |
| 26 | -.br | |
| 27 | -annotate clone grep pull stash update | |
| 28 | -.br | |
| 29 | -bisect commit help push status version | |
| 30 | -.br | |
| 31 | -blame dbstat info rebuild sync | |
| 32 | -.br | |
| 33 | -branch delete init remote tag | |
| 34 | -.br | |
| 19 | +add cherrypick grep push sync | |
| 20 | +.br | |
| 21 | +addremove clean help rebuild tag | |
| 22 | +.br | |
| 23 | +all clone info remote timeline | |
| 24 | +.br | |
| 25 | +amend commit init repack tree | |
| 26 | +.br | |
| 27 | +annotate dbstat ls revert ui | |
| 28 | +.br | |
| 29 | +bisect delete merge rm undo | |
| 30 | +.br | |
| 31 | +blame describe merge-base settings unversioned | |
| 32 | +.br | |
| 33 | +branch diff mv sql update | |
| 34 | +.br | |
| 35 | +cat extras open ssl-config version | |
| 36 | +.br | |
| 37 | +changes finfo patch stash xdiff | |
| 38 | +.br | |
| 39 | +chat gdiff pull status | |
| 35 | 40 | |
| 36 | 41 | .SH FEATURES |
| 37 | 42 | |
| 38 | 43 | Features as described on the fossil home page. |
| 39 | 44 | |
| 40 | 45 |
| --- fossil.1 | |
| +++ fossil.1 | |
| @@ -1,6 +1,6 @@ | |
| 1 | .TH FOSSIL "1" "July 2021" "https://fossil-scm.org" "User Commands" |
| 2 | .SH NAME |
| 3 | fossil \- Distributed Version Control System |
| 4 | .SH SYNOPSIS |
| 5 | .B fossil |
| 6 | \fIhelp\fR |
| @@ -14,26 +14,31 @@ | |
| 14 | Fossil is a distributed version control system (DVCS) with built-in |
| 15 | forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server. |
| 16 | |
| 17 | .SH Common COMMANDs: |
| 18 | |
| 19 | add cat diff ls revert timeline |
| 20 | .br |
| 21 | addremove changes extras merge rm ui |
| 22 | .br |
| 23 | all chat finfo mv settings undo |
| 24 | .br |
| 25 | amend clean gdiff open sql unversioned |
| 26 | .br |
| 27 | annotate clone grep pull stash update |
| 28 | .br |
| 29 | bisect commit help push status version |
| 30 | .br |
| 31 | blame dbstat info rebuild sync |
| 32 | .br |
| 33 | branch delete init remote tag |
| 34 | .br |
| 35 | |
| 36 | .SH FEATURES |
| 37 | |
| 38 | Features as described on the fossil home page. |
| 39 | |
| 40 |
| --- fossil.1 | |
| +++ fossil.1 | |
| @@ -1,6 +1,6 @@ | |
| 1 | .TH FOSSIL "1" "Oct 2024" "https://fossil-scm.org" "User Commands" |
| 2 | .SH NAME |
| 3 | fossil \- Distributed Version Control System |
| 4 | .SH SYNOPSIS |
| 5 | .B fossil |
| 6 | \fIhelp\fR |
| @@ -14,26 +14,31 @@ | |
| 14 | Fossil is a distributed version control system (DVCS) with built-in |
| 15 | forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server. |
| 16 | |
| 17 | .SH Common COMMANDs: |
| 18 | |
| 19 | add cherrypick grep push sync |
| 20 | .br |
| 21 | addremove clean help rebuild tag |
| 22 | .br |
| 23 | all clone info remote timeline |
| 24 | .br |
| 25 | amend commit init repack tree |
| 26 | .br |
| 27 | annotate dbstat ls revert ui |
| 28 | .br |
| 29 | bisect delete merge rm undo |
| 30 | .br |
| 31 | blame describe merge-base settings unversioned |
| 32 | .br |
| 33 | branch diff mv sql update |
| 34 | .br |
| 35 | cat extras open ssl-config version |
| 36 | .br |
| 37 | changes finfo patch stash xdiff |
| 38 | .br |
| 39 | chat gdiff pull status |
| 40 | |
| 41 | .SH FEATURES |
| 42 | |
| 43 | Features as described on the fossil home page. |
| 44 | |
| 45 |
+4
-1
| --- skins/darkmode/css.txt | ||
| +++ skins/darkmode/css.txt | ||
| @@ -98,14 +98,17 @@ | ||
| 98 | 98 | } |
| 99 | 99 | .fileage tr:hover, |
| 100 | 100 | div.filetreeline:hover { |
| 101 | 101 | background-color: #333; |
| 102 | 102 | } |
| 103 | +div.file-change-line button { | |
| 104 | + background-color: #484848 | |
| 105 | +} | |
| 103 | 106 | .button, |
| 104 | 107 | button { |
| 105 | 108 | color: #aaa; |
| 106 | - background-color: #444; | |
| 109 | + background-color: #484848; | |
| 107 | 110 | border-radius: 5px; |
| 108 | 111 | border: 0 |
| 109 | 112 | } |
| 110 | 113 | .button:hover, |
| 111 | 114 | button:hover { |
| 112 | 115 |
| --- skins/darkmode/css.txt | |
| +++ skins/darkmode/css.txt | |
| @@ -98,14 +98,17 @@ | |
| 98 | } |
| 99 | .fileage tr:hover, |
| 100 | div.filetreeline:hover { |
| 101 | background-color: #333; |
| 102 | } |
| 103 | .button, |
| 104 | button { |
| 105 | color: #aaa; |
| 106 | background-color: #444; |
| 107 | border-radius: 5px; |
| 108 | border: 0 |
| 109 | } |
| 110 | .button:hover, |
| 111 | button:hover { |
| 112 |
| --- skins/darkmode/css.txt | |
| +++ skins/darkmode/css.txt | |
| @@ -98,14 +98,17 @@ | |
| 98 | } |
| 99 | .fileage tr:hover, |
| 100 | div.filetreeline:hover { |
| 101 | background-color: #333; |
| 102 | } |
| 103 | div.file-change-line button { |
| 104 | background-color: #484848 |
| 105 | } |
| 106 | .button, |
| 107 | button { |
| 108 | color: #aaa; |
| 109 | background-color: #484848; |
| 110 | border-radius: 5px; |
| 111 | border: 0 |
| 112 | } |
| 113 | .button:hover, |
| 114 | button:hover { |
| 115 |
+7
-6
| --- skins/default/css.txt | ||
| +++ skins/default/css.txt | ||
| @@ -42,10 +42,11 @@ | ||
| 42 | 42 | .artifact h1.page-title, |
| 43 | 43 | .dir h1.page-title, |
| 44 | 44 | .doc h1.page-title, |
| 45 | 45 | .wiki h1.page-title { |
| 46 | 46 | display: block; /* …for potentially long doc titles… */ |
| 47 | + color: #444; | |
| 47 | 48 | } |
| 48 | 49 | .artifact .title > .page-title, |
| 49 | 50 | .dir .title > .page-title, |
| 50 | 51 | .doc .title > .page-title, |
| 51 | 52 | .wiki .title > .page-title { |
| @@ -725,16 +726,16 @@ | ||
| 725 | 726 | margin-left: 20pt; /* special case for MD in forum; need less indent */ |
| 726 | 727 | } |
| 727 | 728 | |
| 728 | 729 | /* Fossil UI uses these, but in sufficiently constrained ways that we |
| 729 | 730 | * don't have to be nearly as careful to avoid an overreach. */ |
| 730 | - .doc > .content h1, .artifact h1, .dir h1, .fileedit h1, .wiki h1 { margin-left: 10pt; } | |
| 731 | - .doc > .content h2, .artifact h2, .dir h2, .fileedit h2, .wiki h2 { margin-left: 20pt; } | |
| 732 | - .doc > .content h3, .artifact h3, .dir h3, .fileedit h3, .wiki h3 { margin-left: 30pt; } | |
| 733 | - .doc > .content h4, .artifact h4, .dir h4, .fileedit h4, .wiki h4 { margin-left: 40pt; } | |
| 734 | - .doc > .content h5, .artifact h5, .dir h5, .fileedit h5, .wiki h5 { margin-left: 50pt; } | |
| 735 | - .doc > .content hr, .artifact hr, .dir hr, .fileedit hr, .wiki hr { margin-left: 10pt; } | |
| 731 | + .doc > .content h1, .artifact .content h1, .dir .content h1, .fileedit .content h1, .wiki .content h1 { margin-left: 10pt; } | |
| 732 | + .doc > .content h2, .artifact .content h2, .dir .content h2, .fileedit .content h2, .wiki .content h2 { margin-left: 20pt; } | |
| 733 | + .doc > .content h3, .artifact .content h3, .dir .content h3, .fileedit .content h3, .wiki .content h3 { margin-left: 30pt; } | |
| 734 | + .doc > .content h4, .artifact .content h4, .dir .content h4, .fileedit .content h4, .wiki .content h4 { margin-left: 40pt; } | |
| 735 | + .doc > .content h5, .artifact .content h5, .dir .content h5, .fileedit .content h5, .wiki .content h5 { margin-left: 50pt; } | |
| 736 | + .doc > .content hr, .artifact .content hr, .dir .content hr, .fileedit .content hr, .wiki .content hr { margin-left: 10pt; } | |
| 736 | 737 | |
| 737 | 738 | /* Don't need to be nearly as careful with tags Fossil UI doesn't use. */ |
| 738 | 739 | .doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; } |
| 739 | 740 | .doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; } |
| 740 | 741 | .doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; } |
| 741 | 742 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -42,10 +42,11 @@ | |
| 42 | .artifact h1.page-title, |
| 43 | .dir h1.page-title, |
| 44 | .doc h1.page-title, |
| 45 | .wiki h1.page-title { |
| 46 | display: block; /* …for potentially long doc titles… */ |
| 47 | } |
| 48 | .artifact .title > .page-title, |
| 49 | .dir .title > .page-title, |
| 50 | .doc .title > .page-title, |
| 51 | .wiki .title > .page-title { |
| @@ -725,16 +726,16 @@ | |
| 725 | margin-left: 20pt; /* special case for MD in forum; need less indent */ |
| 726 | } |
| 727 | |
| 728 | /* Fossil UI uses these, but in sufficiently constrained ways that we |
| 729 | * don't have to be nearly as careful to avoid an overreach. */ |
| 730 | .doc > .content h1, .artifact h1, .dir h1, .fileedit h1, .wiki h1 { margin-left: 10pt; } |
| 731 | .doc > .content h2, .artifact h2, .dir h2, .fileedit h2, .wiki h2 { margin-left: 20pt; } |
| 732 | .doc > .content h3, .artifact h3, .dir h3, .fileedit h3, .wiki h3 { margin-left: 30pt; } |
| 733 | .doc > .content h4, .artifact h4, .dir h4, .fileedit h4, .wiki h4 { margin-left: 40pt; } |
| 734 | .doc > .content h5, .artifact h5, .dir h5, .fileedit h5, .wiki h5 { margin-left: 50pt; } |
| 735 | .doc > .content hr, .artifact hr, .dir hr, .fileedit hr, .wiki hr { margin-left: 10pt; } |
| 736 | |
| 737 | /* Don't need to be nearly as careful with tags Fossil UI doesn't use. */ |
| 738 | .doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; } |
| 739 | .doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; } |
| 740 | .doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; } |
| 741 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -42,10 +42,11 @@ | |
| 42 | .artifact h1.page-title, |
| 43 | .dir h1.page-title, |
| 44 | .doc h1.page-title, |
| 45 | .wiki h1.page-title { |
| 46 | display: block; /* …for potentially long doc titles… */ |
| 47 | color: #444; |
| 48 | } |
| 49 | .artifact .title > .page-title, |
| 50 | .dir .title > .page-title, |
| 51 | .doc .title > .page-title, |
| 52 | .wiki .title > .page-title { |
| @@ -725,16 +726,16 @@ | |
| 726 | margin-left: 20pt; /* special case for MD in forum; need less indent */ |
| 727 | } |
| 728 | |
| 729 | /* Fossil UI uses these, but in sufficiently constrained ways that we |
| 730 | * don't have to be nearly as careful to avoid an overreach. */ |
| 731 | .doc > .content h1, .artifact .content h1, .dir .content h1, .fileedit .content h1, .wiki .content h1 { margin-left: 10pt; } |
| 732 | .doc > .content h2, .artifact .content h2, .dir .content h2, .fileedit .content h2, .wiki .content h2 { margin-left: 20pt; } |
| 733 | .doc > .content h3, .artifact .content h3, .dir .content h3, .fileedit .content h3, .wiki .content h3 { margin-left: 30pt; } |
| 734 | .doc > .content h4, .artifact .content h4, .dir .content h4, .fileedit .content h4, .wiki .content h4 { margin-left: 40pt; } |
| 735 | .doc > .content h5, .artifact .content h5, .dir .content h5, .fileedit .content h5, .wiki .content h5 { margin-left: 50pt; } |
| 736 | .doc > .content hr, .artifact .content hr, .dir .content hr, .fileedit .content hr, .wiki .content hr { margin-left: 10pt; } |
| 737 | |
| 738 | /* Don't need to be nearly as careful with tags Fossil UI doesn't use. */ |
| 739 | .doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; } |
| 740 | .doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; } |
| 741 | .doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; } |
| 742 |
+1
-1
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -353,11 +353,11 @@ | ||
| 353 | 353 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 354 | 354 | ** option does not appear on the command line then the "ignore-glob" setting |
| 355 | 355 | ** is used. If the --clean option does not appear on the command line then |
| 356 | 356 | ** the "clean-glob" setting is used. |
| 357 | 357 | ** |
| 358 | -** If files are attempted to be added explicitly on the command line which | |
| 358 | +** When attempting to explicitly add files on the commandline, and if those | |
| 359 | 359 | ** match "ignore-glob", a confirmation is asked first. This can be prevented |
| 360 | 360 | ** using the -f|--force option. |
| 361 | 361 | ** |
| 362 | 362 | ** The --case-sensitive option determines whether or not filenames should |
| 363 | 363 | ** be treated case sensitive or not. If the option is not given, the default |
| 364 | 364 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -353,11 +353,11 @@ | |
| 353 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 354 | ** option does not appear on the command line then the "ignore-glob" setting |
| 355 | ** is used. If the --clean option does not appear on the command line then |
| 356 | ** the "clean-glob" setting is used. |
| 357 | ** |
| 358 | ** If files are attempted to be added explicitly on the command line which |
| 359 | ** match "ignore-glob", a confirmation is asked first. This can be prevented |
| 360 | ** using the -f|--force option. |
| 361 | ** |
| 362 | ** The --case-sensitive option determines whether or not filenames should |
| 363 | ** be treated case sensitive or not. If the option is not given, the default |
| 364 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -353,11 +353,11 @@ | |
| 353 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 354 | ** option does not appear on the command line then the "ignore-glob" setting |
| 355 | ** is used. If the --clean option does not appear on the command line then |
| 356 | ** the "clean-glob" setting is used. |
| 357 | ** |
| 358 | ** When attempting to explicitly add files on the commandline, and if those |
| 359 | ** match "ignore-glob", a confirmation is asked first. This can be prevented |
| 360 | ** using the -f|--force option. |
| 361 | ** |
| 362 | ** The --case-sensitive option determines whether or not filenames should |
| 363 | ** be treated case sensitive or not. If the option is not given, the default |
| 364 |
+23
-6
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -50,11 +50,10 @@ | ||
| 50 | 50 | for(i=iStart; i<g.argc; i++){ |
| 51 | 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | 52 | } |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | - | |
| 56 | 55 | /* |
| 57 | 56 | ** COMMAND: all |
| 58 | 57 | ** |
| 59 | 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 60 | 59 | ** |
| @@ -429,44 +428,62 @@ | ||
| 429 | 428 | "add cache changes clean dbstat extras fts-config git ignore " |
| 430 | 429 | "info list ls pull push rebuild remote " |
| 431 | 430 | "server settings sync ui unset whatis"); |
| 432 | 431 | } |
| 433 | 432 | verify_all_options(); |
| 434 | - db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); | |
| 433 | + db_multi_exec( | |
| 434 | + "CREATE TEMP TABLE repolist(\n" | |
| 435 | + " name TEXT, -- Filename\n" | |
| 436 | + " tag TEXT, -- Key for the GLOBAL_CONFIG table entry\n" | |
| 437 | + " inode TEXT -- Unique identifier for this file\n" | |
| 438 | + ");\n" | |
| 439 | + | |
| 440 | + /* The seenFile() table holds inode names for entries that have | |
| 441 | + ** already been processed. */ | |
| 442 | + "CREATE TEMP TABLE seenFile(x TEXT COLLATE nocase);\n" | |
| 443 | + | |
| 444 | + /* The toDel() table holds the "tag" for entries that need to be | |
| 445 | + ** deleted because they are redundant or no longer exist */ | |
| 446 | + "CREATE TEMP TABLE toDel(x TEXT);\n" | |
| 447 | + ); | |
| 448 | + sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0, | |
| 449 | + file_inode_sql_func, 0, 0); | |
| 435 | 450 | if( useCheckouts ){ |
| 436 | 451 | db_multi_exec( |
| 437 | 452 | "INSERT INTO repolist " |
| 438 | - "SELECT DISTINCT substr(name, 7), name COLLATE nocase" | |
| 453 | + "SELECT substr(name, 7), name, inode(substr(name,7))" | |
| 439 | 454 | " FROM global_config" |
| 440 | 455 | " WHERE substr(name, 1, 6)=='ckout:'" |
| 441 | 456 | " ORDER BY 1" |
| 442 | 457 | ); |
| 443 | 458 | }else{ |
| 444 | 459 | db_multi_exec( |
| 445 | 460 | "INSERT INTO repolist " |
| 446 | - "SELECT DISTINCT substr(name, 6), name COLLATE nocase" | |
| 461 | + "SELECT substr(name, 6), name, inode(substr(name,6))" | |
| 447 | 462 | " FROM global_config" |
| 448 | 463 | " WHERE substr(name, 1, 5)=='repo:'" |
| 449 | 464 | " ORDER BY 1" |
| 450 | 465 | ); |
| 451 | 466 | } |
| 452 | - db_multi_exec("CREATE TEMP TABLE toDel(x TEXT)"); | |
| 453 | - db_prepare(&q, "SELECT name, tag FROM repolist ORDER BY 1"); | |
| 467 | + db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1"); | |
| 454 | 468 | while( db_step(&q)==SQLITE_ROW ){ |
| 455 | 469 | int rc; |
| 456 | 470 | const char *zFilename = db_column_text(&q, 0); |
| 471 | + const char *zInode = db_column_text(&q,2); | |
| 457 | 472 | #if !USE_SEE |
| 458 | 473 | if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; |
| 459 | 474 | #endif |
| 460 | 475 | if( file_access(zFilename, F_OK) |
| 461 | 476 | || !file_is_canonical(zFilename) |
| 462 | 477 | || (useCheckouts && file_isdir(zFilename, ExtFILE)!=1) |
| 478 | + || db_exists("SELECT 1 FROM temp.seenFile where x=%Q", zInode) | |
| 463 | 479 | ){ |
| 464 | 480 | db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1)); |
| 465 | 481 | nToDel++; |
| 466 | 482 | continue; |
| 467 | 483 | } |
| 484 | + db_multi_exec("INSERT INTO seenFile(x) VALUES(%Q)", zInode); | |
| 468 | 485 | if( zCmd[0]=='l' ){ |
| 469 | 486 | fossil_print("%s\n", zFilename); |
| 470 | 487 | continue; |
| 471 | 488 | }else if( showFile ){ |
| 472 | 489 | fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository", |
| 473 | 490 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -50,11 +50,10 @@ | |
| 50 | for(i=iStart; i<g.argc; i++){ |
| 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | |
| 56 | /* |
| 57 | ** COMMAND: all |
| 58 | ** |
| 59 | ** Usage: %fossil all SUBCOMMAND ... |
| 60 | ** |
| @@ -429,44 +428,62 @@ | |
| 429 | "add cache changes clean dbstat extras fts-config git ignore " |
| 430 | "info list ls pull push rebuild remote " |
| 431 | "server settings sync ui unset whatis"); |
| 432 | } |
| 433 | verify_all_options(); |
| 434 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 435 | if( useCheckouts ){ |
| 436 | db_multi_exec( |
| 437 | "INSERT INTO repolist " |
| 438 | "SELECT DISTINCT substr(name, 7), name COLLATE nocase" |
| 439 | " FROM global_config" |
| 440 | " WHERE substr(name, 1, 6)=='ckout:'" |
| 441 | " ORDER BY 1" |
| 442 | ); |
| 443 | }else{ |
| 444 | db_multi_exec( |
| 445 | "INSERT INTO repolist " |
| 446 | "SELECT DISTINCT substr(name, 6), name COLLATE nocase" |
| 447 | " FROM global_config" |
| 448 | " WHERE substr(name, 1, 5)=='repo:'" |
| 449 | " ORDER BY 1" |
| 450 | ); |
| 451 | } |
| 452 | db_multi_exec("CREATE TEMP TABLE toDel(x TEXT)"); |
| 453 | db_prepare(&q, "SELECT name, tag FROM repolist ORDER BY 1"); |
| 454 | while( db_step(&q)==SQLITE_ROW ){ |
| 455 | int rc; |
| 456 | const char *zFilename = db_column_text(&q, 0); |
| 457 | #if !USE_SEE |
| 458 | if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; |
| 459 | #endif |
| 460 | if( file_access(zFilename, F_OK) |
| 461 | || !file_is_canonical(zFilename) |
| 462 | || (useCheckouts && file_isdir(zFilename, ExtFILE)!=1) |
| 463 | ){ |
| 464 | db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1)); |
| 465 | nToDel++; |
| 466 | continue; |
| 467 | } |
| 468 | if( zCmd[0]=='l' ){ |
| 469 | fossil_print("%s\n", zFilename); |
| 470 | continue; |
| 471 | }else if( showFile ){ |
| 472 | fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository", |
| 473 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -50,11 +50,10 @@ | |
| 50 | for(i=iStart; i<g.argc; i++){ |
| 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** COMMAND: all |
| 57 | ** |
| 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | ** |
| @@ -429,44 +428,62 @@ | |
| 428 | "add cache changes clean dbstat extras fts-config git ignore " |
| 429 | "info list ls pull push rebuild remote " |
| 430 | "server settings sync ui unset whatis"); |
| 431 | } |
| 432 | verify_all_options(); |
| 433 | db_multi_exec( |
| 434 | "CREATE TEMP TABLE repolist(\n" |
| 435 | " name TEXT, -- Filename\n" |
| 436 | " tag TEXT, -- Key for the GLOBAL_CONFIG table entry\n" |
| 437 | " inode TEXT -- Unique identifier for this file\n" |
| 438 | ");\n" |
| 439 | |
| 440 | /* The seenFile() table holds inode names for entries that have |
| 441 | ** already been processed. */ |
| 442 | "CREATE TEMP TABLE seenFile(x TEXT COLLATE nocase);\n" |
| 443 | |
| 444 | /* The toDel() table holds the "tag" for entries that need to be |
| 445 | ** deleted because they are redundant or no longer exist */ |
| 446 | "CREATE TEMP TABLE toDel(x TEXT);\n" |
| 447 | ); |
| 448 | sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0, |
| 449 | file_inode_sql_func, 0, 0); |
| 450 | if( useCheckouts ){ |
| 451 | db_multi_exec( |
| 452 | "INSERT INTO repolist " |
| 453 | "SELECT substr(name, 7), name, inode(substr(name,7))" |
| 454 | " FROM global_config" |
| 455 | " WHERE substr(name, 1, 6)=='ckout:'" |
| 456 | " ORDER BY 1" |
| 457 | ); |
| 458 | }else{ |
| 459 | db_multi_exec( |
| 460 | "INSERT INTO repolist " |
| 461 | "SELECT substr(name, 6), name, inode(substr(name,6))" |
| 462 | " FROM global_config" |
| 463 | " WHERE substr(name, 1, 5)=='repo:'" |
| 464 | " ORDER BY 1" |
| 465 | ); |
| 466 | } |
| 467 | db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1"); |
| 468 | while( db_step(&q)==SQLITE_ROW ){ |
| 469 | int rc; |
| 470 | const char *zFilename = db_column_text(&q, 0); |
| 471 | const char *zInode = db_column_text(&q,2); |
| 472 | #if !USE_SEE |
| 473 | if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; |
| 474 | #endif |
| 475 | if( file_access(zFilename, F_OK) |
| 476 | || !file_is_canonical(zFilename) |
| 477 | || (useCheckouts && file_isdir(zFilename, ExtFILE)!=1) |
| 478 | || db_exists("SELECT 1 FROM temp.seenFile where x=%Q", zInode) |
| 479 | ){ |
| 480 | db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1)); |
| 481 | nToDel++; |
| 482 | continue; |
| 483 | } |
| 484 | db_multi_exec("INSERT INTO seenFile(x) VALUES(%Q)", zInode); |
| 485 | if( zCmd[0]=='l' ){ |
| 486 | fossil_print("%s\n", zFilename); |
| 487 | continue; |
| 488 | }else if( showFile ){ |
| 489 | fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository", |
| 490 |
+8
-1
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -634,11 +634,12 @@ | ||
| 634 | 634 | /* |
| 635 | 635 | ** Output HTML to show a list of attachments. |
| 636 | 636 | */ |
| 637 | 637 | void attachment_list( |
| 638 | 638 | const char *zTarget, /* Object that things are attached to */ |
| 639 | - const char *zHeader /* Header to display with attachments */ | |
| 639 | + const char *zHeader, /* Header to display with attachments */ | |
| 640 | + int fHorizontalRule /* Insert <hr> separator above header */ | |
| 640 | 641 | ){ |
| 641 | 642 | int cnt = 0; |
| 642 | 643 | Stmt q; |
| 643 | 644 | db_prepare(&q, |
| 644 | 645 | "SELECT datetime(mtime,toLocal()), filename, user," |
| @@ -654,11 +655,16 @@ | ||
| 654 | 655 | const char *zUser = db_column_text(&q, 2); |
| 655 | 656 | const char *zUuid = db_column_text(&q, 3); |
| 656 | 657 | const char *zSrc = db_column_text(&q, 4); |
| 657 | 658 | const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; |
| 658 | 659 | if( cnt==0 ){ |
| 660 | + @ <section class='attachlist'> | |
| 661 | + if( fHorizontalRule ){ | |
| 662 | + @ <hr> | |
| 663 | + } | |
| 659 | 664 | @ %s(zHeader) |
| 665 | + @ <ul> | |
| 660 | 666 | } |
| 661 | 667 | cnt++; |
| 662 | 668 | @ <li> |
| 663 | 669 | @ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a> |
| 664 | 670 | @ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</a>] |
| @@ -667,10 +673,11 @@ | ||
| 667 | 673 | @ [%z(href("%R/ainfo/%!S",zUuid))details</a>] |
| 668 | 674 | @ </li> |
| 669 | 675 | } |
| 670 | 676 | if( cnt ){ |
| 671 | 677 | @ </ul> |
| 678 | + @ </section> | |
| 672 | 679 | } |
| 673 | 680 | db_finalize(&q); |
| 674 | 681 | |
| 675 | 682 | } |
| 676 | 683 | |
| 677 | 684 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -634,11 +634,12 @@ | |
| 634 | /* |
| 635 | ** Output HTML to show a list of attachments. |
| 636 | */ |
| 637 | void attachment_list( |
| 638 | const char *zTarget, /* Object that things are attached to */ |
| 639 | const char *zHeader /* Header to display with attachments */ |
| 640 | ){ |
| 641 | int cnt = 0; |
| 642 | Stmt q; |
| 643 | db_prepare(&q, |
| 644 | "SELECT datetime(mtime,toLocal()), filename, user," |
| @@ -654,11 +655,16 @@ | |
| 654 | const char *zUser = db_column_text(&q, 2); |
| 655 | const char *zUuid = db_column_text(&q, 3); |
| 656 | const char *zSrc = db_column_text(&q, 4); |
| 657 | const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; |
| 658 | if( cnt==0 ){ |
| 659 | @ %s(zHeader) |
| 660 | } |
| 661 | cnt++; |
| 662 | @ <li> |
| 663 | @ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a> |
| 664 | @ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</a>] |
| @@ -667,10 +673,11 @@ | |
| 667 | @ [%z(href("%R/ainfo/%!S",zUuid))details</a>] |
| 668 | @ </li> |
| 669 | } |
| 670 | if( cnt ){ |
| 671 | @ </ul> |
| 672 | } |
| 673 | db_finalize(&q); |
| 674 | |
| 675 | } |
| 676 | |
| 677 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -634,11 +634,12 @@ | |
| 634 | /* |
| 635 | ** Output HTML to show a list of attachments. |
| 636 | */ |
| 637 | void attachment_list( |
| 638 | const char *zTarget, /* Object that things are attached to */ |
| 639 | const char *zHeader, /* Header to display with attachments */ |
| 640 | int fHorizontalRule /* Insert <hr> separator above header */ |
| 641 | ){ |
| 642 | int cnt = 0; |
| 643 | Stmt q; |
| 644 | db_prepare(&q, |
| 645 | "SELECT datetime(mtime,toLocal()), filename, user," |
| @@ -654,11 +655,16 @@ | |
| 655 | const char *zUser = db_column_text(&q, 2); |
| 656 | const char *zUuid = db_column_text(&q, 3); |
| 657 | const char *zSrc = db_column_text(&q, 4); |
| 658 | const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; |
| 659 | if( cnt==0 ){ |
| 660 | @ <section class='attachlist'> |
| 661 | if( fHorizontalRule ){ |
| 662 | @ <hr> |
| 663 | } |
| 664 | @ %s(zHeader) |
| 665 | @ <ul> |
| 666 | } |
| 667 | cnt++; |
| 668 | @ <li> |
| 669 | @ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a> |
| 670 | @ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</a>] |
| @@ -667,10 +673,11 @@ | |
| 673 | @ [%z(href("%R/ainfo/%!S",zUuid))details</a>] |
| 674 | @ </li> |
| 675 | } |
| 676 | if( cnt ){ |
| 677 | @ </ul> |
| 678 | @ </section> |
| 679 | } |
| 680 | db_finalize(&q); |
| 681 | |
| 682 | } |
| 683 | |
| 684 |
+12
-4
| --- src/bisect.c | ||
| +++ src/bisect.c | ||
| @@ -543,14 +543,16 @@ | ||
| 543 | 543 | ** |
| 544 | 544 | ** > fossil bisect vlist|ls|status ?-a|--all? |
| 545 | 545 | ** |
| 546 | 546 | ** List the versions in between the inner-most "bad" and "good". |
| 547 | 547 | ** |
| 548 | -** > fossil bisect ui | |
| 548 | +** > fossil bisect ui ?HOST@USER:PATH? | |
| 549 | 549 | ** |
| 550 | 550 | ** Like "fossil ui" except start on a timeline that shows only the |
| 551 | -** check-ins that are part of the current bisect. | |
| 551 | +** check-ins that are part of the current bisect. If the optional | |
| 552 | +** fourth term is added, then information is shown for the bisect that | |
| 553 | +** occurred in the PATH directory by USER on remote machine HOST. | |
| 552 | 554 | ** |
| 553 | 555 | ** > fossil bisect undo |
| 554 | 556 | ** |
| 555 | 557 | ** Undo the most recent "good", "bad", or "skip" command. |
| 556 | 558 | */ |
| @@ -719,17 +721,23 @@ | ||
| 719 | 721 | } |
| 720 | 722 | }else if( strncmp(zCmd, "reset", n)==0 ){ |
| 721 | 723 | bisect_reset(); |
| 722 | 724 | }else if( strcmp(zCmd, "ui")==0 ){ |
| 723 | 725 | char *newArgv[8]; |
| 726 | + verify_all_options(); | |
| 724 | 727 | newArgv[0] = g.argv[0]; |
| 725 | 728 | newArgv[1] = "ui"; |
| 726 | 729 | newArgv[2] = "--page"; |
| 727 | 730 | newArgv[3] = "timeline?bisect"; |
| 728 | - newArgv[4] = 0; | |
| 731 | + if( g.argc==4 ){ | |
| 732 | + newArgv[4] = g.argv[3]; | |
| 733 | + g.argc = 5; | |
| 734 | + }else{ | |
| 735 | + g.argc = 4; | |
| 736 | + } | |
| 737 | + newArgv[g.argc] = 0; | |
| 729 | 738 | g.argv = newArgv; |
| 730 | - g.argc = 4; | |
| 731 | 739 | cmd_webserver(); |
| 732 | 740 | }else if( strncmp(zCmd, "vlist", n)==0 |
| 733 | 741 | || strncmp(zCmd, "ls", n)==0 |
| 734 | 742 | || strncmp(zCmd, "status", n)==0 |
| 735 | 743 | ){ |
| 736 | 744 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -543,14 +543,16 @@ | |
| 543 | ** |
| 544 | ** > fossil bisect vlist|ls|status ?-a|--all? |
| 545 | ** |
| 546 | ** List the versions in between the inner-most "bad" and "good". |
| 547 | ** |
| 548 | ** > fossil bisect ui |
| 549 | ** |
| 550 | ** Like "fossil ui" except start on a timeline that shows only the |
| 551 | ** check-ins that are part of the current bisect. |
| 552 | ** |
| 553 | ** > fossil bisect undo |
| 554 | ** |
| 555 | ** Undo the most recent "good", "bad", or "skip" command. |
| 556 | */ |
| @@ -719,17 +721,23 @@ | |
| 719 | } |
| 720 | }else if( strncmp(zCmd, "reset", n)==0 ){ |
| 721 | bisect_reset(); |
| 722 | }else if( strcmp(zCmd, "ui")==0 ){ |
| 723 | char *newArgv[8]; |
| 724 | newArgv[0] = g.argv[0]; |
| 725 | newArgv[1] = "ui"; |
| 726 | newArgv[2] = "--page"; |
| 727 | newArgv[3] = "timeline?bisect"; |
| 728 | newArgv[4] = 0; |
| 729 | g.argv = newArgv; |
| 730 | g.argc = 4; |
| 731 | cmd_webserver(); |
| 732 | }else if( strncmp(zCmd, "vlist", n)==0 |
| 733 | || strncmp(zCmd, "ls", n)==0 |
| 734 | || strncmp(zCmd, "status", n)==0 |
| 735 | ){ |
| 736 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -543,14 +543,16 @@ | |
| 543 | ** |
| 544 | ** > fossil bisect vlist|ls|status ?-a|--all? |
| 545 | ** |
| 546 | ** List the versions in between the inner-most "bad" and "good". |
| 547 | ** |
| 548 | ** > fossil bisect ui ?HOST@USER:PATH? |
| 549 | ** |
| 550 | ** Like "fossil ui" except start on a timeline that shows only the |
| 551 | ** check-ins that are part of the current bisect. If the optional |
| 552 | ** fourth term is added, then information is shown for the bisect that |
| 553 | ** occurred in the PATH directory by USER on remote machine HOST. |
| 554 | ** |
| 555 | ** > fossil bisect undo |
| 556 | ** |
| 557 | ** Undo the most recent "good", "bad", or "skip" command. |
| 558 | */ |
| @@ -719,17 +721,23 @@ | |
| 721 | } |
| 722 | }else if( strncmp(zCmd, "reset", n)==0 ){ |
| 723 | bisect_reset(); |
| 724 | }else if( strcmp(zCmd, "ui")==0 ){ |
| 725 | char *newArgv[8]; |
| 726 | verify_all_options(); |
| 727 | newArgv[0] = g.argv[0]; |
| 728 | newArgv[1] = "ui"; |
| 729 | newArgv[2] = "--page"; |
| 730 | newArgv[3] = "timeline?bisect"; |
| 731 | if( g.argc==4 ){ |
| 732 | newArgv[4] = g.argv[3]; |
| 733 | g.argc = 5; |
| 734 | }else{ |
| 735 | g.argc = 4; |
| 736 | } |
| 737 | newArgv[g.argc] = 0; |
| 738 | g.argv = newArgv; |
| 739 | cmd_webserver(); |
| 740 | }else if( strncmp(zCmd, "vlist", n)==0 |
| 741 | || strncmp(zCmd, "ls", n)==0 |
| 742 | || strncmp(zCmd, "status", n)==0 |
| 743 | ){ |
| 744 |
+49
-1
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -665,11 +665,12 @@ | ||
| 665 | 665 | pBlob->nUsed = dehttpize(pBlob->aData); |
| 666 | 666 | } |
| 667 | 667 | |
| 668 | 668 | /* |
| 669 | 669 | ** Extract N bytes from blob pFrom and use it to initialize blob pTo. |
| 670 | -** Return the actual number of bytes extracted. | |
| 670 | +** Return the actual number of bytes extracted. The cursor position | |
| 671 | +** is advanced by the number of bytes extracted. | |
| 671 | 672 | ** |
| 672 | 673 | ** After this call completes, pTo will be an ephemeral blob. |
| 673 | 674 | */ |
| 674 | 675 | int blob_extract(Blob *pFrom, int N, Blob *pTo){ |
| 675 | 676 | blob_is_init(pFrom); |
| @@ -687,10 +688,57 @@ | ||
| 687 | 688 | pTo->iCursor = 0; |
| 688 | 689 | pTo->xRealloc = blobReallocStatic; |
| 689 | 690 | pFrom->iCursor += N; |
| 690 | 691 | return N; |
| 691 | 692 | } |
| 693 | + | |
| 694 | +/* | |
| 695 | +** Extract N **lines** of text from blob pFrom beginning at the current | |
| 696 | +** cursor position and use that text to initialize blob pTo. Unlike the | |
| 697 | +** blob_extract() routine, the cursor position is unchanged. | |
| 698 | +** | |
| 699 | +** pTo is assumed to be uninitialized. | |
| 700 | +** | |
| 701 | +** After this call completes, pTo will be an ephemeral blob. | |
| 702 | +*/ | |
| 703 | +int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){ | |
| 704 | + int i; | |
| 705 | + int mx; | |
| 706 | + int iStart; | |
| 707 | + int n; | |
| 708 | + const char *z; | |
| 709 | + | |
| 710 | + blob_zero(pTo); | |
| 711 | + z = pFrom->aData; | |
| 712 | + i = pFrom->iCursor; | |
| 713 | + mx = pFrom->nUsed; | |
| 714 | + while( N>0 ){ | |
| 715 | + while( i<mx && z[i]!='\n' ){ i++; } | |
| 716 | + if( i>=mx ) break; | |
| 717 | + i++; | |
| 718 | + N--; | |
| 719 | + } | |
| 720 | + iStart = pFrom->iCursor; | |
| 721 | + n = blob_extract(pFrom, i-pFrom->iCursor, pTo); | |
| 722 | + pFrom->iCursor = iStart; | |
| 723 | + return n; | |
| 724 | +} | |
| 725 | + | |
| 726 | +/* | |
| 727 | +** Return the number of lines of text in the blob. If the last | |
| 728 | +** line is incomplete (if it does not have a \n at the end) then | |
| 729 | +** it still counts. | |
| 730 | +*/ | |
| 731 | +int blob_linecount(Blob *p){ | |
| 732 | + int n = 0; | |
| 733 | + int i; | |
| 734 | + for(i=0; i<p->nUsed; i++){ | |
| 735 | + if( p->aData[i]=='\n' ) n++; | |
| 736 | + } | |
| 737 | + if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++; | |
| 738 | + return n; | |
| 739 | +} | |
| 692 | 740 | |
| 693 | 741 | /* |
| 694 | 742 | ** Rewind the cursor on a blob back to the beginning. |
| 695 | 743 | */ |
| 696 | 744 | void blob_rewind(Blob *p){ |
| 697 | 745 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -665,11 +665,12 @@ | |
| 665 | pBlob->nUsed = dehttpize(pBlob->aData); |
| 666 | } |
| 667 | |
| 668 | /* |
| 669 | ** Extract N bytes from blob pFrom and use it to initialize blob pTo. |
| 670 | ** Return the actual number of bytes extracted. |
| 671 | ** |
| 672 | ** After this call completes, pTo will be an ephemeral blob. |
| 673 | */ |
| 674 | int blob_extract(Blob *pFrom, int N, Blob *pTo){ |
| 675 | blob_is_init(pFrom); |
| @@ -687,10 +688,57 @@ | |
| 687 | pTo->iCursor = 0; |
| 688 | pTo->xRealloc = blobReallocStatic; |
| 689 | pFrom->iCursor += N; |
| 690 | return N; |
| 691 | } |
| 692 | |
| 693 | /* |
| 694 | ** Rewind the cursor on a blob back to the beginning. |
| 695 | */ |
| 696 | void blob_rewind(Blob *p){ |
| 697 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -665,11 +665,12 @@ | |
| 665 | pBlob->nUsed = dehttpize(pBlob->aData); |
| 666 | } |
| 667 | |
| 668 | /* |
| 669 | ** Extract N bytes from blob pFrom and use it to initialize blob pTo. |
| 670 | ** Return the actual number of bytes extracted. The cursor position |
| 671 | ** is advanced by the number of bytes extracted. |
| 672 | ** |
| 673 | ** After this call completes, pTo will be an ephemeral blob. |
| 674 | */ |
| 675 | int blob_extract(Blob *pFrom, int N, Blob *pTo){ |
| 676 | blob_is_init(pFrom); |
| @@ -687,10 +688,57 @@ | |
| 688 | pTo->iCursor = 0; |
| 689 | pTo->xRealloc = blobReallocStatic; |
| 690 | pFrom->iCursor += N; |
| 691 | return N; |
| 692 | } |
| 693 | |
| 694 | /* |
| 695 | ** Extract N **lines** of text from blob pFrom beginning at the current |
| 696 | ** cursor position and use that text to initialize blob pTo. Unlike the |
| 697 | ** blob_extract() routine, the cursor position is unchanged. |
| 698 | ** |
| 699 | ** pTo is assumed to be uninitialized. |
| 700 | ** |
| 701 | ** After this call completes, pTo will be an ephemeral blob. |
| 702 | */ |
| 703 | int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){ |
| 704 | int i; |
| 705 | int mx; |
| 706 | int iStart; |
| 707 | int n; |
| 708 | const char *z; |
| 709 | |
| 710 | blob_zero(pTo); |
| 711 | z = pFrom->aData; |
| 712 | i = pFrom->iCursor; |
| 713 | mx = pFrom->nUsed; |
| 714 | while( N>0 ){ |
| 715 | while( i<mx && z[i]!='\n' ){ i++; } |
| 716 | if( i>=mx ) break; |
| 717 | i++; |
| 718 | N--; |
| 719 | } |
| 720 | iStart = pFrom->iCursor; |
| 721 | n = blob_extract(pFrom, i-pFrom->iCursor, pTo); |
| 722 | pFrom->iCursor = iStart; |
| 723 | return n; |
| 724 | } |
| 725 | |
| 726 | /* |
| 727 | ** Return the number of lines of text in the blob. If the last |
| 728 | ** line is incomplete (if it does not have a \n at the end) then |
| 729 | ** it still counts. |
| 730 | */ |
| 731 | int blob_linecount(Blob *p){ |
| 732 | int n = 0; |
| 733 | int i; |
| 734 | for(i=0; i<p->nUsed; i++){ |
| 735 | if( p->aData[i]=='\n' ) n++; |
| 736 | } |
| 737 | if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++; |
| 738 | return n; |
| 739 | } |
| 740 | |
| 741 | /* |
| 742 | ** Rewind the cursor on a blob back to the beginning. |
| 743 | */ |
| 744 | void blob_rewind(Blob *p){ |
| 745 |
+4
-3
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -640,12 +640,12 @@ | ||
| 640 | 640 | ** -M|--unmerged List branches not merged into the current branch |
| 641 | 641 | ** -p List only private branches |
| 642 | 642 | ** -r Reverse the sort order |
| 643 | 643 | ** -t Show recently changed branches first |
| 644 | 644 | ** --self List only branches where you participate |
| 645 | -** --username USER List only branches where USER participate | |
| 646 | -** --users N List up to N users partipiating | |
| 645 | +** --username USER List only branches where USER participates | |
| 646 | +** --users N List up to N users participating | |
| 647 | 647 | ** |
| 648 | 648 | ** The current branch is marked with an asterisk. Private branches are |
| 649 | 649 | ** marked with a hash sign. |
| 650 | 650 | ** |
| 651 | 651 | ** If GLOB is given, show only branches matching the pattern. |
| @@ -660,11 +660,12 @@ | ||
| 660 | 660 | ** Create a new branch BRANCH-NAME off of check-in BASIS. |
| 661 | 661 | ** |
| 662 | 662 | ** Options: |
| 663 | 663 | ** --private Branch is private (i.e., remains local) |
| 664 | 664 | ** --bgcolor COLOR Use COLOR instead of automatic background |
| 665 | -** --nosign Do not sign contents on this branch | |
| 665 | +** --nosign Do not sign the manifest for the check-in | |
| 666 | +** that creates this branch | |
| 666 | 667 | ** --nosync Do not auto-sync prior to creating the branch |
| 667 | 668 | ** --date-override DATE DATE to use instead of 'now' |
| 668 | 669 | ** --user-override USER USER to use instead of the current default |
| 669 | 670 | ** |
| 670 | 671 | ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 671 | 672 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -640,12 +640,12 @@ | |
| 640 | ** -M|--unmerged List branches not merged into the current branch |
| 641 | ** -p List only private branches |
| 642 | ** -r Reverse the sort order |
| 643 | ** -t Show recently changed branches first |
| 644 | ** --self List only branches where you participate |
| 645 | ** --username USER List only branches where USER participate |
| 646 | ** --users N List up to N users partipiating |
| 647 | ** |
| 648 | ** The current branch is marked with an asterisk. Private branches are |
| 649 | ** marked with a hash sign. |
| 650 | ** |
| 651 | ** If GLOB is given, show only branches matching the pattern. |
| @@ -660,11 +660,12 @@ | |
| 660 | ** Create a new branch BRANCH-NAME off of check-in BASIS. |
| 661 | ** |
| 662 | ** Options: |
| 663 | ** --private Branch is private (i.e., remains local) |
| 664 | ** --bgcolor COLOR Use COLOR instead of automatic background |
| 665 | ** --nosign Do not sign contents on this branch |
| 666 | ** --nosync Do not auto-sync prior to creating the branch |
| 667 | ** --date-override DATE DATE to use instead of 'now' |
| 668 | ** --user-override USER USER to use instead of the current default |
| 669 | ** |
| 670 | ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 671 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -640,12 +640,12 @@ | |
| 640 | ** -M|--unmerged List branches not merged into the current branch |
| 641 | ** -p List only private branches |
| 642 | ** -r Reverse the sort order |
| 643 | ** -t Show recently changed branches first |
| 644 | ** --self List only branches where you participate |
| 645 | ** --username USER List only branches where USER participates |
| 646 | ** --users N List up to N users participating |
| 647 | ** |
| 648 | ** The current branch is marked with an asterisk. Private branches are |
| 649 | ** marked with a hash sign. |
| 650 | ** |
| 651 | ** If GLOB is given, show only branches matching the pattern. |
| @@ -660,11 +660,12 @@ | |
| 660 | ** Create a new branch BRANCH-NAME off of check-in BASIS. |
| 661 | ** |
| 662 | ** Options: |
| 663 | ** --private Branch is private (i.e., remains local) |
| 664 | ** --bgcolor COLOR Use COLOR instead of automatic background |
| 665 | ** --nosign Do not sign the manifest for the check-in |
| 666 | ** that creates this branch |
| 667 | ** --nosync Do not auto-sync prior to creating the branch |
| 668 | ** --date-override DATE DATE to use instead of 'now' |
| 669 | ** --user-override USER USER to use instead of the current default |
| 670 | ** |
| 671 | ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 672 |
-17
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -1258,22 +1258,5 @@ | ||
| 1258 | 1258 | @ </table></div> |
| 1259 | 1259 | db_finalize(&q1); |
| 1260 | 1260 | db_finalize(&q2); |
| 1261 | 1261 | style_finish_page(); |
| 1262 | 1262 | } |
| 1263 | - | |
| 1264 | -/* | |
| 1265 | -** WEBPAGE: files | |
| 1266 | -** | |
| 1267 | -** Show files as a flat table. If the ci=LABEL query parameter is provided, | |
| 1268 | -** then show all the files in the specified check-in. Without the ci= query | |
| 1269 | -** parameter show all files across all check-ins. | |
| 1270 | -** | |
| 1271 | -** Query parameters: | |
| 1272 | -** | |
| 1273 | -** name=PATH Directory to display. Optional | |
| 1274 | -** ci=LABEL Show only files in this check-in. Optional. | |
| 1275 | -** re=REGEXP Show only files matching REGEXP. Optional. | |
| 1276 | -*/ | |
| 1277 | -void files_page(void){ | |
| 1278 | - return; | |
| 1279 | -} | |
| 1280 | 1263 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -1258,22 +1258,5 @@ | |
| 1258 | @ </table></div> |
| 1259 | db_finalize(&q1); |
| 1260 | db_finalize(&q2); |
| 1261 | style_finish_page(); |
| 1262 | } |
| 1263 | |
| 1264 | /* |
| 1265 | ** WEBPAGE: files |
| 1266 | ** |
| 1267 | ** Show files as a flat table. If the ci=LABEL query parameter is provided, |
| 1268 | ** then show all the files in the specified check-in. Without the ci= query |
| 1269 | ** parameter show all files across all check-ins. |
| 1270 | ** |
| 1271 | ** Query parameters: |
| 1272 | ** |
| 1273 | ** name=PATH Directory to display. Optional |
| 1274 | ** ci=LABEL Show only files in this check-in. Optional. |
| 1275 | ** re=REGEXP Show only files matching REGEXP. Optional. |
| 1276 | */ |
| 1277 | void files_page(void){ |
| 1278 | return; |
| 1279 | } |
| 1280 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -1258,22 +1258,5 @@ | |
| 1258 | @ </table></div> |
| 1259 | db_finalize(&q1); |
| 1260 | db_finalize(&q2); |
| 1261 | style_finish_page(); |
| 1262 | } |
| 1263 |
+3
-1
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -718,11 +718,13 @@ | ||
| 718 | 718 | ** the final one! */ |
| 719 | 719 | } fjs[] = { |
| 720 | 720 | /* This list ordering isn't strictly important. */ |
| 721 | 721 | {"confirmer", 0, 0}, |
| 722 | 722 | {"copybutton", 0, "dom\0"}, |
| 723 | - {"diff", 0, "dom\0fetch\0"}, | |
| 723 | + {"diff", 0, "dom\0fetch\0storage\0" | |
| 724 | + /* maintenance note: "diff" needs "storage" for storing the the | |
| 725 | + ** sbs-sync-scroll toggle. */}, | |
| 724 | 726 | {"dom", 0, 0}, |
| 725 | 727 | {"fetch", 0, 0}, |
| 726 | 728 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 727 | 729 | {"pikchr", 0, "dom\0"}, |
| 728 | 730 | {"popupwidget", 0, "dom\0"}, |
| 729 | 731 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -718,11 +718,13 @@ | |
| 718 | ** the final one! */ |
| 719 | } fjs[] = { |
| 720 | /* This list ordering isn't strictly important. */ |
| 721 | {"confirmer", 0, 0}, |
| 722 | {"copybutton", 0, "dom\0"}, |
| 723 | {"diff", 0, "dom\0fetch\0"}, |
| 724 | {"dom", 0, 0}, |
| 725 | {"fetch", 0, 0}, |
| 726 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 727 | {"pikchr", 0, "dom\0"}, |
| 728 | {"popupwidget", 0, "dom\0"}, |
| 729 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -718,11 +718,13 @@ | |
| 718 | ** the final one! */ |
| 719 | } fjs[] = { |
| 720 | /* This list ordering isn't strictly important. */ |
| 721 | {"confirmer", 0, 0}, |
| 722 | {"copybutton", 0, "dom\0"}, |
| 723 | {"diff", 0, "dom\0fetch\0storage\0" |
| 724 | /* maintenance note: "diff" needs "storage" for storing the the |
| 725 | ** sbs-sync-scroll toggle. */}, |
| 726 | {"dom", 0, 0}, |
| 727 | {"fetch", 0, 0}, |
| 728 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 729 | {"pikchr", 0, "dom\0"}, |
| 730 | {"popupwidget", 0, "dom\0"}, |
| 731 |
+9
-3
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -1137,12 +1137,11 @@ | ||
| 1137 | 1137 | ); |
| 1138 | 1138 | fossil_free(zBranch); |
| 1139 | 1139 | fossil_free(zUuid); |
| 1140 | 1140 | }else if( zType[0]=='w' ){ |
| 1141 | 1141 | /* Wiki page changes */ |
| 1142 | - char *zUuid; | |
| 1143 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 1142 | + char *zUuid = rid_to_uuid(rid); | |
| 1144 | 1143 | wiki_hyperlink_override(zUuid); |
| 1145 | 1144 | if( zMsg[0]=='-' ){ |
| 1146 | 1145 | zRes = mprintf("Delete wiki page <a href='%R/whistory?name=%t'>%h</a>", |
| 1147 | 1146 | zMsg+1, zMsg+1); |
| 1148 | 1147 | }else if( zMsg[0]=='+' ){ |
| @@ -1155,10 +1154,17 @@ | ||
| 1155 | 1154 | }else{ |
| 1156 | 1155 | zRes = mprintf("%W", zMsg); |
| 1157 | 1156 | } |
| 1158 | 1157 | wiki_hyperlink_override(0); |
| 1159 | 1158 | fossil_free(zUuid); |
| 1159 | + }else if( zType[0]=='f' ){ | |
| 1160 | + /* Forum changes */ | |
| 1161 | + char *zUuid = rid_to_uuid(rid); | |
| 1162 | + zRes = mprintf( "%W (artifact: <a href='%R/info/%S'>%S</a>, " | |
| 1163 | + "user: <a href='%R/timeline?u=%t&c=%S'>%h</a>)", | |
| 1164 | + zMsg, zUuid, zUuid, zUser, zUuid, zUser); | |
| 1165 | + fossil_free(zUuid); | |
| 1160 | 1166 | }else{ |
| 1161 | 1167 | /* Anything else */ |
| 1162 | 1168 | zRes = mprintf("%W", zMsg); |
| 1163 | 1169 | } |
| 1164 | 1170 | if( zRes ){ |
| @@ -1196,11 +1202,11 @@ | ||
| 1196 | 1202 | ** |
| 1197 | 1203 | ** --all Download all chat content. Normally only |
| 1198 | 1204 | ** previously undownloaded content is retrieved. |
| 1199 | 1205 | ** --debug Additional debugging output |
| 1200 | 1206 | ** --out DATABASE Store CHAT table in separate database file |
| 1201 | -** DATABASE rather that adding to local clone | |
| 1207 | +** DATABASE rather than adding to local clone | |
| 1202 | 1208 | ** --unsafe Allow the use of unencrypted http:// |
| 1203 | 1209 | ** |
| 1204 | 1210 | ** > fossil chat send [ARGUMENTS] |
| 1205 | 1211 | ** |
| 1206 | 1212 | ** This command sends a new message to the chatroom. The message |
| 1207 | 1213 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -1137,12 +1137,11 @@ | |
| 1137 | ); |
| 1138 | fossil_free(zBranch); |
| 1139 | fossil_free(zUuid); |
| 1140 | }else if( zType[0]=='w' ){ |
| 1141 | /* Wiki page changes */ |
| 1142 | char *zUuid; |
| 1143 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1144 | wiki_hyperlink_override(zUuid); |
| 1145 | if( zMsg[0]=='-' ){ |
| 1146 | zRes = mprintf("Delete wiki page <a href='%R/whistory?name=%t'>%h</a>", |
| 1147 | zMsg+1, zMsg+1); |
| 1148 | }else if( zMsg[0]=='+' ){ |
| @@ -1155,10 +1154,17 @@ | |
| 1155 | }else{ |
| 1156 | zRes = mprintf("%W", zMsg); |
| 1157 | } |
| 1158 | wiki_hyperlink_override(0); |
| 1159 | fossil_free(zUuid); |
| 1160 | }else{ |
| 1161 | /* Anything else */ |
| 1162 | zRes = mprintf("%W", zMsg); |
| 1163 | } |
| 1164 | if( zRes ){ |
| @@ -1196,11 +1202,11 @@ | |
| 1196 | ** |
| 1197 | ** --all Download all chat content. Normally only |
| 1198 | ** previously undownloaded content is retrieved. |
| 1199 | ** --debug Additional debugging output |
| 1200 | ** --out DATABASE Store CHAT table in separate database file |
| 1201 | ** DATABASE rather that adding to local clone |
| 1202 | ** --unsafe Allow the use of unencrypted http:// |
| 1203 | ** |
| 1204 | ** > fossil chat send [ARGUMENTS] |
| 1205 | ** |
| 1206 | ** This command sends a new message to the chatroom. The message |
| 1207 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -1137,12 +1137,11 @@ | |
| 1137 | ); |
| 1138 | fossil_free(zBranch); |
| 1139 | fossil_free(zUuid); |
| 1140 | }else if( zType[0]=='w' ){ |
| 1141 | /* Wiki page changes */ |
| 1142 | char *zUuid = rid_to_uuid(rid); |
| 1143 | wiki_hyperlink_override(zUuid); |
| 1144 | if( zMsg[0]=='-' ){ |
| 1145 | zRes = mprintf("Delete wiki page <a href='%R/whistory?name=%t'>%h</a>", |
| 1146 | zMsg+1, zMsg+1); |
| 1147 | }else if( zMsg[0]=='+' ){ |
| @@ -1155,10 +1154,17 @@ | |
| 1154 | }else{ |
| 1155 | zRes = mprintf("%W", zMsg); |
| 1156 | } |
| 1157 | wiki_hyperlink_override(0); |
| 1158 | fossil_free(zUuid); |
| 1159 | }else if( zType[0]=='f' ){ |
| 1160 | /* Forum changes */ |
| 1161 | char *zUuid = rid_to_uuid(rid); |
| 1162 | zRes = mprintf( "%W (artifact: <a href='%R/info/%S'>%S</a>, " |
| 1163 | "user: <a href='%R/timeline?u=%t&c=%S'>%h</a>)", |
| 1164 | zMsg, zUuid, zUuid, zUser, zUuid, zUser); |
| 1165 | fossil_free(zUuid); |
| 1166 | }else{ |
| 1167 | /* Anything else */ |
| 1168 | zRes = mprintf("%W", zMsg); |
| 1169 | } |
| 1170 | if( zRes ){ |
| @@ -1196,11 +1202,11 @@ | |
| 1202 | ** |
| 1203 | ** --all Download all chat content. Normally only |
| 1204 | ** previously undownloaded content is retrieved. |
| 1205 | ** --debug Additional debugging output |
| 1206 | ** --out DATABASE Store CHAT table in separate database file |
| 1207 | ** DATABASE rather than adding to local clone |
| 1208 | ** --unsafe Allow the use of unencrypted http:// |
| 1209 | ** |
| 1210 | ** > fossil chat send [ARGUMENTS] |
| 1211 | ** |
| 1212 | ** This command sends a new message to the chatroom. The message |
| 1213 |
+114
-14
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -430,10 +430,11 @@ | ||
| 430 | 430 | ** |
| 431 | 431 | ** The "fossil changes --extra" command is equivalent to "fossil extras". |
| 432 | 432 | ** |
| 433 | 433 | ** General options: |
| 434 | 434 | ** --abs-paths Display absolute pathnames |
| 435 | +** -b|--brief Show a single keyword for the status | |
| 435 | 436 | ** --rel-paths Display pathnames relative to the current working |
| 436 | 437 | ** directory |
| 437 | 438 | ** --hash Verify file status using hashing rather than |
| 438 | 439 | ** relying on file mtimes |
| 439 | 440 | ** --case-sensitive BOOL Override case-sensitive setting |
| @@ -493,10 +494,48 @@ | ||
| 493 | 494 | unsigned scanFlags = 0; |
| 494 | 495 | unsigned flags = 0; |
| 495 | 496 | int vid, i; |
| 496 | 497 | |
| 497 | 498 | fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown"); |
| 499 | + | |
| 500 | + if( find_option("brief","b",0) ){ | |
| 501 | + /* The --brief or -b option is special. It cannot be used with any | |
| 502 | + ** other options. It outputs a single keyword which indicates the | |
| 503 | + ** fossil status, for use by shell scripts. The output might be | |
| 504 | + ** one of: | |
| 505 | + ** | |
| 506 | + ** clean The current working directory is within an | |
| 507 | + ** unmodified fossil check-out. | |
| 508 | + ** | |
| 509 | + ** dirty The pwd is within a fossil check-out that has | |
| 510 | + ** uncommitted changes | |
| 511 | + ** | |
| 512 | + ** none The pwd is not within a fossil check-out. | |
| 513 | + */ | |
| 514 | + int chnged; | |
| 515 | + if( g.argc>2 ){ | |
| 516 | + fossil_fatal("No other arguments or options may occur with --brief"); | |
| 517 | + } | |
| 518 | + if( db_open_local(0)==0 ){ | |
| 519 | + fossil_print("none\n"); | |
| 520 | + return; | |
| 521 | + } | |
| 522 | + vid = db_lget_int("checkout", 0); | |
| 523 | + vfile_check_signature(vid, 0); | |
| 524 | + chnged = db_int(0, | |
| 525 | + "SELECT 1 FROM vfile" | |
| 526 | + " WHERE vid=%d" | |
| 527 | + " AND (chnged>0 OR deleted OR rid==0)", | |
| 528 | + vid | |
| 529 | + ); | |
| 530 | + if( chnged ){ | |
| 531 | + fossil_print("dirty\n"); | |
| 532 | + }else{ | |
| 533 | + fossil_print("clean\n"); | |
| 534 | + } | |
| 535 | + return; | |
| 536 | + } | |
| 498 | 537 | |
| 499 | 538 | /* Load affirmative flag options. */ |
| 500 | 539 | for( i=0; i<count(flagDefs); ++i ){ |
| 501 | 540 | if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY)) |
| 502 | 541 | && find_option(flagDefs[i].option, 0, 0) ){ |
| @@ -528,11 +567,11 @@ | ||
| 528 | 567 | } |
| 529 | 568 | |
| 530 | 569 | /* Confirm current working directory is within check-out. */ |
| 531 | 570 | db_must_be_within_tree(); |
| 532 | 571 | |
| 533 | - /* Get check-out version. l*/ | |
| 572 | + /* Get check-out version. */ | |
| 534 | 573 | vid = db_lget_int("checkout", 0); |
| 535 | 574 | |
| 536 | 575 | /* Relative path flag determination is done by a shared function. */ |
| 537 | 576 | if( determine_cwd_relative_option() ){ |
| 538 | 577 | flags |= C_RELPATH; |
| @@ -928,12 +967,12 @@ | ||
| 928 | 967 | /* |
| 929 | 968 | ** COMMAND: tree |
| 930 | 969 | ** |
| 931 | 970 | ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? |
| 932 | 971 | ** |
| 933 | -** List all files in the current check-out in after the fashion of the | |
| 934 | -** "tree" command. If PATHS is included, only the named files | |
| 972 | +** List all files in the current check-out much like the "tree" | |
| 973 | +** command does. If PATHS is included, only the named files | |
| 935 | 974 | ** (or their children if directories) are shown. |
| 936 | 975 | ** |
| 937 | 976 | ** Options: |
| 938 | 977 | ** -r VERSION The specific check-in to list |
| 939 | 978 | ** -R|--repository REPO Extract info from repository REPO |
| @@ -2279,11 +2318,11 @@ | ||
| 2279 | 2318 | ** |
| 2280 | 2319 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2281 | 2320 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2282 | 2321 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2283 | 2322 | ** be older than its ancestor unless the --allow-older option appears. |
| 2284 | -** If any of files in the check-in appear to contain unresolved merge | |
| 2323 | +** If any files in the check-in appear to contain unresolved merge | |
| 2285 | 2324 | ** conflicts, the check-in will not be allowed unless the |
| 2286 | 2325 | ** --allow-conflict option is present. In addition, the entire |
| 2287 | 2326 | ** check-in process may be aborted if a file contains content that |
| 2288 | 2327 | ** appears to be binary, Unicode text, or text with CR/LF line endings |
| 2289 | 2328 | ** unless the interactive user chooses to proceed. If there is no |
| @@ -2319,11 +2358,11 @@ | ||
| 2319 | 2358 | ** than relying on file mtimes |
| 2320 | 2359 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2321 | 2360 | ** behave as if the user had entered 'yes' to |
| 2322 | 2361 | ** the question of whether to proceed despite |
| 2323 | 2362 | ** the skew. |
| 2324 | -** --ignore-oversize Do not warning the user about oversized files | |
| 2363 | +** --ignore-oversize Do not warn the user about oversized files | |
| 2325 | 2364 | ** --integrate Close all merged-in branches |
| 2326 | 2365 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment |
| 2327 | 2366 | ** -M|--message-file FILE Read the commit comment from given file |
| 2328 | 2367 | ** --mimetype MIMETYPE Mimetype of check-in comment |
| 2329 | 2368 | ** -n|--dry-run If given, display instead of run actions |
| @@ -2395,10 +2434,12 @@ | ||
| 2395 | 2434 | Blob ans; /* Answer to continuation prompts */ |
| 2396 | 2435 | char cReply; /* First character of ans */ |
| 2397 | 2436 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2398 | 2437 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2399 | 2438 | int mxSize; |
| 2439 | + char *zCurBranch = 0; /* The current branch name of checkout */ | |
| 2440 | + char *zNewBranch = 0; /* The branch name after update */ | |
| 2400 | 2441 | |
| 2401 | 2442 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2402 | 2443 | url_proxy_options(); |
| 2403 | 2444 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2404 | 2445 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2469,10 +2510,55 @@ | ||
| 2469 | 2510 | } |
| 2470 | 2511 | }else{ |
| 2471 | 2512 | privateParent = content_is_private(vid); |
| 2472 | 2513 | } |
| 2473 | 2514 | |
| 2515 | + user_select(); | |
| 2516 | + /* | |
| 2517 | + ** Check that the user exists. | |
| 2518 | + */ | |
| 2519 | + if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ | |
| 2520 | + fossil_fatal("no such user: %s", g.zLogin); | |
| 2521 | + } | |
| 2522 | + | |
| 2523 | + /* | |
| 2524 | + ** Detect if the branch name has changed from the parent check-in | |
| 2525 | + ** and prompt if necessary | |
| 2526 | + **/ | |
| 2527 | + zCurBranch = db_text(0, | |
| 2528 | + " SELECT value FROM tagxref AS tx" | |
| 2529 | + " WHERE rid=(SELECT pid" | |
| 2530 | + " FROM tagxref LEFT JOIN event ON srcid=objid" | |
| 2531 | + " LEFT JOIN plink ON rid=cid" | |
| 2532 | + " WHERE rid=%d AND tagxref.tagid=%d" | |
| 2533 | + " AND srcid!=origid" | |
| 2534 | + " AND tagtype=2 AND coalesce(euser,user)!=%Q)" | |
| 2535 | + " AND tx.tagid=%d", | |
| 2536 | + vid, TAG_BRANCH, g.zLogin, TAG_BRANCH | |
| 2537 | + ); | |
| 2538 | + if( zCurBranch!=0 && zCurBranch[0]!=0 | |
| 2539 | + && forceFlag==0 | |
| 2540 | + && noPrompt==0 | |
| 2541 | + ){ | |
| 2542 | + zNewBranch = branch_of_rid(vid); | |
| 2543 | + fossil_warning( | |
| 2544 | + "WARNING: The parent check-in [%.10s] has been moved from branch\n" | |
| 2545 | + " '%s' over to branch '%s'.", | |
| 2546 | + rid_to_uuid(vid), zCurBranch, zNewBranch | |
| 2547 | + ); | |
| 2548 | + prompt_user("Commit anyway? (y/N) ", &ans); | |
| 2549 | + cReply = blob_str(&ans)[0]; | |
| 2550 | + blob_reset(&ans); | |
| 2551 | + if( cReply!='y' && cReply!='Y' ){ | |
| 2552 | + fossil_fatal("Abandoning commit because branch has changed"); | |
| 2553 | + } | |
| 2554 | + fossil_free(zNewBranch); | |
| 2555 | + fossil_free(zCurBranch); | |
| 2556 | + zCurBranch = branch_of_rid(vid); | |
| 2557 | + } | |
| 2558 | + if( zCurBranch==0 ) zCurBranch = branch_of_rid(vid); | |
| 2559 | + | |
| 2474 | 2560 | /* Track the "private" status */ |
| 2475 | 2561 | g.markPrivate = privateFlag || privateParent; |
| 2476 | 2562 | if( privateFlag && !privateParent ){ |
| 2477 | 2563 | /* Apply default branch name ("private") and color ("orange") if not |
| 2478 | 2564 | ** specified otherwise on the command-line, and if the parent is not |
| @@ -2521,11 +2607,11 @@ | ||
| 2521 | 2607 | ** |
| 2522 | 2608 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2523 | 2609 | ** the autosync above, then also try to avoid deltas, unless the |
| 2524 | 2610 | ** --delta option is specified. The remote repo will send the |
| 2525 | 2611 | ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" |
| 2526 | - ** setting is enabled. | |
| 2612 | + ** setting enabled. | |
| 2527 | 2613 | */ |
| 2528 | 2614 | if( !db_get_boolean("seen-delta-manifest",0) |
| 2529 | 2615 | || db_get_boolean("forbid-delta-manifests",0) |
| 2530 | 2616 | || g.bAvoidDeltaManifests |
| 2531 | 2617 | ){ |
| @@ -2600,18 +2686,10 @@ | ||
| 2600 | 2686 | "'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo); |
| 2601 | 2687 | } |
| 2602 | 2688 | db_finalize(&q); |
| 2603 | 2689 | } |
| 2604 | 2690 | |
| 2605 | - user_select(); | |
| 2606 | - /* | |
| 2607 | - ** Check that the user exists. | |
| 2608 | - */ | |
| 2609 | - if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ | |
| 2610 | - fossil_fatal("no such user: %s", g.zLogin); | |
| 2611 | - } | |
| 2612 | - | |
| 2613 | 2691 | hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0); |
| 2614 | 2692 | db_begin_transaction(); |
| 2615 | 2693 | db_record_repository_filename(0); |
| 2616 | 2694 | if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ |
| 2617 | 2695 | fossil_fatal("nothing has changed; use --allow-empty to override"); |
| @@ -2674,10 +2752,32 @@ | ||
| 2674 | 2752 | " WHERE tagid=%d AND rid=%d AND tagtype>0" |
| 2675 | 2753 | " AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch)) |
| 2676 | 2754 | ){ |
| 2677 | 2755 | fossil_fatal("cannot commit against a closed leaf"); |
| 2678 | 2756 | } |
| 2757 | + | |
| 2758 | + /* Require confirmation to continue with the check-in if the branch | |
| 2759 | + ** has changed and the committer did not provide the same branch | |
| 2760 | + */ | |
| 2761 | + zNewBranch = branch_of_rid(vid); | |
| 2762 | + if( fossil_strcmp(zCurBranch, zNewBranch)!=0 | |
| 2763 | + && fossil_strcmp(sCiInfo.zBranch, zNewBranch)!=0 | |
| 2764 | + && forceFlag==0 | |
| 2765 | + && noPrompt==0 | |
| 2766 | + ){ | |
| 2767 | + fossil_warning("parent check-in [%.10s] branch changed from '%s' to '%s'", | |
| 2768 | + rid_to_uuid(vid), zCurBranch, zNewBranch); | |
| 2769 | + prompt_user("continue (y/N)? ", &ans); | |
| 2770 | + cReply = blob_str(&ans)[0]; | |
| 2771 | + blob_reset(&ans); | |
| 2772 | + if( cReply!='y' && cReply!='Y' ){ | |
| 2773 | + fossil_fatal("Abandoning commit because branch has changed"); | |
| 2774 | + } | |
| 2775 | + fossil_free(zCurBranch); | |
| 2776 | + zCurBranch = branch_of_rid(vid); | |
| 2777 | + } | |
| 2778 | + fossil_free(zNewBranch); | |
| 2679 | 2779 | |
| 2680 | 2780 | /* Always exit the loop on the second pass */ |
| 2681 | 2781 | if( bRecheck ) break; |
| 2682 | 2782 | |
| 2683 | 2783 | |
| 2684 | 2784 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -430,10 +430,11 @@ | |
| 430 | ** |
| 431 | ** The "fossil changes --extra" command is equivalent to "fossil extras". |
| 432 | ** |
| 433 | ** General options: |
| 434 | ** --abs-paths Display absolute pathnames |
| 435 | ** --rel-paths Display pathnames relative to the current working |
| 436 | ** directory |
| 437 | ** --hash Verify file status using hashing rather than |
| 438 | ** relying on file mtimes |
| 439 | ** --case-sensitive BOOL Override case-sensitive setting |
| @@ -493,10 +494,48 @@ | |
| 493 | unsigned scanFlags = 0; |
| 494 | unsigned flags = 0; |
| 495 | int vid, i; |
| 496 | |
| 497 | fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown"); |
| 498 | |
| 499 | /* Load affirmative flag options. */ |
| 500 | for( i=0; i<count(flagDefs); ++i ){ |
| 501 | if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY)) |
| 502 | && find_option(flagDefs[i].option, 0, 0) ){ |
| @@ -528,11 +567,11 @@ | |
| 528 | } |
| 529 | |
| 530 | /* Confirm current working directory is within check-out. */ |
| 531 | db_must_be_within_tree(); |
| 532 | |
| 533 | /* Get check-out version. l*/ |
| 534 | vid = db_lget_int("checkout", 0); |
| 535 | |
| 536 | /* Relative path flag determination is done by a shared function. */ |
| 537 | if( determine_cwd_relative_option() ){ |
| 538 | flags |= C_RELPATH; |
| @@ -928,12 +967,12 @@ | |
| 928 | /* |
| 929 | ** COMMAND: tree |
| 930 | ** |
| 931 | ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? |
| 932 | ** |
| 933 | ** List all files in the current check-out in after the fashion of the |
| 934 | ** "tree" command. If PATHS is included, only the named files |
| 935 | ** (or their children if directories) are shown. |
| 936 | ** |
| 937 | ** Options: |
| 938 | ** -r VERSION The specific check-in to list |
| 939 | ** -R|--repository REPO Extract info from repository REPO |
| @@ -2279,11 +2318,11 @@ | |
| 2279 | ** |
| 2280 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2281 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2282 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2283 | ** be older than its ancestor unless the --allow-older option appears. |
| 2284 | ** If any of files in the check-in appear to contain unresolved merge |
| 2285 | ** conflicts, the check-in will not be allowed unless the |
| 2286 | ** --allow-conflict option is present. In addition, the entire |
| 2287 | ** check-in process may be aborted if a file contains content that |
| 2288 | ** appears to be binary, Unicode text, or text with CR/LF line endings |
| 2289 | ** unless the interactive user chooses to proceed. If there is no |
| @@ -2319,11 +2358,11 @@ | |
| 2319 | ** than relying on file mtimes |
| 2320 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2321 | ** behave as if the user had entered 'yes' to |
| 2322 | ** the question of whether to proceed despite |
| 2323 | ** the skew. |
| 2324 | ** --ignore-oversize Do not warning the user about oversized files |
| 2325 | ** --integrate Close all merged-in branches |
| 2326 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment |
| 2327 | ** -M|--message-file FILE Read the commit comment from given file |
| 2328 | ** --mimetype MIMETYPE Mimetype of check-in comment |
| 2329 | ** -n|--dry-run If given, display instead of run actions |
| @@ -2395,10 +2434,12 @@ | |
| 2395 | Blob ans; /* Answer to continuation prompts */ |
| 2396 | char cReply; /* First character of ans */ |
| 2397 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2398 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2399 | int mxSize; |
| 2400 | |
| 2401 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2402 | url_proxy_options(); |
| 2403 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2404 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2469,10 +2510,55 @@ | |
| 2469 | } |
| 2470 | }else{ |
| 2471 | privateParent = content_is_private(vid); |
| 2472 | } |
| 2473 | |
| 2474 | /* Track the "private" status */ |
| 2475 | g.markPrivate = privateFlag || privateParent; |
| 2476 | if( privateFlag && !privateParent ){ |
| 2477 | /* Apply default branch name ("private") and color ("orange") if not |
| 2478 | ** specified otherwise on the command-line, and if the parent is not |
| @@ -2521,11 +2607,11 @@ | |
| 2521 | ** |
| 2522 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2523 | ** the autosync above, then also try to avoid deltas, unless the |
| 2524 | ** --delta option is specified. The remote repo will send the |
| 2525 | ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" |
| 2526 | ** setting is enabled. |
| 2527 | */ |
| 2528 | if( !db_get_boolean("seen-delta-manifest",0) |
| 2529 | || db_get_boolean("forbid-delta-manifests",0) |
| 2530 | || g.bAvoidDeltaManifests |
| 2531 | ){ |
| @@ -2600,18 +2686,10 @@ | |
| 2600 | "'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo); |
| 2601 | } |
| 2602 | db_finalize(&q); |
| 2603 | } |
| 2604 | |
| 2605 | user_select(); |
| 2606 | /* |
| 2607 | ** Check that the user exists. |
| 2608 | */ |
| 2609 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 2610 | fossil_fatal("no such user: %s", g.zLogin); |
| 2611 | } |
| 2612 | |
| 2613 | hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0); |
| 2614 | db_begin_transaction(); |
| 2615 | db_record_repository_filename(0); |
| 2616 | if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ |
| 2617 | fossil_fatal("nothing has changed; use --allow-empty to override"); |
| @@ -2674,10 +2752,32 @@ | |
| 2674 | " WHERE tagid=%d AND rid=%d AND tagtype>0" |
| 2675 | " AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch)) |
| 2676 | ){ |
| 2677 | fossil_fatal("cannot commit against a closed leaf"); |
| 2678 | } |
| 2679 | |
| 2680 | /* Always exit the loop on the second pass */ |
| 2681 | if( bRecheck ) break; |
| 2682 | |
| 2683 | |
| 2684 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -430,10 +430,11 @@ | |
| 430 | ** |
| 431 | ** The "fossil changes --extra" command is equivalent to "fossil extras". |
| 432 | ** |
| 433 | ** General options: |
| 434 | ** --abs-paths Display absolute pathnames |
| 435 | ** -b|--brief Show a single keyword for the status |
| 436 | ** --rel-paths Display pathnames relative to the current working |
| 437 | ** directory |
| 438 | ** --hash Verify file status using hashing rather than |
| 439 | ** relying on file mtimes |
| 440 | ** --case-sensitive BOOL Override case-sensitive setting |
| @@ -493,10 +494,48 @@ | |
| 494 | unsigned scanFlags = 0; |
| 495 | unsigned flags = 0; |
| 496 | int vid, i; |
| 497 | |
| 498 | fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown"); |
| 499 | |
| 500 | if( find_option("brief","b",0) ){ |
| 501 | /* The --brief or -b option is special. It cannot be used with any |
| 502 | ** other options. It outputs a single keyword which indicates the |
| 503 | ** fossil status, for use by shell scripts. The output might be |
| 504 | ** one of: |
| 505 | ** |
| 506 | ** clean The current working directory is within an |
| 507 | ** unmodified fossil check-out. |
| 508 | ** |
| 509 | ** dirty The pwd is within a fossil check-out that has |
| 510 | ** uncommitted changes |
| 511 | ** |
| 512 | ** none The pwd is not within a fossil check-out. |
| 513 | */ |
| 514 | int chnged; |
| 515 | if( g.argc>2 ){ |
| 516 | fossil_fatal("No other arguments or options may occur with --brief"); |
| 517 | } |
| 518 | if( db_open_local(0)==0 ){ |
| 519 | fossil_print("none\n"); |
| 520 | return; |
| 521 | } |
| 522 | vid = db_lget_int("checkout", 0); |
| 523 | vfile_check_signature(vid, 0); |
| 524 | chnged = db_int(0, |
| 525 | "SELECT 1 FROM vfile" |
| 526 | " WHERE vid=%d" |
| 527 | " AND (chnged>0 OR deleted OR rid==0)", |
| 528 | vid |
| 529 | ); |
| 530 | if( chnged ){ |
| 531 | fossil_print("dirty\n"); |
| 532 | }else{ |
| 533 | fossil_print("clean\n"); |
| 534 | } |
| 535 | return; |
| 536 | } |
| 537 | |
| 538 | /* Load affirmative flag options. */ |
| 539 | for( i=0; i<count(flagDefs); ++i ){ |
| 540 | if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY)) |
| 541 | && find_option(flagDefs[i].option, 0, 0) ){ |
| @@ -528,11 +567,11 @@ | |
| 567 | } |
| 568 | |
| 569 | /* Confirm current working directory is within check-out. */ |
| 570 | db_must_be_within_tree(); |
| 571 | |
| 572 | /* Get check-out version. */ |
| 573 | vid = db_lget_int("checkout", 0); |
| 574 | |
| 575 | /* Relative path flag determination is done by a shared function. */ |
| 576 | if( determine_cwd_relative_option() ){ |
| 577 | flags |= C_RELPATH; |
| @@ -928,12 +967,12 @@ | |
| 967 | /* |
| 968 | ** COMMAND: tree |
| 969 | ** |
| 970 | ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? |
| 971 | ** |
| 972 | ** List all files in the current check-out much like the "tree" |
| 973 | ** command does. If PATHS is included, only the named files |
| 974 | ** (or their children if directories) are shown. |
| 975 | ** |
| 976 | ** Options: |
| 977 | ** -r VERSION The specific check-in to list |
| 978 | ** -R|--repository REPO Extract info from repository REPO |
| @@ -2279,11 +2318,11 @@ | |
| 2318 | ** |
| 2319 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2320 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2321 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2322 | ** be older than its ancestor unless the --allow-older option appears. |
| 2323 | ** If any files in the check-in appear to contain unresolved merge |
| 2324 | ** conflicts, the check-in will not be allowed unless the |
| 2325 | ** --allow-conflict option is present. In addition, the entire |
| 2326 | ** check-in process may be aborted if a file contains content that |
| 2327 | ** appears to be binary, Unicode text, or text with CR/LF line endings |
| 2328 | ** unless the interactive user chooses to proceed. If there is no |
| @@ -2319,11 +2358,11 @@ | |
| 2358 | ** than relying on file mtimes |
| 2359 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2360 | ** behave as if the user had entered 'yes' to |
| 2361 | ** the question of whether to proceed despite |
| 2362 | ** the skew. |
| 2363 | ** --ignore-oversize Do not warn the user about oversized files |
| 2364 | ** --integrate Close all merged-in branches |
| 2365 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment |
| 2366 | ** -M|--message-file FILE Read the commit comment from given file |
| 2367 | ** --mimetype MIMETYPE Mimetype of check-in comment |
| 2368 | ** -n|--dry-run If given, display instead of run actions |
| @@ -2395,10 +2434,12 @@ | |
| 2434 | Blob ans; /* Answer to continuation prompts */ |
| 2435 | char cReply; /* First character of ans */ |
| 2436 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2437 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2438 | int mxSize; |
| 2439 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2440 | char *zNewBranch = 0; /* The branch name after update */ |
| 2441 | |
| 2442 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2443 | url_proxy_options(); |
| 2444 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2445 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2469,10 +2510,55 @@ | |
| 2510 | } |
| 2511 | }else{ |
| 2512 | privateParent = content_is_private(vid); |
| 2513 | } |
| 2514 | |
| 2515 | user_select(); |
| 2516 | /* |
| 2517 | ** Check that the user exists. |
| 2518 | */ |
| 2519 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 2520 | fossil_fatal("no such user: %s", g.zLogin); |
| 2521 | } |
| 2522 | |
| 2523 | /* |
| 2524 | ** Detect if the branch name has changed from the parent check-in |
| 2525 | ** and prompt if necessary |
| 2526 | **/ |
| 2527 | zCurBranch = db_text(0, |
| 2528 | " SELECT value FROM tagxref AS tx" |
| 2529 | " WHERE rid=(SELECT pid" |
| 2530 | " FROM tagxref LEFT JOIN event ON srcid=objid" |
| 2531 | " LEFT JOIN plink ON rid=cid" |
| 2532 | " WHERE rid=%d AND tagxref.tagid=%d" |
| 2533 | " AND srcid!=origid" |
| 2534 | " AND tagtype=2 AND coalesce(euser,user)!=%Q)" |
| 2535 | " AND tx.tagid=%d", |
| 2536 | vid, TAG_BRANCH, g.zLogin, TAG_BRANCH |
| 2537 | ); |
| 2538 | if( zCurBranch!=0 && zCurBranch[0]!=0 |
| 2539 | && forceFlag==0 |
| 2540 | && noPrompt==0 |
| 2541 | ){ |
| 2542 | zNewBranch = branch_of_rid(vid); |
| 2543 | fossil_warning( |
| 2544 | "WARNING: The parent check-in [%.10s] has been moved from branch\n" |
| 2545 | " '%s' over to branch '%s'.", |
| 2546 | rid_to_uuid(vid), zCurBranch, zNewBranch |
| 2547 | ); |
| 2548 | prompt_user("Commit anyway? (y/N) ", &ans); |
| 2549 | cReply = blob_str(&ans)[0]; |
| 2550 | blob_reset(&ans); |
| 2551 | if( cReply!='y' && cReply!='Y' ){ |
| 2552 | fossil_fatal("Abandoning commit because branch has changed"); |
| 2553 | } |
| 2554 | fossil_free(zNewBranch); |
| 2555 | fossil_free(zCurBranch); |
| 2556 | zCurBranch = branch_of_rid(vid); |
| 2557 | } |
| 2558 | if( zCurBranch==0 ) zCurBranch = branch_of_rid(vid); |
| 2559 | |
| 2560 | /* Track the "private" status */ |
| 2561 | g.markPrivate = privateFlag || privateParent; |
| 2562 | if( privateFlag && !privateParent ){ |
| 2563 | /* Apply default branch name ("private") and color ("orange") if not |
| 2564 | ** specified otherwise on the command-line, and if the parent is not |
| @@ -2521,11 +2607,11 @@ | |
| 2607 | ** |
| 2608 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2609 | ** the autosync above, then also try to avoid deltas, unless the |
| 2610 | ** --delta option is specified. The remote repo will send the |
| 2611 | ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" |
| 2612 | ** setting enabled. |
| 2613 | */ |
| 2614 | if( !db_get_boolean("seen-delta-manifest",0) |
| 2615 | || db_get_boolean("forbid-delta-manifests",0) |
| 2616 | || g.bAvoidDeltaManifests |
| 2617 | ){ |
| @@ -2600,18 +2686,10 @@ | |
| 2686 | "'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo); |
| 2687 | } |
| 2688 | db_finalize(&q); |
| 2689 | } |
| 2690 | |
| 2691 | hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0); |
| 2692 | db_begin_transaction(); |
| 2693 | db_record_repository_filename(0); |
| 2694 | if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ |
| 2695 | fossil_fatal("nothing has changed; use --allow-empty to override"); |
| @@ -2674,10 +2752,32 @@ | |
| 2752 | " WHERE tagid=%d AND rid=%d AND tagtype>0" |
| 2753 | " AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch)) |
| 2754 | ){ |
| 2755 | fossil_fatal("cannot commit against a closed leaf"); |
| 2756 | } |
| 2757 | |
| 2758 | /* Require confirmation to continue with the check-in if the branch |
| 2759 | ** has changed and the committer did not provide the same branch |
| 2760 | */ |
| 2761 | zNewBranch = branch_of_rid(vid); |
| 2762 | if( fossil_strcmp(zCurBranch, zNewBranch)!=0 |
| 2763 | && fossil_strcmp(sCiInfo.zBranch, zNewBranch)!=0 |
| 2764 | && forceFlag==0 |
| 2765 | && noPrompt==0 |
| 2766 | ){ |
| 2767 | fossil_warning("parent check-in [%.10s] branch changed from '%s' to '%s'", |
| 2768 | rid_to_uuid(vid), zCurBranch, zNewBranch); |
| 2769 | prompt_user("continue (y/N)? ", &ans); |
| 2770 | cReply = blob_str(&ans)[0]; |
| 2771 | blob_reset(&ans); |
| 2772 | if( cReply!='y' && cReply!='Y' ){ |
| 2773 | fossil_fatal("Abandoning commit because branch has changed"); |
| 2774 | } |
| 2775 | fossil_free(zCurBranch); |
| 2776 | zCurBranch = branch_of_rid(vid); |
| 2777 | } |
| 2778 | fossil_free(zNewBranch); |
| 2779 | |
| 2780 | /* Always exit the loop on the second pass */ |
| 2781 | if( bRecheck ) break; |
| 2782 | |
| 2783 | |
| 2784 |
+221
-71
| --- src/comformat.c | ||
| +++ src/comformat.c | ||
| @@ -31,10 +31,117 @@ | ||
| 31 | 31 | #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ |
| 32 | 32 | #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ |
| 33 | 33 | #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ |
| 34 | 34 | #endif |
| 35 | 35 | |
| 36 | +/********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/ | |
| 37 | +/* Lookup table to estimate the number of columns consumed by a Unicode | |
| 38 | +** character. | |
| 39 | +*/ | |
| 40 | +static const struct { | |
| 41 | + unsigned char w; /* Width of the character in columns */ | |
| 42 | + int iFirst; /* First character in a span having this width */ | |
| 43 | +} aUWidth[] = { | |
| 44 | + /* {1, 0x00000}, */ | |
| 45 | + {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, | |
| 46 | + {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, | |
| 47 | + {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, | |
| 48 | + {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, | |
| 49 | + {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, | |
| 50 | + {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, | |
| 51 | + {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, | |
| 52 | + {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, | |
| 53 | + {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, | |
| 54 | + {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, | |
| 55 | + {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, | |
| 56 | + {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, | |
| 57 | + {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, | |
| 58 | + {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, | |
| 59 | + {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, | |
| 60 | + {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, | |
| 61 | + {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, | |
| 62 | + {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, | |
| 63 | + {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, | |
| 64 | + {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, | |
| 65 | + {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, | |
| 66 | + {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, | |
| 67 | + {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, | |
| 68 | + {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, | |
| 69 | + {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, | |
| 70 | + {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, | |
| 71 | + {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, | |
| 72 | + {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, | |
| 73 | + {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, | |
| 74 | + {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, | |
| 75 | + {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, | |
| 76 | + {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, | |
| 77 | + {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, | |
| 78 | + {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, | |
| 79 | + {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, | |
| 80 | + {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, | |
| 81 | + {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, | |
| 82 | + {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, | |
| 83 | + {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, | |
| 84 | + {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, | |
| 85 | + {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, | |
| 86 | + {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, | |
| 87 | + {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, | |
| 88 | + {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, | |
| 89 | + {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, | |
| 90 | + {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, | |
| 91 | + {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, | |
| 92 | + {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, | |
| 93 | + {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, | |
| 94 | + {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, | |
| 95 | + {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, | |
| 96 | + {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, | |
| 97 | + {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, | |
| 98 | + {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, | |
| 99 | + {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, | |
| 100 | + {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, | |
| 101 | + {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, | |
| 102 | + {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, | |
| 103 | + {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, | |
| 104 | + {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, | |
| 105 | + {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} | |
| 106 | +}; | |
| 107 | + | |
| 108 | +/* | |
| 109 | +** Return an estimate of the width, in columns, for the single Unicode | |
| 110 | +** character c. For normal characters, the answer is always 1. But the | |
| 111 | +** estimate might be 0 or 2 for zero-width and double-width characters. | |
| 112 | +** | |
| 113 | +** Different display devices display unicode using different widths. So | |
| 114 | +** it is impossible to know that true display width with 100% accuracy. | |
| 115 | +** Inaccuracies in the width estimates might cause columns to be misaligned. | |
| 116 | +** Unfortunately, there is nothing we can do about that. | |
| 117 | +*/ | |
| 118 | +static int cli_wcwidth(int c){ | |
| 119 | + int iFirst, iLast; | |
| 120 | + | |
| 121 | + /* Fast path for common characters */ | |
| 122 | + if( c<=0x300 ) return 1; | |
| 123 | + | |
| 124 | + /* The general case */ | |
| 125 | + iFirst = 0; | |
| 126 | + iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; | |
| 127 | + while( iFirst<iLast-1 ){ | |
| 128 | + int iMid = (iFirst+iLast)/2; | |
| 129 | + int cMid = aUWidth[iMid].iFirst; | |
| 130 | + if( cMid < c ){ | |
| 131 | + iFirst = iMid; | |
| 132 | + }else if( cMid > c ){ | |
| 133 | + iLast = iMid - 1; | |
| 134 | + }else{ | |
| 135 | + return aUWidth[iMid].w; | |
| 136 | + } | |
| 137 | + } | |
| 138 | + if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; | |
| 139 | + return aUWidth[iLast].w; | |
| 140 | +} | |
| 141 | +/******* End of code copied from SQLite *************************************/ | |
| 142 | + | |
| 36 | 143 | /* |
| 37 | 144 | ** This is the previous value used by most external callers when they |
| 38 | 145 | ** needed to specify a default maximum line length to be used with the |
| 39 | 146 | ** comment_print() function. |
| 40 | 147 | */ |
| @@ -108,62 +215,99 @@ | ||
| 108 | 215 | ** algorithm, the NUL character is treated the same as a spacing character. |
| 109 | 216 | */ |
| 110 | 217 | static int comment_next_space( |
| 111 | 218 | const char *zLine, /* [in] The comment line being printed. */ |
| 112 | 219 | int index, /* [in] The current character index being handled. */ |
| 113 | - int *distUTF8 /* [out] Distance to next space in UTF-8 sequences. */ | |
| 220 | + int maxChars, /* [in] Optimization hint to abort before space found. */ | |
| 221 | + int *sumWidth /* [out] Summated width of all characters to next space. */ | |
| 114 | 222 | ){ |
| 115 | - int nextIndex = index + 1; | |
| 116 | - int fNonASCII=0; | |
| 223 | + int cchUTF8, utf32, wcwidth = 0; | |
| 224 | + int nextIndex = index; | |
| 117 | 225 | for(;;){ |
| 118 | - char c = zLine[nextIndex]; | |
| 119 | - if( (c&0x80)==0x80 ) fNonASCII=1; | |
| 120 | - if( c==0 || fossil_isspace(c) ){ | |
| 121 | - if( distUTF8 ){ | |
| 122 | - if( fNonASCII!=0 ){ | |
| 123 | - *distUTF8 = strlen_utf8(&zLine[index], nextIndex-index); | |
| 124 | - }else{ | |
| 125 | - *distUTF8 = nextIndex-index; | |
| 126 | - } | |
| 127 | - } | |
| 226 | + char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32); | |
| 227 | + nextIndex += cchUTF8; | |
| 228 | + wcwidth += cli_wcwidth(utf32); | |
| 229 | + if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) || | |
| 230 | + wcwidth>maxChars ){ | |
| 231 | + *sumWidth = wcwidth; | |
| 128 | 232 | return nextIndex; |
| 129 | 233 | } |
| 130 | - nextIndex++; | |
| 131 | 234 | } |
| 132 | 235 | return 0; /* NOT REACHED */ |
| 133 | 236 | } |
| 134 | 237 | |
| 135 | 238 | /* |
| 136 | -** Count the number of UTF-8 sequences in a string. Incomplete, ill-formed and | |
| 137 | -** overlong sequences are counted as one sequence. The invalid lead bytes 0xC0 | |
| 138 | -** to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte | |
| 139 | -** sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF are | |
| 140 | -** treated as invalid 1-byte sequences (as lone trail bytes). | |
| 141 | -** Combining characters and East Asian Wide and Fullwidth characters are counted | |
| 142 | -** as one, so this function does not calculate the effective "display width". | |
| 239 | +** Return information about the next (single- or multi-byte) character in the | |
| 240 | +** specified UTF-8 string: The number of UTF-8 code units (in this case: bytes) | |
| 241 | +** and the decoded UTF-32 code point. Incomplete, ill-formed and overlong | |
| 242 | +** sequences are consumed together as one invalid code point. The invalid lead | |
| 243 | +** bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- | |
| 244 | +** and 4-byte sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF | |
| 245 | +** are treated as invalid 1-byte sequences (as lone trail bytes), all resulting | |
| 246 | +** in one invalid code point. Invalid UTF-8 sequences encoding a non-scalar code | |
| 247 | +** point (UTF-16 surrogates U+D800 to U+DFFF) are allowed. | |
| 143 | 248 | */ |
| 144 | -int strlen_utf8(const char *zString, int lengthBytes){ | |
| 145 | - int i; /* Counted bytes. */ | |
| 146 | - int lengthUTF8; /* Counted UTF-8 sequences. */ | |
| 147 | -#if 0 | |
| 148 | - assert( lengthBytes>=0 ); | |
| 249 | +void char_info_utf8( | |
| 250 | + const char *z, | |
| 251 | + int *pCchUTF8, | |
| 252 | + int *pUtf32 | |
| 253 | +){ | |
| 254 | + int i = 0; /* Counted bytes. */ | |
| 255 | + int cchUTF8 = 1; /* Code units consumed. */ | |
| 256 | + int maxUTF8 = 1; /* Expected sequence length. */ | |
| 257 | + char c = z[i++]; | |
| 258 | + if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */ | |
| 259 | + *pCchUTF8 = 1; | |
| 260 | + *pUtf32 = (int)z[0]; | |
| 261 | + return; | |
| 262 | + } | |
| 263 | + else if( (c&0xe0)==0xc0 ) maxUTF8 = 2; /* UTF-8 lead byte 110vvvvv */ | |
| 264 | + else if( (c&0xf0)==0xe0 ) maxUTF8 = 3; /* UTF-8 lead byte 1110vvvv */ | |
| 265 | + else if( (c&0xf8)==0xf0 ) maxUTF8 = 4; /* UTF-8 lead byte 11110vvv */ | |
| 266 | + while( cchUTF8<maxUTF8 && | |
| 267 | + (z[i]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ | |
| 268 | + cchUTF8++; | |
| 269 | + i++; | |
| 270 | + } | |
| 271 | + *pCchUTF8 = cchUTF8; | |
| 272 | + if( cchUTF8!=maxUTF8 || /* Incomplete UTF-8 sequence. */ | |
| 273 | + ( cchUTF8==1 && (c&0x80)==0x80 )){ /* Lone UTF-8 trail byte. */ | |
| 274 | + *pUtf32 = 0xfffd; /* U+FFFD Replacement Character */ | |
| 275 | +#ifdef FOSSIL_DEBUG | |
| 276 | + assert( *pUtf32!=0xfffd ); /* Invalid UTF-8 sequence. */ | |
| 149 | 277 | #endif |
| 150 | - for(i=0, lengthUTF8=0; i<lengthBytes; i++, lengthUTF8++){ | |
| 151 | - char c = zString[i]; | |
| 152 | - int cchUTF8=1; /* Code units consumed. */ | |
| 153 | - int maxUTF8=1; /* Expected sequence length. */ | |
| 154 | - if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ | |
| 155 | - else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ | |
| 156 | - else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ | |
| 157 | - while( cchUTF8<maxUTF8 && | |
| 158 | - i<lengthBytes-1 && | |
| 159 | - (zString[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ | |
| 160 | - cchUTF8++; | |
| 161 | - i++; | |
| 162 | - } | |
| 163 | - } | |
| 164 | - return lengthUTF8; | |
| 278 | + return; | |
| 279 | + } | |
| 280 | + switch( cchUTF8 ){ | |
| 281 | + case 4: | |
| 282 | + *pUtf32 = | |
| 283 | + ( (z[0] & 0x0f)<<18 ) | | |
| 284 | + ( (z[1] & 0x3f)<<12 ) | | |
| 285 | + ( (z[2] & 0x3f)<< 6 ) | | |
| 286 | + ( (z[4] & 0x3f)<< 0 ) ; | |
| 287 | + break; | |
| 288 | + case 3: | |
| 289 | + *pUtf32 = | |
| 290 | + ( (z[0] & 0x0f)<<12 ) | | |
| 291 | + ( (z[1] & 0x3f)<< 6 ) | | |
| 292 | + ( (z[2] & 0x3f)<< 0 ) ; | |
| 293 | + break; | |
| 294 | + case 2: | |
| 295 | + *pUtf32 = | |
| 296 | + ( (z[0] & 0x1f)<< 6 ) | | |
| 297 | + ( (z[1] & 0x3f)<< 0 ) ; | |
| 298 | + break; | |
| 299 | + default: | |
| 300 | + *pUtf32 = 0xfffd; /* U+FFFD Replacement Character */ | |
| 301 | + break; | |
| 302 | + } | |
| 303 | +#ifdef FOSSIL_DEBUG | |
| 304 | + assert( | |
| 305 | + *pUtf32>=0 && *pUtf32<=0x10ffff && /* Valid range U+0000 to U+10FFFF. */ | |
| 306 | + *pUtf32<0xd800 && *pUtf32>0xdfff /* Non-scalar (UTF-16 surrogates). */ | |
| 307 | + ); | |
| 308 | +#endif | |
| 165 | 309 | } |
| 166 | 310 | |
| 167 | 311 | /* |
| 168 | 312 | ** This function is called when printing a logical comment line to calculate |
| 169 | 313 | ** the necessary indenting. The caller needs to emit the indenting spaces. |
| @@ -206,11 +350,10 @@ | ||
| 206 | 350 | int *pLineCnt, /* [in/out] Pointer to the total line count. */ |
| 207 | 351 | const char **pzLine /* [out] Pointer to the end of the logical line. */ |
| 208 | 352 | ){ |
| 209 | 353 | int index = 0, charCnt = 0, lineCnt = 0, maxChars, i; |
| 210 | 354 | char zBuf[400]; int iBuf=0; /* Output buffer and counter. */ |
| 211 | - int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ | |
| 212 | 355 | if( !zLine ) return; |
| 213 | 356 | if( lineChars<=0 ) return; |
| 214 | 357 | #if 0 |
| 215 | 358 | assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */ |
| 216 | 359 | assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */ |
| @@ -229,10 +372,11 @@ | ||
| 229 | 372 | /* Limit line indent to fit output buffer. */ |
| 230 | 373 | origIndent = sizeof(zBuf)-6; |
| 231 | 374 | } |
| 232 | 375 | maxChars = lineChars; |
| 233 | 376 | for(;;){ |
| 377 | + int cchUTF8, utf32; | |
| 234 | 378 | int useChars = 1; |
| 235 | 379 | char c = zLine[index]; |
| 236 | 380 | /* Flush the output buffer if there's no space left for at least one more |
| 237 | 381 | ** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces, |
| 238 | 382 | ** a new line, and a terminating NULL. */ |
| @@ -260,43 +404,47 @@ | ||
| 260 | 404 | if( c=='\n' ){ |
| 261 | 405 | lineCnt++; |
| 262 | 406 | charCnt = 0; |
| 263 | 407 | useChars = 0; |
| 264 | 408 | }else if( c=='\t' ){ |
| 265 | - int distUTF8; | |
| 266 | - int nextIndex = comment_next_space(zLine, index, &distUTF8); | |
| 267 | - if( nextIndex<=0 || distUTF8>maxChars ){ | |
| 409 | + int sumWidth; | |
| 410 | + int nextIndex = comment_next_space(zLine, index, maxChars, &sumWidth); | |
| 411 | + if( nextIndex<=0 || sumWidth>maxChars ){ | |
| 268 | 412 | break; |
| 269 | 413 | } |
| 270 | 414 | charCnt++; |
| 271 | 415 | useChars = COMMENT_TAB_WIDTH; |
| 272 | 416 | if( maxChars<useChars ){ |
| 273 | 417 | zBuf[iBuf++] = ' '; |
| 274 | 418 | break; |
| 275 | 419 | } |
| 276 | 420 | }else if( wordBreak && fossil_isspace(c) ){ |
| 277 | - int distUTF8; | |
| 278 | - int nextIndex = comment_next_space(zLine, index, &distUTF8); | |
| 279 | - if( nextIndex<=0 || distUTF8>=maxChars ){ | |
| 421 | + int sumWidth; | |
| 422 | + int nextIndex = comment_next_space(zLine, index, maxChars, &sumWidth); | |
| 423 | + if( nextIndex<=0 || sumWidth>=maxChars ){ | |
| 280 | 424 | break; |
| 281 | 425 | } |
| 282 | 426 | charCnt++; |
| 283 | 427 | }else{ |
| 284 | 428 | charCnt++; |
| 285 | 429 | } |
| 286 | 430 | assert( c!='\n' || charCnt==0 ); |
| 287 | 431 | zBuf[iBuf++] = c; |
| 288 | - /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */ | |
| 289 | - cchUTF8=1; /* Code units consumed. */ | |
| 290 | - maxUTF8=1; /* Expected sequence length. */ | |
| 291 | - if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ | |
| 292 | - else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ | |
| 293 | - else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ | |
| 294 | - while( cchUTF8<maxUTF8 && | |
| 295 | - (zLine[index]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ | |
| 296 | - cchUTF8++; | |
| 297 | - zBuf[iBuf++] = zLine[index++]; | |
| 432 | + char_info_utf8(&zLine[index-1],&cchUTF8,&utf32); | |
| 433 | + if( cchUTF8>1 ){ | |
| 434 | + int wcwidth; | |
| 435 | + wcwidth = cli_wcwidth(utf32); | |
| 436 | + if( wcwidth>maxChars && lineChars>=wcwidth ){ /* rollback */ | |
| 437 | + index--; | |
| 438 | + iBuf--; | |
| 439 | + zBuf[iBuf] = 0; | |
| 440 | + break; | |
| 441 | + } | |
| 442 | + for( ; cchUTF8>1; cchUTF8-- ){ | |
| 443 | + zBuf[iBuf++] = zLine[index++]; | |
| 444 | + } | |
| 445 | + useChars += wcwidth - 1; | |
| 298 | 446 | } |
| 299 | 447 | maxChars -= useChars; |
| 300 | 448 | if( maxChars<=0 ) break; |
| 301 | 449 | if( c=='\n' ) break; |
| 302 | 450 | } |
| @@ -338,11 +486,10 @@ | ||
| 338 | 486 | int si, sk, i, k, kc; |
| 339 | 487 | int doIndent = 0; |
| 340 | 488 | char *zBuf; |
| 341 | 489 | char zBuffer[400]; |
| 342 | 490 | int lineCnt = 0; |
| 343 | - int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ | |
| 344 | 491 | |
| 345 | 492 | if( width<0 ){ |
| 346 | 493 | comment_set_maxchars(indent, &maxChars); |
| 347 | 494 | } |
| 348 | 495 | if( zText==0 ) zText = "(NULL)"; |
| @@ -364,25 +511,25 @@ | ||
| 364 | 511 | } |
| 365 | 512 | if( zBuf!=zBuffer) fossil_free(zBuf); |
| 366 | 513 | return lineCnt; |
| 367 | 514 | } |
| 368 | 515 | for(sk=si=i=k=kc=0; zText[i] && kc<maxChars; i++){ |
| 516 | + int cchUTF8, utf32; | |
| 369 | 517 | char c = zText[i]; |
| 370 | 518 | kc++; /* Count complete UTF-8 sequences. */ |
| 371 | - /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */ | |
| 372 | - cchUTF8=1; /* Code units consumed. */ | |
| 373 | - maxUTF8=1; /* Expected sequence length. */ | |
| 374 | - if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ | |
| 375 | - else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ | |
| 376 | - else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ | |
| 377 | - if( maxUTF8>1 ){ | |
| 378 | - zBuf[k++] = c; | |
| 379 | - while( cchUTF8<maxUTF8 && | |
| 380 | - (zText[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ | |
| 381 | - cchUTF8++; | |
| 519 | + char_info_utf8(&zText[i],&cchUTF8,&utf32); | |
| 520 | + if( cchUTF8>1 ){ | |
| 521 | + int wcwidth; | |
| 522 | + wcwidth = cli_wcwidth(utf32); | |
| 523 | + if( kc+wcwidth-1>maxChars && maxChars>=wcwidth ){ /* rollback */ | |
| 524 | + kc--; | |
| 525 | + break; | |
| 526 | + } | |
| 527 | + for( i--; cchUTF8>0; cchUTF8-- ){ | |
| 382 | 528 | zBuf[k++] = zText[++i]; |
| 383 | 529 | } |
| 530 | + kc += wcwidth - 1; | |
| 384 | 531 | } |
| 385 | 532 | else if( fossil_isspace(c) ){ |
| 386 | 533 | si = i; |
| 387 | 534 | sk = k; |
| 388 | 535 | if( k==0 || zBuf[k-1]!=' ' ){ |
| @@ -497,11 +644,13 @@ | ||
| 497 | 644 | zLine = zText; |
| 498 | 645 | for(;;){ |
| 499 | 646 | comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, |
| 500 | 647 | maxChars, trimCrLf, trimSpace, wordBreak, origBreak, |
| 501 | 648 | &lineCnt, &zLine); |
| 502 | - if( !zLine || !zLine[0] ) break; | |
| 649 | + if( zLine==0 ) break; | |
| 650 | + while( fossil_isspace(zLine[0]) ) zLine++; | |
| 651 | + if( zLine[0]==0 ) break; | |
| 503 | 652 | } |
| 504 | 653 | return lineCnt; |
| 505 | 654 | } |
| 506 | 655 | |
| 507 | 656 | /* |
| @@ -597,10 +746,11 @@ | ||
| 597 | 746 | if( zIndent ){ |
| 598 | 747 | indent = atoi(zIndent); |
| 599 | 748 | }else{ |
| 600 | 749 | indent = -1; /* automatic */ |
| 601 | 750 | } |
| 751 | + verify_all_options(); | |
| 602 | 752 | if( g.argc!=4 && g.argc!=5 ){ |
| 603 | 753 | usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?"); |
| 604 | 754 | } |
| 605 | 755 | zPrefix = g.argv[2]; |
| 606 | 756 | zText = g.argv[3]; |
| 607 | 757 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -31,10 +31,117 @@ | |
| 31 | #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ |
| 32 | #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ |
| 33 | #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ |
| 34 | #endif |
| 35 | |
| 36 | /* |
| 37 | ** This is the previous value used by most external callers when they |
| 38 | ** needed to specify a default maximum line length to be used with the |
| 39 | ** comment_print() function. |
| 40 | */ |
| @@ -108,62 +215,99 @@ | |
| 108 | ** algorithm, the NUL character is treated the same as a spacing character. |
| 109 | */ |
| 110 | static int comment_next_space( |
| 111 | const char *zLine, /* [in] The comment line being printed. */ |
| 112 | int index, /* [in] The current character index being handled. */ |
| 113 | int *distUTF8 /* [out] Distance to next space in UTF-8 sequences. */ |
| 114 | ){ |
| 115 | int nextIndex = index + 1; |
| 116 | int fNonASCII=0; |
| 117 | for(;;){ |
| 118 | char c = zLine[nextIndex]; |
| 119 | if( (c&0x80)==0x80 ) fNonASCII=1; |
| 120 | if( c==0 || fossil_isspace(c) ){ |
| 121 | if( distUTF8 ){ |
| 122 | if( fNonASCII!=0 ){ |
| 123 | *distUTF8 = strlen_utf8(&zLine[index], nextIndex-index); |
| 124 | }else{ |
| 125 | *distUTF8 = nextIndex-index; |
| 126 | } |
| 127 | } |
| 128 | return nextIndex; |
| 129 | } |
| 130 | nextIndex++; |
| 131 | } |
| 132 | return 0; /* NOT REACHED */ |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | ** Count the number of UTF-8 sequences in a string. Incomplete, ill-formed and |
| 137 | ** overlong sequences are counted as one sequence. The invalid lead bytes 0xC0 |
| 138 | ** to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte |
| 139 | ** sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF are |
| 140 | ** treated as invalid 1-byte sequences (as lone trail bytes). |
| 141 | ** Combining characters and East Asian Wide and Fullwidth characters are counted |
| 142 | ** as one, so this function does not calculate the effective "display width". |
| 143 | */ |
| 144 | int strlen_utf8(const char *zString, int lengthBytes){ |
| 145 | int i; /* Counted bytes. */ |
| 146 | int lengthUTF8; /* Counted UTF-8 sequences. */ |
| 147 | #if 0 |
| 148 | assert( lengthBytes>=0 ); |
| 149 | #endif |
| 150 | for(i=0, lengthUTF8=0; i<lengthBytes; i++, lengthUTF8++){ |
| 151 | char c = zString[i]; |
| 152 | int cchUTF8=1; /* Code units consumed. */ |
| 153 | int maxUTF8=1; /* Expected sequence length. */ |
| 154 | if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ |
| 155 | else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ |
| 156 | else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ |
| 157 | while( cchUTF8<maxUTF8 && |
| 158 | i<lengthBytes-1 && |
| 159 | (zString[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ |
| 160 | cchUTF8++; |
| 161 | i++; |
| 162 | } |
| 163 | } |
| 164 | return lengthUTF8; |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | ** This function is called when printing a logical comment line to calculate |
| 169 | ** the necessary indenting. The caller needs to emit the indenting spaces. |
| @@ -206,11 +350,10 @@ | |
| 206 | int *pLineCnt, /* [in/out] Pointer to the total line count. */ |
| 207 | const char **pzLine /* [out] Pointer to the end of the logical line. */ |
| 208 | ){ |
| 209 | int index = 0, charCnt = 0, lineCnt = 0, maxChars, i; |
| 210 | char zBuf[400]; int iBuf=0; /* Output buffer and counter. */ |
| 211 | int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ |
| 212 | if( !zLine ) return; |
| 213 | if( lineChars<=0 ) return; |
| 214 | #if 0 |
| 215 | assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */ |
| 216 | assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */ |
| @@ -229,10 +372,11 @@ | |
| 229 | /* Limit line indent to fit output buffer. */ |
| 230 | origIndent = sizeof(zBuf)-6; |
| 231 | } |
| 232 | maxChars = lineChars; |
| 233 | for(;;){ |
| 234 | int useChars = 1; |
| 235 | char c = zLine[index]; |
| 236 | /* Flush the output buffer if there's no space left for at least one more |
| 237 | ** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces, |
| 238 | ** a new line, and a terminating NULL. */ |
| @@ -260,43 +404,47 @@ | |
| 260 | if( c=='\n' ){ |
| 261 | lineCnt++; |
| 262 | charCnt = 0; |
| 263 | useChars = 0; |
| 264 | }else if( c=='\t' ){ |
| 265 | int distUTF8; |
| 266 | int nextIndex = comment_next_space(zLine, index, &distUTF8); |
| 267 | if( nextIndex<=0 || distUTF8>maxChars ){ |
| 268 | break; |
| 269 | } |
| 270 | charCnt++; |
| 271 | useChars = COMMENT_TAB_WIDTH; |
| 272 | if( maxChars<useChars ){ |
| 273 | zBuf[iBuf++] = ' '; |
| 274 | break; |
| 275 | } |
| 276 | }else if( wordBreak && fossil_isspace(c) ){ |
| 277 | int distUTF8; |
| 278 | int nextIndex = comment_next_space(zLine, index, &distUTF8); |
| 279 | if( nextIndex<=0 || distUTF8>=maxChars ){ |
| 280 | break; |
| 281 | } |
| 282 | charCnt++; |
| 283 | }else{ |
| 284 | charCnt++; |
| 285 | } |
| 286 | assert( c!='\n' || charCnt==0 ); |
| 287 | zBuf[iBuf++] = c; |
| 288 | /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */ |
| 289 | cchUTF8=1; /* Code units consumed. */ |
| 290 | maxUTF8=1; /* Expected sequence length. */ |
| 291 | if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ |
| 292 | else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ |
| 293 | else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ |
| 294 | while( cchUTF8<maxUTF8 && |
| 295 | (zLine[index]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ |
| 296 | cchUTF8++; |
| 297 | zBuf[iBuf++] = zLine[index++]; |
| 298 | } |
| 299 | maxChars -= useChars; |
| 300 | if( maxChars<=0 ) break; |
| 301 | if( c=='\n' ) break; |
| 302 | } |
| @@ -338,11 +486,10 @@ | |
| 338 | int si, sk, i, k, kc; |
| 339 | int doIndent = 0; |
| 340 | char *zBuf; |
| 341 | char zBuffer[400]; |
| 342 | int lineCnt = 0; |
| 343 | int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ |
| 344 | |
| 345 | if( width<0 ){ |
| 346 | comment_set_maxchars(indent, &maxChars); |
| 347 | } |
| 348 | if( zText==0 ) zText = "(NULL)"; |
| @@ -364,25 +511,25 @@ | |
| 364 | } |
| 365 | if( zBuf!=zBuffer) fossil_free(zBuf); |
| 366 | return lineCnt; |
| 367 | } |
| 368 | for(sk=si=i=k=kc=0; zText[i] && kc<maxChars; i++){ |
| 369 | char c = zText[i]; |
| 370 | kc++; /* Count complete UTF-8 sequences. */ |
| 371 | /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */ |
| 372 | cchUTF8=1; /* Code units consumed. */ |
| 373 | maxUTF8=1; /* Expected sequence length. */ |
| 374 | if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ |
| 375 | else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ |
| 376 | else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ |
| 377 | if( maxUTF8>1 ){ |
| 378 | zBuf[k++] = c; |
| 379 | while( cchUTF8<maxUTF8 && |
| 380 | (zText[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ |
| 381 | cchUTF8++; |
| 382 | zBuf[k++] = zText[++i]; |
| 383 | } |
| 384 | } |
| 385 | else if( fossil_isspace(c) ){ |
| 386 | si = i; |
| 387 | sk = k; |
| 388 | if( k==0 || zBuf[k-1]!=' ' ){ |
| @@ -497,11 +644,13 @@ | |
| 497 | zLine = zText; |
| 498 | for(;;){ |
| 499 | comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, |
| 500 | maxChars, trimCrLf, trimSpace, wordBreak, origBreak, |
| 501 | &lineCnt, &zLine); |
| 502 | if( !zLine || !zLine[0] ) break; |
| 503 | } |
| 504 | return lineCnt; |
| 505 | } |
| 506 | |
| 507 | /* |
| @@ -597,10 +746,11 @@ | |
| 597 | if( zIndent ){ |
| 598 | indent = atoi(zIndent); |
| 599 | }else{ |
| 600 | indent = -1; /* automatic */ |
| 601 | } |
| 602 | if( g.argc!=4 && g.argc!=5 ){ |
| 603 | usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?"); |
| 604 | } |
| 605 | zPrefix = g.argv[2]; |
| 606 | zText = g.argv[3]; |
| 607 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -31,10 +31,117 @@ | |
| 31 | #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ |
| 32 | #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ |
| 33 | #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ |
| 34 | #endif |
| 35 | |
| 36 | /********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/ |
| 37 | /* Lookup table to estimate the number of columns consumed by a Unicode |
| 38 | ** character. |
| 39 | */ |
| 40 | static const struct { |
| 41 | unsigned char w; /* Width of the character in columns */ |
| 42 | int iFirst; /* First character in a span having this width */ |
| 43 | } aUWidth[] = { |
| 44 | /* {1, 0x00000}, */ |
| 45 | {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, |
| 46 | {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, |
| 47 | {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, |
| 48 | {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, |
| 49 | {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, |
| 50 | {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, |
| 51 | {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, |
| 52 | {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, |
| 53 | {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, |
| 54 | {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, |
| 55 | {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, |
| 56 | {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, |
| 57 | {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, |
| 58 | {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, |
| 59 | {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, |
| 60 | {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, |
| 61 | {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, |
| 62 | {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, |
| 63 | {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, |
| 64 | {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, |
| 65 | {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, |
| 66 | {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, |
| 67 | {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, |
| 68 | {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, |
| 69 | {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, |
| 70 | {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, |
| 71 | {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, |
| 72 | {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, |
| 73 | {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, |
| 74 | {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, |
| 75 | {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, |
| 76 | {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, |
| 77 | {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, |
| 78 | {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, |
| 79 | {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, |
| 80 | {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, |
| 81 | {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, |
| 82 | {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, |
| 83 | {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, |
| 84 | {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, |
| 85 | {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, |
| 86 | {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, |
| 87 | {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, |
| 88 | {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, |
| 89 | {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, |
| 90 | {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, |
| 91 | {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, |
| 92 | {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, |
| 93 | {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, |
| 94 | {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, |
| 95 | {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, |
| 96 | {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, |
| 97 | {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, |
| 98 | {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, |
| 99 | {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, |
| 100 | {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, |
| 101 | {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, |
| 102 | {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, |
| 103 | {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, |
| 104 | {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, |
| 105 | {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} |
| 106 | }; |
| 107 | |
| 108 | /* |
| 109 | ** Return an estimate of the width, in columns, for the single Unicode |
| 110 | ** character c. For normal characters, the answer is always 1. But the |
| 111 | ** estimate might be 0 or 2 for zero-width and double-width characters. |
| 112 | ** |
| 113 | ** Different display devices display unicode using different widths. So |
| 114 | ** it is impossible to know that true display width with 100% accuracy. |
| 115 | ** Inaccuracies in the width estimates might cause columns to be misaligned. |
| 116 | ** Unfortunately, there is nothing we can do about that. |
| 117 | */ |
| 118 | static int cli_wcwidth(int c){ |
| 119 | int iFirst, iLast; |
| 120 | |
| 121 | /* Fast path for common characters */ |
| 122 | if( c<=0x300 ) return 1; |
| 123 | |
| 124 | /* The general case */ |
| 125 | iFirst = 0; |
| 126 | iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; |
| 127 | while( iFirst<iLast-1 ){ |
| 128 | int iMid = (iFirst+iLast)/2; |
| 129 | int cMid = aUWidth[iMid].iFirst; |
| 130 | if( cMid < c ){ |
| 131 | iFirst = iMid; |
| 132 | }else if( cMid > c ){ |
| 133 | iLast = iMid - 1; |
| 134 | }else{ |
| 135 | return aUWidth[iMid].w; |
| 136 | } |
| 137 | } |
| 138 | if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; |
| 139 | return aUWidth[iLast].w; |
| 140 | } |
| 141 | /******* End of code copied from SQLite *************************************/ |
| 142 | |
| 143 | /* |
| 144 | ** This is the previous value used by most external callers when they |
| 145 | ** needed to specify a default maximum line length to be used with the |
| 146 | ** comment_print() function. |
| 147 | */ |
| @@ -108,62 +215,99 @@ | |
| 215 | ** algorithm, the NUL character is treated the same as a spacing character. |
| 216 | */ |
| 217 | static int comment_next_space( |
| 218 | const char *zLine, /* [in] The comment line being printed. */ |
| 219 | int index, /* [in] The current character index being handled. */ |
| 220 | int maxChars, /* [in] Optimization hint to abort before space found. */ |
| 221 | int *sumWidth /* [out] Summated width of all characters to next space. */ |
| 222 | ){ |
| 223 | int cchUTF8, utf32, wcwidth = 0; |
| 224 | int nextIndex = index; |
| 225 | for(;;){ |
| 226 | char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32); |
| 227 | nextIndex += cchUTF8; |
| 228 | wcwidth += cli_wcwidth(utf32); |
| 229 | if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) || |
| 230 | wcwidth>maxChars ){ |
| 231 | *sumWidth = wcwidth; |
| 232 | return nextIndex; |
| 233 | } |
| 234 | } |
| 235 | return 0; /* NOT REACHED */ |
| 236 | } |
| 237 | |
| 238 | /* |
| 239 | ** Return information about the next (single- or multi-byte) character in the |
| 240 | ** specified UTF-8 string: The number of UTF-8 code units (in this case: bytes) |
| 241 | ** and the decoded UTF-32 code point. Incomplete, ill-formed and overlong |
| 242 | ** sequences are consumed together as one invalid code point. The invalid lead |
| 243 | ** bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- |
| 244 | ** and 4-byte sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF |
| 245 | ** are treated as invalid 1-byte sequences (as lone trail bytes), all resulting |
| 246 | ** in one invalid code point. Invalid UTF-8 sequences encoding a non-scalar code |
| 247 | ** point (UTF-16 surrogates U+D800 to U+DFFF) are allowed. |
| 248 | */ |
| 249 | void char_info_utf8( |
| 250 | const char *z, |
| 251 | int *pCchUTF8, |
| 252 | int *pUtf32 |
| 253 | ){ |
| 254 | int i = 0; /* Counted bytes. */ |
| 255 | int cchUTF8 = 1; /* Code units consumed. */ |
| 256 | int maxUTF8 = 1; /* Expected sequence length. */ |
| 257 | char c = z[i++]; |
| 258 | if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */ |
| 259 | *pCchUTF8 = 1; |
| 260 | *pUtf32 = (int)z[0]; |
| 261 | return; |
| 262 | } |
| 263 | else if( (c&0xe0)==0xc0 ) maxUTF8 = 2; /* UTF-8 lead byte 110vvvvv */ |
| 264 | else if( (c&0xf0)==0xe0 ) maxUTF8 = 3; /* UTF-8 lead byte 1110vvvv */ |
| 265 | else if( (c&0xf8)==0xf0 ) maxUTF8 = 4; /* UTF-8 lead byte 11110vvv */ |
| 266 | while( cchUTF8<maxUTF8 && |
| 267 | (z[i]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ |
| 268 | cchUTF8++; |
| 269 | i++; |
| 270 | } |
| 271 | *pCchUTF8 = cchUTF8; |
| 272 | if( cchUTF8!=maxUTF8 || /* Incomplete UTF-8 sequence. */ |
| 273 | ( cchUTF8==1 && (c&0x80)==0x80 )){ /* Lone UTF-8 trail byte. */ |
| 274 | *pUtf32 = 0xfffd; /* U+FFFD Replacement Character */ |
| 275 | #ifdef FOSSIL_DEBUG |
| 276 | assert( *pUtf32!=0xfffd ); /* Invalid UTF-8 sequence. */ |
| 277 | #endif |
| 278 | return; |
| 279 | } |
| 280 | switch( cchUTF8 ){ |
| 281 | case 4: |
| 282 | *pUtf32 = |
| 283 | ( (z[0] & 0x0f)<<18 ) | |
| 284 | ( (z[1] & 0x3f)<<12 ) | |
| 285 | ( (z[2] & 0x3f)<< 6 ) | |
| 286 | ( (z[4] & 0x3f)<< 0 ) ; |
| 287 | break; |
| 288 | case 3: |
| 289 | *pUtf32 = |
| 290 | ( (z[0] & 0x0f)<<12 ) | |
| 291 | ( (z[1] & 0x3f)<< 6 ) | |
| 292 | ( (z[2] & 0x3f)<< 0 ) ; |
| 293 | break; |
| 294 | case 2: |
| 295 | *pUtf32 = |
| 296 | ( (z[0] & 0x1f)<< 6 ) | |
| 297 | ( (z[1] & 0x3f)<< 0 ) ; |
| 298 | break; |
| 299 | default: |
| 300 | *pUtf32 = 0xfffd; /* U+FFFD Replacement Character */ |
| 301 | break; |
| 302 | } |
| 303 | #ifdef FOSSIL_DEBUG |
| 304 | assert( |
| 305 | *pUtf32>=0 && *pUtf32<=0x10ffff && /* Valid range U+0000 to U+10FFFF. */ |
| 306 | *pUtf32<0xd800 && *pUtf32>0xdfff /* Non-scalar (UTF-16 surrogates). */ |
| 307 | ); |
| 308 | #endif |
| 309 | } |
| 310 | |
| 311 | /* |
| 312 | ** This function is called when printing a logical comment line to calculate |
| 313 | ** the necessary indenting. The caller needs to emit the indenting spaces. |
| @@ -206,11 +350,10 @@ | |
| 350 | int *pLineCnt, /* [in/out] Pointer to the total line count. */ |
| 351 | const char **pzLine /* [out] Pointer to the end of the logical line. */ |
| 352 | ){ |
| 353 | int index = 0, charCnt = 0, lineCnt = 0, maxChars, i; |
| 354 | char zBuf[400]; int iBuf=0; /* Output buffer and counter. */ |
| 355 | if( !zLine ) return; |
| 356 | if( lineChars<=0 ) return; |
| 357 | #if 0 |
| 358 | assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */ |
| 359 | assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */ |
| @@ -229,10 +372,11 @@ | |
| 372 | /* Limit line indent to fit output buffer. */ |
| 373 | origIndent = sizeof(zBuf)-6; |
| 374 | } |
| 375 | maxChars = lineChars; |
| 376 | for(;;){ |
| 377 | int cchUTF8, utf32; |
| 378 | int useChars = 1; |
| 379 | char c = zLine[index]; |
| 380 | /* Flush the output buffer if there's no space left for at least one more |
| 381 | ** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces, |
| 382 | ** a new line, and a terminating NULL. */ |
| @@ -260,43 +404,47 @@ | |
| 404 | if( c=='\n' ){ |
| 405 | lineCnt++; |
| 406 | charCnt = 0; |
| 407 | useChars = 0; |
| 408 | }else if( c=='\t' ){ |
| 409 | int sumWidth; |
| 410 | int nextIndex = comment_next_space(zLine, index, maxChars, &sumWidth); |
| 411 | if( nextIndex<=0 || sumWidth>maxChars ){ |
| 412 | break; |
| 413 | } |
| 414 | charCnt++; |
| 415 | useChars = COMMENT_TAB_WIDTH; |
| 416 | if( maxChars<useChars ){ |
| 417 | zBuf[iBuf++] = ' '; |
| 418 | break; |
| 419 | } |
| 420 | }else if( wordBreak && fossil_isspace(c) ){ |
| 421 | int sumWidth; |
| 422 | int nextIndex = comment_next_space(zLine, index, maxChars, &sumWidth); |
| 423 | if( nextIndex<=0 || sumWidth>=maxChars ){ |
| 424 | break; |
| 425 | } |
| 426 | charCnt++; |
| 427 | }else{ |
| 428 | charCnt++; |
| 429 | } |
| 430 | assert( c!='\n' || charCnt==0 ); |
| 431 | zBuf[iBuf++] = c; |
| 432 | char_info_utf8(&zLine[index-1],&cchUTF8,&utf32); |
| 433 | if( cchUTF8>1 ){ |
| 434 | int wcwidth; |
| 435 | wcwidth = cli_wcwidth(utf32); |
| 436 | if( wcwidth>maxChars && lineChars>=wcwidth ){ /* rollback */ |
| 437 | index--; |
| 438 | iBuf--; |
| 439 | zBuf[iBuf] = 0; |
| 440 | break; |
| 441 | } |
| 442 | for( ; cchUTF8>1; cchUTF8-- ){ |
| 443 | zBuf[iBuf++] = zLine[index++]; |
| 444 | } |
| 445 | useChars += wcwidth - 1; |
| 446 | } |
| 447 | maxChars -= useChars; |
| 448 | if( maxChars<=0 ) break; |
| 449 | if( c=='\n' ) break; |
| 450 | } |
| @@ -338,11 +486,10 @@ | |
| 486 | int si, sk, i, k, kc; |
| 487 | int doIndent = 0; |
| 488 | char *zBuf; |
| 489 | char zBuffer[400]; |
| 490 | int lineCnt = 0; |
| 491 | |
| 492 | if( width<0 ){ |
| 493 | comment_set_maxchars(indent, &maxChars); |
| 494 | } |
| 495 | if( zText==0 ) zText = "(NULL)"; |
| @@ -364,25 +511,25 @@ | |
| 511 | } |
| 512 | if( zBuf!=zBuffer) fossil_free(zBuf); |
| 513 | return lineCnt; |
| 514 | } |
| 515 | for(sk=si=i=k=kc=0; zText[i] && kc<maxChars; i++){ |
| 516 | int cchUTF8, utf32; |
| 517 | char c = zText[i]; |
| 518 | kc++; /* Count complete UTF-8 sequences. */ |
| 519 | char_info_utf8(&zText[i],&cchUTF8,&utf32); |
| 520 | if( cchUTF8>1 ){ |
| 521 | int wcwidth; |
| 522 | wcwidth = cli_wcwidth(utf32); |
| 523 | if( kc+wcwidth-1>maxChars && maxChars>=wcwidth ){ /* rollback */ |
| 524 | kc--; |
| 525 | break; |
| 526 | } |
| 527 | for( i--; cchUTF8>0; cchUTF8-- ){ |
| 528 | zBuf[k++] = zText[++i]; |
| 529 | } |
| 530 | kc += wcwidth - 1; |
| 531 | } |
| 532 | else if( fossil_isspace(c) ){ |
| 533 | si = i; |
| 534 | sk = k; |
| 535 | if( k==0 || zBuf[k-1]!=' ' ){ |
| @@ -497,11 +644,13 @@ | |
| 644 | zLine = zText; |
| 645 | for(;;){ |
| 646 | comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, |
| 647 | maxChars, trimCrLf, trimSpace, wordBreak, origBreak, |
| 648 | &lineCnt, &zLine); |
| 649 | if( zLine==0 ) break; |
| 650 | while( fossil_isspace(zLine[0]) ) zLine++; |
| 651 | if( zLine[0]==0 ) break; |
| 652 | } |
| 653 | return lineCnt; |
| 654 | } |
| 655 | |
| 656 | /* |
| @@ -597,10 +746,11 @@ | |
| 746 | if( zIndent ){ |
| 747 | indent = atoi(zIndent); |
| 748 | }else{ |
| 749 | indent = -1; /* automatic */ |
| 750 | } |
| 751 | verify_all_options(); |
| 752 | if( g.argc!=4 && g.argc!=5 ){ |
| 753 | usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?"); |
| 754 | } |
| 755 | zPrefix = g.argv[2]; |
| 756 | zText = g.argv[3]; |
| 757 |
+28
-9
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -118,24 +118,15 @@ | ||
| 118 | 118 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 119 | 119 | { "default-csp", CONFIGSET_SKIN }, |
| 120 | 120 | { "sitemap-extra", CONFIGSET_SKIN }, |
| 121 | 121 | { "safe-html", CONFIGSET_SKIN }, |
| 122 | 122 | |
| 123 | -#ifdef FOSSIL_ENABLE_TH1_DOCS | |
| 124 | - { "th1-docs", CONFIGSET_TH1 }, | |
| 125 | -#endif | |
| 126 | 123 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 127 | 124 | { "th1-hooks", CONFIGSET_TH1 }, |
| 128 | 125 | #endif |
| 129 | - { "th1-setup", CONFIGSET_TH1 }, | |
| 130 | 126 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 131 | 127 | |
| 132 | -#ifdef FOSSIL_ENABLE_TCL | |
| 133 | - { "tcl", CONFIGSET_TH1 }, | |
| 134 | - { "tcl-setup", CONFIGSET_TH1 }, | |
| 135 | -#endif | |
| 136 | - | |
| 137 | 128 | { "project-name", CONFIGSET_PROJ }, |
| 138 | 129 | { "short-project-name", CONFIGSET_PROJ }, |
| 139 | 130 | { "project-description", CONFIGSET_PROJ }, |
| 140 | 131 | { "index-page", CONFIGSET_PROJ }, |
| 141 | 132 | { "manifest", CONFIGSET_PROJ }, |
| @@ -239,17 +230,35 @@ | ||
| 239 | 230 | ** |
| 240 | 231 | ** "Safe" in the previous paragraph means the permission is granted to |
| 241 | 232 | ** export the property. In other words, the requesting side has presented |
| 242 | 233 | ** login credentials and has sufficient capabilities to access the requested |
| 243 | 234 | ** information. |
| 235 | +** | |
| 236 | +** Settings which are specifically flagged as sensitive will (as of | |
| 237 | +** 2024-10-15) cause this function to return 0, regardless of user | |
| 238 | +** permissions. As an example, if the th1-setup setting were not | |
| 239 | +** sensitive then a malicious repo admin could set that to include | |
| 240 | +** arbitrary TCL code and affect users who configure fossil with the | |
| 241 | +** --with-tcl flag. | |
| 244 | 242 | */ |
| 245 | 243 | int configure_is_exportable(const char *zName){ |
| 246 | 244 | int i; |
| 247 | 245 | int n = strlen(zName); |
| 246 | + Setting *pSet; | |
| 248 | 247 | if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ |
| 248 | + char * zCpy; | |
| 249 | 249 | zName++; |
| 250 | 250 | n -= 2; |
| 251 | + zCpy = fossil_strndup(zName, (ssize_t)n); | |
| 252 | + pSet = db_find_setting(zCpy, 0); | |
| 253 | + fossil_free(zCpy); | |
| 254 | + }else{ | |
| 255 | + pSet = db_find_setting(zName, 0); | |
| 256 | + } | |
| 257 | + if( pSet && pSet->sensitive ){ | |
| 258 | + /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ | |
| 259 | + return 0; | |
| 251 | 260 | } |
| 252 | 261 | for(i=0; i<count(aConfig); i++){ |
| 253 | 262 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 254 | 263 | int m = aConfig[i].groupMask; |
| 255 | 264 | if( !g.perm.Admin ){ |
| @@ -414,10 +423,15 @@ | ||
| 414 | 423 | if( nToken>=count(azToken)-1 ) break; |
| 415 | 424 | } |
| 416 | 425 | if( nToken<2 ) return; |
| 417 | 426 | if( aType[ii].zName[0]=='/' ){ |
| 418 | 427 | thisMask = configure_is_exportable(azToken[1]); |
| 428 | + if( 0==thisMask ){ | |
| 429 | + fossil_warning("Skipping non-exportable setting: %s = %s", | |
| 430 | + azToken[1], nToken>3 ? azToken[3] : "?"); | |
| 431 | + /* Will be skipped below */ | |
| 432 | + } | |
| 419 | 433 | }else{ |
| 420 | 434 | thisMask = configure_is_exportable(aType[ii].zName); |
| 421 | 435 | } |
| 422 | 436 | if( (thisMask & groupMask)==0 ) return; |
| 423 | 437 | if( (thisMask & checkMask)!=0 ){ |
| @@ -681,10 +695,15 @@ | ||
| 681 | 695 | } |
| 682 | 696 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 683 | 697 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 684 | 698 | for(ii=0; ii<count(aConfig); ii++){ |
| 685 | 699 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| 700 | + const Setting * pSet = db_find_setting(aConfig[ii].zName, 0); | |
| 701 | + if( pSet && pSet->sensitive ){ | |
| 702 | + /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ | |
| 703 | + continue; | |
| 704 | + } | |
| 686 | 705 | db_bind_text(&q, ":name", aConfig[ii].zName); |
| 687 | 706 | while( db_step(&q)==SQLITE_ROW ){ |
| 688 | 707 | blob_appendf(&rec,"%s %s value %s", |
| 689 | 708 | db_column_text(&q, 0), |
| 690 | 709 | db_column_text(&q, 1), |
| 691 | 710 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -118,24 +118,15 @@ | |
| 118 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 119 | { "default-csp", CONFIGSET_SKIN }, |
| 120 | { "sitemap-extra", CONFIGSET_SKIN }, |
| 121 | { "safe-html", CONFIGSET_SKIN }, |
| 122 | |
| 123 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 124 | { "th1-docs", CONFIGSET_TH1 }, |
| 125 | #endif |
| 126 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 127 | { "th1-hooks", CONFIGSET_TH1 }, |
| 128 | #endif |
| 129 | { "th1-setup", CONFIGSET_TH1 }, |
| 130 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 131 | |
| 132 | #ifdef FOSSIL_ENABLE_TCL |
| 133 | { "tcl", CONFIGSET_TH1 }, |
| 134 | { "tcl-setup", CONFIGSET_TH1 }, |
| 135 | #endif |
| 136 | |
| 137 | { "project-name", CONFIGSET_PROJ }, |
| 138 | { "short-project-name", CONFIGSET_PROJ }, |
| 139 | { "project-description", CONFIGSET_PROJ }, |
| 140 | { "index-page", CONFIGSET_PROJ }, |
| 141 | { "manifest", CONFIGSET_PROJ }, |
| @@ -239,17 +230,35 @@ | |
| 239 | ** |
| 240 | ** "Safe" in the previous paragraph means the permission is granted to |
| 241 | ** export the property. In other words, the requesting side has presented |
| 242 | ** login credentials and has sufficient capabilities to access the requested |
| 243 | ** information. |
| 244 | */ |
| 245 | int configure_is_exportable(const char *zName){ |
| 246 | int i; |
| 247 | int n = strlen(zName); |
| 248 | if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ |
| 249 | zName++; |
| 250 | n -= 2; |
| 251 | } |
| 252 | for(i=0; i<count(aConfig); i++){ |
| 253 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 254 | int m = aConfig[i].groupMask; |
| 255 | if( !g.perm.Admin ){ |
| @@ -414,10 +423,15 @@ | |
| 414 | if( nToken>=count(azToken)-1 ) break; |
| 415 | } |
| 416 | if( nToken<2 ) return; |
| 417 | if( aType[ii].zName[0]=='/' ){ |
| 418 | thisMask = configure_is_exportable(azToken[1]); |
| 419 | }else{ |
| 420 | thisMask = configure_is_exportable(aType[ii].zName); |
| 421 | } |
| 422 | if( (thisMask & groupMask)==0 ) return; |
| 423 | if( (thisMask & checkMask)!=0 ){ |
| @@ -681,10 +695,15 @@ | |
| 681 | } |
| 682 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 683 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 684 | for(ii=0; ii<count(aConfig); ii++){ |
| 685 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| 686 | db_bind_text(&q, ":name", aConfig[ii].zName); |
| 687 | while( db_step(&q)==SQLITE_ROW ){ |
| 688 | blob_appendf(&rec,"%s %s value %s", |
| 689 | db_column_text(&q, 0), |
| 690 | db_column_text(&q, 1), |
| 691 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -118,24 +118,15 @@ | |
| 118 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 119 | { "default-csp", CONFIGSET_SKIN }, |
| 120 | { "sitemap-extra", CONFIGSET_SKIN }, |
| 121 | { "safe-html", CONFIGSET_SKIN }, |
| 122 | |
| 123 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 124 | { "th1-hooks", CONFIGSET_TH1 }, |
| 125 | #endif |
| 126 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 127 | |
| 128 | { "project-name", CONFIGSET_PROJ }, |
| 129 | { "short-project-name", CONFIGSET_PROJ }, |
| 130 | { "project-description", CONFIGSET_PROJ }, |
| 131 | { "index-page", CONFIGSET_PROJ }, |
| 132 | { "manifest", CONFIGSET_PROJ }, |
| @@ -239,17 +230,35 @@ | |
| 230 | ** |
| 231 | ** "Safe" in the previous paragraph means the permission is granted to |
| 232 | ** export the property. In other words, the requesting side has presented |
| 233 | ** login credentials and has sufficient capabilities to access the requested |
| 234 | ** information. |
| 235 | ** |
| 236 | ** Settings which are specifically flagged as sensitive will (as of |
| 237 | ** 2024-10-15) cause this function to return 0, regardless of user |
| 238 | ** permissions. As an example, if the th1-setup setting were not |
| 239 | ** sensitive then a malicious repo admin could set that to include |
| 240 | ** arbitrary TCL code and affect users who configure fossil with the |
| 241 | ** --with-tcl flag. |
| 242 | */ |
| 243 | int configure_is_exportable(const char *zName){ |
| 244 | int i; |
| 245 | int n = strlen(zName); |
| 246 | Setting *pSet; |
| 247 | if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ |
| 248 | char * zCpy; |
| 249 | zName++; |
| 250 | n -= 2; |
| 251 | zCpy = fossil_strndup(zName, (ssize_t)n); |
| 252 | pSet = db_find_setting(zCpy, 0); |
| 253 | fossil_free(zCpy); |
| 254 | }else{ |
| 255 | pSet = db_find_setting(zName, 0); |
| 256 | } |
| 257 | if( pSet && pSet->sensitive ){ |
| 258 | /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ |
| 259 | return 0; |
| 260 | } |
| 261 | for(i=0; i<count(aConfig); i++){ |
| 262 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 263 | int m = aConfig[i].groupMask; |
| 264 | if( !g.perm.Admin ){ |
| @@ -414,10 +423,15 @@ | |
| 423 | if( nToken>=count(azToken)-1 ) break; |
| 424 | } |
| 425 | if( nToken<2 ) return; |
| 426 | if( aType[ii].zName[0]=='/' ){ |
| 427 | thisMask = configure_is_exportable(azToken[1]); |
| 428 | if( 0==thisMask ){ |
| 429 | fossil_warning("Skipping non-exportable setting: %s = %s", |
| 430 | azToken[1], nToken>3 ? azToken[3] : "?"); |
| 431 | /* Will be skipped below */ |
| 432 | } |
| 433 | }else{ |
| 434 | thisMask = configure_is_exportable(aType[ii].zName); |
| 435 | } |
| 436 | if( (thisMask & groupMask)==0 ) return; |
| 437 | if( (thisMask & checkMask)!=0 ){ |
| @@ -681,10 +695,15 @@ | |
| 695 | } |
| 696 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 697 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 698 | for(ii=0; ii<count(aConfig); ii++){ |
| 699 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| 700 | const Setting * pSet = db_find_setting(aConfig[ii].zName, 0); |
| 701 | if( pSet && pSet->sensitive ){ |
| 702 | /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ |
| 703 | continue; |
| 704 | } |
| 705 | db_bind_text(&q, ":name", aConfig[ii].zName); |
| 706 | while( db_step(&q)==SQLITE_ROW ){ |
| 707 | blob_appendf(&rec,"%s %s value %s", |
| 708 | db_column_text(&q, 0), |
| 709 | db_column_text(&q, 1), |
| 710 |
M
src/db.c
+13
-14
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1557,10 +1557,12 @@ | ||
| 1557 | 1557 | sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0, |
| 1558 | 1558 | url_nouser_func,0,0); |
| 1559 | 1559 | sqlite3_create_function(db, "chat_msg_from_event", 4, |
| 1560 | 1560 | SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, |
| 1561 | 1561 | chat_msg_from_event, 0, 0); |
| 1562 | + sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0, | |
| 1563 | + file_inode_sql_func,0,0); | |
| 1562 | 1564 | |
| 1563 | 1565 | } |
| 1564 | 1566 | |
| 1565 | 1567 | #if USE_SEE |
| 1566 | 1568 | /* |
| @@ -4080,11 +4082,11 @@ | ||
| 4080 | 4082 | ** specified then that version is checked out. Otherwise the most recent |
| 4081 | 4083 | ** check-in on the main branch (usually "trunk") is used. |
| 4082 | 4084 | ** |
| 4083 | 4085 | ** REPOSITORY can be the filename for a repository that already exists on the |
| 4084 | 4086 | ** local machine or it can be a URI for a remote repository. If REPOSITORY |
| 4085 | -** is a URI in one of the formats recognized by the [[clone]] command, then | |
| 4087 | +** is a URI in one of the formats recognized by the [[clone]] command, the | |
| 4086 | 4088 | ** remote repo is first cloned, then the clone is opened. The clone will be |
| 4087 | 4089 | ** stored in the current directory, or in DIR if the "--repodir DIR" option |
| 4088 | 4090 | ** is used. The name of the clone will be taken from the last term of the URI. |
| 4089 | 4091 | ** For "http:" and "https:" URIs, you can append an extra term to the end of |
| 4090 | 4092 | ** the URI to get any repository name you like. For example: |
| @@ -4681,17 +4683,10 @@ | ||
| 4681 | 4683 | ** to obtain a check-in lock during auto-sync, the server will |
| 4682 | 4684 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4683 | 4685 | ** which will cause the client to avoid generating a delta |
| 4684 | 4686 | ** manifest. |
| 4685 | 4687 | */ |
| 4686 | -/* | |
| 4687 | -** SETTING: forum-close-policy boolean default=off | |
| 4688 | -** If true, forum moderators may close/re-open forum posts, and reply | |
| 4689 | -** to closed posts. If false, only administrators may do so. Note that | |
| 4690 | -** this only affects the forum web UI, not post-closing tags which | |
| 4691 | -** arrive via the command-line or from synchronization with a remote. | |
| 4692 | -*/ | |
| 4693 | 4688 | /* |
| 4694 | 4689 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4695 | 4690 | ** The value is an external command to run when performing a graphical |
| 4696 | 4691 | ** diff. If undefined, text diff will be used. |
| 4697 | 4692 | */ |
| @@ -4841,15 +4836,19 @@ | ||
| 4841 | 4836 | ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" |
| 4842 | 4837 | ** environment variable is consulted. If "off", a direct HTTP connection is |
| 4843 | 4838 | ** used. |
| 4844 | 4839 | */ |
| 4845 | 4840 | /* |
| 4846 | -** SETTING: redirect-to-https default=0 width=-1 | |
| 4847 | -** Specifies whether or not to redirect http:// requests to | |
| 4848 | -** https:// URIs. A value of 0 (the default) means not to | |
| 4841 | +** SETTING: redirect-to-https default=0 width=2 | |
| 4842 | +** Specifies whether or not to redirect unencrypted "http://" requests to | |
| 4843 | +** encrypted "https://" URIs. A value of 0 (the default) means do not | |
| 4849 | 4844 | ** redirect, 1 means to redirect only the /login page, and 2 |
| 4850 | 4845 | ** means to always redirect. |
| 4846 | +** | |
| 4847 | +** For security, a value of 2 is recommended. The default value is 0 | |
| 4848 | +** because not all sites are TLS-capable. But you should definitely enable | |
| 4849 | +** TLS and change this setting to 2 for all public-facing repositories. | |
| 4851 | 4850 | */ |
| 4852 | 4851 | /* |
| 4853 | 4852 | ** SETTING: relative-paths boolean default=on |
| 4854 | 4853 | ** When showing changes and extras, report paths relative |
| 4855 | 4854 | ** to the current working directory. |
| @@ -4967,11 +4966,11 @@ | ||
| 4967 | 4966 | ** If enabled, special TH1 commands will be called before and |
| 4968 | 4967 | ** after any Fossil command or web page. |
| 4969 | 4968 | */ |
| 4970 | 4969 | #endif |
| 4971 | 4970 | /* |
| 4972 | -** SETTING: th1-setup width=40 block-text | |
| 4971 | +** SETTING: th1-setup width=40 block-text sensitive | |
| 4973 | 4972 | ** This is the setup script to be evaluated after creating |
| 4974 | 4973 | ** and initializing the TH1 interpreter. By default, this |
| 4975 | 4974 | ** is empty and no extra setup is performed. |
| 4976 | 4975 | */ |
| 4977 | 4976 | /* |
| @@ -5358,11 +5357,11 @@ | ||
| 5358 | 5357 | } |
| 5359 | 5358 | |
| 5360 | 5359 | /* |
| 5361 | 5360 | ** Compute a "fingerprint" on the repository. A fingerprint is used |
| 5362 | 5361 | ** to verify that that the repository has not been replaced by a clone |
| 5363 | -** of the same repository. More precisely, a fingerprint are used to | |
| 5362 | +** of the same repository. More precisely, a fingerprint is used to | |
| 5364 | 5363 | ** verify that the mapping between SHA3 hashes and RID values is unchanged. |
| 5365 | 5364 | ** |
| 5366 | 5365 | ** The check-out database ("localdb") stores RID values. When associating |
| 5367 | 5366 | ** a check-out database against a repository database, it is useful to verify |
| 5368 | 5367 | ** the fingerprint so that we know tha the RID values in the check-out |
| @@ -5423,11 +5422,11 @@ | ||
| 5423 | 5422 | ** COMMAND: test-fingerprint |
| 5424 | 5423 | ** |
| 5425 | 5424 | ** Usage: %fossil test-fingerprint ?RCVID? |
| 5426 | 5425 | ** |
| 5427 | 5426 | ** Display the repository fingerprint using the supplied RCVID or |
| 5428 | -** using the latest RCVID if not is given on the command line. | |
| 5427 | +** using the latest RCVID if none is given on the command line. | |
| 5429 | 5428 | ** Show both the legacy and the newer version of the fingerprint, |
| 5430 | 5429 | ** and the currently stored fingerprint if there is one. |
| 5431 | 5430 | */ |
| 5432 | 5431 | void test_fingerprint(void){ |
| 5433 | 5432 | int rcvid = 0; |
| 5434 | 5433 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1557,10 +1557,12 @@ | |
| 1557 | sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0, |
| 1558 | url_nouser_func,0,0); |
| 1559 | sqlite3_create_function(db, "chat_msg_from_event", 4, |
| 1560 | SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, |
| 1561 | chat_msg_from_event, 0, 0); |
| 1562 | |
| 1563 | } |
| 1564 | |
| 1565 | #if USE_SEE |
| 1566 | /* |
| @@ -4080,11 +4082,11 @@ | |
| 4080 | ** specified then that version is checked out. Otherwise the most recent |
| 4081 | ** check-in on the main branch (usually "trunk") is used. |
| 4082 | ** |
| 4083 | ** REPOSITORY can be the filename for a repository that already exists on the |
| 4084 | ** local machine or it can be a URI for a remote repository. If REPOSITORY |
| 4085 | ** is a URI in one of the formats recognized by the [[clone]] command, then |
| 4086 | ** remote repo is first cloned, then the clone is opened. The clone will be |
| 4087 | ** stored in the current directory, or in DIR if the "--repodir DIR" option |
| 4088 | ** is used. The name of the clone will be taken from the last term of the URI. |
| 4089 | ** For "http:" and "https:" URIs, you can append an extra term to the end of |
| 4090 | ** the URI to get any repository name you like. For example: |
| @@ -4681,17 +4683,10 @@ | |
| 4681 | ** to obtain a check-in lock during auto-sync, the server will |
| 4682 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4683 | ** which will cause the client to avoid generating a delta |
| 4684 | ** manifest. |
| 4685 | */ |
| 4686 | /* |
| 4687 | ** SETTING: forum-close-policy boolean default=off |
| 4688 | ** If true, forum moderators may close/re-open forum posts, and reply |
| 4689 | ** to closed posts. If false, only administrators may do so. Note that |
| 4690 | ** this only affects the forum web UI, not post-closing tags which |
| 4691 | ** arrive via the command-line or from synchronization with a remote. |
| 4692 | */ |
| 4693 | /* |
| 4694 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4695 | ** The value is an external command to run when performing a graphical |
| 4696 | ** diff. If undefined, text diff will be used. |
| 4697 | */ |
| @@ -4841,15 +4836,19 @@ | |
| 4841 | ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" |
| 4842 | ** environment variable is consulted. If "off", a direct HTTP connection is |
| 4843 | ** used. |
| 4844 | */ |
| 4845 | /* |
| 4846 | ** SETTING: redirect-to-https default=0 width=-1 |
| 4847 | ** Specifies whether or not to redirect http:// requests to |
| 4848 | ** https:// URIs. A value of 0 (the default) means not to |
| 4849 | ** redirect, 1 means to redirect only the /login page, and 2 |
| 4850 | ** means to always redirect. |
| 4851 | */ |
| 4852 | /* |
| 4853 | ** SETTING: relative-paths boolean default=on |
| 4854 | ** When showing changes and extras, report paths relative |
| 4855 | ** to the current working directory. |
| @@ -4967,11 +4966,11 @@ | |
| 4967 | ** If enabled, special TH1 commands will be called before and |
| 4968 | ** after any Fossil command or web page. |
| 4969 | */ |
| 4970 | #endif |
| 4971 | /* |
| 4972 | ** SETTING: th1-setup width=40 block-text |
| 4973 | ** This is the setup script to be evaluated after creating |
| 4974 | ** and initializing the TH1 interpreter. By default, this |
| 4975 | ** is empty and no extra setup is performed. |
| 4976 | */ |
| 4977 | /* |
| @@ -5358,11 +5357,11 @@ | |
| 5358 | } |
| 5359 | |
| 5360 | /* |
| 5361 | ** Compute a "fingerprint" on the repository. A fingerprint is used |
| 5362 | ** to verify that that the repository has not been replaced by a clone |
| 5363 | ** of the same repository. More precisely, a fingerprint are used to |
| 5364 | ** verify that the mapping between SHA3 hashes and RID values is unchanged. |
| 5365 | ** |
| 5366 | ** The check-out database ("localdb") stores RID values. When associating |
| 5367 | ** a check-out database against a repository database, it is useful to verify |
| 5368 | ** the fingerprint so that we know tha the RID values in the check-out |
| @@ -5423,11 +5422,11 @@ | |
| 5423 | ** COMMAND: test-fingerprint |
| 5424 | ** |
| 5425 | ** Usage: %fossil test-fingerprint ?RCVID? |
| 5426 | ** |
| 5427 | ** Display the repository fingerprint using the supplied RCVID or |
| 5428 | ** using the latest RCVID if not is given on the command line. |
| 5429 | ** Show both the legacy and the newer version of the fingerprint, |
| 5430 | ** and the currently stored fingerprint if there is one. |
| 5431 | */ |
| 5432 | void test_fingerprint(void){ |
| 5433 | int rcvid = 0; |
| 5434 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1557,10 +1557,12 @@ | |
| 1557 | sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0, |
| 1558 | url_nouser_func,0,0); |
| 1559 | sqlite3_create_function(db, "chat_msg_from_event", 4, |
| 1560 | SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, |
| 1561 | chat_msg_from_event, 0, 0); |
| 1562 | sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0, |
| 1563 | file_inode_sql_func,0,0); |
| 1564 | |
| 1565 | } |
| 1566 | |
| 1567 | #if USE_SEE |
| 1568 | /* |
| @@ -4080,11 +4082,11 @@ | |
| 4082 | ** specified then that version is checked out. Otherwise the most recent |
| 4083 | ** check-in on the main branch (usually "trunk") is used. |
| 4084 | ** |
| 4085 | ** REPOSITORY can be the filename for a repository that already exists on the |
| 4086 | ** local machine or it can be a URI for a remote repository. If REPOSITORY |
| 4087 | ** is a URI in one of the formats recognized by the [[clone]] command, the |
| 4088 | ** remote repo is first cloned, then the clone is opened. The clone will be |
| 4089 | ** stored in the current directory, or in DIR if the "--repodir DIR" option |
| 4090 | ** is used. The name of the clone will be taken from the last term of the URI. |
| 4091 | ** For "http:" and "https:" URIs, you can append an extra term to the end of |
| 4092 | ** the URI to get any repository name you like. For example: |
| @@ -4681,17 +4683,10 @@ | |
| 4683 | ** to obtain a check-in lock during auto-sync, the server will |
| 4684 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4685 | ** which will cause the client to avoid generating a delta |
| 4686 | ** manifest. |
| 4687 | */ |
| 4688 | /* |
| 4689 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4690 | ** The value is an external command to run when performing a graphical |
| 4691 | ** diff. If undefined, text diff will be used. |
| 4692 | */ |
| @@ -4841,15 +4836,19 @@ | |
| 4836 | ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" |
| 4837 | ** environment variable is consulted. If "off", a direct HTTP connection is |
| 4838 | ** used. |
| 4839 | */ |
| 4840 | /* |
| 4841 | ** SETTING: redirect-to-https default=0 width=2 |
| 4842 | ** Specifies whether or not to redirect unencrypted "http://" requests to |
| 4843 | ** encrypted "https://" URIs. A value of 0 (the default) means do not |
| 4844 | ** redirect, 1 means to redirect only the /login page, and 2 |
| 4845 | ** means to always redirect. |
| 4846 | ** |
| 4847 | ** For security, a value of 2 is recommended. The default value is 0 |
| 4848 | ** because not all sites are TLS-capable. But you should definitely enable |
| 4849 | ** TLS and change this setting to 2 for all public-facing repositories. |
| 4850 | */ |
| 4851 | /* |
| 4852 | ** SETTING: relative-paths boolean default=on |
| 4853 | ** When showing changes and extras, report paths relative |
| 4854 | ** to the current working directory. |
| @@ -4967,11 +4966,11 @@ | |
| 4966 | ** If enabled, special TH1 commands will be called before and |
| 4967 | ** after any Fossil command or web page. |
| 4968 | */ |
| 4969 | #endif |
| 4970 | /* |
| 4971 | ** SETTING: th1-setup width=40 block-text sensitive |
| 4972 | ** This is the setup script to be evaluated after creating |
| 4973 | ** and initializing the TH1 interpreter. By default, this |
| 4974 | ** is empty and no extra setup is performed. |
| 4975 | */ |
| 4976 | /* |
| @@ -5358,11 +5357,11 @@ | |
| 5357 | } |
| 5358 | |
| 5359 | /* |
| 5360 | ** Compute a "fingerprint" on the repository. A fingerprint is used |
| 5361 | ** to verify that that the repository has not been replaced by a clone |
| 5362 | ** of the same repository. More precisely, a fingerprint is used to |
| 5363 | ** verify that the mapping between SHA3 hashes and RID values is unchanged. |
| 5364 | ** |
| 5365 | ** The check-out database ("localdb") stores RID values. When associating |
| 5366 | ** a check-out database against a repository database, it is useful to verify |
| 5367 | ** the fingerprint so that we know tha the RID values in the check-out |
| @@ -5423,11 +5422,11 @@ | |
| 5422 | ** COMMAND: test-fingerprint |
| 5423 | ** |
| 5424 | ** Usage: %fossil test-fingerprint ?RCVID? |
| 5425 | ** |
| 5426 | ** Display the repository fingerprint using the supplied RCVID or |
| 5427 | ** using the latest RCVID if none is given on the command line. |
| 5428 | ** Show both the legacy and the newer version of the fingerprint, |
| 5429 | ** and the currently stored fingerprint if there is one. |
| 5430 | */ |
| 5431 | void test_fingerprint(void){ |
| 5432 | int rcvid = 0; |
| 5433 |
+52
-10
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -762,10 +762,19 @@ | ||
| 762 | 762 | border-bottom: 3px solid gold; |
| 763 | 763 | } |
| 764 | 764 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 765 | 765 | border-left: 1px solid gold; |
| 766 | 766 | } |
| 767 | +body.cpage-info .file-change-line, | |
| 768 | +body.cpage-vdiff .file-change-line { | |
| 769 | + margin-top: 16px; | |
| 770 | + margin-bottom: 16px; | |
| 771 | + margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; | |
| 772 | + display: flex; | |
| 773 | + flex-direction: row; | |
| 774 | + justify-content: space-between; | |
| 775 | +} | |
| 767 | 776 | |
| 768 | 777 | span.modpending { |
| 769 | 778 | color: #b03800; |
| 770 | 779 | font-style: italic; |
| 771 | 780 | } |
| @@ -919,10 +928,17 @@ | ||
| 919 | 928 | padding: 1px; |
| 920 | 929 | } |
| 921 | 930 | div.forum_body p { |
| 922 | 931 | margin-top: 0; |
| 923 | 932 | } |
| 933 | +div.forum-editor-widget{ | |
| 934 | + display: flex; | |
| 935 | + flex-direction: column; | |
| 936 | +} | |
| 937 | +div.forum-editor-widget > textarea { | |
| 938 | + max-width: initial; | |
| 939 | +} | |
| 924 | 940 | td.form_label { |
| 925 | 941 | vertical-align: top; |
| 926 | 942 | text-align: right; |
| 927 | 943 | } |
| 928 | 944 | .debug { |
| @@ -982,10 +998,17 @@ | ||
| 982 | 998 | ** in the thread view. */ |
| 983 | 999 | } |
| 984 | 1000 | .forum div > form { |
| 985 | 1001 | margin: 0.5em 0; |
| 986 | 1002 | display: inline-block; |
| 1003 | +} | |
| 1004 | +body.cpage-forumedit div > form, | |
| 1005 | +body.cpage-forume2 div > form{ | |
| 1006 | + width: 100%; | |
| 1007 | +} | |
| 1008 | +.forum div > form > * { | |
| 1009 | + margin-bottom: 0.35em; | |
| 987 | 1010 | } |
| 988 | 1011 | .forum-post-collapser { |
| 989 | 1012 | /* Common style for the bottom-of-post and right-of-post |
| 990 | 1013 | expand/collapse widgets. */ |
| 991 | 1014 | font-size: 0.8em; |
| @@ -1681,22 +1704,45 @@ | ||
| 1681 | 1704 | DOM structure: |
| 1682 | 1705 | <DIV.pikchr-wrapper> |
| 1683 | 1706 | <DIV.pikchr-svg> |
| 1684 | 1707 | <SVG.pikchr>...</SVG> |
| 1685 | 1708 | </DIV.pikchr-svg> |
| 1686 | - <PRE.pikchr-src>...</PRE> | |
| 1709 | + <DIV.pikchr-src> | |
| 1710 | + <PRE>pikchr source code</PRE> | |
| 1711 | + <SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> | |
| 1712 | + <!-- ^^^ is unhidden and activated by JS code --> | |
| 1713 | + </DIV.pikchr-src> | |
| 1687 | 1714 | </DIV.pikchr-wrapper> |
| 1688 | 1715 | |
| 1689 | 1716 | ************************************************************/ |
| 1690 | 1717 | div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} |
| 1691 | 1718 | div.pikchr-svg {/*wrapper for SVG.pikchr element*/} |
| 1692 | 1719 | svg.pikchr {/*pikchr SVG*/ |
| 1693 | 1720 | width: 100%/*necessary for SOME SVGs for Chrome!*/; |
| 1694 | 1721 | } |
| 1695 | -pre.pikchr-src {/*source code view for a pikchr (see fossil.pikchr.js)*/ | |
| 1722 | + | |
| 1723 | +div.pikchr-src { | |
| 1724 | + /*Wrapper for source code view of a pikchr (see fossil.pikchr.js)*/ | |
| 1725 | + display: flex; | |
| 1726 | + flex-direction: column; | |
| 1727 | +} | |
| 1728 | +div.pikchr-src > pre { | |
| 1729 | + /*Source code for a pikchr*/ | |
| 1696 | 1730 | box-sizing: border-box; |
| 1697 | 1731 | text-align: left; |
| 1732 | +} | |
| 1733 | +div.pikchr-src > span { | |
| 1734 | + /*Wrapper for a link to open a pikchr in /pikchrshow*/ | |
| 1735 | + margin-top: 0.5em; | |
| 1736 | + margin-bottom: 0.5em; | |
| 1737 | + font-size: 85%; | |
| 1738 | +} | |
| 1739 | +div.pikchr-src > span::before { | |
| 1740 | + content: "["; | |
| 1741 | +} | |
| 1742 | +div.pikchr-src > span::after { | |
| 1743 | + content: "]"; | |
| 1698 | 1744 | } |
| 1699 | 1745 | /* The .source-inline class tells the .source class that the |
| 1700 | 1746 | source view, when enabled, should be "inline" (same position |
| 1701 | 1747 | as the graphic), else the sources are shifted to the left as |
| 1702 | 1748 | if they were "plain text". */ |
| @@ -1712,14 +1758,14 @@ | ||
| 1712 | 1758 | still-seemingly-legitimate browsers don't support grid mode. */ |
| 1713 | 1759 | } |
| 1714 | 1760 | div.pikchr-wrapper.center > div.pikchr-svg { |
| 1715 | 1761 | width: 100%/*necessary for Chrome!*/; |
| 1716 | 1762 | } |
| 1717 | -div.pikchr-wrapper.center:not(.source) > pre.pikchr-src, | |
| 1763 | +div.pikchr-wrapper.center:not(.source) > div.pikchr-src, | |
| 1718 | 1764 | div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, |
| 1719 | 1765 | /* ^^^ Centered non-source-view elements */ |
| 1720 | -div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src, | |
| 1766 | +div.pikchr-wrapper.center.source.source-inline div.pikchr-src, | |
| 1721 | 1767 | div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg |
| 1722 | 1768 | /* ^^^ Centered inline-source-view elements */{ |
| 1723 | 1769 | display:inline-block/*allows parent text-align to do the alignment*/; |
| 1724 | 1770 | /* ^^^^ Browser incompatibility: inline-block causes the centered |
| 1725 | 1771 | pikchr to shrink to the point of illegiblity in Chrome. The |
| @@ -1743,14 +1789,14 @@ | ||
| 1743 | 1789 | padding: 4em; |
| 1744 | 1790 | } |
| 1745 | 1791 | |
| 1746 | 1792 | /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and |
| 1747 | 1793 | svg.pikchr visibility... */ |
| 1748 | -div.pikchr-wrapper.source > pre.pikchr-src { | |
| 1794 | +div.pikchr-wrapper.source > div.pikchr-src { | |
| 1749 | 1795 | /* Source code ^^^^^^^ is visible, else it is hidden */ |
| 1750 | 1796 | } |
| 1751 | -div.pikchr-wrapper:not(.source) > pre.pikchr-src { | |
| 1797 | +div.pikchr-wrapper:not(.source) > div.pikchr-src { | |
| 1752 | 1798 | /* Hide sources when image is being shown. */ |
| 1753 | 1799 | position: absolute !important; |
| 1754 | 1800 | opacity: 0 !important; |
| 1755 | 1801 | pointer-events: none !important; |
| 1756 | 1802 | display: none !important; |
| @@ -1795,14 +1841,10 @@ | ||
| 1795 | 1841 | } |
| 1796 | 1842 | body.fossil-dark-style .settings-icon { |
| 1797 | 1843 | filter: invert(100%); |
| 1798 | 1844 | } |
| 1799 | 1845 | |
| 1800 | -input[type="checkbox"].diff-toggle { | |
| 1801 | - float: right; | |
| 1802 | -} | |
| 1803 | - | |
| 1804 | 1846 | body.branch .brlist > table > tbody > tr:hover:not(.selected), |
| 1805 | 1847 | body.branch .brlist > table > tbody > tr.selected { |
| 1806 | 1848 | background-color: #ffc; |
| 1807 | 1849 | } |
| 1808 | 1850 | body.branch .brlist > table > tbody td:first-child > input { |
| 1809 | 1851 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -762,10 +762,19 @@ | |
| 762 | border-bottom: 3px solid gold; |
| 763 | } |
| 764 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 765 | border-left: 1px solid gold; |
| 766 | } |
| 767 | |
| 768 | span.modpending { |
| 769 | color: #b03800; |
| 770 | font-style: italic; |
| 771 | } |
| @@ -919,10 +928,17 @@ | |
| 919 | padding: 1px; |
| 920 | } |
| 921 | div.forum_body p { |
| 922 | margin-top: 0; |
| 923 | } |
| 924 | td.form_label { |
| 925 | vertical-align: top; |
| 926 | text-align: right; |
| 927 | } |
| 928 | .debug { |
| @@ -982,10 +998,17 @@ | |
| 982 | ** in the thread view. */ |
| 983 | } |
| 984 | .forum div > form { |
| 985 | margin: 0.5em 0; |
| 986 | display: inline-block; |
| 987 | } |
| 988 | .forum-post-collapser { |
| 989 | /* Common style for the bottom-of-post and right-of-post |
| 990 | expand/collapse widgets. */ |
| 991 | font-size: 0.8em; |
| @@ -1681,22 +1704,45 @@ | |
| 1681 | DOM structure: |
| 1682 | <DIV.pikchr-wrapper> |
| 1683 | <DIV.pikchr-svg> |
| 1684 | <SVG.pikchr>...</SVG> |
| 1685 | </DIV.pikchr-svg> |
| 1686 | <PRE.pikchr-src>...</PRE> |
| 1687 | </DIV.pikchr-wrapper> |
| 1688 | |
| 1689 | ************************************************************/ |
| 1690 | div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} |
| 1691 | div.pikchr-svg {/*wrapper for SVG.pikchr element*/} |
| 1692 | svg.pikchr {/*pikchr SVG*/ |
| 1693 | width: 100%/*necessary for SOME SVGs for Chrome!*/; |
| 1694 | } |
| 1695 | pre.pikchr-src {/*source code view for a pikchr (see fossil.pikchr.js)*/ |
| 1696 | box-sizing: border-box; |
| 1697 | text-align: left; |
| 1698 | } |
| 1699 | /* The .source-inline class tells the .source class that the |
| 1700 | source view, when enabled, should be "inline" (same position |
| 1701 | as the graphic), else the sources are shifted to the left as |
| 1702 | if they were "plain text". */ |
| @@ -1712,14 +1758,14 @@ | |
| 1712 | still-seemingly-legitimate browsers don't support grid mode. */ |
| 1713 | } |
| 1714 | div.pikchr-wrapper.center > div.pikchr-svg { |
| 1715 | width: 100%/*necessary for Chrome!*/; |
| 1716 | } |
| 1717 | div.pikchr-wrapper.center:not(.source) > pre.pikchr-src, |
| 1718 | div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, |
| 1719 | /* ^^^ Centered non-source-view elements */ |
| 1720 | div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src, |
| 1721 | div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg |
| 1722 | /* ^^^ Centered inline-source-view elements */{ |
| 1723 | display:inline-block/*allows parent text-align to do the alignment*/; |
| 1724 | /* ^^^^ Browser incompatibility: inline-block causes the centered |
| 1725 | pikchr to shrink to the point of illegiblity in Chrome. The |
| @@ -1743,14 +1789,14 @@ | |
| 1743 | padding: 4em; |
| 1744 | } |
| 1745 | |
| 1746 | /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and |
| 1747 | svg.pikchr visibility... */ |
| 1748 | div.pikchr-wrapper.source > pre.pikchr-src { |
| 1749 | /* Source code ^^^^^^^ is visible, else it is hidden */ |
| 1750 | } |
| 1751 | div.pikchr-wrapper:not(.source) > pre.pikchr-src { |
| 1752 | /* Hide sources when image is being shown. */ |
| 1753 | position: absolute !important; |
| 1754 | opacity: 0 !important; |
| 1755 | pointer-events: none !important; |
| 1756 | display: none !important; |
| @@ -1795,14 +1841,10 @@ | |
| 1795 | } |
| 1796 | body.fossil-dark-style .settings-icon { |
| 1797 | filter: invert(100%); |
| 1798 | } |
| 1799 | |
| 1800 | input[type="checkbox"].diff-toggle { |
| 1801 | float: right; |
| 1802 | } |
| 1803 | |
| 1804 | body.branch .brlist > table > tbody > tr:hover:not(.selected), |
| 1805 | body.branch .brlist > table > tbody > tr.selected { |
| 1806 | background-color: #ffc; |
| 1807 | } |
| 1808 | body.branch .brlist > table > tbody td:first-child > input { |
| 1809 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -762,10 +762,19 @@ | |
| 762 | border-bottom: 3px solid gold; |
| 763 | } |
| 764 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 765 | border-left: 1px solid gold; |
| 766 | } |
| 767 | body.cpage-info .file-change-line, |
| 768 | body.cpage-vdiff .file-change-line { |
| 769 | margin-top: 16px; |
| 770 | margin-bottom: 16px; |
| 771 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 772 | display: flex; |
| 773 | flex-direction: row; |
| 774 | justify-content: space-between; |
| 775 | } |
| 776 | |
| 777 | span.modpending { |
| 778 | color: #b03800; |
| 779 | font-style: italic; |
| 780 | } |
| @@ -919,10 +928,17 @@ | |
| 928 | padding: 1px; |
| 929 | } |
| 930 | div.forum_body p { |
| 931 | margin-top: 0; |
| 932 | } |
| 933 | div.forum-editor-widget{ |
| 934 | display: flex; |
| 935 | flex-direction: column; |
| 936 | } |
| 937 | div.forum-editor-widget > textarea { |
| 938 | max-width: initial; |
| 939 | } |
| 940 | td.form_label { |
| 941 | vertical-align: top; |
| 942 | text-align: right; |
| 943 | } |
| 944 | .debug { |
| @@ -982,10 +998,17 @@ | |
| 998 | ** in the thread view. */ |
| 999 | } |
| 1000 | .forum div > form { |
| 1001 | margin: 0.5em 0; |
| 1002 | display: inline-block; |
| 1003 | } |
| 1004 | body.cpage-forumedit div > form, |
| 1005 | body.cpage-forume2 div > form{ |
| 1006 | width: 100%; |
| 1007 | } |
| 1008 | .forum div > form > * { |
| 1009 | margin-bottom: 0.35em; |
| 1010 | } |
| 1011 | .forum-post-collapser { |
| 1012 | /* Common style for the bottom-of-post and right-of-post |
| 1013 | expand/collapse widgets. */ |
| 1014 | font-size: 0.8em; |
| @@ -1681,22 +1704,45 @@ | |
| 1704 | DOM structure: |
| 1705 | <DIV.pikchr-wrapper> |
| 1706 | <DIV.pikchr-svg> |
| 1707 | <SVG.pikchr>...</SVG> |
| 1708 | </DIV.pikchr-svg> |
| 1709 | <DIV.pikchr-src> |
| 1710 | <PRE>pikchr source code</PRE> |
| 1711 | <SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> |
| 1712 | <!-- ^^^ is unhidden and activated by JS code --> |
| 1713 | </DIV.pikchr-src> |
| 1714 | </DIV.pikchr-wrapper> |
| 1715 | |
| 1716 | ************************************************************/ |
| 1717 | div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} |
| 1718 | div.pikchr-svg {/*wrapper for SVG.pikchr element*/} |
| 1719 | svg.pikchr {/*pikchr SVG*/ |
| 1720 | width: 100%/*necessary for SOME SVGs for Chrome!*/; |
| 1721 | } |
| 1722 | |
| 1723 | div.pikchr-src { |
| 1724 | /*Wrapper for source code view of a pikchr (see fossil.pikchr.js)*/ |
| 1725 | display: flex; |
| 1726 | flex-direction: column; |
| 1727 | } |
| 1728 | div.pikchr-src > pre { |
| 1729 | /*Source code for a pikchr*/ |
| 1730 | box-sizing: border-box; |
| 1731 | text-align: left; |
| 1732 | } |
| 1733 | div.pikchr-src > span { |
| 1734 | /*Wrapper for a link to open a pikchr in /pikchrshow*/ |
| 1735 | margin-top: 0.5em; |
| 1736 | margin-bottom: 0.5em; |
| 1737 | font-size: 85%; |
| 1738 | } |
| 1739 | div.pikchr-src > span::before { |
| 1740 | content: "["; |
| 1741 | } |
| 1742 | div.pikchr-src > span::after { |
| 1743 | content: "]"; |
| 1744 | } |
| 1745 | /* The .source-inline class tells the .source class that the |
| 1746 | source view, when enabled, should be "inline" (same position |
| 1747 | as the graphic), else the sources are shifted to the left as |
| 1748 | if they were "plain text". */ |
| @@ -1712,14 +1758,14 @@ | |
| 1758 | still-seemingly-legitimate browsers don't support grid mode. */ |
| 1759 | } |
| 1760 | div.pikchr-wrapper.center > div.pikchr-svg { |
| 1761 | width: 100%/*necessary for Chrome!*/; |
| 1762 | } |
| 1763 | div.pikchr-wrapper.center:not(.source) > div.pikchr-src, |
| 1764 | div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, |
| 1765 | /* ^^^ Centered non-source-view elements */ |
| 1766 | div.pikchr-wrapper.center.source.source-inline div.pikchr-src, |
| 1767 | div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg |
| 1768 | /* ^^^ Centered inline-source-view elements */{ |
| 1769 | display:inline-block/*allows parent text-align to do the alignment*/; |
| 1770 | /* ^^^^ Browser incompatibility: inline-block causes the centered |
| 1771 | pikchr to shrink to the point of illegiblity in Chrome. The |
| @@ -1743,14 +1789,14 @@ | |
| 1789 | padding: 4em; |
| 1790 | } |
| 1791 | |
| 1792 | /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and |
| 1793 | svg.pikchr visibility... */ |
| 1794 | div.pikchr-wrapper.source > div.pikchr-src { |
| 1795 | /* Source code ^^^^^^^ is visible, else it is hidden */ |
| 1796 | } |
| 1797 | div.pikchr-wrapper:not(.source) > div.pikchr-src { |
| 1798 | /* Hide sources when image is being shown. */ |
| 1799 | position: absolute !important; |
| 1800 | opacity: 0 !important; |
| 1801 | pointer-events: none !important; |
| 1802 | display: none !important; |
| @@ -1795,14 +1841,10 @@ | |
| 1841 | } |
| 1842 | body.fossil-dark-style .settings-icon { |
| 1843 | filter: invert(100%); |
| 1844 | } |
| 1845 | |
| 1846 | body.branch .brlist > table > tbody > tr:hover:not(.selected), |
| 1847 | body.branch .brlist > table > tbody > tr.selected { |
| 1848 | background-color: #ffc; |
| 1849 | } |
| 1850 | body.branch .brlist > table > tbody td:first-child > input { |
| 1851 |
+52
-10
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -762,10 +762,19 @@ | ||
| 762 | 762 | border-bottom: 3px solid gold; |
| 763 | 763 | } |
| 764 | 764 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 765 | 765 | border-left: 1px solid gold; |
| 766 | 766 | } |
| 767 | +body.cpage-info .file-change-line, | |
| 768 | +body.cpage-vdiff .file-change-line { | |
| 769 | + margin-top: 16px; | |
| 770 | + margin-bottom: 16px; | |
| 771 | + margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; | |
| 772 | + display: flex; | |
| 773 | + flex-direction: row; | |
| 774 | + justify-content: space-between; | |
| 775 | +} | |
| 767 | 776 | |
| 768 | 777 | span.modpending { |
| 769 | 778 | color: #b03800; |
| 770 | 779 | font-style: italic; |
| 771 | 780 | } |
| @@ -919,10 +928,17 @@ | ||
| 919 | 928 | padding: 1px; |
| 920 | 929 | } |
| 921 | 930 | div.forum_body p { |
| 922 | 931 | margin-top: 0; |
| 923 | 932 | } |
| 933 | +div.forum-editor-widget{ | |
| 934 | + display: flex; | |
| 935 | + flex-direction: column; | |
| 936 | +} | |
| 937 | +div.forum-editor-widget > textarea { | |
| 938 | + max-width: initial; | |
| 939 | +} | |
| 924 | 940 | td.form_label { |
| 925 | 941 | vertical-align: top; |
| 926 | 942 | text-align: right; |
| 927 | 943 | } |
| 928 | 944 | .debug { |
| @@ -982,10 +998,17 @@ | ||
| 982 | 998 | ** in the thread view. */ |
| 983 | 999 | } |
| 984 | 1000 | .forum div > form { |
| 985 | 1001 | margin: 0.5em 0; |
| 986 | 1002 | display: inline-block; |
| 1003 | +} | |
| 1004 | +body.cpage-forumedit div > form, | |
| 1005 | +body.cpage-forume2 div > form{ | |
| 1006 | + width: 100%; | |
| 1007 | +} | |
| 1008 | +.forum div > form > * { | |
| 1009 | + margin-bottom: 0.35em; | |
| 987 | 1010 | } |
| 988 | 1011 | .forum-post-collapser { |
| 989 | 1012 | /* Common style for the bottom-of-post and right-of-post |
| 990 | 1013 | expand/collapse widgets. */ |
| 991 | 1014 | font-size: 0.8em; |
| @@ -1681,22 +1704,45 @@ | ||
| 1681 | 1704 | DOM structure: |
| 1682 | 1705 | <DIV.pikchr-wrapper> |
| 1683 | 1706 | <DIV.pikchr-svg> |
| 1684 | 1707 | <SVG.pikchr>...</SVG> |
| 1685 | 1708 | </DIV.pikchr-svg> |
| 1686 | - <PRE.pikchr-src>...</PRE> | |
| 1709 | + <DIV.pikchr-src> | |
| 1710 | + <PRE>pikchr source code</PRE> | |
| 1711 | + <SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> | |
| 1712 | + <!-- ^^^ is unhidden and activated by JS code --> | |
| 1713 | + </DIV.pikchr-src> | |
| 1687 | 1714 | </DIV.pikchr-wrapper> |
| 1688 | 1715 | |
| 1689 | 1716 | ************************************************************/ |
| 1690 | 1717 | div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} |
| 1691 | 1718 | div.pikchr-svg {/*wrapper for SVG.pikchr element*/} |
| 1692 | 1719 | svg.pikchr {/*pikchr SVG*/ |
| 1693 | 1720 | width: 100%/*necessary for SOME SVGs for Chrome!*/; |
| 1694 | 1721 | } |
| 1695 | -pre.pikchr-src {/*source code view for a pikchr (see fossil.pikchr.js)*/ | |
| 1722 | + | |
| 1723 | +div.pikchr-src { | |
| 1724 | + /*Wrapper for source code view of a pikchr (see fossil.pikchr.js)*/ | |
| 1725 | + display: flex; | |
| 1726 | + flex-direction: column; | |
| 1727 | +} | |
| 1728 | +div.pikchr-src > pre { | |
| 1729 | + /*Source code for a pikchr*/ | |
| 1696 | 1730 | box-sizing: border-box; |
| 1697 | 1731 | text-align: left; |
| 1732 | +} | |
| 1733 | +div.pikchr-src > span { | |
| 1734 | + /*Wrapper for a link to open a pikchr in /pikchrshow*/ | |
| 1735 | + margin-top: 0.5em; | |
| 1736 | + margin-bottom: 0.5em; | |
| 1737 | + font-size: 85%; | |
| 1738 | +} | |
| 1739 | +div.pikchr-src > span::before { | |
| 1740 | + content: "["; | |
| 1741 | +} | |
| 1742 | +div.pikchr-src > span::after { | |
| 1743 | + content: "]"; | |
| 1698 | 1744 | } |
| 1699 | 1745 | /* The .source-inline class tells the .source class that the |
| 1700 | 1746 | source view, when enabled, should be "inline" (same position |
| 1701 | 1747 | as the graphic), else the sources are shifted to the left as |
| 1702 | 1748 | if they were "plain text". */ |
| @@ -1712,14 +1758,14 @@ | ||
| 1712 | 1758 | still-seemingly-legitimate browsers don't support grid mode. */ |
| 1713 | 1759 | } |
| 1714 | 1760 | div.pikchr-wrapper.center > div.pikchr-svg { |
| 1715 | 1761 | width: 100%/*necessary for Chrome!*/; |
| 1716 | 1762 | } |
| 1717 | -div.pikchr-wrapper.center:not(.source) > pre.pikchr-src, | |
| 1763 | +div.pikchr-wrapper.center:not(.source) > div.pikchr-src, | |
| 1718 | 1764 | div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, |
| 1719 | 1765 | /* ^^^ Centered non-source-view elements */ |
| 1720 | -div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src, | |
| 1766 | +div.pikchr-wrapper.center.source.source-inline div.pikchr-src, | |
| 1721 | 1767 | div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg |
| 1722 | 1768 | /* ^^^ Centered inline-source-view elements */{ |
| 1723 | 1769 | display:inline-block/*allows parent text-align to do the alignment*/; |
| 1724 | 1770 | /* ^^^^ Browser incompatibility: inline-block causes the centered |
| 1725 | 1771 | pikchr to shrink to the point of illegiblity in Chrome. The |
| @@ -1743,14 +1789,14 @@ | ||
| 1743 | 1789 | padding: 4em; |
| 1744 | 1790 | } |
| 1745 | 1791 | |
| 1746 | 1792 | /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and |
| 1747 | 1793 | svg.pikchr visibility... */ |
| 1748 | -div.pikchr-wrapper.source > pre.pikchr-src { | |
| 1794 | +div.pikchr-wrapper.source > div.pikchr-src { | |
| 1749 | 1795 | /* Source code ^^^^^^^ is visible, else it is hidden */ |
| 1750 | 1796 | } |
| 1751 | -div.pikchr-wrapper:not(.source) > pre.pikchr-src { | |
| 1797 | +div.pikchr-wrapper:not(.source) > div.pikchr-src { | |
| 1752 | 1798 | /* Hide sources when image is being shown. */ |
| 1753 | 1799 | position: absolute !important; |
| 1754 | 1800 | opacity: 0 !important; |
| 1755 | 1801 | pointer-events: none !important; |
| 1756 | 1802 | display: none !important; |
| @@ -1795,14 +1841,10 @@ | ||
| 1795 | 1841 | } |
| 1796 | 1842 | body.fossil-dark-style .settings-icon { |
| 1797 | 1843 | filter: invert(100%); |
| 1798 | 1844 | } |
| 1799 | 1845 | |
| 1800 | -input[type="checkbox"].diff-toggle { | |
| 1801 | - float: right; | |
| 1802 | -} | |
| 1803 | - | |
| 1804 | 1846 | body.branch .brlist > table > tbody > tr:hover:not(.selected), |
| 1805 | 1847 | body.branch .brlist > table > tbody > tr.selected { |
| 1806 | 1848 | background-color: #ffc; |
| 1807 | 1849 | } |
| 1808 | 1850 | body.branch .brlist > table > tbody td:first-child > input { |
| 1809 | 1851 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -762,10 +762,19 @@ | |
| 762 | border-bottom: 3px solid gold; |
| 763 | } |
| 764 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 765 | border-left: 1px solid gold; |
| 766 | } |
| 767 | |
| 768 | span.modpending { |
| 769 | color: #b03800; |
| 770 | font-style: italic; |
| 771 | } |
| @@ -919,10 +928,17 @@ | |
| 919 | padding: 1px; |
| 920 | } |
| 921 | div.forum_body p { |
| 922 | margin-top: 0; |
| 923 | } |
| 924 | td.form_label { |
| 925 | vertical-align: top; |
| 926 | text-align: right; |
| 927 | } |
| 928 | .debug { |
| @@ -982,10 +998,17 @@ | |
| 982 | ** in the thread view. */ |
| 983 | } |
| 984 | .forum div > form { |
| 985 | margin: 0.5em 0; |
| 986 | display: inline-block; |
| 987 | } |
| 988 | .forum-post-collapser { |
| 989 | /* Common style for the bottom-of-post and right-of-post |
| 990 | expand/collapse widgets. */ |
| 991 | font-size: 0.8em; |
| @@ -1681,22 +1704,45 @@ | |
| 1681 | DOM structure: |
| 1682 | <DIV.pikchr-wrapper> |
| 1683 | <DIV.pikchr-svg> |
| 1684 | <SVG.pikchr>...</SVG> |
| 1685 | </DIV.pikchr-svg> |
| 1686 | <PRE.pikchr-src>...</PRE> |
| 1687 | </DIV.pikchr-wrapper> |
| 1688 | |
| 1689 | ************************************************************/ |
| 1690 | div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} |
| 1691 | div.pikchr-svg {/*wrapper for SVG.pikchr element*/} |
| 1692 | svg.pikchr {/*pikchr SVG*/ |
| 1693 | width: 100%/*necessary for SOME SVGs for Chrome!*/; |
| 1694 | } |
| 1695 | pre.pikchr-src {/*source code view for a pikchr (see fossil.pikchr.js)*/ |
| 1696 | box-sizing: border-box; |
| 1697 | text-align: left; |
| 1698 | } |
| 1699 | /* The .source-inline class tells the .source class that the |
| 1700 | source view, when enabled, should be "inline" (same position |
| 1701 | as the graphic), else the sources are shifted to the left as |
| 1702 | if they were "plain text". */ |
| @@ -1712,14 +1758,14 @@ | |
| 1712 | still-seemingly-legitimate browsers don't support grid mode. */ |
| 1713 | } |
| 1714 | div.pikchr-wrapper.center > div.pikchr-svg { |
| 1715 | width: 100%/*necessary for Chrome!*/; |
| 1716 | } |
| 1717 | div.pikchr-wrapper.center:not(.source) > pre.pikchr-src, |
| 1718 | div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, |
| 1719 | /* ^^^ Centered non-source-view elements */ |
| 1720 | div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src, |
| 1721 | div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg |
| 1722 | /* ^^^ Centered inline-source-view elements */{ |
| 1723 | display:inline-block/*allows parent text-align to do the alignment*/; |
| 1724 | /* ^^^^ Browser incompatibility: inline-block causes the centered |
| 1725 | pikchr to shrink to the point of illegiblity in Chrome. The |
| @@ -1743,14 +1789,14 @@ | |
| 1743 | padding: 4em; |
| 1744 | } |
| 1745 | |
| 1746 | /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and |
| 1747 | svg.pikchr visibility... */ |
| 1748 | div.pikchr-wrapper.source > pre.pikchr-src { |
| 1749 | /* Source code ^^^^^^^ is visible, else it is hidden */ |
| 1750 | } |
| 1751 | div.pikchr-wrapper:not(.source) > pre.pikchr-src { |
| 1752 | /* Hide sources when image is being shown. */ |
| 1753 | position: absolute !important; |
| 1754 | opacity: 0 !important; |
| 1755 | pointer-events: none !important; |
| 1756 | display: none !important; |
| @@ -1795,14 +1841,10 @@ | |
| 1795 | } |
| 1796 | body.fossil-dark-style .settings-icon { |
| 1797 | filter: invert(100%); |
| 1798 | } |
| 1799 | |
| 1800 | input[type="checkbox"].diff-toggle { |
| 1801 | float: right; |
| 1802 | } |
| 1803 | |
| 1804 | body.branch .brlist > table > tbody > tr:hover:not(.selected), |
| 1805 | body.branch .brlist > table > tbody > tr.selected { |
| 1806 | background-color: #ffc; |
| 1807 | } |
| 1808 | body.branch .brlist > table > tbody td:first-child > input { |
| 1809 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -762,10 +762,19 @@ | |
| 762 | border-bottom: 3px solid gold; |
| 763 | } |
| 764 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 765 | border-left: 1px solid gold; |
| 766 | } |
| 767 | body.cpage-info .file-change-line, |
| 768 | body.cpage-vdiff .file-change-line { |
| 769 | margin-top: 16px; |
| 770 | margin-bottom: 16px; |
| 771 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 772 | display: flex; |
| 773 | flex-direction: row; |
| 774 | justify-content: space-between; |
| 775 | } |
| 776 | |
| 777 | span.modpending { |
| 778 | color: #b03800; |
| 779 | font-style: italic; |
| 780 | } |
| @@ -919,10 +928,17 @@ | |
| 928 | padding: 1px; |
| 929 | } |
| 930 | div.forum_body p { |
| 931 | margin-top: 0; |
| 932 | } |
| 933 | div.forum-editor-widget{ |
| 934 | display: flex; |
| 935 | flex-direction: column; |
| 936 | } |
| 937 | div.forum-editor-widget > textarea { |
| 938 | max-width: initial; |
| 939 | } |
| 940 | td.form_label { |
| 941 | vertical-align: top; |
| 942 | text-align: right; |
| 943 | } |
| 944 | .debug { |
| @@ -982,10 +998,17 @@ | |
| 998 | ** in the thread view. */ |
| 999 | } |
| 1000 | .forum div > form { |
| 1001 | margin: 0.5em 0; |
| 1002 | display: inline-block; |
| 1003 | } |
| 1004 | body.cpage-forumedit div > form, |
| 1005 | body.cpage-forume2 div > form{ |
| 1006 | width: 100%; |
| 1007 | } |
| 1008 | .forum div > form > * { |
| 1009 | margin-bottom: 0.35em; |
| 1010 | } |
| 1011 | .forum-post-collapser { |
| 1012 | /* Common style for the bottom-of-post and right-of-post |
| 1013 | expand/collapse widgets. */ |
| 1014 | font-size: 0.8em; |
| @@ -1681,22 +1704,45 @@ | |
| 1704 | DOM structure: |
| 1705 | <DIV.pikchr-wrapper> |
| 1706 | <DIV.pikchr-svg> |
| 1707 | <SVG.pikchr>...</SVG> |
| 1708 | </DIV.pikchr-svg> |
| 1709 | <DIV.pikchr-src> |
| 1710 | <PRE>pikchr source code</PRE> |
| 1711 | <SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> |
| 1712 | <!-- ^^^ is unhidden and activated by JS code --> |
| 1713 | </DIV.pikchr-src> |
| 1714 | </DIV.pikchr-wrapper> |
| 1715 | |
| 1716 | ************************************************************/ |
| 1717 | div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} |
| 1718 | div.pikchr-svg {/*wrapper for SVG.pikchr element*/} |
| 1719 | svg.pikchr {/*pikchr SVG*/ |
| 1720 | width: 100%/*necessary for SOME SVGs for Chrome!*/; |
| 1721 | } |
| 1722 | |
| 1723 | div.pikchr-src { |
| 1724 | /*Wrapper for source code view of a pikchr (see fossil.pikchr.js)*/ |
| 1725 | display: flex; |
| 1726 | flex-direction: column; |
| 1727 | } |
| 1728 | div.pikchr-src > pre { |
| 1729 | /*Source code for a pikchr*/ |
| 1730 | box-sizing: border-box; |
| 1731 | text-align: left; |
| 1732 | } |
| 1733 | div.pikchr-src > span { |
| 1734 | /*Wrapper for a link to open a pikchr in /pikchrshow*/ |
| 1735 | margin-top: 0.5em; |
| 1736 | margin-bottom: 0.5em; |
| 1737 | font-size: 85%; |
| 1738 | } |
| 1739 | div.pikchr-src > span::before { |
| 1740 | content: "["; |
| 1741 | } |
| 1742 | div.pikchr-src > span::after { |
| 1743 | content: "]"; |
| 1744 | } |
| 1745 | /* The .source-inline class tells the .source class that the |
| 1746 | source view, when enabled, should be "inline" (same position |
| 1747 | as the graphic), else the sources are shifted to the left as |
| 1748 | if they were "plain text". */ |
| @@ -1712,14 +1758,14 @@ | |
| 1758 | still-seemingly-legitimate browsers don't support grid mode. */ |
| 1759 | } |
| 1760 | div.pikchr-wrapper.center > div.pikchr-svg { |
| 1761 | width: 100%/*necessary for Chrome!*/; |
| 1762 | } |
| 1763 | div.pikchr-wrapper.center:not(.source) > div.pikchr-src, |
| 1764 | div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, |
| 1765 | /* ^^^ Centered non-source-view elements */ |
| 1766 | div.pikchr-wrapper.center.source.source-inline div.pikchr-src, |
| 1767 | div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg |
| 1768 | /* ^^^ Centered inline-source-view elements */{ |
| 1769 | display:inline-block/*allows parent text-align to do the alignment*/; |
| 1770 | /* ^^^^ Browser incompatibility: inline-block causes the centered |
| 1771 | pikchr to shrink to the point of illegiblity in Chrome. The |
| @@ -1743,14 +1789,14 @@ | |
| 1789 | padding: 4em; |
| 1790 | } |
| 1791 | |
| 1792 | /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and |
| 1793 | svg.pikchr visibility... */ |
| 1794 | div.pikchr-wrapper.source > div.pikchr-src { |
| 1795 | /* Source code ^^^^^^^ is visible, else it is hidden */ |
| 1796 | } |
| 1797 | div.pikchr-wrapper:not(.source) > div.pikchr-src { |
| 1798 | /* Hide sources when image is being shown. */ |
| 1799 | position: absolute !important; |
| 1800 | opacity: 0 !important; |
| 1801 | pointer-events: none !important; |
| 1802 | display: none !important; |
| @@ -1795,14 +1841,10 @@ | |
| 1841 | } |
| 1842 | body.fossil-dark-style .settings-icon { |
| 1843 | filter: invert(100%); |
| 1844 | } |
| 1845 | |
| 1846 | body.branch .brlist > table > tbody > tr:hover:not(.selected), |
| 1847 | body.branch .brlist > table > tbody > tr.selected { |
| 1848 | background-color: #ffc; |
| 1849 | } |
| 1850 | body.branch .brlist > table > tbody td:first-child > input { |
| 1851 |
+50
-48
| --- src/delta.c | ||
| +++ src/delta.c | ||
| @@ -225,59 +225,61 @@ | ||
| 225 | 225 | ** of four bytes. |
| 226 | 226 | */ |
| 227 | 227 | static unsigned int checksum(const char *zIn, size_t N){ |
| 228 | 228 | static const int byteOrderTest = 1; |
| 229 | 229 | const unsigned char *z = (const unsigned char *)zIn; |
| 230 | - const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3]; | |
| 231 | 230 | unsigned sum = 0; |
| 232 | - assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */ | |
| 233 | - if( 0==*(char*)&byteOrderTest ){ | |
| 234 | - /* This is a big-endian machine */ | |
| 235 | - while( z<zEnd ){ | |
| 236 | - sum += *(unsigned*)z; | |
| 237 | - z += 4; | |
| 238 | - } | |
| 239 | - }else{ | |
| 240 | - /* A little-endian machine */ | |
| 241 | -#if GCC_VERSION>=4003000 | |
| 242 | - while( z<zEnd ){ | |
| 243 | - sum += __builtin_bswap32(*(unsigned*)z); | |
| 244 | - z += 4; | |
| 245 | - } | |
| 246 | -#elif defined(_MSC_VER) && _MSC_VER>=1300 | |
| 247 | - while( z<zEnd ){ | |
| 248 | - sum += _byteswap_ulong(*(unsigned*)z); | |
| 249 | - z += 4; | |
| 250 | - } | |
| 251 | -#else | |
| 252 | - unsigned sum0 = 0; | |
| 253 | - unsigned sum1 = 0; | |
| 254 | - unsigned sum2 = 0; | |
| 255 | - while(N >= 16){ | |
| 256 | - sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); | |
| 257 | - sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); | |
| 258 | - sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); | |
| 259 | - sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); | |
| 260 | - z += 16; | |
| 261 | - N -= 16; | |
| 262 | - } | |
| 263 | - while(N >= 4){ | |
| 264 | - sum0 += z[0]; | |
| 265 | - sum1 += z[1]; | |
| 266 | - sum2 += z[2]; | |
| 267 | - sum += z[3]; | |
| 268 | - z += 4; | |
| 269 | - N -= 4; | |
| 270 | - } | |
| 271 | - sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); | |
| 272 | -#endif | |
| 273 | - } | |
| 274 | - switch(N&3){ | |
| 275 | - case 3: sum += (z[2] << 8); | |
| 276 | - case 2: sum += (z[1] << 16); | |
| 277 | - case 1: sum += (z[0] << 24); | |
| 278 | - default: ; | |
| 231 | + if( N>0 ){ | |
| 232 | + const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3]; | |
| 233 | + assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */ | |
| 234 | + if( 0==*(char*)&byteOrderTest ){ | |
| 235 | + /* This is a big-endian machine */ | |
| 236 | + while( z<zEnd ){ | |
| 237 | + sum += *(unsigned*)z; | |
| 238 | + z += 4; | |
| 239 | + } | |
| 240 | + }else{ | |
| 241 | + /* A little-endian machine */ | |
| 242 | + #if GCC_VERSION>=4003000 | |
| 243 | + while( z<zEnd ){ | |
| 244 | + sum += __builtin_bswap32(*(unsigned*)z); | |
| 245 | + z += 4; | |
| 246 | + } | |
| 247 | + #elif defined(_MSC_VER) && _MSC_VER>=1300 | |
| 248 | + while( z<zEnd ){ | |
| 249 | + sum += _byteswap_ulong(*(unsigned*)z); | |
| 250 | + z += 4; | |
| 251 | + } | |
| 252 | + #else | |
| 253 | + unsigned sum0 = 0; | |
| 254 | + unsigned sum1 = 0; | |
| 255 | + unsigned sum2 = 0; | |
| 256 | + while(N >= 16){ | |
| 257 | + sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); | |
| 258 | + sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); | |
| 259 | + sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); | |
| 260 | + sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); | |
| 261 | + z += 16; | |
| 262 | + N -= 16; | |
| 263 | + } | |
| 264 | + while(N >= 4){ | |
| 265 | + sum0 += z[0]; | |
| 266 | + sum1 += z[1]; | |
| 267 | + sum2 += z[2]; | |
| 268 | + sum += z[3]; | |
| 269 | + z += 4; | |
| 270 | + N -= 4; | |
| 271 | + } | |
| 272 | + sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); | |
| 273 | + #endif | |
| 274 | + } | |
| 275 | + switch(N&3){ | |
| 276 | + case 3: sum += (z[2] << 8); | |
| 277 | + case 2: sum += (z[1] << 16); | |
| 278 | + case 1: sum += (z[0] << 24); | |
| 279 | + default: ; | |
| 280 | + } | |
| 279 | 281 | } |
| 280 | 282 | return sum; |
| 281 | 283 | } |
| 282 | 284 | |
| 283 | 285 | /* |
| 284 | 286 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -225,59 +225,61 @@ | |
| 225 | ** of four bytes. |
| 226 | */ |
| 227 | static unsigned int checksum(const char *zIn, size_t N){ |
| 228 | static const int byteOrderTest = 1; |
| 229 | const unsigned char *z = (const unsigned char *)zIn; |
| 230 | const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3]; |
| 231 | unsigned sum = 0; |
| 232 | assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */ |
| 233 | if( 0==*(char*)&byteOrderTest ){ |
| 234 | /* This is a big-endian machine */ |
| 235 | while( z<zEnd ){ |
| 236 | sum += *(unsigned*)z; |
| 237 | z += 4; |
| 238 | } |
| 239 | }else{ |
| 240 | /* A little-endian machine */ |
| 241 | #if GCC_VERSION>=4003000 |
| 242 | while( z<zEnd ){ |
| 243 | sum += __builtin_bswap32(*(unsigned*)z); |
| 244 | z += 4; |
| 245 | } |
| 246 | #elif defined(_MSC_VER) && _MSC_VER>=1300 |
| 247 | while( z<zEnd ){ |
| 248 | sum += _byteswap_ulong(*(unsigned*)z); |
| 249 | z += 4; |
| 250 | } |
| 251 | #else |
| 252 | unsigned sum0 = 0; |
| 253 | unsigned sum1 = 0; |
| 254 | unsigned sum2 = 0; |
| 255 | while(N >= 16){ |
| 256 | sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); |
| 257 | sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); |
| 258 | sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); |
| 259 | sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); |
| 260 | z += 16; |
| 261 | N -= 16; |
| 262 | } |
| 263 | while(N >= 4){ |
| 264 | sum0 += z[0]; |
| 265 | sum1 += z[1]; |
| 266 | sum2 += z[2]; |
| 267 | sum += z[3]; |
| 268 | z += 4; |
| 269 | N -= 4; |
| 270 | } |
| 271 | sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); |
| 272 | #endif |
| 273 | } |
| 274 | switch(N&3){ |
| 275 | case 3: sum += (z[2] << 8); |
| 276 | case 2: sum += (z[1] << 16); |
| 277 | case 1: sum += (z[0] << 24); |
| 278 | default: ; |
| 279 | } |
| 280 | return sum; |
| 281 | } |
| 282 | |
| 283 | /* |
| 284 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -225,59 +225,61 @@ | |
| 225 | ** of four bytes. |
| 226 | */ |
| 227 | static unsigned int checksum(const char *zIn, size_t N){ |
| 228 | static const int byteOrderTest = 1; |
| 229 | const unsigned char *z = (const unsigned char *)zIn; |
| 230 | unsigned sum = 0; |
| 231 | if( N>0 ){ |
| 232 | const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3]; |
| 233 | assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */ |
| 234 | if( 0==*(char*)&byteOrderTest ){ |
| 235 | /* This is a big-endian machine */ |
| 236 | while( z<zEnd ){ |
| 237 | sum += *(unsigned*)z; |
| 238 | z += 4; |
| 239 | } |
| 240 | }else{ |
| 241 | /* A little-endian machine */ |
| 242 | #if GCC_VERSION>=4003000 |
| 243 | while( z<zEnd ){ |
| 244 | sum += __builtin_bswap32(*(unsigned*)z); |
| 245 | z += 4; |
| 246 | } |
| 247 | #elif defined(_MSC_VER) && _MSC_VER>=1300 |
| 248 | while( z<zEnd ){ |
| 249 | sum += _byteswap_ulong(*(unsigned*)z); |
| 250 | z += 4; |
| 251 | } |
| 252 | #else |
| 253 | unsigned sum0 = 0; |
| 254 | unsigned sum1 = 0; |
| 255 | unsigned sum2 = 0; |
| 256 | while(N >= 16){ |
| 257 | sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); |
| 258 | sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); |
| 259 | sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); |
| 260 | sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); |
| 261 | z += 16; |
| 262 | N -= 16; |
| 263 | } |
| 264 | while(N >= 4){ |
| 265 | sum0 += z[0]; |
| 266 | sum1 += z[1]; |
| 267 | sum2 += z[2]; |
| 268 | sum += z[3]; |
| 269 | z += 4; |
| 270 | N -= 4; |
| 271 | } |
| 272 | sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); |
| 273 | #endif |
| 274 | } |
| 275 | switch(N&3){ |
| 276 | case 3: sum += (z[2] << 8); |
| 277 | case 2: sum += (z[1] << 16); |
| 278 | case 1: sum += (z[0] << 24); |
| 279 | default: ; |
| 280 | } |
| 281 | } |
| 282 | return sum; |
| 283 | } |
| 284 | |
| 285 | /* |
| 286 |
+146
-7
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -50,10 +50,11 @@ | ||
| 50 | 50 | #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ |
| 51 | 51 | #define DIFF_TCL 0x00080000 /* For the --tk option */ |
| 52 | 52 | #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ |
| 53 | 53 | #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ |
| 54 | 54 | #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ |
| 55 | +#define DIFF_BY_TOKEN 0x01000000 /* Split on tokens, not lines */ | |
| 55 | 56 | |
| 56 | 57 | /* |
| 57 | 58 | ** Per file information that may influence output. |
| 58 | 59 | */ |
| 59 | 60 | #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ |
| @@ -319,10 +320,113 @@ | ||
| 319 | 320 | |
| 320 | 321 | /* Return results */ |
| 321 | 322 | *pnLine = nLine; |
| 322 | 323 | return a; |
| 323 | 324 | } |
| 325 | + | |
| 326 | +/* | |
| 327 | +** Character classes for the purpose of tokenization. | |
| 328 | +** | |
| 329 | +** 1 - alphanumeric | |
| 330 | +** 2 - whitespace | |
| 331 | +** 3 - punctuation | |
| 332 | +*/ | |
| 333 | +static char aTCharClass[256] = { | |
| 334 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 335 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 336 | + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | |
| 337 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, | |
| 338 | + | |
| 339 | + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 340 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, | |
| 341 | + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 342 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, | |
| 343 | + | |
| 344 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 345 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 346 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 347 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 348 | + | |
| 349 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 350 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 351 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 352 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 | |
| 353 | +}; | |
| 354 | + | |
| 355 | +/* | |
| 356 | +** Count the number of tokens in the given string. | |
| 357 | +*/ | |
| 358 | +static int count_tokens(const unsigned char *p, int n){ | |
| 359 | + int nToken = 0; | |
| 360 | + int iPrev = 0; | |
| 361 | + int i; | |
| 362 | + for(i=0; i<n; i++){ | |
| 363 | + char x = aTCharClass[p[i]]; | |
| 364 | + if( x!=iPrev ){ | |
| 365 | + iPrev = x; | |
| 366 | + nToken++; | |
| 367 | + } | |
| 368 | + } | |
| 369 | + return nToken; | |
| 370 | +} | |
| 371 | + | |
| 372 | +/* | |
| 373 | +** Return an array of DLine objects containing a pointer to the | |
| 374 | +** start of each token and a hash of that token. The lower | |
| 375 | +** bits of the hash store the length of each token. | |
| 376 | +** | |
| 377 | +** This is like break_into_lines() except that it works with tokens | |
| 378 | +** instead of lines. A token is: | |
| 379 | +** | |
| 380 | +** * A contiguous sequence of alphanumeric characters. | |
| 381 | +** * A contiguous sequence of whitespace | |
| 382 | +** * A contiguous sequence of punctuation characters. | |
| 383 | +** | |
| 384 | +** Return 0 if the file is binary or contains a line that is | |
| 385 | +** too long. | |
| 386 | +*/ | |
| 387 | +static DLine *break_into_tokens( | |
| 388 | + const char *z, | |
| 389 | + int n, | |
| 390 | + int *pnToken, | |
| 391 | + u64 diffFlags | |
| 392 | +){ | |
| 393 | + int nToken, i, k; | |
| 394 | + u64 h, h2; | |
| 395 | + DLine *a; | |
| 396 | + unsigned char *p = (unsigned char*)z; | |
| 397 | + | |
| 398 | + nToken = count_tokens(p, n); | |
| 399 | + a = fossil_malloc( sizeof(a[0])*(nToken+1) ); | |
| 400 | + memset(a, 0, sizeof(a[0])*(nToken+1)); | |
| 401 | + if( n==0 ){ | |
| 402 | + *pnToken = 0; | |
| 403 | + return a; | |
| 404 | + } | |
| 405 | + i = 0; | |
| 406 | + while( n>0 ){ | |
| 407 | + char x = aTCharClass[*p]; | |
| 408 | + h = 0xcbf29ce484222325LL; | |
| 409 | + for(k=1; k<n && aTCharClass[p[k]]==x; k++){ | |
| 410 | + h ^= p[k]; | |
| 411 | + h *= 0x100000001b3LL; | |
| 412 | + } | |
| 413 | + a[i].z = (char*)p; | |
| 414 | + a[i].n = k; | |
| 415 | + a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | k; | |
| 416 | + h2 = h % nToken; | |
| 417 | + a[i].iNext = a[h2].iHash; | |
| 418 | + a[h2].iHash = i+1; | |
| 419 | + p += k; n -= k; | |
| 420 | + i++; | |
| 421 | + }; | |
| 422 | + assert( i==nToken ); | |
| 423 | + | |
| 424 | + /* Return results */ | |
| 425 | + *pnToken = nToken; | |
| 426 | + return a; | |
| 427 | +} | |
| 324 | 428 | |
| 325 | 429 | /* |
| 326 | 430 | ** Return zero if two DLine elements are identical. |
| 327 | 431 | */ |
| 328 | 432 | static int compare_dline(const DLine *pA, const DLine *pB){ |
| @@ -2462,11 +2566,11 @@ | ||
| 2462 | 2566 | int span; /* combined width of the input sequences */ |
| 2463 | 2567 | int cutoff = 4; /* Max hash chain entries to follow */ |
| 2464 | 2568 | int nextCutoff = -1; /* Value of cutoff for next iteration */ |
| 2465 | 2569 | |
| 2466 | 2570 | span = (iE1 - iS1) + (iE2 - iS2); |
| 2467 | - bestScore = -10000; | |
| 2571 | + bestScore = -9223300000*(sqlite3_int64)1000000000; | |
| 2468 | 2572 | score = 0; |
| 2469 | 2573 | iSXb = iSXp = iS1; |
| 2470 | 2574 | iEXb = iEXp = iS1; |
| 2471 | 2575 | iSYb = iSYp = iS2; |
| 2472 | 2576 | iEYb = iEYp = iS2; |
| @@ -2997,14 +3101,21 @@ | ||
| 2997 | 3101 | if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ |
| 2998 | 3102 | c.xDiffer = compare_dline_ignore_allws; |
| 2999 | 3103 | }else{ |
| 3000 | 3104 | c.xDiffer = compare_dline; |
| 3001 | 3105 | } |
| 3002 | - c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), | |
| 3003 | - &c.nFrom, pCfg->diffFlags); | |
| 3004 | - c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), | |
| 3005 | - &c.nTo, pCfg->diffFlags); | |
| 3106 | + if( pCfg->diffFlags & DIFF_BY_TOKEN ){ | |
| 3107 | + c.aFrom = break_into_tokens(blob_str(pA_Blob), blob_size(pA_Blob), | |
| 3108 | + &c.nFrom, pCfg->diffFlags); | |
| 3109 | + c.aTo = break_into_tokens(blob_str(pB_Blob), blob_size(pB_Blob), | |
| 3110 | + &c.nTo, pCfg->diffFlags); | |
| 3111 | + }else{ | |
| 3112 | + c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), | |
| 3113 | + &c.nFrom, pCfg->diffFlags); | |
| 3114 | + c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), | |
| 3115 | + &c.nTo, pCfg->diffFlags); | |
| 3116 | + } | |
| 3006 | 3117 | if( c.aFrom==0 || c.aTo==0 ){ |
| 3007 | 3118 | fossil_free(c.aFrom); |
| 3008 | 3119 | fossil_free(c.aTo); |
| 3009 | 3120 | if( pOut ){ |
| 3010 | 3121 | diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags); |
| @@ -3035,10 +3146,26 @@ | ||
| 3035 | 3146 | } |
| 3036 | 3147 | } |
| 3037 | 3148 | if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){ |
| 3038 | 3149 | diff_optimize(&c); |
| 3039 | 3150 | } |
| 3151 | + if( (pCfg->diffFlags & DIFF_BY_TOKEN)!=0 ){ | |
| 3152 | + /* Convert token counts into byte counts. */ | |
| 3153 | + int i; | |
| 3154 | + int iA = 0; | |
| 3155 | + int iB = 0; | |
| 3156 | + for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ | |
| 3157 | + int k, sum; | |
| 3158 | + for(k=0, sum=0; k<c.aEdit[i]; k++) sum += c.aFrom[iA++].n; | |
| 3159 | + iB += c.aEdit[i]; | |
| 3160 | + c.aEdit[i] = sum; | |
| 3161 | + for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n; | |
| 3162 | + c.aEdit[i+1] = sum; | |
| 3163 | + for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; | |
| 3164 | + c.aEdit[i+2] = sum; | |
| 3165 | + } | |
| 3166 | + } | |
| 3040 | 3167 | |
| 3041 | 3168 | if( pOut ){ |
| 3042 | 3169 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 3043 | 3170 | int nDel = 0, nIns = 0, i; |
| 3044 | 3171 | for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ |
| @@ -3049,11 +3176,11 @@ | ||
| 3049 | 3176 | g.diffCnt[2] += nDel; |
| 3050 | 3177 | if( nIns+nDel ){ |
| 3051 | 3178 | g.diffCnt[0]++; |
| 3052 | 3179 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3053 | 3180 | } |
| 3054 | - }else if( pCfg->diffFlags & DIFF_RAW ){ | |
| 3181 | + }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ | |
| 3055 | 3182 | const int *R = c.aEdit; |
| 3056 | 3183 | unsigned int r; |
| 3057 | 3184 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| 3058 | 3185 | blob_appendf(pOut, " copy %6d delete %6d insert %6d\n", |
| 3059 | 3186 | R[r], R[r+1], R[r+2]); |
| @@ -3100,20 +3227,29 @@ | ||
| 3100 | 3227 | ** Initialize the DiffConfig object using command-line options. |
| 3101 | 3228 | ** |
| 3102 | 3229 | ** Process diff-related command-line options and return an appropriate |
| 3103 | 3230 | ** "diffFlags" integer. |
| 3104 | 3231 | ** |
| 3232 | +** -b|--browser Show the diff output in a web-browser | |
| 3105 | 3233 | ** --brief Show filenames only DIFF_BRIEF |
| 3234 | +** --by Shorthand for "--browser -y" | |
| 3106 | 3235 | ** -c|--context N N lines of context. nContext |
| 3236 | +** --dark Use dark mode for Tcl/Tk and HTML output | |
| 3107 | 3237 | ** --html Format for HTML DIFF_HTML |
| 3238 | +** -i|--internal Use built-in diff, not an external tool | |
| 3108 | 3239 | ** --invert Invert the diff DIFF_INVERT |
| 3240 | +** --json Output formatted as JSON | |
| 3109 | 3241 | ** -n|--linenum Show line numbers DIFF_LINENO |
| 3242 | +** -N|--new-file Alias for --verbose | |
| 3110 | 3243 | ** --noopt Disable optimization DIFF_NOOPT |
| 3111 | 3244 | ** --numstat Show change counts DIFF_NUMSTAT |
| 3112 | 3245 | ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR |
| 3246 | +** --tcl Tcl-formatted output used internally by --tk | |
| 3113 | 3247 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 3248 | +** -v|--verbose Show complete text of added or deleted files | |
| 3114 | 3249 | ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS |
| 3250 | +** --webpage Format output as a stand-alone HTML webpage | |
| 3115 | 3251 | ** -W|--width N N character lines. wColumn |
| 3116 | 3252 | ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE |
| 3117 | 3253 | ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS |
| 3118 | 3254 | */ |
| 3119 | 3255 | void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){ |
| @@ -3157,10 +3293,13 @@ | ||
| 3157 | 3293 | |
| 3158 | 3294 | /* Undocumented and unsupported flags used for development |
| 3159 | 3295 | ** debugging and analysis: */ |
| 3160 | 3296 | if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG; |
| 3161 | 3297 | if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW; |
| 3298 | + if( find_option("bytoken",0,0)!=0 ){ | |
| 3299 | + diffFlags = DIFF_RAW|DIFF_BY_TOKEN; | |
| 3300 | + } | |
| 3162 | 3301 | } |
| 3163 | 3302 | if( (z = find_option("context","c",1))!=0 ){ |
| 3164 | 3303 | char *zEnd; |
| 3165 | 3304 | f = (int)strtol(z, &zEnd, 10); |
| 3166 | 3305 | if( zEnd[0]==0 && errno!=ERANGE ){ |
| @@ -3771,11 +3910,11 @@ | ||
| 3771 | 3910 | int szHash; /* Display size of a version hash */ |
| 3772 | 3911 | Blob treename; /* Name of file to be annotated */ |
| 3773 | 3912 | char *zFilename; /* Name of file to be annotated */ |
| 3774 | 3913 | |
| 3775 | 3914 | bBlame = g.argv[1][0]!='a'; |
| 3776 | - zRevision = find_option("r","revision",1); | |
| 3915 | + zRevision = find_option("revision","r",1); | |
| 3777 | 3916 | zLimit = find_option("limit","n",1); |
| 3778 | 3917 | zOrig = find_option("origin","o",1); |
| 3779 | 3918 | showLog = find_option("log","l",0)!=0; |
| 3780 | 3919 | if( find_option("ignore-trailing-space","Z",0)!=0 ){ |
| 3781 | 3920 | annFlags = DIFF_IGNORE_EOLWS; |
| 3782 | 3921 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -50,10 +50,11 @@ | |
| 50 | #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ |
| 51 | #define DIFF_TCL 0x00080000 /* For the --tk option */ |
| 52 | #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ |
| 53 | #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ |
| 54 | #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ |
| 55 | |
| 56 | /* |
| 57 | ** Per file information that may influence output. |
| 58 | */ |
| 59 | #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ |
| @@ -319,10 +320,113 @@ | |
| 319 | |
| 320 | /* Return results */ |
| 321 | *pnLine = nLine; |
| 322 | return a; |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | ** Return zero if two DLine elements are identical. |
| 327 | */ |
| 328 | static int compare_dline(const DLine *pA, const DLine *pB){ |
| @@ -2462,11 +2566,11 @@ | |
| 2462 | int span; /* combined width of the input sequences */ |
| 2463 | int cutoff = 4; /* Max hash chain entries to follow */ |
| 2464 | int nextCutoff = -1; /* Value of cutoff for next iteration */ |
| 2465 | |
| 2466 | span = (iE1 - iS1) + (iE2 - iS2); |
| 2467 | bestScore = -10000; |
| 2468 | score = 0; |
| 2469 | iSXb = iSXp = iS1; |
| 2470 | iEXb = iEXp = iS1; |
| 2471 | iSYb = iSYp = iS2; |
| 2472 | iEYb = iEYp = iS2; |
| @@ -2997,14 +3101,21 @@ | |
| 2997 | if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ |
| 2998 | c.xDiffer = compare_dline_ignore_allws; |
| 2999 | }else{ |
| 3000 | c.xDiffer = compare_dline; |
| 3001 | } |
| 3002 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| 3003 | &c.nFrom, pCfg->diffFlags); |
| 3004 | c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), |
| 3005 | &c.nTo, pCfg->diffFlags); |
| 3006 | if( c.aFrom==0 || c.aTo==0 ){ |
| 3007 | fossil_free(c.aFrom); |
| 3008 | fossil_free(c.aTo); |
| 3009 | if( pOut ){ |
| 3010 | diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags); |
| @@ -3035,10 +3146,26 @@ | |
| 3035 | } |
| 3036 | } |
| 3037 | if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){ |
| 3038 | diff_optimize(&c); |
| 3039 | } |
| 3040 | |
| 3041 | if( pOut ){ |
| 3042 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 3043 | int nDel = 0, nIns = 0, i; |
| 3044 | for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ |
| @@ -3049,11 +3176,11 @@ | |
| 3049 | g.diffCnt[2] += nDel; |
| 3050 | if( nIns+nDel ){ |
| 3051 | g.diffCnt[0]++; |
| 3052 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3053 | } |
| 3054 | }else if( pCfg->diffFlags & DIFF_RAW ){ |
| 3055 | const int *R = c.aEdit; |
| 3056 | unsigned int r; |
| 3057 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| 3058 | blob_appendf(pOut, " copy %6d delete %6d insert %6d\n", |
| 3059 | R[r], R[r+1], R[r+2]); |
| @@ -3100,20 +3227,29 @@ | |
| 3100 | ** Initialize the DiffConfig object using command-line options. |
| 3101 | ** |
| 3102 | ** Process diff-related command-line options and return an appropriate |
| 3103 | ** "diffFlags" integer. |
| 3104 | ** |
| 3105 | ** --brief Show filenames only DIFF_BRIEF |
| 3106 | ** -c|--context N N lines of context. nContext |
| 3107 | ** --html Format for HTML DIFF_HTML |
| 3108 | ** --invert Invert the diff DIFF_INVERT |
| 3109 | ** -n|--linenum Show line numbers DIFF_LINENO |
| 3110 | ** --noopt Disable optimization DIFF_NOOPT |
| 3111 | ** --numstat Show change counts DIFF_NUMSTAT |
| 3112 | ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR |
| 3113 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 3114 | ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS |
| 3115 | ** -W|--width N N character lines. wColumn |
| 3116 | ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE |
| 3117 | ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS |
| 3118 | */ |
| 3119 | void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){ |
| @@ -3157,10 +3293,13 @@ | |
| 3157 | |
| 3158 | /* Undocumented and unsupported flags used for development |
| 3159 | ** debugging and analysis: */ |
| 3160 | if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG; |
| 3161 | if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW; |
| 3162 | } |
| 3163 | if( (z = find_option("context","c",1))!=0 ){ |
| 3164 | char *zEnd; |
| 3165 | f = (int)strtol(z, &zEnd, 10); |
| 3166 | if( zEnd[0]==0 && errno!=ERANGE ){ |
| @@ -3771,11 +3910,11 @@ | |
| 3771 | int szHash; /* Display size of a version hash */ |
| 3772 | Blob treename; /* Name of file to be annotated */ |
| 3773 | char *zFilename; /* Name of file to be annotated */ |
| 3774 | |
| 3775 | bBlame = g.argv[1][0]!='a'; |
| 3776 | zRevision = find_option("r","revision",1); |
| 3777 | zLimit = find_option("limit","n",1); |
| 3778 | zOrig = find_option("origin","o",1); |
| 3779 | showLog = find_option("log","l",0)!=0; |
| 3780 | if( find_option("ignore-trailing-space","Z",0)!=0 ){ |
| 3781 | annFlags = DIFF_IGNORE_EOLWS; |
| 3782 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -50,10 +50,11 @@ | |
| 50 | #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ |
| 51 | #define DIFF_TCL 0x00080000 /* For the --tk option */ |
| 52 | #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ |
| 53 | #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ |
| 54 | #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ |
| 55 | #define DIFF_BY_TOKEN 0x01000000 /* Split on tokens, not lines */ |
| 56 | |
| 57 | /* |
| 58 | ** Per file information that may influence output. |
| 59 | */ |
| 60 | #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ |
| @@ -319,10 +320,113 @@ | |
| 320 | |
| 321 | /* Return results */ |
| 322 | *pnLine = nLine; |
| 323 | return a; |
| 324 | } |
| 325 | |
| 326 | /* |
| 327 | ** Character classes for the purpose of tokenization. |
| 328 | ** |
| 329 | ** 1 - alphanumeric |
| 330 | ** 2 - whitespace |
| 331 | ** 3 - punctuation |
| 332 | */ |
| 333 | static char aTCharClass[256] = { |
| 334 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 335 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 336 | 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
| 337 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, |
| 338 | |
| 339 | 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 340 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, |
| 341 | 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 342 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, |
| 343 | |
| 344 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 345 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 346 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 347 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 348 | |
| 349 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 350 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 351 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 352 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 |
| 353 | }; |
| 354 | |
| 355 | /* |
| 356 | ** Count the number of tokens in the given string. |
| 357 | */ |
| 358 | static int count_tokens(const unsigned char *p, int n){ |
| 359 | int nToken = 0; |
| 360 | int iPrev = 0; |
| 361 | int i; |
| 362 | for(i=0; i<n; i++){ |
| 363 | char x = aTCharClass[p[i]]; |
| 364 | if( x!=iPrev ){ |
| 365 | iPrev = x; |
| 366 | nToken++; |
| 367 | } |
| 368 | } |
| 369 | return nToken; |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | ** Return an array of DLine objects containing a pointer to the |
| 374 | ** start of each token and a hash of that token. The lower |
| 375 | ** bits of the hash store the length of each token. |
| 376 | ** |
| 377 | ** This is like break_into_lines() except that it works with tokens |
| 378 | ** instead of lines. A token is: |
| 379 | ** |
| 380 | ** * A contiguous sequence of alphanumeric characters. |
| 381 | ** * A contiguous sequence of whitespace |
| 382 | ** * A contiguous sequence of punctuation characters. |
| 383 | ** |
| 384 | ** Return 0 if the file is binary or contains a line that is |
| 385 | ** too long. |
| 386 | */ |
| 387 | static DLine *break_into_tokens( |
| 388 | const char *z, |
| 389 | int n, |
| 390 | int *pnToken, |
| 391 | u64 diffFlags |
| 392 | ){ |
| 393 | int nToken, i, k; |
| 394 | u64 h, h2; |
| 395 | DLine *a; |
| 396 | unsigned char *p = (unsigned char*)z; |
| 397 | |
| 398 | nToken = count_tokens(p, n); |
| 399 | a = fossil_malloc( sizeof(a[0])*(nToken+1) ); |
| 400 | memset(a, 0, sizeof(a[0])*(nToken+1)); |
| 401 | if( n==0 ){ |
| 402 | *pnToken = 0; |
| 403 | return a; |
| 404 | } |
| 405 | i = 0; |
| 406 | while( n>0 ){ |
| 407 | char x = aTCharClass[*p]; |
| 408 | h = 0xcbf29ce484222325LL; |
| 409 | for(k=1; k<n && aTCharClass[p[k]]==x; k++){ |
| 410 | h ^= p[k]; |
| 411 | h *= 0x100000001b3LL; |
| 412 | } |
| 413 | a[i].z = (char*)p; |
| 414 | a[i].n = k; |
| 415 | a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | k; |
| 416 | h2 = h % nToken; |
| 417 | a[i].iNext = a[h2].iHash; |
| 418 | a[h2].iHash = i+1; |
| 419 | p += k; n -= k; |
| 420 | i++; |
| 421 | }; |
| 422 | assert( i==nToken ); |
| 423 | |
| 424 | /* Return results */ |
| 425 | *pnToken = nToken; |
| 426 | return a; |
| 427 | } |
| 428 | |
| 429 | /* |
| 430 | ** Return zero if two DLine elements are identical. |
| 431 | */ |
| 432 | static int compare_dline(const DLine *pA, const DLine *pB){ |
| @@ -2462,11 +2566,11 @@ | |
| 2566 | int span; /* combined width of the input sequences */ |
| 2567 | int cutoff = 4; /* Max hash chain entries to follow */ |
| 2568 | int nextCutoff = -1; /* Value of cutoff for next iteration */ |
| 2569 | |
| 2570 | span = (iE1 - iS1) + (iE2 - iS2); |
| 2571 | bestScore = -9223300000*(sqlite3_int64)1000000000; |
| 2572 | score = 0; |
| 2573 | iSXb = iSXp = iS1; |
| 2574 | iEXb = iEXp = iS1; |
| 2575 | iSYb = iSYp = iS2; |
| 2576 | iEYb = iEYp = iS2; |
| @@ -2997,14 +3101,21 @@ | |
| 3101 | if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ |
| 3102 | c.xDiffer = compare_dline_ignore_allws; |
| 3103 | }else{ |
| 3104 | c.xDiffer = compare_dline; |
| 3105 | } |
| 3106 | if( pCfg->diffFlags & DIFF_BY_TOKEN ){ |
| 3107 | c.aFrom = break_into_tokens(blob_str(pA_Blob), blob_size(pA_Blob), |
| 3108 | &c.nFrom, pCfg->diffFlags); |
| 3109 | c.aTo = break_into_tokens(blob_str(pB_Blob), blob_size(pB_Blob), |
| 3110 | &c.nTo, pCfg->diffFlags); |
| 3111 | }else{ |
| 3112 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| 3113 | &c.nFrom, pCfg->diffFlags); |
| 3114 | c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), |
| 3115 | &c.nTo, pCfg->diffFlags); |
| 3116 | } |
| 3117 | if( c.aFrom==0 || c.aTo==0 ){ |
| 3118 | fossil_free(c.aFrom); |
| 3119 | fossil_free(c.aTo); |
| 3120 | if( pOut ){ |
| 3121 | diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags); |
| @@ -3035,10 +3146,26 @@ | |
| 3146 | } |
| 3147 | } |
| 3148 | if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){ |
| 3149 | diff_optimize(&c); |
| 3150 | } |
| 3151 | if( (pCfg->diffFlags & DIFF_BY_TOKEN)!=0 ){ |
| 3152 | /* Convert token counts into byte counts. */ |
| 3153 | int i; |
| 3154 | int iA = 0; |
| 3155 | int iB = 0; |
| 3156 | for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ |
| 3157 | int k, sum; |
| 3158 | for(k=0, sum=0; k<c.aEdit[i]; k++) sum += c.aFrom[iA++].n; |
| 3159 | iB += c.aEdit[i]; |
| 3160 | c.aEdit[i] = sum; |
| 3161 | for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n; |
| 3162 | c.aEdit[i+1] = sum; |
| 3163 | for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; |
| 3164 | c.aEdit[i+2] = sum; |
| 3165 | } |
| 3166 | } |
| 3167 | |
| 3168 | if( pOut ){ |
| 3169 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 3170 | int nDel = 0, nIns = 0, i; |
| 3171 | for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ |
| @@ -3049,11 +3176,11 @@ | |
| 3176 | g.diffCnt[2] += nDel; |
| 3177 | if( nIns+nDel ){ |
| 3178 | g.diffCnt[0]++; |
| 3179 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3180 | } |
| 3181 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3182 | const int *R = c.aEdit; |
| 3183 | unsigned int r; |
| 3184 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| 3185 | blob_appendf(pOut, " copy %6d delete %6d insert %6d\n", |
| 3186 | R[r], R[r+1], R[r+2]); |
| @@ -3100,20 +3227,29 @@ | |
| 3227 | ** Initialize the DiffConfig object using command-line options. |
| 3228 | ** |
| 3229 | ** Process diff-related command-line options and return an appropriate |
| 3230 | ** "diffFlags" integer. |
| 3231 | ** |
| 3232 | ** -b|--browser Show the diff output in a web-browser |
| 3233 | ** --brief Show filenames only DIFF_BRIEF |
| 3234 | ** --by Shorthand for "--browser -y" |
| 3235 | ** -c|--context N N lines of context. nContext |
| 3236 | ** --dark Use dark mode for Tcl/Tk and HTML output |
| 3237 | ** --html Format for HTML DIFF_HTML |
| 3238 | ** -i|--internal Use built-in diff, not an external tool |
| 3239 | ** --invert Invert the diff DIFF_INVERT |
| 3240 | ** --json Output formatted as JSON |
| 3241 | ** -n|--linenum Show line numbers DIFF_LINENO |
| 3242 | ** -N|--new-file Alias for --verbose |
| 3243 | ** --noopt Disable optimization DIFF_NOOPT |
| 3244 | ** --numstat Show change counts DIFF_NUMSTAT |
| 3245 | ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR |
| 3246 | ** --tcl Tcl-formatted output used internally by --tk |
| 3247 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 3248 | ** -v|--verbose Show complete text of added or deleted files |
| 3249 | ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS |
| 3250 | ** --webpage Format output as a stand-alone HTML webpage |
| 3251 | ** -W|--width N N character lines. wColumn |
| 3252 | ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE |
| 3253 | ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS |
| 3254 | */ |
| 3255 | void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){ |
| @@ -3157,10 +3293,13 @@ | |
| 3293 | |
| 3294 | /* Undocumented and unsupported flags used for development |
| 3295 | ** debugging and analysis: */ |
| 3296 | if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG; |
| 3297 | if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW; |
| 3298 | if( find_option("bytoken",0,0)!=0 ){ |
| 3299 | diffFlags = DIFF_RAW|DIFF_BY_TOKEN; |
| 3300 | } |
| 3301 | } |
| 3302 | if( (z = find_option("context","c",1))!=0 ){ |
| 3303 | char *zEnd; |
| 3304 | f = (int)strtol(z, &zEnd, 10); |
| 3305 | if( zEnd[0]==0 && errno!=ERANGE ){ |
| @@ -3771,11 +3910,11 @@ | |
| 3910 | int szHash; /* Display size of a version hash */ |
| 3911 | Blob treename; /* Name of file to be annotated */ |
| 3912 | char *zFilename; /* Name of file to be annotated */ |
| 3913 | |
| 3914 | bBlame = g.argv[1][0]!='a'; |
| 3915 | zRevision = find_option("revision","r",1); |
| 3916 | zLimit = find_option("limit","n",1); |
| 3917 | zOrig = find_option("origin","o",1); |
| 3918 | showLog = find_option("log","l",0)!=0; |
| 3919 | if( find_option("ignore-trailing-space","Z",0)!=0 ){ |
| 3920 | annFlags = DIFF_IGNORE_EOLWS; |
| 3921 |
+1
-1
| --- src/diff.tcl | ||
| +++ src/diff.tcl | ||
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | 4 | # looks like this: |
| 5 | 5 | # |
| 6 | -# set fossilcmd {| "./fossil" diff --html -y -i -v} | |
| 6 | +# set fossilcmd {| "./fossil" diff --tcl -i -v} | |
| 7 | 7 | # |
| 8 | 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | 9 | # |
| 10 | 10 | set prog { |
| 11 | 11 | package require Tk |
| 12 | 12 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -1,11 +1,11 @@ | |
| 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | # looks like this: |
| 5 | # |
| 6 | # set fossilcmd {| "./fossil" diff --html -y -i -v} |
| 7 | # |
| 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | # |
| 10 | set prog { |
| 11 | package require Tk |
| 12 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -1,11 +1,11 @@ | |
| 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | # looks like this: |
| 5 | # |
| 6 | # set fossilcmd {| "./fossil" diff --tcl -i -v} |
| 7 | # |
| 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | # |
| 10 | set prog { |
| 11 | package require Tk |
| 12 |
+8
-8
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -249,19 +249,19 @@ | ||
| 249 | 249 | @ margin: 0 0 0 0; |
| 250 | 250 | @ line-height: inherit; |
| 251 | 251 | @ font-size: inherit; |
| 252 | 252 | @ } |
| 253 | 253 | @ td.diffln { |
| 254 | -@ width: 1px; | |
| 254 | +@ width: fit-content; | |
| 255 | 255 | @ text-align: right; |
| 256 | 256 | @ padding: 0 1em 0 0; |
| 257 | 257 | @ } |
| 258 | 258 | @ td.difflne { |
| 259 | 259 | @ padding-bottom: 0.4em; |
| 260 | 260 | @ } |
| 261 | 261 | @ td.diffsep { |
| 262 | -@ width: 1px; | |
| 262 | +@ width: fit-content; | |
| 263 | 263 | @ padding: 0 0.3em 0 1em; |
| 264 | 264 | @ line-height: inherit; |
| 265 | 265 | @ font-size: inherit; |
| 266 | 266 | @ } |
| 267 | 267 | @ td.diffsep pre { |
| @@ -379,19 +379,19 @@ | ||
| 379 | 379 | @ margin: 0 0 0 0; |
| 380 | 380 | @ line-height: inherit; |
| 381 | 381 | @ font-size: inherit; |
| 382 | 382 | @ } |
| 383 | 383 | @ td.diffln { |
| 384 | -@ width: 1px; | |
| 384 | +@ width: fit-content; | |
| 385 | 385 | @ text-align: right; |
| 386 | 386 | @ padding: 0 1em 0 0; |
| 387 | 387 | @ } |
| 388 | 388 | @ td.difflne { |
| 389 | 389 | @ padding-bottom: 0.4em; |
| 390 | 390 | @ } |
| 391 | 391 | @ td.diffsep { |
| 392 | -@ width: 1px; | |
| 392 | +@ width: fit-content; | |
| 393 | 393 | @ padding: 0 0.3em 0 1em; |
| 394 | 394 | @ line-height: inherit; |
| 395 | 395 | @ font-size: inherit; |
| 396 | 396 | @ } |
| 397 | 397 | @ td.diffsep pre { |
| @@ -1266,11 +1266,11 @@ | ||
| 1266 | 1266 | ** -n|--linenum Show line numbers |
| 1267 | 1267 | ** -N|--new-file Alias for --verbose |
| 1268 | 1268 | ** --numstat Show only the number of added and deleted lines |
| 1269 | 1269 | ** -y|--side-by-side Side-by-side diff |
| 1270 | 1270 | ** --strip-trailing-cr Strip trailing CR |
| 1271 | -** --tcl Tcl-formated output used internally by --tk | |
| 1271 | +** --tcl Tcl-formatted output used internally by --tk | |
| 1272 | 1272 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1273 | 1273 | ** --tk Launch a Tcl/Tk GUI for display |
| 1274 | 1274 | ** --to VERSION Select VERSION as target for the diff |
| 1275 | 1275 | ** --undo Diff against the "undo" buffer |
| 1276 | 1276 | ** --unified Unified diff |
| @@ -1312,11 +1312,12 @@ | ||
| 1312 | 1312 | zFrom = mprintf("root:%s", zBranch); |
| 1313 | 1313 | } |
| 1314 | 1314 | if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ |
| 1315 | 1315 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1316 | 1316 | } |
| 1317 | - g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; | |
| 1317 | + diff_options(&DCfg, isGDiff, 0); | |
| 1318 | + determine_exec_relative_option(1); | |
| 1318 | 1319 | if( 0==zCheckin ){ |
| 1319 | 1320 | if( zTo==0 || againstUndo ){ |
| 1320 | 1321 | db_must_be_within_tree(); |
| 1321 | 1322 | }else if( zFrom==0 ){ |
| 1322 | 1323 | fossil_fatal("must use --from if --to is present"); |
| @@ -1324,13 +1325,12 @@ | ||
| 1324 | 1325 | db_find_and_open_repository(0, 0); |
| 1325 | 1326 | } |
| 1326 | 1327 | }else{ |
| 1327 | 1328 | db_find_and_open_repository(0, 0); |
| 1328 | 1329 | } |
| 1329 | - diff_options(&DCfg, isGDiff, 0); | |
| 1330 | - determine_exec_relative_option(1); | |
| 1331 | 1330 | verify_all_options(); |
| 1331 | + g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; | |
| 1332 | 1332 | if( g.argc>=3 ){ |
| 1333 | 1333 | int i; |
| 1334 | 1334 | Blob fname; |
| 1335 | 1335 | pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) ); |
| 1336 | 1336 | memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1)); |
| 1337 | 1337 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -249,19 +249,19 @@ | |
| 249 | @ margin: 0 0 0 0; |
| 250 | @ line-height: inherit; |
| 251 | @ font-size: inherit; |
| 252 | @ } |
| 253 | @ td.diffln { |
| 254 | @ width: 1px; |
| 255 | @ text-align: right; |
| 256 | @ padding: 0 1em 0 0; |
| 257 | @ } |
| 258 | @ td.difflne { |
| 259 | @ padding-bottom: 0.4em; |
| 260 | @ } |
| 261 | @ td.diffsep { |
| 262 | @ width: 1px; |
| 263 | @ padding: 0 0.3em 0 1em; |
| 264 | @ line-height: inherit; |
| 265 | @ font-size: inherit; |
| 266 | @ } |
| 267 | @ td.diffsep pre { |
| @@ -379,19 +379,19 @@ | |
| 379 | @ margin: 0 0 0 0; |
| 380 | @ line-height: inherit; |
| 381 | @ font-size: inherit; |
| 382 | @ } |
| 383 | @ td.diffln { |
| 384 | @ width: 1px; |
| 385 | @ text-align: right; |
| 386 | @ padding: 0 1em 0 0; |
| 387 | @ } |
| 388 | @ td.difflne { |
| 389 | @ padding-bottom: 0.4em; |
| 390 | @ } |
| 391 | @ td.diffsep { |
| 392 | @ width: 1px; |
| 393 | @ padding: 0 0.3em 0 1em; |
| 394 | @ line-height: inherit; |
| 395 | @ font-size: inherit; |
| 396 | @ } |
| 397 | @ td.diffsep pre { |
| @@ -1266,11 +1266,11 @@ | |
| 1266 | ** -n|--linenum Show line numbers |
| 1267 | ** -N|--new-file Alias for --verbose |
| 1268 | ** --numstat Show only the number of added and deleted lines |
| 1269 | ** -y|--side-by-side Side-by-side diff |
| 1270 | ** --strip-trailing-cr Strip trailing CR |
| 1271 | ** --tcl Tcl-formated output used internally by --tk |
| 1272 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1273 | ** --tk Launch a Tcl/Tk GUI for display |
| 1274 | ** --to VERSION Select VERSION as target for the diff |
| 1275 | ** --undo Diff against the "undo" buffer |
| 1276 | ** --unified Unified diff |
| @@ -1312,11 +1312,12 @@ | |
| 1312 | zFrom = mprintf("root:%s", zBranch); |
| 1313 | } |
| 1314 | if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ |
| 1315 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1316 | } |
| 1317 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1318 | if( 0==zCheckin ){ |
| 1319 | if( zTo==0 || againstUndo ){ |
| 1320 | db_must_be_within_tree(); |
| 1321 | }else if( zFrom==0 ){ |
| 1322 | fossil_fatal("must use --from if --to is present"); |
| @@ -1324,13 +1325,12 @@ | |
| 1324 | db_find_and_open_repository(0, 0); |
| 1325 | } |
| 1326 | }else{ |
| 1327 | db_find_and_open_repository(0, 0); |
| 1328 | } |
| 1329 | diff_options(&DCfg, isGDiff, 0); |
| 1330 | determine_exec_relative_option(1); |
| 1331 | verify_all_options(); |
| 1332 | if( g.argc>=3 ){ |
| 1333 | int i; |
| 1334 | Blob fname; |
| 1335 | pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) ); |
| 1336 | memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1)); |
| 1337 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -249,19 +249,19 @@ | |
| 249 | @ margin: 0 0 0 0; |
| 250 | @ line-height: inherit; |
| 251 | @ font-size: inherit; |
| 252 | @ } |
| 253 | @ td.diffln { |
| 254 | @ width: fit-content; |
| 255 | @ text-align: right; |
| 256 | @ padding: 0 1em 0 0; |
| 257 | @ } |
| 258 | @ td.difflne { |
| 259 | @ padding-bottom: 0.4em; |
| 260 | @ } |
| 261 | @ td.diffsep { |
| 262 | @ width: fit-content; |
| 263 | @ padding: 0 0.3em 0 1em; |
| 264 | @ line-height: inherit; |
| 265 | @ font-size: inherit; |
| 266 | @ } |
| 267 | @ td.diffsep pre { |
| @@ -379,19 +379,19 @@ | |
| 379 | @ margin: 0 0 0 0; |
| 380 | @ line-height: inherit; |
| 381 | @ font-size: inherit; |
| 382 | @ } |
| 383 | @ td.diffln { |
| 384 | @ width: fit-content; |
| 385 | @ text-align: right; |
| 386 | @ padding: 0 1em 0 0; |
| 387 | @ } |
| 388 | @ td.difflne { |
| 389 | @ padding-bottom: 0.4em; |
| 390 | @ } |
| 391 | @ td.diffsep { |
| 392 | @ width: fit-content; |
| 393 | @ padding: 0 0.3em 0 1em; |
| 394 | @ line-height: inherit; |
| 395 | @ font-size: inherit; |
| 396 | @ } |
| 397 | @ td.diffsep pre { |
| @@ -1266,11 +1266,11 @@ | |
| 1266 | ** -n|--linenum Show line numbers |
| 1267 | ** -N|--new-file Alias for --verbose |
| 1268 | ** --numstat Show only the number of added and deleted lines |
| 1269 | ** -y|--side-by-side Side-by-side diff |
| 1270 | ** --strip-trailing-cr Strip trailing CR |
| 1271 | ** --tcl Tcl-formatted output used internally by --tk |
| 1272 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1273 | ** --tk Launch a Tcl/Tk GUI for display |
| 1274 | ** --to VERSION Select VERSION as target for the diff |
| 1275 | ** --undo Diff against the "undo" buffer |
| 1276 | ** --unified Unified diff |
| @@ -1312,11 +1312,12 @@ | |
| 1312 | zFrom = mprintf("root:%s", zBranch); |
| 1313 | } |
| 1314 | if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ |
| 1315 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1316 | } |
| 1317 | diff_options(&DCfg, isGDiff, 0); |
| 1318 | determine_exec_relative_option(1); |
| 1319 | if( 0==zCheckin ){ |
| 1320 | if( zTo==0 || againstUndo ){ |
| 1321 | db_must_be_within_tree(); |
| 1322 | }else if( zFrom==0 ){ |
| 1323 | fossil_fatal("must use --from if --to is present"); |
| @@ -1324,13 +1325,12 @@ | |
| 1325 | db_find_and_open_repository(0, 0); |
| 1326 | } |
| 1327 | }else{ |
| 1328 | db_find_and_open_repository(0, 0); |
| 1329 | } |
| 1330 | verify_all_options(); |
| 1331 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1332 | if( g.argc>=3 ){ |
| 1333 | int i; |
| 1334 | Blob fname; |
| 1335 | pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) ); |
| 1336 | memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1)); |
| 1337 |
+26
-1
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -1307,17 +1307,42 @@ | ||
| 1307 | 1307 | } |
| 1308 | 1308 | |
| 1309 | 1309 | /* |
| 1310 | 1310 | ** Return a pointer to the setting information array. |
| 1311 | 1311 | ** |
| 1312 | -** This routine provides access to the aSetting2[] array which is created | |
| 1312 | +** This routine provides access to the aSetting[] array which is created | |
| 1313 | 1313 | ** by the mkindex utility program and included with <page_index.h>. |
| 1314 | 1314 | */ |
| 1315 | 1315 | const Setting *setting_info(int *pnCount){ |
| 1316 | 1316 | if( pnCount ) *pnCount = (int)(sizeof(aSetting)/sizeof(aSetting[0])) - 1; |
| 1317 | 1317 | return aSetting; |
| 1318 | 1318 | } |
| 1319 | + | |
| 1320 | +/* | |
| 1321 | +** Return a pointer to a specific Setting entry for the setting named | |
| 1322 | +** in the argument. Or return NULL if no such setting exists. | |
| 1323 | +** | |
| 1324 | +** The pointer returned points into the middle of the global aSetting[] | |
| 1325 | +** array that is generated by mkindex. Use setting_info() to fetch the | |
| 1326 | +** whole array. Use this routine to fetch a specific entry. | |
| 1327 | +*/ | |
| 1328 | +const Setting *setting_find(const char *zName){ | |
| 1329 | + int iFirst = 0; | |
| 1330 | + int iLast = ArraySize(aSetting)-1; | |
| 1331 | + while( iFirst<=iLast ){ | |
| 1332 | + int iCur = (iFirst+iLast)/2; | |
| 1333 | + int c = strcmp(aSetting[iCur].name, zName); | |
| 1334 | + if( c<0 ){ | |
| 1335 | + iFirst = iCur+1; | |
| 1336 | + }else if( c>0 ){ | |
| 1337 | + iLast = iCur-1; | |
| 1338 | + }else{ | |
| 1339 | + return &aSetting[iCur]; | |
| 1340 | + } | |
| 1341 | + } | |
| 1342 | + return 0; | |
| 1343 | +} | |
| 1319 | 1344 | |
| 1320 | 1345 | /***************************************************************************** |
| 1321 | 1346 | ** A virtual table for accessing the information in aCommand[], and |
| 1322 | 1347 | ** especially the help-text |
| 1323 | 1348 | */ |
| 1324 | 1349 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -1307,17 +1307,42 @@ | |
| 1307 | } |
| 1308 | |
| 1309 | /* |
| 1310 | ** Return a pointer to the setting information array. |
| 1311 | ** |
| 1312 | ** This routine provides access to the aSetting2[] array which is created |
| 1313 | ** by the mkindex utility program and included with <page_index.h>. |
| 1314 | */ |
| 1315 | const Setting *setting_info(int *pnCount){ |
| 1316 | if( pnCount ) *pnCount = (int)(sizeof(aSetting)/sizeof(aSetting[0])) - 1; |
| 1317 | return aSetting; |
| 1318 | } |
| 1319 | |
| 1320 | /***************************************************************************** |
| 1321 | ** A virtual table for accessing the information in aCommand[], and |
| 1322 | ** especially the help-text |
| 1323 | */ |
| 1324 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -1307,17 +1307,42 @@ | |
| 1307 | } |
| 1308 | |
| 1309 | /* |
| 1310 | ** Return a pointer to the setting information array. |
| 1311 | ** |
| 1312 | ** This routine provides access to the aSetting[] array which is created |
| 1313 | ** by the mkindex utility program and included with <page_index.h>. |
| 1314 | */ |
| 1315 | const Setting *setting_info(int *pnCount){ |
| 1316 | if( pnCount ) *pnCount = (int)(sizeof(aSetting)/sizeof(aSetting[0])) - 1; |
| 1317 | return aSetting; |
| 1318 | } |
| 1319 | |
| 1320 | /* |
| 1321 | ** Return a pointer to a specific Setting entry for the setting named |
| 1322 | ** in the argument. Or return NULL if no such setting exists. |
| 1323 | ** |
| 1324 | ** The pointer returned points into the middle of the global aSetting[] |
| 1325 | ** array that is generated by mkindex. Use setting_info() to fetch the |
| 1326 | ** whole array. Use this routine to fetch a specific entry. |
| 1327 | */ |
| 1328 | const Setting *setting_find(const char *zName){ |
| 1329 | int iFirst = 0; |
| 1330 | int iLast = ArraySize(aSetting)-1; |
| 1331 | while( iFirst<=iLast ){ |
| 1332 | int iCur = (iFirst+iLast)/2; |
| 1333 | int c = strcmp(aSetting[iCur].name, zName); |
| 1334 | if( c<0 ){ |
| 1335 | iFirst = iCur+1; |
| 1336 | }else if( c>0 ){ |
| 1337 | iLast = iCur-1; |
| 1338 | }else{ |
| 1339 | return &aSetting[iCur]; |
| 1340 | } |
| 1341 | } |
| 1342 | return 0; |
| 1343 | } |
| 1344 | |
| 1345 | /***************************************************************************** |
| 1346 | ** A virtual table for accessing the information in aCommand[], and |
| 1347 | ** especially the help-text |
| 1348 | */ |
| 1349 |
+2
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -305,10 +305,12 @@ | ||
| 305 | 305 | { "xlsx", 4, "application/vnd.openxmlformats-" |
| 306 | 306 | "officedocument.spreadsheetml.sheet"}, |
| 307 | 307 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 308 | 308 | { "xml", 3, "text/xml" }, |
| 309 | 309 | { "xpm", 3, "image/x-xpixmap" }, |
| 310 | + { "xsl", 3, "text/xml" }, | |
| 311 | + { "xslt", 4, "text/xml" }, | |
| 310 | 312 | { "xwd", 3, "image/x-xwindowdump" }, |
| 311 | 313 | { "xyz", 3, "chemical/x-pdb" }, |
| 312 | 314 | { "zip", 3, "application/zip" }, |
| 313 | 315 | }; |
| 314 | 316 | |
| 315 | 317 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -305,10 +305,12 @@ | |
| 305 | { "xlsx", 4, "application/vnd.openxmlformats-" |
| 306 | "officedocument.spreadsheetml.sheet"}, |
| 307 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 308 | { "xml", 3, "text/xml" }, |
| 309 | { "xpm", 3, "image/x-xpixmap" }, |
| 310 | { "xwd", 3, "image/x-xwindowdump" }, |
| 311 | { "xyz", 3, "chemical/x-pdb" }, |
| 312 | { "zip", 3, "application/zip" }, |
| 313 | }; |
| 314 | |
| 315 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -305,10 +305,12 @@ | |
| 305 | { "xlsx", 4, "application/vnd.openxmlformats-" |
| 306 | "officedocument.spreadsheetml.sheet"}, |
| 307 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 308 | { "xml", 3, "text/xml" }, |
| 309 | { "xpm", 3, "image/x-xpixmap" }, |
| 310 | { "xsl", 3, "text/xml" }, |
| 311 | { "xslt", 4, "text/xml" }, |
| 312 | { "xwd", 3, "image/x-xwindowdump" }, |
| 313 | { "xyz", 3, "chemical/x-pdb" }, |
| 314 | { "zip", 3, "application/zip" }, |
| 315 | }; |
| 316 | |
| 317 |
+1
-1
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -229,11 +229,11 @@ | ||
| 229 | 229 | } |
| 230 | 230 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 231 | 231 | " FROM tag" |
| 232 | 232 | " WHERE tagname GLOB 'event-%q*'", |
| 233 | 233 | zId); |
| 234 | - attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); | |
| 234 | + attachment_list(zFullId, "<h2>Attachments:</h2>", 1); | |
| 235 | 235 | document_emit_js(); |
| 236 | 236 | style_finish_page(); |
| 237 | 237 | manifest_destroy(pTNote); |
| 238 | 238 | } |
| 239 | 239 | |
| 240 | 240 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -229,11 +229,11 @@ | |
| 229 | } |
| 230 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 231 | " FROM tag" |
| 232 | " WHERE tagname GLOB 'event-%q*'", |
| 233 | zId); |
| 234 | attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); |
| 235 | document_emit_js(); |
| 236 | style_finish_page(); |
| 237 | manifest_destroy(pTNote); |
| 238 | } |
| 239 | |
| 240 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -229,11 +229,11 @@ | |
| 229 | } |
| 230 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 231 | " FROM tag" |
| 232 | " WHERE tagname GLOB 'event-%q*'", |
| 233 | zId); |
| 234 | attachment_list(zFullId, "<h2>Attachments:</h2>", 1); |
| 235 | document_emit_js(); |
| 236 | style_finish_page(); |
| 237 | manifest_destroy(pTNote); |
| 238 | } |
| 239 | |
| 240 |
+99
-9
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1302,25 +1302,37 @@ | ||
| 1302 | 1302 | } |
| 1303 | 1303 | } |
| 1304 | 1304 | |
| 1305 | 1305 | /* |
| 1306 | 1306 | ** Compute a canonical pathname for a file or directory. |
| 1307 | -** Make the name absolute if it is relative. | |
| 1308 | -** Remove redundant / characters | |
| 1309 | -** Remove all /./ path elements. | |
| 1310 | -** Convert /A/../ to just / | |
| 1307 | +** | |
| 1308 | +** * Make the name absolute if it is relative. | |
| 1309 | +** * Remove redundant / characters | |
| 1310 | +** * Remove all /./ path elements. | |
| 1311 | +** * Convert /A/../ to just / | |
| 1312 | +** * On windows, add the drive letter prefix. | |
| 1313 | +** | |
| 1311 | 1314 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1312 | 1315 | ** is retained. |
| 1313 | 1316 | ** |
| 1314 | 1317 | ** See also: file_canonical_name_dup() |
| 1315 | 1318 | */ |
| 1316 | 1319 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1320 | + char zPwd[2000]; | |
| 1317 | 1321 | blob_zero(pOut); |
| 1318 | 1322 | if( file_is_absolute_path(zOrigName) ){ |
| 1319 | - blob_appendf(pOut, "%/", zOrigName); | |
| 1323 | +#if defined(_WIN32) | |
| 1324 | + if( fossil_isdirsep(zOrigName[0]) ){ | |
| 1325 | + /* Add the drive letter to the full pathname */ | |
| 1326 | + file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); | |
| 1327 | + blob_appendf(pOut, "%.2s%/", zPwd, zOrigName); | |
| 1328 | + }else | |
| 1329 | +#endif | |
| 1330 | + { | |
| 1331 | + blob_appendf(pOut, "%/", zOrigName); | |
| 1332 | + } | |
| 1320 | 1333 | }else{ |
| 1321 | - char zPwd[2000]; | |
| 1322 | 1334 | file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); |
| 1323 | 1335 | if( zPwd[0]=='/' && strlen(zPwd)==1 ){ |
| 1324 | 1336 | /* when on '/', don't add an extra '/' */ |
| 1325 | 1337 | if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){ |
| 1326 | 1338 | /* '.' when on '/' mean '/' */ |
| @@ -1372,10 +1384,11 @@ | ||
| 1372 | 1384 | ** just fossil_strdup(). But for case-insenstiive but "case preserving" |
| 1373 | 1385 | ** filesystems, such as on MacOS or Windows, we want the filename to be |
| 1374 | 1386 | ** in the preserved casing. That's what this routine does. |
| 1375 | 1387 | */ |
| 1376 | 1388 | char *file_case_preferred_name(const char *zDir, const char *zPath){ |
| 1389 | +#ifndef _WIN32 /* Call win32_file_case_preferred_name() on Windows. */ | |
| 1377 | 1390 | DIR *d; |
| 1378 | 1391 | int i; |
| 1379 | 1392 | char *zResult = 0; |
| 1380 | 1393 | void *zNative = 0; |
| 1381 | 1394 | |
| @@ -1407,10 +1420,13 @@ | ||
| 1407 | 1420 | closedir(d); |
| 1408 | 1421 | } |
| 1409 | 1422 | fossil_path_free(zNative); |
| 1410 | 1423 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1411 | 1424 | return zResult; |
| 1425 | +#else /* !_WIN32 */ | |
| 1426 | + return win32_file_case_preferred_name(zDir,zPath); | |
| 1427 | +#endif /* !_WIN32 */ | |
| 1412 | 1428 | } |
| 1413 | 1429 | |
| 1414 | 1430 | /* |
| 1415 | 1431 | ** COMMAND: test-case-filename |
| 1416 | 1432 | ** |
| @@ -1637,13 +1653,16 @@ | ||
| 1637 | 1653 | fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1638 | 1654 | filenames_are_case_sensitive()); |
| 1639 | 1655 | if( zAllow ){ |
| 1640 | 1656 | g.allowSymlinks = !is_false(zAllow); |
| 1641 | 1657 | } |
| 1642 | - if( zRoot==0 ) zRoot = g.zLocalRoot; | |
| 1658 | + if( zRoot==0 ) zRoot = g.zLocalRoot==0 ? "" : g.zLocalRoot; | |
| 1643 | 1659 | fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); |
| 1644 | 1660 | fossil_print("local-root = [%s]\n", zRoot); |
| 1661 | + if( g.db==0 ) sqlite3_open(":memory:", &g.db); | |
| 1662 | + sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0, | |
| 1663 | + file_inode_sql_func, 0, 0); | |
| 1645 | 1664 | for(i=2; i<g.argc; i++){ |
| 1646 | 1665 | char *z; |
| 1647 | 1666 | emitFileStat(g.argv[i], slashFlag, resetFlag); |
| 1648 | 1667 | z = file_canonical_name_dup(g.argv[i]); |
| 1649 | 1668 | fossil_print(" file_canonical_name = %s\n", z); |
| @@ -1653,10 +1672,13 @@ | ||
| 1653 | 1672 | }else{ |
| 1654 | 1673 | int n = file_nondir_objects_on_path(zRoot, z); |
| 1655 | 1674 | fossil_print("%.*s\n", n, z); |
| 1656 | 1675 | } |
| 1657 | 1676 | fossil_free(z); |
| 1677 | + z = db_text(0, "SELECT inode(%Q)", g.argv[i]); | |
| 1678 | + fossil_print(" file_inode_sql_func = \"%s\"\n", z); | |
| 1679 | + fossil_free(z); | |
| 1658 | 1680 | } |
| 1659 | 1681 | } |
| 1660 | 1682 | |
| 1661 | 1683 | /* |
| 1662 | 1684 | ** COMMAND: test-canonical-name |
| @@ -1695,13 +1717,15 @@ | ||
| 1695 | 1717 | ** Canonical names are full pathnames using "/" not "\" and which |
| 1696 | 1718 | ** contain no "/./" or "/../" terms. |
| 1697 | 1719 | */ |
| 1698 | 1720 | int file_is_canonical(const char *z){ |
| 1699 | 1721 | int i; |
| 1700 | - if( z[0]!='/' | |
| 1722 | + if( | |
| 1701 | 1723 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1702 | - && (!fossil_isupper(z[0]) || z[1]!=':' || z[2]!='/') | |
| 1724 | + !fossil_isupper(z[0]) || z[1]!=':' || !fossil_isdirsep(z[2]) | |
| 1725 | +#else | |
| 1726 | + z[0]!='/' | |
| 1703 | 1727 | #endif |
| 1704 | 1728 | ) return 0; |
| 1705 | 1729 | |
| 1706 | 1730 | for(i=0; z[i]; i++){ |
| 1707 | 1731 | if( z[i]=='\\' ) return 0; |
| @@ -2949,18 +2973,84 @@ | ||
| 2949 | 2973 | ** Returns 1 if the given directory contains a file named .fslckout, 2 |
| 2950 | 2974 | ** if it contains a file named _FOSSIL_, else returns 0. |
| 2951 | 2975 | */ |
| 2952 | 2976 | int dir_has_ckout_db(const char *zDir){ |
| 2953 | 2977 | int rc = 0; |
| 2978 | + i64 sz; | |
| 2954 | 2979 | char * zCkoutDb = mprintf("%//.fslckout", zDir); |
| 2955 | 2980 | if(file_isfile(zCkoutDb, ExtFILE)){ |
| 2956 | 2981 | rc = 1; |
| 2957 | 2982 | }else{ |
| 2958 | 2983 | fossil_free(zCkoutDb); |
| 2959 | 2984 | zCkoutDb = mprintf("%//_FOSSIL_", zDir); |
| 2960 | 2985 | if(file_isfile(zCkoutDb, ExtFILE)){ |
| 2961 | 2986 | rc = 2; |
| 2962 | 2987 | } |
| 2988 | + } | |
| 2989 | + if( rc && ((sz = file_size(zCkoutDb, ExtFILE))<1024 || (sz%512)!=0) ){ | |
| 2990 | + rc = 0; | |
| 2963 | 2991 | } |
| 2964 | 2992 | fossil_free(zCkoutDb); |
| 2965 | 2993 | return rc; |
| 2966 | 2994 | } |
| 2995 | + | |
| 2996 | +/* | |
| 2997 | +** This is the implementation of inode(FILENAME) SQL function. | |
| 2998 | +** | |
| 2999 | +** dev_inode(FILENAME) returns a string. If FILENAME exists and is | |
| 3000 | +** a regular file, then the return string is of the form: | |
| 3001 | +** | |
| 3002 | +** DEV/INODE | |
| 3003 | +** | |
| 3004 | +** Where DEV and INODE are the device number and inode number for | |
| 3005 | +** the file. On Windows, the volume serial number (DEV) and file | |
| 3006 | +** identifier (INODE) are used to compute the value, see comments | |
| 3007 | +** on the win32_file_id() function. | |
| 3008 | +** | |
| 3009 | +** If FILENAME does not exist, then the return is an empty string. | |
| 3010 | +** | |
| 3011 | +** The value of inode() can be used to eliminate files from a list | |
| 3012 | +** that have duplicates because they have differing names due to links. | |
| 3013 | +** | |
| 3014 | +** Code that wants to use this SQL function needs to first register | |
| 3015 | +** it using a call such as the following: | |
| 3016 | +** | |
| 3017 | +** sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0, | |
| 3018 | +** file_inode_sql_func, 0, 0); | |
| 3019 | +*/ | |
| 3020 | +void file_inode_sql_func( | |
| 3021 | + sqlite3_context *context, | |
| 3022 | + int argc, | |
| 3023 | + sqlite3_value **argv | |
| 3024 | +){ | |
| 3025 | + const char *zFilename; | |
| 3026 | + assert( argc==1 ); | |
| 3027 | + zFilename = (const char*)sqlite3_value_text(argv[0]); | |
| 3028 | + if( zFilename==0 || zFilename[0]==0 || file_access(zFilename,F_OK) ){ | |
| 3029 | + sqlite3_result_text(context, "", 0, SQLITE_STATIC); | |
| 3030 | + return; | |
| 3031 | + } | |
| 3032 | +#if defined(_WIN32) | |
| 3033 | + { | |
| 3034 | + char *zFileId = win32_file_id(zFilename); | |
| 3035 | + if( zFileId ){ | |
| 3036 | + sqlite3_result_text(context, zFileId, -1, fossil_free); | |
| 3037 | + }else{ | |
| 3038 | + sqlite3_result_text(context, "", 0, SQLITE_STATIC); | |
| 3039 | + } | |
| 3040 | + } | |
| 3041 | +#else | |
| 3042 | + { | |
| 3043 | + struct stat buf; | |
| 3044 | + int rc; | |
| 3045 | + memset(&buf, 0, sizeof(buf)); | |
| 3046 | + rc = stat(zFilename, &buf); | |
| 3047 | + if( rc ){ | |
| 3048 | + sqlite3_result_text(context, "", 0, SQLITE_STATIC); | |
| 3049 | + }else{ | |
| 3050 | + sqlite3_result_text(context, | |
| 3051 | + mprintf("%lld/%lld", (i64)buf.st_dev, (i64)buf.st_ino), -1, | |
| 3052 | + fossil_free); | |
| 3053 | + } | |
| 3054 | + } | |
| 3055 | +#endif | |
| 3056 | +} | |
| 2967 | 3057 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1302,25 +1302,37 @@ | |
| 1302 | } |
| 1303 | } |
| 1304 | |
| 1305 | /* |
| 1306 | ** Compute a canonical pathname for a file or directory. |
| 1307 | ** Make the name absolute if it is relative. |
| 1308 | ** Remove redundant / characters |
| 1309 | ** Remove all /./ path elements. |
| 1310 | ** Convert /A/../ to just / |
| 1311 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1312 | ** is retained. |
| 1313 | ** |
| 1314 | ** See also: file_canonical_name_dup() |
| 1315 | */ |
| 1316 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1317 | blob_zero(pOut); |
| 1318 | if( file_is_absolute_path(zOrigName) ){ |
| 1319 | blob_appendf(pOut, "%/", zOrigName); |
| 1320 | }else{ |
| 1321 | char zPwd[2000]; |
| 1322 | file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); |
| 1323 | if( zPwd[0]=='/' && strlen(zPwd)==1 ){ |
| 1324 | /* when on '/', don't add an extra '/' */ |
| 1325 | if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){ |
| 1326 | /* '.' when on '/' mean '/' */ |
| @@ -1372,10 +1384,11 @@ | |
| 1372 | ** just fossil_strdup(). But for case-insenstiive but "case preserving" |
| 1373 | ** filesystems, such as on MacOS or Windows, we want the filename to be |
| 1374 | ** in the preserved casing. That's what this routine does. |
| 1375 | */ |
| 1376 | char *file_case_preferred_name(const char *zDir, const char *zPath){ |
| 1377 | DIR *d; |
| 1378 | int i; |
| 1379 | char *zResult = 0; |
| 1380 | void *zNative = 0; |
| 1381 | |
| @@ -1407,10 +1420,13 @@ | |
| 1407 | closedir(d); |
| 1408 | } |
| 1409 | fossil_path_free(zNative); |
| 1410 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1411 | return zResult; |
| 1412 | } |
| 1413 | |
| 1414 | /* |
| 1415 | ** COMMAND: test-case-filename |
| 1416 | ** |
| @@ -1637,13 +1653,16 @@ | |
| 1637 | fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1638 | filenames_are_case_sensitive()); |
| 1639 | if( zAllow ){ |
| 1640 | g.allowSymlinks = !is_false(zAllow); |
| 1641 | } |
| 1642 | if( zRoot==0 ) zRoot = g.zLocalRoot; |
| 1643 | fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); |
| 1644 | fossil_print("local-root = [%s]\n", zRoot); |
| 1645 | for(i=2; i<g.argc; i++){ |
| 1646 | char *z; |
| 1647 | emitFileStat(g.argv[i], slashFlag, resetFlag); |
| 1648 | z = file_canonical_name_dup(g.argv[i]); |
| 1649 | fossil_print(" file_canonical_name = %s\n", z); |
| @@ -1653,10 +1672,13 @@ | |
| 1653 | }else{ |
| 1654 | int n = file_nondir_objects_on_path(zRoot, z); |
| 1655 | fossil_print("%.*s\n", n, z); |
| 1656 | } |
| 1657 | fossil_free(z); |
| 1658 | } |
| 1659 | } |
| 1660 | |
| 1661 | /* |
| 1662 | ** COMMAND: test-canonical-name |
| @@ -1695,13 +1717,15 @@ | |
| 1695 | ** Canonical names are full pathnames using "/" not "\" and which |
| 1696 | ** contain no "/./" or "/../" terms. |
| 1697 | */ |
| 1698 | int file_is_canonical(const char *z){ |
| 1699 | int i; |
| 1700 | if( z[0]!='/' |
| 1701 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1702 | && (!fossil_isupper(z[0]) || z[1]!=':' || z[2]!='/') |
| 1703 | #endif |
| 1704 | ) return 0; |
| 1705 | |
| 1706 | for(i=0; z[i]; i++){ |
| 1707 | if( z[i]=='\\' ) return 0; |
| @@ -2949,18 +2973,84 @@ | |
| 2949 | ** Returns 1 if the given directory contains a file named .fslckout, 2 |
| 2950 | ** if it contains a file named _FOSSIL_, else returns 0. |
| 2951 | */ |
| 2952 | int dir_has_ckout_db(const char *zDir){ |
| 2953 | int rc = 0; |
| 2954 | char * zCkoutDb = mprintf("%//.fslckout", zDir); |
| 2955 | if(file_isfile(zCkoutDb, ExtFILE)){ |
| 2956 | rc = 1; |
| 2957 | }else{ |
| 2958 | fossil_free(zCkoutDb); |
| 2959 | zCkoutDb = mprintf("%//_FOSSIL_", zDir); |
| 2960 | if(file_isfile(zCkoutDb, ExtFILE)){ |
| 2961 | rc = 2; |
| 2962 | } |
| 2963 | } |
| 2964 | fossil_free(zCkoutDb); |
| 2965 | return rc; |
| 2966 | } |
| 2967 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1302,25 +1302,37 @@ | |
| 1302 | } |
| 1303 | } |
| 1304 | |
| 1305 | /* |
| 1306 | ** Compute a canonical pathname for a file or directory. |
| 1307 | ** |
| 1308 | ** * Make the name absolute if it is relative. |
| 1309 | ** * Remove redundant / characters |
| 1310 | ** * Remove all /./ path elements. |
| 1311 | ** * Convert /A/../ to just / |
| 1312 | ** * On windows, add the drive letter prefix. |
| 1313 | ** |
| 1314 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1315 | ** is retained. |
| 1316 | ** |
| 1317 | ** See also: file_canonical_name_dup() |
| 1318 | */ |
| 1319 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1320 | char zPwd[2000]; |
| 1321 | blob_zero(pOut); |
| 1322 | if( file_is_absolute_path(zOrigName) ){ |
| 1323 | #if defined(_WIN32) |
| 1324 | if( fossil_isdirsep(zOrigName[0]) ){ |
| 1325 | /* Add the drive letter to the full pathname */ |
| 1326 | file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); |
| 1327 | blob_appendf(pOut, "%.2s%/", zPwd, zOrigName); |
| 1328 | }else |
| 1329 | #endif |
| 1330 | { |
| 1331 | blob_appendf(pOut, "%/", zOrigName); |
| 1332 | } |
| 1333 | }else{ |
| 1334 | file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); |
| 1335 | if( zPwd[0]=='/' && strlen(zPwd)==1 ){ |
| 1336 | /* when on '/', don't add an extra '/' */ |
| 1337 | if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){ |
| 1338 | /* '.' when on '/' mean '/' */ |
| @@ -1372,10 +1384,11 @@ | |
| 1384 | ** just fossil_strdup(). But for case-insenstiive but "case preserving" |
| 1385 | ** filesystems, such as on MacOS or Windows, we want the filename to be |
| 1386 | ** in the preserved casing. That's what this routine does. |
| 1387 | */ |
| 1388 | char *file_case_preferred_name(const char *zDir, const char *zPath){ |
| 1389 | #ifndef _WIN32 /* Call win32_file_case_preferred_name() on Windows. */ |
| 1390 | DIR *d; |
| 1391 | int i; |
| 1392 | char *zResult = 0; |
| 1393 | void *zNative = 0; |
| 1394 | |
| @@ -1407,10 +1420,13 @@ | |
| 1420 | closedir(d); |
| 1421 | } |
| 1422 | fossil_path_free(zNative); |
| 1423 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1424 | return zResult; |
| 1425 | #else /* !_WIN32 */ |
| 1426 | return win32_file_case_preferred_name(zDir,zPath); |
| 1427 | #endif /* !_WIN32 */ |
| 1428 | } |
| 1429 | |
| 1430 | /* |
| 1431 | ** COMMAND: test-case-filename |
| 1432 | ** |
| @@ -1637,13 +1653,16 @@ | |
| 1653 | fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1654 | filenames_are_case_sensitive()); |
| 1655 | if( zAllow ){ |
| 1656 | g.allowSymlinks = !is_false(zAllow); |
| 1657 | } |
| 1658 | if( zRoot==0 ) zRoot = g.zLocalRoot==0 ? "" : g.zLocalRoot; |
| 1659 | fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); |
| 1660 | fossil_print("local-root = [%s]\n", zRoot); |
| 1661 | if( g.db==0 ) sqlite3_open(":memory:", &g.db); |
| 1662 | sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0, |
| 1663 | file_inode_sql_func, 0, 0); |
| 1664 | for(i=2; i<g.argc; i++){ |
| 1665 | char *z; |
| 1666 | emitFileStat(g.argv[i], slashFlag, resetFlag); |
| 1667 | z = file_canonical_name_dup(g.argv[i]); |
| 1668 | fossil_print(" file_canonical_name = %s\n", z); |
| @@ -1653,10 +1672,13 @@ | |
| 1672 | }else{ |
| 1673 | int n = file_nondir_objects_on_path(zRoot, z); |
| 1674 | fossil_print("%.*s\n", n, z); |
| 1675 | } |
| 1676 | fossil_free(z); |
| 1677 | z = db_text(0, "SELECT inode(%Q)", g.argv[i]); |
| 1678 | fossil_print(" file_inode_sql_func = \"%s\"\n", z); |
| 1679 | fossil_free(z); |
| 1680 | } |
| 1681 | } |
| 1682 | |
| 1683 | /* |
| 1684 | ** COMMAND: test-canonical-name |
| @@ -1695,13 +1717,15 @@ | |
| 1717 | ** Canonical names are full pathnames using "/" not "\" and which |
| 1718 | ** contain no "/./" or "/../" terms. |
| 1719 | */ |
| 1720 | int file_is_canonical(const char *z){ |
| 1721 | int i; |
| 1722 | if( |
| 1723 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1724 | !fossil_isupper(z[0]) || z[1]!=':' || !fossil_isdirsep(z[2]) |
| 1725 | #else |
| 1726 | z[0]!='/' |
| 1727 | #endif |
| 1728 | ) return 0; |
| 1729 | |
| 1730 | for(i=0; z[i]; i++){ |
| 1731 | if( z[i]=='\\' ) return 0; |
| @@ -2949,18 +2973,84 @@ | |
| 2973 | ** Returns 1 if the given directory contains a file named .fslckout, 2 |
| 2974 | ** if it contains a file named _FOSSIL_, else returns 0. |
| 2975 | */ |
| 2976 | int dir_has_ckout_db(const char *zDir){ |
| 2977 | int rc = 0; |
| 2978 | i64 sz; |
| 2979 | char * zCkoutDb = mprintf("%//.fslckout", zDir); |
| 2980 | if(file_isfile(zCkoutDb, ExtFILE)){ |
| 2981 | rc = 1; |
| 2982 | }else{ |
| 2983 | fossil_free(zCkoutDb); |
| 2984 | zCkoutDb = mprintf("%//_FOSSIL_", zDir); |
| 2985 | if(file_isfile(zCkoutDb, ExtFILE)){ |
| 2986 | rc = 2; |
| 2987 | } |
| 2988 | } |
| 2989 | if( rc && ((sz = file_size(zCkoutDb, ExtFILE))<1024 || (sz%512)!=0) ){ |
| 2990 | rc = 0; |
| 2991 | } |
| 2992 | fossil_free(zCkoutDb); |
| 2993 | return rc; |
| 2994 | } |
| 2995 | |
| 2996 | /* |
| 2997 | ** This is the implementation of inode(FILENAME) SQL function. |
| 2998 | ** |
| 2999 | ** dev_inode(FILENAME) returns a string. If FILENAME exists and is |
| 3000 | ** a regular file, then the return string is of the form: |
| 3001 | ** |
| 3002 | ** DEV/INODE |
| 3003 | ** |
| 3004 | ** Where DEV and INODE are the device number and inode number for |
| 3005 | ** the file. On Windows, the volume serial number (DEV) and file |
| 3006 | ** identifier (INODE) are used to compute the value, see comments |
| 3007 | ** on the win32_file_id() function. |
| 3008 | ** |
| 3009 | ** If FILENAME does not exist, then the return is an empty string. |
| 3010 | ** |
| 3011 | ** The value of inode() can be used to eliminate files from a list |
| 3012 | ** that have duplicates because they have differing names due to links. |
| 3013 | ** |
| 3014 | ** Code that wants to use this SQL function needs to first register |
| 3015 | ** it using a call such as the following: |
| 3016 | ** |
| 3017 | ** sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0, |
| 3018 | ** file_inode_sql_func, 0, 0); |
| 3019 | */ |
| 3020 | void file_inode_sql_func( |
| 3021 | sqlite3_context *context, |
| 3022 | int argc, |
| 3023 | sqlite3_value **argv |
| 3024 | ){ |
| 3025 | const char *zFilename; |
| 3026 | assert( argc==1 ); |
| 3027 | zFilename = (const char*)sqlite3_value_text(argv[0]); |
| 3028 | if( zFilename==0 || zFilename[0]==0 || file_access(zFilename,F_OK) ){ |
| 3029 | sqlite3_result_text(context, "", 0, SQLITE_STATIC); |
| 3030 | return; |
| 3031 | } |
| 3032 | #if defined(_WIN32) |
| 3033 | { |
| 3034 | char *zFileId = win32_file_id(zFilename); |
| 3035 | if( zFileId ){ |
| 3036 | sqlite3_result_text(context, zFileId, -1, fossil_free); |
| 3037 | }else{ |
| 3038 | sqlite3_result_text(context, "", 0, SQLITE_STATIC); |
| 3039 | } |
| 3040 | } |
| 3041 | #else |
| 3042 | { |
| 3043 | struct stat buf; |
| 3044 | int rc; |
| 3045 | memset(&buf, 0, sizeof(buf)); |
| 3046 | rc = stat(zFilename, &buf); |
| 3047 | if( rc ){ |
| 3048 | sqlite3_result_text(context, "", 0, SQLITE_STATIC); |
| 3049 | }else{ |
| 3050 | sqlite3_result_text(context, |
| 3051 | mprintf("%lld/%lld", (i64)buf.st_dev, (i64)buf.st_ino), -1, |
| 3052 | fossil_free); |
| 3053 | } |
| 3054 | } |
| 3055 | #endif |
| 3056 | } |
| 3057 |
+13
-5
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -185,11 +185,11 @@ | ||
| 185 | 185 | zLimit = find_option("limit","n",1); |
| 186 | 186 | zWidth = find_option("width","W",1); |
| 187 | 187 | iLimit = zLimit ? atoi(zLimit) : -1; |
| 188 | 188 | zOffset = find_option("offset",0,1); |
| 189 | 189 | iOffset = zOffset ? atoi(zOffset) : 0; |
| 190 | - iBrief = (find_option("brief","b",0) == 0); | |
| 190 | + iBrief = find_option("brief","b",0) != 0; | |
| 191 | 191 | if( iLimit==0 ){ |
| 192 | 192 | iLimit = -1; |
| 193 | 193 | } |
| 194 | 194 | if( zWidth ){ |
| 195 | 195 | iWidth = atoi(zWidth); |
| @@ -228,11 +228,11 @@ | ||
| 228 | 228 | " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", |
| 229 | 229 | TAG_BRANCH, zFilename, filename_collation(), |
| 230 | 230 | iLimit, iOffset |
| 231 | 231 | ); |
| 232 | 232 | blob_zero(&line); |
| 233 | - if( iBrief ){ | |
| 233 | + if( iBrief == 0 ){ | |
| 234 | 234 | fossil_print("History for %s\n", blob_str(&fname)); |
| 235 | 235 | } |
| 236 | 236 | while( db_step(&q)==SQLITE_ROW ){ |
| 237 | 237 | const char *zFileUuid = db_column_text(&q, 0); |
| 238 | 238 | const char *zCiUuid = db_column_text(&q,1); |
| @@ -240,11 +240,11 @@ | ||
| 240 | 240 | const char *zCom = db_column_text(&q, 3); |
| 241 | 241 | const char *zUser = db_column_text(&q, 4); |
| 242 | 242 | const char *zBr = db_column_text(&q, 5); |
| 243 | 243 | char *zOut; |
| 244 | 244 | if( zBr==0 ) zBr = "trunk"; |
| 245 | - if( iBrief ){ | |
| 245 | + if( iBrief == 0 ){ | |
| 246 | 246 | fossil_print("%s ", zDate); |
| 247 | 247 | zOut = mprintf( |
| 248 | 248 | "[%S] %s (user: %s, artifact: [%S], branch: %s)", |
| 249 | 249 | zCiUuid, zCom, zUser, zFileUuid, zBr); |
| 250 | 250 | comment_print(zOut, zCom, 11, iWidth, get_comment_format()); |
| @@ -620,10 +620,11 @@ | ||
| 620 | 620 | int fnid = db_column_int(&q, 13); |
| 621 | 621 | const char *zFName = db_column_text(&q,14); |
| 622 | 622 | int gidx; |
| 623 | 623 | char zTime[10]; |
| 624 | 624 | int nParent = 0; |
| 625 | + int bIsModified = 0; | |
| 625 | 626 | GraphRowId aParent[GR_MAX_RAIL]; |
| 626 | 627 | |
| 627 | 628 | db_bind_int(&qparent, ":fid", frid); |
| 628 | 629 | db_bind_int(&qparent, ":mid", fmid); |
| 629 | 630 | db_bind_int(&qparent, ":fnid", fnid); |
| @@ -661,19 +662,26 @@ | ||
| 661 | 662 | @ </td> |
| 662 | 663 | if( zBgClr && zBgClr[0] ){ |
| 663 | 664 | @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> |
| 664 | 665 | }else{ |
| 665 | 666 | @ <td class="timeline%s(zStyle)Cell"> |
| 667 | + } | |
| 668 | + if( zPUuid && zUuid && fossil_strcmp(zPUuid, zUuid)!=0 ){ | |
| 669 | + bIsModified = 1; | |
| 666 | 670 | } |
| 667 | 671 | if( tmFlags & TIMELINE_COMPACT ){ |
| 668 | 672 | @ <span class='timelineCompactComment' data-id='%d(frid)'> |
| 669 | 673 | }else{ |
| 670 | 674 | @ <span class='timeline%s(zStyle)Comment'> |
| 671 | 675 | if( pfnid ){ |
| 672 | 676 | char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d", |
| 673 | 677 | pfnid); |
| 674 | - @ <b>Renamed</b> %h(zPrevName) → %h(zFName). | |
| 678 | + if( bIsModified ){ | |
| 679 | + @ <b>Renamed and modified</b> %h(zPrevName) → %h(zFName). | |
| 680 | + }else{ | |
| 681 | + @ <b>Renamed</b> %h(zPrevName) → %h(zFName). | |
| 682 | + } | |
| 675 | 683 | fossil_free(zPrevName); |
| 676 | 684 | } |
| 677 | 685 | if( zUuid && ridTo==0 && nParent==0 ){ |
| 678 | 686 | @ <b>Added:</b> |
| 679 | 687 | } |
| @@ -756,11 +764,11 @@ | ||
| 756 | 764 | @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) |
| 757 | 765 | @ [annotate]</a> |
| 758 | 766 | @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) |
| 759 | 767 | @ [blame]</a> |
| 760 | 768 | @ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins using]</a> |
| 761 | - if( fpid>0 ){ | |
| 769 | + if( fpid>0 && bIsModified!=0 ){ | |
| 762 | 770 | @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a> |
| 763 | 771 | } |
| 764 | 772 | if( fileedit_is_editable(zFName) ){ |
| 765 | 773 | @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\ |
| 766 | 774 | @ [edit]</a> |
| 767 | 775 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -185,11 +185,11 @@ | |
| 185 | zLimit = find_option("limit","n",1); |
| 186 | zWidth = find_option("width","W",1); |
| 187 | iLimit = zLimit ? atoi(zLimit) : -1; |
| 188 | zOffset = find_option("offset",0,1); |
| 189 | iOffset = zOffset ? atoi(zOffset) : 0; |
| 190 | iBrief = (find_option("brief","b",0) == 0); |
| 191 | if( iLimit==0 ){ |
| 192 | iLimit = -1; |
| 193 | } |
| 194 | if( zWidth ){ |
| 195 | iWidth = atoi(zWidth); |
| @@ -228,11 +228,11 @@ | |
| 228 | " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", |
| 229 | TAG_BRANCH, zFilename, filename_collation(), |
| 230 | iLimit, iOffset |
| 231 | ); |
| 232 | blob_zero(&line); |
| 233 | if( iBrief ){ |
| 234 | fossil_print("History for %s\n", blob_str(&fname)); |
| 235 | } |
| 236 | while( db_step(&q)==SQLITE_ROW ){ |
| 237 | const char *zFileUuid = db_column_text(&q, 0); |
| 238 | const char *zCiUuid = db_column_text(&q,1); |
| @@ -240,11 +240,11 @@ | |
| 240 | const char *zCom = db_column_text(&q, 3); |
| 241 | const char *zUser = db_column_text(&q, 4); |
| 242 | const char *zBr = db_column_text(&q, 5); |
| 243 | char *zOut; |
| 244 | if( zBr==0 ) zBr = "trunk"; |
| 245 | if( iBrief ){ |
| 246 | fossil_print("%s ", zDate); |
| 247 | zOut = mprintf( |
| 248 | "[%S] %s (user: %s, artifact: [%S], branch: %s)", |
| 249 | zCiUuid, zCom, zUser, zFileUuid, zBr); |
| 250 | comment_print(zOut, zCom, 11, iWidth, get_comment_format()); |
| @@ -620,10 +620,11 @@ | |
| 620 | int fnid = db_column_int(&q, 13); |
| 621 | const char *zFName = db_column_text(&q,14); |
| 622 | int gidx; |
| 623 | char zTime[10]; |
| 624 | int nParent = 0; |
| 625 | GraphRowId aParent[GR_MAX_RAIL]; |
| 626 | |
| 627 | db_bind_int(&qparent, ":fid", frid); |
| 628 | db_bind_int(&qparent, ":mid", fmid); |
| 629 | db_bind_int(&qparent, ":fnid", fnid); |
| @@ -661,19 +662,26 @@ | |
| 661 | @ </td> |
| 662 | if( zBgClr && zBgClr[0] ){ |
| 663 | @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> |
| 664 | }else{ |
| 665 | @ <td class="timeline%s(zStyle)Cell"> |
| 666 | } |
| 667 | if( tmFlags & TIMELINE_COMPACT ){ |
| 668 | @ <span class='timelineCompactComment' data-id='%d(frid)'> |
| 669 | }else{ |
| 670 | @ <span class='timeline%s(zStyle)Comment'> |
| 671 | if( pfnid ){ |
| 672 | char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d", |
| 673 | pfnid); |
| 674 | @ <b>Renamed</b> %h(zPrevName) → %h(zFName). |
| 675 | fossil_free(zPrevName); |
| 676 | } |
| 677 | if( zUuid && ridTo==0 && nParent==0 ){ |
| 678 | @ <b>Added:</b> |
| 679 | } |
| @@ -756,11 +764,11 @@ | |
| 756 | @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) |
| 757 | @ [annotate]</a> |
| 758 | @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) |
| 759 | @ [blame]</a> |
| 760 | @ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins using]</a> |
| 761 | if( fpid>0 ){ |
| 762 | @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a> |
| 763 | } |
| 764 | if( fileedit_is_editable(zFName) ){ |
| 765 | @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\ |
| 766 | @ [edit]</a> |
| 767 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -185,11 +185,11 @@ | |
| 185 | zLimit = find_option("limit","n",1); |
| 186 | zWidth = find_option("width","W",1); |
| 187 | iLimit = zLimit ? atoi(zLimit) : -1; |
| 188 | zOffset = find_option("offset",0,1); |
| 189 | iOffset = zOffset ? atoi(zOffset) : 0; |
| 190 | iBrief = find_option("brief","b",0) != 0; |
| 191 | if( iLimit==0 ){ |
| 192 | iLimit = -1; |
| 193 | } |
| 194 | if( zWidth ){ |
| 195 | iWidth = atoi(zWidth); |
| @@ -228,11 +228,11 @@ | |
| 228 | " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", |
| 229 | TAG_BRANCH, zFilename, filename_collation(), |
| 230 | iLimit, iOffset |
| 231 | ); |
| 232 | blob_zero(&line); |
| 233 | if( iBrief == 0 ){ |
| 234 | fossil_print("History for %s\n", blob_str(&fname)); |
| 235 | } |
| 236 | while( db_step(&q)==SQLITE_ROW ){ |
| 237 | const char *zFileUuid = db_column_text(&q, 0); |
| 238 | const char *zCiUuid = db_column_text(&q,1); |
| @@ -240,11 +240,11 @@ | |
| 240 | const char *zCom = db_column_text(&q, 3); |
| 241 | const char *zUser = db_column_text(&q, 4); |
| 242 | const char *zBr = db_column_text(&q, 5); |
| 243 | char *zOut; |
| 244 | if( zBr==0 ) zBr = "trunk"; |
| 245 | if( iBrief == 0 ){ |
| 246 | fossil_print("%s ", zDate); |
| 247 | zOut = mprintf( |
| 248 | "[%S] %s (user: %s, artifact: [%S], branch: %s)", |
| 249 | zCiUuid, zCom, zUser, zFileUuid, zBr); |
| 250 | comment_print(zOut, zCom, 11, iWidth, get_comment_format()); |
| @@ -620,10 +620,11 @@ | |
| 620 | int fnid = db_column_int(&q, 13); |
| 621 | const char *zFName = db_column_text(&q,14); |
| 622 | int gidx; |
| 623 | char zTime[10]; |
| 624 | int nParent = 0; |
| 625 | int bIsModified = 0; |
| 626 | GraphRowId aParent[GR_MAX_RAIL]; |
| 627 | |
| 628 | db_bind_int(&qparent, ":fid", frid); |
| 629 | db_bind_int(&qparent, ":mid", fmid); |
| 630 | db_bind_int(&qparent, ":fnid", fnid); |
| @@ -661,19 +662,26 @@ | |
| 662 | @ </td> |
| 663 | if( zBgClr && zBgClr[0] ){ |
| 664 | @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> |
| 665 | }else{ |
| 666 | @ <td class="timeline%s(zStyle)Cell"> |
| 667 | } |
| 668 | if( zPUuid && zUuid && fossil_strcmp(zPUuid, zUuid)!=0 ){ |
| 669 | bIsModified = 1; |
| 670 | } |
| 671 | if( tmFlags & TIMELINE_COMPACT ){ |
| 672 | @ <span class='timelineCompactComment' data-id='%d(frid)'> |
| 673 | }else{ |
| 674 | @ <span class='timeline%s(zStyle)Comment'> |
| 675 | if( pfnid ){ |
| 676 | char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d", |
| 677 | pfnid); |
| 678 | if( bIsModified ){ |
| 679 | @ <b>Renamed and modified</b> %h(zPrevName) → %h(zFName). |
| 680 | }else{ |
| 681 | @ <b>Renamed</b> %h(zPrevName) → %h(zFName). |
| 682 | } |
| 683 | fossil_free(zPrevName); |
| 684 | } |
| 685 | if( zUuid && ridTo==0 && nParent==0 ){ |
| 686 | @ <b>Added:</b> |
| 687 | } |
| @@ -756,11 +764,11 @@ | |
| 764 | @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) |
| 765 | @ [annotate]</a> |
| 766 | @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) |
| 767 | @ [blame]</a> |
| 768 | @ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins using]</a> |
| 769 | if( fpid>0 && bIsModified!=0 ){ |
| 770 | @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a> |
| 771 | } |
| 772 | if( fileedit_is_editable(zFName) ){ |
| 773 | @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\ |
| 774 | @ [edit]</a> |
| 775 |
+63
-35
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -1397,12 +1397,13 @@ | ||
| 1397 | 1397 | @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" |
| 1398 | 1398 | @ maxlength="125"><br> |
| 1399 | 1399 | } |
| 1400 | 1400 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1401 | 1401 | mimetype_option_menu(zMimetype, "mimetype"); |
| 1402 | - @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \ | |
| 1403 | - @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br> | |
| 1402 | + @ <div class="forum-editor-widget"> | |
| 1403 | + @ <textarea aria-label="Content:" name="content" class="wikiedit" \ | |
| 1404 | + @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea></div> | |
| 1404 | 1405 | } |
| 1405 | 1406 | |
| 1406 | 1407 | /* |
| 1407 | 1408 | ** WEBPAGE: forumpost_close hidden |
| 1408 | 1409 | ** WEBPAGE: forumpost_reopen hidden |
| @@ -1760,20 +1761,35 @@ | ||
| 1760 | 1761 | @ </form> |
| 1761 | 1762 | forum_emit_js(); |
| 1762 | 1763 | style_finish_page(); |
| 1763 | 1764 | } |
| 1764 | 1765 | |
| 1766 | +/* | |
| 1767 | +** SETTING: forum-close-policy boolean default=off | |
| 1768 | +** If true, forum moderators may close/re-open forum posts, and reply | |
| 1769 | +** to closed posts. If false, only administrators may do so. Note that | |
| 1770 | +** this only affects the forum web UI, not post-closing tags which | |
| 1771 | +** arrive via the command-line or from synchronization with a remote. | |
| 1772 | +*/ | |
| 1773 | +/* | |
| 1774 | +** SETTING: forum-title width=20 default=Forum | |
| 1775 | +** This is the name or "title" of the Forum for this repository. The | |
| 1776 | +** default is just "Forum". But in some setups, admins might want to | |
| 1777 | +** change it to "Developer Forum" or "User Forum" or whatever other name | |
| 1778 | +** seems more appropriate for the particular usage. | |
| 1779 | +*/ | |
| 1780 | + | |
| 1765 | 1781 | /* |
| 1766 | 1782 | ** WEBPAGE: setup_forum |
| 1767 | 1783 | ** |
| 1768 | 1784 | ** Forum configuration and metrics. |
| 1769 | 1785 | */ |
| 1770 | 1786 | void forum_setup(void){ |
| 1771 | 1787 | /* boolean config settings specific to the forum. */ |
| 1772 | - const char * zSettingsBool[] = { | |
| 1773 | - "forum-close-policy", | |
| 1774 | - NULL /* sentinel entry */ | |
| 1788 | + static const char *azForumSettings[] = { | |
| 1789 | + "forum-close-policy", | |
| 1790 | + "forum-title", | |
| 1775 | 1791 | }; |
| 1776 | 1792 | |
| 1777 | 1793 | login_check_credentials(); |
| 1778 | 1794 | if( !g.perm.Setup ){ |
| 1779 | 1795 | login_needed(g.anon.Setup); |
| @@ -1788,45 +1804,40 @@ | ||
| 1788 | 1804 | @ <p><a href='%R/forum'>Forum posts</a>: |
| 1789 | 1805 | @ <a href='%R/timeline?y=f'>%d(nPosts)</a></p> |
| 1790 | 1806 | } |
| 1791 | 1807 | |
| 1792 | 1808 | @ <h2>Supervisors</h2> |
| 1793 | - @ <p>Users with capabilities 's', 'a', or '6'.</p> | |
| 1794 | 1809 | { |
| 1795 | 1810 | Stmt q = empty_Stmt; |
| 1796 | - int nRows = 0; | |
| 1797 | 1811 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1798 | 1812 | "WHERE cap GLOB '*[as6]*' ORDER BY login"); |
| 1799 | 1813 | @ <table class='bordered'> |
| 1800 | 1814 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| 1801 | 1815 | @ <tbody> |
| 1802 | 1816 | while( SQLITE_ROW==db_step(&q) ){ |
| 1803 | 1817 | const int iUid = db_column_int(&q, 0); |
| 1804 | 1818 | const char *zUser = db_column_text(&q, 1); |
| 1805 | 1819 | const char *zCap = db_column_text(&q, 2); |
| 1806 | - ++nRows; | |
| 1807 | 1820 | @ <tr> |
| 1808 | 1821 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1809 | 1822 | @ <td>(%h(zCap))</td> |
| 1810 | 1823 | @ </tr> |
| 1811 | 1824 | } |
| 1812 | 1825 | db_finalize(&q); |
| 1813 | 1826 | @</tbody></table> |
| 1814 | - if( 0==nRows ){ | |
| 1815 | - @ No supervisors | |
| 1816 | - }else{ | |
| 1817 | - @ %d(nRows) supervisor(s) | |
| 1818 | - } | |
| 1819 | 1827 | } |
| 1820 | 1828 | |
| 1821 | 1829 | @ <h2>Moderators</h2> |
| 1822 | - @ <p>Users with capability '5'.</p> | |
| 1823 | - { | |
| 1830 | + if( db_int(0, "SELECT count(*) FROM user " | |
| 1831 | + " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){ | |
| 1832 | + @ <p>No non-supervisor moderators | |
| 1833 | + }else{ | |
| 1824 | 1834 | Stmt q = empty_Stmt; |
| 1825 | 1835 | int nRows = 0; |
| 1826 | 1836 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1827 | - "WHERE cap GLOB '*5*' ORDER BY login"); | |
| 1837 | + "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'" | |
| 1838 | + " ORDER BY login"); | |
| 1828 | 1839 | @ <table class='bordered'> |
| 1829 | 1840 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| 1830 | 1841 | @ <tbody> |
| 1831 | 1842 | while( SQLITE_ROW==db_step(&q) ){ |
| 1832 | 1843 | const int iUid = db_column_int(&q, 0); |
| @@ -1838,43 +1849,59 @@ | ||
| 1838 | 1849 | @ <td>(%h(zCap))</td> |
| 1839 | 1850 | @ </tr> |
| 1840 | 1851 | } |
| 1841 | 1852 | db_finalize(&q); |
| 1842 | 1853 | @ </tbody></table> |
| 1843 | - if( 0==nRows ){ | |
| 1844 | - @ No non-supervisor moderators | |
| 1845 | - }else{ | |
| 1846 | - @ %d(nRows) moderator(s) | |
| 1847 | - } | |
| 1848 | 1854 | } |
| 1849 | 1855 | |
| 1850 | 1856 | @ <h2>Settings</h2> |
| 1851 | - @ <p>Configuration settings specific to the forum.</p> | |
| 1852 | 1857 | if( P("submit") && cgi_csrf_safe(2) ){ |
| 1853 | 1858 | int i = 0; |
| 1854 | - const char *zSetting; | |
| 1855 | 1859 | db_begin_transaction(); |
| 1856 | - while( (zSetting = zSettingsBool[i++]) ){ | |
| 1857 | - const char *z = P(zSetting); | |
| 1858 | - if( !z || !z[0] ) z = "off"; | |
| 1859 | - db_set(zSetting/*works-like:"x"*/, z, 0); | |
| 1860 | + for(i=0; i<ArraySize(azForumSettings); i++){ | |
| 1861 | + char zQP[4]; | |
| 1862 | + const char *z; | |
| 1863 | + const Setting *pSetting = setting_find(azForumSettings[i]); | |
| 1864 | + if( pSetting==0 ) continue; | |
| 1865 | + zQP[0] = 'a'+i; | |
| 1866 | + zQP[1] = zQP[0]; | |
| 1867 | + zQP[2] = 0; | |
| 1868 | + z = P(zQP); | |
| 1869 | + if( z==0 || z[0]==0 ) continue; | |
| 1870 | + db_set(pSetting->name/*works-like:"x"*/, z, 0); | |
| 1860 | 1871 | } |
| 1861 | 1872 | db_end_transaction(0); |
| 1862 | 1873 | @ <p><em>Settings saved.</em></p> |
| 1863 | 1874 | } |
| 1864 | 1875 | { |
| 1865 | 1876 | int i = 0; |
| 1866 | - const char *zSetting; | |
| 1867 | 1877 | @ <form action="%R/setup_forum" method="post"> |
| 1868 | 1878 | login_insert_csrf_secret(); |
| 1869 | 1879 | @ <table class='forum-settings-list'><tbody> |
| 1870 | - while( (zSetting = zSettingsBool[i++]) ){ | |
| 1871 | - @ <tr><td> | |
| 1872 | - onoff_attribute("", zSetting, zSetting/*works-like:"x"*/, 0, 0); | |
| 1873 | - @ </td><td> | |
| 1874 | - @ <a href='%R/help?cmd=%h(zSetting)'>%h(zSetting)</a> | |
| 1875 | - @ </td></tr> | |
| 1880 | + for(i=0; i<ArraySize(azForumSettings); i++){ | |
| 1881 | + char zQP[4]; | |
| 1882 | + const Setting *pSetting = setting_find(azForumSettings[i]); | |
| 1883 | + if( pSetting==0 ) continue; | |
| 1884 | + zQP[0] = 'a'+i; | |
| 1885 | + zQP[1] = zQP[0]; | |
| 1886 | + zQP[2] = 0; | |
| 1887 | + if( pSetting->width==0 ){ | |
| 1888 | + /* Boolean setting */ | |
| 1889 | + @ <tr><td align="right"> | |
| 1890 | + @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>: | |
| 1891 | + @ </td><td> | |
| 1892 | + onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0); | |
| 1893 | + @ </td></tr> | |
| 1894 | + }else{ | |
| 1895 | + /* Text value setting */ | |
| 1896 | + @ <tr><td align="right"> | |
| 1897 | + @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>: | |
| 1898 | + @ </td><td> | |
| 1899 | + entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/, | |
| 1900 | + pSetting->def, 0); | |
| 1901 | + @ </td></tr> | |
| 1902 | + } | |
| 1876 | 1903 | } |
| 1877 | 1904 | @ </tbody></table> |
| 1878 | 1905 | @ <input type='submit' name='submit' value='Apply changes'> |
| 1879 | 1906 | @ </form> |
| 1880 | 1907 | } |
| @@ -1909,11 +1936,12 @@ | ||
| 1909 | 1936 | login_needed(g.anon.RdForum); |
| 1910 | 1937 | return; |
| 1911 | 1938 | } |
| 1912 | 1939 | cgi_check_for_malice(); |
| 1913 | 1940 | style_set_current_feature("forum"); |
| 1914 | - style_header( "%s", isSearch ? "Forum Search Results" : "Forum" ); | |
| 1941 | + style_header("%s%s", db_get("forum-title","Forum"), | |
| 1942 | + isSearch ? " Search Results" : ""); | |
| 1915 | 1943 | style_submenu_element("Timeline", "%R/timeline?ss=v&y=f&vfx"); |
| 1916 | 1944 | if( g.perm.WrForum ){ |
| 1917 | 1945 | style_submenu_element("New Thread","%R/forumnew"); |
| 1918 | 1946 | }else{ |
| 1919 | 1947 | /* Can't combine this with previous case using the ternary operator |
| 1920 | 1948 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1397,12 +1397,13 @@ | |
| 1397 | @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" |
| 1398 | @ maxlength="125"><br> |
| 1399 | } |
| 1400 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1401 | mimetype_option_menu(zMimetype, "mimetype"); |
| 1402 | @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \ |
| 1403 | @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br> |
| 1404 | } |
| 1405 | |
| 1406 | /* |
| 1407 | ** WEBPAGE: forumpost_close hidden |
| 1408 | ** WEBPAGE: forumpost_reopen hidden |
| @@ -1760,20 +1761,35 @@ | |
| 1760 | @ </form> |
| 1761 | forum_emit_js(); |
| 1762 | style_finish_page(); |
| 1763 | } |
| 1764 | |
| 1765 | /* |
| 1766 | ** WEBPAGE: setup_forum |
| 1767 | ** |
| 1768 | ** Forum configuration and metrics. |
| 1769 | */ |
| 1770 | void forum_setup(void){ |
| 1771 | /* boolean config settings specific to the forum. */ |
| 1772 | const char * zSettingsBool[] = { |
| 1773 | "forum-close-policy", |
| 1774 | NULL /* sentinel entry */ |
| 1775 | }; |
| 1776 | |
| 1777 | login_check_credentials(); |
| 1778 | if( !g.perm.Setup ){ |
| 1779 | login_needed(g.anon.Setup); |
| @@ -1788,45 +1804,40 @@ | |
| 1788 | @ <p><a href='%R/forum'>Forum posts</a>: |
| 1789 | @ <a href='%R/timeline?y=f'>%d(nPosts)</a></p> |
| 1790 | } |
| 1791 | |
| 1792 | @ <h2>Supervisors</h2> |
| 1793 | @ <p>Users with capabilities 's', 'a', or '6'.</p> |
| 1794 | { |
| 1795 | Stmt q = empty_Stmt; |
| 1796 | int nRows = 0; |
| 1797 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1798 | "WHERE cap GLOB '*[as6]*' ORDER BY login"); |
| 1799 | @ <table class='bordered'> |
| 1800 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| 1801 | @ <tbody> |
| 1802 | while( SQLITE_ROW==db_step(&q) ){ |
| 1803 | const int iUid = db_column_int(&q, 0); |
| 1804 | const char *zUser = db_column_text(&q, 1); |
| 1805 | const char *zCap = db_column_text(&q, 2); |
| 1806 | ++nRows; |
| 1807 | @ <tr> |
| 1808 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1809 | @ <td>(%h(zCap))</td> |
| 1810 | @ </tr> |
| 1811 | } |
| 1812 | db_finalize(&q); |
| 1813 | @</tbody></table> |
| 1814 | if( 0==nRows ){ |
| 1815 | @ No supervisors |
| 1816 | }else{ |
| 1817 | @ %d(nRows) supervisor(s) |
| 1818 | } |
| 1819 | } |
| 1820 | |
| 1821 | @ <h2>Moderators</h2> |
| 1822 | @ <p>Users with capability '5'.</p> |
| 1823 | { |
| 1824 | Stmt q = empty_Stmt; |
| 1825 | int nRows = 0; |
| 1826 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1827 | "WHERE cap GLOB '*5*' ORDER BY login"); |
| 1828 | @ <table class='bordered'> |
| 1829 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| 1830 | @ <tbody> |
| 1831 | while( SQLITE_ROW==db_step(&q) ){ |
| 1832 | const int iUid = db_column_int(&q, 0); |
| @@ -1838,43 +1849,59 @@ | |
| 1838 | @ <td>(%h(zCap))</td> |
| 1839 | @ </tr> |
| 1840 | } |
| 1841 | db_finalize(&q); |
| 1842 | @ </tbody></table> |
| 1843 | if( 0==nRows ){ |
| 1844 | @ No non-supervisor moderators |
| 1845 | }else{ |
| 1846 | @ %d(nRows) moderator(s) |
| 1847 | } |
| 1848 | } |
| 1849 | |
| 1850 | @ <h2>Settings</h2> |
| 1851 | @ <p>Configuration settings specific to the forum.</p> |
| 1852 | if( P("submit") && cgi_csrf_safe(2) ){ |
| 1853 | int i = 0; |
| 1854 | const char *zSetting; |
| 1855 | db_begin_transaction(); |
| 1856 | while( (zSetting = zSettingsBool[i++]) ){ |
| 1857 | const char *z = P(zSetting); |
| 1858 | if( !z || !z[0] ) z = "off"; |
| 1859 | db_set(zSetting/*works-like:"x"*/, z, 0); |
| 1860 | } |
| 1861 | db_end_transaction(0); |
| 1862 | @ <p><em>Settings saved.</em></p> |
| 1863 | } |
| 1864 | { |
| 1865 | int i = 0; |
| 1866 | const char *zSetting; |
| 1867 | @ <form action="%R/setup_forum" method="post"> |
| 1868 | login_insert_csrf_secret(); |
| 1869 | @ <table class='forum-settings-list'><tbody> |
| 1870 | while( (zSetting = zSettingsBool[i++]) ){ |
| 1871 | @ <tr><td> |
| 1872 | onoff_attribute("", zSetting, zSetting/*works-like:"x"*/, 0, 0); |
| 1873 | @ </td><td> |
| 1874 | @ <a href='%R/help?cmd=%h(zSetting)'>%h(zSetting)</a> |
| 1875 | @ </td></tr> |
| 1876 | } |
| 1877 | @ </tbody></table> |
| 1878 | @ <input type='submit' name='submit' value='Apply changes'> |
| 1879 | @ </form> |
| 1880 | } |
| @@ -1909,11 +1936,12 @@ | |
| 1909 | login_needed(g.anon.RdForum); |
| 1910 | return; |
| 1911 | } |
| 1912 | cgi_check_for_malice(); |
| 1913 | style_set_current_feature("forum"); |
| 1914 | style_header( "%s", isSearch ? "Forum Search Results" : "Forum" ); |
| 1915 | style_submenu_element("Timeline", "%R/timeline?ss=v&y=f&vfx"); |
| 1916 | if( g.perm.WrForum ){ |
| 1917 | style_submenu_element("New Thread","%R/forumnew"); |
| 1918 | }else{ |
| 1919 | /* Can't combine this with previous case using the ternary operator |
| 1920 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1397,12 +1397,13 @@ | |
| 1397 | @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" |
| 1398 | @ maxlength="125"><br> |
| 1399 | } |
| 1400 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1401 | mimetype_option_menu(zMimetype, "mimetype"); |
| 1402 | @ <div class="forum-editor-widget"> |
| 1403 | @ <textarea aria-label="Content:" name="content" class="wikiedit" \ |
| 1404 | @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea></div> |
| 1405 | } |
| 1406 | |
| 1407 | /* |
| 1408 | ** WEBPAGE: forumpost_close hidden |
| 1409 | ** WEBPAGE: forumpost_reopen hidden |
| @@ -1760,20 +1761,35 @@ | |
| 1761 | @ </form> |
| 1762 | forum_emit_js(); |
| 1763 | style_finish_page(); |
| 1764 | } |
| 1765 | |
| 1766 | /* |
| 1767 | ** SETTING: forum-close-policy boolean default=off |
| 1768 | ** If true, forum moderators may close/re-open forum posts, and reply |
| 1769 | ** to closed posts. If false, only administrators may do so. Note that |
| 1770 | ** this only affects the forum web UI, not post-closing tags which |
| 1771 | ** arrive via the command-line or from synchronization with a remote. |
| 1772 | */ |
| 1773 | /* |
| 1774 | ** SETTING: forum-title width=20 default=Forum |
| 1775 | ** This is the name or "title" of the Forum for this repository. The |
| 1776 | ** default is just "Forum". But in some setups, admins might want to |
| 1777 | ** change it to "Developer Forum" or "User Forum" or whatever other name |
| 1778 | ** seems more appropriate for the particular usage. |
| 1779 | */ |
| 1780 | |
| 1781 | /* |
| 1782 | ** WEBPAGE: setup_forum |
| 1783 | ** |
| 1784 | ** Forum configuration and metrics. |
| 1785 | */ |
| 1786 | void forum_setup(void){ |
| 1787 | /* boolean config settings specific to the forum. */ |
| 1788 | static const char *azForumSettings[] = { |
| 1789 | "forum-close-policy", |
| 1790 | "forum-title", |
| 1791 | }; |
| 1792 | |
| 1793 | login_check_credentials(); |
| 1794 | if( !g.perm.Setup ){ |
| 1795 | login_needed(g.anon.Setup); |
| @@ -1788,45 +1804,40 @@ | |
| 1804 | @ <p><a href='%R/forum'>Forum posts</a>: |
| 1805 | @ <a href='%R/timeline?y=f'>%d(nPosts)</a></p> |
| 1806 | } |
| 1807 | |
| 1808 | @ <h2>Supervisors</h2> |
| 1809 | { |
| 1810 | Stmt q = empty_Stmt; |
| 1811 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1812 | "WHERE cap GLOB '*[as6]*' ORDER BY login"); |
| 1813 | @ <table class='bordered'> |
| 1814 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| 1815 | @ <tbody> |
| 1816 | while( SQLITE_ROW==db_step(&q) ){ |
| 1817 | const int iUid = db_column_int(&q, 0); |
| 1818 | const char *zUser = db_column_text(&q, 1); |
| 1819 | const char *zCap = db_column_text(&q, 2); |
| 1820 | @ <tr> |
| 1821 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1822 | @ <td>(%h(zCap))</td> |
| 1823 | @ </tr> |
| 1824 | } |
| 1825 | db_finalize(&q); |
| 1826 | @</tbody></table> |
| 1827 | } |
| 1828 | |
| 1829 | @ <h2>Moderators</h2> |
| 1830 | if( db_int(0, "SELECT count(*) FROM user " |
| 1831 | " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){ |
| 1832 | @ <p>No non-supervisor moderators |
| 1833 | }else{ |
| 1834 | Stmt q = empty_Stmt; |
| 1835 | int nRows = 0; |
| 1836 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1837 | "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'" |
| 1838 | " ORDER BY login"); |
| 1839 | @ <table class='bordered'> |
| 1840 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| 1841 | @ <tbody> |
| 1842 | while( SQLITE_ROW==db_step(&q) ){ |
| 1843 | const int iUid = db_column_int(&q, 0); |
| @@ -1838,43 +1849,59 @@ | |
| 1849 | @ <td>(%h(zCap))</td> |
| 1850 | @ </tr> |
| 1851 | } |
| 1852 | db_finalize(&q); |
| 1853 | @ </tbody></table> |
| 1854 | } |
| 1855 | |
| 1856 | @ <h2>Settings</h2> |
| 1857 | if( P("submit") && cgi_csrf_safe(2) ){ |
| 1858 | int i = 0; |
| 1859 | db_begin_transaction(); |
| 1860 | for(i=0; i<ArraySize(azForumSettings); i++){ |
| 1861 | char zQP[4]; |
| 1862 | const char *z; |
| 1863 | const Setting *pSetting = setting_find(azForumSettings[i]); |
| 1864 | if( pSetting==0 ) continue; |
| 1865 | zQP[0] = 'a'+i; |
| 1866 | zQP[1] = zQP[0]; |
| 1867 | zQP[2] = 0; |
| 1868 | z = P(zQP); |
| 1869 | if( z==0 || z[0]==0 ) continue; |
| 1870 | db_set(pSetting->name/*works-like:"x"*/, z, 0); |
| 1871 | } |
| 1872 | db_end_transaction(0); |
| 1873 | @ <p><em>Settings saved.</em></p> |
| 1874 | } |
| 1875 | { |
| 1876 | int i = 0; |
| 1877 | @ <form action="%R/setup_forum" method="post"> |
| 1878 | login_insert_csrf_secret(); |
| 1879 | @ <table class='forum-settings-list'><tbody> |
| 1880 | for(i=0; i<ArraySize(azForumSettings); i++){ |
| 1881 | char zQP[4]; |
| 1882 | const Setting *pSetting = setting_find(azForumSettings[i]); |
| 1883 | if( pSetting==0 ) continue; |
| 1884 | zQP[0] = 'a'+i; |
| 1885 | zQP[1] = zQP[0]; |
| 1886 | zQP[2] = 0; |
| 1887 | if( pSetting->width==0 ){ |
| 1888 | /* Boolean setting */ |
| 1889 | @ <tr><td align="right"> |
| 1890 | @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>: |
| 1891 | @ </td><td> |
| 1892 | onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0); |
| 1893 | @ </td></tr> |
| 1894 | }else{ |
| 1895 | /* Text value setting */ |
| 1896 | @ <tr><td align="right"> |
| 1897 | @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>: |
| 1898 | @ </td><td> |
| 1899 | entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/, |
| 1900 | pSetting->def, 0); |
| 1901 | @ </td></tr> |
| 1902 | } |
| 1903 | } |
| 1904 | @ </tbody></table> |
| 1905 | @ <input type='submit' name='submit' value='Apply changes'> |
| 1906 | @ </form> |
| 1907 | } |
| @@ -1909,11 +1936,12 @@ | |
| 1936 | login_needed(g.anon.RdForum); |
| 1937 | return; |
| 1938 | } |
| 1939 | cgi_check_for_malice(); |
| 1940 | style_set_current_feature("forum"); |
| 1941 | style_header("%s%s", db_get("forum-title","Forum"), |
| 1942 | isSearch ? " Search Results" : ""); |
| 1943 | style_submenu_element("Timeline", "%R/timeline?ss=v&y=f&vfx"); |
| 1944 | if( g.perm.WrForum ){ |
| 1945 | style_submenu_element("New Thread","%R/forumnew"); |
| 1946 | }else{ |
| 1947 | /* Can't combine this with previous case using the ternary operator |
| 1948 |
+25
-5
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -6,20 +6,39 @@ | ||
| 6 | 6 | /** |
| 7 | 7 | Adds toggle checkboxes to each file entry in the diff views for |
| 8 | 8 | /info and similar pages. |
| 9 | 9 | */ |
| 10 | 10 | const D = window.fossil.dom; |
| 11 | + const allToggles = [/*collection of all diff-toggle checkboxes */]; | |
| 11 | 12 | const addToggle = function(diffElem){ |
| 12 | 13 | const sib = diffElem.previousElementSibling, |
| 13 | - btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; | |
| 14 | + btnOne = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; | |
| 14 | 15 | if(!sib) return; |
| 15 | - D.append(sib,btn); | |
| 16 | - btn.addEventListener('click', function(){ | |
| 17 | - diffElem.classList.toggle('hidden'); | |
| 16 | + const lblToggle = D.append(D.label(null, " Toggle "), btnOne); | |
| 17 | + const wrapper = D.append(D.span(), lblToggle); | |
| 18 | + const btnAll = D.button("all"); | |
| 19 | + btnAll.$cb = btnOne; | |
| 20 | + allToggles.push(btnOne); | |
| 21 | + D.append(sib, D.append(wrapper, lblToggle, D.text(" "), btnAll)); | |
| 22 | + btnOne.addEventListener('change', function(){ | |
| 23 | + diffElem.classList[this.checked ? 'remove' : 'add']('hidden'); | |
| 24 | + }, false); | |
| 25 | + btnAll.addEventListener('click', function(){ | |
| 26 | + /* Toggle all entries to match this line's new state. Note that | |
| 27 | + we use click() instead of cb.checked=... so that the | |
| 28 | + on-change event handler fires. */ | |
| 29 | + const checked = !this.$cb.checked; | |
| 30 | + allToggles.forEach( (cb)=>{ | |
| 31 | + if(cb.checked!==checked) cb.click(); | |
| 32 | + }); | |
| 18 | 33 | }, false); |
| 19 | 34 | }; |
| 20 | - document.querySelectorAll('table.diff').forEach(addToggle); | |
| 35 | + if( !document.querySelector('body.fdiff') ){ | |
| 36 | + /* Don't show the diff toggle button for /fdiff because it only | |
| 37 | + has a single file to show (and also a different DOM layout). */ | |
| 38 | + document.querySelectorAll('table.diff').forEach(addToggle); | |
| 39 | + } | |
| 21 | 40 | }); |
| 22 | 41 | |
| 23 | 42 | window.fossil.onPageLoad(function(){ |
| 24 | 43 | const F = window.fossil, D = F.dom; |
| 25 | 44 | const Diff = F.diff = { |
| @@ -638,10 +657,11 @@ | ||
| 638 | 657 | const potentialParents = [ /* possible parents for the checkbox */ |
| 639 | 658 | /* Put the most likely pages at the end, as array.pop() is more |
| 640 | 659 | efficient than array.shift() (see loop below). */ |
| 641 | 660 | /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons', |
| 642 | 661 | /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons', |
| 662 | + /* /fdiff */ 'body.fdiff form div.submenu', | |
| 643 | 663 | /* /vdiff */ 'body.vdiff form div.submenu', |
| 644 | 664 | /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu' |
| 645 | 665 | ]; |
| 646 | 666 | while( potentialParents.length ){ |
| 647 | 667 | if( (eToggleParent = document.querySelector(potentialParents.pop())) ){ |
| 648 | 668 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -6,20 +6,39 @@ | |
| 6 | /** |
| 7 | Adds toggle checkboxes to each file entry in the diff views for |
| 8 | /info and similar pages. |
| 9 | */ |
| 10 | const D = window.fossil.dom; |
| 11 | const addToggle = function(diffElem){ |
| 12 | const sib = diffElem.previousElementSibling, |
| 13 | btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 14 | if(!sib) return; |
| 15 | D.append(sib,btn); |
| 16 | btn.addEventListener('click', function(){ |
| 17 | diffElem.classList.toggle('hidden'); |
| 18 | }, false); |
| 19 | }; |
| 20 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 21 | }); |
| 22 | |
| 23 | window.fossil.onPageLoad(function(){ |
| 24 | const F = window.fossil, D = F.dom; |
| 25 | const Diff = F.diff = { |
| @@ -638,10 +657,11 @@ | |
| 638 | const potentialParents = [ /* possible parents for the checkbox */ |
| 639 | /* Put the most likely pages at the end, as array.pop() is more |
| 640 | efficient than array.shift() (see loop below). */ |
| 641 | /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons', |
| 642 | /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons', |
| 643 | /* /vdiff */ 'body.vdiff form div.submenu', |
| 644 | /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu' |
| 645 | ]; |
| 646 | while( potentialParents.length ){ |
| 647 | if( (eToggleParent = document.querySelector(potentialParents.pop())) ){ |
| 648 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -6,20 +6,39 @@ | |
| 6 | /** |
| 7 | Adds toggle checkboxes to each file entry in the diff views for |
| 8 | /info and similar pages. |
| 9 | */ |
| 10 | const D = window.fossil.dom; |
| 11 | const allToggles = [/*collection of all diff-toggle checkboxes */]; |
| 12 | const addToggle = function(diffElem){ |
| 13 | const sib = diffElem.previousElementSibling, |
| 14 | btnOne = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 15 | if(!sib) return; |
| 16 | const lblToggle = D.append(D.label(null, " Toggle "), btnOne); |
| 17 | const wrapper = D.append(D.span(), lblToggle); |
| 18 | const btnAll = D.button("all"); |
| 19 | btnAll.$cb = btnOne; |
| 20 | allToggles.push(btnOne); |
| 21 | D.append(sib, D.append(wrapper, lblToggle, D.text(" "), btnAll)); |
| 22 | btnOne.addEventListener('change', function(){ |
| 23 | diffElem.classList[this.checked ? 'remove' : 'add']('hidden'); |
| 24 | }, false); |
| 25 | btnAll.addEventListener('click', function(){ |
| 26 | /* Toggle all entries to match this line's new state. Note that |
| 27 | we use click() instead of cb.checked=... so that the |
| 28 | on-change event handler fires. */ |
| 29 | const checked = !this.$cb.checked; |
| 30 | allToggles.forEach( (cb)=>{ |
| 31 | if(cb.checked!==checked) cb.click(); |
| 32 | }); |
| 33 | }, false); |
| 34 | }; |
| 35 | if( !document.querySelector('body.fdiff') ){ |
| 36 | /* Don't show the diff toggle button for /fdiff because it only |
| 37 | has a single file to show (and also a different DOM layout). */ |
| 38 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 39 | } |
| 40 | }); |
| 41 | |
| 42 | window.fossil.onPageLoad(function(){ |
| 43 | const F = window.fossil, D = F.dom; |
| 44 | const Diff = F.diff = { |
| @@ -638,10 +657,11 @@ | |
| 657 | const potentialParents = [ /* possible parents for the checkbox */ |
| 658 | /* Put the most likely pages at the end, as array.pop() is more |
| 659 | efficient than array.shift() (see loop below). */ |
| 660 | /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons', |
| 661 | /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons', |
| 662 | /* /fdiff */ 'body.fdiff form div.submenu', |
| 663 | /* /vdiff */ 'body.vdiff form div.submenu', |
| 664 | /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu' |
| 665 | ]; |
| 666 | while( potentialParents.length ){ |
| 667 | if( (eToggleParent = document.querySelector(potentialParents.pop())) ){ |
| 668 |
+3
-1
| --- src/fossil.dom.js | ||
| +++ src/fossil.dom.js | ||
| @@ -87,11 +87,13 @@ | ||
| 87 | 87 | const rc = document.createElement('label'); |
| 88 | 88 | if(forElem){ |
| 89 | 89 | if(forElem instanceof HTMLElement){ |
| 90 | 90 | forElem = this.attr(forElem, 'id'); |
| 91 | 91 | } |
| 92 | - dom.attr(rc, 'for', forElem); | |
| 92 | + if(forElem){ | |
| 93 | + dom.attr(rc, 'for', forElem); | |
| 94 | + } | |
| 93 | 95 | } |
| 94 | 96 | if(text) this.append(rc, text); |
| 95 | 97 | return rc; |
| 96 | 98 | }; |
| 97 | 99 | /** |
| 98 | 100 |
| --- src/fossil.dom.js | |
| +++ src/fossil.dom.js | |
| @@ -87,11 +87,13 @@ | |
| 87 | const rc = document.createElement('label'); |
| 88 | if(forElem){ |
| 89 | if(forElem instanceof HTMLElement){ |
| 90 | forElem = this.attr(forElem, 'id'); |
| 91 | } |
| 92 | dom.attr(rc, 'for', forElem); |
| 93 | } |
| 94 | if(text) this.append(rc, text); |
| 95 | return rc; |
| 96 | }; |
| 97 | /** |
| 98 |
| --- src/fossil.dom.js | |
| +++ src/fossil.dom.js | |
| @@ -87,11 +87,13 @@ | |
| 87 | const rc = document.createElement('label'); |
| 88 | if(forElem){ |
| 89 | if(forElem instanceof HTMLElement){ |
| 90 | forElem = this.attr(forElem, 'id'); |
| 91 | } |
| 92 | if(forElem){ |
| 93 | dom.attr(rc, 'for', forElem); |
| 94 | } |
| 95 | } |
| 96 | if(text) this.append(rc, text); |
| 97 | return rc; |
| 98 | }; |
| 99 | /** |
| 100 |
+1
-1
| --- src/fossil.page.chat.js | ||
| +++ src/fossil.page.chat.js | ||
| @@ -2132,11 +2132,11 @@ | ||
| 2132 | 2132 | s.value ? 'add' : 'remove' |
| 2133 | 2133 | ]('compact'); |
| 2134 | 2134 | Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus(); |
| 2135 | 2135 | }); |
| 2136 | 2136 | Chat.settings.addListener('edit-ctrl-send',function(s){ |
| 2137 | - const label = (s.value ? "Ctrl-" : "")+"Enter submits message"; | |
| 2137 | + const label = (s.value ? "Ctrl-" : "")+"Enter submits message."; | |
| 2138 | 2138 | Chat.e.inputFields.forEach((e)=>{ |
| 2139 | 2139 | const v = e.dataset.placeholder0 + " " +label; |
| 2140 | 2140 | if(e.isContentEditable) e.dataset.placeholder = v; |
| 2141 | 2141 | else D.attr(e,'placeholder',v); |
| 2142 | 2142 | }); |
| 2143 | 2143 |
| --- src/fossil.page.chat.js | |
| +++ src/fossil.page.chat.js | |
| @@ -2132,11 +2132,11 @@ | |
| 2132 | s.value ? 'add' : 'remove' |
| 2133 | ]('compact'); |
| 2134 | Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus(); |
| 2135 | }); |
| 2136 | Chat.settings.addListener('edit-ctrl-send',function(s){ |
| 2137 | const label = (s.value ? "Ctrl-" : "")+"Enter submits message"; |
| 2138 | Chat.e.inputFields.forEach((e)=>{ |
| 2139 | const v = e.dataset.placeholder0 + " " +label; |
| 2140 | if(e.isContentEditable) e.dataset.placeholder = v; |
| 2141 | else D.attr(e,'placeholder',v); |
| 2142 | }); |
| 2143 |
| --- src/fossil.page.chat.js | |
| +++ src/fossil.page.chat.js | |
| @@ -2132,11 +2132,11 @@ | |
| 2132 | s.value ? 'add' : 'remove' |
| 2133 | ]('compact'); |
| 2134 | Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus(); |
| 2135 | }); |
| 2136 | Chat.settings.addListener('edit-ctrl-send',function(s){ |
| 2137 | const label = (s.value ? "Ctrl-" : "")+"Enter submits message."; |
| 2138 | Chat.e.inputFields.forEach((e)=>{ |
| 2139 | const v = e.dataset.placeholder0 + " " +label; |
| 2140 | if(e.isContentEditable) e.dataset.placeholder = v; |
| 2141 | else D.attr(e,'placeholder',v); |
| 2142 | }); |
| 2143 |
+15
-3
| --- src/fossil.page.pikchrshowasm.js | ||
| +++ src/fossil.page.pikchrshowasm.js | ||
| @@ -206,11 +206,11 @@ | ||
| 206 | 206 | const text = getCurrentText(); |
| 207 | 207 | if(text) PS.render(text); |
| 208 | 208 | }; |
| 209 | 209 | const setCurrentText = function(txt){ |
| 210 | 210 | taInput.value = txt; |
| 211 | - renderCurrentText(); | |
| 211 | + renderCurrentText(); | |
| 212 | 212 | }; |
| 213 | 213 | PS.e.btnRender.addEventListener('click',function(ev){ |
| 214 | 214 | ev.preventDefault(); |
| 215 | 215 | renderCurrentText(); |
| 216 | 216 | },false); |
| @@ -427,13 +427,25 @@ | ||
| 427 | 427 | btnToggle.addEventListener('click', function(){ |
| 428 | 428 | fs.classList.toggle('collapsed'); |
| 429 | 429 | content.forEach((d)=>d.classList.toggle('hidden')); |
| 430 | 430 | }, false); |
| 431 | 431 | }); |
| 432 | + | |
| 433 | + if(window.sessionStorage){ | |
| 434 | + /* If sessionStorage['pikchr-xfer'] exists and the "fromSession" | |
| 435 | + URL argument was passed to this page, load the pikchr source | |
| 436 | + from the session. This is used by the "open in pikchrshow" | |
| 437 | + link in the forum. */ | |
| 438 | + const src = window.sessionStorage.getItem('pikchr-xfer'); | |
| 439 | + if( src && (new URL(self.location.href).searchParams).has('fromSession') ){ | |
| 440 | + taInput.value = src; | |
| 441 | + window.sessionStorage.removeItem('pikchr-xfer'); | |
| 442 | + } | |
| 443 | + } | |
| 432 | 444 | |
| 433 | 445 | PS.e.btnRender.click(); |
| 434 | - | |
| 446 | + | |
| 435 | 447 | /** Debounce handler for auto-rendering while typing. */ |
| 436 | 448 | const debounceAutoRender = F.debounce(function f(){ |
| 437 | 449 | if(!PS._isDirty) return; |
| 438 | 450 | const text = getCurrentText(); |
| 439 | 451 | if(f._ === text){ |
| @@ -618,11 +630,11 @@ | ||
| 618 | 630 | "Darlene" above aligned |
| 619 | 631 | |
| 620 | 632 | # fill in content for the Alice lane |
| 621 | 633 | right |
| 622 | 634 | A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ |
| 623 | - fill white thickness 1.5px "1" | |
| 635 | + fill white thickness 1.5px "1" | |
| 624 | 636 | arrow right 50% |
| 625 | 637 | circle same "2" |
| 626 | 638 | arrow right until even with first box.e - (0.65,0.0) |
| 627 | 639 | ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px |
| 628 | 640 | A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 |
| 629 | 641 |
| --- src/fossil.page.pikchrshowasm.js | |
| +++ src/fossil.page.pikchrshowasm.js | |
| @@ -206,11 +206,11 @@ | |
| 206 | const text = getCurrentText(); |
| 207 | if(text) PS.render(text); |
| 208 | }; |
| 209 | const setCurrentText = function(txt){ |
| 210 | taInput.value = txt; |
| 211 | renderCurrentText(); |
| 212 | }; |
| 213 | PS.e.btnRender.addEventListener('click',function(ev){ |
| 214 | ev.preventDefault(); |
| 215 | renderCurrentText(); |
| 216 | },false); |
| @@ -427,13 +427,25 @@ | |
| 427 | btnToggle.addEventListener('click', function(){ |
| 428 | fs.classList.toggle('collapsed'); |
| 429 | content.forEach((d)=>d.classList.toggle('hidden')); |
| 430 | }, false); |
| 431 | }); |
| 432 | |
| 433 | PS.e.btnRender.click(); |
| 434 | |
| 435 | /** Debounce handler for auto-rendering while typing. */ |
| 436 | const debounceAutoRender = F.debounce(function f(){ |
| 437 | if(!PS._isDirty) return; |
| 438 | const text = getCurrentText(); |
| 439 | if(f._ === text){ |
| @@ -618,11 +630,11 @@ | |
| 618 | "Darlene" above aligned |
| 619 | |
| 620 | # fill in content for the Alice lane |
| 621 | right |
| 622 | A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ |
| 623 | fill white thickness 1.5px "1" |
| 624 | arrow right 50% |
| 625 | circle same "2" |
| 626 | arrow right until even with first box.e - (0.65,0.0) |
| 627 | ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px |
| 628 | A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 |
| 629 |
| --- src/fossil.page.pikchrshowasm.js | |
| +++ src/fossil.page.pikchrshowasm.js | |
| @@ -206,11 +206,11 @@ | |
| 206 | const text = getCurrentText(); |
| 207 | if(text) PS.render(text); |
| 208 | }; |
| 209 | const setCurrentText = function(txt){ |
| 210 | taInput.value = txt; |
| 211 | renderCurrentText(); |
| 212 | }; |
| 213 | PS.e.btnRender.addEventListener('click',function(ev){ |
| 214 | ev.preventDefault(); |
| 215 | renderCurrentText(); |
| 216 | },false); |
| @@ -427,13 +427,25 @@ | |
| 427 | btnToggle.addEventListener('click', function(){ |
| 428 | fs.classList.toggle('collapsed'); |
| 429 | content.forEach((d)=>d.classList.toggle('hidden')); |
| 430 | }, false); |
| 431 | }); |
| 432 | |
| 433 | if(window.sessionStorage){ |
| 434 | /* If sessionStorage['pikchr-xfer'] exists and the "fromSession" |
| 435 | URL argument was passed to this page, load the pikchr source |
| 436 | from the session. This is used by the "open in pikchrshow" |
| 437 | link in the forum. */ |
| 438 | const src = window.sessionStorage.getItem('pikchr-xfer'); |
| 439 | if( src && (new URL(self.location.href).searchParams).has('fromSession') ){ |
| 440 | taInput.value = src; |
| 441 | window.sessionStorage.removeItem('pikchr-xfer'); |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | PS.e.btnRender.click(); |
| 446 | |
| 447 | /** Debounce handler for auto-rendering while typing. */ |
| 448 | const debounceAutoRender = F.debounce(function f(){ |
| 449 | if(!PS._isDirty) return; |
| 450 | const text = getCurrentText(); |
| 451 | if(f._ === text){ |
| @@ -618,11 +630,11 @@ | |
| 630 | "Darlene" above aligned |
| 631 | |
| 632 | # fill in content for the Alice lane |
| 633 | right |
| 634 | A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ |
| 635 | fill white thickness 1.5px "1" |
| 636 | arrow right 50% |
| 637 | circle same "2" |
| 638 | arrow right until even with first box.e - (0.65,0.0) |
| 639 | ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px |
| 640 | A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 |
| 641 |
+54
-6
| --- src/fossil.pikchr.js | ||
| +++ src/fossil.pikchr.js | ||
| @@ -38,11 +38,14 @@ | ||
| 38 | 38 | This code expects the following structure around the SVGs, and |
| 39 | 39 | will not process any which don't match this: |
| 40 | 40 | |
| 41 | 41 | <DIV.pikchr-wrapper> |
| 42 | 42 | <DIV.pikchr-svg><SVG.pikchr></SVG></DIV> |
| 43 | - <PRE.pikchr-src></PRE> | |
| 43 | + <DIV.pikchr-src> | |
| 44 | + <PRE>pikchr source code</PRE> | |
| 45 | + <SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> | |
| 46 | + </DIV> | |
| 44 | 47 | </DIV> |
| 45 | 48 | */ |
| 46 | 49 | P.addSrcView = function f(svg){ |
| 47 | 50 | if(!f.hasOwnProperty('parentClick')){ |
| 48 | 51 | f.parentClick = function(ev){ |
| @@ -58,10 +61,46 @@ | ||
| 58 | 61 | this.classList.toggle('source'); |
| 59 | 62 | ev.stopPropagation(); |
| 60 | 63 | ev.preventDefault(); |
| 61 | 64 | } |
| 62 | 65 | }; |
| 66 | + /** | |
| 67 | + Event handler for the "open in pikchrshow" links: store the | |
| 68 | + source code for the link's pikchr in | |
| 69 | + window.sessionStorage['pikchr-xfer'] then open | |
| 70 | + /pikchrshow?fromSession to trigger loading of that pikchr. | |
| 71 | + */ | |
| 72 | + f.clickPikchrShow = function(ev){ | |
| 73 | + const pId = this.dataset['pikchrid'] /* ID of the associated pikchr source code element */; | |
| 74 | + if(!pId) return; | |
| 75 | + const ePikchr = this.parentNode.parentNode.querySelector('#'+pId); | |
| 76 | + if(!ePikchr) return; | |
| 77 | + ev.stopPropagation() /* keep pikchr source view from toggling */; | |
| 78 | + window.sessionStorage.setItem('pikchr-xfer', ePikchr.innerText); | |
| 79 | + /* | |
| 80 | + After returning from this function the link element will | |
| 81 | + open [/pikchrshow?fromSession], and pikchrshow will extract | |
| 82 | + the pikchr source code from sessionStorage['pikchr-xfer']. | |
| 83 | + | |
| 84 | + Quirks of this ^^^ design: | |
| 85 | + | |
| 86 | + We use only a single slot in sessionStorage. We could | |
| 87 | + alternately use a key like pikchr-$pId and pass that key on | |
| 88 | + to /pikchrshow via fromSession=pikchr-$pId, but that would | |
| 89 | + eventually lead to stale session entries if loading of | |
| 90 | + pikchrshow were interrupted at an untimely point. The | |
| 91 | + down-side of _not_ doing that is that some user (or | |
| 92 | + automation) options multiple "open in pikchrshow" links | |
| 93 | + rapidly enough, the will open the same pikchr (the one which | |
| 94 | + was stored in the session's slot most recently). The | |
| 95 | + current approach should be fine for normal human interaction | |
| 96 | + speeds, but if it proves to be a problem we can instead use | |
| 97 | + the above-described approach of storing each pikchr in its | |
| 98 | + own session slot and simply accept that there may be stale | |
| 99 | + entries at some point. | |
| 100 | + */ | |
| 101 | + }; | |
| 63 | 102 | }; |
| 64 | 103 | if(!svg) svg = 'svg.pikchr'; |
| 65 | 104 | if('string' === typeof svg){ |
| 66 | 105 | document.querySelectorAll(svg).forEach((e)=>f.call(this, e)); |
| 67 | 106 | return this; |
| @@ -71,15 +110,24 @@ | ||
| 71 | 110 | } |
| 72 | 111 | if(svg.dataset.pikchrProcessed){ |
| 73 | 112 | return this; |
| 74 | 113 | } |
| 75 | 114 | svg.dataset.pikchrProcessed = 1; |
| 76 | - const parent = svg.parentNode.parentNode /* outermost div.pikchr-wrapper */; | |
| 77 | - const srcView = parent ? svg.parentNode.nextElementSibling : undefined; | |
| 78 | - if(!srcView || !srcView.classList.contains('pikchr-src')){ | |
| 115 | + const parent = svg.parentNode.parentNode /* outermost DIV.pikchr-wrapper */; | |
| 116 | + const srcView = parent ? svg.parentNode.nextElementSibling /* DIV.pikchr-src */ : undefined; | |
| 117 | + if(srcView && srcView.classList.contains('pikchr-src')){ | |
| 79 | 118 | /* Without this element, there's nothing for us to do here. */ |
| 80 | - return this; | |
| 119 | + parent.addEventListener('click', f.parentClick, false); | |
| 120 | + const eSpan = window.sessionStorage | |
| 121 | + ? srcView.querySelector('span') /* "open in..." link wrapper */ | |
| 122 | + : undefined; | |
| 123 | + if(eSpan){ | |
| 124 | + const openLink = eSpan.querySelector('a'); | |
| 125 | + if(openLink){ | |
| 126 | + openLink.addEventListener('click', f.clickPikchrShow, false); | |
| 127 | + eSpan.classList.remove('hidden'); | |
| 128 | + } | |
| 129 | + } | |
| 81 | 130 | } |
| 82 | - parent.addEventListener('click', f.parentClick, false); | |
| 83 | 131 | return this; |
| 84 | 132 | }; |
| 85 | 133 | })(window.fossil); |
| 86 | 134 |
| --- src/fossil.pikchr.js | |
| +++ src/fossil.pikchr.js | |
| @@ -38,11 +38,14 @@ | |
| 38 | This code expects the following structure around the SVGs, and |
| 39 | will not process any which don't match this: |
| 40 | |
| 41 | <DIV.pikchr-wrapper> |
| 42 | <DIV.pikchr-svg><SVG.pikchr></SVG></DIV> |
| 43 | <PRE.pikchr-src></PRE> |
| 44 | </DIV> |
| 45 | */ |
| 46 | P.addSrcView = function f(svg){ |
| 47 | if(!f.hasOwnProperty('parentClick')){ |
| 48 | f.parentClick = function(ev){ |
| @@ -58,10 +61,46 @@ | |
| 58 | this.classList.toggle('source'); |
| 59 | ev.stopPropagation(); |
| 60 | ev.preventDefault(); |
| 61 | } |
| 62 | }; |
| 63 | }; |
| 64 | if(!svg) svg = 'svg.pikchr'; |
| 65 | if('string' === typeof svg){ |
| 66 | document.querySelectorAll(svg).forEach((e)=>f.call(this, e)); |
| 67 | return this; |
| @@ -71,15 +110,24 @@ | |
| 71 | } |
| 72 | if(svg.dataset.pikchrProcessed){ |
| 73 | return this; |
| 74 | } |
| 75 | svg.dataset.pikchrProcessed = 1; |
| 76 | const parent = svg.parentNode.parentNode /* outermost div.pikchr-wrapper */; |
| 77 | const srcView = parent ? svg.parentNode.nextElementSibling : undefined; |
| 78 | if(!srcView || !srcView.classList.contains('pikchr-src')){ |
| 79 | /* Without this element, there's nothing for us to do here. */ |
| 80 | return this; |
| 81 | } |
| 82 | parent.addEventListener('click', f.parentClick, false); |
| 83 | return this; |
| 84 | }; |
| 85 | })(window.fossil); |
| 86 |
| --- src/fossil.pikchr.js | |
| +++ src/fossil.pikchr.js | |
| @@ -38,11 +38,14 @@ | |
| 38 | This code expects the following structure around the SVGs, and |
| 39 | will not process any which don't match this: |
| 40 | |
| 41 | <DIV.pikchr-wrapper> |
| 42 | <DIV.pikchr-svg><SVG.pikchr></SVG></DIV> |
| 43 | <DIV.pikchr-src> |
| 44 | <PRE>pikchr source code</PRE> |
| 45 | <SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> |
| 46 | </DIV> |
| 47 | </DIV> |
| 48 | */ |
| 49 | P.addSrcView = function f(svg){ |
| 50 | if(!f.hasOwnProperty('parentClick')){ |
| 51 | f.parentClick = function(ev){ |
| @@ -58,10 +61,46 @@ | |
| 61 | this.classList.toggle('source'); |
| 62 | ev.stopPropagation(); |
| 63 | ev.preventDefault(); |
| 64 | } |
| 65 | }; |
| 66 | /** |
| 67 | Event handler for the "open in pikchrshow" links: store the |
| 68 | source code for the link's pikchr in |
| 69 | window.sessionStorage['pikchr-xfer'] then open |
| 70 | /pikchrshow?fromSession to trigger loading of that pikchr. |
| 71 | */ |
| 72 | f.clickPikchrShow = function(ev){ |
| 73 | const pId = this.dataset['pikchrid'] /* ID of the associated pikchr source code element */; |
| 74 | if(!pId) return; |
| 75 | const ePikchr = this.parentNode.parentNode.querySelector('#'+pId); |
| 76 | if(!ePikchr) return; |
| 77 | ev.stopPropagation() /* keep pikchr source view from toggling */; |
| 78 | window.sessionStorage.setItem('pikchr-xfer', ePikchr.innerText); |
| 79 | /* |
| 80 | After returning from this function the link element will |
| 81 | open [/pikchrshow?fromSession], and pikchrshow will extract |
| 82 | the pikchr source code from sessionStorage['pikchr-xfer']. |
| 83 | |
| 84 | Quirks of this ^^^ design: |
| 85 | |
| 86 | We use only a single slot in sessionStorage. We could |
| 87 | alternately use a key like pikchr-$pId and pass that key on |
| 88 | to /pikchrshow via fromSession=pikchr-$pId, but that would |
| 89 | eventually lead to stale session entries if loading of |
| 90 | pikchrshow were interrupted at an untimely point. The |
| 91 | down-side of _not_ doing that is that some user (or |
| 92 | automation) options multiple "open in pikchrshow" links |
| 93 | rapidly enough, the will open the same pikchr (the one which |
| 94 | was stored in the session's slot most recently). The |
| 95 | current approach should be fine for normal human interaction |
| 96 | speeds, but if it proves to be a problem we can instead use |
| 97 | the above-described approach of storing each pikchr in its |
| 98 | own session slot and simply accept that there may be stale |
| 99 | entries at some point. |
| 100 | */ |
| 101 | }; |
| 102 | }; |
| 103 | if(!svg) svg = 'svg.pikchr'; |
| 104 | if('string' === typeof svg){ |
| 105 | document.querySelectorAll(svg).forEach((e)=>f.call(this, e)); |
| 106 | return this; |
| @@ -71,15 +110,24 @@ | |
| 110 | } |
| 111 | if(svg.dataset.pikchrProcessed){ |
| 112 | return this; |
| 113 | } |
| 114 | svg.dataset.pikchrProcessed = 1; |
| 115 | const parent = svg.parentNode.parentNode /* outermost DIV.pikchr-wrapper */; |
| 116 | const srcView = parent ? svg.parentNode.nextElementSibling /* DIV.pikchr-src */ : undefined; |
| 117 | if(srcView && srcView.classList.contains('pikchr-src')){ |
| 118 | /* Without this element, there's nothing for us to do here. */ |
| 119 | parent.addEventListener('click', f.parentClick, false); |
| 120 | const eSpan = window.sessionStorage |
| 121 | ? srcView.querySelector('span') /* "open in..." link wrapper */ |
| 122 | : undefined; |
| 123 | if(eSpan){ |
| 124 | const openLink = eSpan.querySelector('a'); |
| 125 | if(openLink){ |
| 126 | openLink.addEventListener('click', f.clickPikchrShow, false); |
| 127 | eSpan.classList.remove('hidden'); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | return this; |
| 132 | }; |
| 133 | })(window.fossil); |
| 134 |
+2
-2
| --- src/glob.c | ||
| +++ src/glob.c | ||
| @@ -34,12 +34,12 @@ | ||
| 34 | 34 | ** Commas and whitespace are considered to be element delimters. Each |
| 35 | 35 | ** element of the GLOB list may optionally be enclosed in either '...' or |
| 36 | 36 | ** "...". This allows commas and/or whitespace to be used in the elements |
| 37 | 37 | ** themselves. |
| 38 | 38 | ** |
| 39 | -** This routine makes no effort to free the memory space it uses, which | |
| 40 | -** currently consists of a blob object and its contents. | |
| 39 | +** The returned string is owned by the caller, who must fossil_free() | |
| 40 | +** it. | |
| 41 | 41 | */ |
| 42 | 42 | char *glob_expr(const char *zVal, const char *zGlobList){ |
| 43 | 43 | Blob expr; |
| 44 | 44 | const char *zSep = "("; |
| 45 | 45 | int nTerm = 0; |
| 46 | 46 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -34,12 +34,12 @@ | |
| 34 | ** Commas and whitespace are considered to be element delimters. Each |
| 35 | ** element of the GLOB list may optionally be enclosed in either '...' or |
| 36 | ** "...". This allows commas and/or whitespace to be used in the elements |
| 37 | ** themselves. |
| 38 | ** |
| 39 | ** This routine makes no effort to free the memory space it uses, which |
| 40 | ** currently consists of a blob object and its contents. |
| 41 | */ |
| 42 | char *glob_expr(const char *zVal, const char *zGlobList){ |
| 43 | Blob expr; |
| 44 | const char *zSep = "("; |
| 45 | int nTerm = 0; |
| 46 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -34,12 +34,12 @@ | |
| 34 | ** Commas and whitespace are considered to be element delimters. Each |
| 35 | ** element of the GLOB list may optionally be enclosed in either '...' or |
| 36 | ** "...". This allows commas and/or whitespace to be used in the elements |
| 37 | ** themselves. |
| 38 | ** |
| 39 | ** The returned string is owned by the caller, who must fossil_free() |
| 40 | ** it. |
| 41 | */ |
| 42 | char *glob_expr(const char *zVal, const char *zGlobList){ |
| 43 | Blob expr; |
| 44 | const char *zSep = "("; |
| 45 | int nTerm = 0; |
| 46 |
+6
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -768,10 +768,11 @@ | ||
| 768 | 768 | ** a GET request where there is no PAYLOAD. |
| 769 | 769 | ** |
| 770 | 770 | ** Options: |
| 771 | 771 | ** --compress Use ZLIB compression on the payload |
| 772 | 772 | ** --mimetype TYPE Mimetype of the payload |
| 773 | +** --no-cert-verify Disable TLS cert verification | |
| 773 | 774 | ** --out FILE Store the reply in FILE |
| 774 | 775 | ** -v Verbose output |
| 775 | 776 | ** --xfer PAYLOAD in a Fossil xfer protocol message |
| 776 | 777 | */ |
| 777 | 778 | void test_httpmsg_command(void){ |
| @@ -783,10 +784,15 @@ | ||
| 783 | 784 | |
| 784 | 785 | zMimetype = find_option("mimetype",0,1); |
| 785 | 786 | zOutFile = find_option("out","o",1); |
| 786 | 787 | if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE; |
| 787 | 788 | if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS; |
| 789 | + if( find_option("no-cert-verify",0,0)!=0 ){ | |
| 790 | + #ifdef FOSSIL_ENABLE_SSL | |
| 791 | + ssl_disable_cert_verification(); | |
| 792 | + #endif | |
| 793 | + } | |
| 788 | 794 | if( find_option("xfer",0,0)!=0 ){ |
| 789 | 795 | mHttpFlags |= HTTP_USE_LOGIN; |
| 790 | 796 | mHttpFlags &= ~HTTP_GENERIC; |
| 791 | 797 | } |
| 792 | 798 | verify_all_options(); |
| 793 | 799 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -768,10 +768,11 @@ | |
| 768 | ** a GET request where there is no PAYLOAD. |
| 769 | ** |
| 770 | ** Options: |
| 771 | ** --compress Use ZLIB compression on the payload |
| 772 | ** --mimetype TYPE Mimetype of the payload |
| 773 | ** --out FILE Store the reply in FILE |
| 774 | ** -v Verbose output |
| 775 | ** --xfer PAYLOAD in a Fossil xfer protocol message |
| 776 | */ |
| 777 | void test_httpmsg_command(void){ |
| @@ -783,10 +784,15 @@ | |
| 783 | |
| 784 | zMimetype = find_option("mimetype",0,1); |
| 785 | zOutFile = find_option("out","o",1); |
| 786 | if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE; |
| 787 | if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS; |
| 788 | if( find_option("xfer",0,0)!=0 ){ |
| 789 | mHttpFlags |= HTTP_USE_LOGIN; |
| 790 | mHttpFlags &= ~HTTP_GENERIC; |
| 791 | } |
| 792 | verify_all_options(); |
| 793 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -768,10 +768,11 @@ | |
| 768 | ** a GET request where there is no PAYLOAD. |
| 769 | ** |
| 770 | ** Options: |
| 771 | ** --compress Use ZLIB compression on the payload |
| 772 | ** --mimetype TYPE Mimetype of the payload |
| 773 | ** --no-cert-verify Disable TLS cert verification |
| 774 | ** --out FILE Store the reply in FILE |
| 775 | ** -v Verbose output |
| 776 | ** --xfer PAYLOAD in a Fossil xfer protocol message |
| 777 | */ |
| 778 | void test_httpmsg_command(void){ |
| @@ -783,10 +784,15 @@ | |
| 784 | |
| 785 | zMimetype = find_option("mimetype",0,1); |
| 786 | zOutFile = find_option("out","o",1); |
| 787 | if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE; |
| 788 | if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS; |
| 789 | if( find_option("no-cert-verify",0,0)!=0 ){ |
| 790 | #ifdef FOSSIL_ENABLE_SSL |
| 791 | ssl_disable_cert_verification(); |
| 792 | #endif |
| 793 | } |
| 794 | if( find_option("xfer",0,0)!=0 ){ |
| 795 | mHttpFlags |= HTTP_USE_LOGIN; |
| 796 | mHttpFlags &= ~HTTP_GENERIC; |
| 797 | } |
| 798 | verify_all_options(); |
| 799 |
+4
-6
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -403,16 +403,14 @@ | ||
| 403 | 403 | blob_append(&reply, buf, len); |
| 404 | 404 | |
| 405 | 405 | bbuf = blob_buffer(&reply); |
| 406 | 406 | len = blob_size(&reply); |
| 407 | 407 | while(end < len) { |
| 408 | - if(bbuf[end] == '\r') { | |
| 409 | - if(len - end < 4) { | |
| 410 | - /* need more data */ | |
| 411 | - break; | |
| 412 | - } | |
| 413 | - if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) { | |
| 408 | + if( bbuf[end]=='\n' ) { | |
| 409 | + if( (end+1<len && bbuf[end+1]=='\n') | |
| 410 | + || (end+2<len && bbuf[end+1]=='\r' && bbuf[end+2]=='\n') | |
| 411 | + ){ | |
| 414 | 412 | done = 1; |
| 415 | 413 | break; |
| 416 | 414 | } |
| 417 | 415 | } |
| 418 | 416 | end++; |
| 419 | 417 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -403,16 +403,14 @@ | |
| 403 | blob_append(&reply, buf, len); |
| 404 | |
| 405 | bbuf = blob_buffer(&reply); |
| 406 | len = blob_size(&reply); |
| 407 | while(end < len) { |
| 408 | if(bbuf[end] == '\r') { |
| 409 | if(len - end < 4) { |
| 410 | /* need more data */ |
| 411 | break; |
| 412 | } |
| 413 | if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) { |
| 414 | done = 1; |
| 415 | break; |
| 416 | } |
| 417 | } |
| 418 | end++; |
| 419 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -403,16 +403,14 @@ | |
| 403 | blob_append(&reply, buf, len); |
| 404 | |
| 405 | bbuf = blob_buffer(&reply); |
| 406 | len = blob_size(&reply); |
| 407 | while(end < len) { |
| 408 | if( bbuf[end]=='\n' ) { |
| 409 | if( (end+1<len && bbuf[end+1]=='\n') |
| 410 | || (end+2<len && bbuf[end+1]=='\r' && bbuf[end+2]=='\n') |
| 411 | ){ |
| 412 | done = 1; |
| 413 | break; |
| 414 | } |
| 415 | } |
| 416 | end++; |
| 417 |
+4
-1
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -103,11 +103,14 @@ | ||
| 103 | 103 | /* |
| 104 | 104 | ** Initialize a Blob to the name of the configured SSH command. |
| 105 | 105 | */ |
| 106 | 106 | void transport_ssh_command(Blob *p){ |
| 107 | 107 | char *zSsh; /* The base SSH command */ |
| 108 | - zSsh = db_get("ssh-command", zDefaultSshCmd); | |
| 108 | + zSsh = g.zSshCmd; | |
| 109 | + if( zSsh==0 || zSsh[0]==0 ){ | |
| 110 | + zSsh = db_get("ssh-command", zDefaultSshCmd); | |
| 111 | + } | |
| 109 | 112 | blob_init(p, zSsh, -1); |
| 110 | 113 | } |
| 111 | 114 | |
| 112 | 115 | /* |
| 113 | 116 | ** SSH initialization of the transport layer |
| 114 | 117 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -103,11 +103,14 @@ | |
| 103 | /* |
| 104 | ** Initialize a Blob to the name of the configured SSH command. |
| 105 | */ |
| 106 | void transport_ssh_command(Blob *p){ |
| 107 | char *zSsh; /* The base SSH command */ |
| 108 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 109 | blob_init(p, zSsh, -1); |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** SSH initialization of the transport layer |
| 114 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -103,11 +103,14 @@ | |
| 103 | /* |
| 104 | ** Initialize a Blob to the name of the configured SSH command. |
| 105 | */ |
| 106 | void transport_ssh_command(Blob *p){ |
| 107 | char *zSsh; /* The base SSH command */ |
| 108 | zSsh = g.zSshCmd; |
| 109 | if( zSsh==0 || zSsh[0]==0 ){ |
| 110 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 111 | } |
| 112 | blob_init(p, zSsh, -1); |
| 113 | } |
| 114 | |
| 115 | /* |
| 116 | ** SSH initialization of the transport layer |
| 117 |
+152
-17
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -372,11 +372,13 @@ | ||
| 372 | 372 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 373 | 373 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 374 | 374 | DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ |
| 375 | 375 | int mperm /* executable or symlink permission for zNew */ |
| 376 | 376 | ){ |
| 377 | - @ <p> | |
| 377 | + @ <div class='file-change-line'><span> | |
| 378 | + /* Maintenance reminder: the extra level of SPAN is for | |
| 379 | + ** arranging new elements via JS. */ | |
| 378 | 380 | if( !g.perm.Hyperlink ){ |
| 379 | 381 | if( zNew==0 ){ |
| 380 | 382 | @ Deleted %h(zName). |
| 381 | 383 | }else if( zOld==0 ){ |
| 382 | 384 | @ Added %h(zName). |
| @@ -391,10 +393,11 @@ | ||
| 391 | 393 | @ %h(zName) became a regular file. |
| 392 | 394 | } |
| 393 | 395 | }else{ |
| 394 | 396 | @ Changes to %h(zName). |
| 395 | 397 | } |
| 398 | + @ </span></div> | |
| 396 | 399 | if( pCfg ){ |
| 397 | 400 | append_diff(zOld, zNew, pCfg); |
| 398 | 401 | } |
| 399 | 402 | }else{ |
| 400 | 403 | if( zOld && zNew ){ |
| @@ -436,18 +439,22 @@ | ||
| 436 | 439 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 437 | 440 | }else{ |
| 438 | 441 | @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ |
| 439 | 442 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 440 | 443 | } |
| 441 | - if( pCfg ){ | |
| 442 | - append_diff(zOld, zNew, pCfg); | |
| 443 | - }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ | |
| 444 | - @ | |
| 445 | - @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a> | |
| 444 | + if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ | |
| 445 | + if( pCfg ){ | |
| 446 | + @ </span></div> | |
| 447 | + append_diff(zOld, zNew, pCfg); | |
| 448 | + }else{ | |
| 449 | + @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a> | |
| 450 | + @ </span></div> | |
| 451 | + } | |
| 452 | + }else{ | |
| 453 | + @ </span></div> | |
| 446 | 454 | } |
| 447 | 455 | } |
| 448 | - @ </p> | |
| 449 | 456 | } |
| 450 | 457 | |
| 451 | 458 | /* |
| 452 | 459 | ** Generate javascript to enhance HTML diffs. |
| 453 | 460 | */ |
| @@ -600,10 +607,130 @@ | ||
| 600 | 607 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 601 | 608 | 0, 0, 0, rid, 0, 0); |
| 602 | 609 | db_finalize(&q); |
| 603 | 610 | style_finish_page(); |
| 604 | 611 | } |
| 612 | + | |
| 613 | +/* | |
| 614 | +** WEBPAGE: ckout | |
| 615 | +** | |
| 616 | +** Show information about the current checkout. This page only functions | |
| 617 | +** if the web server is run on a loopback interface (in other words, was | |
| 618 | +** started using "fossil ui" or similar) from with on open check-out. | |
| 619 | +*/ | |
| 620 | +void ckout_page(void){ | |
| 621 | + int vid; | |
| 622 | + char *zHostname; | |
| 623 | + char *zCwd; | |
| 624 | + int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ | |
| 625 | + DiffConfig DCfg,*pCfg; /* Diff details */ | |
| 626 | + const char *zHome; /* Home directory */ | |
| 627 | + Stmt q; | |
| 628 | + | |
| 629 | + if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ | |
| 630 | + cgi_redirect("%R/home"); | |
| 631 | + return; | |
| 632 | + } | |
| 633 | + diffType = preferred_diff_type(); | |
| 634 | + pCfg = construct_diff_flags(diffType, &DCfg); | |
| 635 | + vid = db_lget_int("checkout", 0); | |
| 636 | + db_unprotect(PROTECT_ALL); | |
| 637 | + vfile_check_signature(vid, CKSIG_ENOTFILE); | |
| 638 | + db_protect_pop(); | |
| 639 | + style_set_current_feature("vinfo"); | |
| 640 | + zHostname = fossil_hostname(); | |
| 641 | + zCwd = file_getcwd(0,0); | |
| 642 | + zHome = fossil_getenv("HOME"); | |
| 643 | + if( zHome ){ | |
| 644 | + int nHome = (int)strlen(zHome); | |
| 645 | + if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){ | |
| 646 | + zCwd = mprintf("~%s", zCwd+nHome); | |
| 647 | + } | |
| 648 | + } | |
| 649 | + if( zHostname ){ | |
| 650 | + style_header("Checkout Status: %h on %h", zCwd, zHostname); | |
| 651 | + }else{ | |
| 652 | + style_header("Checkout Status: %h", zCwd); | |
| 653 | + } | |
| 654 | + render_checkin_context(vid, 0, 0, 0); | |
| 655 | + if( pCfg==0 ){ | |
| 656 | + style_finish_page(); | |
| 657 | + return; | |
| 658 | + } | |
| 659 | + db_prepare(&q, | |
| 660 | + /* 0 1 2 3 4 5 6 */ | |
| 661 | + "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" | |
| 662 | + " FROM vfile LEFT JOIN blob USING(rid)" | |
| 663 | + " WHERE vid=%d" | |
| 664 | + " AND (deleted OR chnged OR rid==0)" | |
| 665 | + " ORDER BY pathname /*scan*/", | |
| 666 | + vid | |
| 667 | + ); | |
| 668 | + if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){ | |
| 669 | + pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; | |
| 670 | + }else{ | |
| 671 | + pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; | |
| 672 | + } | |
| 673 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 674 | + const char *zTreename = db_column_text(&q,0); | |
| 675 | + int isDeleted = db_column_int(&q, 1); | |
| 676 | + int isChnged = db_column_int(&q,2); | |
| 677 | + int isNew = db_column_int(&q,3); | |
| 678 | + int srcid = db_column_int(&q, 4); | |
| 679 | + int isLink = db_column_int(&q, 5); | |
| 680 | + const char *zUuid = db_column_text(&q, 6); | |
| 681 | + int showDiff = 1; | |
| 682 | + | |
| 683 | + pCfg->diffFlags &= (~DIFF_FILE_MASK); | |
| 684 | + if( isDeleted ){ | |
| 685 | + @ <p>DELETED %h(zTreename)</p> | |
| 686 | + pCfg->diffFlags |= DIFF_FILE_DELETED; | |
| 687 | + showDiff = 0; | |
| 688 | + }else if( file_access(zTreename, F_OK) ){ | |
| 689 | + @ <p>MISSING %h(zTreename)</p> | |
| 690 | + showDiff = 0; | |
| 691 | + }else if( isNew ){ | |
| 692 | + @ <p>ADDED %h(zTreename)</p> | |
| 693 | + pCfg->diffFlags |= DIFF_FILE_ADDED; | |
| 694 | + srcid = 0; | |
| 695 | + showDiff = 0; | |
| 696 | + }else if( isChnged==3 ){ | |
| 697 | + @ <p>ADDED_BY_MERGE %h(zTreename)</p> | |
| 698 | + pCfg->diffFlags |= DIFF_FILE_ADDED; | |
| 699 | + srcid = 0; | |
| 700 | + showDiff = 0; | |
| 701 | + }else if( isChnged==5 ){ | |
| 702 | + @ <p>ADDED_BY_INTEGRATE %h(zTreename)</p> | |
| 703 | + pCfg->diffFlags |= DIFF_FILE_ADDED; | |
| 704 | + srcid = 0; | |
| 705 | + showDiff = 0; | |
| 706 | + }else{ | |
| 707 | + @ <p>CHANGED %h(zTreename)</p> | |
| 708 | + } | |
| 709 | + if( showDiff ){ | |
| 710 | + Blob old, new; | |
| 711 | + if( !isLink != !file_islink(zTreename) ){ | |
| 712 | + @ %s(DIFF_CANNOT_COMPUTE_SYMLINK) | |
| 713 | + continue; | |
| 714 | + } | |
| 715 | + if( srcid>0 ){ | |
| 716 | + content_get(srcid, &old); | |
| 717 | + pCfg->zLeftHash = zUuid; | |
| 718 | + }else{ | |
| 719 | + blob_zero(&old); | |
| 720 | + pCfg->zLeftHash = 0; | |
| 721 | + } | |
| 722 | + blob_read_from_file(&new, zTreename, ExtFILE); | |
| 723 | + text_diff(&old, &new, cgi_output_blob(), pCfg); | |
| 724 | + blob_reset(&old); | |
| 725 | + blob_reset(&new); | |
| 726 | + } | |
| 727 | + } | |
| 728 | + db_finalize(&q); | |
| 729 | + append_diff_javascript(diffType); | |
| 730 | + style_finish_page(); | |
| 731 | +} | |
| 605 | 732 | |
| 606 | 733 | /* |
| 607 | 734 | ** WEBPAGE: vinfo |
| 608 | 735 | ** WEBPAGE: ci |
| 609 | 736 | ** URL: /ci/ARTIFACTID |
| @@ -1191,22 +1318,25 @@ | ||
| 1191 | 1318 | const char *zBranch; |
| 1192 | 1319 | const char *zFrom; |
| 1193 | 1320 | const char *zTo; |
| 1194 | 1321 | const char *zRe; |
| 1195 | 1322 | const char *zGlob; |
| 1323 | + Glob * pGlob = 0; | |
| 1196 | 1324 | char *zMergeOrigin = 0; |
| 1197 | 1325 | ReCompiled *pRe = 0; |
| 1198 | 1326 | DiffConfig DCfg, *pCfg = 0; |
| 1199 | 1327 | int graphFlags = 0; |
| 1200 | - Blob qp; | |
| 1328 | + Blob qp; /* non-glob= query parameters for generated links */ | |
| 1329 | + Blob qpGlob; /* glob= query parameter for generated links */ | |
| 1201 | 1330 | int bInvert = PB("inv"); |
| 1202 | 1331 | |
| 1203 | 1332 | login_check_credentials(); |
| 1204 | 1333 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1205 | 1334 | login_anonymous_available(); |
| 1206 | 1335 | fossil_nice_default(); |
| 1207 | 1336 | blob_init(&qp, 0, 0); |
| 1337 | + blob_init(&qpGlob, 0, 0); | |
| 1208 | 1338 | diffType = preferred_diff_type(); |
| 1209 | 1339 | zRe = P("regex"); |
| 1210 | 1340 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1211 | 1341 | zBranch = P("branch"); |
| 1212 | 1342 | if( zBranch && zBranch[0]==0 ) zBranch = 0; |
| @@ -1246,11 +1376,12 @@ | ||
| 1246 | 1376 | } |
| 1247 | 1377 | if( zGlob ){ |
| 1248 | 1378 | if( !*zGlob ){ |
| 1249 | 1379 | zGlob = NULL; |
| 1250 | 1380 | }else{ |
| 1251 | - blob_appendf(&qp, "&glob=%T", zGlob); | |
| 1381 | + blob_appendf(&qpGlob, "&glob=%T", zGlob); | |
| 1382 | + pGlob = glob_create(zGlob); | |
| 1252 | 1383 | } |
| 1253 | 1384 | } |
| 1254 | 1385 | if( PB("nc") ){ |
| 1255 | 1386 | graphFlags |= TIMELINE_NOCOLOR; |
| 1256 | 1387 | blob_appendf(&qp, "&nc"); |
| @@ -1263,20 +1394,22 @@ | ||
| 1263 | 1394 | style_set_current_feature("vdiff"); |
| 1264 | 1395 | if( zBranch==0 ){ |
| 1265 | 1396 | style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo); |
| 1266 | 1397 | } |
| 1267 | 1398 | if( diffType!=0 ){ |
| 1268 | - style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b", &qp); | |
| 1399 | + style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob); | |
| 1269 | 1400 | } |
| 1270 | 1401 | if( diffType!=2 ){ |
| 1271 | - style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b", &qp); | |
| 1402 | + style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp, | |
| 1403 | + &qpGlob); | |
| 1272 | 1404 | } |
| 1273 | 1405 | if( diffType!=1 ) { |
| 1274 | - style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b", &qp); | |
| 1406 | + style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b%b", &qp, &qpGlob); | |
| 1275 | 1407 | } |
| 1276 | 1408 | if( zBranch==0 ){ |
| 1277 | - style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b", diffType, &qp); | |
| 1409 | + style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b%b", diffType, | |
| 1410 | + &qp, &qpGlob); | |
| 1278 | 1411 | } |
| 1279 | 1412 | if( zGlob ){ |
| 1280 | 1413 | style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp); |
| 1281 | 1414 | }else{ |
| 1282 | 1415 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, |
| @@ -1321,10 +1454,11 @@ | ||
| 1321 | 1454 | @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> |
| 1322 | 1455 | } |
| 1323 | 1456 | @<hr><p> |
| 1324 | 1457 | } |
| 1325 | 1458 | blob_reset(&qp); |
| 1459 | + blob_reset(&qpGlob); | |
| 1326 | 1460 | |
| 1327 | 1461 | manifest_file_rewind(pFrom); |
| 1328 | 1462 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1329 | 1463 | manifest_file_rewind(pTo); |
| 1330 | 1464 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1337,37 +1471,38 @@ | ||
| 1337 | 1471 | cmp = -1; |
| 1338 | 1472 | }else{ |
| 1339 | 1473 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 1340 | 1474 | } |
| 1341 | 1475 | if( cmp<0 ){ |
| 1342 | - if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){ | |
| 1476 | + if( !pGlob || glob_match(pGlob, pFileFrom->zName) ){ | |
| 1343 | 1477 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1344 | 1478 | pFileFrom->zUuid, 0, 0, pCfg, 0); |
| 1345 | 1479 | } |
| 1346 | 1480 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1347 | 1481 | }else if( cmp>0 ){ |
| 1348 | - if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){ | |
| 1482 | + if( !pGlob || glob_match(pGlob, pFileTo->zName) ){ | |
| 1349 | 1483 | append_file_change_line(zTo, pFileTo->zName, |
| 1350 | 1484 | 0, pFileTo->zUuid, 0, pCfg, |
| 1351 | 1485 | manifest_file_mperm(pFileTo)); |
| 1352 | 1486 | } |
| 1353 | 1487 | pFileTo = manifest_file_next(pTo, 0); |
| 1354 | 1488 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1355 | 1489 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1356 | 1490 | pFileTo = manifest_file_next(pTo, 0); |
| 1357 | 1491 | }else{ |
| 1358 | - if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0 | |
| 1359 | - || sqlite3_strglob(zGlob, pFileTo->zName)==0) ){ | |
| 1492 | + if(!pGlob || (glob_match(pGlob, pFileFrom->zName) | |
| 1493 | + || glob_match(pGlob, pFileTo->zName)) ){ | |
| 1360 | 1494 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1361 | 1495 | pFileFrom->zUuid, |
| 1362 | 1496 | pFileTo->zUuid, 0, pCfg, |
| 1363 | 1497 | manifest_file_mperm(pFileTo)); |
| 1364 | 1498 | } |
| 1365 | 1499 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1366 | 1500 | pFileTo = manifest_file_next(pTo, 0); |
| 1367 | 1501 | } |
| 1368 | 1502 | } |
| 1503 | + glob_free(pGlob); | |
| 1369 | 1504 | manifest_destroy(pFrom); |
| 1370 | 1505 | manifest_destroy(pTo); |
| 1371 | 1506 | append_diff_javascript(diffType); |
| 1372 | 1507 | style_finish_page(); |
| 1373 | 1508 | } |
| 1374 | 1509 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -372,11 +372,13 @@ | |
| 372 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 373 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 374 | DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ |
| 375 | int mperm /* executable or symlink permission for zNew */ |
| 376 | ){ |
| 377 | @ <p> |
| 378 | if( !g.perm.Hyperlink ){ |
| 379 | if( zNew==0 ){ |
| 380 | @ Deleted %h(zName). |
| 381 | }else if( zOld==0 ){ |
| 382 | @ Added %h(zName). |
| @@ -391,10 +393,11 @@ | |
| 391 | @ %h(zName) became a regular file. |
| 392 | } |
| 393 | }else{ |
| 394 | @ Changes to %h(zName). |
| 395 | } |
| 396 | if( pCfg ){ |
| 397 | append_diff(zOld, zNew, pCfg); |
| 398 | } |
| 399 | }else{ |
| 400 | if( zOld && zNew ){ |
| @@ -436,18 +439,22 @@ | |
| 436 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 437 | }else{ |
| 438 | @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ |
| 439 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 440 | } |
| 441 | if( pCfg ){ |
| 442 | append_diff(zOld, zNew, pCfg); |
| 443 | }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 444 | @ |
| 445 | @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a> |
| 446 | } |
| 447 | } |
| 448 | @ </p> |
| 449 | } |
| 450 | |
| 451 | /* |
| 452 | ** Generate javascript to enhance HTML diffs. |
| 453 | */ |
| @@ -600,10 +607,130 @@ | |
| 600 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 601 | 0, 0, 0, rid, 0, 0); |
| 602 | db_finalize(&q); |
| 603 | style_finish_page(); |
| 604 | } |
| 605 | |
| 606 | /* |
| 607 | ** WEBPAGE: vinfo |
| 608 | ** WEBPAGE: ci |
| 609 | ** URL: /ci/ARTIFACTID |
| @@ -1191,22 +1318,25 @@ | |
| 1191 | const char *zBranch; |
| 1192 | const char *zFrom; |
| 1193 | const char *zTo; |
| 1194 | const char *zRe; |
| 1195 | const char *zGlob; |
| 1196 | char *zMergeOrigin = 0; |
| 1197 | ReCompiled *pRe = 0; |
| 1198 | DiffConfig DCfg, *pCfg = 0; |
| 1199 | int graphFlags = 0; |
| 1200 | Blob qp; |
| 1201 | int bInvert = PB("inv"); |
| 1202 | |
| 1203 | login_check_credentials(); |
| 1204 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1205 | login_anonymous_available(); |
| 1206 | fossil_nice_default(); |
| 1207 | blob_init(&qp, 0, 0); |
| 1208 | diffType = preferred_diff_type(); |
| 1209 | zRe = P("regex"); |
| 1210 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1211 | zBranch = P("branch"); |
| 1212 | if( zBranch && zBranch[0]==0 ) zBranch = 0; |
| @@ -1246,11 +1376,12 @@ | |
| 1246 | } |
| 1247 | if( zGlob ){ |
| 1248 | if( !*zGlob ){ |
| 1249 | zGlob = NULL; |
| 1250 | }else{ |
| 1251 | blob_appendf(&qp, "&glob=%T", zGlob); |
| 1252 | } |
| 1253 | } |
| 1254 | if( PB("nc") ){ |
| 1255 | graphFlags |= TIMELINE_NOCOLOR; |
| 1256 | blob_appendf(&qp, "&nc"); |
| @@ -1263,20 +1394,22 @@ | |
| 1263 | style_set_current_feature("vdiff"); |
| 1264 | if( zBranch==0 ){ |
| 1265 | style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo); |
| 1266 | } |
| 1267 | if( diffType!=0 ){ |
| 1268 | style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b", &qp); |
| 1269 | } |
| 1270 | if( diffType!=2 ){ |
| 1271 | style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b", &qp); |
| 1272 | } |
| 1273 | if( diffType!=1 ) { |
| 1274 | style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b", &qp); |
| 1275 | } |
| 1276 | if( zBranch==0 ){ |
| 1277 | style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b", diffType, &qp); |
| 1278 | } |
| 1279 | if( zGlob ){ |
| 1280 | style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp); |
| 1281 | }else{ |
| 1282 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, |
| @@ -1321,10 +1454,11 @@ | |
| 1321 | @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> |
| 1322 | } |
| 1323 | @<hr><p> |
| 1324 | } |
| 1325 | blob_reset(&qp); |
| 1326 | |
| 1327 | manifest_file_rewind(pFrom); |
| 1328 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1329 | manifest_file_rewind(pTo); |
| 1330 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1337,37 +1471,38 @@ | |
| 1337 | cmp = -1; |
| 1338 | }else{ |
| 1339 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 1340 | } |
| 1341 | if( cmp<0 ){ |
| 1342 | if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){ |
| 1343 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1344 | pFileFrom->zUuid, 0, 0, pCfg, 0); |
| 1345 | } |
| 1346 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1347 | }else if( cmp>0 ){ |
| 1348 | if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){ |
| 1349 | append_file_change_line(zTo, pFileTo->zName, |
| 1350 | 0, pFileTo->zUuid, 0, pCfg, |
| 1351 | manifest_file_mperm(pFileTo)); |
| 1352 | } |
| 1353 | pFileTo = manifest_file_next(pTo, 0); |
| 1354 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1355 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1356 | pFileTo = manifest_file_next(pTo, 0); |
| 1357 | }else{ |
| 1358 | if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0 |
| 1359 | || sqlite3_strglob(zGlob, pFileTo->zName)==0) ){ |
| 1360 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1361 | pFileFrom->zUuid, |
| 1362 | pFileTo->zUuid, 0, pCfg, |
| 1363 | manifest_file_mperm(pFileTo)); |
| 1364 | } |
| 1365 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1366 | pFileTo = manifest_file_next(pTo, 0); |
| 1367 | } |
| 1368 | } |
| 1369 | manifest_destroy(pFrom); |
| 1370 | manifest_destroy(pTo); |
| 1371 | append_diff_javascript(diffType); |
| 1372 | style_finish_page(); |
| 1373 | } |
| 1374 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -372,11 +372,13 @@ | |
| 372 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 373 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 374 | DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ |
| 375 | int mperm /* executable or symlink permission for zNew */ |
| 376 | ){ |
| 377 | @ <div class='file-change-line'><span> |
| 378 | /* Maintenance reminder: the extra level of SPAN is for |
| 379 | ** arranging new elements via JS. */ |
| 380 | if( !g.perm.Hyperlink ){ |
| 381 | if( zNew==0 ){ |
| 382 | @ Deleted %h(zName). |
| 383 | }else if( zOld==0 ){ |
| 384 | @ Added %h(zName). |
| @@ -391,10 +393,11 @@ | |
| 393 | @ %h(zName) became a regular file. |
| 394 | } |
| 395 | }else{ |
| 396 | @ Changes to %h(zName). |
| 397 | } |
| 398 | @ </span></div> |
| 399 | if( pCfg ){ |
| 400 | append_diff(zOld, zNew, pCfg); |
| 401 | } |
| 402 | }else{ |
| 403 | if( zOld && zNew ){ |
| @@ -436,18 +439,22 @@ | |
| 439 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 440 | }else{ |
| 441 | @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ |
| 442 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 443 | } |
| 444 | if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 445 | if( pCfg ){ |
| 446 | @ </span></div> |
| 447 | append_diff(zOld, zNew, pCfg); |
| 448 | }else{ |
| 449 | @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a> |
| 450 | @ </span></div> |
| 451 | } |
| 452 | }else{ |
| 453 | @ </span></div> |
| 454 | } |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | /* |
| 459 | ** Generate javascript to enhance HTML diffs. |
| 460 | */ |
| @@ -600,10 +607,130 @@ | |
| 607 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 608 | 0, 0, 0, rid, 0, 0); |
| 609 | db_finalize(&q); |
| 610 | style_finish_page(); |
| 611 | } |
| 612 | |
| 613 | /* |
| 614 | ** WEBPAGE: ckout |
| 615 | ** |
| 616 | ** Show information about the current checkout. This page only functions |
| 617 | ** if the web server is run on a loopback interface (in other words, was |
| 618 | ** started using "fossil ui" or similar) from with on open check-out. |
| 619 | */ |
| 620 | void ckout_page(void){ |
| 621 | int vid; |
| 622 | char *zHostname; |
| 623 | char *zCwd; |
| 624 | int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ |
| 625 | DiffConfig DCfg,*pCfg; /* Diff details */ |
| 626 | const char *zHome; /* Home directory */ |
| 627 | Stmt q; |
| 628 | |
| 629 | if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ |
| 630 | cgi_redirect("%R/home"); |
| 631 | return; |
| 632 | } |
| 633 | diffType = preferred_diff_type(); |
| 634 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 635 | vid = db_lget_int("checkout", 0); |
| 636 | db_unprotect(PROTECT_ALL); |
| 637 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 638 | db_protect_pop(); |
| 639 | style_set_current_feature("vinfo"); |
| 640 | zHostname = fossil_hostname(); |
| 641 | zCwd = file_getcwd(0,0); |
| 642 | zHome = fossil_getenv("HOME"); |
| 643 | if( zHome ){ |
| 644 | int nHome = (int)strlen(zHome); |
| 645 | if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){ |
| 646 | zCwd = mprintf("~%s", zCwd+nHome); |
| 647 | } |
| 648 | } |
| 649 | if( zHostname ){ |
| 650 | style_header("Checkout Status: %h on %h", zCwd, zHostname); |
| 651 | }else{ |
| 652 | style_header("Checkout Status: %h", zCwd); |
| 653 | } |
| 654 | render_checkin_context(vid, 0, 0, 0); |
| 655 | if( pCfg==0 ){ |
| 656 | style_finish_page(); |
| 657 | return; |
| 658 | } |
| 659 | db_prepare(&q, |
| 660 | /* 0 1 2 3 4 5 6 */ |
| 661 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| 662 | " FROM vfile LEFT JOIN blob USING(rid)" |
| 663 | " WHERE vid=%d" |
| 664 | " AND (deleted OR chnged OR rid==0)" |
| 665 | " ORDER BY pathname /*scan*/", |
| 666 | vid |
| 667 | ); |
| 668 | if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){ |
| 669 | pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; |
| 670 | }else{ |
| 671 | pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; |
| 672 | } |
| 673 | while( db_step(&q)==SQLITE_ROW ){ |
| 674 | const char *zTreename = db_column_text(&q,0); |
| 675 | int isDeleted = db_column_int(&q, 1); |
| 676 | int isChnged = db_column_int(&q,2); |
| 677 | int isNew = db_column_int(&q,3); |
| 678 | int srcid = db_column_int(&q, 4); |
| 679 | int isLink = db_column_int(&q, 5); |
| 680 | const char *zUuid = db_column_text(&q, 6); |
| 681 | int showDiff = 1; |
| 682 | |
| 683 | pCfg->diffFlags &= (~DIFF_FILE_MASK); |
| 684 | if( isDeleted ){ |
| 685 | @ <p>DELETED %h(zTreename)</p> |
| 686 | pCfg->diffFlags |= DIFF_FILE_DELETED; |
| 687 | showDiff = 0; |
| 688 | }else if( file_access(zTreename, F_OK) ){ |
| 689 | @ <p>MISSING %h(zTreename)</p> |
| 690 | showDiff = 0; |
| 691 | }else if( isNew ){ |
| 692 | @ <p>ADDED %h(zTreename)</p> |
| 693 | pCfg->diffFlags |= DIFF_FILE_ADDED; |
| 694 | srcid = 0; |
| 695 | showDiff = 0; |
| 696 | }else if( isChnged==3 ){ |
| 697 | @ <p>ADDED_BY_MERGE %h(zTreename)</p> |
| 698 | pCfg->diffFlags |= DIFF_FILE_ADDED; |
| 699 | srcid = 0; |
| 700 | showDiff = 0; |
| 701 | }else if( isChnged==5 ){ |
| 702 | @ <p>ADDED_BY_INTEGRATE %h(zTreename)</p> |
| 703 | pCfg->diffFlags |= DIFF_FILE_ADDED; |
| 704 | srcid = 0; |
| 705 | showDiff = 0; |
| 706 | }else{ |
| 707 | @ <p>CHANGED %h(zTreename)</p> |
| 708 | } |
| 709 | if( showDiff ){ |
| 710 | Blob old, new; |
| 711 | if( !isLink != !file_islink(zTreename) ){ |
| 712 | @ %s(DIFF_CANNOT_COMPUTE_SYMLINK) |
| 713 | continue; |
| 714 | } |
| 715 | if( srcid>0 ){ |
| 716 | content_get(srcid, &old); |
| 717 | pCfg->zLeftHash = zUuid; |
| 718 | }else{ |
| 719 | blob_zero(&old); |
| 720 | pCfg->zLeftHash = 0; |
| 721 | } |
| 722 | blob_read_from_file(&new, zTreename, ExtFILE); |
| 723 | text_diff(&old, &new, cgi_output_blob(), pCfg); |
| 724 | blob_reset(&old); |
| 725 | blob_reset(&new); |
| 726 | } |
| 727 | } |
| 728 | db_finalize(&q); |
| 729 | append_diff_javascript(diffType); |
| 730 | style_finish_page(); |
| 731 | } |
| 732 | |
| 733 | /* |
| 734 | ** WEBPAGE: vinfo |
| 735 | ** WEBPAGE: ci |
| 736 | ** URL: /ci/ARTIFACTID |
| @@ -1191,22 +1318,25 @@ | |
| 1318 | const char *zBranch; |
| 1319 | const char *zFrom; |
| 1320 | const char *zTo; |
| 1321 | const char *zRe; |
| 1322 | const char *zGlob; |
| 1323 | Glob * pGlob = 0; |
| 1324 | char *zMergeOrigin = 0; |
| 1325 | ReCompiled *pRe = 0; |
| 1326 | DiffConfig DCfg, *pCfg = 0; |
| 1327 | int graphFlags = 0; |
| 1328 | Blob qp; /* non-glob= query parameters for generated links */ |
| 1329 | Blob qpGlob; /* glob= query parameter for generated links */ |
| 1330 | int bInvert = PB("inv"); |
| 1331 | |
| 1332 | login_check_credentials(); |
| 1333 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1334 | login_anonymous_available(); |
| 1335 | fossil_nice_default(); |
| 1336 | blob_init(&qp, 0, 0); |
| 1337 | blob_init(&qpGlob, 0, 0); |
| 1338 | diffType = preferred_diff_type(); |
| 1339 | zRe = P("regex"); |
| 1340 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1341 | zBranch = P("branch"); |
| 1342 | if( zBranch && zBranch[0]==0 ) zBranch = 0; |
| @@ -1246,11 +1376,12 @@ | |
| 1376 | } |
| 1377 | if( zGlob ){ |
| 1378 | if( !*zGlob ){ |
| 1379 | zGlob = NULL; |
| 1380 | }else{ |
| 1381 | blob_appendf(&qpGlob, "&glob=%T", zGlob); |
| 1382 | pGlob = glob_create(zGlob); |
| 1383 | } |
| 1384 | } |
| 1385 | if( PB("nc") ){ |
| 1386 | graphFlags |= TIMELINE_NOCOLOR; |
| 1387 | blob_appendf(&qp, "&nc"); |
| @@ -1263,20 +1394,22 @@ | |
| 1394 | style_set_current_feature("vdiff"); |
| 1395 | if( zBranch==0 ){ |
| 1396 | style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo); |
| 1397 | } |
| 1398 | if( diffType!=0 ){ |
| 1399 | style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob); |
| 1400 | } |
| 1401 | if( diffType!=2 ){ |
| 1402 | style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp, |
| 1403 | &qpGlob); |
| 1404 | } |
| 1405 | if( diffType!=1 ) { |
| 1406 | style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b%b", &qp, &qpGlob); |
| 1407 | } |
| 1408 | if( zBranch==0 ){ |
| 1409 | style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b%b", diffType, |
| 1410 | &qp, &qpGlob); |
| 1411 | } |
| 1412 | if( zGlob ){ |
| 1413 | style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp); |
| 1414 | }else{ |
| 1415 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, |
| @@ -1321,10 +1454,11 @@ | |
| 1454 | @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> |
| 1455 | } |
| 1456 | @<hr><p> |
| 1457 | } |
| 1458 | blob_reset(&qp); |
| 1459 | blob_reset(&qpGlob); |
| 1460 | |
| 1461 | manifest_file_rewind(pFrom); |
| 1462 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1463 | manifest_file_rewind(pTo); |
| 1464 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1337,37 +1471,38 @@ | |
| 1471 | cmp = -1; |
| 1472 | }else{ |
| 1473 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 1474 | } |
| 1475 | if( cmp<0 ){ |
| 1476 | if( !pGlob || glob_match(pGlob, pFileFrom->zName) ){ |
| 1477 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1478 | pFileFrom->zUuid, 0, 0, pCfg, 0); |
| 1479 | } |
| 1480 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1481 | }else if( cmp>0 ){ |
| 1482 | if( !pGlob || glob_match(pGlob, pFileTo->zName) ){ |
| 1483 | append_file_change_line(zTo, pFileTo->zName, |
| 1484 | 0, pFileTo->zUuid, 0, pCfg, |
| 1485 | manifest_file_mperm(pFileTo)); |
| 1486 | } |
| 1487 | pFileTo = manifest_file_next(pTo, 0); |
| 1488 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1489 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1490 | pFileTo = manifest_file_next(pTo, 0); |
| 1491 | }else{ |
| 1492 | if(!pGlob || (glob_match(pGlob, pFileFrom->zName) |
| 1493 | || glob_match(pGlob, pFileTo->zName)) ){ |
| 1494 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1495 | pFileFrom->zUuid, |
| 1496 | pFileTo->zUuid, 0, pCfg, |
| 1497 | manifest_file_mperm(pFileTo)); |
| 1498 | } |
| 1499 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1500 | pFileTo = manifest_file_next(pTo, 0); |
| 1501 | } |
| 1502 | } |
| 1503 | glob_free(pGlob); |
| 1504 | manifest_destroy(pFrom); |
| 1505 | manifest_destroy(pTo); |
| 1506 | append_diff_javascript(diffType); |
| 1507 | style_finish_page(); |
| 1508 | } |
| 1509 |
+2
-1
| --- src/interwiki.c | ||
| +++ src/interwiki.c | ||
| @@ -275,13 +275,14 @@ | ||
| 275 | 275 | db_prepare(&q, |
| 276 | 276 | "SELECT substr(name,11), value->>'base'" |
| 277 | 277 | " FROM config WHERE name glob 'interwiki:*' AND json_valid(value)" |
| 278 | 278 | " ORDER BY name;" |
| 279 | 279 | ); |
| 280 | + blob_append(out, "<blockquote>", -1); | |
| 280 | 281 | while( db_step(&q)==SQLITE_ROW ){ |
| 281 | 282 | if( n==0 ){ |
| 282 | - blob_appendf(out, "<blockquote><table>\n"); | |
| 283 | + blob_appendf(out, "<table>\n"); | |
| 283 | 284 | } |
| 284 | 285 | blob_appendf(out,"<tr><td>%h</td><td> → </td>", |
| 285 | 286 | db_column_text(&q,0)); |
| 286 | 287 | blob_appendf(out,"<td>%h</td></tr>\n", |
| 287 | 288 | db_column_text(&q,1)); |
| 288 | 289 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -275,13 +275,14 @@ | |
| 275 | db_prepare(&q, |
| 276 | "SELECT substr(name,11), value->>'base'" |
| 277 | " FROM config WHERE name glob 'interwiki:*' AND json_valid(value)" |
| 278 | " ORDER BY name;" |
| 279 | ); |
| 280 | while( db_step(&q)==SQLITE_ROW ){ |
| 281 | if( n==0 ){ |
| 282 | blob_appendf(out, "<blockquote><table>\n"); |
| 283 | } |
| 284 | blob_appendf(out,"<tr><td>%h</td><td> → </td>", |
| 285 | db_column_text(&q,0)); |
| 286 | blob_appendf(out,"<td>%h</td></tr>\n", |
| 287 | db_column_text(&q,1)); |
| 288 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -275,13 +275,14 @@ | |
| 275 | db_prepare(&q, |
| 276 | "SELECT substr(name,11), value->>'base'" |
| 277 | " FROM config WHERE name glob 'interwiki:*' AND json_valid(value)" |
| 278 | " ORDER BY name;" |
| 279 | ); |
| 280 | blob_append(out, "<blockquote>", -1); |
| 281 | while( db_step(&q)==SQLITE_ROW ){ |
| 282 | if( n==0 ){ |
| 283 | blob_appendf(out, "<table>\n"); |
| 284 | } |
| 285 | blob_appendf(out,"<tr><td>%h</td><td> → </td>", |
| 286 | db_column_text(&q,0)); |
| 287 | blob_appendf(out,"<td>%h</td></tr>\n", |
| 288 | db_column_text(&q,1)); |
| 289 |
+16
-4
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -751,11 +751,11 @@ | ||
| 751 | 751 | if( anonFlag ){ |
| 752 | 752 | @ <input type="hidden" name="anon" value="1"> |
| 753 | 753 | } |
| 754 | 754 | if( g.zLogin ){ |
| 755 | 755 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 756 | - @ <input type="submit" name="out" value="Logout"></p> | |
| 756 | + @ <input type="submit" name="out" value="Logout" autofocus></p> | |
| 757 | 757 | @ </form> |
| 758 | 758 | }else{ |
| 759 | 759 | unsigned int uSeed = captcha_seed(); |
| 760 | 760 | if( g.zLogin==0 && (anonFlag || zGoto==0) ){ |
| 761 | 761 | zAnonPw = db_text(0, "SELECT pw FROM user" |
| @@ -777,11 +777,11 @@ | ||
| 777 | 777 | @ </span></td></tr> |
| 778 | 778 | } |
| 779 | 779 | @ <tr> |
| 780 | 780 | @ <td class="form_label" id="userlabel1">User ID:</td> |
| 781 | 781 | @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ |
| 782 | - @ size="30" value="%s(anonFlag?"anonymous":"")"></td> | |
| 782 | + @ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td> | |
| 783 | 783 | @ </tr> |
| 784 | 784 | @ <tr> |
| 785 | 785 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 786 | 786 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 787 | 787 | @ name="p" value="" size="30">\ |
| @@ -1302,20 +1302,32 @@ | ||
| 1302 | 1302 | */ |
| 1303 | 1303 | void login_restrict_robot_access(void){ |
| 1304 | 1304 | const char *zReferer; |
| 1305 | 1305 | const char *zGlob; |
| 1306 | 1306 | int isMatch = 1; |
| 1307 | + int nQP; /* Number of query parameters other than name= */ | |
| 1307 | 1308 | if( g.zLogin!=0 ) return; |
| 1308 | 1309 | zGlob = db_get("robot-restrict",0); |
| 1309 | 1310 | if( zGlob==0 || zGlob[0]==0 ) return; |
| 1310 | 1311 | if( g.isHuman ){ |
| 1311 | 1312 | zReferer = P("HTTP_REFERER"); |
| 1312 | 1313 | if( zReferer && zReferer[0]!=0 ) return; |
| 1313 | 1314 | } |
| 1314 | - if( cgi_qp_count()<1 ) return; | |
| 1315 | + nQP = cgi_qp_count(); | |
| 1316 | + if( nQP<1 ) return; | |
| 1315 | 1317 | isMatch = glob_multi_match(zGlob, g.zPath); |
| 1316 | 1318 | if( !isMatch ) return; |
| 1319 | + | |
| 1320 | + /* Check for exceptions to the restriction on the number of query | |
| 1321 | + ** parameters. */ | |
| 1322 | + zGlob = db_get("robot-restrict-qp",0); | |
| 1323 | + if( zGlob && zGlob[0] ){ | |
| 1324 | + char *zPath = mprintf("%s/%d", g.zPath, nQP); | |
| 1325 | + isMatch = glob_multi_match(zGlob, zPath); | |
| 1326 | + fossil_free(zPath); | |
| 1327 | + if( isMatch ) return; | |
| 1328 | + } | |
| 1317 | 1329 | |
| 1318 | 1330 | /* If we reach this point, it means we have a situation where we |
| 1319 | 1331 | ** want to restrict the activity of a robot. |
| 1320 | 1332 | */ |
| 1321 | 1333 | g.isHuman = 0; |
| @@ -2243,11 +2255,11 @@ | ||
| 2243 | 2255 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> |
| 2244 | 2256 | @ <table class="login_out"> |
| 2245 | 2257 | @ <tr> |
| 2246 | 2258 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 2247 | 2259 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 2248 | - @ value="%h(zUserID)" size="30"></td> | |
| 2260 | + @ value="%h(zUserID)" size="30" autofocus></td> | |
| 2249 | 2261 | @ |
| 2250 | 2262 | if( iErrLine==1 ){ |
| 2251 | 2263 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 2252 | 2264 | } |
| 2253 | 2265 | @ <tr> |
| 2254 | 2266 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -751,11 +751,11 @@ | |
| 751 | if( anonFlag ){ |
| 752 | @ <input type="hidden" name="anon" value="1"> |
| 753 | } |
| 754 | if( g.zLogin ){ |
| 755 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 756 | @ <input type="submit" name="out" value="Logout"></p> |
| 757 | @ </form> |
| 758 | }else{ |
| 759 | unsigned int uSeed = captcha_seed(); |
| 760 | if( g.zLogin==0 && (anonFlag || zGoto==0) ){ |
| 761 | zAnonPw = db_text(0, "SELECT pw FROM user" |
| @@ -777,11 +777,11 @@ | |
| 777 | @ </span></td></tr> |
| 778 | } |
| 779 | @ <tr> |
| 780 | @ <td class="form_label" id="userlabel1">User ID:</td> |
| 781 | @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ |
| 782 | @ size="30" value="%s(anonFlag?"anonymous":"")"></td> |
| 783 | @ </tr> |
| 784 | @ <tr> |
| 785 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 786 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 787 | @ name="p" value="" size="30">\ |
| @@ -1302,20 +1302,32 @@ | |
| 1302 | */ |
| 1303 | void login_restrict_robot_access(void){ |
| 1304 | const char *zReferer; |
| 1305 | const char *zGlob; |
| 1306 | int isMatch = 1; |
| 1307 | if( g.zLogin!=0 ) return; |
| 1308 | zGlob = db_get("robot-restrict",0); |
| 1309 | if( zGlob==0 || zGlob[0]==0 ) return; |
| 1310 | if( g.isHuman ){ |
| 1311 | zReferer = P("HTTP_REFERER"); |
| 1312 | if( zReferer && zReferer[0]!=0 ) return; |
| 1313 | } |
| 1314 | if( cgi_qp_count()<1 ) return; |
| 1315 | isMatch = glob_multi_match(zGlob, g.zPath); |
| 1316 | if( !isMatch ) return; |
| 1317 | |
| 1318 | /* If we reach this point, it means we have a situation where we |
| 1319 | ** want to restrict the activity of a robot. |
| 1320 | */ |
| 1321 | g.isHuman = 0; |
| @@ -2243,11 +2255,11 @@ | |
| 2243 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> |
| 2244 | @ <table class="login_out"> |
| 2245 | @ <tr> |
| 2246 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 2247 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 2248 | @ value="%h(zUserID)" size="30"></td> |
| 2249 | @ |
| 2250 | if( iErrLine==1 ){ |
| 2251 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 2252 | } |
| 2253 | @ <tr> |
| 2254 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -751,11 +751,11 @@ | |
| 751 | if( anonFlag ){ |
| 752 | @ <input type="hidden" name="anon" value="1"> |
| 753 | } |
| 754 | if( g.zLogin ){ |
| 755 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 756 | @ <input type="submit" name="out" value="Logout" autofocus></p> |
| 757 | @ </form> |
| 758 | }else{ |
| 759 | unsigned int uSeed = captcha_seed(); |
| 760 | if( g.zLogin==0 && (anonFlag || zGoto==0) ){ |
| 761 | zAnonPw = db_text(0, "SELECT pw FROM user" |
| @@ -777,11 +777,11 @@ | |
| 777 | @ </span></td></tr> |
| 778 | } |
| 779 | @ <tr> |
| 780 | @ <td class="form_label" id="userlabel1">User ID:</td> |
| 781 | @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ |
| 782 | @ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td> |
| 783 | @ </tr> |
| 784 | @ <tr> |
| 785 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 786 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 787 | @ name="p" value="" size="30">\ |
| @@ -1302,20 +1302,32 @@ | |
| 1302 | */ |
| 1303 | void login_restrict_robot_access(void){ |
| 1304 | const char *zReferer; |
| 1305 | const char *zGlob; |
| 1306 | int isMatch = 1; |
| 1307 | int nQP; /* Number of query parameters other than name= */ |
| 1308 | if( g.zLogin!=0 ) return; |
| 1309 | zGlob = db_get("robot-restrict",0); |
| 1310 | if( zGlob==0 || zGlob[0]==0 ) return; |
| 1311 | if( g.isHuman ){ |
| 1312 | zReferer = P("HTTP_REFERER"); |
| 1313 | if( zReferer && zReferer[0]!=0 ) return; |
| 1314 | } |
| 1315 | nQP = cgi_qp_count(); |
| 1316 | if( nQP<1 ) return; |
| 1317 | isMatch = glob_multi_match(zGlob, g.zPath); |
| 1318 | if( !isMatch ) return; |
| 1319 | |
| 1320 | /* Check for exceptions to the restriction on the number of query |
| 1321 | ** parameters. */ |
| 1322 | zGlob = db_get("robot-restrict-qp",0); |
| 1323 | if( zGlob && zGlob[0] ){ |
| 1324 | char *zPath = mprintf("%s/%d", g.zPath, nQP); |
| 1325 | isMatch = glob_multi_match(zGlob, zPath); |
| 1326 | fossil_free(zPath); |
| 1327 | if( isMatch ) return; |
| 1328 | } |
| 1329 | |
| 1330 | /* If we reach this point, it means we have a situation where we |
| 1331 | ** want to restrict the activity of a robot. |
| 1332 | */ |
| 1333 | g.isHuman = 0; |
| @@ -2243,11 +2255,11 @@ | |
| 2255 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> |
| 2256 | @ <table class="login_out"> |
| 2257 | @ <tr> |
| 2258 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 2259 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 2260 | @ value="%h(zUserID)" size="30" autofocus></td> |
| 2261 | @ |
| 2262 | if( iErrLine==1 ){ |
| 2263 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 2264 | } |
| 2265 | @ <tr> |
| 2266 |
+13
-10
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -2040,20 +2040,27 @@ | ||
| 2040 | 2040 | */ |
| 2041 | 2041 | set_base_url(0); |
| 2042 | 2042 | if( fossil_redirect_to_https_if_needed(2) ) return; |
| 2043 | 2043 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 2044 | 2044 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 2045 | - /* Second special case: If the PATH_INFO is blank, issue a redirect to | |
| 2046 | - ** the home page identified by the "index-page" setting in the repository | |
| 2047 | - ** CONFIG table, to "/index" if there no "index-page" setting. */ | |
| 2045 | + /* Second special case: If the PATH_INFO is blank, issue a redirect: | |
| 2046 | + ** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set. | |
| 2047 | + ** (2) to the home page identified by the "index-page" setting | |
| 2048 | + ** in the repository CONFIG table | |
| 2049 | + ** (3) to "/index" if there no "index-page" setting in CONFIG | |
| 2050 | + */ | |
| 2048 | 2051 | #ifdef FOSSIL_ENABLE_JSON |
| 2049 | 2052 | if(g.json.isJsonMode){ |
| 2050 | 2053 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 2051 | 2054 | fossil_exit(0); |
| 2052 | 2055 | } |
| 2053 | 2056 | #endif |
| 2054 | - fossil_redirect_home() /*does not return*/; | |
| 2057 | + if( g.useLocalauth && g.localOpen ){ | |
| 2058 | + cgi_redirectf("%R/ckout"); | |
| 2059 | + }else{ | |
| 2060 | + fossil_redirect_home() /*does not return*/; | |
| 2061 | + } | |
| 2055 | 2062 | }else{ |
| 2056 | 2063 | zPath = mprintf("%s", zPathInfo); |
| 2057 | 2064 | } |
| 2058 | 2065 | |
| 2059 | 2066 | /* Make g.zPath point to the first element of the path. Make |
| @@ -3356,11 +3363,11 @@ | ||
| 3356 | 3363 | const char * zDir = g.argv[2]; |
| 3357 | 3364 | if(dir_has_ckout_db(zDir)){ |
| 3358 | 3365 | if(0!=file_chdir(zDir, 0)){ |
| 3359 | 3366 | fossil_fatal("Cannot chdir to %s", zDir); |
| 3360 | 3367 | } |
| 3361 | - findServerArg = 99; | |
| 3368 | + findServerArg = g.argc; | |
| 3362 | 3369 | fCreate = 0; |
| 3363 | 3370 | g.argv[2] = 0; |
| 3364 | 3371 | --g.argc; |
| 3365 | 3372 | } |
| 3366 | 3373 | } |
| @@ -3384,15 +3391,11 @@ | ||
| 3384 | 3391 | } |
| 3385 | 3392 | if( !zRemote ){ |
| 3386 | 3393 | find_server_repository(findServerArg, fCreate); |
| 3387 | 3394 | } |
| 3388 | 3395 | if( zInitPage==0 ){ |
| 3389 | - if( isUiCmd && g.localOpen ){ | |
| 3390 | - zInitPage = "timeline?c=current"; | |
| 3391 | - }else{ | |
| 3392 | - zInitPage = ""; | |
| 3393 | - } | |
| 3396 | + zInitPage = ""; | |
| 3394 | 3397 | } |
| 3395 | 3398 | if( zPort ){ |
| 3396 | 3399 | if( strchr(zPort,':') ){ |
| 3397 | 3400 | int i; |
| 3398 | 3401 | for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){} |
| 3399 | 3402 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -2040,20 +2040,27 @@ | |
| 2040 | */ |
| 2041 | set_base_url(0); |
| 2042 | if( fossil_redirect_to_https_if_needed(2) ) return; |
| 2043 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 2044 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 2045 | /* Second special case: If the PATH_INFO is blank, issue a redirect to |
| 2046 | ** the home page identified by the "index-page" setting in the repository |
| 2047 | ** CONFIG table, to "/index" if there no "index-page" setting. */ |
| 2048 | #ifdef FOSSIL_ENABLE_JSON |
| 2049 | if(g.json.isJsonMode){ |
| 2050 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 2051 | fossil_exit(0); |
| 2052 | } |
| 2053 | #endif |
| 2054 | fossil_redirect_home() /*does not return*/; |
| 2055 | }else{ |
| 2056 | zPath = mprintf("%s", zPathInfo); |
| 2057 | } |
| 2058 | |
| 2059 | /* Make g.zPath point to the first element of the path. Make |
| @@ -3356,11 +3363,11 @@ | |
| 3356 | const char * zDir = g.argv[2]; |
| 3357 | if(dir_has_ckout_db(zDir)){ |
| 3358 | if(0!=file_chdir(zDir, 0)){ |
| 3359 | fossil_fatal("Cannot chdir to %s", zDir); |
| 3360 | } |
| 3361 | findServerArg = 99; |
| 3362 | fCreate = 0; |
| 3363 | g.argv[2] = 0; |
| 3364 | --g.argc; |
| 3365 | } |
| 3366 | } |
| @@ -3384,15 +3391,11 @@ | |
| 3384 | } |
| 3385 | if( !zRemote ){ |
| 3386 | find_server_repository(findServerArg, fCreate); |
| 3387 | } |
| 3388 | if( zInitPage==0 ){ |
| 3389 | if( isUiCmd && g.localOpen ){ |
| 3390 | zInitPage = "timeline?c=current"; |
| 3391 | }else{ |
| 3392 | zInitPage = ""; |
| 3393 | } |
| 3394 | } |
| 3395 | if( zPort ){ |
| 3396 | if( strchr(zPort,':') ){ |
| 3397 | int i; |
| 3398 | for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){} |
| 3399 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -2040,20 +2040,27 @@ | |
| 2040 | */ |
| 2041 | set_base_url(0); |
| 2042 | if( fossil_redirect_to_https_if_needed(2) ) return; |
| 2043 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 2044 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 2045 | /* Second special case: If the PATH_INFO is blank, issue a redirect: |
| 2046 | ** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set. |
| 2047 | ** (2) to the home page identified by the "index-page" setting |
| 2048 | ** in the repository CONFIG table |
| 2049 | ** (3) to "/index" if there no "index-page" setting in CONFIG |
| 2050 | */ |
| 2051 | #ifdef FOSSIL_ENABLE_JSON |
| 2052 | if(g.json.isJsonMode){ |
| 2053 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 2054 | fossil_exit(0); |
| 2055 | } |
| 2056 | #endif |
| 2057 | if( g.useLocalauth && g.localOpen ){ |
| 2058 | cgi_redirectf("%R/ckout"); |
| 2059 | }else{ |
| 2060 | fossil_redirect_home() /*does not return*/; |
| 2061 | } |
| 2062 | }else{ |
| 2063 | zPath = mprintf("%s", zPathInfo); |
| 2064 | } |
| 2065 | |
| 2066 | /* Make g.zPath point to the first element of the path. Make |
| @@ -3356,11 +3363,11 @@ | |
| 3363 | const char * zDir = g.argv[2]; |
| 3364 | if(dir_has_ckout_db(zDir)){ |
| 3365 | if(0!=file_chdir(zDir, 0)){ |
| 3366 | fossil_fatal("Cannot chdir to %s", zDir); |
| 3367 | } |
| 3368 | findServerArg = g.argc; |
| 3369 | fCreate = 0; |
| 3370 | g.argv[2] = 0; |
| 3371 | --g.argc; |
| 3372 | } |
| 3373 | } |
| @@ -3384,15 +3391,11 @@ | |
| 3391 | } |
| 3392 | if( !zRemote ){ |
| 3393 | find_server_repository(findServerArg, fCreate); |
| 3394 | } |
| 3395 | if( zInitPage==0 ){ |
| 3396 | zInitPage = ""; |
| 3397 | } |
| 3398 | if( zPort ){ |
| 3399 | if( strchr(zPort,':') ){ |
| 3400 | int i; |
| 3401 | for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){} |
| 3402 |
+2
-1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -248,10 +248,11 @@ | ||
| 248 | 248 | $(SRCDIR)/hbmenu.js \ |
| 249 | 249 | $(SRCDIR)/href.js \ |
| 250 | 250 | $(SRCDIR)/login.js \ |
| 251 | 251 | $(SRCDIR)/markdown.md \ |
| 252 | 252 | $(SRCDIR)/menu.js \ |
| 253 | + $(SRCDIR)/merge.tcl \ | |
| 253 | 254 | $(SRCDIR)/scroll.js \ |
| 254 | 255 | $(SRCDIR)/skin.js \ |
| 255 | 256 | $(SRCDIR)/sorttable.js \ |
| 256 | 257 | $(SRCDIR)/sounds/0.wav \ |
| 257 | 258 | $(SRCDIR)/sounds/1.wav \ |
| @@ -704,11 +705,11 @@ | ||
| 704 | 705 | |
| 705 | 706 | # The USE_LINENOISE variable may be undefined, set to 0, or set |
| 706 | 707 | # to 1. If it is set to 0, then there is no need to build or link |
| 707 | 708 | # the linenoise.o object. |
| 708 | 709 | LINENOISE_DEF.0 = |
| 709 | -LINENOISE_DEF.1 = -DHAVE_LINENOISE | |
| 710 | +LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 | |
| 710 | 711 | LINENOISE_DEF. = $(LINENOISE_DEF.0) |
| 711 | 712 | LINENOISE_OBJ.0 = |
| 712 | 713 | LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o |
| 713 | 714 | LINENOISE_OBJ. = $(LINENOISE_OBJ.0) |
| 714 | 715 | |
| 715 | 716 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -248,10 +248,11 @@ | |
| 248 | $(SRCDIR)/hbmenu.js \ |
| 249 | $(SRCDIR)/href.js \ |
| 250 | $(SRCDIR)/login.js \ |
| 251 | $(SRCDIR)/markdown.md \ |
| 252 | $(SRCDIR)/menu.js \ |
| 253 | $(SRCDIR)/scroll.js \ |
| 254 | $(SRCDIR)/skin.js \ |
| 255 | $(SRCDIR)/sorttable.js \ |
| 256 | $(SRCDIR)/sounds/0.wav \ |
| 257 | $(SRCDIR)/sounds/1.wav \ |
| @@ -704,11 +705,11 @@ | |
| 704 | |
| 705 | # The USE_LINENOISE variable may be undefined, set to 0, or set |
| 706 | # to 1. If it is set to 0, then there is no need to build or link |
| 707 | # the linenoise.o object. |
| 708 | LINENOISE_DEF.0 = |
| 709 | LINENOISE_DEF.1 = -DHAVE_LINENOISE |
| 710 | LINENOISE_DEF. = $(LINENOISE_DEF.0) |
| 711 | LINENOISE_OBJ.0 = |
| 712 | LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o |
| 713 | LINENOISE_OBJ. = $(LINENOISE_OBJ.0) |
| 714 | |
| 715 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -248,10 +248,11 @@ | |
| 248 | $(SRCDIR)/hbmenu.js \ |
| 249 | $(SRCDIR)/href.js \ |
| 250 | $(SRCDIR)/login.js \ |
| 251 | $(SRCDIR)/markdown.md \ |
| 252 | $(SRCDIR)/menu.js \ |
| 253 | $(SRCDIR)/merge.tcl \ |
| 254 | $(SRCDIR)/scroll.js \ |
| 255 | $(SRCDIR)/skin.js \ |
| 256 | $(SRCDIR)/sorttable.js \ |
| 257 | $(SRCDIR)/sounds/0.wav \ |
| 258 | $(SRCDIR)/sounds/1.wav \ |
| @@ -704,11 +705,11 @@ | |
| 705 | |
| 706 | # The USE_LINENOISE variable may be undefined, set to 0, or set |
| 707 | # to 1. If it is set to 0, then there is no need to build or link |
| 708 | # the linenoise.o object. |
| 709 | LINENOISE_DEF.0 = |
| 710 | LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 |
| 711 | LINENOISE_DEF. = $(LINENOISE_DEF.0) |
| 712 | LINENOISE_OBJ.0 = |
| 713 | LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o |
| 714 | LINENOISE_OBJ. = $(LINENOISE_OBJ.0) |
| 715 | |
| 716 |
+7
-3
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -2334,11 +2334,11 @@ | ||
| 2334 | 2334 | int i, rc = TH_OK; |
| 2335 | 2335 | Manifest *p; |
| 2336 | 2336 | int parentid = 0; |
| 2337 | 2337 | int permitHooks = (flags & MC_PERMIT_HOOKS); |
| 2338 | 2338 | const char *zScript = 0; |
| 2339 | - const char *zUuid = 0; | |
| 2339 | + char *zUuid = 0; | |
| 2340 | 2340 | |
| 2341 | 2341 | if( g.fSqlTrace ){ |
| 2342 | 2342 | fossil_trace("-- manifest_crosslink(%d)\n", rid); |
| 2343 | 2343 | } |
| 2344 | 2344 | manifest_create_event_triggers(); |
| @@ -2370,11 +2370,11 @@ | ||
| 2370 | 2370 | } |
| 2371 | 2371 | db_begin_transaction(); |
| 2372 | 2372 | if( p->type==CFTYPE_MANIFEST ){ |
| 2373 | 2373 | if( permitHooks ){ |
| 2374 | 2374 | zScript = xfer_commit_code(); |
| 2375 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 2375 | + zUuid = rid_to_uuid(rid); | |
| 2376 | 2376 | } |
| 2377 | 2377 | if( p->nCherrypick && db_table_exists("repository","cherrypick") ){ |
| 2378 | 2378 | int i; |
| 2379 | 2379 | for(i=0; i<p->nCherrypick; i++){ |
| 2380 | 2380 | db_multi_exec( |
| @@ -2722,11 +2722,12 @@ | ||
| 2722 | 2722 | branchMove = 0; |
| 2723 | 2723 | if( permitHooks && db_exists("SELECT 1 FROM event, blob" |
| 2724 | 2724 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 2725 | 2725 | " AND blob.uuid=%Q", zTagUuid) ){ |
| 2726 | 2726 | zScript = xfer_commit_code(); |
| 2727 | - zUuid = zTagUuid; | |
| 2727 | + fossil_free(zUuid); | |
| 2728 | + zUuid = fossil_strdup(zTagUuid); | |
| 2728 | 2729 | } |
| 2729 | 2730 | } |
| 2730 | 2731 | zName = p->aTag[i].zName; |
| 2731 | 2732 | zValue = p->aTag[i].zValue; |
| 2732 | 2733 | if( strcmp(zName, "*branch")==0 ){ |
| @@ -2805,10 +2806,12 @@ | ||
| 2805 | 2806 | } |
| 2806 | 2807 | if( p->type==CFTYPE_FORUM ){ |
| 2807 | 2808 | int froot, fprev, firt; |
| 2808 | 2809 | char *zFType; |
| 2809 | 2810 | char *zTitle; |
| 2811 | + | |
| 2812 | + assert( 0==zUuid ); | |
| 2810 | 2813 | schema_forum(); |
| 2811 | 2814 | search_doc_touch('f', rid, 0); |
| 2812 | 2815 | froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid; |
| 2813 | 2816 | fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0; |
| 2814 | 2817 | firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0; |
| @@ -2877,10 +2880,11 @@ | ||
| 2877 | 2880 | rc = xfer_run_common_script(); |
| 2878 | 2881 | if( rc==TH_OK ){ |
| 2879 | 2882 | rc = xfer_run_script(zScript, zUuid, 0); |
| 2880 | 2883 | } |
| 2881 | 2884 | } |
| 2885 | + fossil_free(zUuid); | |
| 2882 | 2886 | if( p->type==CFTYPE_MANIFEST ){ |
| 2883 | 2887 | manifest_cache_insert(p); |
| 2884 | 2888 | }else{ |
| 2885 | 2889 | manifest_destroy(p); |
| 2886 | 2890 | } |
| 2887 | 2891 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2334,11 +2334,11 @@ | |
| 2334 | int i, rc = TH_OK; |
| 2335 | Manifest *p; |
| 2336 | int parentid = 0; |
| 2337 | int permitHooks = (flags & MC_PERMIT_HOOKS); |
| 2338 | const char *zScript = 0; |
| 2339 | const char *zUuid = 0; |
| 2340 | |
| 2341 | if( g.fSqlTrace ){ |
| 2342 | fossil_trace("-- manifest_crosslink(%d)\n", rid); |
| 2343 | } |
| 2344 | manifest_create_event_triggers(); |
| @@ -2370,11 +2370,11 @@ | |
| 2370 | } |
| 2371 | db_begin_transaction(); |
| 2372 | if( p->type==CFTYPE_MANIFEST ){ |
| 2373 | if( permitHooks ){ |
| 2374 | zScript = xfer_commit_code(); |
| 2375 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2376 | } |
| 2377 | if( p->nCherrypick && db_table_exists("repository","cherrypick") ){ |
| 2378 | int i; |
| 2379 | for(i=0; i<p->nCherrypick; i++){ |
| 2380 | db_multi_exec( |
| @@ -2722,11 +2722,12 @@ | |
| 2722 | branchMove = 0; |
| 2723 | if( permitHooks && db_exists("SELECT 1 FROM event, blob" |
| 2724 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 2725 | " AND blob.uuid=%Q", zTagUuid) ){ |
| 2726 | zScript = xfer_commit_code(); |
| 2727 | zUuid = zTagUuid; |
| 2728 | } |
| 2729 | } |
| 2730 | zName = p->aTag[i].zName; |
| 2731 | zValue = p->aTag[i].zValue; |
| 2732 | if( strcmp(zName, "*branch")==0 ){ |
| @@ -2805,10 +2806,12 @@ | |
| 2805 | } |
| 2806 | if( p->type==CFTYPE_FORUM ){ |
| 2807 | int froot, fprev, firt; |
| 2808 | char *zFType; |
| 2809 | char *zTitle; |
| 2810 | schema_forum(); |
| 2811 | search_doc_touch('f', rid, 0); |
| 2812 | froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid; |
| 2813 | fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0; |
| 2814 | firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0; |
| @@ -2877,10 +2880,11 @@ | |
| 2877 | rc = xfer_run_common_script(); |
| 2878 | if( rc==TH_OK ){ |
| 2879 | rc = xfer_run_script(zScript, zUuid, 0); |
| 2880 | } |
| 2881 | } |
| 2882 | if( p->type==CFTYPE_MANIFEST ){ |
| 2883 | manifest_cache_insert(p); |
| 2884 | }else{ |
| 2885 | manifest_destroy(p); |
| 2886 | } |
| 2887 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2334,11 +2334,11 @@ | |
| 2334 | int i, rc = TH_OK; |
| 2335 | Manifest *p; |
| 2336 | int parentid = 0; |
| 2337 | int permitHooks = (flags & MC_PERMIT_HOOKS); |
| 2338 | const char *zScript = 0; |
| 2339 | char *zUuid = 0; |
| 2340 | |
| 2341 | if( g.fSqlTrace ){ |
| 2342 | fossil_trace("-- manifest_crosslink(%d)\n", rid); |
| 2343 | } |
| 2344 | manifest_create_event_triggers(); |
| @@ -2370,11 +2370,11 @@ | |
| 2370 | } |
| 2371 | db_begin_transaction(); |
| 2372 | if( p->type==CFTYPE_MANIFEST ){ |
| 2373 | if( permitHooks ){ |
| 2374 | zScript = xfer_commit_code(); |
| 2375 | zUuid = rid_to_uuid(rid); |
| 2376 | } |
| 2377 | if( p->nCherrypick && db_table_exists("repository","cherrypick") ){ |
| 2378 | int i; |
| 2379 | for(i=0; i<p->nCherrypick; i++){ |
| 2380 | db_multi_exec( |
| @@ -2722,11 +2722,12 @@ | |
| 2722 | branchMove = 0; |
| 2723 | if( permitHooks && db_exists("SELECT 1 FROM event, blob" |
| 2724 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 2725 | " AND blob.uuid=%Q", zTagUuid) ){ |
| 2726 | zScript = xfer_commit_code(); |
| 2727 | fossil_free(zUuid); |
| 2728 | zUuid = fossil_strdup(zTagUuid); |
| 2729 | } |
| 2730 | } |
| 2731 | zName = p->aTag[i].zName; |
| 2732 | zValue = p->aTag[i].zValue; |
| 2733 | if( strcmp(zName, "*branch")==0 ){ |
| @@ -2805,10 +2806,12 @@ | |
| 2806 | } |
| 2807 | if( p->type==CFTYPE_FORUM ){ |
| 2808 | int froot, fprev, firt; |
| 2809 | char *zFType; |
| 2810 | char *zTitle; |
| 2811 | |
| 2812 | assert( 0==zUuid ); |
| 2813 | schema_forum(); |
| 2814 | search_doc_touch('f', rid, 0); |
| 2815 | froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid; |
| 2816 | fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0; |
| 2817 | firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0; |
| @@ -2877,10 +2880,11 @@ | |
| 2880 | rc = xfer_run_common_script(); |
| 2881 | if( rc==TH_OK ){ |
| 2882 | rc = xfer_run_script(zScript, zUuid, 0); |
| 2883 | } |
| 2884 | } |
| 2885 | fossil_free(zUuid); |
| 2886 | if( p->type==CFTYPE_MANIFEST ){ |
| 2887 | manifest_cache_insert(p); |
| 2888 | }else{ |
| 2889 | manifest_destroy(p); |
| 2890 | } |
| 2891 |
+465
-6
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -20,10 +20,372 @@ | ||
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "merge.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | + | |
| 26 | +/* | |
| 27 | +** Bring up a Tcl/Tk GUI to show details of the most recent merge. | |
| 28 | +*/ | |
| 29 | +static void merge_info_tk(int bDark, int bAll, int nContext){ | |
| 30 | + int i; | |
| 31 | + Blob script; | |
| 32 | + const char *zTempFile = 0; | |
| 33 | + char *zCmd; | |
| 34 | + const char *zTclsh; | |
| 35 | + zTclsh = find_option("tclsh",0,1); | |
| 36 | + if( zTclsh==0 ){ | |
| 37 | + zTclsh = db_get("tclsh",0); | |
| 38 | + } | |
| 39 | + /* The undocumented --script FILENAME option causes the Tk script to | |
| 40 | + ** be written into the FILENAME instead of being run. This is used | |
| 41 | + ** for testing and debugging. */ | |
| 42 | + zTempFile = find_option("script",0,1); | |
| 43 | + verify_all_options(); | |
| 44 | + | |
| 45 | + blob_zero(&script); | |
| 46 | + blob_appendf(&script, "set ncontext %d\n", nContext); | |
| 47 | + blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n", | |
| 48 | + g.nameOfExe); | |
| 49 | + blob_appendf(&script, "set filelist [list"); | |
| 50 | + if( g.argc==2 ){ | |
| 51 | + /* No files named on the command-line. Use every file mentioned | |
| 52 | + ** in the MERGESTAT table to generate the file list. */ | |
| 53 | + Stmt q; | |
| 54 | + int cnt = 0; | |
| 55 | + db_prepare(&q, | |
| 56 | + "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0)," | |
| 57 | + "('MERGE',1),('ADDED',2),('UPDATE',2))" | |
| 58 | + "SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)" | |
| 59 | + " %s ORDER BY pri, 1", | |
| 60 | + bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/ | |
| 61 | + ); | |
| 62 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 63 | + blob_appendf(&script," %s ", db_column_text(&q,1)); | |
| 64 | + blob_append_tcl_literal(&script, db_column_text(&q,0), | |
| 65 | + db_column_bytes(&q,0)); | |
| 66 | + cnt++; | |
| 67 | + } | |
| 68 | + db_finalize(&q); | |
| 69 | + if( cnt==0 ){ | |
| 70 | + fossil_print( | |
| 71 | + "No interesting changes in this merge. Use --all to see everything\n" | |
| 72 | + ); | |
| 73 | + return; | |
| 74 | + } | |
| 75 | + }else{ | |
| 76 | + /* Use only files named on the command-line in the file list. | |
| 77 | + ** But verify each file named is actually found in the MERGESTAT | |
| 78 | + ** table first. */ | |
| 79 | + for(i=2; i<g.argc; i++){ | |
| 80 | + char *zFile; /* Input filename */ | |
| 81 | + char *zTreename; /* Name of the file in the tree */ | |
| 82 | + Blob fname; /* Filename relative to root */ | |
| 83 | + char *zOp; /* Operation on this file */ | |
| 84 | + zFile = mprintf("%/", g.argv[i]); | |
| 85 | + file_tree_name(zFile, &fname, 0, 1); | |
| 86 | + fossil_free(zFile); | |
| 87 | + zTreename = blob_str(&fname); | |
| 88 | + zOp = db_text(0, "SELECT op FROM mergestat WHERE fn=%Q or fnr=%Q", | |
| 89 | + zTreename, zTreename); | |
| 90 | + blob_appendf(&script, " %s ", zOp); | |
| 91 | + fossil_free(zOp); | |
| 92 | + blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename)); | |
| 93 | + blob_reset(&fname); | |
| 94 | + } | |
| 95 | + } | |
| 96 | + blob_appendf(&script, "]\n"); | |
| 97 | + blob_appendf(&script, "set darkmode %d\n", bDark!=0); | |
| 98 | + blob_appendf(&script, "%s", builtin_file("merge.tcl", 0)); | |
| 99 | + if( zTempFile ){ | |
| 100 | + blob_write_to_file(&script, zTempFile); | |
| 101 | + fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile); | |
| 102 | + }else{ | |
| 103 | +#if defined(FOSSIL_ENABLE_TCL) | |
| 104 | + Th_FossilInit(TH_INIT_DEFAULT); | |
| 105 | + if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script), | |
| 106 | + blob_size(&script), 1, 1, 0)==TCL_OK ){ | |
| 107 | + blob_reset(&script); | |
| 108 | + return; | |
| 109 | + } | |
| 110 | + /* | |
| 111 | + * If evaluation of the Tcl script fails, the reason may be that Tk | |
| 112 | + * could not be found by the loaded Tcl, or that Tcl cannot be loaded | |
| 113 | + * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback | |
| 114 | + * to using the external "tclsh", if available. | |
| 115 | + */ | |
| 116 | +#endif | |
| 117 | + zTempFile = write_blob_to_temp_file(&script); | |
| 118 | + zCmd = mprintf("%$ %$", zTclsh, zTempFile); | |
| 119 | + fossil_system(zCmd); | |
| 120 | + file_delete(zTempFile); | |
| 121 | + fossil_free(zCmd); | |
| 122 | + } | |
| 123 | + blob_reset(&script); | |
| 124 | +} | |
| 125 | + | |
| 126 | +/* | |
| 127 | +** Generate a TCL list on standard output that can be fed into the | |
| 128 | +** merge.tcl script to show the details of the most recent merge | |
| 129 | +** command associated with file "zFName". zFName must be the filename | |
| 130 | +** relative to the root of the check-in - in other words a "tree name". | |
| 131 | +** | |
| 132 | +** When this routine is called, we know that the mergestat table | |
| 133 | +** exists, but we do not know if zFName is mentioned in that table. | |
| 134 | +*/ | |
| 135 | +static void merge_info_tcl(const char *zFName, int nContext){ | |
| 136 | + const char *zTreename;/* Name of the file in the tree */ | |
| 137 | + Stmt q; /* To query the MERGESTAT table */ | |
| 138 | + MergeBuilder mb; /* The merge builder object */ | |
| 139 | + Blob pivot,v1,v2,out; /* Blobs for holding content */ | |
| 140 | + const char *zFN; /* A filename */ | |
| 141 | + int rid; /* RID value */ | |
| 142 | + int sz; /* File size value */ | |
| 143 | + | |
| 144 | + zTreename = zFName; | |
| 145 | + db_prepare(&q, | |
| 146 | + /* 0 1 2 3 4 5 6 7 */ | |
| 147 | + "SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr" | |
| 148 | + " FROM mergestat" | |
| 149 | + " WHERE fnp=%Q OR fnr=%Q", | |
| 150 | + zTreename, zTreename | |
| 151 | + ); | |
| 152 | + if( db_step(&q)!=SQLITE_ROW ){ | |
| 153 | + db_finalize(&q); | |
| 154 | + fossil_print("ERROR {don't know anything about file: %s}\n", zTreename); | |
| 155 | + return; | |
| 156 | + } | |
| 157 | + mergebuilder_init_tcl(&mb); | |
| 158 | + mb.nContext = nContext; | |
| 159 | + | |
| 160 | + /* Set up the pivot */ | |
| 161 | + zFN = db_column_text(&q, 0); | |
| 162 | + if( zFN==0 ){ | |
| 163 | + /* No pivot because the file was added */ | |
| 164 | + mb.zPivot = "(no baseline)"; | |
| 165 | + blob_zero(&pivot); | |
| 166 | + }else{ | |
| 167 | + mb.zPivot = mprintf("%s (baseline)", file_tail(zFN)); | |
| 168 | + rid = db_column_int(&q, 1); | |
| 169 | + content_get(rid, &pivot); | |
| 170 | + } | |
| 171 | + mb.pPivot = &pivot; | |
| 172 | + | |
| 173 | + /* Set up the merge-in as V2 */ | |
| 174 | + zFN = db_column_text(&q, 5); | |
| 175 | + if( zFN==0 ){ | |
| 176 | + /* File deleted in the merged-in branch */ | |
| 177 | + mb.zV2 = "(deleted file)"; | |
| 178 | + blob_zero(&v2); | |
| 179 | + }else{ | |
| 180 | + mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN)); | |
| 181 | + rid = db_column_int(&q, 6); | |
| 182 | + content_get(rid, &v2); | |
| 183 | + } | |
| 184 | + mb.pV2 = &v2; | |
| 185 | + | |
| 186 | + /* Set up the local content as V1 */ | |
| 187 | + zFN = db_column_text(&q, 2); | |
| 188 | + if( zFN==0 ){ | |
| 189 | + /* File added by merge */ | |
| 190 | + mb.zV1 = "(no original)"; | |
| 191 | + blob_zero(&v1); | |
| 192 | + }else{ | |
| 193 | + mb.zV1 = mprintf("%s (local)", file_tail(zFN)); | |
| 194 | + rid = db_column_int(&q, 3); | |
| 195 | + sz = db_column_int(&q, 4); | |
| 196 | + if( rid==0 && sz>0 ){ | |
| 197 | + /* The origin file had been edited so we'll have to pull its | |
| 198 | + ** original content out of the undo buffer */ | |
| 199 | + Stmt q2; | |
| 200 | + db_prepare(&q2, | |
| 201 | + "SELECT content FROM undo" | |
| 202 | + " WHERE pathname=%Q AND octet_length(content)=%d", | |
| 203 | + zFN, sz | |
| 204 | + ); | |
| 205 | + blob_zero(&v1); | |
| 206 | + if( db_step(&q2)==SQLITE_ROW ){ | |
| 207 | + db_column_blob(&q2, 0, &v1); | |
| 208 | + }else{ | |
| 209 | + mb.zV1 = "(local content missing)"; | |
| 210 | + } | |
| 211 | + db_finalize(&q2); | |
| 212 | + }else{ | |
| 213 | + /* The origin file was unchanged when the merge first occurred */ | |
| 214 | + content_get(rid, &v1); | |
| 215 | + } | |
| 216 | + } | |
| 217 | + mb.pV1 = &v1; | |
| 218 | + | |
| 219 | + /* Set up the output */ | |
| 220 | + zFN = db_column_text(&q, 7); | |
| 221 | + if( zFN==0 ){ | |
| 222 | + mb.zOut = "(Merge Result)"; | |
| 223 | + }else{ | |
| 224 | + mb.zOut = mprintf("%s (after merge)", file_tail(zFN)); | |
| 225 | + } | |
| 226 | + blob_zero(&out); | |
| 227 | + mb.pOut = &out; | |
| 228 | + | |
| 229 | + merge_three_blobs(&mb); | |
| 230 | + blob_write_to_file(&out, "-"); | |
| 231 | + | |
| 232 | + mb.xDestroy(&mb); | |
| 233 | + blob_reset(&pivot); | |
| 234 | + blob_reset(&v1); | |
| 235 | + blob_reset(&v2); | |
| 236 | + blob_reset(&out); | |
| 237 | + db_finalize(&q); | |
| 238 | +} | |
| 239 | + | |
| 240 | +/* | |
| 241 | +** COMMAND: merge-info | |
| 242 | +** | |
| 243 | +** Usage: %fossil merge-info [OPTIONS] | |
| 244 | +** | |
| 245 | +** Display information about the most recent merge operation. | |
| 246 | +** | |
| 247 | +** Options: | |
| 248 | +** -a|--all Show all file changes that happened because of | |
| 249 | +** the merge. Normally only MERGE, CONFLICT, and ERROR | |
| 250 | +** lines are shown | |
| 251 | +** -c|--context N Show N lines of context around each change, | |
| 252 | +** with negative N meaning show all content. Only | |
| 253 | +** meaningful in combination with --tcl or --tk. | |
| 254 | +** --dark Use dark mode for the Tcl/Tk-based GUI | |
| 255 | +** --tcl FILE Generate (to stdout) a TCL list containing | |
| 256 | +** information needed to display the changes to | |
| 257 | +** FILE caused by the most recent merge. FILE must | |
| 258 | +** be a pathname relative to the root of the check-out. | |
| 259 | +** --tk Bring up a Tcl/Tk GUI that shows the changes | |
| 260 | +** associated with the most recent merge. | |
| 261 | +** | |
| 262 | +*/ | |
| 263 | +void merge_info_cmd(void){ | |
| 264 | + const char *zCnt; | |
| 265 | + const char *zTcl; | |
| 266 | + int bTk; | |
| 267 | + int bDark; | |
| 268 | + int bAll; | |
| 269 | + int nContext; | |
| 270 | + Stmt q; | |
| 271 | + const char *zWhere; | |
| 272 | + int cnt = 0; | |
| 273 | + | |
| 274 | + db_must_be_within_tree(); | |
| 275 | + zTcl = find_option("tcl", 0, 1); | |
| 276 | + bTk = find_option("tk", 0, 0)!=0; | |
| 277 | + zCnt = find_option("context", "c", 1); | |
| 278 | + bDark = find_option("dark", 0, 0)!=0; | |
| 279 | + bAll = find_option("all", "a", 0)!=0; | |
| 280 | + if( bTk==0 ){ | |
| 281 | + verify_all_options(); | |
| 282 | + if( g.argc>2 ){ | |
| 283 | + usage("[OPTIONS]"); | |
| 284 | + } | |
| 285 | + } | |
| 286 | + if( zCnt ){ | |
| 287 | + nContext = atoi(zCnt); | |
| 288 | + if( nContext<0 ) nContext = 0xfffffff; | |
| 289 | + }else{ | |
| 290 | + nContext = 6; | |
| 291 | + } | |
| 292 | + if( !db_table_exists("localdb","mergestat") ){ | |
| 293 | + if( zTcl ){ | |
| 294 | + fossil_print("ERROR {no merge data available}\n"); | |
| 295 | + }else{ | |
| 296 | + fossil_print("No merge data is available\n"); | |
| 297 | + } | |
| 298 | + return; | |
| 299 | + } | |
| 300 | + if( bTk ){ | |
| 301 | + merge_info_tk(bDark, bAll, nContext); | |
| 302 | + return; | |
| 303 | + } | |
| 304 | + if( zTcl ){ | |
| 305 | + merge_info_tcl(zTcl, nContext); | |
| 306 | + return; | |
| 307 | + } | |
| 308 | + if( bAll ){ | |
| 309 | + zWhere = ""; | |
| 310 | + }else{ | |
| 311 | + zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; | |
| 312 | + } | |
| 313 | + db_prepare(&q, | |
| 314 | + "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0)," | |
| 315 | + "('MERGE',1),('ADDED',2),('UPDATE',2))" | |
| 316 | + | |
| 317 | + /* 0 1 2 */ | |
| 318 | + "SELECT op, coalesce(fnr,fn), msg" | |
| 319 | + " FROM mergestat JOIN priority USING(op)" | |
| 320 | + " %s" | |
| 321 | + " ORDER BY pri, coalesce(fnr,fn)", | |
| 322 | + zWhere /*safe-for-%s*/ | |
| 323 | + ); | |
| 324 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 325 | + const char *zOp = db_column_text(&q, 0); | |
| 326 | + const char *zName = db_column_text(&q, 1); | |
| 327 | + const char *zErr = db_column_text(&q, 2); | |
| 328 | + if( zErr && fossil_strcmp(zOp,"CONFLICT")!=0 ){ | |
| 329 | + fossil_print("%-9s %s (%s)\n", zOp, zName, zErr); | |
| 330 | + }else{ | |
| 331 | + fossil_print("%-9s %s\n", zOp, zName); | |
| 332 | + } | |
| 333 | + cnt++; | |
| 334 | + } | |
| 335 | + db_finalize(&q); | |
| 336 | + if( !bAll && cnt==0 ){ | |
| 337 | + fossil_print( | |
| 338 | + "No interesting changes in this merge. Use --all to see everything.\n" | |
| 339 | + ); | |
| 340 | + } | |
| 341 | +} | |
| 342 | + | |
| 343 | +/* | |
| 344 | +** Erase all information about prior merges. Do this, for example, after | |
| 345 | +** a commit. | |
| 346 | +*/ | |
| 347 | +void merge_info_forget(void){ | |
| 348 | + db_multi_exec( | |
| 349 | + "DROP TABLE IF EXISTS localdb.mergestat;" | |
| 350 | + "DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';" | |
| 351 | + ); | |
| 352 | +} | |
| 353 | + | |
| 354 | + | |
| 355 | +/* | |
| 356 | +** Initialize the MERGESTAT table. | |
| 357 | +** | |
| 358 | +** Notes about mergestat: | |
| 359 | +** | |
| 360 | +** * ridv is a positive integer and sz is NULL if the V file contained | |
| 361 | +** no local edits prior to the merge. If the V file was modified prior | |
| 362 | +** to the merge then ridv is NULL and sz is the size of the file prior | |
| 363 | +** to merge. | |
| 364 | +** | |
| 365 | +** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was | |
| 366 | +** added by merge. | |
| 367 | +*/ | |
| 368 | +void merge_info_init(void){ | |
| 369 | + merge_info_forget(); | |
| 370 | + db_multi_exec( | |
| 371 | + "CREATE TABLE localdb.mergestat(\n" | |
| 372 | + " op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n" | |
| 373 | + " fnp TEXT, -- Name of the pivot file (P)\n" | |
| 374 | + " ridp INT, -- RID for the pivot file\n" | |
| 375 | + " fn TEXT, -- Name of origin file (V)\n" | |
| 376 | + " ridv INT, -- RID for origin file, or NULL if previously edited\n" | |
| 377 | + " sz INT, -- Size of origin file in bytes, NULL if unedited\n" | |
| 378 | + " fnm TEXT, -- Name of the file being merged in (M)\n" | |
| 379 | + " ridm INT, -- RID for the merge-in file\n" | |
| 380 | + " fnr TEXT, -- Name of the final output file, after all renaming\n" | |
| 381 | + " nc INT DEFAULT 0, -- Number of conflicts\n" | |
| 382 | + " msg TEXT -- Error message\n" | |
| 383 | + ");" | |
| 384 | + ); | |
| 385 | +} | |
| 386 | + | |
| 25 | 387 | /* |
| 26 | 388 | ** Print information about a particular check-in. |
| 27 | 389 | */ |
| 28 | 390 | void print_checkin_description(int rid, int indent, const char *zLabel){ |
| 29 | 391 | Stmt q; |
| @@ -295,10 +657,13 @@ | ||
| 295 | 657 | ** Files which are renamed in the merged-in branch will be renamed in |
| 296 | 658 | ** the current check-out. |
| 297 | 659 | ** |
| 298 | 660 | ** If the VERSION argument is omitted, then Fossil attempts to find |
| 299 | 661 | ** a recent fork on the current branch to merge. |
| 662 | +** | |
| 663 | +** Note that this command does not commit the merge, as that is a | |
| 664 | +** separate step. | |
| 300 | 665 | ** |
| 301 | 666 | ** If there are multiple VERSION arguments, then each VERSION is merged |
| 302 | 667 | ** (or cherrypicked) in the order that they appear on the command-line. |
| 303 | 668 | ** |
| 304 | 669 | ** Options: |
| @@ -320,11 +685,11 @@ | ||
| 320 | 685 | ** --force-missing Force the merge even if there is missing content |
| 321 | 686 | ** --integrate Merged branch will be closed when committing |
| 322 | 687 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 323 | 688 | ** used for merging, named *-baseline, *-original, |
| 324 | 689 | ** and *-merge. |
| 325 | -** -n|--dry-run If given, display instead of run actions | |
| 690 | +** -n|--dry-run Do not actually change files on disk | |
| 326 | 691 | ** --nosync Do not auto-sync prior to merging |
| 327 | 692 | ** -v|--verbose Show additional details of the merge |
| 328 | 693 | */ |
| 329 | 694 | void merge_cmd(void){ |
| 330 | 695 | int vid; /* Current version "V" */ |
| @@ -797,15 +1162,21 @@ | ||
| 797 | 1162 | |
| 798 | 1163 | /************************************************************************ |
| 799 | 1164 | ** All of the information needed to do the merge is now contained in the |
| 800 | 1165 | ** FV table. Starting here, we begin to actually carry out the merge. |
| 801 | 1166 | ** |
| 802 | - ** First, find files that have changed from P->M but not P->V. | |
| 1167 | + ** Begin by constructing the localdb.mergestat table. | |
| 1168 | + */ | |
| 1169 | + merge_info_init(); | |
| 1170 | + | |
| 1171 | + /* | |
| 1172 | + ** Find files that have changed from P->M but not P->V. | |
| 803 | 1173 | ** Copy the M content over into V. |
| 804 | 1174 | */ |
| 805 | 1175 | db_prepare(&q, |
| 806 | - "SELECT idv, ridm, fn, islinkm FROM fv" | |
| 1176 | + /* 0 1 2 3 4 5 6 7 */ | |
| 1177 | + "SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv" | |
| 807 | 1178 | " WHERE idp>0 AND idv>0 AND idm>0" |
| 808 | 1179 | " AND ridm!=ridp AND ridv=ridp AND NOT chnged" |
| 809 | 1180 | ); |
| 810 | 1181 | while( db_step(&q)==SQLITE_ROW ){ |
| 811 | 1182 | int idv = db_column_int(&q, 0); |
| @@ -822,10 +1193,21 @@ | ||
| 822 | 1193 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 823 | 1194 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| 824 | 1195 | ); |
| 825 | 1196 | vfile_to_disk(0, idv, 0, 0); |
| 826 | 1197 | } |
| 1198 | + db_multi_exec( | |
| 1199 | + "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)" | |
| 1200 | + "VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)", | |
| 1201 | + /* fnp */ db_column_text(&q, 4), | |
| 1202 | + /* ridp */ db_column_int(&q,5), | |
| 1203 | + /* fn */ zName, | |
| 1204 | + /* ridv */ db_column_int(&q,6), | |
| 1205 | + /* fnm */ db_column_text(&q, 7), | |
| 1206 | + /* ridm */ ridm, | |
| 1207 | + /* fnr */ zName | |
| 1208 | + ); | |
| 827 | 1209 | } |
| 828 | 1210 | db_finalize(&q); |
| 829 | 1211 | |
| 830 | 1212 | /* |
| 831 | 1213 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| @@ -833,11 +1215,15 @@ | ||
| 833 | 1215 | ** Proceed even if the file doesn't exist on P, just like the common ancestor |
| 834 | 1216 | ** of M and V is an empty file. In this case, merge conflict marks will be |
| 835 | 1217 | ** added to the file and user will be forced to take a decision. |
| 836 | 1218 | */ |
| 837 | 1219 | db_prepare(&q, |
| 838 | - "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv" | |
| 1220 | + /* 0 1 2 3 4 5 6 7 8 */ | |
| 1221 | + "SELECT ridm, idv, ridp, ridv, %z, fn, isexe, islinkv, islinkm," | |
| 1222 | + /* 9 10 11 */ | |
| 1223 | + " fnp, fnm, chnged" | |
| 1224 | + " FROM fv" | |
| 839 | 1225 | " WHERE idv>0 AND idm>0" |
| 840 | 1226 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 841 | 1227 | glob_expr("fv.fn", zBinGlob) |
| 842 | 1228 | ); |
| 843 | 1229 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -848,12 +1234,14 @@ | ||
| 848 | 1234 | int isBinary = db_column_int(&q, 4); |
| 849 | 1235 | const char *zName = db_column_text(&q, 5); |
| 850 | 1236 | int isExe = db_column_int(&q, 6); |
| 851 | 1237 | int islinkv = db_column_int(&q, 7); |
| 852 | 1238 | int islinkm = db_column_int(&q, 8); |
| 1239 | + int chnged = db_column_int(&q, 11); | |
| 853 | 1240 | int rc; |
| 854 | 1241 | char *zFullPath; |
| 1242 | + const char *zType = "MERGE"; | |
| 855 | 1243 | Blob m, p, r; |
| 856 | 1244 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 857 | 1245 | if( verboseFlag ){ |
| 858 | 1246 | fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", |
| 859 | 1247 | zName, ridp, ridm, ridv); |
| @@ -861,13 +1249,29 @@ | ||
| 861 | 1249 | fossil_print("MERGE %s\n", zName); |
| 862 | 1250 | } |
| 863 | 1251 | if( islinkv || islinkm ){ |
| 864 | 1252 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 865 | 1253 | nConflict++; |
| 1254 | + db_multi_exec( | |
| 1255 | + "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)" | |
| 1256 | + "VALUES('ERROR',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')", | |
| 1257 | + /* fnp */ db_column_text(&q, 9), | |
| 1258 | + /* ridp */ ridp, | |
| 1259 | + /* fn */ zName, | |
| 1260 | + /* ridv */ ridv, | |
| 1261 | + /* fnm */ db_column_text(&q, 10), | |
| 1262 | + /* ridm */ ridm, | |
| 1263 | + /* fnr */ zName | |
| 1264 | + ); | |
| 866 | 1265 | }else{ |
| 1266 | + i64 sz; | |
| 1267 | + const char *zErrMsg = 0; | |
| 1268 | + int nc = 0; | |
| 1269 | + | |
| 867 | 1270 | if( !dryRunFlag ) undo_save(zName); |
| 868 | 1271 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1272 | + sz = file_size(zFullPath, ExtFILE); | |
| 869 | 1273 | content_get(ridp, &p); |
| 870 | 1274 | content_get(ridm, &m); |
| 871 | 1275 | if( isBinary ){ |
| 872 | 1276 | rc = -1; |
| 873 | 1277 | blob_zero(&r); |
| @@ -884,15 +1288,38 @@ | ||
| 884 | 1288 | db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv); |
| 885 | 1289 | if( rc>0 ){ |
| 886 | 1290 | fossil_print("***** %d merge conflict%s in %s\n", |
| 887 | 1291 | rc, rc>1 ? "s" : "", zName); |
| 888 | 1292 | nConflict++; |
| 1293 | + nc = rc; | |
| 1294 | + zErrMsg = "merge conflicts"; | |
| 1295 | + zType = "CONFLICT"; | |
| 889 | 1296 | } |
| 890 | 1297 | }else{ |
| 891 | 1298 | fossil_print("***** Cannot merge binary file %s\n", zName); |
| 892 | 1299 | nConflict++; |
| 1300 | + nc = 1; | |
| 1301 | + zErrMsg = "cannot merge binary file"; | |
| 1302 | + zType = "ERROR"; | |
| 893 | 1303 | } |
| 1304 | + db_multi_exec( | |
| 1305 | + "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)" | |
| 1306 | + "VALUES(%Q,%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%lld,NULL),%Q,%d," | |
| 1307 | + "%Q,%d,%Q)", | |
| 1308 | + /* op */ zType, | |
| 1309 | + /* fnp */ db_column_text(&q, 9), | |
| 1310 | + /* ridp */ ridp, | |
| 1311 | + /* fn */ zName, | |
| 1312 | + /* ridv */ chnged==0, ridv, | |
| 1313 | + /* sz */ chnged!=0, sz, | |
| 1314 | + /* fnm */ db_column_text(&q, 10), | |
| 1315 | + /* ridm */ ridm, | |
| 1316 | + /* fnr */ zName, | |
| 1317 | + /* nc */ nc, | |
| 1318 | + /* msg */ zErrMsg | |
| 1319 | + ); | |
| 1320 | + fossil_free(zFullPath); | |
| 894 | 1321 | blob_reset(&p); |
| 895 | 1322 | blob_reset(&m); |
| 896 | 1323 | blob_reset(&r); |
| 897 | 1324 | } |
| 898 | 1325 | vmerge_insert(idv, ridm); |
| @@ -901,22 +1328,33 @@ | ||
| 901 | 1328 | |
| 902 | 1329 | /* |
| 903 | 1330 | ** Drop files that are in P and V but not in M |
| 904 | 1331 | */ |
| 905 | 1332 | db_prepare(&q, |
| 906 | - "SELECT idv, fn, chnged FROM fv" | |
| 1333 | + "SELECT idv, fn, chnged, ridv FROM fv" | |
| 907 | 1334 | " WHERE idp>0 AND idv>0 AND idm=0" |
| 908 | 1335 | ); |
| 909 | 1336 | while( db_step(&q)==SQLITE_ROW ){ |
| 910 | 1337 | int idv = db_column_int(&q, 0); |
| 911 | 1338 | const char *zName = db_column_text(&q, 1); |
| 912 | 1339 | int chnged = db_column_int(&q, 2); |
| 1340 | + int ridv = db_column_int(&q, 3); | |
| 1341 | + int sz = -1; | |
| 1342 | + const char *zErrMsg = 0; | |
| 1343 | + int nc = 0; | |
| 913 | 1344 | /* Delete the file idv */ |
| 914 | 1345 | fossil_print("DELETE %s\n", zName); |
| 915 | 1346 | if( chnged ){ |
| 1347 | + char *zFullPath; | |
| 916 | 1348 | fossil_warning("WARNING: local edits lost for %s", zName); |
| 917 | 1349 | nConflict++; |
| 1350 | + ridv = 0; | |
| 1351 | + nc = 1; | |
| 1352 | + zErrMsg = "local edits lost"; | |
| 1353 | + zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); | |
| 1354 | + sz = file_size(zFullPath, ExtFILE); | |
| 1355 | + fossil_free(zFullPath); | |
| 918 | 1356 | } |
| 919 | 1357 | if( !dryRunFlag ) undo_save(zName); |
| 920 | 1358 | db_multi_exec( |
| 921 | 1359 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 922 | 1360 | ); |
| @@ -923,10 +1361,20 @@ | ||
| 923 | 1361 | if( !dryRunFlag ){ |
| 924 | 1362 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 925 | 1363 | file_delete(zFullPath); |
| 926 | 1364 | free(zFullPath); |
| 927 | 1365 | } |
| 1366 | + db_multi_exec( | |
| 1367 | + "INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)" | |
| 1368 | + "VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL)," | |
| 1369 | + "NULL,NULL,%d,%Q)", | |
| 1370 | + /* fn */ zName, | |
| 1371 | + /* ridv */ chnged==0, ridv, | |
| 1372 | + /* sz */ chnged!=0, sz, | |
| 1373 | + /* nc */ nc, | |
| 1374 | + /* msg */ zErrMsg | |
| 1375 | + ); | |
| 928 | 1376 | } |
| 929 | 1377 | db_finalize(&q); |
| 930 | 1378 | |
| 931 | 1379 | /* For certain sets of renames (e.g. A -> B and B -> A), a file that is |
| 932 | 1380 | ** being renamed must first be moved to a temporary location to avoid |
| @@ -953,10 +1401,14 @@ | ||
| 953 | 1401 | const char *zNewName = db_column_text(&q, 2); |
| 954 | 1402 | int isExe = db_column_int(&q, 3); |
| 955 | 1403 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 956 | 1404 | if( !dryRunFlag ) undo_save(zOldName); |
| 957 | 1405 | if( !dryRunFlag ) undo_save(zNewName); |
| 1406 | + db_multi_exec( | |
| 1407 | + "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q", | |
| 1408 | + zOldName | |
| 1409 | + ); | |
| 958 | 1410 | db_multi_exec( |
| 959 | 1411 | "UPDATE vfile SET pathname=NULL, origname=pathname" |
| 960 | 1412 | " WHERE vid=%d AND pathname=%Q;" |
| 961 | 1413 | "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" |
| 962 | 1414 | " WHERE id=%d;", |
| @@ -1006,11 +1458,11 @@ | ||
| 1006 | 1458 | |
| 1007 | 1459 | /* |
| 1008 | 1460 | ** Insert into V any files that are not in V or P but are in M. |
| 1009 | 1461 | */ |
| 1010 | 1462 | db_prepare(&q, |
| 1011 | - "SELECT idm, fnm FROM fv" | |
| 1463 | + "SELECT idm, fnm, ridm FROM fv" | |
| 1012 | 1464 | " WHERE idp=0 AND idv=0 AND idm>0" |
| 1013 | 1465 | ); |
| 1014 | 1466 | while( db_step(&q)==SQLITE_ROW ){ |
| 1015 | 1467 | int idm = db_column_int(&q, 0); |
| 1016 | 1468 | const char *zName; |
| @@ -1039,10 +1491,17 @@ | ||
| 1039 | 1491 | nOverwrite++; |
| 1040 | 1492 | }else{ |
| 1041 | 1493 | fossil_print("ADDED %s\n", zName); |
| 1042 | 1494 | } |
| 1043 | 1495 | fossil_free(zFullName); |
| 1496 | + db_multi_exec( | |
| 1497 | + "INSERT INTO mergestat(op,fnm,ridm,fnr)" | |
| 1498 | + "VALUES('ADDED',%Q,%d,%Q)", | |
| 1499 | + /* fnm */ zName, | |
| 1500 | + /* ridm */ db_column_int(&q,2), | |
| 1501 | + /* fnr */ zName | |
| 1502 | + ); | |
| 1044 | 1503 | if( !dryRunFlag ){ |
| 1045 | 1504 | undo_save(zName); |
| 1046 | 1505 | vfile_to_disk(0, idm, 0, 0); |
| 1047 | 1506 | } |
| 1048 | 1507 | } |
| 1049 | 1508 | |
| 1050 | 1509 | ADDED src/merge.tcl |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -20,10 +20,372 @@ | |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "merge.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** Print information about a particular check-in. |
| 27 | */ |
| 28 | void print_checkin_description(int rid, int indent, const char *zLabel){ |
| 29 | Stmt q; |
| @@ -295,10 +657,13 @@ | |
| 295 | ** Files which are renamed in the merged-in branch will be renamed in |
| 296 | ** the current check-out. |
| 297 | ** |
| 298 | ** If the VERSION argument is omitted, then Fossil attempts to find |
| 299 | ** a recent fork on the current branch to merge. |
| 300 | ** |
| 301 | ** If there are multiple VERSION arguments, then each VERSION is merged |
| 302 | ** (or cherrypicked) in the order that they appear on the command-line. |
| 303 | ** |
| 304 | ** Options: |
| @@ -320,11 +685,11 @@ | |
| 320 | ** --force-missing Force the merge even if there is missing content |
| 321 | ** --integrate Merged branch will be closed when committing |
| 322 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 323 | ** used for merging, named *-baseline, *-original, |
| 324 | ** and *-merge. |
| 325 | ** -n|--dry-run If given, display instead of run actions |
| 326 | ** --nosync Do not auto-sync prior to merging |
| 327 | ** -v|--verbose Show additional details of the merge |
| 328 | */ |
| 329 | void merge_cmd(void){ |
| 330 | int vid; /* Current version "V" */ |
| @@ -797,15 +1162,21 @@ | |
| 797 | |
| 798 | /************************************************************************ |
| 799 | ** All of the information needed to do the merge is now contained in the |
| 800 | ** FV table. Starting here, we begin to actually carry out the merge. |
| 801 | ** |
| 802 | ** First, find files that have changed from P->M but not P->V. |
| 803 | ** Copy the M content over into V. |
| 804 | */ |
| 805 | db_prepare(&q, |
| 806 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 807 | " WHERE idp>0 AND idv>0 AND idm>0" |
| 808 | " AND ridm!=ridp AND ridv=ridp AND NOT chnged" |
| 809 | ); |
| 810 | while( db_step(&q)==SQLITE_ROW ){ |
| 811 | int idv = db_column_int(&q, 0); |
| @@ -822,10 +1193,21 @@ | |
| 822 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 823 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| 824 | ); |
| 825 | vfile_to_disk(0, idv, 0, 0); |
| 826 | } |
| 827 | } |
| 828 | db_finalize(&q); |
| 829 | |
| 830 | /* |
| 831 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| @@ -833,11 +1215,15 @@ | |
| 833 | ** Proceed even if the file doesn't exist on P, just like the common ancestor |
| 834 | ** of M and V is an empty file. In this case, merge conflict marks will be |
| 835 | ** added to the file and user will be forced to take a decision. |
| 836 | */ |
| 837 | db_prepare(&q, |
| 838 | "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv" |
| 839 | " WHERE idv>0 AND idm>0" |
| 840 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 841 | glob_expr("fv.fn", zBinGlob) |
| 842 | ); |
| 843 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -848,12 +1234,14 @@ | |
| 848 | int isBinary = db_column_int(&q, 4); |
| 849 | const char *zName = db_column_text(&q, 5); |
| 850 | int isExe = db_column_int(&q, 6); |
| 851 | int islinkv = db_column_int(&q, 7); |
| 852 | int islinkm = db_column_int(&q, 8); |
| 853 | int rc; |
| 854 | char *zFullPath; |
| 855 | Blob m, p, r; |
| 856 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 857 | if( verboseFlag ){ |
| 858 | fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", |
| 859 | zName, ridp, ridm, ridv); |
| @@ -861,13 +1249,29 @@ | |
| 861 | fossil_print("MERGE %s\n", zName); |
| 862 | } |
| 863 | if( islinkv || islinkm ){ |
| 864 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 865 | nConflict++; |
| 866 | }else{ |
| 867 | if( !dryRunFlag ) undo_save(zName); |
| 868 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 869 | content_get(ridp, &p); |
| 870 | content_get(ridm, &m); |
| 871 | if( isBinary ){ |
| 872 | rc = -1; |
| 873 | blob_zero(&r); |
| @@ -884,15 +1288,38 @@ | |
| 884 | db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv); |
| 885 | if( rc>0 ){ |
| 886 | fossil_print("***** %d merge conflict%s in %s\n", |
| 887 | rc, rc>1 ? "s" : "", zName); |
| 888 | nConflict++; |
| 889 | } |
| 890 | }else{ |
| 891 | fossil_print("***** Cannot merge binary file %s\n", zName); |
| 892 | nConflict++; |
| 893 | } |
| 894 | blob_reset(&p); |
| 895 | blob_reset(&m); |
| 896 | blob_reset(&r); |
| 897 | } |
| 898 | vmerge_insert(idv, ridm); |
| @@ -901,22 +1328,33 @@ | |
| 901 | |
| 902 | /* |
| 903 | ** Drop files that are in P and V but not in M |
| 904 | */ |
| 905 | db_prepare(&q, |
| 906 | "SELECT idv, fn, chnged FROM fv" |
| 907 | " WHERE idp>0 AND idv>0 AND idm=0" |
| 908 | ); |
| 909 | while( db_step(&q)==SQLITE_ROW ){ |
| 910 | int idv = db_column_int(&q, 0); |
| 911 | const char *zName = db_column_text(&q, 1); |
| 912 | int chnged = db_column_int(&q, 2); |
| 913 | /* Delete the file idv */ |
| 914 | fossil_print("DELETE %s\n", zName); |
| 915 | if( chnged ){ |
| 916 | fossil_warning("WARNING: local edits lost for %s", zName); |
| 917 | nConflict++; |
| 918 | } |
| 919 | if( !dryRunFlag ) undo_save(zName); |
| 920 | db_multi_exec( |
| 921 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 922 | ); |
| @@ -923,10 +1361,20 @@ | |
| 923 | if( !dryRunFlag ){ |
| 924 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 925 | file_delete(zFullPath); |
| 926 | free(zFullPath); |
| 927 | } |
| 928 | } |
| 929 | db_finalize(&q); |
| 930 | |
| 931 | /* For certain sets of renames (e.g. A -> B and B -> A), a file that is |
| 932 | ** being renamed must first be moved to a temporary location to avoid |
| @@ -953,10 +1401,14 @@ | |
| 953 | const char *zNewName = db_column_text(&q, 2); |
| 954 | int isExe = db_column_int(&q, 3); |
| 955 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 956 | if( !dryRunFlag ) undo_save(zOldName); |
| 957 | if( !dryRunFlag ) undo_save(zNewName); |
| 958 | db_multi_exec( |
| 959 | "UPDATE vfile SET pathname=NULL, origname=pathname" |
| 960 | " WHERE vid=%d AND pathname=%Q;" |
| 961 | "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" |
| 962 | " WHERE id=%d;", |
| @@ -1006,11 +1458,11 @@ | |
| 1006 | |
| 1007 | /* |
| 1008 | ** Insert into V any files that are not in V or P but are in M. |
| 1009 | */ |
| 1010 | db_prepare(&q, |
| 1011 | "SELECT idm, fnm FROM fv" |
| 1012 | " WHERE idp=0 AND idv=0 AND idm>0" |
| 1013 | ); |
| 1014 | while( db_step(&q)==SQLITE_ROW ){ |
| 1015 | int idm = db_column_int(&q, 0); |
| 1016 | const char *zName; |
| @@ -1039,10 +1491,17 @@ | |
| 1039 | nOverwrite++; |
| 1040 | }else{ |
| 1041 | fossil_print("ADDED %s\n", zName); |
| 1042 | } |
| 1043 | fossil_free(zFullName); |
| 1044 | if( !dryRunFlag ){ |
| 1045 | undo_save(zName); |
| 1046 | vfile_to_disk(0, idm, 0, 0); |
| 1047 | } |
| 1048 | } |
| 1049 | |
| 1050 | DDED src/merge.tcl |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -20,10 +20,372 @@ | |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "merge.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | |
| 26 | /* |
| 27 | ** Bring up a Tcl/Tk GUI to show details of the most recent merge. |
| 28 | */ |
| 29 | static void merge_info_tk(int bDark, int bAll, int nContext){ |
| 30 | int i; |
| 31 | Blob script; |
| 32 | const char *zTempFile = 0; |
| 33 | char *zCmd; |
| 34 | const char *zTclsh; |
| 35 | zTclsh = find_option("tclsh",0,1); |
| 36 | if( zTclsh==0 ){ |
| 37 | zTclsh = db_get("tclsh",0); |
| 38 | } |
| 39 | /* The undocumented --script FILENAME option causes the Tk script to |
| 40 | ** be written into the FILENAME instead of being run. This is used |
| 41 | ** for testing and debugging. */ |
| 42 | zTempFile = find_option("script",0,1); |
| 43 | verify_all_options(); |
| 44 | |
| 45 | blob_zero(&script); |
| 46 | blob_appendf(&script, "set ncontext %d\n", nContext); |
| 47 | blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n", |
| 48 | g.nameOfExe); |
| 49 | blob_appendf(&script, "set filelist [list"); |
| 50 | if( g.argc==2 ){ |
| 51 | /* No files named on the command-line. Use every file mentioned |
| 52 | ** in the MERGESTAT table to generate the file list. */ |
| 53 | Stmt q; |
| 54 | int cnt = 0; |
| 55 | db_prepare(&q, |
| 56 | "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0)," |
| 57 | "('MERGE',1),('ADDED',2),('UPDATE',2))" |
| 58 | "SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)" |
| 59 | " %s ORDER BY pri, 1", |
| 60 | bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/ |
| 61 | ); |
| 62 | while( db_step(&q)==SQLITE_ROW ){ |
| 63 | blob_appendf(&script," %s ", db_column_text(&q,1)); |
| 64 | blob_append_tcl_literal(&script, db_column_text(&q,0), |
| 65 | db_column_bytes(&q,0)); |
| 66 | cnt++; |
| 67 | } |
| 68 | db_finalize(&q); |
| 69 | if( cnt==0 ){ |
| 70 | fossil_print( |
| 71 | "No interesting changes in this merge. Use --all to see everything\n" |
| 72 | ); |
| 73 | return; |
| 74 | } |
| 75 | }else{ |
| 76 | /* Use only files named on the command-line in the file list. |
| 77 | ** But verify each file named is actually found in the MERGESTAT |
| 78 | ** table first. */ |
| 79 | for(i=2; i<g.argc; i++){ |
| 80 | char *zFile; /* Input filename */ |
| 81 | char *zTreename; /* Name of the file in the tree */ |
| 82 | Blob fname; /* Filename relative to root */ |
| 83 | char *zOp; /* Operation on this file */ |
| 84 | zFile = mprintf("%/", g.argv[i]); |
| 85 | file_tree_name(zFile, &fname, 0, 1); |
| 86 | fossil_free(zFile); |
| 87 | zTreename = blob_str(&fname); |
| 88 | zOp = db_text(0, "SELECT op FROM mergestat WHERE fn=%Q or fnr=%Q", |
| 89 | zTreename, zTreename); |
| 90 | blob_appendf(&script, " %s ", zOp); |
| 91 | fossil_free(zOp); |
| 92 | blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename)); |
| 93 | blob_reset(&fname); |
| 94 | } |
| 95 | } |
| 96 | blob_appendf(&script, "]\n"); |
| 97 | blob_appendf(&script, "set darkmode %d\n", bDark!=0); |
| 98 | blob_appendf(&script, "%s", builtin_file("merge.tcl", 0)); |
| 99 | if( zTempFile ){ |
| 100 | blob_write_to_file(&script, zTempFile); |
| 101 | fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 102 | }else{ |
| 103 | #if defined(FOSSIL_ENABLE_TCL) |
| 104 | Th_FossilInit(TH_INIT_DEFAULT); |
| 105 | if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script), |
| 106 | blob_size(&script), 1, 1, 0)==TCL_OK ){ |
| 107 | blob_reset(&script); |
| 108 | return; |
| 109 | } |
| 110 | /* |
| 111 | * If evaluation of the Tcl script fails, the reason may be that Tk |
| 112 | * could not be found by the loaded Tcl, or that Tcl cannot be loaded |
| 113 | * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback |
| 114 | * to using the external "tclsh", if available. |
| 115 | */ |
| 116 | #endif |
| 117 | zTempFile = write_blob_to_temp_file(&script); |
| 118 | zCmd = mprintf("%$ %$", zTclsh, zTempFile); |
| 119 | fossil_system(zCmd); |
| 120 | file_delete(zTempFile); |
| 121 | fossil_free(zCmd); |
| 122 | } |
| 123 | blob_reset(&script); |
| 124 | } |
| 125 | |
| 126 | /* |
| 127 | ** Generate a TCL list on standard output that can be fed into the |
| 128 | ** merge.tcl script to show the details of the most recent merge |
| 129 | ** command associated with file "zFName". zFName must be the filename |
| 130 | ** relative to the root of the check-in - in other words a "tree name". |
| 131 | ** |
| 132 | ** When this routine is called, we know that the mergestat table |
| 133 | ** exists, but we do not know if zFName is mentioned in that table. |
| 134 | */ |
| 135 | static void merge_info_tcl(const char *zFName, int nContext){ |
| 136 | const char *zTreename;/* Name of the file in the tree */ |
| 137 | Stmt q; /* To query the MERGESTAT table */ |
| 138 | MergeBuilder mb; /* The merge builder object */ |
| 139 | Blob pivot,v1,v2,out; /* Blobs for holding content */ |
| 140 | const char *zFN; /* A filename */ |
| 141 | int rid; /* RID value */ |
| 142 | int sz; /* File size value */ |
| 143 | |
| 144 | zTreename = zFName; |
| 145 | db_prepare(&q, |
| 146 | /* 0 1 2 3 4 5 6 7 */ |
| 147 | "SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr" |
| 148 | " FROM mergestat" |
| 149 | " WHERE fnp=%Q OR fnr=%Q", |
| 150 | zTreename, zTreename |
| 151 | ); |
| 152 | if( db_step(&q)!=SQLITE_ROW ){ |
| 153 | db_finalize(&q); |
| 154 | fossil_print("ERROR {don't know anything about file: %s}\n", zTreename); |
| 155 | return; |
| 156 | } |
| 157 | mergebuilder_init_tcl(&mb); |
| 158 | mb.nContext = nContext; |
| 159 | |
| 160 | /* Set up the pivot */ |
| 161 | zFN = db_column_text(&q, 0); |
| 162 | if( zFN==0 ){ |
| 163 | /* No pivot because the file was added */ |
| 164 | mb.zPivot = "(no baseline)"; |
| 165 | blob_zero(&pivot); |
| 166 | }else{ |
| 167 | mb.zPivot = mprintf("%s (baseline)", file_tail(zFN)); |
| 168 | rid = db_column_int(&q, 1); |
| 169 | content_get(rid, &pivot); |
| 170 | } |
| 171 | mb.pPivot = &pivot; |
| 172 | |
| 173 | /* Set up the merge-in as V2 */ |
| 174 | zFN = db_column_text(&q, 5); |
| 175 | if( zFN==0 ){ |
| 176 | /* File deleted in the merged-in branch */ |
| 177 | mb.zV2 = "(deleted file)"; |
| 178 | blob_zero(&v2); |
| 179 | }else{ |
| 180 | mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN)); |
| 181 | rid = db_column_int(&q, 6); |
| 182 | content_get(rid, &v2); |
| 183 | } |
| 184 | mb.pV2 = &v2; |
| 185 | |
| 186 | /* Set up the local content as V1 */ |
| 187 | zFN = db_column_text(&q, 2); |
| 188 | if( zFN==0 ){ |
| 189 | /* File added by merge */ |
| 190 | mb.zV1 = "(no original)"; |
| 191 | blob_zero(&v1); |
| 192 | }else{ |
| 193 | mb.zV1 = mprintf("%s (local)", file_tail(zFN)); |
| 194 | rid = db_column_int(&q, 3); |
| 195 | sz = db_column_int(&q, 4); |
| 196 | if( rid==0 && sz>0 ){ |
| 197 | /* The origin file had been edited so we'll have to pull its |
| 198 | ** original content out of the undo buffer */ |
| 199 | Stmt q2; |
| 200 | db_prepare(&q2, |
| 201 | "SELECT content FROM undo" |
| 202 | " WHERE pathname=%Q AND octet_length(content)=%d", |
| 203 | zFN, sz |
| 204 | ); |
| 205 | blob_zero(&v1); |
| 206 | if( db_step(&q2)==SQLITE_ROW ){ |
| 207 | db_column_blob(&q2, 0, &v1); |
| 208 | }else{ |
| 209 | mb.zV1 = "(local content missing)"; |
| 210 | } |
| 211 | db_finalize(&q2); |
| 212 | }else{ |
| 213 | /* The origin file was unchanged when the merge first occurred */ |
| 214 | content_get(rid, &v1); |
| 215 | } |
| 216 | } |
| 217 | mb.pV1 = &v1; |
| 218 | |
| 219 | /* Set up the output */ |
| 220 | zFN = db_column_text(&q, 7); |
| 221 | if( zFN==0 ){ |
| 222 | mb.zOut = "(Merge Result)"; |
| 223 | }else{ |
| 224 | mb.zOut = mprintf("%s (after merge)", file_tail(zFN)); |
| 225 | } |
| 226 | blob_zero(&out); |
| 227 | mb.pOut = &out; |
| 228 | |
| 229 | merge_three_blobs(&mb); |
| 230 | blob_write_to_file(&out, "-"); |
| 231 | |
| 232 | mb.xDestroy(&mb); |
| 233 | blob_reset(&pivot); |
| 234 | blob_reset(&v1); |
| 235 | blob_reset(&v2); |
| 236 | blob_reset(&out); |
| 237 | db_finalize(&q); |
| 238 | } |
| 239 | |
| 240 | /* |
| 241 | ** COMMAND: merge-info |
| 242 | ** |
| 243 | ** Usage: %fossil merge-info [OPTIONS] |
| 244 | ** |
| 245 | ** Display information about the most recent merge operation. |
| 246 | ** |
| 247 | ** Options: |
| 248 | ** -a|--all Show all file changes that happened because of |
| 249 | ** the merge. Normally only MERGE, CONFLICT, and ERROR |
| 250 | ** lines are shown |
| 251 | ** -c|--context N Show N lines of context around each change, |
| 252 | ** with negative N meaning show all content. Only |
| 253 | ** meaningful in combination with --tcl or --tk. |
| 254 | ** --dark Use dark mode for the Tcl/Tk-based GUI |
| 255 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 256 | ** information needed to display the changes to |
| 257 | ** FILE caused by the most recent merge. FILE must |
| 258 | ** be a pathname relative to the root of the check-out. |
| 259 | ** --tk Bring up a Tcl/Tk GUI that shows the changes |
| 260 | ** associated with the most recent merge. |
| 261 | ** |
| 262 | */ |
| 263 | void merge_info_cmd(void){ |
| 264 | const char *zCnt; |
| 265 | const char *zTcl; |
| 266 | int bTk; |
| 267 | int bDark; |
| 268 | int bAll; |
| 269 | int nContext; |
| 270 | Stmt q; |
| 271 | const char *zWhere; |
| 272 | int cnt = 0; |
| 273 | |
| 274 | db_must_be_within_tree(); |
| 275 | zTcl = find_option("tcl", 0, 1); |
| 276 | bTk = find_option("tk", 0, 0)!=0; |
| 277 | zCnt = find_option("context", "c", 1); |
| 278 | bDark = find_option("dark", 0, 0)!=0; |
| 279 | bAll = find_option("all", "a", 0)!=0; |
| 280 | if( bTk==0 ){ |
| 281 | verify_all_options(); |
| 282 | if( g.argc>2 ){ |
| 283 | usage("[OPTIONS]"); |
| 284 | } |
| 285 | } |
| 286 | if( zCnt ){ |
| 287 | nContext = atoi(zCnt); |
| 288 | if( nContext<0 ) nContext = 0xfffffff; |
| 289 | }else{ |
| 290 | nContext = 6; |
| 291 | } |
| 292 | if( !db_table_exists("localdb","mergestat") ){ |
| 293 | if( zTcl ){ |
| 294 | fossil_print("ERROR {no merge data available}\n"); |
| 295 | }else{ |
| 296 | fossil_print("No merge data is available\n"); |
| 297 | } |
| 298 | return; |
| 299 | } |
| 300 | if( bTk ){ |
| 301 | merge_info_tk(bDark, bAll, nContext); |
| 302 | return; |
| 303 | } |
| 304 | if( zTcl ){ |
| 305 | merge_info_tcl(zTcl, nContext); |
| 306 | return; |
| 307 | } |
| 308 | if( bAll ){ |
| 309 | zWhere = ""; |
| 310 | }else{ |
| 311 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 312 | } |
| 313 | db_prepare(&q, |
| 314 | "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0)," |
| 315 | "('MERGE',1),('ADDED',2),('UPDATE',2))" |
| 316 | |
| 317 | /* 0 1 2 */ |
| 318 | "SELECT op, coalesce(fnr,fn), msg" |
| 319 | " FROM mergestat JOIN priority USING(op)" |
| 320 | " %s" |
| 321 | " ORDER BY pri, coalesce(fnr,fn)", |
| 322 | zWhere /*safe-for-%s*/ |
| 323 | ); |
| 324 | while( db_step(&q)==SQLITE_ROW ){ |
| 325 | const char *zOp = db_column_text(&q, 0); |
| 326 | const char *zName = db_column_text(&q, 1); |
| 327 | const char *zErr = db_column_text(&q, 2); |
| 328 | if( zErr && fossil_strcmp(zOp,"CONFLICT")!=0 ){ |
| 329 | fossil_print("%-9s %s (%s)\n", zOp, zName, zErr); |
| 330 | }else{ |
| 331 | fossil_print("%-9s %s\n", zOp, zName); |
| 332 | } |
| 333 | cnt++; |
| 334 | } |
| 335 | db_finalize(&q); |
| 336 | if( !bAll && cnt==0 ){ |
| 337 | fossil_print( |
| 338 | "No interesting changes in this merge. Use --all to see everything.\n" |
| 339 | ); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | ** Erase all information about prior merges. Do this, for example, after |
| 345 | ** a commit. |
| 346 | */ |
| 347 | void merge_info_forget(void){ |
| 348 | db_multi_exec( |
| 349 | "DROP TABLE IF EXISTS localdb.mergestat;" |
| 350 | "DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';" |
| 351 | ); |
| 352 | } |
| 353 | |
| 354 | |
| 355 | /* |
| 356 | ** Initialize the MERGESTAT table. |
| 357 | ** |
| 358 | ** Notes about mergestat: |
| 359 | ** |
| 360 | ** * ridv is a positive integer and sz is NULL if the V file contained |
| 361 | ** no local edits prior to the merge. If the V file was modified prior |
| 362 | ** to the merge then ridv is NULL and sz is the size of the file prior |
| 363 | ** to merge. |
| 364 | ** |
| 365 | ** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was |
| 366 | ** added by merge. |
| 367 | */ |
| 368 | void merge_info_init(void){ |
| 369 | merge_info_forget(); |
| 370 | db_multi_exec( |
| 371 | "CREATE TABLE localdb.mergestat(\n" |
| 372 | " op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n" |
| 373 | " fnp TEXT, -- Name of the pivot file (P)\n" |
| 374 | " ridp INT, -- RID for the pivot file\n" |
| 375 | " fn TEXT, -- Name of origin file (V)\n" |
| 376 | " ridv INT, -- RID for origin file, or NULL if previously edited\n" |
| 377 | " sz INT, -- Size of origin file in bytes, NULL if unedited\n" |
| 378 | " fnm TEXT, -- Name of the file being merged in (M)\n" |
| 379 | " ridm INT, -- RID for the merge-in file\n" |
| 380 | " fnr TEXT, -- Name of the final output file, after all renaming\n" |
| 381 | " nc INT DEFAULT 0, -- Number of conflicts\n" |
| 382 | " msg TEXT -- Error message\n" |
| 383 | ");" |
| 384 | ); |
| 385 | } |
| 386 | |
| 387 | /* |
| 388 | ** Print information about a particular check-in. |
| 389 | */ |
| 390 | void print_checkin_description(int rid, int indent, const char *zLabel){ |
| 391 | Stmt q; |
| @@ -295,10 +657,13 @@ | |
| 657 | ** Files which are renamed in the merged-in branch will be renamed in |
| 658 | ** the current check-out. |
| 659 | ** |
| 660 | ** If the VERSION argument is omitted, then Fossil attempts to find |
| 661 | ** a recent fork on the current branch to merge. |
| 662 | ** |
| 663 | ** Note that this command does not commit the merge, as that is a |
| 664 | ** separate step. |
| 665 | ** |
| 666 | ** If there are multiple VERSION arguments, then each VERSION is merged |
| 667 | ** (or cherrypicked) in the order that they appear on the command-line. |
| 668 | ** |
| 669 | ** Options: |
| @@ -320,11 +685,11 @@ | |
| 685 | ** --force-missing Force the merge even if there is missing content |
| 686 | ** --integrate Merged branch will be closed when committing |
| 687 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 688 | ** used for merging, named *-baseline, *-original, |
| 689 | ** and *-merge. |
| 690 | ** -n|--dry-run Do not actually change files on disk |
| 691 | ** --nosync Do not auto-sync prior to merging |
| 692 | ** -v|--verbose Show additional details of the merge |
| 693 | */ |
| 694 | void merge_cmd(void){ |
| 695 | int vid; /* Current version "V" */ |
| @@ -797,15 +1162,21 @@ | |
| 1162 | |
| 1163 | /************************************************************************ |
| 1164 | ** All of the information needed to do the merge is now contained in the |
| 1165 | ** FV table. Starting here, we begin to actually carry out the merge. |
| 1166 | ** |
| 1167 | ** Begin by constructing the localdb.mergestat table. |
| 1168 | */ |
| 1169 | merge_info_init(); |
| 1170 | |
| 1171 | /* |
| 1172 | ** Find files that have changed from P->M but not P->V. |
| 1173 | ** Copy the M content over into V. |
| 1174 | */ |
| 1175 | db_prepare(&q, |
| 1176 | /* 0 1 2 3 4 5 6 7 */ |
| 1177 | "SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv" |
| 1178 | " WHERE idp>0 AND idv>0 AND idm>0" |
| 1179 | " AND ridm!=ridp AND ridv=ridp AND NOT chnged" |
| 1180 | ); |
| 1181 | while( db_step(&q)==SQLITE_ROW ){ |
| 1182 | int idv = db_column_int(&q, 0); |
| @@ -822,10 +1193,21 @@ | |
| 1193 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 1194 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| 1195 | ); |
| 1196 | vfile_to_disk(0, idv, 0, 0); |
| 1197 | } |
| 1198 | db_multi_exec( |
| 1199 | "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)" |
| 1200 | "VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)", |
| 1201 | /* fnp */ db_column_text(&q, 4), |
| 1202 | /* ridp */ db_column_int(&q,5), |
| 1203 | /* fn */ zName, |
| 1204 | /* ridv */ db_column_int(&q,6), |
| 1205 | /* fnm */ db_column_text(&q, 7), |
| 1206 | /* ridm */ ridm, |
| 1207 | /* fnr */ zName |
| 1208 | ); |
| 1209 | } |
| 1210 | db_finalize(&q); |
| 1211 | |
| 1212 | /* |
| 1213 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| @@ -833,11 +1215,15 @@ | |
| 1215 | ** Proceed even if the file doesn't exist on P, just like the common ancestor |
| 1216 | ** of M and V is an empty file. In this case, merge conflict marks will be |
| 1217 | ** added to the file and user will be forced to take a decision. |
| 1218 | */ |
| 1219 | db_prepare(&q, |
| 1220 | /* 0 1 2 3 4 5 6 7 8 */ |
| 1221 | "SELECT ridm, idv, ridp, ridv, %z, fn, isexe, islinkv, islinkm," |
| 1222 | /* 9 10 11 */ |
| 1223 | " fnp, fnm, chnged" |
| 1224 | " FROM fv" |
| 1225 | " WHERE idv>0 AND idm>0" |
| 1226 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 1227 | glob_expr("fv.fn", zBinGlob) |
| 1228 | ); |
| 1229 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -848,12 +1234,14 @@ | |
| 1234 | int isBinary = db_column_int(&q, 4); |
| 1235 | const char *zName = db_column_text(&q, 5); |
| 1236 | int isExe = db_column_int(&q, 6); |
| 1237 | int islinkv = db_column_int(&q, 7); |
| 1238 | int islinkm = db_column_int(&q, 8); |
| 1239 | int chnged = db_column_int(&q, 11); |
| 1240 | int rc; |
| 1241 | char *zFullPath; |
| 1242 | const char *zType = "MERGE"; |
| 1243 | Blob m, p, r; |
| 1244 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 1245 | if( verboseFlag ){ |
| 1246 | fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", |
| 1247 | zName, ridp, ridm, ridv); |
| @@ -861,13 +1249,29 @@ | |
| 1249 | fossil_print("MERGE %s\n", zName); |
| 1250 | } |
| 1251 | if( islinkv || islinkm ){ |
| 1252 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 1253 | nConflict++; |
| 1254 | db_multi_exec( |
| 1255 | "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)" |
| 1256 | "VALUES('ERROR',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')", |
| 1257 | /* fnp */ db_column_text(&q, 9), |
| 1258 | /* ridp */ ridp, |
| 1259 | /* fn */ zName, |
| 1260 | /* ridv */ ridv, |
| 1261 | /* fnm */ db_column_text(&q, 10), |
| 1262 | /* ridm */ ridm, |
| 1263 | /* fnr */ zName |
| 1264 | ); |
| 1265 | }else{ |
| 1266 | i64 sz; |
| 1267 | const char *zErrMsg = 0; |
| 1268 | int nc = 0; |
| 1269 | |
| 1270 | if( !dryRunFlag ) undo_save(zName); |
| 1271 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1272 | sz = file_size(zFullPath, ExtFILE); |
| 1273 | content_get(ridp, &p); |
| 1274 | content_get(ridm, &m); |
| 1275 | if( isBinary ){ |
| 1276 | rc = -1; |
| 1277 | blob_zero(&r); |
| @@ -884,15 +1288,38 @@ | |
| 1288 | db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv); |
| 1289 | if( rc>0 ){ |
| 1290 | fossil_print("***** %d merge conflict%s in %s\n", |
| 1291 | rc, rc>1 ? "s" : "", zName); |
| 1292 | nConflict++; |
| 1293 | nc = rc; |
| 1294 | zErrMsg = "merge conflicts"; |
| 1295 | zType = "CONFLICT"; |
| 1296 | } |
| 1297 | }else{ |
| 1298 | fossil_print("***** Cannot merge binary file %s\n", zName); |
| 1299 | nConflict++; |
| 1300 | nc = 1; |
| 1301 | zErrMsg = "cannot merge binary file"; |
| 1302 | zType = "ERROR"; |
| 1303 | } |
| 1304 | db_multi_exec( |
| 1305 | "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)" |
| 1306 | "VALUES(%Q,%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%lld,NULL),%Q,%d," |
| 1307 | "%Q,%d,%Q)", |
| 1308 | /* op */ zType, |
| 1309 | /* fnp */ db_column_text(&q, 9), |
| 1310 | /* ridp */ ridp, |
| 1311 | /* fn */ zName, |
| 1312 | /* ridv */ chnged==0, ridv, |
| 1313 | /* sz */ chnged!=0, sz, |
| 1314 | /* fnm */ db_column_text(&q, 10), |
| 1315 | /* ridm */ ridm, |
| 1316 | /* fnr */ zName, |
| 1317 | /* nc */ nc, |
| 1318 | /* msg */ zErrMsg |
| 1319 | ); |
| 1320 | fossil_free(zFullPath); |
| 1321 | blob_reset(&p); |
| 1322 | blob_reset(&m); |
| 1323 | blob_reset(&r); |
| 1324 | } |
| 1325 | vmerge_insert(idv, ridm); |
| @@ -901,22 +1328,33 @@ | |
| 1328 | |
| 1329 | /* |
| 1330 | ** Drop files that are in P and V but not in M |
| 1331 | */ |
| 1332 | db_prepare(&q, |
| 1333 | "SELECT idv, fn, chnged, ridv FROM fv" |
| 1334 | " WHERE idp>0 AND idv>0 AND idm=0" |
| 1335 | ); |
| 1336 | while( db_step(&q)==SQLITE_ROW ){ |
| 1337 | int idv = db_column_int(&q, 0); |
| 1338 | const char *zName = db_column_text(&q, 1); |
| 1339 | int chnged = db_column_int(&q, 2); |
| 1340 | int ridv = db_column_int(&q, 3); |
| 1341 | int sz = -1; |
| 1342 | const char *zErrMsg = 0; |
| 1343 | int nc = 0; |
| 1344 | /* Delete the file idv */ |
| 1345 | fossil_print("DELETE %s\n", zName); |
| 1346 | if( chnged ){ |
| 1347 | char *zFullPath; |
| 1348 | fossil_warning("WARNING: local edits lost for %s", zName); |
| 1349 | nConflict++; |
| 1350 | ridv = 0; |
| 1351 | nc = 1; |
| 1352 | zErrMsg = "local edits lost"; |
| 1353 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1354 | sz = file_size(zFullPath, ExtFILE); |
| 1355 | fossil_free(zFullPath); |
| 1356 | } |
| 1357 | if( !dryRunFlag ) undo_save(zName); |
| 1358 | db_multi_exec( |
| 1359 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 1360 | ); |
| @@ -923,10 +1361,20 @@ | |
| 1361 | if( !dryRunFlag ){ |
| 1362 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 1363 | file_delete(zFullPath); |
| 1364 | free(zFullPath); |
| 1365 | } |
| 1366 | db_multi_exec( |
| 1367 | "INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)" |
| 1368 | "VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL)," |
| 1369 | "NULL,NULL,%d,%Q)", |
| 1370 | /* fn */ zName, |
| 1371 | /* ridv */ chnged==0, ridv, |
| 1372 | /* sz */ chnged!=0, sz, |
| 1373 | /* nc */ nc, |
| 1374 | /* msg */ zErrMsg |
| 1375 | ); |
| 1376 | } |
| 1377 | db_finalize(&q); |
| 1378 | |
| 1379 | /* For certain sets of renames (e.g. A -> B and B -> A), a file that is |
| 1380 | ** being renamed must first be moved to a temporary location to avoid |
| @@ -953,10 +1401,14 @@ | |
| 1401 | const char *zNewName = db_column_text(&q, 2); |
| 1402 | int isExe = db_column_int(&q, 3); |
| 1403 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 1404 | if( !dryRunFlag ) undo_save(zOldName); |
| 1405 | if( !dryRunFlag ) undo_save(zNewName); |
| 1406 | db_multi_exec( |
| 1407 | "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q", |
| 1408 | zOldName |
| 1409 | ); |
| 1410 | db_multi_exec( |
| 1411 | "UPDATE vfile SET pathname=NULL, origname=pathname" |
| 1412 | " WHERE vid=%d AND pathname=%Q;" |
| 1413 | "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" |
| 1414 | " WHERE id=%d;", |
| @@ -1006,11 +1458,11 @@ | |
| 1458 | |
| 1459 | /* |
| 1460 | ** Insert into V any files that are not in V or P but are in M. |
| 1461 | */ |
| 1462 | db_prepare(&q, |
| 1463 | "SELECT idm, fnm, ridm FROM fv" |
| 1464 | " WHERE idp=0 AND idv=0 AND idm>0" |
| 1465 | ); |
| 1466 | while( db_step(&q)==SQLITE_ROW ){ |
| 1467 | int idm = db_column_int(&q, 0); |
| 1468 | const char *zName; |
| @@ -1039,10 +1491,17 @@ | |
| 1491 | nOverwrite++; |
| 1492 | }else{ |
| 1493 | fossil_print("ADDED %s\n", zName); |
| 1494 | } |
| 1495 | fossil_free(zFullName); |
| 1496 | db_multi_exec( |
| 1497 | "INSERT INTO mergestat(op,fnm,ridm,fnr)" |
| 1498 | "VALUES('ADDED',%Q,%d,%Q)", |
| 1499 | /* fnm */ zName, |
| 1500 | /* ridm */ db_column_int(&q,2), |
| 1501 | /* fnr */ zName |
| 1502 | ); |
| 1503 | if( !dryRunFlag ){ |
| 1504 | undo_save(zName); |
| 1505 | vfile_to_disk(0, idm, 0, 0); |
| 1506 | } |
| 1507 | } |
| 1508 | |
| 1509 | DDED src/merge.tcl |
+581
| --- a/src/merge.tcl | ||
| +++ b/src/merge.tcl | ||
| @@ -0,0 +1,581 @@ | ||
| 1 | +# Show details of a 3-way merge operation. The left-most column is the | |
| 2 | +# common ancestor. The next two columns are edits of that common ancestor. | |
| 3 | +# The right-most column is the result of the merge. | |
| 4 | +# | |
| 5 | +# There is always a "fossilcmd" variable which tells the script how to | |
| 6 | +# invoke Fossil to get the information it needs. This script will | |
| 7 | +# automatically append "-c N" to tell Fossil how much context it wants. True for debugging output | |
| 8 | +# | |
| 9 | +# If the "filelist" global variable is defined, then it is a list of | |
| 10 | +# alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and | |
| 11 | +# filenames. In that case, the initial display shows the changes for | |
| 12 | +# the first pair on the list and there is a optionmenu that allows the | |
| 13 | +# useelect otheere should also be a global variable named "ncontext" which is the | |
| 14 | +# number of lines of context to display. The value of this variable | |
| 15 | +# controls the "-c N" argument that is appended to fossilcmdht { | |
| 16 | + TITLE {Fossil Merge} | |
| 17 | + LN_COL_BG #dddddd | |
| 18 | + LN_COL_FG #444444 | |
| 19 | + TXT_COL_BG #ffffff | |
| 20 | + TXT_COL_FG #000000 | |
| 21 | + MKR_COL_BG #444444 | |
| 22 | + MKR_COL_FG #dddddd | |
| 23 | + CHNG_BG #d0d070 | |
| 24 | + ADD_BG #c0ffc0 | |
| 25 | + RM_BG #ffc0c0 | |
| 26 | + HR_FG #444444 | |
| 27 | + HR_PAD_TOP 4 | |
| 28 | + HR_PAD_BTM 8 | |
| 29 | + FN_BG #444444 | |
| 30 | + FN_FG #ffffff | |
| 31 | + FN_PAD 5 | |
| 32 | + ERR_FG #ee0000 | |
| 33 | + PADX 5 | |
| 34 | + WIDTH 80 | |
| 35 | + HEIGHT 45 | |
| 36 | + LB_HEIGHT 25 | |
| 37 | +} | |
| 38 | + | |
| 39 | +array set CFG_dark { | |
| 40 | + TITLE {Fossil Merge} | |
| 41 | + LN_COL_BG #dddddd | |
| 42 | + LN_COL_FG #444444 | |
| 43 | + TXT_COL_BG #3f3f3f | |
| 44 | + TXT_COL_FG #dcdccc | |
| 45 | + MKR_COL_BG #444444 | |
| 46 | + MKR_COL_FG #dddddd | |
| 47 | + CHNG_BG #6a6a00 | |
| 48 | + ADD_BG #57934c | |
| 49 | + RM_BG #ef6767 | |
| 50 | + HR_FG #444444 | |
| 51 | + HR_PAD_TOP 4 | |
| 52 | + HR_PAD_BTM 8 | |
| 53 | + FN_BG #5e5e5e | |
| 54 | + FN_FG #ffffff | |
| 55 | + FN_PAD 5 | |
| 56 | + ERR_FG #ee0000 | |
| 57 | + PADX 5 | |
| 58 | + WIDTH 80 | |
| 59 | + HEIGHT 45 | |
| 60 | + LB_HEIGHT 25 | |
| 61 | +} | |
| 62 | + | |
| 63 | +array set CFG_arr { | |
| 64 | + 0 CFG_light | |
| 65 | + 1 CFG_dark | |
| 66 | +} | |
| 67 | + | |
| 68 | +array set CFG [array get $CFG_arr($darkmode)] | |
| 69 | + | |
| 70 | +if {![namespace exists ttk]} { | |
| 71 | + interp alias {} ::ttk::scrollbar {} ::scrollbar | |
| 72 | + interp alias {} ::ttk::menubutton {} ::menubutton | |
| 73 | +} | |
| 74 | + | |
| 75 | +proc dehtml {x} { | |
| 76 | + set x [regsub -all {<[^>]*>} $x {}] | |
| 77 | + return [string map {& & < < > > ' ' " \"} $x] | |
| 78 | +} | |
| 79 | + | |
| 80 | +proc cols {} { | |
| 81 | + return [list .lnA .txtA .lnB .txtB .lnC .txtC .lnD .txtD] | |
| 82 | +} | |
| 83 | + | |
| 84 | +proc colType {c} { | |
| 85 | + regexp {[a-z]+} $c type | |
| 86 | + return $type | |
| 87 | +} | |
| 88 | + | |
| 89 | +proc readMercmdes currentails of a 3-way merge operation# Show det for | |
| 90 | +# the first pair on the list and there is a optionmenu that allows the | |
| 91 | +# user to select other fiels on the list. | |
| 92 | +# | |
| 93 | +# This header comment is stripped off by the "mkbuiltin.c" program. | |
| 94 | +# | |
| 95 | +package require Tk | |
| 96 | + | |
| 97 | +array set CFG_light { | |
| 98 | + TITLE {Fossil Merge} | |
| 99 | + LN_COL_BG #dddddd | |
| 100 | + LN_COL_FG #444444 | |
| 101 | + TXT_COL_BG #ffffff | |
| 102 | + TXT_COL_FG #000000 | |
| 103 | + MKR_COL_BG #444444 | |
| 104 | + MKR_COL_FG #dddddd | |
| 105 | + CHNG_BG #d0d070 | |
| 106 | + ADD_BG #c0ffc0 | |
| 107 | + RM_BG #ffc0c0 | |
| 108 | + HR_FG #444444 | |
| 109 | + HR_PAD_TOP 4 | |
| 110 | + HR_PAD_BTM 8 | |
| 111 | + FN_BG #444444 | |
| 112 | + FN_FG #ffffff | |
| 113 | + FN_PAD 5 | |
| 114 | + ERR_FG #ee0000 | |
| 115 | + PADX 5 | |
| 116 | + WIDTH 80 | |
| 117 | + HEIGHT 45 | |
| 118 | + LB_HEIGHT 25 | |
| 119 | +} | |
| 120 | + | |
| 121 | +array set CFG_dark { | |
| 122 | + TITLE {Fossil Merge} | |
| 123 | + LN_COL_BG #dddddd | |
| 124 | + LN_COL_FG #444444 | |
| 125 | + TXT_COL_BG #3f3f3f | |
| 126 | + TXT_COL_FG #dcdccc | |
| 127 | + MKR_COL_BG #444444 | |
| 128 | + MKR_COL_FG #dddddd | |
| 129 | + CHNG_BG #6a6a00 | |
| 130 | + ADD_BG #57934c | |
| 131 | + RM_BG #ef6767 | |
| 132 | + HR_FG #444444 | |
| 133 | + HR_PAD_TOP 4 | |
| 134 | + HR_PAD_BTM 8 | |
| 135 | + FN_BG #5e5e5e | |
| 136 | + FN_FG #ffffff | |
| 137 | + FN_PAD 5 | |
| 138 | + ERR_FG #ee0000 | |
| 139 | + PADX 5 | |
| 140 | + WIDTH 80 | |
| 141 | + HEIGHT 45 | |
| 142 | + LB_HEIGHT 25 | |
| 143 | +} | |
| 144 | + | |
| 145 | +array set CFG_arr { | |
| 146 | + 0 CFG_light | |
| 147 | + 1 CFG_dark | |
| 148 | +} | |
| 149 | + | |
| 150 | +array set CFG [array get $CFG_arr($darkmode)] | |
| 151 | + | |
| 152 | +if {![namespace exists ttk]} { | |
| 153 | + interp alias {} ::ttk::scrollbar {} ::scrollbar | |
| 154 | + interp alias {} ::ttk::menubutton {} ::menubutton | |
| 155 | +} | |
| 156 | + | |
| 157 | +proc dehtml {x} { | |
| 158 | + set x [regsub -all {<[^>]*>} $x {}] | |
| 159 | + return [string map {& & < < > > ' ' " \"} $x] | |
| 160 | +} | |
| 161 | + | |
| 162 | +proc cols {} { | |
| 163 | + return [list .lnA .txtA .lnB .txtB .lnC .txtC .lnD .txtD] | |
| 164 | +} | |
| 165 | + | |
| 166 | +proc colType {c} { | |
| 167 | + regexp {[a-z]+} $c type | |
| 168 | + return $type | |
| 169 | +} | |
| 170 | + | |
| 171 | +proc readMerge {args} { | |
| 172 | + global fossilexe ncontext current_file debug | |
| 173 | + if {$ncontext=="All"} { | |
| 174 | + set cmd "| $fossilexe merge-info -c -1" | |
| 175 | + } else { | |
| 176 | + set cmd "| $fossilexe merge-info -c $ncontext" | |
| 177 | + } | |
| 178 | + if {[info exists current_file]} { | |
| 179 | + regsub {^[A-Z]+ } $current_file {} fn | |
| 180 | + lappend cmd -tcl $fn | |
| 181 | + } | |
| 182 | + if {$debug} { | |
| 183 | + regsub {^\| +} $cmd {} cmd2 | |
| 184 | + puts $cmd2 | |
| 185 | + flush stdout | |
| 186 | + } | |
| 187 | + if {[catch { | |
| 188 | + set in [open $cmd r] | |
| 189 | + fconfigure $in -encoding utf-8 | |
| 190 | + set mergetxt [read $in] | |
| 191 | + close $in | |
| 192 | + } msg]} { | |
| 193 | + tk_messageBox -message "Unable to run command: \"$cmd\"" | |
| 194 | + set mergetxt {} | |
| 195 | + } | |
| 196 | + foreach c [cols] { | |
| 197 | + $c config -state normal | |
| 198 | + $c delete 1.0 end | |
| 199 | + } | |
| 200 | + set lnA 1 | |
| 201 | + set lnB 1 | |
| 202 | + set lnC 1 | |
| 203 | + set lnD 1 | |
| 204 | + foreach {A B C D} $mergetxt { | |
| 205 | + set key1 [string index $A 0] | |
| 206 | + if {$key1=="S"} { | |
| 207 | + scan [string range $A 1 end] "%d %d %d %d" nA nB nC nD | |
| 208 | + foreach x {A B C D} { | |
| 209 | + set N [set n$x] | |
| 210 | + incr ln$x $N | |
| 211 | + if {$N>0} { | |
| 212 | + .ln$x insert end ...\n hrln | |
| 213 | + .txt$x insert end [string repeat . 30]\n hrtxt | |
| 214 | + } else { | |
| 215 | + .ln$x insert end \n hrln | |
| 216 | + .txt$x insert end \n hrtxt | |
| 217 | + } | |
| 218 | + } | |
| 219 | + continue | |
| 220 | + } | |
| 221 | + set key2 [string index $B 0] | |
| 222 | + set key3 [string index $C 0] | |
| 223 | + set key4 [string index $D 0] | |
| 224 | + if {$key1=="."} { | |
| 225 | + .lnA insert end \n - | |
| 226 | + .txtA insert end \n - | |
| 227 | + } elseif {$key1=="N"} { | |
| 228 | + .nameA config -text [string range $A 1 end] | |
| 229 | + } else { | |
| 230 | + .lnA insert end $lnA\n - | |
| 231 | + incr lnA | |
| 232 | + if {$key1=="X"} { | |
| 233 | + .txtA insert end [string range $A 1 end]\n rm | |
| 234 | + } else { | |
| 235 | + .txtA insert end [string range $A 1 end]\n - | |
| 236 | + } | |
| 237 | + } | |
| 238 | + if {$key2=="."} { | |
| 239 | + .lnB insert end \n - | |
| 240 | + .txtB insert end \n - | |
| 241 | + } elseif {$key2=="N"} { | |
| 242 | + .nameB config -text [string range $B 1 end] | |
| 243 | + } else { | |
| 244 | + .lnB insert end $lnB\n - | |
| 245 | + incr lnB | |
| 246 | + if {$key4=="2"} {set tag chng} {set tag -} | |
| 247 | + if {$key2=="1"} { | |
| 248 | + .txtB insert end [string range $A 1 end]\n $tag | |
| 249 | + } elseif {$key2=="X"} { | |
| 250 | + .txtB insert end [string range $B 1 end]\n rm | |
| 251 | + } else { | |
| 252 | + .txtB insert end [string range $B 1 end]\n $tag | |
| 253 | + } | |
| 254 | + } | |
| 255 | + if {$key3=="."} { | |
| 256 | + .lnC insert end \n - | |
| 257 | + .txtC insert end \n - | |
| 258 | + } elseif {$key3=="N"} { | |
| 259 | + .nameC config -text [string range $C 1 end] | |
| 260 | + } else { | |
| 261 | + .lnC insert end $lnC\n - | |
| 262 | + incr lnC | |
| 263 | + if {$key4=="3"} {set tag add} {set tag -} | |
| 264 | + if {$key3=="1"} { | |
| 265 | + .txtC insert end [string range $A 1 end]\n $tag | |
| 266 | + } elseif {$key3=="2"} { | |
| 267 | + .txtC insert end [string range $B 1 end]\n chng | |
| 268 | + } elseif {$key3=="X"} { | |
| 269 | + .txtC insert end [string range $C 1 end]\n rm | |
| 270 | + } else { | |
| 271 | + .txtC insert end [string range $C 1 end]\n $tag | |
| 272 | + } | |
| 273 | + } | |
| 274 | + if {$key4=="."} { | |
| 275 | + .lnD insert end \n - | |
| 276 | + .txtD insert end \n - | |
| 277 | + } elseif {$key4=="N"} { | |
| 278 | + .nameD config -text [string range $D 1 end] | |
| 279 | + } else { | |
| 280 | + .lnD insert end $lnD\n - | |
| 281 | + incr lnD | |
| 282 | + if {$key4=="1"} { | |
| 283 | + .txtD insert end [string range $A 1 end]\n - | |
| 284 | + } elseif {$key4=="2"} { | |
| 285 | + .txtD insert end [string range $B 1 end]\n chng | |
| 286 | + } elseif {$key4=="3"} { | |
| 287 | + .txtD insert end [string range $C 1 end]\n add | |
| 288 | + } elseif {$key4=="X"} { | |
| 289 | + .txtD insert end [string range $D 1 end]\n rm | |
| 290 | + } else { | |
| 291 | + .txtD insert end [string range $D 1 end]\n - | |
| 292 | + } | |
| 293 | + } | |
| 294 | + } | |
| 295 | + foreach c [cols] { | |
| 296 | + set type [colType $c] | |
| 297 | + if {$type ne "txt"} { | |
| 298 | + $c config -width 6; # $widths($type) | |
| 299 | + } | |
| 300 | + $c config -state disabled | |
| 301 | + } | |
| 302 | + set mx $lnA | |
| 303 | + if {$lnB>$mx} {set mx $lnB} | |
| 304 | + if {$lnC>$mx} {set mx $lnC} | |
| 305 | + if {$lnD>$mx} {set mx $lnD} | |
| 306 | + global lnWidth | |
| 307 | + set lnWidth [string length [format +%d $mx]] | |
| 308 | + .lnA config -width $lnWidth | |
| 309 | + .lnB config -width $lnWidth | |
| 310 | + .lnC config -width $lnWidth | |
| 311 | + .lnD config -width $lnWidth | |
| 312 | + grid columnconfig . {0 2 4 6} -minsize $lnWidth | |
| 313 | +} | |
| 314 | + | |
| 315 | +proc viewDiff {idx} { | |
| 316 | + .txtA yview $idx | |
| 317 | + .txtA xview moveto 0 | |
| 318 | +} | |
| 319 | + | |
| 320 | +proc cycleDiffs {{reverse 0}} { | |
| 321 | + if {$reverse} { | |
| 322 | + set range [.txtA tag prevrange fn @0,0 1.0] | |
| 323 | + if {$range eq ""} { | |
| 324 | + viewDiff {fn.last -1c} | |
| 325 | + } else { | |
| 326 | + viewDiff [lindex $range 0] | |
| 327 | + } | |
| 328 | + } else { | |
| 329 | + set range [.txtA tag nextrange fn {@0,0 +1c} end] | |
| 330 | + if {$range eq "" || [lindex [.txtA yview] 1] == 1} { | |
| 331 | + viewDiff fn.first | |
| 332 | + } else { | |
| 333 | + viewDiff [lindex $range 0] | |
| 334 | + } | |
| 335 | + } | |
| 336 | +} | |
| 337 | + | |
| 338 | +proc xvis {col} { | |
| 339 | + set view [$col xview] | |
| 340 | + return [expr {[lindex $view 1]-[lindex $view 0]}] | |
| 341 | +} | |
| 342 | + | |
| 343 | +proc scroll-x {args} { | |
| 344 | + set c .txt[expr {[xvis .txtA] < [xvis .txtB] ? "A" : "B"}] | |
| 345 | + eval $c xview $args | |
| 346 | +} | |
| 347 | + | |
| 348 | +interp alias {} scroll-y {} .txtA yview | |
| 349 | + | |
| 350 | +proc noop {args} {} | |
| 351 | + | |
| 352 | +proc enableSync {axis} { | |
| 353 | + update idletasks | |
| 354 | + interp alias {} sync-$axis {} | |
| 355 | + rename _sync-$axis sync-$axis | |
| 356 | +} | |
| 357 | + | |
| 358 | +proc disableSync {axis} { | |
| 359 | + rename sync-$axis _sync-$axis | |
| 360 | + interp alias {} sync-$axis {} noop | |
| 361 | +} | |
| 362 | + | |
| 363 | +proc sync-y {first last} { | |
| 364 | + disableSync y | |
| 365 | + foreach c [cols] { | |
| 366 | + $c yview moveto $first | |
| 367 | + } | |
| 368 | + if {$first > 0 || $last < 1} { | |
| 369 | + grid .sby | |
| 370 | + .sby set $first $last | |
| 371 | + } else { | |
| 372 | + grid remove .sby | |
| 373 | + } | |
| 374 | + enableSync y | |
| 375 | +} | |
| 376 | + | |
| 377 | +wm withdraw . | |
| 378 | +wm title . $CFG(TITLE) | |
| 379 | +wm iconname . $CFG(TITLE) | |
| 380 | +# Keystroke bindings for on the top-level window for navigation and | |
| 381 | +# control also fire when those same keystrokes are pressed in the | |
| 382 | +# Search entry box. Disable them, to prevent the diff screen from | |
| 383 | +# disappearing abruptly and unexpectedly when searching for "q". | |
| 384 | +# | |
| 385 | +bind . <Control-q> exit | |
| 386 | +bind . <Control-p> {catch searchPrev; break} | |
| 387 | +bind . <Control-n> {catch searchNext; break} | |
| 388 | +bind . <Escape><Escape> exit | |
| 389 | +bind . <Destroy> {after 0 exit} | |
| 390 | +bind . <Tab> {cycleDiffs; break} | |
| 391 | +bind . <<PrevWindow>> {cycleDiffs 1; break} | |
| 392 | +bind . <Control-f> {searchOnOff; break} | |
| 393 | +bind . <Control-g> {catch searchNext; break} | |
| 394 | +bind . <Return> { | |
| 395 | + event generate .bb.files <1> | |
| 396 | + event generate .bb.files <ButtonRelease-1> | |
| 397 | + break | |
| 398 | +} | |
| 399 | +foreach {key axis args} { | |
| 400 | + Up y {scroll -5 units} | |
| 401 | + k y {scroll -5 units} | |
| 402 | + Down y {scroll 5 units} | |
| 403 | + j y {scroll 5 units} | |
| 404 | + Left x {scroll -5 units} | |
| 405 | + h x {scroll -5 units} | |
| 406 | + Right x {scroll 5 units} | |
| 407 | + l x {scroll 5 units} | |
| 408 | + Prior y {scroll -1 page} | |
| 409 | + b y {scroll -1 page} | |
| 410 | + Next y {scroll 1 page} | |
| 411 | + space y {scroll 1 page} | |
| 412 | + Home y {moveto 0} | |
| 413 | + g y {moveto 0} | |
| 414 | + End y {moveto 1} | |
| 415 | +} { | |
| 416 | + bind . <$key> "scroll-$axis $args; break" | |
| 417 | + bind . <Shift-$key> continue | |
| 418 | +} | |
| 419 | + | |
| 420 | +frame .bb | |
| 421 | +::ttk::menubutton .bb.diff2 -text {2-way diffs | |
| 422 | +.bb.diff2.m add command -label {baseline vs. local} -command {two-way 12} | |
| 423 | +.bb.diff2.m add command -label {baseline vs. merge-in} -command {two-way 13} | |
| 424 | +.bb.diff2.m add command -label {local vs. merge-in} -command {two-way 23} | |
| 425 | + | |
| 426 | +# Bring up a separate two-way diff between a pair of columns | |
| 427 | +# the argument is one of: | |
| 428 | +# 12 Baseline versus Local | |
| 429 | +# 13 Baseline versus Merge-in | |
| 430 | +# 23 Local versus Merge-in | |
| 431 | +# | |
| 432 | +proc two-way {mode} { | |
| 433 | + global current_file fossilexe debug darkmode ncontext | |
| 434 | + regsub {^[A-Z]+ } $current_file {} fn | |
| 435 | + set cmd $fossilexe | |
| 436 | + lappend cmd merge-info --diff$mode $fn -c $ncontext | |
| 437 | + if {$darkmode} { | |
| 438 | + lappend cmd --dark | |
| 439 | + } | |
| 440 | + if {$debug} { | |
| 441 | + lappend cc {*}$cmd & | |
| 442 | +} | |
| 443 | + | |
| 444 | +set useOptionMenu 1 | |
| 445 | +if {[info exists filelist]} { | |
| 446 | + set current_file "[lindex $filelist 0] [lindex $filelist 1]" | |
| 447 | + if {[llength $filelist]>2} { | |
| 448 | + trace add variable current_file write readMerge | |
| 449 | + | |
| 450 | + if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} { | |
| 451 | + set fnlist {} | |
| 452 | + foreach {op fn} $filelist {lappend fnlist "$op $fn"} | |
| 453 | + tk_optionMenu .bb.files current_file {*}$fnlist | |
| 454 | + } else { | |
| 455 | + set useOptionMenu 0 | |
| 456 | + ::ttk::menubutton .bb.files -text $current_file | |
| 457 | + if {[tk windowingsystem] eq "win32"} { | |
| 458 | + ::ttk::style theme use winnative | |
| 459 | + .bb.files configure -padding {20 1 10 2} | |
| 460 | + } | |
| 461 | + toplevel .wfiles | |
| 462 | + wm withdraw .wfiles | |
| 463 | + update idletasks | |
| 464 | + wm transient .wfiles . | |
| 465 | + wm overrideredirect .wfiles 1 | |
| 466 | + set ht [expr {[llength $filelist]/2}] | |
| 467 | + if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)} | |
| 468 | + listbox .wfiles.lb -width 0 -height $ht -activestyle none \ | |
| 469 | + -yscroll {.wfiles.sb set} | |
| 470 | + set mx 1 | |
| 471 | + foreach {op fn} $filelist { | |
| 472 | + set n [string length $fn] | |
| 473 | + if {$n>$mx} {set mx $n} | |
| 474 | + .wfiles.lb insert end "$op $fn" | |
| 475 | + } | |
| 476 | + .bb.files config -width $mx | |
| 477 | + ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview} | |
| 478 | + grid .wfiles.lb .wfiles.sb -sticky ns | |
| 479 | + bind .bb.files <1> { | |
| 480 | + set x [winfo rootx %W] | |
| 481 | + set y [expr {[winfo rooty %W]+[winfo height %W]}] | |
| 482 | + wm geometry .wfiles +$x+$y | |
| 483 | + wm deiconify .wfiles | |
| 484 | + focus .wfiles.lb | |
| 485 | + } | |
| 486 | + bind .wfiles <FocusOut> {wm withdraw .wfiles} | |
| 487 | + bind .wfiles <Escape> {focus .} | |
| 488 | + foreach evt {1 Return} { | |
| 489 | + bind .wfiles.lb <$evt> { | |
| 490 | + set ii [%W curselection] | |
| 491 | + set ::current_file [%W get $ii] | |
| 492 | + .bb.files config -text $::current_file | |
| 493 | + focus . | |
| 494 | + break | |
| 495 | + } | |
| 496 | + } | |
| 497 | + bind .wfiles.lb <Motion> { | |
| 498 | + %W selection clear 0 end | |
| 499 | + %W selection set @%x,%y | |
| 500 | + } | |
| 501 | + } | |
| 502 | + } | |
| 503 | +} | |
| 504 | + | |
| 505 | +label .bb.ctxtag -text "Context:" | |
| 506 | +set context_choices {3 6 12 25 50 100 All} | |
| 507 | +if {$ncontext<0} {set ncontext All} | |
| 508 | +trace add variable ncontext write readMerge | |
| 509 | +if {$tcl_platform(os)=="Darwin" || $useOptionMenu} { | |
| 510 | + tk_optionMenu .bb.ctx ncontext {*}$context_choices | |
| 511 | +} else { | |
| 512 | + ::ttk::menubutton .bb.ctx -text $ncontext | |
| 513 | + if {[tk windowingsystem] eq "win32"} { | |
| 514 | + ::ttk::style theme use winnative | |
| 515 | + .bb.ctx configure -padding {20 1 10 2} | |
| 516 | + } | |
| 517 | + toplevel .wctx | |
| 518 | + wm withdraw .wctx | |
| 519 | + update idletasks | |
| 520 | + wm transient .wctx . | |
| 521 | + wm overrideredirect .wctx 1 | |
| 522 | + listbox .wctx.lb -width 0 -height 7 -activestyle none | |
| 523 | + .wctx.lb insert end {*}$context_choices | |
| 524 | + pack .wctx.lb | |
| 525 | + bind .bb.ctx <1> { | |
| 526 | + set x [winfo rootx %W] | |
| 527 | + set y [expr {[winfo rooty %W]+[winfo height %W]}] | |
| 528 | + wm geometry .wctx +$x+$y | |
| 529 | + wm deiconify .wctx | |
| 530 | + focus .wctx.lb | |
| 531 | + } | |
| 532 | + bind .wctx <FocusOut> {wm withdraw .wctx} | |
| 533 | + bind .wctx <Escape> {focus .} | |
| 534 | + foreach evt {1 Return} { | |
| 535 | + bind .wctx.lb <$evt> { | |
| 536 | + set ::ncontext [lindex $::context_choices [%W curselection]] | |
| 537 | + .bb.ctx config -text $::ncontext | |
| 538 | + focus . | |
| 539 | + break | |
| 540 | + } | |
| 541 | + } | |
| 542 | + bind .wctx.lb <Motion> { | |
| 543 | + %W selection clear 0 end | |
| 544 | + %W selection set @%x,%y | |
| 545 | + } | |
| 546 | +} | |
| 547 | + | |
| 548 | +foreach {side syncCol} {A .txtA B .txtB C .txtC D .txtD} { | |
| 549 | + set ln .ln$side | |
| 550 | + text $ln -width 6 | |
| 551 | + $ln tag config - -justify right | |
| 552 | + | |
| 553 | + set txt .txt$side | |
| 554 | + text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \ | |
| 555 | + -xscroll ".sbx$side set" | |
| 556 | + catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5 | |
| 557 | + foreach tag {add rm chng} { | |
| 558 | + $txt tag config $tag -background $CFG([string toupper $tag]_BG) | |
| 559 | + $txt tag lower $tag | |
| 560 | + } | |
| 561 | + $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \ | |
| 562 | + -justify center | |
| 563 | + $txt tag config err -foreground $CFG(ERR_FG) | |
| 564 | +} | |
| 565 | +text .mkr | |
| 566 | + | |
| 567 | +set mxwidth [lindex [wm maxsize .] 0] | |
| 568 | +while {$CFG(WIDTH)>=40} { | |
| 569 | + set wanted [expr {([winfo reqwidth .lnA]+[winfo reqwidth .txtA])*4+30}] | |
| 570 | + if {$wanted<=$mxwidth} break | |
| 571 | + incr CFG(WIDTH) -10 | |
| 572 | + .txtA config -width $CFG(WIDTH) | |
| 573 | + .txtB config -width $CFG(WIDTH) | |
| 574 | + .txtC config -width $CFG(WIDTH) | |
| 575 | + .txtD config -width $CFG(WIDTH) | |
| 576 | +} | |
| 577 | + | |
| 578 | +foreach c [cols] { | |
| 579 | + set keyPrefix [string toupper [colType $c]]_COL_ | |
| 580 | + if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}} | |
| 581 | + $c co |
| --- a/src/merge.tcl | |
| +++ b/src/merge.tcl | |
| @@ -0,0 +1,581 @@ | |
| --- a/src/merge.tcl | |
| +++ b/src/merge.tcl | |
| @@ -0,0 +1,581 @@ | |
| 1 | # Show details of a 3-way merge operation. The left-most column is the |
| 2 | # common ancestor. The next two columns are edits of that common ancestor. |
| 3 | # The right-most column is the result of the merge. |
| 4 | # |
| 5 | # There is always a "fossilcmd" variable which tells the script how to |
| 6 | # invoke Fossil to get the information it needs. This script will |
| 7 | # automatically append "-c N" to tell Fossil how much context it wants. True for debugging output |
| 8 | # |
| 9 | # If the "filelist" global variable is defined, then it is a list of |
| 10 | # alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and |
| 11 | # filenames. In that case, the initial display shows the changes for |
| 12 | # the first pair on the list and there is a optionmenu that allows the |
| 13 | # useelect otheere should also be a global variable named "ncontext" which is the |
| 14 | # number of lines of context to display. The value of this variable |
| 15 | # controls the "-c N" argument that is appended to fossilcmdht { |
| 16 | TITLE {Fossil Merge} |
| 17 | LN_COL_BG #dddddd |
| 18 | LN_COL_FG #444444 |
| 19 | TXT_COL_BG #ffffff |
| 20 | TXT_COL_FG #000000 |
| 21 | MKR_COL_BG #444444 |
| 22 | MKR_COL_FG #dddddd |
| 23 | CHNG_BG #d0d070 |
| 24 | ADD_BG #c0ffc0 |
| 25 | RM_BG #ffc0c0 |
| 26 | HR_FG #444444 |
| 27 | HR_PAD_TOP 4 |
| 28 | HR_PAD_BTM 8 |
| 29 | FN_BG #444444 |
| 30 | FN_FG #ffffff |
| 31 | FN_PAD 5 |
| 32 | ERR_FG #ee0000 |
| 33 | PADX 5 |
| 34 | WIDTH 80 |
| 35 | HEIGHT 45 |
| 36 | LB_HEIGHT 25 |
| 37 | } |
| 38 | |
| 39 | array set CFG_dark { |
| 40 | TITLE {Fossil Merge} |
| 41 | LN_COL_BG #dddddd |
| 42 | LN_COL_FG #444444 |
| 43 | TXT_COL_BG #3f3f3f |
| 44 | TXT_COL_FG #dcdccc |
| 45 | MKR_COL_BG #444444 |
| 46 | MKR_COL_FG #dddddd |
| 47 | CHNG_BG #6a6a00 |
| 48 | ADD_BG #57934c |
| 49 | RM_BG #ef6767 |
| 50 | HR_FG #444444 |
| 51 | HR_PAD_TOP 4 |
| 52 | HR_PAD_BTM 8 |
| 53 | FN_BG #5e5e5e |
| 54 | FN_FG #ffffff |
| 55 | FN_PAD 5 |
| 56 | ERR_FG #ee0000 |
| 57 | PADX 5 |
| 58 | WIDTH 80 |
| 59 | HEIGHT 45 |
| 60 | LB_HEIGHT 25 |
| 61 | } |
| 62 | |
| 63 | array set CFG_arr { |
| 64 | 0 CFG_light |
| 65 | 1 CFG_dark |
| 66 | } |
| 67 | |
| 68 | array set CFG [array get $CFG_arr($darkmode)] |
| 69 | |
| 70 | if {![namespace exists ttk]} { |
| 71 | interp alias {} ::ttk::scrollbar {} ::scrollbar |
| 72 | interp alias {} ::ttk::menubutton {} ::menubutton |
| 73 | } |
| 74 | |
| 75 | proc dehtml {x} { |
| 76 | set x [regsub -all {<[^>]*>} $x {}] |
| 77 | return [string map {& & < < > > ' ' " \"} $x] |
| 78 | } |
| 79 | |
| 80 | proc cols {} { |
| 81 | return [list .lnA .txtA .lnB .txtB .lnC .txtC .lnD .txtD] |
| 82 | } |
| 83 | |
| 84 | proc colType {c} { |
| 85 | regexp {[a-z]+} $c type |
| 86 | return $type |
| 87 | } |
| 88 | |
| 89 | proc readMercmdes currentails of a 3-way merge operation# Show det for |
| 90 | # the first pair on the list and there is a optionmenu that allows the |
| 91 | # user to select other fiels on the list. |
| 92 | # |
| 93 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 94 | # |
| 95 | package require Tk |
| 96 | |
| 97 | array set CFG_light { |
| 98 | TITLE {Fossil Merge} |
| 99 | LN_COL_BG #dddddd |
| 100 | LN_COL_FG #444444 |
| 101 | TXT_COL_BG #ffffff |
| 102 | TXT_COL_FG #000000 |
| 103 | MKR_COL_BG #444444 |
| 104 | MKR_COL_FG #dddddd |
| 105 | CHNG_BG #d0d070 |
| 106 | ADD_BG #c0ffc0 |
| 107 | RM_BG #ffc0c0 |
| 108 | HR_FG #444444 |
| 109 | HR_PAD_TOP 4 |
| 110 | HR_PAD_BTM 8 |
| 111 | FN_BG #444444 |
| 112 | FN_FG #ffffff |
| 113 | FN_PAD 5 |
| 114 | ERR_FG #ee0000 |
| 115 | PADX 5 |
| 116 | WIDTH 80 |
| 117 | HEIGHT 45 |
| 118 | LB_HEIGHT 25 |
| 119 | } |
| 120 | |
| 121 | array set CFG_dark { |
| 122 | TITLE {Fossil Merge} |
| 123 | LN_COL_BG #dddddd |
| 124 | LN_COL_FG #444444 |
| 125 | TXT_COL_BG #3f3f3f |
| 126 | TXT_COL_FG #dcdccc |
| 127 | MKR_COL_BG #444444 |
| 128 | MKR_COL_FG #dddddd |
| 129 | CHNG_BG #6a6a00 |
| 130 | ADD_BG #57934c |
| 131 | RM_BG #ef6767 |
| 132 | HR_FG #444444 |
| 133 | HR_PAD_TOP 4 |
| 134 | HR_PAD_BTM 8 |
| 135 | FN_BG #5e5e5e |
| 136 | FN_FG #ffffff |
| 137 | FN_PAD 5 |
| 138 | ERR_FG #ee0000 |
| 139 | PADX 5 |
| 140 | WIDTH 80 |
| 141 | HEIGHT 45 |
| 142 | LB_HEIGHT 25 |
| 143 | } |
| 144 | |
| 145 | array set CFG_arr { |
| 146 | 0 CFG_light |
| 147 | 1 CFG_dark |
| 148 | } |
| 149 | |
| 150 | array set CFG [array get $CFG_arr($darkmode)] |
| 151 | |
| 152 | if {![namespace exists ttk]} { |
| 153 | interp alias {} ::ttk::scrollbar {} ::scrollbar |
| 154 | interp alias {} ::ttk::menubutton {} ::menubutton |
| 155 | } |
| 156 | |
| 157 | proc dehtml {x} { |
| 158 | set x [regsub -all {<[^>]*>} $x {}] |
| 159 | return [string map {& & < < > > ' ' " \"} $x] |
| 160 | } |
| 161 | |
| 162 | proc cols {} { |
| 163 | return [list .lnA .txtA .lnB .txtB .lnC .txtC .lnD .txtD] |
| 164 | } |
| 165 | |
| 166 | proc colType {c} { |
| 167 | regexp {[a-z]+} $c type |
| 168 | return $type |
| 169 | } |
| 170 | |
| 171 | proc readMerge {args} { |
| 172 | global fossilexe ncontext current_file debug |
| 173 | if {$ncontext=="All"} { |
| 174 | set cmd "| $fossilexe merge-info -c -1" |
| 175 | } else { |
| 176 | set cmd "| $fossilexe merge-info -c $ncontext" |
| 177 | } |
| 178 | if {[info exists current_file]} { |
| 179 | regsub {^[A-Z]+ } $current_file {} fn |
| 180 | lappend cmd -tcl $fn |
| 181 | } |
| 182 | if {$debug} { |
| 183 | regsub {^\| +} $cmd {} cmd2 |
| 184 | puts $cmd2 |
| 185 | flush stdout |
| 186 | } |
| 187 | if {[catch { |
| 188 | set in [open $cmd r] |
| 189 | fconfigure $in -encoding utf-8 |
| 190 | set mergetxt [read $in] |
| 191 | close $in |
| 192 | } msg]} { |
| 193 | tk_messageBox -message "Unable to run command: \"$cmd\"" |
| 194 | set mergetxt {} |
| 195 | } |
| 196 | foreach c [cols] { |
| 197 | $c config -state normal |
| 198 | $c delete 1.0 end |
| 199 | } |
| 200 | set lnA 1 |
| 201 | set lnB 1 |
| 202 | set lnC 1 |
| 203 | set lnD 1 |
| 204 | foreach {A B C D} $mergetxt { |
| 205 | set key1 [string index $A 0] |
| 206 | if {$key1=="S"} { |
| 207 | scan [string range $A 1 end] "%d %d %d %d" nA nB nC nD |
| 208 | foreach x {A B C D} { |
| 209 | set N [set n$x] |
| 210 | incr ln$x $N |
| 211 | if {$N>0} { |
| 212 | .ln$x insert end ...\n hrln |
| 213 | .txt$x insert end [string repeat . 30]\n hrtxt |
| 214 | } else { |
| 215 | .ln$x insert end \n hrln |
| 216 | .txt$x insert end \n hrtxt |
| 217 | } |
| 218 | } |
| 219 | continue |
| 220 | } |
| 221 | set key2 [string index $B 0] |
| 222 | set key3 [string index $C 0] |
| 223 | set key4 [string index $D 0] |
| 224 | if {$key1=="."} { |
| 225 | .lnA insert end \n - |
| 226 | .txtA insert end \n - |
| 227 | } elseif {$key1=="N"} { |
| 228 | .nameA config -text [string range $A 1 end] |
| 229 | } else { |
| 230 | .lnA insert end $lnA\n - |
| 231 | incr lnA |
| 232 | if {$key1=="X"} { |
| 233 | .txtA insert end [string range $A 1 end]\n rm |
| 234 | } else { |
| 235 | .txtA insert end [string range $A 1 end]\n - |
| 236 | } |
| 237 | } |
| 238 | if {$key2=="."} { |
| 239 | .lnB insert end \n - |
| 240 | .txtB insert end \n - |
| 241 | } elseif {$key2=="N"} { |
| 242 | .nameB config -text [string range $B 1 end] |
| 243 | } else { |
| 244 | .lnB insert end $lnB\n - |
| 245 | incr lnB |
| 246 | if {$key4=="2"} {set tag chng} {set tag -} |
| 247 | if {$key2=="1"} { |
| 248 | .txtB insert end [string range $A 1 end]\n $tag |
| 249 | } elseif {$key2=="X"} { |
| 250 | .txtB insert end [string range $B 1 end]\n rm |
| 251 | } else { |
| 252 | .txtB insert end [string range $B 1 end]\n $tag |
| 253 | } |
| 254 | } |
| 255 | if {$key3=="."} { |
| 256 | .lnC insert end \n - |
| 257 | .txtC insert end \n - |
| 258 | } elseif {$key3=="N"} { |
| 259 | .nameC config -text [string range $C 1 end] |
| 260 | } else { |
| 261 | .lnC insert end $lnC\n - |
| 262 | incr lnC |
| 263 | if {$key4=="3"} {set tag add} {set tag -} |
| 264 | if {$key3=="1"} { |
| 265 | .txtC insert end [string range $A 1 end]\n $tag |
| 266 | } elseif {$key3=="2"} { |
| 267 | .txtC insert end [string range $B 1 end]\n chng |
| 268 | } elseif {$key3=="X"} { |
| 269 | .txtC insert end [string range $C 1 end]\n rm |
| 270 | } else { |
| 271 | .txtC insert end [string range $C 1 end]\n $tag |
| 272 | } |
| 273 | } |
| 274 | if {$key4=="."} { |
| 275 | .lnD insert end \n - |
| 276 | .txtD insert end \n - |
| 277 | } elseif {$key4=="N"} { |
| 278 | .nameD config -text [string range $D 1 end] |
| 279 | } else { |
| 280 | .lnD insert end $lnD\n - |
| 281 | incr lnD |
| 282 | if {$key4=="1"} { |
| 283 | .txtD insert end [string range $A 1 end]\n - |
| 284 | } elseif {$key4=="2"} { |
| 285 | .txtD insert end [string range $B 1 end]\n chng |
| 286 | } elseif {$key4=="3"} { |
| 287 | .txtD insert end [string range $C 1 end]\n add |
| 288 | } elseif {$key4=="X"} { |
| 289 | .txtD insert end [string range $D 1 end]\n rm |
| 290 | } else { |
| 291 | .txtD insert end [string range $D 1 end]\n - |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | foreach c [cols] { |
| 296 | set type [colType $c] |
| 297 | if {$type ne "txt"} { |
| 298 | $c config -width 6; # $widths($type) |
| 299 | } |
| 300 | $c config -state disabled |
| 301 | } |
| 302 | set mx $lnA |
| 303 | if {$lnB>$mx} {set mx $lnB} |
| 304 | if {$lnC>$mx} {set mx $lnC} |
| 305 | if {$lnD>$mx} {set mx $lnD} |
| 306 | global lnWidth |
| 307 | set lnWidth [string length [format +%d $mx]] |
| 308 | .lnA config -width $lnWidth |
| 309 | .lnB config -width $lnWidth |
| 310 | .lnC config -width $lnWidth |
| 311 | .lnD config -width $lnWidth |
| 312 | grid columnconfig . {0 2 4 6} -minsize $lnWidth |
| 313 | } |
| 314 | |
| 315 | proc viewDiff {idx} { |
| 316 | .txtA yview $idx |
| 317 | .txtA xview moveto 0 |
| 318 | } |
| 319 | |
| 320 | proc cycleDiffs {{reverse 0}} { |
| 321 | if {$reverse} { |
| 322 | set range [.txtA tag prevrange fn @0,0 1.0] |
| 323 | if {$range eq ""} { |
| 324 | viewDiff {fn.last -1c} |
| 325 | } else { |
| 326 | viewDiff [lindex $range 0] |
| 327 | } |
| 328 | } else { |
| 329 | set range [.txtA tag nextrange fn {@0,0 +1c} end] |
| 330 | if {$range eq "" || [lindex [.txtA yview] 1] == 1} { |
| 331 | viewDiff fn.first |
| 332 | } else { |
| 333 | viewDiff [lindex $range 0] |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | proc xvis {col} { |
| 339 | set view [$col xview] |
| 340 | return [expr {[lindex $view 1]-[lindex $view 0]}] |
| 341 | } |
| 342 | |
| 343 | proc scroll-x {args} { |
| 344 | set c .txt[expr {[xvis .txtA] < [xvis .txtB] ? "A" : "B"}] |
| 345 | eval $c xview $args |
| 346 | } |
| 347 | |
| 348 | interp alias {} scroll-y {} .txtA yview |
| 349 | |
| 350 | proc noop {args} {} |
| 351 | |
| 352 | proc enableSync {axis} { |
| 353 | update idletasks |
| 354 | interp alias {} sync-$axis {} |
| 355 | rename _sync-$axis sync-$axis |
| 356 | } |
| 357 | |
| 358 | proc disableSync {axis} { |
| 359 | rename sync-$axis _sync-$axis |
| 360 | interp alias {} sync-$axis {} noop |
| 361 | } |
| 362 | |
| 363 | proc sync-y {first last} { |
| 364 | disableSync y |
| 365 | foreach c [cols] { |
| 366 | $c yview moveto $first |
| 367 | } |
| 368 | if {$first > 0 || $last < 1} { |
| 369 | grid .sby |
| 370 | .sby set $first $last |
| 371 | } else { |
| 372 | grid remove .sby |
| 373 | } |
| 374 | enableSync y |
| 375 | } |
| 376 | |
| 377 | wm withdraw . |
| 378 | wm title . $CFG(TITLE) |
| 379 | wm iconname . $CFG(TITLE) |
| 380 | # Keystroke bindings for on the top-level window for navigation and |
| 381 | # control also fire when those same keystrokes are pressed in the |
| 382 | # Search entry box. Disable them, to prevent the diff screen from |
| 383 | # disappearing abruptly and unexpectedly when searching for "q". |
| 384 | # |
| 385 | bind . <Control-q> exit |
| 386 | bind . <Control-p> {catch searchPrev; break} |
| 387 | bind . <Control-n> {catch searchNext; break} |
| 388 | bind . <Escape><Escape> exit |
| 389 | bind . <Destroy> {after 0 exit} |
| 390 | bind . <Tab> {cycleDiffs; break} |
| 391 | bind . <<PrevWindow>> {cycleDiffs 1; break} |
| 392 | bind . <Control-f> {searchOnOff; break} |
| 393 | bind . <Control-g> {catch searchNext; break} |
| 394 | bind . <Return> { |
| 395 | event generate .bb.files <1> |
| 396 | event generate .bb.files <ButtonRelease-1> |
| 397 | break |
| 398 | } |
| 399 | foreach {key axis args} { |
| 400 | Up y {scroll -5 units} |
| 401 | k y {scroll -5 units} |
| 402 | Down y {scroll 5 units} |
| 403 | j y {scroll 5 units} |
| 404 | Left x {scroll -5 units} |
| 405 | h x {scroll -5 units} |
| 406 | Right x {scroll 5 units} |
| 407 | l x {scroll 5 units} |
| 408 | Prior y {scroll -1 page} |
| 409 | b y {scroll -1 page} |
| 410 | Next y {scroll 1 page} |
| 411 | space y {scroll 1 page} |
| 412 | Home y {moveto 0} |
| 413 | g y {moveto 0} |
| 414 | End y {moveto 1} |
| 415 | } { |
| 416 | bind . <$key> "scroll-$axis $args; break" |
| 417 | bind . <Shift-$key> continue |
| 418 | } |
| 419 | |
| 420 | frame .bb |
| 421 | ::ttk::menubutton .bb.diff2 -text {2-way diffs |
| 422 | .bb.diff2.m add command -label {baseline vs. local} -command {two-way 12} |
| 423 | .bb.diff2.m add command -label {baseline vs. merge-in} -command {two-way 13} |
| 424 | .bb.diff2.m add command -label {local vs. merge-in} -command {two-way 23} |
| 425 | |
| 426 | # Bring up a separate two-way diff between a pair of columns |
| 427 | # the argument is one of: |
| 428 | # 12 Baseline versus Local |
| 429 | # 13 Baseline versus Merge-in |
| 430 | # 23 Local versus Merge-in |
| 431 | # |
| 432 | proc two-way {mode} { |
| 433 | global current_file fossilexe debug darkmode ncontext |
| 434 | regsub {^[A-Z]+ } $current_file {} fn |
| 435 | set cmd $fossilexe |
| 436 | lappend cmd merge-info --diff$mode $fn -c $ncontext |
| 437 | if {$darkmode} { |
| 438 | lappend cmd --dark |
| 439 | } |
| 440 | if {$debug} { |
| 441 | lappend cc {*}$cmd & |
| 442 | } |
| 443 | |
| 444 | set useOptionMenu 1 |
| 445 | if {[info exists filelist]} { |
| 446 | set current_file "[lindex $filelist 0] [lindex $filelist 1]" |
| 447 | if {[llength $filelist]>2} { |
| 448 | trace add variable current_file write readMerge |
| 449 | |
| 450 | if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} { |
| 451 | set fnlist {} |
| 452 | foreach {op fn} $filelist {lappend fnlist "$op $fn"} |
| 453 | tk_optionMenu .bb.files current_file {*}$fnlist |
| 454 | } else { |
| 455 | set useOptionMenu 0 |
| 456 | ::ttk::menubutton .bb.files -text $current_file |
| 457 | if {[tk windowingsystem] eq "win32"} { |
| 458 | ::ttk::style theme use winnative |
| 459 | .bb.files configure -padding {20 1 10 2} |
| 460 | } |
| 461 | toplevel .wfiles |
| 462 | wm withdraw .wfiles |
| 463 | update idletasks |
| 464 | wm transient .wfiles . |
| 465 | wm overrideredirect .wfiles 1 |
| 466 | set ht [expr {[llength $filelist]/2}] |
| 467 | if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)} |
| 468 | listbox .wfiles.lb -width 0 -height $ht -activestyle none \ |
| 469 | -yscroll {.wfiles.sb set} |
| 470 | set mx 1 |
| 471 | foreach {op fn} $filelist { |
| 472 | set n [string length $fn] |
| 473 | if {$n>$mx} {set mx $n} |
| 474 | .wfiles.lb insert end "$op $fn" |
| 475 | } |
| 476 | .bb.files config -width $mx |
| 477 | ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview} |
| 478 | grid .wfiles.lb .wfiles.sb -sticky ns |
| 479 | bind .bb.files <1> { |
| 480 | set x [winfo rootx %W] |
| 481 | set y [expr {[winfo rooty %W]+[winfo height %W]}] |
| 482 | wm geometry .wfiles +$x+$y |
| 483 | wm deiconify .wfiles |
| 484 | focus .wfiles.lb |
| 485 | } |
| 486 | bind .wfiles <FocusOut> {wm withdraw .wfiles} |
| 487 | bind .wfiles <Escape> {focus .} |
| 488 | foreach evt {1 Return} { |
| 489 | bind .wfiles.lb <$evt> { |
| 490 | set ii [%W curselection] |
| 491 | set ::current_file [%W get $ii] |
| 492 | .bb.files config -text $::current_file |
| 493 | focus . |
| 494 | break |
| 495 | } |
| 496 | } |
| 497 | bind .wfiles.lb <Motion> { |
| 498 | %W selection clear 0 end |
| 499 | %W selection set @%x,%y |
| 500 | } |
| 501 | } |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | label .bb.ctxtag -text "Context:" |
| 506 | set context_choices {3 6 12 25 50 100 All} |
| 507 | if {$ncontext<0} {set ncontext All} |
| 508 | trace add variable ncontext write readMerge |
| 509 | if {$tcl_platform(os)=="Darwin" || $useOptionMenu} { |
| 510 | tk_optionMenu .bb.ctx ncontext {*}$context_choices |
| 511 | } else { |
| 512 | ::ttk::menubutton .bb.ctx -text $ncontext |
| 513 | if {[tk windowingsystem] eq "win32"} { |
| 514 | ::ttk::style theme use winnative |
| 515 | .bb.ctx configure -padding {20 1 10 2} |
| 516 | } |
| 517 | toplevel .wctx |
| 518 | wm withdraw .wctx |
| 519 | update idletasks |
| 520 | wm transient .wctx . |
| 521 | wm overrideredirect .wctx 1 |
| 522 | listbox .wctx.lb -width 0 -height 7 -activestyle none |
| 523 | .wctx.lb insert end {*}$context_choices |
| 524 | pack .wctx.lb |
| 525 | bind .bb.ctx <1> { |
| 526 | set x [winfo rootx %W] |
| 527 | set y [expr {[winfo rooty %W]+[winfo height %W]}] |
| 528 | wm geometry .wctx +$x+$y |
| 529 | wm deiconify .wctx |
| 530 | focus .wctx.lb |
| 531 | } |
| 532 | bind .wctx <FocusOut> {wm withdraw .wctx} |
| 533 | bind .wctx <Escape> {focus .} |
| 534 | foreach evt {1 Return} { |
| 535 | bind .wctx.lb <$evt> { |
| 536 | set ::ncontext [lindex $::context_choices [%W curselection]] |
| 537 | .bb.ctx config -text $::ncontext |
| 538 | focus . |
| 539 | break |
| 540 | } |
| 541 | } |
| 542 | bind .wctx.lb <Motion> { |
| 543 | %W selection clear 0 end |
| 544 | %W selection set @%x,%y |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | foreach {side syncCol} {A .txtA B .txtB C .txtC D .txtD} { |
| 549 | set ln .ln$side |
| 550 | text $ln -width 6 |
| 551 | $ln tag config - -justify right |
| 552 | |
| 553 | set txt .txt$side |
| 554 | text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \ |
| 555 | -xscroll ".sbx$side set" |
| 556 | catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5 |
| 557 | foreach tag {add rm chng} { |
| 558 | $txt tag config $tag -background $CFG([string toupper $tag]_BG) |
| 559 | $txt tag lower $tag |
| 560 | } |
| 561 | $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \ |
| 562 | -justify center |
| 563 | $txt tag config err -foreground $CFG(ERR_FG) |
| 564 | } |
| 565 | text .mkr |
| 566 | |
| 567 | set mxwidth [lindex [wm maxsize .] 0] |
| 568 | while {$CFG(WIDTH)>=40} { |
| 569 | set wanted [expr {([winfo reqwidth .lnA]+[winfo reqwidth .txtA])*4+30}] |
| 570 | if {$wanted<=$mxwidth} break |
| 571 | incr CFG(WIDTH) -10 |
| 572 | .txtA config -width $CFG(WIDTH) |
| 573 | .txtB config -width $CFG(WIDTH) |
| 574 | .txtC config -width $CFG(WIDTH) |
| 575 | .txtD config -width $CFG(WIDTH) |
| 576 | } |
| 577 | |
| 578 | foreach c [cols] { |
| 579 | set keyPrefix [string toupper [colType $c]]_COL_ |
| 580 | if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}} |
| 581 | $c co |
+804
-168
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -75,75 +75,17 @@ | ||
| 75 | 75 | if( aC1[2]!=aC2[2] ) return 0; |
| 76 | 76 | if( sameLines(pV1, pV2, aC1[2]) ) return 1; |
| 77 | 77 | return 0; |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | -/* | |
| 81 | -** The aC[] array contains triples of integers. Within each triple, the | |
| 82 | -** elements are: | |
| 83 | -** | |
| 84 | -** (0) The number of lines to copy | |
| 85 | -** (1) The number of lines to delete | |
| 86 | -** (2) The number of liens to insert | |
| 87 | -** | |
| 88 | -** Suppose we want to advance over sz lines of the original file. This routine | |
| 89 | -** returns true if that advance would land us on a copy operation. It | |
| 90 | -** returns false if the advance would end on a delete. | |
| 91 | -*/ | |
| 92 | -static int ends_at_CPY(int *aC, int sz){ | |
| 93 | - while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ | |
| 94 | - if( aC[0]>=sz ) return 1; | |
| 95 | - sz -= aC[0]; | |
| 96 | - if( aC[1]>sz ) return 0; | |
| 97 | - sz -= aC[1]; | |
| 98 | - aC += 3; | |
| 99 | - } | |
| 100 | - return 1; | |
| 101 | -} | |
| 102 | - | |
| 103 | -/* | |
| 104 | -** pSrc contains an edited file where aC[] describes the edit. Part of | |
| 105 | -** pSrc has already been output. This routine outputs additional lines | |
| 106 | -** of pSrc - lines that correspond to the next sz lines of the original | |
| 107 | -** unedited file. | |
| 108 | -** | |
| 109 | -** Note that sz counts the number of lines of text in the original file. | |
| 110 | -** But text is output from the edited file. So the number of lines transfer | |
| 111 | -** to pOut might be different from sz. Fewer lines appear in pOut if there | |
| 112 | -** are deletes. More lines appear if there are inserts. | |
| 113 | -** | |
| 114 | -** The aC[] array is updated and the new index into aC[] is returned. | |
| 115 | -*/ | |
| 116 | -static int output_one_side( | |
| 117 | - Blob *pOut, /* Write to this blob */ | |
| 118 | - Blob *pSrc, /* The edited file that is to be copied to pOut */ | |
| 119 | - int *aC, /* Array of integer triples describing the edit */ | |
| 120 | - int i, /* Index in aC[] of current location in pSrc */ | |
| 121 | - int sz, /* Number of lines in unedited source to output */ | |
| 122 | - int *pLn /* Line number counter */ | |
| 123 | -){ | |
| 124 | - while( sz>0 ){ | |
| 125 | - if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; | |
| 126 | - if( aC[i]>=sz ){ | |
| 127 | - blob_copy_lines(pOut, pSrc, sz); *pLn += sz; | |
| 128 | - aC[i] -= sz; | |
| 129 | - break; | |
| 130 | - } | |
| 131 | - blob_copy_lines(pOut, pSrc, aC[i]); *pLn += aC[i]; | |
| 132 | - blob_copy_lines(pOut, pSrc, aC[i+2]); *pLn += aC[i+2]; | |
| 133 | - sz -= aC[i] + aC[i+1]; | |
| 134 | - i += 3; | |
| 135 | - } | |
| 136 | - return i; | |
| 137 | -} | |
| 138 | - | |
| 139 | 80 | /* |
| 140 | 81 | ** Text of boundary markers for merge conflicts. |
| 141 | 82 | */ |
| 142 | 83 | static const char *const mergeMarker[] = { |
| 143 | 84 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 144 | 85 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<", |
| 86 | + "####### SUGGESTED CONFLICT RESOLUTION follows ###################", | |
| 145 | 87 | "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||", |
| 146 | 88 | "======= MERGED IN content follows ===============================", |
| 147 | 89 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" |
| 148 | 90 | }; |
| 149 | 91 | |
| @@ -186,10 +128,615 @@ | ||
| 186 | 128 | ensure_line_end(pOut, useCrLf); |
| 187 | 129 | blob_append(pOut, mergeMarker[iMark], -1); |
| 188 | 130 | if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); |
| 189 | 131 | ensure_line_end(pOut, useCrLf); |
| 190 | 132 | } |
| 133 | + | |
| 134 | +#if INTERFACE | |
| 135 | +/* | |
| 136 | +** This is an abstract class for constructing a merge. | |
| 137 | +** Subclasses of this object format the merge output in different ways. | |
| 138 | +** | |
| 139 | +** To subclass, create an instance of the MergeBuilder object and fill | |
| 140 | +** in appropriate method implementations. | |
| 141 | +*/ | |
| 142 | +struct MergeBuilder { | |
| 143 | + void (*xStart)(MergeBuilder*); | |
| 144 | + void (*xSame)(MergeBuilder*, unsigned int); | |
| 145 | + void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int); | |
| 146 | + void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int); | |
| 147 | + void (*xChngBoth)(MergeBuilder*, unsigned int, unsigned int); | |
| 148 | + void (*xConflict)(MergeBuilder*, unsigned int, unsigned int, unsigned int); | |
| 149 | + void (*xEnd)(MergeBuilder*); | |
| 150 | + void (*xDestroy)(MergeBuilder*); | |
| 151 | + const char *zPivot; /* Label or name for the pivot */ | |
| 152 | + const char *zV1; /* Label or name for the V1 file */ | |
| 153 | + const char *zV2; /* Label or name for the V2 file */ | |
| 154 | + const char *zOut; /* Label or name for the output */ | |
| 155 | + Blob *pPivot; /* The common ancestor */ | |
| 156 | + Blob *pV1; /* First variant (local copy) */ | |
| 157 | + Blob *pV2; /* Second variant (merged in) */ | |
| 158 | + Blob *pOut; /* Write merge results here */ | |
| 159 | + int useCrLf; /* Use CRLF line endings */ | |
| 160 | + int nContext; /* Size of unchanged line boundaries */ | |
| 161 | + unsigned int mxPivot; /* Number of lines in the pivot */ | |
| 162 | + unsigned int mxV1; /* Number of lines in V1 */ | |
| 163 | + unsigned int mxV2; /* Number of lines in V2 */ | |
| 164 | + unsigned int lnPivot; /* Lines read from pivot */ | |
| 165 | + unsigned int lnV1; /* Lines read from v1 */ | |
| 166 | + unsigned int lnV2; /* Lines read from v2 */ | |
| 167 | + unsigned int lnOut; /* Lines written to out */ | |
| 168 | + unsigned int nConflict; /* Number of conflicts seen */ | |
| 169 | + u64 diffFlags; /* Flags for difference engine */ | |
| 170 | +}; | |
| 171 | +#endif /* INTERFACE */ | |
| 172 | + | |
| 173 | + | |
| 174 | +/************************* Generic MergeBuilder ******************************/ | |
| 175 | +/* These are generic methods for MergeBuilder. They just output debugging | |
| 176 | +** information. But some of them are useful as base methods for other useful | |
| 177 | +** implementations of MergeBuilder. | |
| 178 | +*/ | |
| 179 | + | |
| 180 | +/* xStart() and xEnd() are called to generate header and fotter information | |
| 181 | +** in the output. This is a no-op in the generic implementation. | |
| 182 | +*/ | |
| 183 | +static void dbgStartEnd(MergeBuilder *p){ (void)p; } | |
| 184 | + | |
| 185 | +/* The next N lines of PIVOT are unchanged in both V1 and V2 | |
| 186 | +*/ | |
| 187 | +static void dbgSame(MergeBuilder *p, unsigned int N){ | |
| 188 | + blob_appendf(p->pOut, | |
| 189 | + "COPY %u from BASELINE(%u..%u) or V1(%u..%u) or V2(%u..%u)\n", | |
| 190 | + N, p->lnPivot+1, p->lnPivot+N, p->lnV1+1, p->lnV1+N, | |
| 191 | + p->lnV2+1, p->lnV2+N); | |
| 192 | + p->lnPivot += N; | |
| 193 | + p->lnV1 += N; | |
| 194 | + p->lnV2 += N; | |
| 195 | +} | |
| 196 | + | |
| 197 | +/* The next nPivot lines of the PIVOT are changed into nV1 lines by V1 | |
| 198 | +*/ | |
| 199 | +static void dbgChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ | |
| 200 | + blob_appendf(p->pOut, "COPY %u from V1(%u..%u)\n", | |
| 201 | + nV1, p->lnV1+1, p->lnV1+nV1); | |
| 202 | + p->lnPivot += nPivot; | |
| 203 | + p->lnV2 += nPivot; | |
| 204 | + p->lnV1 += nV1; | |
| 205 | +} | |
| 206 | + | |
| 207 | +/* The next nPivot lines of the PIVOT are changed into nV2 lines by V2 | |
| 208 | +*/ | |
| 209 | +static void dbgChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ | |
| 210 | + blob_appendf(p->pOut, "COPY %u lines FROM V2(%u..%u)\n", | |
| 211 | + nV2, p->lnV2+1, p->lnV2+nV2); | |
| 212 | + p->lnPivot += nPivot; | |
| 213 | + p->lnV1 += nPivot; | |
| 214 | + p->lnV2 += nV2; | |
| 215 | +} | |
| 216 | + | |
| 217 | +/* The next nPivot lines of the PIVOT are changed into nV lines from V1 and | |
| 218 | +** V2, which should be the same. In other words, the same change is found | |
| 219 | +** in both V1 and V2. | |
| 220 | +*/ | |
| 221 | +static void dbgChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ | |
| 222 | + blob_appendf(p->pOut, "COPY %u lines from V1(%u..%u) or V2(%u..%u)\n", | |
| 223 | + nV, p->lnV1+1, p->lnV1+nV, p->lnV2+1, p->lnV2+nV); | |
| 224 | + p->lnPivot += nPivot; | |
| 225 | + p->lnV1 += nV; | |
| 226 | + p->lnV2 += nV; | |
| 227 | +} | |
| 228 | + | |
| 229 | +/* V1 and V2 have different and overlapping changes. The next nPivot lines | |
| 230 | +** of the PIVOT are converted into nV1 lines of V1 and nV2 lines of V2. | |
| 231 | +*/ | |
| 232 | +static void dbgConflict( | |
| 233 | + MergeBuilder *p, | |
| 234 | + unsigned int nPivot, | |
| 235 | + unsigned int nV1, | |
| 236 | + unsigned int nV2 | |
| 237 | +){ | |
| 238 | + blob_appendf(p->pOut, | |
| 239 | + "CONFLICT %u,%u,%u BASELINE(%u..%u) versus V1(%u..%u) versus V2(%u..%u)\n", | |
| 240 | + nPivot, nV1, nV2, | |
| 241 | + p->lnPivot+1, p->lnPivot+nPivot, | |
| 242 | + p->lnV1+1, p->lnV1+nV1, | |
| 243 | + p->lnV2+1, p->lnV2+nV2); | |
| 244 | + p->lnV1 += nV1; | |
| 245 | + p->lnPivot += nPivot; | |
| 246 | + p->lnV2 += nV2; | |
| 247 | +} | |
| 248 | + | |
| 249 | +/* Generic destructor for the MergeBuilder object | |
| 250 | +*/ | |
| 251 | +static void dbgDestroy(MergeBuilder *p){ | |
| 252 | + memset(p, 0, sizeof(*p)); | |
| 253 | +} | |
| 254 | + | |
| 255 | +/* Generic initializer for a MergeBuilder object | |
| 256 | +*/ | |
| 257 | +static void mergebuilder_init(MergeBuilder *p){ | |
| 258 | + memset(p, 0, sizeof(*p)); | |
| 259 | + p->xStart = dbgStartEnd; | |
| 260 | + p->xSame = dbgSame; | |
| 261 | + p->xChngV1 = dbgChngV1; | |
| 262 | + p->xChngV2 = dbgChngV2; | |
| 263 | + p->xChngBoth = dbgChngBoth; | |
| 264 | + p->xConflict = dbgConflict; | |
| 265 | + p->xEnd = dbgStartEnd; | |
| 266 | + p->xDestroy = dbgDestroy; | |
| 267 | +} | |
| 268 | + | |
| 269 | +/************************* MergeBuilderToken ********************************/ | |
| 270 | +/* This version of MergeBuilder actually performs a merge on file that | |
| 271 | +** are broken up into tokens instead of lines, and puts the result in pOut. | |
| 272 | +*/ | |
| 273 | +static void tokenSame(MergeBuilder *p, unsigned int N){ | |
| 274 | + blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N); | |
| 275 | + p->pPivot->iCursor += N; | |
| 276 | + p->pV1->iCursor += N; | |
| 277 | + p->pV2->iCursor += N; | |
| 278 | +} | |
| 279 | +static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){ | |
| 280 | + blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1); | |
| 281 | + p->pPivot->iCursor += nPivot; | |
| 282 | + p->pV1->iCursor += nV1; | |
| 283 | + p->pV2->iCursor += nPivot; | |
| 284 | +} | |
| 285 | +static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){ | |
| 286 | + blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2); | |
| 287 | + p->pPivot->iCursor += nPivot; | |
| 288 | + p->pV1->iCursor += nPivot; | |
| 289 | + p->pV2->iCursor += nV2; | |
| 290 | +} | |
| 291 | +static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){ | |
| 292 | + blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV); | |
| 293 | + p->pPivot->iCursor += nPivot; | |
| 294 | + p->pV1->iCursor += nV; | |
| 295 | + p->pV2->iCursor += nV; | |
| 296 | +} | |
| 297 | +static void tokenConflict( | |
| 298 | + MergeBuilder *p, | |
| 299 | + unsigned int nPivot, | |
| 300 | + unsigned int nV1, | |
| 301 | + unsigned int nV2 | |
| 302 | +){ | |
| 303 | + /* For a token-merge conflict, use the text from the merge-in */ | |
| 304 | + blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2); | |
| 305 | + p->pPivot->iCursor += nPivot; | |
| 306 | + p->pV1->iCursor += nV1; | |
| 307 | + p->pV2->iCursor += nV2; | |
| 308 | +} | |
| 309 | +static void mergebuilder_init_token(MergeBuilder *p){ | |
| 310 | + mergebuilder_init(p); | |
| 311 | + p->xSame = tokenSame; | |
| 312 | + p->xChngV1 = tokenChngV1; | |
| 313 | + p->xChngV2 = tokenChngV2; | |
| 314 | + p->xChngBoth = tokenChngBoth; | |
| 315 | + p->xConflict = tokenConflict; | |
| 316 | + p->diffFlags = DIFF_BY_TOKEN; | |
| 317 | +} | |
| 318 | + | |
| 319 | +/* | |
| 320 | +** Attempt to do a low-level merge on a conflict. The conflict is | |
| 321 | +** described by the first four parameters, which are the same as the | |
| 322 | +** arguments to the xConflict method of the MergeBuilder object. | |
| 323 | +** This routine attempts to resolve the conflict by looking at | |
| 324 | +** elements of the conflict region that are finer grain than complete | |
| 325 | +** lines of text. | |
| 326 | +** | |
| 327 | +** The result is written into Blob pOut. pOut is initialized by this | |
| 328 | +** routine. | |
| 329 | +*/ | |
| 330 | +int merge_try_to_resolve_conflict( | |
| 331 | + MergeBuilder *pMB, /* MergeBuilder that encounter conflict */ | |
| 332 | + unsigned int nPivot, /* Lines of conflict in the pivot */ | |
| 333 | + unsigned int nV1, /* Lines of conflict in V1 */ | |
| 334 | + unsigned int nV2, /* Lines of conflict in V2 */ | |
| 335 | + Blob *pOut /* Write resolution text here */ | |
| 336 | +){ | |
| 337 | + int nConflict; | |
| 338 | + MergeBuilder mb; | |
| 339 | + Blob pv, v1, v2; | |
| 340 | + mergebuilder_init_token(&mb); | |
| 341 | + blob_extract_lines(pMB->pPivot, nPivot, &pv); | |
| 342 | + blob_extract_lines(pMB->pV1, nV1, &v1); | |
| 343 | + blob_extract_lines(pMB->pV2, nV2, &v2); | |
| 344 | + blob_zero(pOut); | |
| 345 | + blob_materialize(&pv); | |
| 346 | + blob_materialize(&v1); | |
| 347 | + blob_materialize(&v2); | |
| 348 | + mb.pPivot = &pv; | |
| 349 | + mb.pV1 = &v1; | |
| 350 | + mb.pV2 = &v2; | |
| 351 | + mb.pOut = pOut; | |
| 352 | + nConflict = merge_three_blobs(&mb); | |
| 353 | + blob_reset(&pv); | |
| 354 | + blob_reset(&v1); | |
| 355 | + blob_reset(&v2); | |
| 356 | + /* mb has not allocated any resources, so we do not need to invoke | |
| 357 | + ** the xDestroy method. */ | |
| 358 | + blob_add_final_newline(pOut); | |
| 359 | + return nConflict; | |
| 360 | +} | |
| 361 | + | |
| 362 | + | |
| 363 | +/************************* MergeBuilderText **********************************/ | |
| 364 | +/* This version of MergeBuilder actually performs a merge on file and puts | |
| 365 | +** the result in pOut | |
| 366 | +*/ | |
| 367 | +static void txtStart(MergeBuilder *p){ | |
| 368 | + /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), | |
| 369 | + ** keep it in the output. This should be secure enough not to cause | |
| 370 | + ** unintended changes to the merged file and consistent with what | |
| 371 | + ** users are using in their source files. | |
| 372 | + */ | |
| 373 | + if( starts_with_utf8_bom(p->pV1, 0) && starts_with_utf8_bom(p->pV2, 0) ){ | |
| 374 | + blob_append(p->pOut, (char*)get_utf8_bom(0), -1); | |
| 375 | + } | |
| 376 | + if( contains_crlf(p->pV1) && contains_crlf(p->pV2) ){ | |
| 377 | + p->useCrLf = 1; | |
| 378 | + } | |
| 379 | +} | |
| 380 | +static void txtSame(MergeBuilder *p, unsigned int N){ | |
| 381 | + blob_copy_lines(p->pOut, p->pPivot, N); p->lnPivot += N; | |
| 382 | + blob_copy_lines(0, p->pV1, N); p->lnV1 += N; | |
| 383 | + blob_copy_lines(0, p->pV2, N); p->lnV2 += N; | |
| 384 | +} | |
| 385 | +static void txtChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ | |
| 386 | + blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot; | |
| 387 | + blob_copy_lines(0, p->pV2, nPivot); p->lnV2 += nPivot; | |
| 388 | + blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1; | |
| 389 | +} | |
| 390 | +static void txtChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ | |
| 391 | + blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot; | |
| 392 | + blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot; | |
| 393 | + blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2; | |
| 394 | +} | |
| 395 | +static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ | |
| 396 | + blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot; | |
| 397 | + blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV; | |
| 398 | + blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV; | |
| 399 | +} | |
| 400 | +static void txtConflict( | |
| 401 | + MergeBuilder *p, | |
| 402 | + unsigned int nPivot, | |
| 403 | + unsigned int nV1, | |
| 404 | + unsigned int nV2 | |
| 405 | +){ | |
| 406 | + int nRes; /* Lines in the computed conflict resolution */ | |
| 407 | + Blob res; /* Text of the conflict resolution */ | |
| 408 | + | |
| 409 | + merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res); | |
| 410 | + nRes = blob_linecount(&res); | |
| 411 | + | |
| 412 | + append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf); | |
| 413 | + blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1; | |
| 414 | + | |
| 415 | + if( nRes>0 ){ | |
| 416 | + append_merge_mark(p->pOut, 1, 0, p->useCrLf); | |
| 417 | + blob_copy_lines(p->pOut, &res, nRes); | |
| 418 | + } | |
| 419 | + blob_reset(&res); | |
| 420 | + | |
| 421 | + append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf); | |
| 422 | + blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot; | |
| 423 | + | |
| 424 | + append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf); | |
| 425 | + blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2; | |
| 426 | + | |
| 427 | + append_merge_mark(p->pOut, 4, -1, p->useCrLf); | |
| 428 | +} | |
| 429 | +static void mergebuilder_init_text(MergeBuilder *p){ | |
| 430 | + mergebuilder_init(p); | |
| 431 | + p->xStart = txtStart; | |
| 432 | + p->xSame = txtSame; | |
| 433 | + p->xChngV1 = txtChngV1; | |
| 434 | + p->xChngV2 = txtChngV2; | |
| 435 | + p->xChngBoth = txtChngBoth; | |
| 436 | + p->xConflict = txtConflict; | |
| 437 | +} | |
| 438 | + | |
| 439 | +/************************* MergeBuilderTcl **********************************/ | |
| 440 | +/* Generate merge output formatted for reading by a TCL script. | |
| 441 | +** | |
| 442 | +** The output consists of lines of text, each with 4 tokens. The tokens | |
| 443 | +** represent the content for one line from baseline, v1, v2, and output | |
| 444 | +** respectively. The first character of each token provides auxiliary | |
| 445 | +** information: | |
| 446 | +** | |
| 447 | +** . This line is omitted. | |
| 448 | +** N Name of the file. | |
| 449 | +** T Literal text follows that should have a \n terminator. | |
| 450 | +** R Literal text follows that needs a \r\n terminator. | |
| 451 | +** X Merge conflict. | |
| 452 | +** Z Literal text without a line terminator. | |
| 453 | +** S Skipped lines. Followed by number of lines to skip. | |
| 454 | +** 1 Text is a copy of token 1 | |
| 455 | +** 2 Use data from data-token 2 | |
| 456 | +** 3 Use data from data-token 3 | |
| 457 | +*/ | |
| 458 | + | |
| 459 | +/* Write text that goes into the interior of a double-quoted string in TCL */ | |
| 460 | +static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){ | |
| 461 | + int j; | |
| 462 | + for(j=0; j<nIn; j++){ | |
| 463 | + char c = zIn[j]; | |
| 464 | + if( c=='\\' ){ | |
| 465 | + blob_append(pOut, "\\\\", 2); | |
| 466 | + }else if( c=='"' ){ | |
| 467 | + blob_append(pOut, "\\\"", 2); | |
| 468 | + }else if( c<' ' || c>0x7e ){ | |
| 469 | + char z[5]; | |
| 470 | + z[0] = '\\'; | |
| 471 | + z[1] = "01234567"[(c>>6)&0x3]; | |
| 472 | + z[2] = "01234567"[(c>>3)&0x7]; | |
| 473 | + z[3] = "01234567"[c&0x7]; | |
| 474 | + z[4] = 0; | |
| 475 | + blob_append(pOut, z, 4); | |
| 476 | + }else{ | |
| 477 | + blob_append_char(pOut, c); | |
| 478 | + } | |
| 479 | + } | |
| 480 | +} | |
| 481 | + | |
| 482 | +/* Copy one line of text from pIn and append to pOut, encoded as TCL */ | |
| 483 | +static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){ | |
| 484 | + int i, k; | |
| 485 | + for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){} | |
| 486 | + if( i==pIn->nUsed ){ | |
| 487 | + k = i; | |
| 488 | + }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){ | |
| 489 | + k = i-1; | |
| 490 | + i++; | |
| 491 | + }else{ | |
| 492 | + k = i; | |
| 493 | + i++; | |
| 494 | + } | |
| 495 | + blob_append_char(pOut, '"'); | |
| 496 | + blob_append_char(pOut, cType); | |
| 497 | + tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor); | |
| 498 | + pIn->iCursor = i; | |
| 499 | + blob_append_char(pOut, '"'); | |
| 500 | +} | |
| 501 | +static void tclStart(MergeBuilder *p){ | |
| 502 | + Blob *pOut = p->pOut; | |
| 503 | + blob_append(pOut, "\"N", 2); | |
| 504 | + tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot)); | |
| 505 | + blob_append(pOut, "\" \"N", 4); | |
| 506 | + tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1)); | |
| 507 | + blob_append(pOut, "\" \"N", 4); | |
| 508 | + tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2)); | |
| 509 | + blob_append(pOut, "\" \"N", 4); | |
| 510 | + if( p->zOut ){ | |
| 511 | + tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut)); | |
| 512 | + }else{ | |
| 513 | + blob_append(pOut, "(Merge Result)", -1); | |
| 514 | + } | |
| 515 | + blob_append(pOut, "\"\n", 2); | |
| 516 | +} | |
| 517 | +static void tclSame(MergeBuilder *p, unsigned int N){ | |
| 518 | + int i = 0; | |
| 519 | + int nSkip; | |
| 520 | + | |
| 521 | + if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){ | |
| 522 | + while( i<N && i<p->nContext ){ | |
| 523 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 524 | + blob_append(p->pOut, " 1 1 1\n", 7); | |
| 525 | + i++; | |
| 526 | + } | |
| 527 | + nSkip = N - p->nContext*2; | |
| 528 | + }else{ | |
| 529 | + nSkip = N - p->nContext; | |
| 530 | + } | |
| 531 | + if( nSkip>0 ){ | |
| 532 | + blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n", | |
| 533 | + nSkip, nSkip, nSkip, nSkip); | |
| 534 | + blob_copy_lines(0, p->pPivot, nSkip); | |
| 535 | + i += nSkip; | |
| 536 | + } | |
| 537 | + | |
| 538 | + p->lnPivot += N; | |
| 539 | + p->lnV1 += N; | |
| 540 | + p->lnV2 += N; | |
| 541 | + | |
| 542 | + if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){ | |
| 543 | + while( i<N ){ | |
| 544 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 545 | + blob_append(p->pOut, " 1 1 1\n", 7); | |
| 546 | + i++; | |
| 547 | + } | |
| 548 | + } | |
| 549 | + | |
| 550 | + blob_copy_lines(0, p->pV1, N); | |
| 551 | + blob_copy_lines(0, p->pV2, N); | |
| 552 | +} | |
| 553 | +static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ | |
| 554 | + int i; | |
| 555 | + for(i=0; i<nPivot && i<nV1; i++){ | |
| 556 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 557 | + blob_append_char(p->pOut, ' '); | |
| 558 | + tclLineOfText(p->pOut, p->pV1, 'T'); | |
| 559 | + blob_append(p->pOut, " 1 2\n", 5); | |
| 560 | + } | |
| 561 | + while( i<nPivot ){ | |
| 562 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 563 | + blob_append(p->pOut, " . 1 .\n", 7); | |
| 564 | + i++; | |
| 565 | + } | |
| 566 | + while( i<nV1 ){ | |
| 567 | + blob_append(p->pOut, ". ", 2); | |
| 568 | + tclLineOfText(p->pOut, p->pV1, 'T'); | |
| 569 | + blob_append(p->pOut, " . 2\n", 5); | |
| 570 | + i++; | |
| 571 | + } | |
| 572 | + p->lnPivot += nPivot; | |
| 573 | + p->lnV1 += nV1; | |
| 574 | + p->lnV2 += nPivot; | |
| 575 | + blob_copy_lines(0, p->pV2, nPivot); | |
| 576 | +} | |
| 577 | +static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ | |
| 578 | + int i; | |
| 579 | + for(i=0; i<nPivot && i<nV2; i++){ | |
| 580 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 581 | + blob_append(p->pOut, " 1 ", 3); | |
| 582 | + tclLineOfText(p->pOut, p->pV2, 'T'); | |
| 583 | + blob_append(p->pOut, " 3\n", 3); | |
| 584 | + } | |
| 585 | + while( i<nPivot ){ | |
| 586 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 587 | + blob_append(p->pOut, " 1 . .\n", 7); | |
| 588 | + i++; | |
| 589 | + } | |
| 590 | + while( i<nV2 ){ | |
| 591 | + blob_append(p->pOut, ". . ", 4); | |
| 592 | + tclLineOfText(p->pOut, p->pV2, 'T'); | |
| 593 | + blob_append(p->pOut, " 3\n", 3); | |
| 594 | + i++; | |
| 595 | + } | |
| 596 | + p->lnPivot += nPivot; | |
| 597 | + p->lnV1 += nPivot; | |
| 598 | + p->lnV2 += nV2; | |
| 599 | + blob_copy_lines(0, p->pV1, nPivot); | |
| 600 | +} | |
| 601 | +static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ | |
| 602 | + int i; | |
| 603 | + for(i=0; i<nPivot && i<nV; i++){ | |
| 604 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 605 | + blob_append_char(p->pOut, ' '); | |
| 606 | + tclLineOfText(p->pOut, p->pV1, 'T'); | |
| 607 | + blob_append(p->pOut, " 2 2\n", 5); | |
| 608 | + } | |
| 609 | + while( i<nPivot ){ | |
| 610 | + tclLineOfText(p->pOut, p->pPivot, 'T'); | |
| 611 | + blob_append(p->pOut, " . . .\n", 7); | |
| 612 | + i++; | |
| 613 | + } | |
| 614 | + while( i<nV ){ | |
| 615 | + blob_append(p->pOut, ". ", 2); | |
| 616 | + tclLineOfText(p->pOut, p->pV1, 'T'); | |
| 617 | + blob_append(p->pOut, " 2 2\n", 5); | |
| 618 | + i++; | |
| 619 | + } | |
| 620 | + p->lnPivot += nPivot; | |
| 621 | + p->lnV1 += nV; | |
| 622 | + p->lnV2 += nV; | |
| 623 | + blob_copy_lines(0, p->pV2, nV); | |
| 624 | +} | |
| 625 | +static void tclConflict( | |
| 626 | + MergeBuilder *p, | |
| 627 | + unsigned int nPivot, | |
| 628 | + unsigned int nV1, | |
| 629 | + unsigned int nV2 | |
| 630 | +){ | |
| 631 | + int mx = nPivot; | |
| 632 | + int i; | |
| 633 | + int nRes; | |
| 634 | + Blob res; | |
| 635 | + | |
| 636 | + merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res); | |
| 637 | + nRes = blob_linecount(&res); | |
| 638 | + if( nV1>mx ) mx = nV1; | |
| 639 | + if( nV2>mx ) mx = nV2; | |
| 640 | + if( nRes>mx ) mx = nRes; | |
| 641 | + if( nRes>0 ){ | |
| 642 | + blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2); | |
| 643 | + } | |
| 644 | + for(i=0; i<mx; i++){ | |
| 645 | + if( i<nPivot ){ | |
| 646 | + tclLineOfText(p->pOut, p->pPivot, 'X'); | |
| 647 | + }else{ | |
| 648 | + blob_append_char(p->pOut, '.'); | |
| 649 | + } | |
| 650 | + blob_append_char(p->pOut, ' '); | |
| 651 | + if( i<nV1 ){ | |
| 652 | + tclLineOfText(p->pOut, p->pV1, 'X'); | |
| 653 | + }else{ | |
| 654 | + blob_append_char(p->pOut, '.'); | |
| 655 | + } | |
| 656 | + blob_append_char(p->pOut, ' '); | |
| 657 | + if( i<nV2 ){ | |
| 658 | + tclLineOfText(p->pOut, p->pV2, 'X'); | |
| 659 | + }else{ | |
| 660 | + blob_append_char(p->pOut, '.'); | |
| 661 | + } | |
| 662 | + if( i<nRes ){ | |
| 663 | + blob_append_char(p->pOut, ' '); | |
| 664 | + tclLineOfText(p->pOut, &res, 'X'); | |
| 665 | + blob_append_char(p->pOut, '\n'); | |
| 666 | + }else{ | |
| 667 | + blob_append(p->pOut, " .\n", 3); | |
| 668 | + } | |
| 669 | + if( i==mx-1 ){ | |
| 670 | + blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3); | |
| 671 | + } | |
| 672 | + } | |
| 673 | + blob_reset(&res); | |
| 674 | + p->lnPivot += nPivot; | |
| 675 | + p->lnV1 += nV1; | |
| 676 | + p->lnV2 += nV2; | |
| 677 | +} | |
| 678 | +void mergebuilder_init_tcl(MergeBuilder *p){ | |
| 679 | + mergebuilder_init(p); | |
| 680 | + p->xStart = tclStart; | |
| 681 | + p->xSame = tclSame; | |
| 682 | + p->xChngV1 = tclChngV1; | |
| 683 | + p->xChngV2 = tclChngV2; | |
| 684 | + p->xChngBoth = tclChngBoth; | |
| 685 | + p->xConflict = tclConflict; | |
| 686 | +} | |
| 687 | +/*****************************************************************************/ | |
| 688 | + | |
| 689 | +/* | |
| 690 | +** The aC[] array contains triples of integers. Within each triple, the | |
| 691 | +** elements are: | |
| 692 | +** | |
| 693 | +** (0) The number of lines to copy | |
| 694 | +** (1) The number of lines to delete | |
| 695 | +** (2) The number of liens to insert | |
| 696 | +** | |
| 697 | +** Suppose we want to advance over sz lines of the original file. This routine | |
| 698 | +** returns true if that advance would land us on a copy operation. It | |
| 699 | +** returns false if the advance would end on a delete. | |
| 700 | +*/ | |
| 701 | +static int ends_with_copy(int *aC, int sz){ | |
| 702 | + while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ | |
| 703 | + if( aC[0]>=sz ) return 1; | |
| 704 | + sz -= aC[0]; | |
| 705 | + if( aC[1]>sz ) return 0; | |
| 706 | + sz -= aC[1]; | |
| 707 | + aC += 3; | |
| 708 | + } | |
| 709 | + return 1; | |
| 710 | +} | |
| 711 | + | |
| 712 | +/* | |
| 713 | +** aC[] is an "edit triple" for changes from A to B. Advance through | |
| 714 | +** this triple to determine the number of lines to bypass on B in order | |
| 715 | +** to match an advance of sz lines on A. | |
| 716 | +*/ | |
| 717 | +static int skip_conflict( | |
| 718 | + int *aC, /* Array of integer triples describing the edit */ | |
| 719 | + int i, /* Index in aC[] of current location */ | |
| 720 | + int sz, /* Lines of A that have been skipped */ | |
| 721 | + unsigned int *pLn /* OUT: Lines of B to skip to keep aligment with A */ | |
| 722 | +){ | |
| 723 | + *pLn = 0; | |
| 724 | + while( sz>0 ){ | |
| 725 | + if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; | |
| 726 | + if( aC[i]>=sz ){ | |
| 727 | + aC[i] -= sz; | |
| 728 | + *pLn += sz; | |
| 729 | + break; | |
| 730 | + } | |
| 731 | + *pLn += aC[i]; | |
| 732 | + *pLn += aC[i+2]; | |
| 733 | + sz -= aC[i] + aC[i+1]; | |
| 734 | + i += 3; | |
| 735 | + } | |
| 736 | + return i; | |
| 737 | +} | |
| 191 | 738 | |
| 192 | 739 | /* |
| 193 | 740 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 194 | 741 | ** |
| 195 | 742 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -199,156 +746,113 @@ | ||
| 199 | 746 | ** The return is 0 upon complete success. If any input file is binary, |
| 200 | 747 | ** -1 is returned and pOut is unmodified. If there are merge |
| 201 | 748 | ** conflicts, the merge proceeds as best as it can and the number |
| 202 | 749 | ** of conflicts is returns |
| 203 | 750 | */ |
| 204 | -static int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ | |
| 751 | +int merge_three_blobs(MergeBuilder *p){ | |
| 205 | 752 | int *aC1; /* Changes from pPivot to pV1 */ |
| 206 | 753 | int *aC2; /* Changes from pPivot to pV2 */ |
| 207 | 754 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 208 | 755 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 209 | 756 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 210 | 757 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 211 | - int useCrLf = 0; | |
| 212 | - int ln1, ln2, lnPivot; /* Line numbers for all files */ | |
| 213 | 758 | DiffConfig DCfg; |
| 214 | 759 | |
| 215 | - blob_zero(pOut); /* Merge results stored in pOut */ | |
| 216 | - | |
| 217 | - /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), | |
| 218 | - ** keep it in the output. This should be secure enough not to cause | |
| 219 | - ** unintended changes to the merged file and consistent with what | |
| 220 | - ** users are using in their source files. | |
| 221 | - */ | |
| 222 | - if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ | |
| 223 | - blob_append(pOut, (char*)get_utf8_bom(0), -1); | |
| 224 | - } | |
| 225 | - | |
| 226 | - /* Check once to see if both pV1 and pV2 contains CR/LF endings. | |
| 227 | - ** If true, CR/LF pair will be used later to append the | |
| 228 | - ** boundary markers for merge conflicts. | |
| 229 | - */ | |
| 230 | - if( contains_crlf(pV1) && contains_crlf(pV2) ){ | |
| 231 | - useCrLf = 1; | |
| 232 | - } | |
| 233 | - | |
| 234 | 760 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 235 | 761 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 236 | 762 | ** an array of integer triples. Within each triple, the first integer |
| 237 | 763 | ** is the number of lines of text to copy directly from the pivot, |
| 238 | 764 | ** the second integer is the number of lines of text to omit from the |
| 239 | 765 | ** pivot, and the third integer is the number of lines of text that are |
| 240 | 766 | ** inserted. The edit array ends with a triple of 0,0,0. |
| 241 | 767 | */ |
| 242 | 768 | diff_config_init(&DCfg, 0); |
| 243 | - aC1 = text_diff(pPivot, pV1, 0, &DCfg); | |
| 244 | - aC2 = text_diff(pPivot, pV2, 0, &DCfg); | |
| 769 | + DCfg.diffFlags = p->diffFlags; | |
| 770 | + aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg); | |
| 771 | + aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg); | |
| 245 | 772 | if( aC1==0 || aC2==0 ){ |
| 246 | 773 | free(aC1); |
| 247 | 774 | free(aC2); |
| 248 | 775 | return -1; |
| 249 | 776 | } |
| 250 | 777 | |
| 251 | - blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ | |
| 252 | - blob_rewind(pV2); | |
| 253 | - blob_rewind(pPivot); | |
| 778 | + blob_rewind(p->pV1); /* Rewind inputs: Needed to reconstruct output */ | |
| 779 | + blob_rewind(p->pV2); | |
| 780 | + blob_rewind(p->pPivot); | |
| 254 | 781 | |
| 255 | 782 | /* Determine the length of the aC1[] and aC2[] change vectors */ |
| 256 | - for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){} | |
| 783 | + p->mxPivot = 0; | |
| 784 | + p->mxV1 = 0; | |
| 785 | + for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){ | |
| 786 | + p->mxPivot += aC1[i1] + aC1[i1+1]; | |
| 787 | + p->mxV1 += aC1[i1] + aC1[i1+2]; | |
| 788 | + } | |
| 257 | 789 | limit1 = i1; |
| 258 | - for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){} | |
| 790 | + p->mxV2 = 0; | |
| 791 | + for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){ | |
| 792 | + p->mxV2 += aC2[i2] + aC2[i2+2]; | |
| 793 | + } | |
| 259 | 794 | limit2 = i2; |
| 260 | 795 | |
| 261 | - DEBUG( | |
| 262 | - for(i1=0; i1<limit1; i1+=3){ | |
| 263 | - printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]); | |
| 264 | - } | |
| 265 | - for(i2=0; i2<limit2; i2+=3){ | |
| 266 | - printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); | |
| 267 | - } | |
| 268 | - ) | |
| 796 | + /* Output header text and do any other required initialization */ | |
| 797 | + p->xStart(p); | |
| 269 | 798 | |
| 270 | 799 | /* Loop over the two edit vectors and use them to compute merged text |
| 271 | 800 | ** which is written into pOut. i1 and i2 are multiples of 3 which are |
| 272 | 801 | ** indices into aC1[] and aC2[] to the edit triple currently being |
| 273 | 802 | ** processed |
| 274 | 803 | */ |
| 275 | 804 | i1 = i2 = 0; |
| 276 | - ln1 = ln2 = lnPivot = 1; | |
| 277 | 805 | while( i1<limit1 && i2<limit2 ){ |
| 278 | - DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", | |
| 279 | - i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], | |
| 280 | - i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) | |
| 281 | - | |
| 282 | 806 | if( aC1[i1]>0 && aC2[i2]>0 ){ |
| 283 | 807 | /* Output text that is unchanged in both V1 and V2 */ |
| 284 | 808 | nCpy = min(aC1[i1], aC2[i2]); |
| 285 | - DEBUG( printf("COPY %d\n", nCpy); ) | |
| 286 | - blob_copy_lines(pOut, pPivot, nCpy); lnPivot += nCpy; | |
| 287 | - blob_copy_lines(0, pV1, nCpy); ln1 += nCpy; | |
| 288 | - blob_copy_lines(0, pV2, nCpy); ln2 += nCpy; | |
| 809 | + p->xSame(p, nCpy); | |
| 289 | 810 | aC1[i1] -= nCpy; |
| 290 | 811 | aC2[i2] -= nCpy; |
| 291 | 812 | }else |
| 292 | 813 | if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){ |
| 293 | 814 | /* Output edits to V2 that occurs within unchanged regions of V1 */ |
| 294 | 815 | nDel = aC2[i2+1]; |
| 295 | 816 | nIns = aC2[i2+2]; |
| 296 | - DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); ) | |
| 297 | - blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; | |
| 298 | - blob_copy_lines(0, pV1, nDel); ln1 += nDel; | |
| 299 | - blob_copy_lines(pOut, pV2, nIns); ln2 += nIns; | |
| 817 | + p->xChngV2(p, nDel, nIns); | |
| 300 | 818 | aC1[i1] -= nDel; |
| 301 | 819 | i2 += 3; |
| 302 | 820 | }else |
| 303 | 821 | if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){ |
| 304 | 822 | /* Output edits to V1 that occur within unchanged regions of V2 */ |
| 305 | 823 | nDel = aC1[i1+1]; |
| 306 | 824 | nIns = aC1[i1+2]; |
| 307 | - DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); ) | |
| 308 | - blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; | |
| 309 | - blob_copy_lines(0, pV2, nDel); ln2 += nDel; | |
| 310 | - blob_copy_lines(pOut, pV1, nIns); ln1 += nIns; | |
| 825 | + p->xChngV1(p, nDel, nIns); | |
| 311 | 826 | aC2[i2] -= nDel; |
| 312 | 827 | i1 += 3; |
| 313 | 828 | }else |
| 314 | - if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){ | |
| 829 | + if( sameEdit(&aC1[i1], &aC2[i2], p->pV1, p->pV2) ){ | |
| 315 | 830 | /* Output edits that are identical in both V1 and V2. */ |
| 316 | 831 | assert( aC1[i1]==0 ); |
| 317 | 832 | nDel = aC1[i1+1]; |
| 318 | 833 | nIns = aC1[i1+2]; |
| 319 | - DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); ) | |
| 320 | - blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; | |
| 321 | - blob_copy_lines(pOut, pV1, nIns); ln1 += nIns; | |
| 322 | - blob_copy_lines(0, pV2, nIns); ln2 += nIns; | |
| 834 | + p->xChngBoth(p, nDel, nIns); | |
| 323 | 835 | i1 += 3; |
| 324 | 836 | i2 += 3; |
| 325 | 837 | }else |
| 326 | 838 | { |
| 327 | 839 | /* We have found a region where different edits to V1 and V2 overlap. |
| 328 | 840 | ** This is a merge conflict. Find the size of the conflict, then |
| 329 | 841 | ** output both possible edits separated by distinctive marks. |
| 330 | 842 | */ |
| 331 | - int sz = 1; /* Size of the conflict in lines */ | |
| 843 | + unsigned int sz = 1; /* Size of the conflict in the pivot, in lines */ | |
| 844 | + unsigned int nV1, nV2; /* Size of conflict in V1 and V2, in lines */ | |
| 332 | 845 | nConflict++; |
| 333 | - while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ | |
| 846 | + while( !ends_with_copy(&aC1[i1], sz) || !ends_with_copy(&aC2[i2], sz) ){ | |
| 334 | 847 | sz++; |
| 335 | 848 | } |
| 336 | - DEBUG( printf("CONFLICT %d\n", sz); ) | |
| 337 | - | |
| 338 | - append_merge_mark(pOut, 0, ln1, useCrLf); | |
| 339 | - i1 = output_one_side(pOut, pV1, aC1, i1, sz, &ln1); | |
| 340 | - | |
| 341 | - append_merge_mark(pOut, 1, lnPivot, useCrLf); | |
| 342 | - blob_copy_lines(pOut, pPivot, sz); lnPivot += sz; | |
| 343 | - | |
| 344 | - append_merge_mark(pOut, 2, ln2, useCrLf); | |
| 345 | - i2 = output_one_side(pOut, pV2, aC2, i2, sz, &ln2); | |
| 346 | - | |
| 347 | - append_merge_mark(pOut, 3, -1, useCrLf); | |
| 348 | - } | |
| 349 | - | |
| 849 | + i1 = skip_conflict(aC1, i1, sz, &nV1); | |
| 850 | + i2 = skip_conflict(aC2, i2, sz, &nV2); | |
| 851 | + p->xConflict(p, sz, nV1, nV2); | |
| 852 | + } | |
| 853 | + | |
| 350 | 854 | /* If we are finished with an edit triple, advance to the next |
| 351 | 855 | ** triple. |
| 352 | 856 | */ |
| 353 | 857 | if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3; |
| 354 | 858 | if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3; |
| @@ -356,20 +860,18 @@ | ||
| 356 | 860 | |
| 357 | 861 | /* When one of the two edit vectors reaches its end, there might still |
| 358 | 862 | ** be an insert in the other edit vector. Output this remaining |
| 359 | 863 | ** insert. |
| 360 | 864 | */ |
| 361 | - DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", | |
| 362 | - i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], | |
| 363 | - i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) | |
| 364 | 865 | if( i1<limit1 && aC1[i1+2]>0 ){ |
| 365 | - DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); ) | |
| 366 | - blob_copy_lines(pOut, pV1, aC1[i1+2]); | |
| 866 | + p->xChngV1(p, 0, aC1[i1+2]); | |
| 367 | 867 | }else if( i2<limit2 && aC2[i2+2]>0 ){ |
| 368 | - DEBUG( printf("INSERT +%d right\n", aC2[i2+2]); ) | |
| 369 | - blob_copy_lines(pOut, pV2, aC2[i2+2]); | |
| 868 | + p->xChngV2(p, 0, aC2[i2+2]); | |
| 370 | 869 | } |
| 870 | + | |
| 871 | + /* Output footer text */ | |
| 872 | + p->xEnd(p); | |
| 371 | 873 | |
| 372 | 874 | free(aC1); |
| 373 | 875 | free(aC2); |
| 374 | 876 | return nConflict; |
| 375 | 877 | } |
| @@ -384,11 +886,12 @@ | ||
| 384 | 886 | const char *z = blob_buffer(p); |
| 385 | 887 | int n = blob_size(p) - len + 1; |
| 386 | 888 | assert( len==(int)strlen(mergeMarker[1]) ); |
| 387 | 889 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 388 | 890 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 389 | - assert( count(mergeMarker)==4 ); | |
| 891 | + assert( len==(int)strlen(mergeMarker[4]) ); | |
| 892 | + assert( count(mergeMarker)==5 ); | |
| 390 | 893 | for(i=0; i<n; ){ |
| 391 | 894 | for(j=0; j<4; j++){ |
| 392 | 895 | if( (memcmp(&z[i], mergeMarker[j], len)==0) ){ |
| 393 | 896 | return 1; |
| 394 | 897 | } |
| @@ -408,18 +911,106 @@ | ||
| 408 | 911 | blob_read_from_file(&file, zFullpath, ExtFILE); |
| 409 | 912 | rc = contains_merge_marker(&file); |
| 410 | 913 | blob_reset(&file); |
| 411 | 914 | return rc; |
| 412 | 915 | } |
| 916 | + | |
| 917 | +/* | |
| 918 | +** Show merge output in a Tcl/Tk window, in response to the --tk option | |
| 919 | +** to the "merge" or "3-way-merge" command. | |
| 920 | +** | |
| 921 | +** If fossil has direct access to a Tcl interpreter (either loaded | |
| 922 | +** dynamically through stubs or linked in statically), we can use it | |
| 923 | +** directly. Otherwise: | |
| 924 | +** (1) Write the Tcl/Tk script used for rendering into a temp file. | |
| 925 | +** (2) Invoke "tclsh" on the temp file using fossil_system(). | |
| 926 | +** (3) Delete the temp file. | |
| 927 | +*/ | |
| 928 | +void merge_tk(const char *zSubCmd, int firstArg){ | |
| 929 | + int i; | |
| 930 | + Blob script; | |
| 931 | + const char *zTempFile = 0; | |
| 932 | + char *zCmd; | |
| 933 | + const char *zTclsh; | |
| 934 | + const char *zCnt; | |
| 935 | + int bDarkMode = find_option("dark",0,0)!=0; | |
| 936 | + int nContext; | |
| 937 | + zCnt = find_option("context", "c", 1); | |
| 938 | + if( zCnt==0 ){ | |
| 939 | + nContext = 6; | |
| 940 | + }else{ | |
| 941 | + nContext = atoi(zCnt); | |
| 942 | + if( nContext<0 ) nContext = 0xfffffff; | |
| 943 | + } | |
| 944 | + blob_zero(&script); | |
| 945 | + blob_appendf(&script, "set ncontext %d\n", nContext); | |
| 946 | + blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl", | |
| 947 | + g.nameOfExe, zSubCmd); | |
| 948 | + find_option("tcl",0,0); | |
| 949 | + find_option("debug",0,0); | |
| 950 | + zTclsh = find_option("tclsh",0,1); | |
| 951 | + if( zTclsh==0 ){ | |
| 952 | + zTclsh = db_get("tclsh",0); | |
| 953 | + } | |
| 954 | + /* The undocumented --script FILENAME option causes the Tk script to | |
| 955 | + ** be written into the FILENAME instead of being run. This is used | |
| 956 | + ** for testing and debugging. */ | |
| 957 | + zTempFile = find_option("script",0,1); | |
| 958 | + verify_all_options(); | |
| 959 | + | |
| 960 | + if( (g.argc - firstArg)!=3 ){ | |
| 961 | + fossil_fatal("Requires 3 filename arguments"); | |
| 962 | + } | |
| 963 | + | |
| 964 | + for(i=firstArg; i<g.argc; i++){ | |
| 965 | + const char *z = g.argv[i]; | |
| 966 | + if( sqlite3_strglob("*}*",z) ){ | |
| 967 | + blob_appendf(&script, " {%/}", z); | |
| 968 | + }else{ | |
| 969 | + int j; | |
| 970 | + blob_append(&script, " ", 1); | |
| 971 | + for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); | |
| 972 | + } | |
| 973 | + } | |
| 974 | + blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); | |
| 975 | + blob_appendf(&script, "%s", builtin_file("merge.tcl", 0)); | |
| 976 | + if( zTempFile ){ | |
| 977 | + blob_write_to_file(&script, zTempFile); | |
| 978 | + fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile); | |
| 979 | + }else{ | |
| 980 | +#if defined(FOSSIL_ENABLE_TCL) | |
| 981 | + Th_FossilInit(TH_INIT_DEFAULT); | |
| 982 | + if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script), | |
| 983 | + blob_size(&script), 1, 1, 0)==TCL_OK ){ | |
| 984 | + blob_reset(&script); | |
| 985 | + return; | |
| 986 | + } | |
| 987 | + /* | |
| 988 | + * If evaluation of the Tcl script fails, the reason may be that Tk | |
| 989 | + * could not be found by the loaded Tcl, or that Tcl cannot be loaded | |
| 990 | + * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback | |
| 991 | + * to using the external "tclsh", if available. | |
| 992 | + */ | |
| 993 | +#endif | |
| 994 | + zTempFile = write_blob_to_temp_file(&script); | |
| 995 | + zCmd = mprintf("%$ %$", zTclsh, zTempFile); | |
| 996 | + fossil_system(zCmd); | |
| 997 | + file_delete(zTempFile); | |
| 998 | + fossil_free(zCmd); | |
| 999 | + } | |
| 1000 | + blob_reset(&script); | |
| 1001 | +} | |
| 1002 | + | |
| 413 | 1003 | |
| 414 | 1004 | /* |
| 415 | 1005 | ** COMMAND: 3-way-merge* |
| 416 | 1006 | ** |
| 417 | -** Usage: %fossil 3-way-merge BASELINE V1 V2 MERGED | |
| 1007 | +** Usage: %fossil 3-way-merge BASELINE V1 V2 [MERGED] | |
| 418 | 1008 | ** |
| 419 | 1009 | ** Inputs are files BASELINE, V1, and V2. The file MERGED is generated |
| 420 | -** as output. | |
| 1010 | +** as output. If no MERGED file is specified, output is sent to | |
| 1011 | +** stdout. | |
| 421 | 1012 | ** |
| 422 | 1013 | ** BASELINE is a common ancestor of two files V1 and V2 that have diverging |
| 423 | 1014 | ** edits. The generated output file MERGED is the combination of all |
| 424 | 1015 | ** changes in both V1 and V2. |
| 425 | 1016 | ** |
| @@ -436,38 +1027,75 @@ | ||
| 436 | 1027 | ** cp Xup.c Xbase.c |
| 437 | 1028 | ** # Verify that everything still works |
| 438 | 1029 | ** fossil commit |
| 439 | 1030 | ** |
| 440 | 1031 | */ |
| 441 | -void delta_3waymerge_cmd(void){ | |
| 442 | - Blob pivot, v1, v2, merged; | |
| 1032 | +void merge_3way_cmd(void){ | |
| 1033 | + MergeBuilder s; | |
| 443 | 1034 | int nConflict; |
| 1035 | + Blob pivot, v1, v2, out; | |
| 1036 | + int noWarn = 0; | |
| 1037 | + const char *zCnt; | |
| 1038 | + | |
| 1039 | + if( find_option("tk", 0, 0)!=0 ){ | |
| 1040 | + merge_tk("3-way-merge", 2); | |
| 1041 | + return; | |
| 1042 | + } | |
| 1043 | + mergebuilder_init_text(&s); | |
| 1044 | + if( find_option("debug", 0, 0) ){ | |
| 1045 | + mergebuilder_init(&s); | |
| 1046 | + } | |
| 1047 | + if( find_option("tcl", 0, 0) ){ | |
| 1048 | + mergebuilder_init_tcl(&s); | |
| 1049 | + noWarn = 1; | |
| 1050 | + } | |
| 1051 | + zCnt = find_option("context", "c", 1); | |
| 1052 | + if( zCnt ){ | |
| 1053 | + s.nContext = atoi(zCnt); | |
| 1054 | + if( s.nContext<0 ) s.nContext = 0xfffffff; | |
| 1055 | + }else{ | |
| 1056 | + s.nContext = 6; | |
| 1057 | + } | |
| 1058 | + blob_zero(&pivot); s.pPivot = &pivot; | |
| 1059 | + blob_zero(&v1); s.pV1 = &v1; | |
| 1060 | + blob_zero(&v2); s.pV2 = &v2; | |
| 1061 | + blob_zero(&out); s.pOut = &out; | |
| 444 | 1062 | |
| 445 | 1063 | /* We should be done with options.. */ |
| 446 | 1064 | verify_all_options(); |
| 447 | 1065 | |
| 448 | - if( g.argc!=6 ){ | |
| 449 | - usage("PIVOT V1 V2 MERGED"); | |
| 1066 | + if( g.argc!=6 && g.argc!=5 ){ | |
| 1067 | + usage("[OPTIONS] PIVOT V1 V2 [MERGED]"); | |
| 450 | 1068 | } |
| 451 | - if( blob_read_from_file(&pivot, g.argv[2], ExtFILE)<0 ){ | |
| 1069 | + s.zPivot = file_tail(g.argv[2]); | |
| 1070 | + s.zV1 = file_tail(g.argv[3]); | |
| 1071 | + s.zV2 = file_tail(g.argv[4]); | |
| 1072 | + if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){ | |
| 452 | 1073 | fossil_fatal("cannot read %s", g.argv[2]); |
| 453 | 1074 | } |
| 454 | - if( blob_read_from_file(&v1, g.argv[3], ExtFILE)<0 ){ | |
| 1075 | + if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){ | |
| 455 | 1076 | fossil_fatal("cannot read %s", g.argv[3]); |
| 456 | 1077 | } |
| 457 | - if( blob_read_from_file(&v2, g.argv[4], ExtFILE)<0 ){ | |
| 1078 | + if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){ | |
| 458 | 1079 | fossil_fatal("cannot read %s", g.argv[4]); |
| 459 | 1080 | } |
| 460 | - nConflict = blob_merge(&pivot, &v1, &v2, &merged); | |
| 461 | - if( blob_write_to_file(&merged, g.argv[5])<(int)blob_size(&merged) ){ | |
| 462 | - fossil_fatal("cannot write %s", g.argv[4]); | |
| 1081 | + nConflict = merge_three_blobs(&s); | |
| 1082 | + if( g.argc==6 ){ | |
| 1083 | + s.zOut = file_tail(g.argv[5]); | |
| 1084 | + blob_write_to_file(s.pOut, g.argv[5]); | |
| 1085 | + }else{ | |
| 1086 | + s.zOut = "(Merge Result)"; | |
| 1087 | + blob_write_to_file(s.pOut, "-"); | |
| 463 | 1088 | } |
| 1089 | + s.xDestroy(&s); | |
| 464 | 1090 | blob_reset(&pivot); |
| 465 | 1091 | blob_reset(&v1); |
| 466 | 1092 | blob_reset(&v2); |
| 467 | - blob_reset(&merged); | |
| 468 | - if( nConflict>0 ) fossil_warning("WARNING: %d merge conflicts", nConflict); | |
| 1093 | + blob_reset(&out); | |
| 1094 | + if( nConflict>0 && !noWarn ){ | |
| 1095 | + fossil_warning("WARNING: %d merge conflicts", nConflict); | |
| 1096 | + } | |
| 469 | 1097 | } |
| 470 | 1098 | |
| 471 | 1099 | /* |
| 472 | 1100 | ** aSubst is an array of string pairs. The first element of each pair is |
| 473 | 1101 | ** a string that begins with %. The second element is a replacement for that |
| @@ -505,32 +1133,32 @@ | ||
| 505 | 1133 | |
| 506 | 1134 | #if INTERFACE |
| 507 | 1135 | /* |
| 508 | 1136 | ** Flags to the 3-way merger |
| 509 | 1137 | */ |
| 510 | -#define MERGE_DRYRUN 0x0001 | |
| 1138 | +#define MERGE_DRYRUN 0x0001 | |
| 511 | 1139 | /* |
| 512 | 1140 | ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain |
| 513 | 1141 | ** its temporary files on error. By default they are removed after the |
| 514 | 1142 | ** merge, regardless of success or failure. |
| 515 | 1143 | */ |
| 516 | -#define MERGE_KEEP_FILES 0x0002 | |
| 1144 | +#define MERGE_KEEP_FILES 0x0002 | |
| 517 | 1145 | #endif |
| 518 | 1146 | |
| 519 | 1147 | |
| 520 | 1148 | /* |
| 521 | -** This routine is a wrapper around blob_merge() with the following | |
| 1149 | +** This routine is a wrapper around merge_three_blobs() with the following | |
| 522 | 1150 | ** enhancements: |
| 523 | 1151 | ** |
| 524 | 1152 | ** (1) If the merge-command is defined, then use the external merging |
| 525 | 1153 | ** program specified instead of the built-in blob-merge to do the |
| 526 | 1154 | ** merging. Panic if the external merger fails. |
| 527 | 1155 | ** ** Not currently implemented ** |
| 528 | 1156 | ** |
| 529 | 1157 | ** (2) If gmerge-command is defined and there are merge conflicts in |
| 530 | -** blob_merge() then invoke the external graphical merger to resolve | |
| 531 | -** the conflicts. | |
| 1158 | +** merge_three_blobs() then invoke the external graphical merger | |
| 1159 | +** to resolve the conflicts. | |
| 532 | 1160 | ** |
| 533 | 1161 | ** (3) If a merge conflict occurs and gmerge-command is not defined, |
| 534 | 1162 | ** then write the pivot, original, and merge-in files to the |
| 535 | 1163 | ** filesystem. |
| 536 | 1164 | */ |
| @@ -539,30 +1167,37 @@ | ||
| 539 | 1167 | const char *zV1, /* Name of file for version merging into (mine) */ |
| 540 | 1168 | Blob *pV2, /* Version merging from (yours) */ |
| 541 | 1169 | Blob *pOut, /* Output written here */ |
| 542 | 1170 | unsigned mergeFlags /* Flags that control operation */ |
| 543 | 1171 | ){ |
| 544 | - Blob v1; /* Content of zV1 */ | |
| 545 | - int rc; /* Return code of subroutines and this routine */ | |
| 1172 | + Blob v1; /* Content of zV1 */ | |
| 1173 | + int rc; /* Return code of subroutines and this routine */ | |
| 546 | 1174 | const char *zGMerge; /* Name of the gmerge command */ |
| 1175 | + MergeBuilder s; /* The merge state */ | |
| 547 | 1176 | |
| 548 | - blob_read_from_file(&v1, zV1, ExtFILE); | |
| 549 | - rc = blob_merge(pPivot, &v1, pV2, pOut); | |
| 1177 | + mergebuilder_init_text(&s); | |
| 1178 | + s.pPivot = pPivot; | |
| 1179 | + s.pV1 = &v1; | |
| 1180 | + s.pV2 = pV2; | |
| 1181 | + blob_zero(pOut); | |
| 1182 | + s.pOut = pOut; | |
| 1183 | + blob_read_from_file(s.pV1, zV1, ExtFILE); | |
| 1184 | + rc = merge_three_blobs(&s); | |
| 550 | 1185 | zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); |
| 551 | 1186 | if( (mergeFlags & MERGE_DRYRUN)==0 |
| 552 | 1187 | && ((zGMerge!=0 && zGMerge[0]!=0) |
| 553 | 1188 | || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ |
| 554 | 1189 | char *zPivot; /* Name of the pivot file */ |
| 555 | 1190 | char *zOrig; /* Name of the original content file */ |
| 556 | 1191 | char *zOther; /* Name of the merge file */ |
| 557 | 1192 | |
| 558 | 1193 | zPivot = file_newname(zV1, "baseline", 1); |
| 559 | - blob_write_to_file(pPivot, zPivot); | |
| 1194 | + blob_write_to_file(s.pPivot, zPivot); | |
| 560 | 1195 | zOrig = file_newname(zV1, "original", 1); |
| 561 | - blob_write_to_file(&v1, zOrig); | |
| 1196 | + blob_write_to_file(s.pV1, zOrig); | |
| 562 | 1197 | zOther = file_newname(zV1, "merge", 1); |
| 563 | - blob_write_to_file(pV2, zOther); | |
| 1198 | + blob_write_to_file(s.pV2, zOther); | |
| 564 | 1199 | if( rc>0 ){ |
| 565 | 1200 | if( zGMerge && zGMerge[0] ){ |
| 566 | 1201 | char *zOut; /* Temporary output file */ |
| 567 | 1202 | char *zCmd; /* Command to invoke */ |
| 568 | 1203 | const char *azSubst[8]; /* Strings to be substituted */ |
| @@ -590,7 +1225,8 @@ | ||
| 590 | 1225 | fossil_free(zPivot); |
| 591 | 1226 | fossil_free(zOrig); |
| 592 | 1227 | fossil_free(zOther); |
| 593 | 1228 | } |
| 594 | 1229 | blob_reset(&v1); |
| 1230 | + s.xDestroy(&s); | |
| 595 | 1231 | return rc; |
| 596 | 1232 | } |
| 597 | 1233 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -75,75 +75,17 @@ | |
| 75 | if( aC1[2]!=aC2[2] ) return 0; |
| 76 | if( sameLines(pV1, pV2, aC1[2]) ) return 1; |
| 77 | return 0; |
| 78 | } |
| 79 | |
| 80 | /* |
| 81 | ** The aC[] array contains triples of integers. Within each triple, the |
| 82 | ** elements are: |
| 83 | ** |
| 84 | ** (0) The number of lines to copy |
| 85 | ** (1) The number of lines to delete |
| 86 | ** (2) The number of liens to insert |
| 87 | ** |
| 88 | ** Suppose we want to advance over sz lines of the original file. This routine |
| 89 | ** returns true if that advance would land us on a copy operation. It |
| 90 | ** returns false if the advance would end on a delete. |
| 91 | */ |
| 92 | static int ends_at_CPY(int *aC, int sz){ |
| 93 | while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ |
| 94 | if( aC[0]>=sz ) return 1; |
| 95 | sz -= aC[0]; |
| 96 | if( aC[1]>sz ) return 0; |
| 97 | sz -= aC[1]; |
| 98 | aC += 3; |
| 99 | } |
| 100 | return 1; |
| 101 | } |
| 102 | |
| 103 | /* |
| 104 | ** pSrc contains an edited file where aC[] describes the edit. Part of |
| 105 | ** pSrc has already been output. This routine outputs additional lines |
| 106 | ** of pSrc - lines that correspond to the next sz lines of the original |
| 107 | ** unedited file. |
| 108 | ** |
| 109 | ** Note that sz counts the number of lines of text in the original file. |
| 110 | ** But text is output from the edited file. So the number of lines transfer |
| 111 | ** to pOut might be different from sz. Fewer lines appear in pOut if there |
| 112 | ** are deletes. More lines appear if there are inserts. |
| 113 | ** |
| 114 | ** The aC[] array is updated and the new index into aC[] is returned. |
| 115 | */ |
| 116 | static int output_one_side( |
| 117 | Blob *pOut, /* Write to this blob */ |
| 118 | Blob *pSrc, /* The edited file that is to be copied to pOut */ |
| 119 | int *aC, /* Array of integer triples describing the edit */ |
| 120 | int i, /* Index in aC[] of current location in pSrc */ |
| 121 | int sz, /* Number of lines in unedited source to output */ |
| 122 | int *pLn /* Line number counter */ |
| 123 | ){ |
| 124 | while( sz>0 ){ |
| 125 | if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; |
| 126 | if( aC[i]>=sz ){ |
| 127 | blob_copy_lines(pOut, pSrc, sz); *pLn += sz; |
| 128 | aC[i] -= sz; |
| 129 | break; |
| 130 | } |
| 131 | blob_copy_lines(pOut, pSrc, aC[i]); *pLn += aC[i]; |
| 132 | blob_copy_lines(pOut, pSrc, aC[i+2]); *pLn += aC[i+2]; |
| 133 | sz -= aC[i] + aC[i+1]; |
| 134 | i += 3; |
| 135 | } |
| 136 | return i; |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | ** Text of boundary markers for merge conflicts. |
| 141 | */ |
| 142 | static const char *const mergeMarker[] = { |
| 143 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 144 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<", |
| 145 | "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||", |
| 146 | "======= MERGED IN content follows ===============================", |
| 147 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" |
| 148 | }; |
| 149 | |
| @@ -186,10 +128,615 @@ | |
| 186 | ensure_line_end(pOut, useCrLf); |
| 187 | blob_append(pOut, mergeMarker[iMark], -1); |
| 188 | if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); |
| 189 | ensure_line_end(pOut, useCrLf); |
| 190 | } |
| 191 | |
| 192 | /* |
| 193 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 194 | ** |
| 195 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -199,156 +746,113 @@ | |
| 199 | ** The return is 0 upon complete success. If any input file is binary, |
| 200 | ** -1 is returned and pOut is unmodified. If there are merge |
| 201 | ** conflicts, the merge proceeds as best as it can and the number |
| 202 | ** of conflicts is returns |
| 203 | */ |
| 204 | static int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ |
| 205 | int *aC1; /* Changes from pPivot to pV1 */ |
| 206 | int *aC2; /* Changes from pPivot to pV2 */ |
| 207 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 208 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 209 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 210 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 211 | int useCrLf = 0; |
| 212 | int ln1, ln2, lnPivot; /* Line numbers for all files */ |
| 213 | DiffConfig DCfg; |
| 214 | |
| 215 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 216 | |
| 217 | /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), |
| 218 | ** keep it in the output. This should be secure enough not to cause |
| 219 | ** unintended changes to the merged file and consistent with what |
| 220 | ** users are using in their source files. |
| 221 | */ |
| 222 | if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ |
| 223 | blob_append(pOut, (char*)get_utf8_bom(0), -1); |
| 224 | } |
| 225 | |
| 226 | /* Check once to see if both pV1 and pV2 contains CR/LF endings. |
| 227 | ** If true, CR/LF pair will be used later to append the |
| 228 | ** boundary markers for merge conflicts. |
| 229 | */ |
| 230 | if( contains_crlf(pV1) && contains_crlf(pV2) ){ |
| 231 | useCrLf = 1; |
| 232 | } |
| 233 | |
| 234 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 235 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 236 | ** an array of integer triples. Within each triple, the first integer |
| 237 | ** is the number of lines of text to copy directly from the pivot, |
| 238 | ** the second integer is the number of lines of text to omit from the |
| 239 | ** pivot, and the third integer is the number of lines of text that are |
| 240 | ** inserted. The edit array ends with a triple of 0,0,0. |
| 241 | */ |
| 242 | diff_config_init(&DCfg, 0); |
| 243 | aC1 = text_diff(pPivot, pV1, 0, &DCfg); |
| 244 | aC2 = text_diff(pPivot, pV2, 0, &DCfg); |
| 245 | if( aC1==0 || aC2==0 ){ |
| 246 | free(aC1); |
| 247 | free(aC2); |
| 248 | return -1; |
| 249 | } |
| 250 | |
| 251 | blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ |
| 252 | blob_rewind(pV2); |
| 253 | blob_rewind(pPivot); |
| 254 | |
| 255 | /* Determine the length of the aC1[] and aC2[] change vectors */ |
| 256 | for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){} |
| 257 | limit1 = i1; |
| 258 | for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){} |
| 259 | limit2 = i2; |
| 260 | |
| 261 | DEBUG( |
| 262 | for(i1=0; i1<limit1; i1+=3){ |
| 263 | printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]); |
| 264 | } |
| 265 | for(i2=0; i2<limit2; i2+=3){ |
| 266 | printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); |
| 267 | } |
| 268 | ) |
| 269 | |
| 270 | /* Loop over the two edit vectors and use them to compute merged text |
| 271 | ** which is written into pOut. i1 and i2 are multiples of 3 which are |
| 272 | ** indices into aC1[] and aC2[] to the edit triple currently being |
| 273 | ** processed |
| 274 | */ |
| 275 | i1 = i2 = 0; |
| 276 | ln1 = ln2 = lnPivot = 1; |
| 277 | while( i1<limit1 && i2<limit2 ){ |
| 278 | DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", |
| 279 | i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], |
| 280 | i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) |
| 281 | |
| 282 | if( aC1[i1]>0 && aC2[i2]>0 ){ |
| 283 | /* Output text that is unchanged in both V1 and V2 */ |
| 284 | nCpy = min(aC1[i1], aC2[i2]); |
| 285 | DEBUG( printf("COPY %d\n", nCpy); ) |
| 286 | blob_copy_lines(pOut, pPivot, nCpy); lnPivot += nCpy; |
| 287 | blob_copy_lines(0, pV1, nCpy); ln1 += nCpy; |
| 288 | blob_copy_lines(0, pV2, nCpy); ln2 += nCpy; |
| 289 | aC1[i1] -= nCpy; |
| 290 | aC2[i2] -= nCpy; |
| 291 | }else |
| 292 | if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){ |
| 293 | /* Output edits to V2 that occurs within unchanged regions of V1 */ |
| 294 | nDel = aC2[i2+1]; |
| 295 | nIns = aC2[i2+2]; |
| 296 | DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); ) |
| 297 | blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; |
| 298 | blob_copy_lines(0, pV1, nDel); ln1 += nDel; |
| 299 | blob_copy_lines(pOut, pV2, nIns); ln2 += nIns; |
| 300 | aC1[i1] -= nDel; |
| 301 | i2 += 3; |
| 302 | }else |
| 303 | if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){ |
| 304 | /* Output edits to V1 that occur within unchanged regions of V2 */ |
| 305 | nDel = aC1[i1+1]; |
| 306 | nIns = aC1[i1+2]; |
| 307 | DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); ) |
| 308 | blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; |
| 309 | blob_copy_lines(0, pV2, nDel); ln2 += nDel; |
| 310 | blob_copy_lines(pOut, pV1, nIns); ln1 += nIns; |
| 311 | aC2[i2] -= nDel; |
| 312 | i1 += 3; |
| 313 | }else |
| 314 | if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){ |
| 315 | /* Output edits that are identical in both V1 and V2. */ |
| 316 | assert( aC1[i1]==0 ); |
| 317 | nDel = aC1[i1+1]; |
| 318 | nIns = aC1[i1+2]; |
| 319 | DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); ) |
| 320 | blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; |
| 321 | blob_copy_lines(pOut, pV1, nIns); ln1 += nIns; |
| 322 | blob_copy_lines(0, pV2, nIns); ln2 += nIns; |
| 323 | i1 += 3; |
| 324 | i2 += 3; |
| 325 | }else |
| 326 | { |
| 327 | /* We have found a region where different edits to V1 and V2 overlap. |
| 328 | ** This is a merge conflict. Find the size of the conflict, then |
| 329 | ** output both possible edits separated by distinctive marks. |
| 330 | */ |
| 331 | int sz = 1; /* Size of the conflict in lines */ |
| 332 | nConflict++; |
| 333 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 334 | sz++; |
| 335 | } |
| 336 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 337 | |
| 338 | append_merge_mark(pOut, 0, ln1, useCrLf); |
| 339 | i1 = output_one_side(pOut, pV1, aC1, i1, sz, &ln1); |
| 340 | |
| 341 | append_merge_mark(pOut, 1, lnPivot, useCrLf); |
| 342 | blob_copy_lines(pOut, pPivot, sz); lnPivot += sz; |
| 343 | |
| 344 | append_merge_mark(pOut, 2, ln2, useCrLf); |
| 345 | i2 = output_one_side(pOut, pV2, aC2, i2, sz, &ln2); |
| 346 | |
| 347 | append_merge_mark(pOut, 3, -1, useCrLf); |
| 348 | } |
| 349 | |
| 350 | /* If we are finished with an edit triple, advance to the next |
| 351 | ** triple. |
| 352 | */ |
| 353 | if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3; |
| 354 | if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3; |
| @@ -356,20 +860,18 @@ | |
| 356 | |
| 357 | /* When one of the two edit vectors reaches its end, there might still |
| 358 | ** be an insert in the other edit vector. Output this remaining |
| 359 | ** insert. |
| 360 | */ |
| 361 | DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", |
| 362 | i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], |
| 363 | i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) |
| 364 | if( i1<limit1 && aC1[i1+2]>0 ){ |
| 365 | DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); ) |
| 366 | blob_copy_lines(pOut, pV1, aC1[i1+2]); |
| 367 | }else if( i2<limit2 && aC2[i2+2]>0 ){ |
| 368 | DEBUG( printf("INSERT +%d right\n", aC2[i2+2]); ) |
| 369 | blob_copy_lines(pOut, pV2, aC2[i2+2]); |
| 370 | } |
| 371 | |
| 372 | free(aC1); |
| 373 | free(aC2); |
| 374 | return nConflict; |
| 375 | } |
| @@ -384,11 +886,12 @@ | |
| 384 | const char *z = blob_buffer(p); |
| 385 | int n = blob_size(p) - len + 1; |
| 386 | assert( len==(int)strlen(mergeMarker[1]) ); |
| 387 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 388 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 389 | assert( count(mergeMarker)==4 ); |
| 390 | for(i=0; i<n; ){ |
| 391 | for(j=0; j<4; j++){ |
| 392 | if( (memcmp(&z[i], mergeMarker[j], len)==0) ){ |
| 393 | return 1; |
| 394 | } |
| @@ -408,18 +911,106 @@ | |
| 408 | blob_read_from_file(&file, zFullpath, ExtFILE); |
| 409 | rc = contains_merge_marker(&file); |
| 410 | blob_reset(&file); |
| 411 | return rc; |
| 412 | } |
| 413 | |
| 414 | /* |
| 415 | ** COMMAND: 3-way-merge* |
| 416 | ** |
| 417 | ** Usage: %fossil 3-way-merge BASELINE V1 V2 MERGED |
| 418 | ** |
| 419 | ** Inputs are files BASELINE, V1, and V2. The file MERGED is generated |
| 420 | ** as output. |
| 421 | ** |
| 422 | ** BASELINE is a common ancestor of two files V1 and V2 that have diverging |
| 423 | ** edits. The generated output file MERGED is the combination of all |
| 424 | ** changes in both V1 and V2. |
| 425 | ** |
| @@ -436,38 +1027,75 @@ | |
| 436 | ** cp Xup.c Xbase.c |
| 437 | ** # Verify that everything still works |
| 438 | ** fossil commit |
| 439 | ** |
| 440 | */ |
| 441 | void delta_3waymerge_cmd(void){ |
| 442 | Blob pivot, v1, v2, merged; |
| 443 | int nConflict; |
| 444 | |
| 445 | /* We should be done with options.. */ |
| 446 | verify_all_options(); |
| 447 | |
| 448 | if( g.argc!=6 ){ |
| 449 | usage("PIVOT V1 V2 MERGED"); |
| 450 | } |
| 451 | if( blob_read_from_file(&pivot, g.argv[2], ExtFILE)<0 ){ |
| 452 | fossil_fatal("cannot read %s", g.argv[2]); |
| 453 | } |
| 454 | if( blob_read_from_file(&v1, g.argv[3], ExtFILE)<0 ){ |
| 455 | fossil_fatal("cannot read %s", g.argv[3]); |
| 456 | } |
| 457 | if( blob_read_from_file(&v2, g.argv[4], ExtFILE)<0 ){ |
| 458 | fossil_fatal("cannot read %s", g.argv[4]); |
| 459 | } |
| 460 | nConflict = blob_merge(&pivot, &v1, &v2, &merged); |
| 461 | if( blob_write_to_file(&merged, g.argv[5])<(int)blob_size(&merged) ){ |
| 462 | fossil_fatal("cannot write %s", g.argv[4]); |
| 463 | } |
| 464 | blob_reset(&pivot); |
| 465 | blob_reset(&v1); |
| 466 | blob_reset(&v2); |
| 467 | blob_reset(&merged); |
| 468 | if( nConflict>0 ) fossil_warning("WARNING: %d merge conflicts", nConflict); |
| 469 | } |
| 470 | |
| 471 | /* |
| 472 | ** aSubst is an array of string pairs. The first element of each pair is |
| 473 | ** a string that begins with %. The second element is a replacement for that |
| @@ -505,32 +1133,32 @@ | |
| 505 | |
| 506 | #if INTERFACE |
| 507 | /* |
| 508 | ** Flags to the 3-way merger |
| 509 | */ |
| 510 | #define MERGE_DRYRUN 0x0001 |
| 511 | /* |
| 512 | ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain |
| 513 | ** its temporary files on error. By default they are removed after the |
| 514 | ** merge, regardless of success or failure. |
| 515 | */ |
| 516 | #define MERGE_KEEP_FILES 0x0002 |
| 517 | #endif |
| 518 | |
| 519 | |
| 520 | /* |
| 521 | ** This routine is a wrapper around blob_merge() with the following |
| 522 | ** enhancements: |
| 523 | ** |
| 524 | ** (1) If the merge-command is defined, then use the external merging |
| 525 | ** program specified instead of the built-in blob-merge to do the |
| 526 | ** merging. Panic if the external merger fails. |
| 527 | ** ** Not currently implemented ** |
| 528 | ** |
| 529 | ** (2) If gmerge-command is defined and there are merge conflicts in |
| 530 | ** blob_merge() then invoke the external graphical merger to resolve |
| 531 | ** the conflicts. |
| 532 | ** |
| 533 | ** (3) If a merge conflict occurs and gmerge-command is not defined, |
| 534 | ** then write the pivot, original, and merge-in files to the |
| 535 | ** filesystem. |
| 536 | */ |
| @@ -539,30 +1167,37 @@ | |
| 539 | const char *zV1, /* Name of file for version merging into (mine) */ |
| 540 | Blob *pV2, /* Version merging from (yours) */ |
| 541 | Blob *pOut, /* Output written here */ |
| 542 | unsigned mergeFlags /* Flags that control operation */ |
| 543 | ){ |
| 544 | Blob v1; /* Content of zV1 */ |
| 545 | int rc; /* Return code of subroutines and this routine */ |
| 546 | const char *zGMerge; /* Name of the gmerge command */ |
| 547 | |
| 548 | blob_read_from_file(&v1, zV1, ExtFILE); |
| 549 | rc = blob_merge(pPivot, &v1, pV2, pOut); |
| 550 | zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); |
| 551 | if( (mergeFlags & MERGE_DRYRUN)==0 |
| 552 | && ((zGMerge!=0 && zGMerge[0]!=0) |
| 553 | || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ |
| 554 | char *zPivot; /* Name of the pivot file */ |
| 555 | char *zOrig; /* Name of the original content file */ |
| 556 | char *zOther; /* Name of the merge file */ |
| 557 | |
| 558 | zPivot = file_newname(zV1, "baseline", 1); |
| 559 | blob_write_to_file(pPivot, zPivot); |
| 560 | zOrig = file_newname(zV1, "original", 1); |
| 561 | blob_write_to_file(&v1, zOrig); |
| 562 | zOther = file_newname(zV1, "merge", 1); |
| 563 | blob_write_to_file(pV2, zOther); |
| 564 | if( rc>0 ){ |
| 565 | if( zGMerge && zGMerge[0] ){ |
| 566 | char *zOut; /* Temporary output file */ |
| 567 | char *zCmd; /* Command to invoke */ |
| 568 | const char *azSubst[8]; /* Strings to be substituted */ |
| @@ -590,7 +1225,8 @@ | |
| 590 | fossil_free(zPivot); |
| 591 | fossil_free(zOrig); |
| 592 | fossil_free(zOther); |
| 593 | } |
| 594 | blob_reset(&v1); |
| 595 | return rc; |
| 596 | } |
| 597 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -75,75 +75,17 @@ | |
| 75 | if( aC1[2]!=aC2[2] ) return 0; |
| 76 | if( sameLines(pV1, pV2, aC1[2]) ) return 1; |
| 77 | return 0; |
| 78 | } |
| 79 | |
| 80 | /* |
| 81 | ** Text of boundary markers for merge conflicts. |
| 82 | */ |
| 83 | static const char *const mergeMarker[] = { |
| 84 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 85 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<", |
| 86 | "####### SUGGESTED CONFLICT RESOLUTION follows ###################", |
| 87 | "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||", |
| 88 | "======= MERGED IN content follows ===============================", |
| 89 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" |
| 90 | }; |
| 91 | |
| @@ -186,10 +128,615 @@ | |
| 128 | ensure_line_end(pOut, useCrLf); |
| 129 | blob_append(pOut, mergeMarker[iMark], -1); |
| 130 | if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); |
| 131 | ensure_line_end(pOut, useCrLf); |
| 132 | } |
| 133 | |
| 134 | #if INTERFACE |
| 135 | /* |
| 136 | ** This is an abstract class for constructing a merge. |
| 137 | ** Subclasses of this object format the merge output in different ways. |
| 138 | ** |
| 139 | ** To subclass, create an instance of the MergeBuilder object and fill |
| 140 | ** in appropriate method implementations. |
| 141 | */ |
| 142 | struct MergeBuilder { |
| 143 | void (*xStart)(MergeBuilder*); |
| 144 | void (*xSame)(MergeBuilder*, unsigned int); |
| 145 | void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int); |
| 146 | void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int); |
| 147 | void (*xChngBoth)(MergeBuilder*, unsigned int, unsigned int); |
| 148 | void (*xConflict)(MergeBuilder*, unsigned int, unsigned int, unsigned int); |
| 149 | void (*xEnd)(MergeBuilder*); |
| 150 | void (*xDestroy)(MergeBuilder*); |
| 151 | const char *zPivot; /* Label or name for the pivot */ |
| 152 | const char *zV1; /* Label or name for the V1 file */ |
| 153 | const char *zV2; /* Label or name for the V2 file */ |
| 154 | const char *zOut; /* Label or name for the output */ |
| 155 | Blob *pPivot; /* The common ancestor */ |
| 156 | Blob *pV1; /* First variant (local copy) */ |
| 157 | Blob *pV2; /* Second variant (merged in) */ |
| 158 | Blob *pOut; /* Write merge results here */ |
| 159 | int useCrLf; /* Use CRLF line endings */ |
| 160 | int nContext; /* Size of unchanged line boundaries */ |
| 161 | unsigned int mxPivot; /* Number of lines in the pivot */ |
| 162 | unsigned int mxV1; /* Number of lines in V1 */ |
| 163 | unsigned int mxV2; /* Number of lines in V2 */ |
| 164 | unsigned int lnPivot; /* Lines read from pivot */ |
| 165 | unsigned int lnV1; /* Lines read from v1 */ |
| 166 | unsigned int lnV2; /* Lines read from v2 */ |
| 167 | unsigned int lnOut; /* Lines written to out */ |
| 168 | unsigned int nConflict; /* Number of conflicts seen */ |
| 169 | u64 diffFlags; /* Flags for difference engine */ |
| 170 | }; |
| 171 | #endif /* INTERFACE */ |
| 172 | |
| 173 | |
| 174 | /************************* Generic MergeBuilder ******************************/ |
| 175 | /* These are generic methods for MergeBuilder. They just output debugging |
| 176 | ** information. But some of them are useful as base methods for other useful |
| 177 | ** implementations of MergeBuilder. |
| 178 | */ |
| 179 | |
| 180 | /* xStart() and xEnd() are called to generate header and fotter information |
| 181 | ** in the output. This is a no-op in the generic implementation. |
| 182 | */ |
| 183 | static void dbgStartEnd(MergeBuilder *p){ (void)p; } |
| 184 | |
| 185 | /* The next N lines of PIVOT are unchanged in both V1 and V2 |
| 186 | */ |
| 187 | static void dbgSame(MergeBuilder *p, unsigned int N){ |
| 188 | blob_appendf(p->pOut, |
| 189 | "COPY %u from BASELINE(%u..%u) or V1(%u..%u) or V2(%u..%u)\n", |
| 190 | N, p->lnPivot+1, p->lnPivot+N, p->lnV1+1, p->lnV1+N, |
| 191 | p->lnV2+1, p->lnV2+N); |
| 192 | p->lnPivot += N; |
| 193 | p->lnV1 += N; |
| 194 | p->lnV2 += N; |
| 195 | } |
| 196 | |
| 197 | /* The next nPivot lines of the PIVOT are changed into nV1 lines by V1 |
| 198 | */ |
| 199 | static void dbgChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ |
| 200 | blob_appendf(p->pOut, "COPY %u from V1(%u..%u)\n", |
| 201 | nV1, p->lnV1+1, p->lnV1+nV1); |
| 202 | p->lnPivot += nPivot; |
| 203 | p->lnV2 += nPivot; |
| 204 | p->lnV1 += nV1; |
| 205 | } |
| 206 | |
| 207 | /* The next nPivot lines of the PIVOT are changed into nV2 lines by V2 |
| 208 | */ |
| 209 | static void dbgChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ |
| 210 | blob_appendf(p->pOut, "COPY %u lines FROM V2(%u..%u)\n", |
| 211 | nV2, p->lnV2+1, p->lnV2+nV2); |
| 212 | p->lnPivot += nPivot; |
| 213 | p->lnV1 += nPivot; |
| 214 | p->lnV2 += nV2; |
| 215 | } |
| 216 | |
| 217 | /* The next nPivot lines of the PIVOT are changed into nV lines from V1 and |
| 218 | ** V2, which should be the same. In other words, the same change is found |
| 219 | ** in both V1 and V2. |
| 220 | */ |
| 221 | static void dbgChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ |
| 222 | blob_appendf(p->pOut, "COPY %u lines from V1(%u..%u) or V2(%u..%u)\n", |
| 223 | nV, p->lnV1+1, p->lnV1+nV, p->lnV2+1, p->lnV2+nV); |
| 224 | p->lnPivot += nPivot; |
| 225 | p->lnV1 += nV; |
| 226 | p->lnV2 += nV; |
| 227 | } |
| 228 | |
| 229 | /* V1 and V2 have different and overlapping changes. The next nPivot lines |
| 230 | ** of the PIVOT are converted into nV1 lines of V1 and nV2 lines of V2. |
| 231 | */ |
| 232 | static void dbgConflict( |
| 233 | MergeBuilder *p, |
| 234 | unsigned int nPivot, |
| 235 | unsigned int nV1, |
| 236 | unsigned int nV2 |
| 237 | ){ |
| 238 | blob_appendf(p->pOut, |
| 239 | "CONFLICT %u,%u,%u BASELINE(%u..%u) versus V1(%u..%u) versus V2(%u..%u)\n", |
| 240 | nPivot, nV1, nV2, |
| 241 | p->lnPivot+1, p->lnPivot+nPivot, |
| 242 | p->lnV1+1, p->lnV1+nV1, |
| 243 | p->lnV2+1, p->lnV2+nV2); |
| 244 | p->lnV1 += nV1; |
| 245 | p->lnPivot += nPivot; |
| 246 | p->lnV2 += nV2; |
| 247 | } |
| 248 | |
| 249 | /* Generic destructor for the MergeBuilder object |
| 250 | */ |
| 251 | static void dbgDestroy(MergeBuilder *p){ |
| 252 | memset(p, 0, sizeof(*p)); |
| 253 | } |
| 254 | |
| 255 | /* Generic initializer for a MergeBuilder object |
| 256 | */ |
| 257 | static void mergebuilder_init(MergeBuilder *p){ |
| 258 | memset(p, 0, sizeof(*p)); |
| 259 | p->xStart = dbgStartEnd; |
| 260 | p->xSame = dbgSame; |
| 261 | p->xChngV1 = dbgChngV1; |
| 262 | p->xChngV2 = dbgChngV2; |
| 263 | p->xChngBoth = dbgChngBoth; |
| 264 | p->xConflict = dbgConflict; |
| 265 | p->xEnd = dbgStartEnd; |
| 266 | p->xDestroy = dbgDestroy; |
| 267 | } |
| 268 | |
| 269 | /************************* MergeBuilderToken ********************************/ |
| 270 | /* This version of MergeBuilder actually performs a merge on file that |
| 271 | ** are broken up into tokens instead of lines, and puts the result in pOut. |
| 272 | */ |
| 273 | static void tokenSame(MergeBuilder *p, unsigned int N){ |
| 274 | blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N); |
| 275 | p->pPivot->iCursor += N; |
| 276 | p->pV1->iCursor += N; |
| 277 | p->pV2->iCursor += N; |
| 278 | } |
| 279 | static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){ |
| 280 | blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1); |
| 281 | p->pPivot->iCursor += nPivot; |
| 282 | p->pV1->iCursor += nV1; |
| 283 | p->pV2->iCursor += nPivot; |
| 284 | } |
| 285 | static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){ |
| 286 | blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2); |
| 287 | p->pPivot->iCursor += nPivot; |
| 288 | p->pV1->iCursor += nPivot; |
| 289 | p->pV2->iCursor += nV2; |
| 290 | } |
| 291 | static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){ |
| 292 | blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV); |
| 293 | p->pPivot->iCursor += nPivot; |
| 294 | p->pV1->iCursor += nV; |
| 295 | p->pV2->iCursor += nV; |
| 296 | } |
| 297 | static void tokenConflict( |
| 298 | MergeBuilder *p, |
| 299 | unsigned int nPivot, |
| 300 | unsigned int nV1, |
| 301 | unsigned int nV2 |
| 302 | ){ |
| 303 | /* For a token-merge conflict, use the text from the merge-in */ |
| 304 | blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2); |
| 305 | p->pPivot->iCursor += nPivot; |
| 306 | p->pV1->iCursor += nV1; |
| 307 | p->pV2->iCursor += nV2; |
| 308 | } |
| 309 | static void mergebuilder_init_token(MergeBuilder *p){ |
| 310 | mergebuilder_init(p); |
| 311 | p->xSame = tokenSame; |
| 312 | p->xChngV1 = tokenChngV1; |
| 313 | p->xChngV2 = tokenChngV2; |
| 314 | p->xChngBoth = tokenChngBoth; |
| 315 | p->xConflict = tokenConflict; |
| 316 | p->diffFlags = DIFF_BY_TOKEN; |
| 317 | } |
| 318 | |
| 319 | /* |
| 320 | ** Attempt to do a low-level merge on a conflict. The conflict is |
| 321 | ** described by the first four parameters, which are the same as the |
| 322 | ** arguments to the xConflict method of the MergeBuilder object. |
| 323 | ** This routine attempts to resolve the conflict by looking at |
| 324 | ** elements of the conflict region that are finer grain than complete |
| 325 | ** lines of text. |
| 326 | ** |
| 327 | ** The result is written into Blob pOut. pOut is initialized by this |
| 328 | ** routine. |
| 329 | */ |
| 330 | int merge_try_to_resolve_conflict( |
| 331 | MergeBuilder *pMB, /* MergeBuilder that encounter conflict */ |
| 332 | unsigned int nPivot, /* Lines of conflict in the pivot */ |
| 333 | unsigned int nV1, /* Lines of conflict in V1 */ |
| 334 | unsigned int nV2, /* Lines of conflict in V2 */ |
| 335 | Blob *pOut /* Write resolution text here */ |
| 336 | ){ |
| 337 | int nConflict; |
| 338 | MergeBuilder mb; |
| 339 | Blob pv, v1, v2; |
| 340 | mergebuilder_init_token(&mb); |
| 341 | blob_extract_lines(pMB->pPivot, nPivot, &pv); |
| 342 | blob_extract_lines(pMB->pV1, nV1, &v1); |
| 343 | blob_extract_lines(pMB->pV2, nV2, &v2); |
| 344 | blob_zero(pOut); |
| 345 | blob_materialize(&pv); |
| 346 | blob_materialize(&v1); |
| 347 | blob_materialize(&v2); |
| 348 | mb.pPivot = &pv; |
| 349 | mb.pV1 = &v1; |
| 350 | mb.pV2 = &v2; |
| 351 | mb.pOut = pOut; |
| 352 | nConflict = merge_three_blobs(&mb); |
| 353 | blob_reset(&pv); |
| 354 | blob_reset(&v1); |
| 355 | blob_reset(&v2); |
| 356 | /* mb has not allocated any resources, so we do not need to invoke |
| 357 | ** the xDestroy method. */ |
| 358 | blob_add_final_newline(pOut); |
| 359 | return nConflict; |
| 360 | } |
| 361 | |
| 362 | |
| 363 | /************************* MergeBuilderText **********************************/ |
| 364 | /* This version of MergeBuilder actually performs a merge on file and puts |
| 365 | ** the result in pOut |
| 366 | */ |
| 367 | static void txtStart(MergeBuilder *p){ |
| 368 | /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), |
| 369 | ** keep it in the output. This should be secure enough not to cause |
| 370 | ** unintended changes to the merged file and consistent with what |
| 371 | ** users are using in their source files. |
| 372 | */ |
| 373 | if( starts_with_utf8_bom(p->pV1, 0) && starts_with_utf8_bom(p->pV2, 0) ){ |
| 374 | blob_append(p->pOut, (char*)get_utf8_bom(0), -1); |
| 375 | } |
| 376 | if( contains_crlf(p->pV1) && contains_crlf(p->pV2) ){ |
| 377 | p->useCrLf = 1; |
| 378 | } |
| 379 | } |
| 380 | static void txtSame(MergeBuilder *p, unsigned int N){ |
| 381 | blob_copy_lines(p->pOut, p->pPivot, N); p->lnPivot += N; |
| 382 | blob_copy_lines(0, p->pV1, N); p->lnV1 += N; |
| 383 | blob_copy_lines(0, p->pV2, N); p->lnV2 += N; |
| 384 | } |
| 385 | static void txtChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ |
| 386 | blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot; |
| 387 | blob_copy_lines(0, p->pV2, nPivot); p->lnV2 += nPivot; |
| 388 | blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1; |
| 389 | } |
| 390 | static void txtChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ |
| 391 | blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot; |
| 392 | blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot; |
| 393 | blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2; |
| 394 | } |
| 395 | static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ |
| 396 | blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot; |
| 397 | blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV; |
| 398 | blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV; |
| 399 | } |
| 400 | static void txtConflict( |
| 401 | MergeBuilder *p, |
| 402 | unsigned int nPivot, |
| 403 | unsigned int nV1, |
| 404 | unsigned int nV2 |
| 405 | ){ |
| 406 | int nRes; /* Lines in the computed conflict resolution */ |
| 407 | Blob res; /* Text of the conflict resolution */ |
| 408 | |
| 409 | merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res); |
| 410 | nRes = blob_linecount(&res); |
| 411 | |
| 412 | append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf); |
| 413 | blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1; |
| 414 | |
| 415 | if( nRes>0 ){ |
| 416 | append_merge_mark(p->pOut, 1, 0, p->useCrLf); |
| 417 | blob_copy_lines(p->pOut, &res, nRes); |
| 418 | } |
| 419 | blob_reset(&res); |
| 420 | |
| 421 | append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf); |
| 422 | blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot; |
| 423 | |
| 424 | append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf); |
| 425 | blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2; |
| 426 | |
| 427 | append_merge_mark(p->pOut, 4, -1, p->useCrLf); |
| 428 | } |
| 429 | static void mergebuilder_init_text(MergeBuilder *p){ |
| 430 | mergebuilder_init(p); |
| 431 | p->xStart = txtStart; |
| 432 | p->xSame = txtSame; |
| 433 | p->xChngV1 = txtChngV1; |
| 434 | p->xChngV2 = txtChngV2; |
| 435 | p->xChngBoth = txtChngBoth; |
| 436 | p->xConflict = txtConflict; |
| 437 | } |
| 438 | |
| 439 | /************************* MergeBuilderTcl **********************************/ |
| 440 | /* Generate merge output formatted for reading by a TCL script. |
| 441 | ** |
| 442 | ** The output consists of lines of text, each with 4 tokens. The tokens |
| 443 | ** represent the content for one line from baseline, v1, v2, and output |
| 444 | ** respectively. The first character of each token provides auxiliary |
| 445 | ** information: |
| 446 | ** |
| 447 | ** . This line is omitted. |
| 448 | ** N Name of the file. |
| 449 | ** T Literal text follows that should have a \n terminator. |
| 450 | ** R Literal text follows that needs a \r\n terminator. |
| 451 | ** X Merge conflict. |
| 452 | ** Z Literal text without a line terminator. |
| 453 | ** S Skipped lines. Followed by number of lines to skip. |
| 454 | ** 1 Text is a copy of token 1 |
| 455 | ** 2 Use data from data-token 2 |
| 456 | ** 3 Use data from data-token 3 |
| 457 | */ |
| 458 | |
| 459 | /* Write text that goes into the interior of a double-quoted string in TCL */ |
| 460 | static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){ |
| 461 | int j; |
| 462 | for(j=0; j<nIn; j++){ |
| 463 | char c = zIn[j]; |
| 464 | if( c=='\\' ){ |
| 465 | blob_append(pOut, "\\\\", 2); |
| 466 | }else if( c=='"' ){ |
| 467 | blob_append(pOut, "\\\"", 2); |
| 468 | }else if( c<' ' || c>0x7e ){ |
| 469 | char z[5]; |
| 470 | z[0] = '\\'; |
| 471 | z[1] = "01234567"[(c>>6)&0x3]; |
| 472 | z[2] = "01234567"[(c>>3)&0x7]; |
| 473 | z[3] = "01234567"[c&0x7]; |
| 474 | z[4] = 0; |
| 475 | blob_append(pOut, z, 4); |
| 476 | }else{ |
| 477 | blob_append_char(pOut, c); |
| 478 | } |
| 479 | } |
| 480 | } |
| 481 | |
| 482 | /* Copy one line of text from pIn and append to pOut, encoded as TCL */ |
| 483 | static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){ |
| 484 | int i, k; |
| 485 | for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){} |
| 486 | if( i==pIn->nUsed ){ |
| 487 | k = i; |
| 488 | }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){ |
| 489 | k = i-1; |
| 490 | i++; |
| 491 | }else{ |
| 492 | k = i; |
| 493 | i++; |
| 494 | } |
| 495 | blob_append_char(pOut, '"'); |
| 496 | blob_append_char(pOut, cType); |
| 497 | tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor); |
| 498 | pIn->iCursor = i; |
| 499 | blob_append_char(pOut, '"'); |
| 500 | } |
| 501 | static void tclStart(MergeBuilder *p){ |
| 502 | Blob *pOut = p->pOut; |
| 503 | blob_append(pOut, "\"N", 2); |
| 504 | tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot)); |
| 505 | blob_append(pOut, "\" \"N", 4); |
| 506 | tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1)); |
| 507 | blob_append(pOut, "\" \"N", 4); |
| 508 | tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2)); |
| 509 | blob_append(pOut, "\" \"N", 4); |
| 510 | if( p->zOut ){ |
| 511 | tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut)); |
| 512 | }else{ |
| 513 | blob_append(pOut, "(Merge Result)", -1); |
| 514 | } |
| 515 | blob_append(pOut, "\"\n", 2); |
| 516 | } |
| 517 | static void tclSame(MergeBuilder *p, unsigned int N){ |
| 518 | int i = 0; |
| 519 | int nSkip; |
| 520 | |
| 521 | if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){ |
| 522 | while( i<N && i<p->nContext ){ |
| 523 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 524 | blob_append(p->pOut, " 1 1 1\n", 7); |
| 525 | i++; |
| 526 | } |
| 527 | nSkip = N - p->nContext*2; |
| 528 | }else{ |
| 529 | nSkip = N - p->nContext; |
| 530 | } |
| 531 | if( nSkip>0 ){ |
| 532 | blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n", |
| 533 | nSkip, nSkip, nSkip, nSkip); |
| 534 | blob_copy_lines(0, p->pPivot, nSkip); |
| 535 | i += nSkip; |
| 536 | } |
| 537 | |
| 538 | p->lnPivot += N; |
| 539 | p->lnV1 += N; |
| 540 | p->lnV2 += N; |
| 541 | |
| 542 | if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){ |
| 543 | while( i<N ){ |
| 544 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 545 | blob_append(p->pOut, " 1 1 1\n", 7); |
| 546 | i++; |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | blob_copy_lines(0, p->pV1, N); |
| 551 | blob_copy_lines(0, p->pV2, N); |
| 552 | } |
| 553 | static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ |
| 554 | int i; |
| 555 | for(i=0; i<nPivot && i<nV1; i++){ |
| 556 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 557 | blob_append_char(p->pOut, ' '); |
| 558 | tclLineOfText(p->pOut, p->pV1, 'T'); |
| 559 | blob_append(p->pOut, " 1 2\n", 5); |
| 560 | } |
| 561 | while( i<nPivot ){ |
| 562 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 563 | blob_append(p->pOut, " . 1 .\n", 7); |
| 564 | i++; |
| 565 | } |
| 566 | while( i<nV1 ){ |
| 567 | blob_append(p->pOut, ". ", 2); |
| 568 | tclLineOfText(p->pOut, p->pV1, 'T'); |
| 569 | blob_append(p->pOut, " . 2\n", 5); |
| 570 | i++; |
| 571 | } |
| 572 | p->lnPivot += nPivot; |
| 573 | p->lnV1 += nV1; |
| 574 | p->lnV2 += nPivot; |
| 575 | blob_copy_lines(0, p->pV2, nPivot); |
| 576 | } |
| 577 | static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ |
| 578 | int i; |
| 579 | for(i=0; i<nPivot && i<nV2; i++){ |
| 580 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 581 | blob_append(p->pOut, " 1 ", 3); |
| 582 | tclLineOfText(p->pOut, p->pV2, 'T'); |
| 583 | blob_append(p->pOut, " 3\n", 3); |
| 584 | } |
| 585 | while( i<nPivot ){ |
| 586 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 587 | blob_append(p->pOut, " 1 . .\n", 7); |
| 588 | i++; |
| 589 | } |
| 590 | while( i<nV2 ){ |
| 591 | blob_append(p->pOut, ". . ", 4); |
| 592 | tclLineOfText(p->pOut, p->pV2, 'T'); |
| 593 | blob_append(p->pOut, " 3\n", 3); |
| 594 | i++; |
| 595 | } |
| 596 | p->lnPivot += nPivot; |
| 597 | p->lnV1 += nPivot; |
| 598 | p->lnV2 += nV2; |
| 599 | blob_copy_lines(0, p->pV1, nPivot); |
| 600 | } |
| 601 | static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ |
| 602 | int i; |
| 603 | for(i=0; i<nPivot && i<nV; i++){ |
| 604 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 605 | blob_append_char(p->pOut, ' '); |
| 606 | tclLineOfText(p->pOut, p->pV1, 'T'); |
| 607 | blob_append(p->pOut, " 2 2\n", 5); |
| 608 | } |
| 609 | while( i<nPivot ){ |
| 610 | tclLineOfText(p->pOut, p->pPivot, 'T'); |
| 611 | blob_append(p->pOut, " . . .\n", 7); |
| 612 | i++; |
| 613 | } |
| 614 | while( i<nV ){ |
| 615 | blob_append(p->pOut, ". ", 2); |
| 616 | tclLineOfText(p->pOut, p->pV1, 'T'); |
| 617 | blob_append(p->pOut, " 2 2\n", 5); |
| 618 | i++; |
| 619 | } |
| 620 | p->lnPivot += nPivot; |
| 621 | p->lnV1 += nV; |
| 622 | p->lnV2 += nV; |
| 623 | blob_copy_lines(0, p->pV2, nV); |
| 624 | } |
| 625 | static void tclConflict( |
| 626 | MergeBuilder *p, |
| 627 | unsigned int nPivot, |
| 628 | unsigned int nV1, |
| 629 | unsigned int nV2 |
| 630 | ){ |
| 631 | int mx = nPivot; |
| 632 | int i; |
| 633 | int nRes; |
| 634 | Blob res; |
| 635 | |
| 636 | merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res); |
| 637 | nRes = blob_linecount(&res); |
| 638 | if( nV1>mx ) mx = nV1; |
| 639 | if( nV2>mx ) mx = nV2; |
| 640 | if( nRes>mx ) mx = nRes; |
| 641 | if( nRes>0 ){ |
| 642 | blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2); |
| 643 | } |
| 644 | for(i=0; i<mx; i++){ |
| 645 | if( i<nPivot ){ |
| 646 | tclLineOfText(p->pOut, p->pPivot, 'X'); |
| 647 | }else{ |
| 648 | blob_append_char(p->pOut, '.'); |
| 649 | } |
| 650 | blob_append_char(p->pOut, ' '); |
| 651 | if( i<nV1 ){ |
| 652 | tclLineOfText(p->pOut, p->pV1, 'X'); |
| 653 | }else{ |
| 654 | blob_append_char(p->pOut, '.'); |
| 655 | } |
| 656 | blob_append_char(p->pOut, ' '); |
| 657 | if( i<nV2 ){ |
| 658 | tclLineOfText(p->pOut, p->pV2, 'X'); |
| 659 | }else{ |
| 660 | blob_append_char(p->pOut, '.'); |
| 661 | } |
| 662 | if( i<nRes ){ |
| 663 | blob_append_char(p->pOut, ' '); |
| 664 | tclLineOfText(p->pOut, &res, 'X'); |
| 665 | blob_append_char(p->pOut, '\n'); |
| 666 | }else{ |
| 667 | blob_append(p->pOut, " .\n", 3); |
| 668 | } |
| 669 | if( i==mx-1 ){ |
| 670 | blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3); |
| 671 | } |
| 672 | } |
| 673 | blob_reset(&res); |
| 674 | p->lnPivot += nPivot; |
| 675 | p->lnV1 += nV1; |
| 676 | p->lnV2 += nV2; |
| 677 | } |
| 678 | void mergebuilder_init_tcl(MergeBuilder *p){ |
| 679 | mergebuilder_init(p); |
| 680 | p->xStart = tclStart; |
| 681 | p->xSame = tclSame; |
| 682 | p->xChngV1 = tclChngV1; |
| 683 | p->xChngV2 = tclChngV2; |
| 684 | p->xChngBoth = tclChngBoth; |
| 685 | p->xConflict = tclConflict; |
| 686 | } |
| 687 | /*****************************************************************************/ |
| 688 | |
| 689 | /* |
| 690 | ** The aC[] array contains triples of integers. Within each triple, the |
| 691 | ** elements are: |
| 692 | ** |
| 693 | ** (0) The number of lines to copy |
| 694 | ** (1) The number of lines to delete |
| 695 | ** (2) The number of liens to insert |
| 696 | ** |
| 697 | ** Suppose we want to advance over sz lines of the original file. This routine |
| 698 | ** returns true if that advance would land us on a copy operation. It |
| 699 | ** returns false if the advance would end on a delete. |
| 700 | */ |
| 701 | static int ends_with_copy(int *aC, int sz){ |
| 702 | while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ |
| 703 | if( aC[0]>=sz ) return 1; |
| 704 | sz -= aC[0]; |
| 705 | if( aC[1]>sz ) return 0; |
| 706 | sz -= aC[1]; |
| 707 | aC += 3; |
| 708 | } |
| 709 | return 1; |
| 710 | } |
| 711 | |
| 712 | /* |
| 713 | ** aC[] is an "edit triple" for changes from A to B. Advance through |
| 714 | ** this triple to determine the number of lines to bypass on B in order |
| 715 | ** to match an advance of sz lines on A. |
| 716 | */ |
| 717 | static int skip_conflict( |
| 718 | int *aC, /* Array of integer triples describing the edit */ |
| 719 | int i, /* Index in aC[] of current location */ |
| 720 | int sz, /* Lines of A that have been skipped */ |
| 721 | unsigned int *pLn /* OUT: Lines of B to skip to keep aligment with A */ |
| 722 | ){ |
| 723 | *pLn = 0; |
| 724 | while( sz>0 ){ |
| 725 | if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; |
| 726 | if( aC[i]>=sz ){ |
| 727 | aC[i] -= sz; |
| 728 | *pLn += sz; |
| 729 | break; |
| 730 | } |
| 731 | *pLn += aC[i]; |
| 732 | *pLn += aC[i+2]; |
| 733 | sz -= aC[i] + aC[i+1]; |
| 734 | i += 3; |
| 735 | } |
| 736 | return i; |
| 737 | } |
| 738 | |
| 739 | /* |
| 740 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 741 | ** |
| 742 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -199,156 +746,113 @@ | |
| 746 | ** The return is 0 upon complete success. If any input file is binary, |
| 747 | ** -1 is returned and pOut is unmodified. If there are merge |
| 748 | ** conflicts, the merge proceeds as best as it can and the number |
| 749 | ** of conflicts is returns |
| 750 | */ |
| 751 | int merge_three_blobs(MergeBuilder *p){ |
| 752 | int *aC1; /* Changes from pPivot to pV1 */ |
| 753 | int *aC2; /* Changes from pPivot to pV2 */ |
| 754 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 755 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 756 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 757 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 758 | DiffConfig DCfg; |
| 759 | |
| 760 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 761 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 762 | ** an array of integer triples. Within each triple, the first integer |
| 763 | ** is the number of lines of text to copy directly from the pivot, |
| 764 | ** the second integer is the number of lines of text to omit from the |
| 765 | ** pivot, and the third integer is the number of lines of text that are |
| 766 | ** inserted. The edit array ends with a triple of 0,0,0. |
| 767 | */ |
| 768 | diff_config_init(&DCfg, 0); |
| 769 | DCfg.diffFlags = p->diffFlags; |
| 770 | aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg); |
| 771 | aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg); |
| 772 | if( aC1==0 || aC2==0 ){ |
| 773 | free(aC1); |
| 774 | free(aC2); |
| 775 | return -1; |
| 776 | } |
| 777 | |
| 778 | blob_rewind(p->pV1); /* Rewind inputs: Needed to reconstruct output */ |
| 779 | blob_rewind(p->pV2); |
| 780 | blob_rewind(p->pPivot); |
| 781 | |
| 782 | /* Determine the length of the aC1[] and aC2[] change vectors */ |
| 783 | p->mxPivot = 0; |
| 784 | p->mxV1 = 0; |
| 785 | for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){ |
| 786 | p->mxPivot += aC1[i1] + aC1[i1+1]; |
| 787 | p->mxV1 += aC1[i1] + aC1[i1+2]; |
| 788 | } |
| 789 | limit1 = i1; |
| 790 | p->mxV2 = 0; |
| 791 | for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){ |
| 792 | p->mxV2 += aC2[i2] + aC2[i2+2]; |
| 793 | } |
| 794 | limit2 = i2; |
| 795 | |
| 796 | /* Output header text and do any other required initialization */ |
| 797 | p->xStart(p); |
| 798 | |
| 799 | /* Loop over the two edit vectors and use them to compute merged text |
| 800 | ** which is written into pOut. i1 and i2 are multiples of 3 which are |
| 801 | ** indices into aC1[] and aC2[] to the edit triple currently being |
| 802 | ** processed |
| 803 | */ |
| 804 | i1 = i2 = 0; |
| 805 | while( i1<limit1 && i2<limit2 ){ |
| 806 | if( aC1[i1]>0 && aC2[i2]>0 ){ |
| 807 | /* Output text that is unchanged in both V1 and V2 */ |
| 808 | nCpy = min(aC1[i1], aC2[i2]); |
| 809 | p->xSame(p, nCpy); |
| 810 | aC1[i1] -= nCpy; |
| 811 | aC2[i2] -= nCpy; |
| 812 | }else |
| 813 | if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){ |
| 814 | /* Output edits to V2 that occurs within unchanged regions of V1 */ |
| 815 | nDel = aC2[i2+1]; |
| 816 | nIns = aC2[i2+2]; |
| 817 | p->xChngV2(p, nDel, nIns); |
| 818 | aC1[i1] -= nDel; |
| 819 | i2 += 3; |
| 820 | }else |
| 821 | if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){ |
| 822 | /* Output edits to V1 that occur within unchanged regions of V2 */ |
| 823 | nDel = aC1[i1+1]; |
| 824 | nIns = aC1[i1+2]; |
| 825 | p->xChngV1(p, nDel, nIns); |
| 826 | aC2[i2] -= nDel; |
| 827 | i1 += 3; |
| 828 | }else |
| 829 | if( sameEdit(&aC1[i1], &aC2[i2], p->pV1, p->pV2) ){ |
| 830 | /* Output edits that are identical in both V1 and V2. */ |
| 831 | assert( aC1[i1]==0 ); |
| 832 | nDel = aC1[i1+1]; |
| 833 | nIns = aC1[i1+2]; |
| 834 | p->xChngBoth(p, nDel, nIns); |
| 835 | i1 += 3; |
| 836 | i2 += 3; |
| 837 | }else |
| 838 | { |
| 839 | /* We have found a region where different edits to V1 and V2 overlap. |
| 840 | ** This is a merge conflict. Find the size of the conflict, then |
| 841 | ** output both possible edits separated by distinctive marks. |
| 842 | */ |
| 843 | unsigned int sz = 1; /* Size of the conflict in the pivot, in lines */ |
| 844 | unsigned int nV1, nV2; /* Size of conflict in V1 and V2, in lines */ |
| 845 | nConflict++; |
| 846 | while( !ends_with_copy(&aC1[i1], sz) || !ends_with_copy(&aC2[i2], sz) ){ |
| 847 | sz++; |
| 848 | } |
| 849 | i1 = skip_conflict(aC1, i1, sz, &nV1); |
| 850 | i2 = skip_conflict(aC2, i2, sz, &nV2); |
| 851 | p->xConflict(p, sz, nV1, nV2); |
| 852 | } |
| 853 | |
| 854 | /* If we are finished with an edit triple, advance to the next |
| 855 | ** triple. |
| 856 | */ |
| 857 | if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3; |
| 858 | if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3; |
| @@ -356,20 +860,18 @@ | |
| 860 | |
| 861 | /* When one of the two edit vectors reaches its end, there might still |
| 862 | ** be an insert in the other edit vector. Output this remaining |
| 863 | ** insert. |
| 864 | */ |
| 865 | if( i1<limit1 && aC1[i1+2]>0 ){ |
| 866 | p->xChngV1(p, 0, aC1[i1+2]); |
| 867 | }else if( i2<limit2 && aC2[i2+2]>0 ){ |
| 868 | p->xChngV2(p, 0, aC2[i2+2]); |
| 869 | } |
| 870 | |
| 871 | /* Output footer text */ |
| 872 | p->xEnd(p); |
| 873 | |
| 874 | free(aC1); |
| 875 | free(aC2); |
| 876 | return nConflict; |
| 877 | } |
| @@ -384,11 +886,12 @@ | |
| 886 | const char *z = blob_buffer(p); |
| 887 | int n = blob_size(p) - len + 1; |
| 888 | assert( len==(int)strlen(mergeMarker[1]) ); |
| 889 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 890 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 891 | assert( len==(int)strlen(mergeMarker[4]) ); |
| 892 | assert( count(mergeMarker)==5 ); |
| 893 | for(i=0; i<n; ){ |
| 894 | for(j=0; j<4; j++){ |
| 895 | if( (memcmp(&z[i], mergeMarker[j], len)==0) ){ |
| 896 | return 1; |
| 897 | } |
| @@ -408,18 +911,106 @@ | |
| 911 | blob_read_from_file(&file, zFullpath, ExtFILE); |
| 912 | rc = contains_merge_marker(&file); |
| 913 | blob_reset(&file); |
| 914 | return rc; |
| 915 | } |
| 916 | |
| 917 | /* |
| 918 | ** Show merge output in a Tcl/Tk window, in response to the --tk option |
| 919 | ** to the "merge" or "3-way-merge" command. |
| 920 | ** |
| 921 | ** If fossil has direct access to a Tcl interpreter (either loaded |
| 922 | ** dynamically through stubs or linked in statically), we can use it |
| 923 | ** directly. Otherwise: |
| 924 | ** (1) Write the Tcl/Tk script used for rendering into a temp file. |
| 925 | ** (2) Invoke "tclsh" on the temp file using fossil_system(). |
| 926 | ** (3) Delete the temp file. |
| 927 | */ |
| 928 | void merge_tk(const char *zSubCmd, int firstArg){ |
| 929 | int i; |
| 930 | Blob script; |
| 931 | const char *zTempFile = 0; |
| 932 | char *zCmd; |
| 933 | const char *zTclsh; |
| 934 | const char *zCnt; |
| 935 | int bDarkMode = find_option("dark",0,0)!=0; |
| 936 | int nContext; |
| 937 | zCnt = find_option("context", "c", 1); |
| 938 | if( zCnt==0 ){ |
| 939 | nContext = 6; |
| 940 | }else{ |
| 941 | nContext = atoi(zCnt); |
| 942 | if( nContext<0 ) nContext = 0xfffffff; |
| 943 | } |
| 944 | blob_zero(&script); |
| 945 | blob_appendf(&script, "set ncontext %d\n", nContext); |
| 946 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl", |
| 947 | g.nameOfExe, zSubCmd); |
| 948 | find_option("tcl",0,0); |
| 949 | find_option("debug",0,0); |
| 950 | zTclsh = find_option("tclsh",0,1); |
| 951 | if( zTclsh==0 ){ |
| 952 | zTclsh = db_get("tclsh",0); |
| 953 | } |
| 954 | /* The undocumented --script FILENAME option causes the Tk script to |
| 955 | ** be written into the FILENAME instead of being run. This is used |
| 956 | ** for testing and debugging. */ |
| 957 | zTempFile = find_option("script",0,1); |
| 958 | verify_all_options(); |
| 959 | |
| 960 | if( (g.argc - firstArg)!=3 ){ |
| 961 | fossil_fatal("Requires 3 filename arguments"); |
| 962 | } |
| 963 | |
| 964 | for(i=firstArg; i<g.argc; i++){ |
| 965 | const char *z = g.argv[i]; |
| 966 | if( sqlite3_strglob("*}*",z) ){ |
| 967 | blob_appendf(&script, " {%/}", z); |
| 968 | }else{ |
| 969 | int j; |
| 970 | blob_append(&script, " ", 1); |
| 971 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 972 | } |
| 973 | } |
| 974 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 975 | blob_appendf(&script, "%s", builtin_file("merge.tcl", 0)); |
| 976 | if( zTempFile ){ |
| 977 | blob_write_to_file(&script, zTempFile); |
| 978 | fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 979 | }else{ |
| 980 | #if defined(FOSSIL_ENABLE_TCL) |
| 981 | Th_FossilInit(TH_INIT_DEFAULT); |
| 982 | if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script), |
| 983 | blob_size(&script), 1, 1, 0)==TCL_OK ){ |
| 984 | blob_reset(&script); |
| 985 | return; |
| 986 | } |
| 987 | /* |
| 988 | * If evaluation of the Tcl script fails, the reason may be that Tk |
| 989 | * could not be found by the loaded Tcl, or that Tcl cannot be loaded |
| 990 | * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback |
| 991 | * to using the external "tclsh", if available. |
| 992 | */ |
| 993 | #endif |
| 994 | zTempFile = write_blob_to_temp_file(&script); |
| 995 | zCmd = mprintf("%$ %$", zTclsh, zTempFile); |
| 996 | fossil_system(zCmd); |
| 997 | file_delete(zTempFile); |
| 998 | fossil_free(zCmd); |
| 999 | } |
| 1000 | blob_reset(&script); |
| 1001 | } |
| 1002 | |
| 1003 | |
| 1004 | /* |
| 1005 | ** COMMAND: 3-way-merge* |
| 1006 | ** |
| 1007 | ** Usage: %fossil 3-way-merge BASELINE V1 V2 [MERGED] |
| 1008 | ** |
| 1009 | ** Inputs are files BASELINE, V1, and V2. The file MERGED is generated |
| 1010 | ** as output. If no MERGED file is specified, output is sent to |
| 1011 | ** stdout. |
| 1012 | ** |
| 1013 | ** BASELINE is a common ancestor of two files V1 and V2 that have diverging |
| 1014 | ** edits. The generated output file MERGED is the combination of all |
| 1015 | ** changes in both V1 and V2. |
| 1016 | ** |
| @@ -436,38 +1027,75 @@ | |
| 1027 | ** cp Xup.c Xbase.c |
| 1028 | ** # Verify that everything still works |
| 1029 | ** fossil commit |
| 1030 | ** |
| 1031 | */ |
| 1032 | void merge_3way_cmd(void){ |
| 1033 | MergeBuilder s; |
| 1034 | int nConflict; |
| 1035 | Blob pivot, v1, v2, out; |
| 1036 | int noWarn = 0; |
| 1037 | const char *zCnt; |
| 1038 | |
| 1039 | if( find_option("tk", 0, 0)!=0 ){ |
| 1040 | merge_tk("3-way-merge", 2); |
| 1041 | return; |
| 1042 | } |
| 1043 | mergebuilder_init_text(&s); |
| 1044 | if( find_option("debug", 0, 0) ){ |
| 1045 | mergebuilder_init(&s); |
| 1046 | } |
| 1047 | if( find_option("tcl", 0, 0) ){ |
| 1048 | mergebuilder_init_tcl(&s); |
| 1049 | noWarn = 1; |
| 1050 | } |
| 1051 | zCnt = find_option("context", "c", 1); |
| 1052 | if( zCnt ){ |
| 1053 | s.nContext = atoi(zCnt); |
| 1054 | if( s.nContext<0 ) s.nContext = 0xfffffff; |
| 1055 | }else{ |
| 1056 | s.nContext = 6; |
| 1057 | } |
| 1058 | blob_zero(&pivot); s.pPivot = &pivot; |
| 1059 | blob_zero(&v1); s.pV1 = &v1; |
| 1060 | blob_zero(&v2); s.pV2 = &v2; |
| 1061 | blob_zero(&out); s.pOut = &out; |
| 1062 | |
| 1063 | /* We should be done with options.. */ |
| 1064 | verify_all_options(); |
| 1065 | |
| 1066 | if( g.argc!=6 && g.argc!=5 ){ |
| 1067 | usage("[OPTIONS] PIVOT V1 V2 [MERGED]"); |
| 1068 | } |
| 1069 | s.zPivot = file_tail(g.argv[2]); |
| 1070 | s.zV1 = file_tail(g.argv[3]); |
| 1071 | s.zV2 = file_tail(g.argv[4]); |
| 1072 | if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){ |
| 1073 | fossil_fatal("cannot read %s", g.argv[2]); |
| 1074 | } |
| 1075 | if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){ |
| 1076 | fossil_fatal("cannot read %s", g.argv[3]); |
| 1077 | } |
| 1078 | if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){ |
| 1079 | fossil_fatal("cannot read %s", g.argv[4]); |
| 1080 | } |
| 1081 | nConflict = merge_three_blobs(&s); |
| 1082 | if( g.argc==6 ){ |
| 1083 | s.zOut = file_tail(g.argv[5]); |
| 1084 | blob_write_to_file(s.pOut, g.argv[5]); |
| 1085 | }else{ |
| 1086 | s.zOut = "(Merge Result)"; |
| 1087 | blob_write_to_file(s.pOut, "-"); |
| 1088 | } |
| 1089 | s.xDestroy(&s); |
| 1090 | blob_reset(&pivot); |
| 1091 | blob_reset(&v1); |
| 1092 | blob_reset(&v2); |
| 1093 | blob_reset(&out); |
| 1094 | if( nConflict>0 && !noWarn ){ |
| 1095 | fossil_warning("WARNING: %d merge conflicts", nConflict); |
| 1096 | } |
| 1097 | } |
| 1098 | |
| 1099 | /* |
| 1100 | ** aSubst is an array of string pairs. The first element of each pair is |
| 1101 | ** a string that begins with %. The second element is a replacement for that |
| @@ -505,32 +1133,32 @@ | |
| 1133 | |
| 1134 | #if INTERFACE |
| 1135 | /* |
| 1136 | ** Flags to the 3-way merger |
| 1137 | */ |
| 1138 | #define MERGE_DRYRUN 0x0001 |
| 1139 | /* |
| 1140 | ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain |
| 1141 | ** its temporary files on error. By default they are removed after the |
| 1142 | ** merge, regardless of success or failure. |
| 1143 | */ |
| 1144 | #define MERGE_KEEP_FILES 0x0002 |
| 1145 | #endif |
| 1146 | |
| 1147 | |
| 1148 | /* |
| 1149 | ** This routine is a wrapper around merge_three_blobs() with the following |
| 1150 | ** enhancements: |
| 1151 | ** |
| 1152 | ** (1) If the merge-command is defined, then use the external merging |
| 1153 | ** program specified instead of the built-in blob-merge to do the |
| 1154 | ** merging. Panic if the external merger fails. |
| 1155 | ** ** Not currently implemented ** |
| 1156 | ** |
| 1157 | ** (2) If gmerge-command is defined and there are merge conflicts in |
| 1158 | ** merge_three_blobs() then invoke the external graphical merger |
| 1159 | ** to resolve the conflicts. |
| 1160 | ** |
| 1161 | ** (3) If a merge conflict occurs and gmerge-command is not defined, |
| 1162 | ** then write the pivot, original, and merge-in files to the |
| 1163 | ** filesystem. |
| 1164 | */ |
| @@ -539,30 +1167,37 @@ | |
| 1167 | const char *zV1, /* Name of file for version merging into (mine) */ |
| 1168 | Blob *pV2, /* Version merging from (yours) */ |
| 1169 | Blob *pOut, /* Output written here */ |
| 1170 | unsigned mergeFlags /* Flags that control operation */ |
| 1171 | ){ |
| 1172 | Blob v1; /* Content of zV1 */ |
| 1173 | int rc; /* Return code of subroutines and this routine */ |
| 1174 | const char *zGMerge; /* Name of the gmerge command */ |
| 1175 | MergeBuilder s; /* The merge state */ |
| 1176 | |
| 1177 | mergebuilder_init_text(&s); |
| 1178 | s.pPivot = pPivot; |
| 1179 | s.pV1 = &v1; |
| 1180 | s.pV2 = pV2; |
| 1181 | blob_zero(pOut); |
| 1182 | s.pOut = pOut; |
| 1183 | blob_read_from_file(s.pV1, zV1, ExtFILE); |
| 1184 | rc = merge_three_blobs(&s); |
| 1185 | zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); |
| 1186 | if( (mergeFlags & MERGE_DRYRUN)==0 |
| 1187 | && ((zGMerge!=0 && zGMerge[0]!=0) |
| 1188 | || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ |
| 1189 | char *zPivot; /* Name of the pivot file */ |
| 1190 | char *zOrig; /* Name of the original content file */ |
| 1191 | char *zOther; /* Name of the merge file */ |
| 1192 | |
| 1193 | zPivot = file_newname(zV1, "baseline", 1); |
| 1194 | blob_write_to_file(s.pPivot, zPivot); |
| 1195 | zOrig = file_newname(zV1, "original", 1); |
| 1196 | blob_write_to_file(s.pV1, zOrig); |
| 1197 | zOther = file_newname(zV1, "merge", 1); |
| 1198 | blob_write_to_file(s.pV2, zOther); |
| 1199 | if( rc>0 ){ |
| 1200 | if( zGMerge && zGMerge[0] ){ |
| 1201 | char *zOut; /* Temporary output file */ |
| 1202 | char *zCmd; /* Command to invoke */ |
| 1203 | const char *azSubst[8]; /* Strings to be substituted */ |
| @@ -590,7 +1225,8 @@ | |
| 1225 | fossil_free(zPivot); |
| 1226 | fossil_free(zOrig); |
| 1227 | fossil_free(zOther); |
| 1228 | } |
| 1229 | blob_reset(&v1); |
| 1230 | s.xDestroy(&s); |
| 1231 | return rc; |
| 1232 | } |
| 1233 |
+6
-2
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -464,18 +464,22 @@ | ||
| 464 | 464 | |
| 465 | 465 | /* Deletions */ |
| 466 | 466 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 467 | 467 | " WHERE origname IS NULL AND delta IS NULL"); |
| 468 | 468 | while( db_step(&q)==SQLITE_ROW ){ |
| 469 | - blob_append_escaped_arg(&cmd, g.nameOfExe, 1); | |
| 470 | - blob_appendf(&cmd, " rm --hard %$\n", db_column_text(&q,0)); | |
| 469 | + if( blob_size(&cmd)==0 ){ | |
| 470 | + blob_append_escaped_arg(&cmd, g.nameOfExe, 1); | |
| 471 | + blob_appendf(&cmd, " rm --hard"); | |
| 472 | + } | |
| 473 | + blob_appendf(&cmd, " %$", db_column_text(&q,0)); | |
| 471 | 474 | if( mFlags & PATCH_VERBOSE ){ |
| 472 | 475 | fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0)); |
| 473 | 476 | } |
| 474 | 477 | } |
| 475 | 478 | db_finalize(&q); |
| 476 | 479 | if( blob_size(&cmd)>0 ){ |
| 480 | + blob_appendf(&cmd, "\n"); | |
| 477 | 481 | if( mFlags & PATCH_DRYRUN ){ |
| 478 | 482 | fossil_print("%s", blob_str(&cmd)); |
| 479 | 483 | }else{ |
| 480 | 484 | int rc = fossil_unsafe_system(blob_str(&cmd)); |
| 481 | 485 | if( rc ){ |
| 482 | 486 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -464,18 +464,22 @@ | |
| 464 | |
| 465 | /* Deletions */ |
| 466 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 467 | " WHERE origname IS NULL AND delta IS NULL"); |
| 468 | while( db_step(&q)==SQLITE_ROW ){ |
| 469 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 470 | blob_appendf(&cmd, " rm --hard %$\n", db_column_text(&q,0)); |
| 471 | if( mFlags & PATCH_VERBOSE ){ |
| 472 | fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0)); |
| 473 | } |
| 474 | } |
| 475 | db_finalize(&q); |
| 476 | if( blob_size(&cmd)>0 ){ |
| 477 | if( mFlags & PATCH_DRYRUN ){ |
| 478 | fossil_print("%s", blob_str(&cmd)); |
| 479 | }else{ |
| 480 | int rc = fossil_unsafe_system(blob_str(&cmd)); |
| 481 | if( rc ){ |
| 482 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -464,18 +464,22 @@ | |
| 464 | |
| 465 | /* Deletions */ |
| 466 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 467 | " WHERE origname IS NULL AND delta IS NULL"); |
| 468 | while( db_step(&q)==SQLITE_ROW ){ |
| 469 | if( blob_size(&cmd)==0 ){ |
| 470 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 471 | blob_appendf(&cmd, " rm --hard"); |
| 472 | } |
| 473 | blob_appendf(&cmd, " %$", db_column_text(&q,0)); |
| 474 | if( mFlags & PATCH_VERBOSE ){ |
| 475 | fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0)); |
| 476 | } |
| 477 | } |
| 478 | db_finalize(&q); |
| 479 | if( blob_size(&cmd)>0 ){ |
| 480 | blob_appendf(&cmd, "\n"); |
| 481 | if( mFlags & PATCH_DRYRUN ){ |
| 482 | fossil_print("%s", blob_str(&cmd)); |
| 483 | }else{ |
| 484 | int rc = fossil_unsafe_system(blob_str(&cmd)); |
| 485 | if( rc ){ |
| 486 |
+1
| --- src/path.c | ||
| +++ src/path.c | ||
| @@ -542,10 +542,11 @@ | ||
| 542 | 542 | pChng = pAll; |
| 543 | 543 | pAll = pAll->pNext; |
| 544 | 544 | fossil_free(pChng); |
| 545 | 545 | } |
| 546 | 546 | } |
| 547 | + path_reset(); | |
| 547 | 548 | } |
| 548 | 549 | |
| 549 | 550 | /* |
| 550 | 551 | ** COMMAND: test-name-changes |
| 551 | 552 | ** |
| 552 | 553 |
| --- src/path.c | |
| +++ src/path.c | |
| @@ -542,10 +542,11 @@ | |
| 542 | pChng = pAll; |
| 543 | pAll = pAll->pNext; |
| 544 | fossil_free(pChng); |
| 545 | } |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | /* |
| 550 | ** COMMAND: test-name-changes |
| 551 | ** |
| 552 |
| --- src/path.c | |
| +++ src/path.c | |
| @@ -542,10 +542,11 @@ | |
| 542 | pChng = pAll; |
| 543 | pAll = pAll->pNext; |
| 544 | fossil_free(pChng); |
| 545 | } |
| 546 | } |
| 547 | path_reset(); |
| 548 | } |
| 549 | |
| 550 | /* |
| 551 | ** COMMAND: test-name-changes |
| 552 | ** |
| 553 |
+12
-2
| --- src/pikchrshow.c | ||
| +++ src/pikchrshow.c | ||
| @@ -207,12 +207,22 @@ | ||
| 207 | 207 | blob_append(pOut, zOut, -1); |
| 208 | 208 | if(PIKCHR_PROCESS_DIV & pikFlags){ |
| 209 | 209 | blob_append(pOut, "</div>\n", 7); |
| 210 | 210 | } |
| 211 | 211 | if(PIKCHR_PROCESS_SRC & pikFlags){ |
| 212 | - blob_appendf(pOut, "<pre class='pikchr-src'>%h</pre>\n", | |
| 213 | - blob_str(&bIn)); | |
| 212 | + static int counter = 0; | |
| 213 | + ++counter; | |
| 214 | + blob_appendf(pOut, "<div class='pikchr-src'>" | |
| 215 | + "<pre id='pikchr-src-%d'>%h</pre>" | |
| 216 | + "<span class='hidden'>" | |
| 217 | + "<a href='%R/pikchrshow?fromSession' " | |
| 218 | + "class='pikchr-src-pikchrshow' target='_new-%d' " | |
| 219 | + "data-pikchrid='pikchr-src-%d' " | |
| 220 | + "title='Open this pikchr in /pikchrshow'" | |
| 221 | + ">→ /pikchrshow</a></span>" | |
| 222 | + "</div>\n", | |
| 223 | + counter, blob_str(&bIn), counter, counter); | |
| 214 | 224 | } |
| 215 | 225 | if(PIKCHR_PROCESS_DIV & pikFlags){ |
| 216 | 226 | blob_append(pOut, "</div>\n", 7); |
| 217 | 227 | } |
| 218 | 228 | }else{ |
| 219 | 229 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -207,12 +207,22 @@ | |
| 207 | blob_append(pOut, zOut, -1); |
| 208 | if(PIKCHR_PROCESS_DIV & pikFlags){ |
| 209 | blob_append(pOut, "</div>\n", 7); |
| 210 | } |
| 211 | if(PIKCHR_PROCESS_SRC & pikFlags){ |
| 212 | blob_appendf(pOut, "<pre class='pikchr-src'>%h</pre>\n", |
| 213 | blob_str(&bIn)); |
| 214 | } |
| 215 | if(PIKCHR_PROCESS_DIV & pikFlags){ |
| 216 | blob_append(pOut, "</div>\n", 7); |
| 217 | } |
| 218 | }else{ |
| 219 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -207,12 +207,22 @@ | |
| 207 | blob_append(pOut, zOut, -1); |
| 208 | if(PIKCHR_PROCESS_DIV & pikFlags){ |
| 209 | blob_append(pOut, "</div>\n", 7); |
| 210 | } |
| 211 | if(PIKCHR_PROCESS_SRC & pikFlags){ |
| 212 | static int counter = 0; |
| 213 | ++counter; |
| 214 | blob_appendf(pOut, "<div class='pikchr-src'>" |
| 215 | "<pre id='pikchr-src-%d'>%h</pre>" |
| 216 | "<span class='hidden'>" |
| 217 | "<a href='%R/pikchrshow?fromSession' " |
| 218 | "class='pikchr-src-pikchrshow' target='_new-%d' " |
| 219 | "data-pikchrid='pikchr-src-%d' " |
| 220 | "title='Open this pikchr in /pikchrshow'" |
| 221 | ">→ /pikchrshow</a></span>" |
| 222 | "</div>\n", |
| 223 | counter, blob_str(&bIn), counter, counter); |
| 224 | } |
| 225 | if(PIKCHR_PROCESS_DIV & pikFlags){ |
| 226 | blob_append(pOut, "</div>\n", 7); |
| 227 | } |
| 228 | }else{ |
| 229 |
+9
-3
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -390,25 +390,31 @@ | ||
| 390 | 390 | } |
| 391 | 391 | manifest_disable_event_triggers(); |
| 392 | 392 | rebuild_update_schema(); |
| 393 | 393 | blob_init(&sql, 0, 0); |
| 394 | 394 | db_unprotect(PROTECT_ALL); |
| 395 | +#ifndef SQLITE_PREPARE_DONT_LOG | |
| 396 | + g.dbIgnoreErrors++; | |
| 397 | +#endif | |
| 395 | 398 | db_prepare(&q, |
| 396 | - "SELECT name FROM sqlite_schema /*scan*/" | |
| 397 | - " WHERE type='table'" | |
| 399 | + "SELECT name FROM pragma_table_list /*scan*/" | |
| 400 | + " WHERE schema='repository' AND type IN ('table','virtual')" | |
| 398 | 401 | " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," |
| 399 | 402 | "'config','shun','private','reportfmt'," |
| 400 | 403 | "'concealed','accesslog','modreq'," |
| 401 | 404 | "'purgeevent','purgeitem','unversioned'," |
| 402 | 405 | "'subscriber','pending_alert','chat')" |
| 403 | 406 | " AND name NOT GLOB 'sqlite_*'" |
| 404 | - " AND name NOT GLOB 'fx_*'" | |
| 407 | + " AND name NOT GLOB 'fx_*';" | |
| 405 | 408 | ); |
| 406 | 409 | while( db_step(&q)==SQLITE_ROW ){ |
| 407 | 410 | blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0)); |
| 408 | 411 | } |
| 409 | 412 | db_finalize(&q); |
| 413 | +#ifndef SQLITE_PREPARE_DONT_LOG | |
| 414 | + g.dbIgnoreErrors--; | |
| 415 | +#endif | |
| 410 | 416 | db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/); |
| 411 | 417 | blob_reset(&sql); |
| 412 | 418 | db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/); |
| 413 | 419 | ticket_create_table(0); |
| 414 | 420 | shun_artifacts(); |
| 415 | 421 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -390,25 +390,31 @@ | |
| 390 | } |
| 391 | manifest_disable_event_triggers(); |
| 392 | rebuild_update_schema(); |
| 393 | blob_init(&sql, 0, 0); |
| 394 | db_unprotect(PROTECT_ALL); |
| 395 | db_prepare(&q, |
| 396 | "SELECT name FROM sqlite_schema /*scan*/" |
| 397 | " WHERE type='table'" |
| 398 | " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," |
| 399 | "'config','shun','private','reportfmt'," |
| 400 | "'concealed','accesslog','modreq'," |
| 401 | "'purgeevent','purgeitem','unversioned'," |
| 402 | "'subscriber','pending_alert','chat')" |
| 403 | " AND name NOT GLOB 'sqlite_*'" |
| 404 | " AND name NOT GLOB 'fx_*'" |
| 405 | ); |
| 406 | while( db_step(&q)==SQLITE_ROW ){ |
| 407 | blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0)); |
| 408 | } |
| 409 | db_finalize(&q); |
| 410 | db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/); |
| 411 | blob_reset(&sql); |
| 412 | db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/); |
| 413 | ticket_create_table(0); |
| 414 | shun_artifacts(); |
| 415 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -390,25 +390,31 @@ | |
| 390 | } |
| 391 | manifest_disable_event_triggers(); |
| 392 | rebuild_update_schema(); |
| 393 | blob_init(&sql, 0, 0); |
| 394 | db_unprotect(PROTECT_ALL); |
| 395 | #ifndef SQLITE_PREPARE_DONT_LOG |
| 396 | g.dbIgnoreErrors++; |
| 397 | #endif |
| 398 | db_prepare(&q, |
| 399 | "SELECT name FROM pragma_table_list /*scan*/" |
| 400 | " WHERE schema='repository' AND type IN ('table','virtual')" |
| 401 | " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," |
| 402 | "'config','shun','private','reportfmt'," |
| 403 | "'concealed','accesslog','modreq'," |
| 404 | "'purgeevent','purgeitem','unversioned'," |
| 405 | "'subscriber','pending_alert','chat')" |
| 406 | " AND name NOT GLOB 'sqlite_*'" |
| 407 | " AND name NOT GLOB 'fx_*';" |
| 408 | ); |
| 409 | while( db_step(&q)==SQLITE_ROW ){ |
| 410 | blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0)); |
| 411 | } |
| 412 | db_finalize(&q); |
| 413 | #ifndef SQLITE_PREPARE_DONT_LOG |
| 414 | g.dbIgnoreErrors--; |
| 415 | #endif |
| 416 | db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/); |
| 417 | blob_reset(&sql); |
| 418 | db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/); |
| 419 | ticket_create_table(0); |
| 420 | shun_artifacts(); |
| 421 |
+4
-5
| --- src/rss.c | ||
| +++ src/rss.c | ||
| @@ -39,11 +39,10 @@ | ||
| 39 | 39 | ** |
| 40 | 40 | ** In addition, name=FILENAME filters for a specific file. This may be |
| 41 | 41 | ** combined with one of the other filters (useful for looking at a specific |
| 42 | 42 | ** branch). |
| 43 | 43 | */ |
| 44 | - | |
| 45 | 44 | void page_timeline_rss(void){ |
| 46 | 45 | Stmt q; |
| 47 | 46 | int nLine=0; |
| 48 | 47 | char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; |
| 49 | 48 | Blob bSQL; |
| @@ -79,13 +78,13 @@ | ||
| 79 | 78 | blob_zero(&bSQL); |
| 80 | 79 | blob_append_sql( &bSQL, "%s", zSQL1/*safe-for-%s*/ ); |
| 81 | 80 | |
| 82 | 81 | if( zType[0]!='a' ){ |
| 83 | 82 | if( zType[0]=='c' && !g.perm.Read ) zType = "x"; |
| 84 | - if( zType[0]=='w' && !g.perm.RdWiki ) zType = "x"; | |
| 85 | - if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x"; | |
| 86 | - if( zType[0]=='f' && !g.perm.RdForum ) zType = "x"; | |
| 83 | + else if( (zType[0]=='w' || zType[0]=='e') && !g.perm.RdWiki ) zType = "x"; | |
| 84 | + else if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x"; | |
| 85 | + else if( zType[0]=='f' && !g.perm.RdForum ) zType = "x"; | |
| 87 | 86 | blob_append_sql(&bSQL, " AND event.type=%Q", zType); |
| 88 | 87 | }else{ |
| 89 | 88 | blob_append_sql(&bSQL, " AND event.type in ("); |
| 90 | 89 | if( g.perm.Read ){ |
| 91 | 90 | blob_append_sql(&bSQL, "'ci',"); |
| @@ -92,11 +91,11 @@ | ||
| 92 | 91 | } |
| 93 | 92 | if( g.perm.RdTkt ){ |
| 94 | 93 | blob_append_sql(&bSQL, "'t',"); |
| 95 | 94 | } |
| 96 | 95 | if( g.perm.RdWiki ){ |
| 97 | - blob_append_sql(&bSQL, "'w',"); | |
| 96 | + blob_append_sql(&bSQL, "'w','e',"); | |
| 98 | 97 | } |
| 99 | 98 | if( g.perm.RdForum ){ |
| 100 | 99 | blob_append_sql(&bSQL, "'f',"); |
| 101 | 100 | } |
| 102 | 101 | blob_append_sql(&bSQL, "'x')"); |
| 103 | 102 |
| --- src/rss.c | |
| +++ src/rss.c | |
| @@ -39,11 +39,10 @@ | |
| 39 | ** |
| 40 | ** In addition, name=FILENAME filters for a specific file. This may be |
| 41 | ** combined with one of the other filters (useful for looking at a specific |
| 42 | ** branch). |
| 43 | */ |
| 44 | |
| 45 | void page_timeline_rss(void){ |
| 46 | Stmt q; |
| 47 | int nLine=0; |
| 48 | char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; |
| 49 | Blob bSQL; |
| @@ -79,13 +78,13 @@ | |
| 79 | blob_zero(&bSQL); |
| 80 | blob_append_sql( &bSQL, "%s", zSQL1/*safe-for-%s*/ ); |
| 81 | |
| 82 | if( zType[0]!='a' ){ |
| 83 | if( zType[0]=='c' && !g.perm.Read ) zType = "x"; |
| 84 | if( zType[0]=='w' && !g.perm.RdWiki ) zType = "x"; |
| 85 | if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x"; |
| 86 | if( zType[0]=='f' && !g.perm.RdForum ) zType = "x"; |
| 87 | blob_append_sql(&bSQL, " AND event.type=%Q", zType); |
| 88 | }else{ |
| 89 | blob_append_sql(&bSQL, " AND event.type in ("); |
| 90 | if( g.perm.Read ){ |
| 91 | blob_append_sql(&bSQL, "'ci',"); |
| @@ -92,11 +91,11 @@ | |
| 92 | } |
| 93 | if( g.perm.RdTkt ){ |
| 94 | blob_append_sql(&bSQL, "'t',"); |
| 95 | } |
| 96 | if( g.perm.RdWiki ){ |
| 97 | blob_append_sql(&bSQL, "'w',"); |
| 98 | } |
| 99 | if( g.perm.RdForum ){ |
| 100 | blob_append_sql(&bSQL, "'f',"); |
| 101 | } |
| 102 | blob_append_sql(&bSQL, "'x')"); |
| 103 |
| --- src/rss.c | |
| +++ src/rss.c | |
| @@ -39,11 +39,10 @@ | |
| 39 | ** |
| 40 | ** In addition, name=FILENAME filters for a specific file. This may be |
| 41 | ** combined with one of the other filters (useful for looking at a specific |
| 42 | ** branch). |
| 43 | */ |
| 44 | void page_timeline_rss(void){ |
| 45 | Stmt q; |
| 46 | int nLine=0; |
| 47 | char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; |
| 48 | Blob bSQL; |
| @@ -79,13 +78,13 @@ | |
| 78 | blob_zero(&bSQL); |
| 79 | blob_append_sql( &bSQL, "%s", zSQL1/*safe-for-%s*/ ); |
| 80 | |
| 81 | if( zType[0]!='a' ){ |
| 82 | if( zType[0]=='c' && !g.perm.Read ) zType = "x"; |
| 83 | else if( (zType[0]=='w' || zType[0]=='e') && !g.perm.RdWiki ) zType = "x"; |
| 84 | else if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x"; |
| 85 | else if( zType[0]=='f' && !g.perm.RdForum ) zType = "x"; |
| 86 | blob_append_sql(&bSQL, " AND event.type=%Q", zType); |
| 87 | }else{ |
| 88 | blob_append_sql(&bSQL, " AND event.type in ("); |
| 89 | if( g.perm.Read ){ |
| 90 | blob_append_sql(&bSQL, "'ci',"); |
| @@ -92,11 +91,11 @@ | |
| 91 | } |
| 92 | if( g.perm.RdTkt ){ |
| 93 | blob_append_sql(&bSQL, "'t',"); |
| 94 | } |
| 95 | if( g.perm.RdWiki ){ |
| 96 | blob_append_sql(&bSQL, "'w','e',"); |
| 97 | } |
| 98 | if( g.perm.RdForum ){ |
| 99 | blob_append_sql(&bSQL, "'f',"); |
| 100 | } |
| 101 | blob_append_sql(&bSQL, "'x')"); |
| 102 |
+7
-7
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -28,11 +28,11 @@ | ||
| 28 | 28 | @ -- ~/.fossil file and that stores information about the users setup. |
| 29 | 29 | @ -- |
| 30 | 30 | @ CREATE TABLE global_config( |
| 31 | 31 | @ name TEXT PRIMARY KEY, |
| 32 | 32 | @ value TEXT |
| 33 | -@ ); | |
| 33 | +@ ) WITHOUT ROWID; | |
| 34 | 34 | @ |
| 35 | 35 | @ -- Identifier for this file type. |
| 36 | 36 | @ -- The integer is the same as 'FSLG'. |
| 37 | 37 | @ PRAGMA application_id=252006675; |
| 38 | 38 | ; |
| @@ -138,11 +138,11 @@ | ||
| 138 | 138 | @ CREATE TABLE config( |
| 139 | 139 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 140 | 140 | @ value CLOB, -- Content of the named parameter |
| 141 | 141 | @ mtime DATE, -- last modified. seconds since 1970 |
| 142 | 142 | @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
| 143 | -@ ); | |
| 143 | +@ ) WITHOUT ROWID; | |
| 144 | 144 | @ |
| 145 | 145 | @ -- Artifacts that should not be processed are identified in the |
| 146 | 146 | @ -- "shun" table. Artifacts that are control-file forgeries or |
| 147 | 147 | @ -- spam or artifacts whose contents violate administrative policy |
| 148 | 148 | @ -- can be shunned in order to prevent them from contaminating |
| @@ -151,14 +151,14 @@ | ||
| 151 | 151 | @ -- Shunned artifacts do not exist in the blob table. Hence they |
| 152 | 152 | @ -- have not artifact ID (rid) and we thus must store their full |
| 153 | 153 | @ -- UUID. |
| 154 | 154 | @ -- |
| 155 | 155 | @ CREATE TABLE shun( |
| 156 | -@ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form | |
| 156 | +@ uuid TEXT PRIMARY KEY,-- UUID of artifact to be shunned. Canonical form | |
| 157 | 157 | @ mtime DATE, -- When added. seconds since 1970 |
| 158 | 158 | @ scom TEXT -- Optional text explaining why the shun occurred |
| 159 | -@ ); | |
| 159 | +@ ) WITHOUT ROWID; | |
| 160 | 160 | @ |
| 161 | 161 | @ -- Artifacts that should not be pushed are stored in the "private" |
| 162 | 162 | @ -- table. Private artifacts are omitted from the "unclustered" and |
| 163 | 163 | @ -- "unsent" tables. |
| 164 | 164 | @ -- |
| @@ -193,11 +193,11 @@ | ||
| 193 | 193 | @ -- |
| 194 | 194 | @ CREATE TABLE concealed( |
| 195 | 195 | @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content |
| 196 | 196 | @ mtime DATE, -- Time created. Seconds since 1970 |
| 197 | 197 | @ content TEXT -- Content intended to be concealed |
| 198 | -@ ); | |
| 198 | +@ ) WITHOUT ROWID; | |
| 199 | 199 | @ |
| 200 | 200 | @ -- The application ID helps the unix "file" command to identify the |
| 201 | 201 | @ -- database as a fossil repository. |
| 202 | 202 | @ PRAGMA application_id=252006673; |
| 203 | 203 | ; |
| @@ -528,11 +528,11 @@ | ||
| 528 | 528 | ** The schema for the local FOSSIL database file found at the root |
| 529 | 529 | ** of every check-out. This database contains the complete state of |
| 530 | 530 | ** the check-out. See also the addendum in zLocalSchemaVmerge[]. |
| 531 | 531 | */ |
| 532 | 532 | const char zLocalSchema[] = |
| 533 | -@ -- The VVAR table holds miscellanous information about the local database | |
| 533 | +@ -- The VVAR table holds miscellanous information about the local checkout | |
| 534 | 534 | @ -- in the form of name-value pairs. This is similar to the VAR table |
| 535 | 535 | @ -- table in the repository except that this table holds information that |
| 536 | 536 | @ -- is specific to the local check-out. |
| 537 | 537 | @ -- |
| 538 | 538 | @ -- Important Variables: |
| @@ -542,11 +542,11 @@ | ||
| 542 | 542 | @ -- |
| 543 | 543 | @ CREATE TABLE vvar( |
| 544 | 544 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 545 | 545 | @ value CLOB, -- Content of the named parameter |
| 546 | 546 | @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
| 547 | -@ ); | |
| 547 | +@ ) WITHOUT ROWID; | |
| 548 | 548 | @ |
| 549 | 549 | @ -- Each entry in the vfile table represents a single file in the |
| 550 | 550 | @ -- current check-out. |
| 551 | 551 | @ -- |
| 552 | 552 | @ -- The file.rid field is 0 for files or folders that have been |
| 553 | 553 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -28,11 +28,11 @@ | |
| 28 | @ -- ~/.fossil file and that stores information about the users setup. |
| 29 | @ -- |
| 30 | @ CREATE TABLE global_config( |
| 31 | @ name TEXT PRIMARY KEY, |
| 32 | @ value TEXT |
| 33 | @ ); |
| 34 | @ |
| 35 | @ -- Identifier for this file type. |
| 36 | @ -- The integer is the same as 'FSLG'. |
| 37 | @ PRAGMA application_id=252006675; |
| 38 | ; |
| @@ -138,11 +138,11 @@ | |
| 138 | @ CREATE TABLE config( |
| 139 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 140 | @ value CLOB, -- Content of the named parameter |
| 141 | @ mtime DATE, -- last modified. seconds since 1970 |
| 142 | @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
| 143 | @ ); |
| 144 | @ |
| 145 | @ -- Artifacts that should not be processed are identified in the |
| 146 | @ -- "shun" table. Artifacts that are control-file forgeries or |
| 147 | @ -- spam or artifacts whose contents violate administrative policy |
| 148 | @ -- can be shunned in order to prevent them from contaminating |
| @@ -151,14 +151,14 @@ | |
| 151 | @ -- Shunned artifacts do not exist in the blob table. Hence they |
| 152 | @ -- have not artifact ID (rid) and we thus must store their full |
| 153 | @ -- UUID. |
| 154 | @ -- |
| 155 | @ CREATE TABLE shun( |
| 156 | @ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form |
| 157 | @ mtime DATE, -- When added. seconds since 1970 |
| 158 | @ scom TEXT -- Optional text explaining why the shun occurred |
| 159 | @ ); |
| 160 | @ |
| 161 | @ -- Artifacts that should not be pushed are stored in the "private" |
| 162 | @ -- table. Private artifacts are omitted from the "unclustered" and |
| 163 | @ -- "unsent" tables. |
| 164 | @ -- |
| @@ -193,11 +193,11 @@ | |
| 193 | @ -- |
| 194 | @ CREATE TABLE concealed( |
| 195 | @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content |
| 196 | @ mtime DATE, -- Time created. Seconds since 1970 |
| 197 | @ content TEXT -- Content intended to be concealed |
| 198 | @ ); |
| 199 | @ |
| 200 | @ -- The application ID helps the unix "file" command to identify the |
| 201 | @ -- database as a fossil repository. |
| 202 | @ PRAGMA application_id=252006673; |
| 203 | ; |
| @@ -528,11 +528,11 @@ | |
| 528 | ** The schema for the local FOSSIL database file found at the root |
| 529 | ** of every check-out. This database contains the complete state of |
| 530 | ** the check-out. See also the addendum in zLocalSchemaVmerge[]. |
| 531 | */ |
| 532 | const char zLocalSchema[] = |
| 533 | @ -- The VVAR table holds miscellanous information about the local database |
| 534 | @ -- in the form of name-value pairs. This is similar to the VAR table |
| 535 | @ -- table in the repository except that this table holds information that |
| 536 | @ -- is specific to the local check-out. |
| 537 | @ -- |
| 538 | @ -- Important Variables: |
| @@ -542,11 +542,11 @@ | |
| 542 | @ -- |
| 543 | @ CREATE TABLE vvar( |
| 544 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 545 | @ value CLOB, -- Content of the named parameter |
| 546 | @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
| 547 | @ ); |
| 548 | @ |
| 549 | @ -- Each entry in the vfile table represents a single file in the |
| 550 | @ -- current check-out. |
| 551 | @ -- |
| 552 | @ -- The file.rid field is 0 for files or folders that have been |
| 553 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -28,11 +28,11 @@ | |
| 28 | @ -- ~/.fossil file and that stores information about the users setup. |
| 29 | @ -- |
| 30 | @ CREATE TABLE global_config( |
| 31 | @ name TEXT PRIMARY KEY, |
| 32 | @ value TEXT |
| 33 | @ ) WITHOUT ROWID; |
| 34 | @ |
| 35 | @ -- Identifier for this file type. |
| 36 | @ -- The integer is the same as 'FSLG'. |
| 37 | @ PRAGMA application_id=252006675; |
| 38 | ; |
| @@ -138,11 +138,11 @@ | |
| 138 | @ CREATE TABLE config( |
| 139 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 140 | @ value CLOB, -- Content of the named parameter |
| 141 | @ mtime DATE, -- last modified. seconds since 1970 |
| 142 | @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
| 143 | @ ) WITHOUT ROWID; |
| 144 | @ |
| 145 | @ -- Artifacts that should not be processed are identified in the |
| 146 | @ -- "shun" table. Artifacts that are control-file forgeries or |
| 147 | @ -- spam or artifacts whose contents violate administrative policy |
| 148 | @ -- can be shunned in order to prevent them from contaminating |
| @@ -151,14 +151,14 @@ | |
| 151 | @ -- Shunned artifacts do not exist in the blob table. Hence they |
| 152 | @ -- have not artifact ID (rid) and we thus must store their full |
| 153 | @ -- UUID. |
| 154 | @ -- |
| 155 | @ CREATE TABLE shun( |
| 156 | @ uuid TEXT PRIMARY KEY,-- UUID of artifact to be shunned. Canonical form |
| 157 | @ mtime DATE, -- When added. seconds since 1970 |
| 158 | @ scom TEXT -- Optional text explaining why the shun occurred |
| 159 | @ ) WITHOUT ROWID; |
| 160 | @ |
| 161 | @ -- Artifacts that should not be pushed are stored in the "private" |
| 162 | @ -- table. Private artifacts are omitted from the "unclustered" and |
| 163 | @ -- "unsent" tables. |
| 164 | @ -- |
| @@ -193,11 +193,11 @@ | |
| 193 | @ -- |
| 194 | @ CREATE TABLE concealed( |
| 195 | @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content |
| 196 | @ mtime DATE, -- Time created. Seconds since 1970 |
| 197 | @ content TEXT -- Content intended to be concealed |
| 198 | @ ) WITHOUT ROWID; |
| 199 | @ |
| 200 | @ -- The application ID helps the unix "file" command to identify the |
| 201 | @ -- database as a fossil repository. |
| 202 | @ PRAGMA application_id=252006673; |
| 203 | ; |
| @@ -528,11 +528,11 @@ | |
| 528 | ** The schema for the local FOSSIL database file found at the root |
| 529 | ** of every check-out. This database contains the complete state of |
| 530 | ** the check-out. See also the addendum in zLocalSchemaVmerge[]. |
| 531 | */ |
| 532 | const char zLocalSchema[] = |
| 533 | @ -- The VVAR table holds miscellanous information about the local checkout |
| 534 | @ -- in the form of name-value pairs. This is similar to the VAR table |
| 535 | @ -- table in the repository except that this table holds information that |
| 536 | @ -- is specific to the local check-out. |
| 537 | @ -- |
| 538 | @ -- Important Variables: |
| @@ -542,11 +542,11 @@ | |
| 542 | @ -- |
| 543 | @ CREATE TABLE vvar( |
| 544 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 545 | @ value CLOB, -- Content of the named parameter |
| 546 | @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
| 547 | @ ) WITHOUT ROWID; |
| 548 | @ |
| 549 | @ -- Each entry in the vfile table represents a single file in the |
| 550 | @ -- current check-out. |
| 551 | @ -- |
| 552 | @ -- The file.rid field is 0 for files or folders that have been |
| 553 |
+83
-64
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -794,31 +794,41 @@ | ||
| 794 | 794 | SRCHFLG_STATIC|SRCHFLG_HTML); |
| 795 | 795 | if( (srchFlags & SRCH_DOC)!=0 ){ |
| 796 | 796 | char *zDocGlob = db_get("doc-glob",""); |
| 797 | 797 | char *zDocBr = db_get("doc-branch","trunk"); |
| 798 | 798 | if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){ |
| 799 | + Glob * pGlob = glob_create(zDocBr) | |
| 800 | + /* We're misusing a Glob as a list of comma-/space-delimited | |
| 801 | + ** tokens. We're not actually doing glob matches here. */; | |
| 802 | + int i; | |
| 799 | 803 | db_multi_exec( |
| 800 | 804 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 801 | 805 | ); |
| 802 | - db_multi_exec( | |
| 803 | - "INSERT INTO x(label,url,score,id,date,snip)" | |
| 804 | - " SELECT printf('Document: %%s',title('d',blob.rid,foci.filename))," | |
| 805 | - " printf('/doc/%T/%%s',foci.filename)," | |
| 806 | - " search_score()," | |
| 807 | - " 'd'||blob.rid," | |
| 808 | - " (SELECT datetime(event.mtime) FROM event" | |
| 809 | - " WHERE objid=symbolic_name_to_rid('trunk'))," | |
| 810 | - " search_snippet()" | |
| 811 | - " FROM foci CROSS JOIN blob" | |
| 812 | - " WHERE checkinID=symbolic_name_to_rid('trunk')" | |
| 813 | - " AND blob.uuid=foci.uuid" | |
| 814 | - " AND search_match(title('d',blob.rid,foci.filename)," | |
| 815 | - " body('d',blob.rid,foci.filename))" | |
| 816 | - " AND %z", | |
| 817 | - zDocBr, glob_expr("foci.filename", zDocGlob) | |
| 818 | - ); | |
| 819 | - } | |
| 806 | + for( i = 0; i < pGlob->nPattern; ++i ){ | |
| 807 | + const char * zBranch = pGlob->azPattern[i]; | |
| 808 | + db_multi_exec( | |
| 809 | + "INSERT INTO x(label,url,score,id,date,snip)" | |
| 810 | + " SELECT printf('Document: %%s',title('d',blob.rid,foci.filename))," | |
| 811 | + " printf('/doc/%T/%%s',foci.filename)," | |
| 812 | + " search_score()," | |
| 813 | + " 'd'||blob.rid," | |
| 814 | + " (SELECT datetime(event.mtime) FROM event" | |
| 815 | + " WHERE objid=symbolic_name_to_rid(%Q))," | |
| 816 | + " search_snippet()" | |
| 817 | + " FROM foci CROSS JOIN blob" | |
| 818 | + " WHERE checkinID=symbolic_name_to_rid(%Q)" | |
| 819 | + " AND blob.uuid=foci.uuid" | |
| 820 | + " AND search_match(title('d',blob.rid,foci.filename)," | |
| 821 | + " body('d',blob.rid,foci.filename))" | |
| 822 | + " AND %z", | |
| 823 | + zBranch, zBranch, zBranch, glob_expr("foci.filename", zDocGlob) | |
| 824 | + ); | |
| 825 | + } | |
| 826 | + glob_free(pGlob); | |
| 827 | + } | |
| 828 | + fossil_free(zDocGlob); | |
| 829 | + fossil_free(zDocBr); | |
| 820 | 830 | } |
| 821 | 831 | if( (srchFlags & SRCH_WIKI)!=0 ){ |
| 822 | 832 | db_multi_exec( |
| 823 | 833 | "WITH wiki(name,rid,mtime) AS (" |
| 824 | 834 | " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)" |
| @@ -1878,59 +1888,68 @@ | ||
| 1878 | 1888 | ** and if the latest check-in on doc-br is in the unindexed set of |
| 1879 | 1889 | ** check-ins, then update all 'd' entries in FTSDOCS that have |
| 1880 | 1890 | ** changed. |
| 1881 | 1891 | */ |
| 1882 | 1892 | static void search_update_doc_index(void){ |
| 1883 | - const char *zDocBr = db_get("doc-branch","trunk"); | |
| 1884 | - int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0; | |
| 1885 | - double rTime; | |
| 1886 | - if( ckid==0 ) return; | |
| 1887 | - if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d" | |
| 1888 | - " AND NOT idxed", ckid) ) return; | |
| 1889 | - | |
| 1890 | - /* If we get this far, it means that changes to 'd' entries are | |
| 1891 | - ** required. */ | |
| 1892 | - rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid); | |
| 1893 | + const char *zDocBranches = db_get("doc-branch","trunk"); | |
| 1894 | + int i; | |
| 1895 | + Glob * pGlob = glob_create(zDocBranches) | |
| 1896 | + /* We're misusing a Glob as a list of comma-/space-delimited | |
| 1897 | + ** tokens. We're not actually doing glob matches here. */; | |
| 1898 | + if( !pGlob ) return; | |
| 1893 | 1899 | db_multi_exec( |
| 1894 | 1900 | "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);" |
| 1895 | 1901 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 1896 | - "INSERT OR IGNORE INTO current_docs(rid, name)" | |
| 1897 | - " SELECT blob.rid, foci.filename FROM foci, blob" | |
| 1898 | - " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid" | |
| 1899 | - " AND %z", | |
| 1900 | - ckid, glob_expr("foci.filename", db_get("doc-glob","")) | |
| 1901 | - ); | |
| 1902 | - db_multi_exec( | |
| 1903 | - "DELETE FROM ftsidx WHERE rowid IN" | |
| 1904 | - " (SELECT rowid FROM ftsdocs WHERE type='d'" | |
| 1905 | - " AND rid NOT IN (SELECT rid FROM current_docs))" | |
| 1906 | - ); | |
| 1907 | - db_multi_exec( | |
| 1908 | - "DELETE FROM ftsdocs WHERE type='d'" | |
| 1909 | - " AND rid NOT IN (SELECT rid FROM current_docs)" | |
| 1910 | - ); | |
| 1911 | - db_multi_exec( | |
| 1912 | - "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)" | |
| 1913 | - " SELECT 'd', rid, name, 0," | |
| 1914 | - " title('d',rid,name)," | |
| 1915 | - " body('d',rid,name)," | |
| 1916 | - " printf('/doc/%T/%%s',urlencode(name))," | |
| 1917 | - " %.17g" | |
| 1918 | - " FROM current_docs", | |
| 1919 | - zDocBr, rTime | |
| 1920 | - ); | |
| 1921 | - db_multi_exec( | |
| 1922 | - "INSERT INTO ftsidx(rowid,title,body)" | |
| 1923 | - " SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed" | |
| 1924 | - ); | |
| 1925 | - db_multi_exec( | |
| 1926 | - "UPDATE ftsdocs SET" | |
| 1927 | - " idxed=1," | |
| 1928 | - " bx=NULL," | |
| 1929 | - " label='Document: '||label" | |
| 1930 | - " WHERE type='d' AND NOT idxed" | |
| 1931 | - ); | |
| 1902 | + ); | |
| 1903 | + for( i = 0; i < pGlob->nPattern; ++i ){ | |
| 1904 | + const char *zDocBr = pGlob->azPattern[i]; | |
| 1905 | + int ckid = symbolic_name_to_rid(zDocBr,"ci"); | |
| 1906 | + double rTime; | |
| 1907 | + if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d" | |
| 1908 | + " AND NOT idxed", ckid) ) continue; | |
| 1909 | + /* If we get this far, it means that changes to 'd' entries are | |
| 1910 | + ** required. */ | |
| 1911 | + rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid); | |
| 1912 | + db_multi_exec( | |
| 1913 | + "INSERT OR IGNORE INTO current_docs(rid, name)" | |
| 1914 | + " SELECT blob.rid, foci.filename FROM foci, blob" | |
| 1915 | + " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid" | |
| 1916 | + " AND %z", | |
| 1917 | + ckid, glob_expr("foci.filename", db_get("doc-glob","")) | |
| 1918 | + ); | |
| 1919 | + db_multi_exec( | |
| 1920 | + "DELETE FROM ftsidx WHERE rowid IN" | |
| 1921 | + " (SELECT rowid FROM ftsdocs WHERE type='d'" | |
| 1922 | + " AND rid NOT IN (SELECT rid FROM current_docs))" | |
| 1923 | + ); | |
| 1924 | + db_multi_exec( | |
| 1925 | + "DELETE FROM ftsdocs WHERE type='d'" | |
| 1926 | + " AND rid NOT IN (SELECT rid FROM current_docs)" | |
| 1927 | + ); | |
| 1928 | + db_multi_exec( | |
| 1929 | + "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)" | |
| 1930 | + " SELECT 'd', rid, name, 0," | |
| 1931 | + " title('d',rid,name)," | |
| 1932 | + " body('d',rid,name)," | |
| 1933 | + " printf('/doc/%T/%%s',urlencode(name))," | |
| 1934 | + " %.17g" | |
| 1935 | + " FROM current_docs", | |
| 1936 | + zDocBr, rTime | |
| 1937 | + ); | |
| 1938 | + db_multi_exec( | |
| 1939 | + "INSERT INTO ftsidx(rowid,title,body)" | |
| 1940 | + " SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed" | |
| 1941 | + ); | |
| 1942 | + db_multi_exec( | |
| 1943 | + "UPDATE ftsdocs SET" | |
| 1944 | + " idxed=1," | |
| 1945 | + " bx=NULL," | |
| 1946 | + " label='Document: '||label" | |
| 1947 | + " WHERE type='d' AND NOT idxed" | |
| 1948 | + ); | |
| 1949 | + } | |
| 1950 | + glob_free(pGlob); | |
| 1932 | 1951 | } |
| 1933 | 1952 | |
| 1934 | 1953 | /* |
| 1935 | 1954 | ** Deal with all of the unindexed 'c' terms in FTSDOCS |
| 1936 | 1955 | */ |
| 1937 | 1956 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -794,31 +794,41 @@ | |
| 794 | SRCHFLG_STATIC|SRCHFLG_HTML); |
| 795 | if( (srchFlags & SRCH_DOC)!=0 ){ |
| 796 | char *zDocGlob = db_get("doc-glob",""); |
| 797 | char *zDocBr = db_get("doc-branch","trunk"); |
| 798 | if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){ |
| 799 | db_multi_exec( |
| 800 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 801 | ); |
| 802 | db_multi_exec( |
| 803 | "INSERT INTO x(label,url,score,id,date,snip)" |
| 804 | " SELECT printf('Document: %%s',title('d',blob.rid,foci.filename))," |
| 805 | " printf('/doc/%T/%%s',foci.filename)," |
| 806 | " search_score()," |
| 807 | " 'd'||blob.rid," |
| 808 | " (SELECT datetime(event.mtime) FROM event" |
| 809 | " WHERE objid=symbolic_name_to_rid('trunk'))," |
| 810 | " search_snippet()" |
| 811 | " FROM foci CROSS JOIN blob" |
| 812 | " WHERE checkinID=symbolic_name_to_rid('trunk')" |
| 813 | " AND blob.uuid=foci.uuid" |
| 814 | " AND search_match(title('d',blob.rid,foci.filename)," |
| 815 | " body('d',blob.rid,foci.filename))" |
| 816 | " AND %z", |
| 817 | zDocBr, glob_expr("foci.filename", zDocGlob) |
| 818 | ); |
| 819 | } |
| 820 | } |
| 821 | if( (srchFlags & SRCH_WIKI)!=0 ){ |
| 822 | db_multi_exec( |
| 823 | "WITH wiki(name,rid,mtime) AS (" |
| 824 | " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)" |
| @@ -1878,59 +1888,68 @@ | |
| 1878 | ** and if the latest check-in on doc-br is in the unindexed set of |
| 1879 | ** check-ins, then update all 'd' entries in FTSDOCS that have |
| 1880 | ** changed. |
| 1881 | */ |
| 1882 | static void search_update_doc_index(void){ |
| 1883 | const char *zDocBr = db_get("doc-branch","trunk"); |
| 1884 | int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0; |
| 1885 | double rTime; |
| 1886 | if( ckid==0 ) return; |
| 1887 | if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d" |
| 1888 | " AND NOT idxed", ckid) ) return; |
| 1889 | |
| 1890 | /* If we get this far, it means that changes to 'd' entries are |
| 1891 | ** required. */ |
| 1892 | rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid); |
| 1893 | db_multi_exec( |
| 1894 | "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);" |
| 1895 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 1896 | "INSERT OR IGNORE INTO current_docs(rid, name)" |
| 1897 | " SELECT blob.rid, foci.filename FROM foci, blob" |
| 1898 | " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid" |
| 1899 | " AND %z", |
| 1900 | ckid, glob_expr("foci.filename", db_get("doc-glob","")) |
| 1901 | ); |
| 1902 | db_multi_exec( |
| 1903 | "DELETE FROM ftsidx WHERE rowid IN" |
| 1904 | " (SELECT rowid FROM ftsdocs WHERE type='d'" |
| 1905 | " AND rid NOT IN (SELECT rid FROM current_docs))" |
| 1906 | ); |
| 1907 | db_multi_exec( |
| 1908 | "DELETE FROM ftsdocs WHERE type='d'" |
| 1909 | " AND rid NOT IN (SELECT rid FROM current_docs)" |
| 1910 | ); |
| 1911 | db_multi_exec( |
| 1912 | "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)" |
| 1913 | " SELECT 'd', rid, name, 0," |
| 1914 | " title('d',rid,name)," |
| 1915 | " body('d',rid,name)," |
| 1916 | " printf('/doc/%T/%%s',urlencode(name))," |
| 1917 | " %.17g" |
| 1918 | " FROM current_docs", |
| 1919 | zDocBr, rTime |
| 1920 | ); |
| 1921 | db_multi_exec( |
| 1922 | "INSERT INTO ftsidx(rowid,title,body)" |
| 1923 | " SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed" |
| 1924 | ); |
| 1925 | db_multi_exec( |
| 1926 | "UPDATE ftsdocs SET" |
| 1927 | " idxed=1," |
| 1928 | " bx=NULL," |
| 1929 | " label='Document: '||label" |
| 1930 | " WHERE type='d' AND NOT idxed" |
| 1931 | ); |
| 1932 | } |
| 1933 | |
| 1934 | /* |
| 1935 | ** Deal with all of the unindexed 'c' terms in FTSDOCS |
| 1936 | */ |
| 1937 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -794,31 +794,41 @@ | |
| 794 | SRCHFLG_STATIC|SRCHFLG_HTML); |
| 795 | if( (srchFlags & SRCH_DOC)!=0 ){ |
| 796 | char *zDocGlob = db_get("doc-glob",""); |
| 797 | char *zDocBr = db_get("doc-branch","trunk"); |
| 798 | if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){ |
| 799 | Glob * pGlob = glob_create(zDocBr) |
| 800 | /* We're misusing a Glob as a list of comma-/space-delimited |
| 801 | ** tokens. We're not actually doing glob matches here. */; |
| 802 | int i; |
| 803 | db_multi_exec( |
| 804 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 805 | ); |
| 806 | for( i = 0; i < pGlob->nPattern; ++i ){ |
| 807 | const char * zBranch = pGlob->azPattern[i]; |
| 808 | db_multi_exec( |
| 809 | "INSERT INTO x(label,url,score,id,date,snip)" |
| 810 | " SELECT printf('Document: %%s',title('d',blob.rid,foci.filename))," |
| 811 | " printf('/doc/%T/%%s',foci.filename)," |
| 812 | " search_score()," |
| 813 | " 'd'||blob.rid," |
| 814 | " (SELECT datetime(event.mtime) FROM event" |
| 815 | " WHERE objid=symbolic_name_to_rid(%Q))," |
| 816 | " search_snippet()" |
| 817 | " FROM foci CROSS JOIN blob" |
| 818 | " WHERE checkinID=symbolic_name_to_rid(%Q)" |
| 819 | " AND blob.uuid=foci.uuid" |
| 820 | " AND search_match(title('d',blob.rid,foci.filename)," |
| 821 | " body('d',blob.rid,foci.filename))" |
| 822 | " AND %z", |
| 823 | zBranch, zBranch, zBranch, glob_expr("foci.filename", zDocGlob) |
| 824 | ); |
| 825 | } |
| 826 | glob_free(pGlob); |
| 827 | } |
| 828 | fossil_free(zDocGlob); |
| 829 | fossil_free(zDocBr); |
| 830 | } |
| 831 | if( (srchFlags & SRCH_WIKI)!=0 ){ |
| 832 | db_multi_exec( |
| 833 | "WITH wiki(name,rid,mtime) AS (" |
| 834 | " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)" |
| @@ -1878,59 +1888,68 @@ | |
| 1888 | ** and if the latest check-in on doc-br is in the unindexed set of |
| 1889 | ** check-ins, then update all 'd' entries in FTSDOCS that have |
| 1890 | ** changed. |
| 1891 | */ |
| 1892 | static void search_update_doc_index(void){ |
| 1893 | const char *zDocBranches = db_get("doc-branch","trunk"); |
| 1894 | int i; |
| 1895 | Glob * pGlob = glob_create(zDocBranches) |
| 1896 | /* We're misusing a Glob as a list of comma-/space-delimited |
| 1897 | ** tokens. We're not actually doing glob matches here. */; |
| 1898 | if( !pGlob ) return; |
| 1899 | db_multi_exec( |
| 1900 | "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);" |
| 1901 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 1902 | ); |
| 1903 | for( i = 0; i < pGlob->nPattern; ++i ){ |
| 1904 | const char *zDocBr = pGlob->azPattern[i]; |
| 1905 | int ckid = symbolic_name_to_rid(zDocBr,"ci"); |
| 1906 | double rTime; |
| 1907 | if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d" |
| 1908 | " AND NOT idxed", ckid) ) continue; |
| 1909 | /* If we get this far, it means that changes to 'd' entries are |
| 1910 | ** required. */ |
| 1911 | rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid); |
| 1912 | db_multi_exec( |
| 1913 | "INSERT OR IGNORE INTO current_docs(rid, name)" |
| 1914 | " SELECT blob.rid, foci.filename FROM foci, blob" |
| 1915 | " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid" |
| 1916 | " AND %z", |
| 1917 | ckid, glob_expr("foci.filename", db_get("doc-glob","")) |
| 1918 | ); |
| 1919 | db_multi_exec( |
| 1920 | "DELETE FROM ftsidx WHERE rowid IN" |
| 1921 | " (SELECT rowid FROM ftsdocs WHERE type='d'" |
| 1922 | " AND rid NOT IN (SELECT rid FROM current_docs))" |
| 1923 | ); |
| 1924 | db_multi_exec( |
| 1925 | "DELETE FROM ftsdocs WHERE type='d'" |
| 1926 | " AND rid NOT IN (SELECT rid FROM current_docs)" |
| 1927 | ); |
| 1928 | db_multi_exec( |
| 1929 | "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)" |
| 1930 | " SELECT 'd', rid, name, 0," |
| 1931 | " title('d',rid,name)," |
| 1932 | " body('d',rid,name)," |
| 1933 | " printf('/doc/%T/%%s',urlencode(name))," |
| 1934 | " %.17g" |
| 1935 | " FROM current_docs", |
| 1936 | zDocBr, rTime |
| 1937 | ); |
| 1938 | db_multi_exec( |
| 1939 | "INSERT INTO ftsidx(rowid,title,body)" |
| 1940 | " SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed" |
| 1941 | ); |
| 1942 | db_multi_exec( |
| 1943 | "UPDATE ftsdocs SET" |
| 1944 | " idxed=1," |
| 1945 | " bx=NULL," |
| 1946 | " label='Document: '||label" |
| 1947 | " WHERE type='d' AND NOT idxed" |
| 1948 | ); |
| 1949 | } |
| 1950 | glob_free(pGlob); |
| 1951 | } |
| 1952 | |
| 1953 | /* |
| 1954 | ** Deal with all of the unindexed 'c' terms in FTSDOCS |
| 1955 | */ |
| 1956 |
+16
-4
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -501,13 +501,24 @@ | ||
| 501 | 501 | @ behavior or to find an SQL injection opportunity or similar. This can |
| 502 | 502 | @ waste hours of CPU time and gigabytes of bandwidth on the server. A |
| 503 | 503 | @ suggested value for this setting is: |
| 504 | 504 | @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>". |
| 505 | 505 | @ (Property: robot-restrict) |
| 506 | - @ <p> | |
| 506 | + @ <br> | |
| 507 | 507 | textarea_attribute("", 2, 80, |
| 508 | 508 | "robot-restrict", "rbrestrict", "", 0); |
| 509 | + @ <br> The following comma-separated GLOB pattern allows for exceptions | |
| 510 | + @ in the maximum number of query parameters before a request is considered | |
| 511 | + @ complex. If this GLOB pattern exists and is non-empty and if it | |
| 512 | + @ matches against the pagename followed by "/" and the number of query | |
| 513 | + @ parameters, then the request is allowed through. For example, the | |
| 514 | + @ suggested pattern of "timeline/[012]" allows the /timeline page to | |
| 515 | + @ pass with up to 2 query parameters besides "name". | |
| 516 | + @ (Property: robot-restrict-qp) | |
| 517 | + @ <br> | |
| 518 | + textarea_attribute("", 2, 80, | |
| 519 | + "robot-restrict-qp", "rbrestrictqp", "", 0); | |
| 509 | 520 | |
| 510 | 521 | @ <hr> |
| 511 | 522 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 512 | 523 | @ </div></form> |
| 513 | 524 | db_end_transaction(0); |
| @@ -2187,14 +2198,15 @@ | ||
| 2187 | 2198 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2188 | 2199 | @ <tr><td><i>(blank)</i><td> |
| 2189 | 2200 | @ <td>Search nothing. (Disables document search).</tr> |
| 2190 | 2201 | @ </table> |
| 2191 | 2202 | @ <hr> |
| 2192 | - entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); | |
| 2203 | + entry_attribute("Document Branches", 20, "doc-branch", "db", "trunk", 0); | |
| 2193 | 2204 | @ <p>When searching documents, use the versions of the files found at the |
| 2194 | - @ type of the "Document Branch" branch. Recommended value: "trunk". | |
| 2195 | - @ Document search is disabled if blank. | |
| 2205 | + @ type of the "Document Branches" branch. Recommended value: "trunk". | |
| 2206 | + @ Document search is disabled if blank. It may be a list of branch names | |
| 2207 | + @ separated by spaces and/or commas. | |
| 2196 | 2208 | @ <hr> |
| 2197 | 2209 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2198 | 2210 | @ <br> |
| 2199 | 2211 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2200 | 2212 | @ <br> |
| 2201 | 2213 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -501,13 +501,24 @@ | |
| 501 | @ behavior or to find an SQL injection opportunity or similar. This can |
| 502 | @ waste hours of CPU time and gigabytes of bandwidth on the server. A |
| 503 | @ suggested value for this setting is: |
| 504 | @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>". |
| 505 | @ (Property: robot-restrict) |
| 506 | @ <p> |
| 507 | textarea_attribute("", 2, 80, |
| 508 | "robot-restrict", "rbrestrict", "", 0); |
| 509 | |
| 510 | @ <hr> |
| 511 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 512 | @ </div></form> |
| 513 | db_end_transaction(0); |
| @@ -2187,14 +2198,15 @@ | |
| 2187 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2188 | @ <tr><td><i>(blank)</i><td> |
| 2189 | @ <td>Search nothing. (Disables document search).</tr> |
| 2190 | @ </table> |
| 2191 | @ <hr> |
| 2192 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2193 | @ <p>When searching documents, use the versions of the files found at the |
| 2194 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2195 | @ Document search is disabled if blank. |
| 2196 | @ <hr> |
| 2197 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2198 | @ <br> |
| 2199 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2200 | @ <br> |
| 2201 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -501,13 +501,24 @@ | |
| 501 | @ behavior or to find an SQL injection opportunity or similar. This can |
| 502 | @ waste hours of CPU time and gigabytes of bandwidth on the server. A |
| 503 | @ suggested value for this setting is: |
| 504 | @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>". |
| 505 | @ (Property: robot-restrict) |
| 506 | @ <br> |
| 507 | textarea_attribute("", 2, 80, |
| 508 | "robot-restrict", "rbrestrict", "", 0); |
| 509 | @ <br> The following comma-separated GLOB pattern allows for exceptions |
| 510 | @ in the maximum number of query parameters before a request is considered |
| 511 | @ complex. If this GLOB pattern exists and is non-empty and if it |
| 512 | @ matches against the pagename followed by "/" and the number of query |
| 513 | @ parameters, then the request is allowed through. For example, the |
| 514 | @ suggested pattern of "timeline/[012]" allows the /timeline page to |
| 515 | @ pass with up to 2 query parameters besides "name". |
| 516 | @ (Property: robot-restrict-qp) |
| 517 | @ <br> |
| 518 | textarea_attribute("", 2, 80, |
| 519 | "robot-restrict-qp", "rbrestrictqp", "", 0); |
| 520 | |
| 521 | @ <hr> |
| 522 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 523 | @ </div></form> |
| 524 | db_end_transaction(0); |
| @@ -2187,14 +2198,15 @@ | |
| 2198 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2199 | @ <tr><td><i>(blank)</i><td> |
| 2200 | @ <td>Search nothing. (Disables document search).</tr> |
| 2201 | @ </table> |
| 2202 | @ <hr> |
| 2203 | entry_attribute("Document Branches", 20, "doc-branch", "db", "trunk", 0); |
| 2204 | @ <p>When searching documents, use the versions of the files found at the |
| 2205 | @ type of the "Document Branches" branch. Recommended value: "trunk". |
| 2206 | @ Document search is disabled if blank. It may be a list of branch names |
| 2207 | @ separated by spaces and/or commas. |
| 2208 | @ <hr> |
| 2209 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2210 | @ <br> |
| 2211 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2212 | @ <br> |
| 2213 |
+7
-5
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -115,11 +115,11 @@ | ||
| 115 | 115 | } |
| 116 | 116 | if( !bUnusedOnly ){ |
| 117 | 117 | style_submenu_element("Unused", "setup_ulist?unused"); |
| 118 | 118 | } |
| 119 | 119 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \ |
| 120 | - @ data-column-types='ktxTTKt' data-init-sort='2'> | |
| 120 | + @ data-column-types='ktxKTKt' data-init-sort='4'> | |
| 121 | 121 | @ <thead><tr> |
| 122 | 122 | @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\ |
| 123 | 123 | @ <th>Alerts</tr></thead> |
| 124 | 124 | @ <tbody> |
| 125 | 125 | db_multi_exec( |
| @@ -161,15 +161,16 @@ | ||
| 161 | 161 | " lower(login) AS sortkey, " |
| 162 | 162 | " CASE WHEN info LIKE '%%expires 20%%'" |
| 163 | 163 | " THEN substr(info,instr(lower(info),'expires')+8,10)" |
| 164 | 164 | " END AS exp," |
| 165 | 165 | "atime," |
| 166 | - " subscriber.ssub, subscriber.subscriberId" | |
| 166 | + " subscriber.ssub, subscriber.subscriberId," | |
| 167 | + " user.mtime AS sorttime" | |
| 167 | 168 | " FROM user LEFT JOIN lastAccess ON login=uname" |
| 168 | 169 | " LEFT JOIN subscriber ON login=suname" |
| 169 | 170 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" |
| 170 | - " ORDER BY sortkey", zWith/*safe-for-%s*/ | |
| 171 | + " ORDER BY sorttime DESC", zWith/*safe-for-%s*/ | |
| 171 | 172 | ); |
| 172 | 173 | rNow = db_double(0.0, "SELECT julianday('now');"); |
| 173 | 174 | while( db_step(&s)==SQLITE_ROW ){ |
| 174 | 175 | int uid = db_column_int(&s, 0); |
| 175 | 176 | const char *zLogin = db_column_text(&s, 1); |
| @@ -180,10 +181,11 @@ | ||
| 180 | 181 | const char *zExp = db_column_text(&s,6); |
| 181 | 182 | double rATime = db_column_double(&s,7); |
| 182 | 183 | char *zAge = 0; |
| 183 | 184 | const char *zSub; |
| 184 | 185 | int sid = db_column_int(&s,9); |
| 186 | + sqlite3_int64 sorttime = db_column_int64(&s, 10); | |
| 185 | 187 | if( rATime>0.0 ){ |
| 186 | 188 | zAge = human_readable_age(rNow - rATime); |
| 187 | 189 | } |
| 188 | 190 | if( bUbg ){ |
| 189 | 191 | @ <tr style='background-color: %h(user_color(zLogin));'> |
| @@ -192,11 +194,11 @@ | ||
| 192 | 194 | } |
| 193 | 195 | @ <td data-sortkey='%h(zSortKey)'>\ |
| 194 | 196 | @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> |
| 195 | 197 | @ <td>%h(zCap) |
| 196 | 198 | @ <td>%h(zInfo) |
| 197 | - @ <td>%h(zDate?zDate:"") | |
| 199 | + @ <td data-sortkey='%09llx(sorttime)'>%h(zDate?zDate:"") | |
| 198 | 200 | @ <td>%h(zExp?zExp:"") |
| 199 | 201 | @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"") |
| 200 | 202 | if( db_column_type(&s,8)==SQLITE_NULL ){ |
| 201 | 203 | @ <td> |
| 202 | 204 | }else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){ |
| @@ -1016,11 +1018,11 @@ | ||
| 1016 | 1018 | style_header("User %h", db_column_text(&q,1)); |
| 1017 | 1019 | @ <table class="label-value"> |
| 1018 | 1020 | @ <tr><th>uid:</th><td>%d(db_column_int(&q,0)) |
| 1019 | 1021 | @ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr> |
| 1020 | 1022 | @ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr> |
| 1021 | - @ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</th></tr> | |
| 1023 | + @ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</td></tr> | |
| 1022 | 1024 | @ <tr><th valign="top">info:</th> |
| 1023 | 1025 | @ <td valign="top"><span style='white-space:pre-line;'>\ |
| 1024 | 1026 | @ %h(db_column_text(&q,5))</span></td></tr> |
| 1025 | 1027 | @ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr> |
| 1026 | 1028 | if( db_column_type(&q,7)!=SQLITE_NULL ){ |
| 1027 | 1029 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -115,11 +115,11 @@ | |
| 115 | } |
| 116 | if( !bUnusedOnly ){ |
| 117 | style_submenu_element("Unused", "setup_ulist?unused"); |
| 118 | } |
| 119 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \ |
| 120 | @ data-column-types='ktxTTKt' data-init-sort='2'> |
| 121 | @ <thead><tr> |
| 122 | @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\ |
| 123 | @ <th>Alerts</tr></thead> |
| 124 | @ <tbody> |
| 125 | db_multi_exec( |
| @@ -161,15 +161,16 @@ | |
| 161 | " lower(login) AS sortkey, " |
| 162 | " CASE WHEN info LIKE '%%expires 20%%'" |
| 163 | " THEN substr(info,instr(lower(info),'expires')+8,10)" |
| 164 | " END AS exp," |
| 165 | "atime," |
| 166 | " subscriber.ssub, subscriber.subscriberId" |
| 167 | " FROM user LEFT JOIN lastAccess ON login=uname" |
| 168 | " LEFT JOIN subscriber ON login=suname" |
| 169 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" |
| 170 | " ORDER BY sortkey", zWith/*safe-for-%s*/ |
| 171 | ); |
| 172 | rNow = db_double(0.0, "SELECT julianday('now');"); |
| 173 | while( db_step(&s)==SQLITE_ROW ){ |
| 174 | int uid = db_column_int(&s, 0); |
| 175 | const char *zLogin = db_column_text(&s, 1); |
| @@ -180,10 +181,11 @@ | |
| 180 | const char *zExp = db_column_text(&s,6); |
| 181 | double rATime = db_column_double(&s,7); |
| 182 | char *zAge = 0; |
| 183 | const char *zSub; |
| 184 | int sid = db_column_int(&s,9); |
| 185 | if( rATime>0.0 ){ |
| 186 | zAge = human_readable_age(rNow - rATime); |
| 187 | } |
| 188 | if( bUbg ){ |
| 189 | @ <tr style='background-color: %h(user_color(zLogin));'> |
| @@ -192,11 +194,11 @@ | |
| 192 | } |
| 193 | @ <td data-sortkey='%h(zSortKey)'>\ |
| 194 | @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> |
| 195 | @ <td>%h(zCap) |
| 196 | @ <td>%h(zInfo) |
| 197 | @ <td>%h(zDate?zDate:"") |
| 198 | @ <td>%h(zExp?zExp:"") |
| 199 | @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"") |
| 200 | if( db_column_type(&s,8)==SQLITE_NULL ){ |
| 201 | @ <td> |
| 202 | }else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){ |
| @@ -1016,11 +1018,11 @@ | |
| 1016 | style_header("User %h", db_column_text(&q,1)); |
| 1017 | @ <table class="label-value"> |
| 1018 | @ <tr><th>uid:</th><td>%d(db_column_int(&q,0)) |
| 1019 | @ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr> |
| 1020 | @ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr> |
| 1021 | @ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</th></tr> |
| 1022 | @ <tr><th valign="top">info:</th> |
| 1023 | @ <td valign="top"><span style='white-space:pre-line;'>\ |
| 1024 | @ %h(db_column_text(&q,5))</span></td></tr> |
| 1025 | @ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr> |
| 1026 | if( db_column_type(&q,7)!=SQLITE_NULL ){ |
| 1027 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -115,11 +115,11 @@ | |
| 115 | } |
| 116 | if( !bUnusedOnly ){ |
| 117 | style_submenu_element("Unused", "setup_ulist?unused"); |
| 118 | } |
| 119 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \ |
| 120 | @ data-column-types='ktxKTKt' data-init-sort='4'> |
| 121 | @ <thead><tr> |
| 122 | @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\ |
| 123 | @ <th>Alerts</tr></thead> |
| 124 | @ <tbody> |
| 125 | db_multi_exec( |
| @@ -161,15 +161,16 @@ | |
| 161 | " lower(login) AS sortkey, " |
| 162 | " CASE WHEN info LIKE '%%expires 20%%'" |
| 163 | " THEN substr(info,instr(lower(info),'expires')+8,10)" |
| 164 | " END AS exp," |
| 165 | "atime," |
| 166 | " subscriber.ssub, subscriber.subscriberId," |
| 167 | " user.mtime AS sorttime" |
| 168 | " FROM user LEFT JOIN lastAccess ON login=uname" |
| 169 | " LEFT JOIN subscriber ON login=suname" |
| 170 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" |
| 171 | " ORDER BY sorttime DESC", zWith/*safe-for-%s*/ |
| 172 | ); |
| 173 | rNow = db_double(0.0, "SELECT julianday('now');"); |
| 174 | while( db_step(&s)==SQLITE_ROW ){ |
| 175 | int uid = db_column_int(&s, 0); |
| 176 | const char *zLogin = db_column_text(&s, 1); |
| @@ -180,10 +181,11 @@ | |
| 181 | const char *zExp = db_column_text(&s,6); |
| 182 | double rATime = db_column_double(&s,7); |
| 183 | char *zAge = 0; |
| 184 | const char *zSub; |
| 185 | int sid = db_column_int(&s,9); |
| 186 | sqlite3_int64 sorttime = db_column_int64(&s, 10); |
| 187 | if( rATime>0.0 ){ |
| 188 | zAge = human_readable_age(rNow - rATime); |
| 189 | } |
| 190 | if( bUbg ){ |
| 191 | @ <tr style='background-color: %h(user_color(zLogin));'> |
| @@ -192,11 +194,11 @@ | |
| 194 | } |
| 195 | @ <td data-sortkey='%h(zSortKey)'>\ |
| 196 | @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> |
| 197 | @ <td>%h(zCap) |
| 198 | @ <td>%h(zInfo) |
| 199 | @ <td data-sortkey='%09llx(sorttime)'>%h(zDate?zDate:"") |
| 200 | @ <td>%h(zExp?zExp:"") |
| 201 | @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"") |
| 202 | if( db_column_type(&s,8)==SQLITE_NULL ){ |
| 203 | @ <td> |
| 204 | }else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){ |
| @@ -1016,11 +1018,11 @@ | |
| 1018 | style_header("User %h", db_column_text(&q,1)); |
| 1019 | @ <table class="label-value"> |
| 1020 | @ <tr><th>uid:</th><td>%d(db_column_int(&q,0)) |
| 1021 | @ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr> |
| 1022 | @ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr> |
| 1023 | @ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</td></tr> |
| 1024 | @ <tr><th valign="top">info:</th> |
| 1025 | @ <td valign="top"><span style='white-space:pre-line;'>\ |
| 1026 | @ %h(db_column_text(&q,5))</span></td></tr> |
| 1027 | @ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr> |
| 1028 | if( db_column_type(&q,7)!=SQLITE_NULL ){ |
| 1029 |
+5
-40
| --- src/sitemap.c | ||
| +++ src/sitemap.c | ||
| @@ -56,22 +56,10 @@ | ||
| 56 | 56 | int i; |
| 57 | 57 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 58 | 58 | int e = atoi(PD("e","0")); |
| 59 | 59 | const char *zExtra; |
| 60 | 60 | |
| 61 | -#if 0 /* Removed 2021-01-26 */ | |
| 62 | - const struct { | |
| 63 | - const char *zTitle; | |
| 64 | - const char *zProperty; | |
| 65 | - } aExtra[] = { | |
| 66 | - { "Documentation", "sitemap-docidx" }, | |
| 67 | - { "Download", "sitemap-download" }, | |
| 68 | - { "License", "sitemap-license" }, | |
| 69 | - { "Contact", "sitemap-contact" }, | |
| 70 | - }; | |
| 71 | -#endif | |
| 72 | - | |
| 73 | 61 | login_check_credentials(); |
| 74 | 62 | if( P("popup")!=0 ){ |
| 75 | 63 | /* The "popup" query parameter |
| 76 | 64 | ** then disable anti-robot defenses */ |
| 77 | 65 | isPopup = 1; |
| @@ -87,26 +75,10 @@ | ||
| 87 | 75 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 88 | 76 | if( (e&1)==0 ){ |
| 89 | 77 | @ <li>%z(href("%R/home"))Home Page</a> |
| 90 | 78 | } |
| 91 | 79 | |
| 92 | -#if 0 /* Removed 2021-01-26 */ | |
| 93 | - for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){ | |
| 94 | - char *z = db_get(aExtra[i].zProperty,0); | |
| 95 | - if( z==0 || z[0]==0 ) continue; | |
| 96 | - if( !inSublist ){ | |
| 97 | - @ <ul> | |
| 98 | - inSublist = 1; | |
| 99 | - } | |
| 100 | - if( z[0]=='/' ){ | |
| 101 | - @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li> | |
| 102 | - }else{ | |
| 103 | - @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li> | |
| 104 | - } | |
| 105 | - } | |
| 106 | -#endif | |
| 107 | - | |
| 108 | 80 | zExtra = db_get("sitemap-extra",0); |
| 109 | 81 | if( zExtra && (e&2)==0 ){ |
| 110 | 82 | int rc; |
| 111 | 83 | char **azExtra = 0; |
| 112 | 84 | int *anExtra; |
| @@ -139,26 +111,18 @@ | ||
| 139 | 111 | } |
| 140 | 112 | Th_Free(g.interp, azExtra); |
| 141 | 113 | } |
| 142 | 114 | if( (e&1)!=0 ) goto end_of_sitemap; |
| 143 | 115 | |
| 144 | -#if 0 /* Removed on 2021-02-11. Make a sitemap-extra entry if you */ | |
| 145 | - /* really want this */ | |
| 146 | - if( srchFlags & SRCH_DOC ){ | |
| 147 | - if( !inSublist ){ | |
| 148 | - @ <ul> | |
| 149 | - inSublist = 1; | |
| 150 | - } | |
| 151 | - @ <li>%z(href("%R/docsrch"))Documentation Search</a></li> | |
| 152 | - } | |
| 153 | -#endif | |
| 154 | - | |
| 155 | 116 | if( inSublist ){ |
| 156 | 117 | @ </ul> |
| 157 | 118 | inSublist = 0; |
| 158 | 119 | } |
| 159 | 120 | @ </li> |
| 121 | + if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){ | |
| 122 | + @ <li>%z(href("%R/ckout"))Checkout Status</a></li> | |
| 123 | + } | |
| 160 | 124 | if( g.perm.Read ){ |
| 161 | 125 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 162 | 126 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 163 | 127 | @ <ul> |
| 164 | 128 | @ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view, |
| @@ -192,11 +156,12 @@ | ||
| 192 | 156 | } |
| 193 | 157 | if( g.perm.Chat ){ |
| 194 | 158 | @ <li>%z(href("%R/chat"))Chat</a></li> |
| 195 | 159 | } |
| 196 | 160 | if( g.perm.RdForum ){ |
| 197 | - @ <li>%z(href("%R/forum"))Forum</a> | |
| 161 | + const char *zTitle = db_get("forum-title","Forum"); | |
| 162 | + @ <li>%z(href("%R/forum"))%h(zTitle)</a> | |
| 198 | 163 | @ <ul> |
| 199 | 164 | @ <li>%z(href("%R/timeline?y=f"))Recent activity</a></li> |
| 200 | 165 | @ </ul> |
| 201 | 166 | @ </li> |
| 202 | 167 | } |
| 203 | 168 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -56,22 +56,10 @@ | |
| 56 | int i; |
| 57 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 58 | int e = atoi(PD("e","0")); |
| 59 | const char *zExtra; |
| 60 | |
| 61 | #if 0 /* Removed 2021-01-26 */ |
| 62 | const struct { |
| 63 | const char *zTitle; |
| 64 | const char *zProperty; |
| 65 | } aExtra[] = { |
| 66 | { "Documentation", "sitemap-docidx" }, |
| 67 | { "Download", "sitemap-download" }, |
| 68 | { "License", "sitemap-license" }, |
| 69 | { "Contact", "sitemap-contact" }, |
| 70 | }; |
| 71 | #endif |
| 72 | |
| 73 | login_check_credentials(); |
| 74 | if( P("popup")!=0 ){ |
| 75 | /* The "popup" query parameter |
| 76 | ** then disable anti-robot defenses */ |
| 77 | isPopup = 1; |
| @@ -87,26 +75,10 @@ | |
| 87 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 88 | if( (e&1)==0 ){ |
| 89 | @ <li>%z(href("%R/home"))Home Page</a> |
| 90 | } |
| 91 | |
| 92 | #if 0 /* Removed 2021-01-26 */ |
| 93 | for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){ |
| 94 | char *z = db_get(aExtra[i].zProperty,0); |
| 95 | if( z==0 || z[0]==0 ) continue; |
| 96 | if( !inSublist ){ |
| 97 | @ <ul> |
| 98 | inSublist = 1; |
| 99 | } |
| 100 | if( z[0]=='/' ){ |
| 101 | @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li> |
| 102 | }else{ |
| 103 | @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li> |
| 104 | } |
| 105 | } |
| 106 | #endif |
| 107 | |
| 108 | zExtra = db_get("sitemap-extra",0); |
| 109 | if( zExtra && (e&2)==0 ){ |
| 110 | int rc; |
| 111 | char **azExtra = 0; |
| 112 | int *anExtra; |
| @@ -139,26 +111,18 @@ | |
| 139 | } |
| 140 | Th_Free(g.interp, azExtra); |
| 141 | } |
| 142 | if( (e&1)!=0 ) goto end_of_sitemap; |
| 143 | |
| 144 | #if 0 /* Removed on 2021-02-11. Make a sitemap-extra entry if you */ |
| 145 | /* really want this */ |
| 146 | if( srchFlags & SRCH_DOC ){ |
| 147 | if( !inSublist ){ |
| 148 | @ <ul> |
| 149 | inSublist = 1; |
| 150 | } |
| 151 | @ <li>%z(href("%R/docsrch"))Documentation Search</a></li> |
| 152 | } |
| 153 | #endif |
| 154 | |
| 155 | if( inSublist ){ |
| 156 | @ </ul> |
| 157 | inSublist = 0; |
| 158 | } |
| 159 | @ </li> |
| 160 | if( g.perm.Read ){ |
| 161 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 162 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 163 | @ <ul> |
| 164 | @ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view, |
| @@ -192,11 +156,12 @@ | |
| 192 | } |
| 193 | if( g.perm.Chat ){ |
| 194 | @ <li>%z(href("%R/chat"))Chat</a></li> |
| 195 | } |
| 196 | if( g.perm.RdForum ){ |
| 197 | @ <li>%z(href("%R/forum"))Forum</a> |
| 198 | @ <ul> |
| 199 | @ <li>%z(href("%R/timeline?y=f"))Recent activity</a></li> |
| 200 | @ </ul> |
| 201 | @ </li> |
| 202 | } |
| 203 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -56,22 +56,10 @@ | |
| 56 | int i; |
| 57 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 58 | int e = atoi(PD("e","0")); |
| 59 | const char *zExtra; |
| 60 | |
| 61 | login_check_credentials(); |
| 62 | if( P("popup")!=0 ){ |
| 63 | /* The "popup" query parameter |
| 64 | ** then disable anti-robot defenses */ |
| 65 | isPopup = 1; |
| @@ -87,26 +75,10 @@ | |
| 75 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 76 | if( (e&1)==0 ){ |
| 77 | @ <li>%z(href("%R/home"))Home Page</a> |
| 78 | } |
| 79 | |
| 80 | zExtra = db_get("sitemap-extra",0); |
| 81 | if( zExtra && (e&2)==0 ){ |
| 82 | int rc; |
| 83 | char **azExtra = 0; |
| 84 | int *anExtra; |
| @@ -139,26 +111,18 @@ | |
| 111 | } |
| 112 | Th_Free(g.interp, azExtra); |
| 113 | } |
| 114 | if( (e&1)!=0 ) goto end_of_sitemap; |
| 115 | |
| 116 | if( inSublist ){ |
| 117 | @ </ul> |
| 118 | inSublist = 0; |
| 119 | } |
| 120 | @ </li> |
| 121 | if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){ |
| 122 | @ <li>%z(href("%R/ckout"))Checkout Status</a></li> |
| 123 | } |
| 124 | if( g.perm.Read ){ |
| 125 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 126 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 127 | @ <ul> |
| 128 | @ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view, |
| @@ -192,11 +156,12 @@ | |
| 156 | } |
| 157 | if( g.perm.Chat ){ |
| 158 | @ <li>%z(href("%R/chat"))Chat</a></li> |
| 159 | } |
| 160 | if( g.perm.RdForum ){ |
| 161 | const char *zTitle = db_get("forum-title","Forum"); |
| 162 | @ <li>%z(href("%R/forum"))%h(zTitle)</a> |
| 163 | @ <ul> |
| 164 | @ <li>%z(href("%R/timeline?y=f"))Recent activity</a></li> |
| 165 | @ </ul> |
| 166 | @ </li> |
| 167 | } |
| 168 |
+9
| --- src/th_tcl.c | ||
| +++ src/th_tcl.c | ||
| @@ -731,10 +731,19 @@ | ||
| 731 | 731 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN)); |
| 732 | 732 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT)); |
| 733 | 733 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR)); |
| 734 | 734 | } |
| 735 | 735 | Tcl_Preserve((ClientData)tclInterp); |
| 736 | +#if ((TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>6) \ | |
| 737 | + || (TCL_MAJOR_VERSION>8)) | |
| 738 | + /* TCL 8.7+ removes Tcl_MakeSafe(): | |
| 739 | + ** https://core.tcl-lang.org/tcl/tktview?name=655300 | |
| 740 | + ** https://core.tcl-lang.org/tips/doc/trunk/tip/624.md | |
| 741 | + ** 8.7 has it in the headers but not in the libs. | |
| 742 | + */ | |
| 743 | +# define Tcl_MakeSafe(X) TCL_OK | |
| 744 | +#endif | |
| 736 | 745 | if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){ |
| 737 | 746 | int nResult; |
| 738 | 747 | const char *zResult = getTclResult(tclInterp, &nResult); |
| 739 | 748 | Th_ErrorMessage(interp, |
| 740 | 749 | "could not make Tcl interpreter 'safe':", zResult, nResult); |
| 741 | 750 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -731,10 +731,19 @@ | |
| 731 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN)); |
| 732 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT)); |
| 733 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR)); |
| 734 | } |
| 735 | Tcl_Preserve((ClientData)tclInterp); |
| 736 | if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){ |
| 737 | int nResult; |
| 738 | const char *zResult = getTclResult(tclInterp, &nResult); |
| 739 | Th_ErrorMessage(interp, |
| 740 | "could not make Tcl interpreter 'safe':", zResult, nResult); |
| 741 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -731,10 +731,19 @@ | |
| 731 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN)); |
| 732 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT)); |
| 733 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR)); |
| 734 | } |
| 735 | Tcl_Preserve((ClientData)tclInterp); |
| 736 | #if ((TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>6) \ |
| 737 | || (TCL_MAJOR_VERSION>8)) |
| 738 | /* TCL 8.7+ removes Tcl_MakeSafe(): |
| 739 | ** https://core.tcl-lang.org/tcl/tktview?name=655300 |
| 740 | ** https://core.tcl-lang.org/tips/doc/trunk/tip/624.md |
| 741 | ** 8.7 has it in the headers but not in the libs. |
| 742 | */ |
| 743 | # define Tcl_MakeSafe(X) TCL_OK |
| 744 | #endif |
| 745 | if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){ |
| 746 | int nResult; |
| 747 | const char *zResult = getTclResult(tclInterp, &nResult); |
| 748 | Th_ErrorMessage(interp, |
| 749 | "could not make Tcl interpreter 'safe':", zResult, nResult); |
| 750 |
+2
-1
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1885,10 +1885,11 @@ | ||
| 1885 | 1885 | char *zPlural; /* Ending for plural forms */ |
| 1886 | 1886 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1887 | 1887 | int haveParameterN; /* True if n= query parameter present */ |
| 1888 | 1888 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1889 | 1889 | |
| 1890 | + login_check_credentials(); | |
| 1890 | 1891 | url_initialize(&url, "timeline"); |
| 1891 | 1892 | cgi_query_parameters_to_url(&url); |
| 1892 | 1893 | |
| 1893 | 1894 | (void)P_NoBot("ss") |
| 1894 | 1895 | /* "ss" is processed via the udc but at least one spider likes to |
| @@ -1965,11 +1966,10 @@ | ||
| 1965 | 1966 | */ |
| 1966 | 1967 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1967 | 1968 | if( pd_rid ){ |
| 1968 | 1969 | p_rid = d_rid = pd_rid; |
| 1969 | 1970 | } |
| 1970 | - login_check_credentials(); | |
| 1971 | 1971 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1972 | 1972 | || (bisectLocal && !g.perm.Setup) |
| 1973 | 1973 | ){ |
| 1974 | 1974 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1975 | 1975 | return; |
| @@ -2012,10 +2012,11 @@ | ||
| 2012 | 2012 | zTagName = z; |
| 2013 | 2013 | zMatchStyle = "brlist"; |
| 2014 | 2014 | } |
| 2015 | 2015 | if( (z = P("rl"))!=0 ){ |
| 2016 | 2016 | zBrName = z; |
| 2017 | + related = 1; | |
| 2017 | 2018 | zMatchStyle = "brlist"; |
| 2018 | 2019 | } |
| 2019 | 2020 | } |
| 2020 | 2021 | |
| 2021 | 2022 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2022 | 2023 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1885,10 +1885,11 @@ | |
| 1885 | char *zPlural; /* Ending for plural forms */ |
| 1886 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1887 | int haveParameterN; /* True if n= query parameter present */ |
| 1888 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1889 | |
| 1890 | url_initialize(&url, "timeline"); |
| 1891 | cgi_query_parameters_to_url(&url); |
| 1892 | |
| 1893 | (void)P_NoBot("ss") |
| 1894 | /* "ss" is processed via the udc but at least one spider likes to |
| @@ -1965,11 +1966,10 @@ | |
| 1965 | */ |
| 1966 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1967 | if( pd_rid ){ |
| 1968 | p_rid = d_rid = pd_rid; |
| 1969 | } |
| 1970 | login_check_credentials(); |
| 1971 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1972 | || (bisectLocal && !g.perm.Setup) |
| 1973 | ){ |
| 1974 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1975 | return; |
| @@ -2012,10 +2012,11 @@ | |
| 2012 | zTagName = z; |
| 2013 | zMatchStyle = "brlist"; |
| 2014 | } |
| 2015 | if( (z = P("rl"))!=0 ){ |
| 2016 | zBrName = z; |
| 2017 | zMatchStyle = "brlist"; |
| 2018 | } |
| 2019 | } |
| 2020 | |
| 2021 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2022 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1885,10 +1885,11 @@ | |
| 1885 | char *zPlural; /* Ending for plural forms */ |
| 1886 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1887 | int haveParameterN; /* True if n= query parameter present */ |
| 1888 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1889 | |
| 1890 | login_check_credentials(); |
| 1891 | url_initialize(&url, "timeline"); |
| 1892 | cgi_query_parameters_to_url(&url); |
| 1893 | |
| 1894 | (void)P_NoBot("ss") |
| 1895 | /* "ss" is processed via the udc but at least one spider likes to |
| @@ -1965,11 +1966,10 @@ | |
| 1966 | */ |
| 1967 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1968 | if( pd_rid ){ |
| 1969 | p_rid = d_rid = pd_rid; |
| 1970 | } |
| 1971 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1972 | || (bisectLocal && !g.perm.Setup) |
| 1973 | ){ |
| 1974 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1975 | return; |
| @@ -2012,10 +2012,11 @@ | |
| 2012 | zTagName = z; |
| 2013 | zMatchStyle = "brlist"; |
| 2014 | } |
| 2015 | if( (z = P("rl"))!=0 ){ |
| 2016 | zBrName = z; |
| 2017 | related = 1; |
| 2018 | zMatchStyle = "brlist"; |
| 2019 | } |
| 2020 | } |
| 2021 | |
| 2022 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2023 |
+2
-1
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1885,10 +1885,11 @@ | ||
| 1885 | 1885 | char *zPlural; /* Ending for plural forms */ |
| 1886 | 1886 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1887 | 1887 | int haveParameterN; /* True if n= query parameter present */ |
| 1888 | 1888 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1889 | 1889 | |
| 1890 | + login_check_credentials(); | |
| 1890 | 1891 | url_initialize(&url, "timeline"); |
| 1891 | 1892 | cgi_query_parameters_to_url(&url); |
| 1892 | 1893 | |
| 1893 | 1894 | (void)P_NoBot("ss") |
| 1894 | 1895 | /* "ss" is processed via the udc but at least one spider likes to |
| @@ -1965,11 +1966,10 @@ | ||
| 1965 | 1966 | */ |
| 1966 | 1967 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1967 | 1968 | if( pd_rid ){ |
| 1968 | 1969 | p_rid = d_rid = pd_rid; |
| 1969 | 1970 | } |
| 1970 | - login_check_credentials(); | |
| 1971 | 1971 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1972 | 1972 | || (bisectLocal && !g.perm.Setup) |
| 1973 | 1973 | ){ |
| 1974 | 1974 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1975 | 1975 | return; |
| @@ -2012,10 +2012,11 @@ | ||
| 2012 | 2012 | zTagName = z; |
| 2013 | 2013 | zMatchStyle = "brlist"; |
| 2014 | 2014 | } |
| 2015 | 2015 | if( (z = P("rl"))!=0 ){ |
| 2016 | 2016 | zBrName = z; |
| 2017 | + related = 1; | |
| 2017 | 2018 | zMatchStyle = "brlist"; |
| 2018 | 2019 | } |
| 2019 | 2020 | } |
| 2020 | 2021 | |
| 2021 | 2022 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2022 | 2023 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1885,10 +1885,11 @@ | |
| 1885 | char *zPlural; /* Ending for plural forms */ |
| 1886 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1887 | int haveParameterN; /* True if n= query parameter present */ |
| 1888 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1889 | |
| 1890 | url_initialize(&url, "timeline"); |
| 1891 | cgi_query_parameters_to_url(&url); |
| 1892 | |
| 1893 | (void)P_NoBot("ss") |
| 1894 | /* "ss" is processed via the udc but at least one spider likes to |
| @@ -1965,11 +1966,10 @@ | |
| 1965 | */ |
| 1966 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1967 | if( pd_rid ){ |
| 1968 | p_rid = d_rid = pd_rid; |
| 1969 | } |
| 1970 | login_check_credentials(); |
| 1971 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1972 | || (bisectLocal && !g.perm.Setup) |
| 1973 | ){ |
| 1974 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1975 | return; |
| @@ -2012,10 +2012,11 @@ | |
| 2012 | zTagName = z; |
| 2013 | zMatchStyle = "brlist"; |
| 2014 | } |
| 2015 | if( (z = P("rl"))!=0 ){ |
| 2016 | zBrName = z; |
| 2017 | zMatchStyle = "brlist"; |
| 2018 | } |
| 2019 | } |
| 2020 | |
| 2021 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2022 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1885,10 +1885,11 @@ | |
| 1885 | char *zPlural; /* Ending for plural forms */ |
| 1886 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1887 | int haveParameterN; /* True if n= query parameter present */ |
| 1888 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1889 | |
| 1890 | login_check_credentials(); |
| 1891 | url_initialize(&url, "timeline"); |
| 1892 | cgi_query_parameters_to_url(&url); |
| 1893 | |
| 1894 | (void)P_NoBot("ss") |
| 1895 | /* "ss" is processed via the udc but at least one spider likes to |
| @@ -1965,11 +1966,10 @@ | |
| 1966 | */ |
| 1967 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1968 | if( pd_rid ){ |
| 1969 | p_rid = d_rid = pd_rid; |
| 1970 | } |
| 1971 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1972 | || (bisectLocal && !g.perm.Setup) |
| 1973 | ){ |
| 1974 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1975 | return; |
| @@ -2012,10 +2012,11 @@ | |
| 2012 | zTagName = z; |
| 2013 | zMatchStyle = "brlist"; |
| 2014 | } |
| 2015 | if( (z = P("rl"))!=0 ){ |
| 2016 | zBrName = z; |
| 2017 | related = 1; |
| 2018 | zMatchStyle = "brlist"; |
| 2019 | } |
| 2020 | } |
| 2021 | |
| 2022 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2023 |
+1
-1
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -781,11 +781,11 @@ | ||
| 781 | 781 | |
| 782 | 782 | zFullName = db_text(0, |
| 783 | 783 | "SELECT tkt_uuid FROM ticket" |
| 784 | 784 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 785 | 785 | if( zFullName ){ |
| 786 | - attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>"); | |
| 786 | + attachment_list(zFullName, "<h2>Attachments:</h2>", 1); | |
| 787 | 787 | } |
| 788 | 788 | |
| 789 | 789 | style_finish_page(); |
| 790 | 790 | } |
| 791 | 791 | |
| 792 | 792 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -781,11 +781,11 @@ | |
| 781 | |
| 782 | zFullName = db_text(0, |
| 783 | "SELECT tkt_uuid FROM ticket" |
| 784 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 785 | if( zFullName ){ |
| 786 | attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>"); |
| 787 | } |
| 788 | |
| 789 | style_finish_page(); |
| 790 | } |
| 791 | |
| 792 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -781,11 +781,11 @@ | |
| 781 | |
| 782 | zFullName = db_text(0, |
| 783 | "SELECT tkt_uuid FROM ticket" |
| 784 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 785 | if( zFullName ){ |
| 786 | attachment_list(zFullName, "<h2>Attachments:</h2>", 1); |
| 787 | } |
| 788 | |
| 789 | style_finish_page(); |
| 790 | } |
| 791 | |
| 792 |
+55
-3
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -132,10 +132,13 @@ | ||
| 132 | 132 | int nUpdate = 0; /* Number of changes of any kind */ |
| 133 | 133 | int bNosync = 0; /* --nosync. Omit the auto-sync */ |
| 134 | 134 | int width; /* Width of printed comment lines */ |
| 135 | 135 | Stmt mtimeXfer; /* Statement to transfer mtimes */ |
| 136 | 136 | const char *zWidth; /* Width option string value */ |
| 137 | + const char *zCurBrName; /* Current branch name */ | |
| 138 | + const char *zNewBrName; /* New branch name */ | |
| 139 | + const char *zBrChgMsg = ""; /* Message to display if branch changes */ | |
| 137 | 140 | |
| 138 | 141 | if( !internalUpdate ){ |
| 139 | 142 | undo_capture_command_line(); |
| 140 | 143 | url_proxy_options(); |
| 141 | 144 | } |
| @@ -163,10 +166,11 @@ | ||
| 163 | 166 | /* We should be done with options.. */ |
| 164 | 167 | verify_all_options(); |
| 165 | 168 | |
| 166 | 169 | db_must_be_within_tree(); |
| 167 | 170 | vid = db_lget_int("checkout", 0); |
| 171 | + zCurBrName = branch_of_rid(vid); | |
| 168 | 172 | user_select(); |
| 169 | 173 | if( !dryRunFlag && !internalUpdate && !bNosync ){ |
| 170 | 174 | if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){ |
| 171 | 175 | fossil_fatal("update abandoned due to sync failure"); |
| 172 | 176 | } |
| @@ -403,10 +407,11 @@ | ||
| 403 | 407 | " WHERE id=:idt" |
| 404 | 408 | ); |
| 405 | 409 | assert( g.zLocalRoot!=0 ); |
| 406 | 410 | assert( strlen(g.zLocalRoot)>0 ); |
| 407 | 411 | assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' ); |
| 412 | + merge_info_init(); | |
| 408 | 413 | while( db_step(&q)==SQLITE_ROW ){ |
| 409 | 414 | const char *zName = db_column_text(&q, 0); /* The filename from root */ |
| 410 | 415 | int idv = db_column_int(&q, 1); /* VFILE entry for current */ |
| 411 | 416 | int ridv = db_column_int(&q, 2); /* RecordID for current */ |
| 412 | 417 | int idt = db_column_int(&q, 3); /* VFILE entry for target */ |
| @@ -418,13 +423,18 @@ | ||
| 418 | 423 | int islinkt = db_column_int(&q, 9); /* Is target file is a link */ |
| 419 | 424 | int deleted = db_column_int(&q, 10); /* Marked for deletion */ |
| 420 | 425 | char *zFullPath; /* Full pathname of the file */ |
| 421 | 426 | char *zFullNewPath; /* Full pathname of dest */ |
| 422 | 427 | char nameChng; /* True if the name changed */ |
| 428 | + const char *zOp = 0; /* Type of change. */ | |
| 429 | + i64 sz = 0; /* Size of the file */ | |
| 430 | + int nc = 0; /* Number of conflicts */ | |
| 431 | + const char *zErrMsg = 0; /* Error message */ | |
| 423 | 432 | |
| 424 | 433 | zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 425 | 434 | zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName); |
| 435 | + sz = file_size(zFullNewPath, ExtFILE); | |
| 426 | 436 | nameChng = fossil_strcmp(zName, zNewName); |
| 427 | 437 | nUpdate++; |
| 428 | 438 | if( deleted ){ |
| 429 | 439 | db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt); |
| 430 | 440 | } |
| @@ -432,10 +442,13 @@ | ||
| 432 | 442 | /* Conflict. This file has been added to the current check-out |
| 433 | 443 | ** but also exists in the target check-out. Use the current version. |
| 434 | 444 | */ |
| 435 | 445 | fossil_print("CONFLICT %s\n", zName); |
| 436 | 446 | nConflict++; |
| 447 | + zOp = "CONFLICT"; | |
| 448 | + nc = 1; | |
| 449 | + zErrMsg = "duplicate file"; | |
| 437 | 450 | }else if( idt>0 && idv==0 ){ |
| 438 | 451 | /* File added in the target. */ |
| 439 | 452 | if( file_isfile_or_link(zFullPath) ){ |
| 440 | 453 | /* Name of backup file with Original content */ |
| 441 | 454 | char *zOrig = file_newname(zFullPath, "original", 1); |
| @@ -444,10 +457,13 @@ | ||
| 444 | 457 | fossil_free(zOrig); |
| 445 | 458 | fossil_print("ADD %s - overwrites an unmanaged file", zName); |
| 446 | 459 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 447 | 460 | fossil_print("\n"); |
| 448 | 461 | nOverwrite++; |
| 462 | + nc = 1; | |
| 463 | + zOp = "CONFLICT"; | |
| 464 | + zErrMsg = "new file overwrites unmanaged file"; | |
| 449 | 465 | }else{ |
| 450 | 466 | fossil_print("ADD %s\n", zName); |
| 451 | 467 | } |
| 452 | 468 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 453 | 469 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| @@ -458,16 +474,18 @@ | ||
| 458 | 474 | }else{ |
| 459 | 475 | fossil_print("UPDATE %s\n", zName); |
| 460 | 476 | } |
| 461 | 477 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 462 | 478 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 479 | + zOp = "UPDATE"; | |
| 463 | 480 | }else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){ |
| 464 | 481 | /* The file missing from the local check-out. Restore it to the |
| 465 | 482 | ** version that appears in the target. */ |
| 466 | 483 | fossil_print("UPDATE %s\n", zName); |
| 467 | 484 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 468 | 485 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 486 | + zOp = "UPDATE"; | |
| 469 | 487 | }else if( idt==0 && idv>0 ){ |
| 470 | 488 | if( ridv==0 ){ |
| 471 | 489 | /* Added in current check-out. Continue to hold the file as |
| 472 | 490 | ** as an addition */ |
| 473 | 491 | db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); |
| @@ -474,10 +492,13 @@ | ||
| 474 | 492 | }else if( chnged ){ |
| 475 | 493 | /* Edited locally but deleted from the target. Do not track the |
| 476 | 494 | ** file but keep the edited version around. */ |
| 477 | 495 | fossil_print("CONFLICT %s - edited locally but deleted by update\n", |
| 478 | 496 | zName); |
| 497 | + zOp = "CONFLICT"; | |
| 498 | + zErrMsg = "edited locally but deleted by update"; | |
| 499 | + nc = 1; | |
| 479 | 500 | nConflict++; |
| 480 | 501 | }else{ |
| 481 | 502 | fossil_print("REMOVE %s\n", zName); |
| 482 | 503 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 483 | 504 | if( !dryRunFlag ){ |
| @@ -496,17 +517,19 @@ | ||
| 496 | 517 | } |
| 497 | 518 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ |
| 498 | 519 | /* Merge the changes in the current tree into the target version */ |
| 499 | 520 | Blob r, t, v; |
| 500 | 521 | int rc; |
| 522 | + | |
| 501 | 523 | if( nameChng ){ |
| 502 | 524 | fossil_print("MERGE %s -> %s\n", zName, zNewName); |
| 503 | 525 | }else{ |
| 504 | 526 | fossil_print("MERGE %s\n", zName); |
| 505 | 527 | } |
| 506 | 528 | if( islinkv || islinkt ){ |
| 507 | 529 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 530 | + zOp = "CONFLICT"; | |
| 508 | 531 | nConflict++; |
| 509 | 532 | }else{ |
| 510 | 533 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 511 | 534 | if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; |
| 512 | 535 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| @@ -517,12 +540,17 @@ | ||
| 517 | 540 | if( !dryRunFlag ){ |
| 518 | 541 | blob_write_to_file(&r, zFullNewPath); |
| 519 | 542 | file_setexe(zFullNewPath, isexe); |
| 520 | 543 | } |
| 521 | 544 | if( rc>0 ){ |
| 545 | + nc = rc; | |
| 546 | + zOp = "CONFLICT"; | |
| 547 | + zErrMsg = "merge conflicts"; | |
| 522 | 548 | fossil_print("***** %d merge conflicts in %s\n", rc, zNewName); |
| 523 | 549 | nConflict++; |
| 550 | + }else{ | |
| 551 | + zOp = "MERGE"; | |
| 524 | 552 | } |
| 525 | 553 | }else{ |
| 526 | 554 | if( !dryRunFlag ){ |
| 527 | 555 | if( !keepMergeFlag ){ |
| 528 | 556 | /* Name of backup file with Original content */ |
| @@ -539,10 +567,13 @@ | ||
| 539 | 567 | if( !dryRunFlag ){ |
| 540 | 568 | fossil_print(", original copy backed up locally"); |
| 541 | 569 | } |
| 542 | 570 | fossil_print("\n"); |
| 543 | 571 | nConflict++; |
| 572 | + zOp = "ERROR"; | |
| 573 | + zErrMsg = "cannot merge binary file"; | |
| 574 | + nc = 1; | |
| 544 | 575 | } |
| 545 | 576 | } |
| 546 | 577 | if( nameChng && !dryRunFlag ) file_delete(zFullPath); |
| 547 | 578 | blob_reset(&v); |
| 548 | 579 | blob_reset(&t); |
| @@ -556,27 +587,48 @@ | ||
| 556 | 587 | db_bind_int(&mtimeXfer, ":idt", idt); |
| 557 | 588 | db_step(&mtimeXfer); |
| 558 | 589 | db_reset(&mtimeXfer); |
| 559 | 590 | if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName); |
| 560 | 591 | } |
| 592 | + } | |
| 593 | + if( zOp!=0 ){ | |
| 594 | + db_multi_exec( | |
| 595 | + "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)" | |
| 596 | + "VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)", | |
| 597 | + /* op */ zOp, | |
| 598 | + /* fnp */ zName, | |
| 599 | + /* ridp */ ridv, | |
| 600 | + /* fn */ zNewName, | |
| 601 | + /* sz */ sz, | |
| 602 | + /* fnm */ zName, | |
| 603 | + /* ridm */ ridt, | |
| 604 | + /* fnr */ zNewName, | |
| 605 | + /* nc */ nc, | |
| 606 | + /* msg */ zErrMsg | |
| 607 | + ); | |
| 561 | 608 | } |
| 562 | 609 | free(zFullPath); |
| 563 | 610 | free(zFullNewPath); |
| 564 | 611 | } |
| 565 | 612 | db_finalize(&q); |
| 566 | 613 | db_finalize(&mtimeXfer); |
| 567 | 614 | fossil_print("%.79c\n",'-'); |
| 615 | + zNewBrName = branch_of_rid(tid); | |
| 616 | + if( g.argc<3 && fossil_strcmp(zCurBrName, zNewBrName)!=0 ){ | |
| 617 | + zBrChgMsg = mprintf(" Branch changed from %s to %s.", | |
| 618 | + zCurBrName, zNewBrName); | |
| 619 | + } | |
| 568 | 620 | if( nUpdate==0 ){ |
| 569 | 621 | show_common_info(tid, "checkout:", 1, 0); |
| 570 | - fossil_print("%-13s None. Already up-to-date\n", "changes:"); | |
| 622 | + fossil_print("%-13s None. Already up-to-date.%s\n", "changes:", zBrChgMsg); | |
| 571 | 623 | }else{ |
| 572 | 624 | fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid), |
| 573 | 625 | db_text("", "SELECT datetime(mtime) || ' UTC' FROM event " |
| 574 | 626 | " WHERE objid=%d", vid)); |
| 575 | 627 | show_common_info(tid, "updated-to:", 1, 0); |
| 576 | - fossil_print("%-13s %d file%s modified.\n", "changes:", | |
| 577 | - nUpdate, nUpdate>1 ? "s" : ""); | |
| 628 | + fossil_print("%-13s %d file%s modified.%s\n", "changes:", | |
| 629 | + nUpdate, nUpdate>1 ? "s" : "", zBrChgMsg); | |
| 578 | 630 | } |
| 579 | 631 | |
| 580 | 632 | /* Report on conflicts |
| 581 | 633 | */ |
| 582 | 634 | if( !dryRunFlag ){ |
| 583 | 635 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -132,10 +132,13 @@ | |
| 132 | int nUpdate = 0; /* Number of changes of any kind */ |
| 133 | int bNosync = 0; /* --nosync. Omit the auto-sync */ |
| 134 | int width; /* Width of printed comment lines */ |
| 135 | Stmt mtimeXfer; /* Statement to transfer mtimes */ |
| 136 | const char *zWidth; /* Width option string value */ |
| 137 | |
| 138 | if( !internalUpdate ){ |
| 139 | undo_capture_command_line(); |
| 140 | url_proxy_options(); |
| 141 | } |
| @@ -163,10 +166,11 @@ | |
| 163 | /* We should be done with options.. */ |
| 164 | verify_all_options(); |
| 165 | |
| 166 | db_must_be_within_tree(); |
| 167 | vid = db_lget_int("checkout", 0); |
| 168 | user_select(); |
| 169 | if( !dryRunFlag && !internalUpdate && !bNosync ){ |
| 170 | if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){ |
| 171 | fossil_fatal("update abandoned due to sync failure"); |
| 172 | } |
| @@ -403,10 +407,11 @@ | |
| 403 | " WHERE id=:idt" |
| 404 | ); |
| 405 | assert( g.zLocalRoot!=0 ); |
| 406 | assert( strlen(g.zLocalRoot)>0 ); |
| 407 | assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' ); |
| 408 | while( db_step(&q)==SQLITE_ROW ){ |
| 409 | const char *zName = db_column_text(&q, 0); /* The filename from root */ |
| 410 | int idv = db_column_int(&q, 1); /* VFILE entry for current */ |
| 411 | int ridv = db_column_int(&q, 2); /* RecordID for current */ |
| 412 | int idt = db_column_int(&q, 3); /* VFILE entry for target */ |
| @@ -418,13 +423,18 @@ | |
| 418 | int islinkt = db_column_int(&q, 9); /* Is target file is a link */ |
| 419 | int deleted = db_column_int(&q, 10); /* Marked for deletion */ |
| 420 | char *zFullPath; /* Full pathname of the file */ |
| 421 | char *zFullNewPath; /* Full pathname of dest */ |
| 422 | char nameChng; /* True if the name changed */ |
| 423 | |
| 424 | zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 425 | zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName); |
| 426 | nameChng = fossil_strcmp(zName, zNewName); |
| 427 | nUpdate++; |
| 428 | if( deleted ){ |
| 429 | db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt); |
| 430 | } |
| @@ -432,10 +442,13 @@ | |
| 432 | /* Conflict. This file has been added to the current check-out |
| 433 | ** but also exists in the target check-out. Use the current version. |
| 434 | */ |
| 435 | fossil_print("CONFLICT %s\n", zName); |
| 436 | nConflict++; |
| 437 | }else if( idt>0 && idv==0 ){ |
| 438 | /* File added in the target. */ |
| 439 | if( file_isfile_or_link(zFullPath) ){ |
| 440 | /* Name of backup file with Original content */ |
| 441 | char *zOrig = file_newname(zFullPath, "original", 1); |
| @@ -444,10 +457,13 @@ | |
| 444 | fossil_free(zOrig); |
| 445 | fossil_print("ADD %s - overwrites an unmanaged file", zName); |
| 446 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 447 | fossil_print("\n"); |
| 448 | nOverwrite++; |
| 449 | }else{ |
| 450 | fossil_print("ADD %s\n", zName); |
| 451 | } |
| 452 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 453 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| @@ -458,16 +474,18 @@ | |
| 458 | }else{ |
| 459 | fossil_print("UPDATE %s\n", zName); |
| 460 | } |
| 461 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 462 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 463 | }else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){ |
| 464 | /* The file missing from the local check-out. Restore it to the |
| 465 | ** version that appears in the target. */ |
| 466 | fossil_print("UPDATE %s\n", zName); |
| 467 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 468 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 469 | }else if( idt==0 && idv>0 ){ |
| 470 | if( ridv==0 ){ |
| 471 | /* Added in current check-out. Continue to hold the file as |
| 472 | ** as an addition */ |
| 473 | db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); |
| @@ -474,10 +492,13 @@ | |
| 474 | }else if( chnged ){ |
| 475 | /* Edited locally but deleted from the target. Do not track the |
| 476 | ** file but keep the edited version around. */ |
| 477 | fossil_print("CONFLICT %s - edited locally but deleted by update\n", |
| 478 | zName); |
| 479 | nConflict++; |
| 480 | }else{ |
| 481 | fossil_print("REMOVE %s\n", zName); |
| 482 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 483 | if( !dryRunFlag ){ |
| @@ -496,17 +517,19 @@ | |
| 496 | } |
| 497 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ |
| 498 | /* Merge the changes in the current tree into the target version */ |
| 499 | Blob r, t, v; |
| 500 | int rc; |
| 501 | if( nameChng ){ |
| 502 | fossil_print("MERGE %s -> %s\n", zName, zNewName); |
| 503 | }else{ |
| 504 | fossil_print("MERGE %s\n", zName); |
| 505 | } |
| 506 | if( islinkv || islinkt ){ |
| 507 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 508 | nConflict++; |
| 509 | }else{ |
| 510 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 511 | if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; |
| 512 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| @@ -517,12 +540,17 @@ | |
| 517 | if( !dryRunFlag ){ |
| 518 | blob_write_to_file(&r, zFullNewPath); |
| 519 | file_setexe(zFullNewPath, isexe); |
| 520 | } |
| 521 | if( rc>0 ){ |
| 522 | fossil_print("***** %d merge conflicts in %s\n", rc, zNewName); |
| 523 | nConflict++; |
| 524 | } |
| 525 | }else{ |
| 526 | if( !dryRunFlag ){ |
| 527 | if( !keepMergeFlag ){ |
| 528 | /* Name of backup file with Original content */ |
| @@ -539,10 +567,13 @@ | |
| 539 | if( !dryRunFlag ){ |
| 540 | fossil_print(", original copy backed up locally"); |
| 541 | } |
| 542 | fossil_print("\n"); |
| 543 | nConflict++; |
| 544 | } |
| 545 | } |
| 546 | if( nameChng && !dryRunFlag ) file_delete(zFullPath); |
| 547 | blob_reset(&v); |
| 548 | blob_reset(&t); |
| @@ -556,27 +587,48 @@ | |
| 556 | db_bind_int(&mtimeXfer, ":idt", idt); |
| 557 | db_step(&mtimeXfer); |
| 558 | db_reset(&mtimeXfer); |
| 559 | if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName); |
| 560 | } |
| 561 | } |
| 562 | free(zFullPath); |
| 563 | free(zFullNewPath); |
| 564 | } |
| 565 | db_finalize(&q); |
| 566 | db_finalize(&mtimeXfer); |
| 567 | fossil_print("%.79c\n",'-'); |
| 568 | if( nUpdate==0 ){ |
| 569 | show_common_info(tid, "checkout:", 1, 0); |
| 570 | fossil_print("%-13s None. Already up-to-date\n", "changes:"); |
| 571 | }else{ |
| 572 | fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid), |
| 573 | db_text("", "SELECT datetime(mtime) || ' UTC' FROM event " |
| 574 | " WHERE objid=%d", vid)); |
| 575 | show_common_info(tid, "updated-to:", 1, 0); |
| 576 | fossil_print("%-13s %d file%s modified.\n", "changes:", |
| 577 | nUpdate, nUpdate>1 ? "s" : ""); |
| 578 | } |
| 579 | |
| 580 | /* Report on conflicts |
| 581 | */ |
| 582 | if( !dryRunFlag ){ |
| 583 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -132,10 +132,13 @@ | |
| 132 | int nUpdate = 0; /* Number of changes of any kind */ |
| 133 | int bNosync = 0; /* --nosync. Omit the auto-sync */ |
| 134 | int width; /* Width of printed comment lines */ |
| 135 | Stmt mtimeXfer; /* Statement to transfer mtimes */ |
| 136 | const char *zWidth; /* Width option string value */ |
| 137 | const char *zCurBrName; /* Current branch name */ |
| 138 | const char *zNewBrName; /* New branch name */ |
| 139 | const char *zBrChgMsg = ""; /* Message to display if branch changes */ |
| 140 | |
| 141 | if( !internalUpdate ){ |
| 142 | undo_capture_command_line(); |
| 143 | url_proxy_options(); |
| 144 | } |
| @@ -163,10 +166,11 @@ | |
| 166 | /* We should be done with options.. */ |
| 167 | verify_all_options(); |
| 168 | |
| 169 | db_must_be_within_tree(); |
| 170 | vid = db_lget_int("checkout", 0); |
| 171 | zCurBrName = branch_of_rid(vid); |
| 172 | user_select(); |
| 173 | if( !dryRunFlag && !internalUpdate && !bNosync ){ |
| 174 | if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){ |
| 175 | fossil_fatal("update abandoned due to sync failure"); |
| 176 | } |
| @@ -403,10 +407,11 @@ | |
| 407 | " WHERE id=:idt" |
| 408 | ); |
| 409 | assert( g.zLocalRoot!=0 ); |
| 410 | assert( strlen(g.zLocalRoot)>0 ); |
| 411 | assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' ); |
| 412 | merge_info_init(); |
| 413 | while( db_step(&q)==SQLITE_ROW ){ |
| 414 | const char *zName = db_column_text(&q, 0); /* The filename from root */ |
| 415 | int idv = db_column_int(&q, 1); /* VFILE entry for current */ |
| 416 | int ridv = db_column_int(&q, 2); /* RecordID for current */ |
| 417 | int idt = db_column_int(&q, 3); /* VFILE entry for target */ |
| @@ -418,13 +423,18 @@ | |
| 423 | int islinkt = db_column_int(&q, 9); /* Is target file is a link */ |
| 424 | int deleted = db_column_int(&q, 10); /* Marked for deletion */ |
| 425 | char *zFullPath; /* Full pathname of the file */ |
| 426 | char *zFullNewPath; /* Full pathname of dest */ |
| 427 | char nameChng; /* True if the name changed */ |
| 428 | const char *zOp = 0; /* Type of change. */ |
| 429 | i64 sz = 0; /* Size of the file */ |
| 430 | int nc = 0; /* Number of conflicts */ |
| 431 | const char *zErrMsg = 0; /* Error message */ |
| 432 | |
| 433 | zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 434 | zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName); |
| 435 | sz = file_size(zFullNewPath, ExtFILE); |
| 436 | nameChng = fossil_strcmp(zName, zNewName); |
| 437 | nUpdate++; |
| 438 | if( deleted ){ |
| 439 | db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt); |
| 440 | } |
| @@ -432,10 +442,13 @@ | |
| 442 | /* Conflict. This file has been added to the current check-out |
| 443 | ** but also exists in the target check-out. Use the current version. |
| 444 | */ |
| 445 | fossil_print("CONFLICT %s\n", zName); |
| 446 | nConflict++; |
| 447 | zOp = "CONFLICT"; |
| 448 | nc = 1; |
| 449 | zErrMsg = "duplicate file"; |
| 450 | }else if( idt>0 && idv==0 ){ |
| 451 | /* File added in the target. */ |
| 452 | if( file_isfile_or_link(zFullPath) ){ |
| 453 | /* Name of backup file with Original content */ |
| 454 | char *zOrig = file_newname(zFullPath, "original", 1); |
| @@ -444,10 +457,13 @@ | |
| 457 | fossil_free(zOrig); |
| 458 | fossil_print("ADD %s - overwrites an unmanaged file", zName); |
| 459 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 460 | fossil_print("\n"); |
| 461 | nOverwrite++; |
| 462 | nc = 1; |
| 463 | zOp = "CONFLICT"; |
| 464 | zErrMsg = "new file overwrites unmanaged file"; |
| 465 | }else{ |
| 466 | fossil_print("ADD %s\n", zName); |
| 467 | } |
| 468 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 469 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| @@ -458,16 +474,18 @@ | |
| 474 | }else{ |
| 475 | fossil_print("UPDATE %s\n", zName); |
| 476 | } |
| 477 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 478 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 479 | zOp = "UPDATE"; |
| 480 | }else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){ |
| 481 | /* The file missing from the local check-out. Restore it to the |
| 482 | ** version that appears in the target. */ |
| 483 | fossil_print("UPDATE %s\n", zName); |
| 484 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 485 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 486 | zOp = "UPDATE"; |
| 487 | }else if( idt==0 && idv>0 ){ |
| 488 | if( ridv==0 ){ |
| 489 | /* Added in current check-out. Continue to hold the file as |
| 490 | ** as an addition */ |
| 491 | db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); |
| @@ -474,10 +492,13 @@ | |
| 492 | }else if( chnged ){ |
| 493 | /* Edited locally but deleted from the target. Do not track the |
| 494 | ** file but keep the edited version around. */ |
| 495 | fossil_print("CONFLICT %s - edited locally but deleted by update\n", |
| 496 | zName); |
| 497 | zOp = "CONFLICT"; |
| 498 | zErrMsg = "edited locally but deleted by update"; |
| 499 | nc = 1; |
| 500 | nConflict++; |
| 501 | }else{ |
| 502 | fossil_print("REMOVE %s\n", zName); |
| 503 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 504 | if( !dryRunFlag ){ |
| @@ -496,17 +517,19 @@ | |
| 517 | } |
| 518 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ |
| 519 | /* Merge the changes in the current tree into the target version */ |
| 520 | Blob r, t, v; |
| 521 | int rc; |
| 522 | |
| 523 | if( nameChng ){ |
| 524 | fossil_print("MERGE %s -> %s\n", zName, zNewName); |
| 525 | }else{ |
| 526 | fossil_print("MERGE %s\n", zName); |
| 527 | } |
| 528 | if( islinkv || islinkt ){ |
| 529 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 530 | zOp = "CONFLICT"; |
| 531 | nConflict++; |
| 532 | }else{ |
| 533 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 534 | if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; |
| 535 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| @@ -517,12 +540,17 @@ | |
| 540 | if( !dryRunFlag ){ |
| 541 | blob_write_to_file(&r, zFullNewPath); |
| 542 | file_setexe(zFullNewPath, isexe); |
| 543 | } |
| 544 | if( rc>0 ){ |
| 545 | nc = rc; |
| 546 | zOp = "CONFLICT"; |
| 547 | zErrMsg = "merge conflicts"; |
| 548 | fossil_print("***** %d merge conflicts in %s\n", rc, zNewName); |
| 549 | nConflict++; |
| 550 | }else{ |
| 551 | zOp = "MERGE"; |
| 552 | } |
| 553 | }else{ |
| 554 | if( !dryRunFlag ){ |
| 555 | if( !keepMergeFlag ){ |
| 556 | /* Name of backup file with Original content */ |
| @@ -539,10 +567,13 @@ | |
| 567 | if( !dryRunFlag ){ |
| 568 | fossil_print(", original copy backed up locally"); |
| 569 | } |
| 570 | fossil_print("\n"); |
| 571 | nConflict++; |
| 572 | zOp = "ERROR"; |
| 573 | zErrMsg = "cannot merge binary file"; |
| 574 | nc = 1; |
| 575 | } |
| 576 | } |
| 577 | if( nameChng && !dryRunFlag ) file_delete(zFullPath); |
| 578 | blob_reset(&v); |
| 579 | blob_reset(&t); |
| @@ -556,27 +587,48 @@ | |
| 587 | db_bind_int(&mtimeXfer, ":idt", idt); |
| 588 | db_step(&mtimeXfer); |
| 589 | db_reset(&mtimeXfer); |
| 590 | if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName); |
| 591 | } |
| 592 | } |
| 593 | if( zOp!=0 ){ |
| 594 | db_multi_exec( |
| 595 | "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)" |
| 596 | "VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)", |
| 597 | /* op */ zOp, |
| 598 | /* fnp */ zName, |
| 599 | /* ridp */ ridv, |
| 600 | /* fn */ zNewName, |
| 601 | /* sz */ sz, |
| 602 | /* fnm */ zName, |
| 603 | /* ridm */ ridt, |
| 604 | /* fnr */ zNewName, |
| 605 | /* nc */ nc, |
| 606 | /* msg */ zErrMsg |
| 607 | ); |
| 608 | } |
| 609 | free(zFullPath); |
| 610 | free(zFullNewPath); |
| 611 | } |
| 612 | db_finalize(&q); |
| 613 | db_finalize(&mtimeXfer); |
| 614 | fossil_print("%.79c\n",'-'); |
| 615 | zNewBrName = branch_of_rid(tid); |
| 616 | if( g.argc<3 && fossil_strcmp(zCurBrName, zNewBrName)!=0 ){ |
| 617 | zBrChgMsg = mprintf(" Branch changed from %s to %s.", |
| 618 | zCurBrName, zNewBrName); |
| 619 | } |
| 620 | if( nUpdate==0 ){ |
| 621 | show_common_info(tid, "checkout:", 1, 0); |
| 622 | fossil_print("%-13s None. Already up-to-date.%s\n", "changes:", zBrChgMsg); |
| 623 | }else{ |
| 624 | fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid), |
| 625 | db_text("", "SELECT datetime(mtime) || ' UTC' FROM event " |
| 626 | " WHERE objid=%d", vid)); |
| 627 | show_common_info(tid, "updated-to:", 1, 0); |
| 628 | fossil_print("%-13s %d file%s modified.%s\n", "changes:", |
| 629 | nUpdate, nUpdate>1 ? "s" : "", zBrChgMsg); |
| 630 | } |
| 631 | |
| 632 | /* Report on conflicts |
| 633 | */ |
| 634 | if( !dryRunFlag ){ |
| 635 |
+4
-4
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -622,14 +622,14 @@ | ||
| 622 | 622 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 623 | 623 | blob_reset(&wiki); |
| 624 | 624 | } |
| 625 | 625 | manifest_destroy(pWiki); |
| 626 | 626 | if( !isPopup ){ |
| 627 | - char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" | |
| 628 | - "Attachments</a>:</h2><ul>", | |
| 627 | + char * zLabel = mprintf("<h2><a href='%R/attachlist?page=%T'>" | |
| 628 | + "Attachments</a>:</h2>", | |
| 629 | 629 | zPageName); |
| 630 | - attachment_list(zPageName, zLabel); | |
| 630 | + attachment_list(zPageName, zLabel, 1); | |
| 631 | 631 | fossil_free(zLabel); |
| 632 | 632 | document_emit_js(/*for optional pikchr support*/); |
| 633 | 633 | style_finish_page(); |
| 634 | 634 | } |
| 635 | 635 | } |
| @@ -1331,11 +1331,11 @@ | ||
| 1331 | 1331 | CX("<div id='fossil-status-bar' " |
| 1332 | 1332 | "title='Status message area. Double-click to clear them.'>" |
| 1333 | 1333 | "Status messages will go here.</div>\n" |
| 1334 | 1334 | /* will be moved into the tab container via JS */); |
| 1335 | 1335 | |
| 1336 | - CX("<div id='wikiedit-edit-status''>" | |
| 1336 | + CX("<div id='wikiedit-edit-status'>" | |
| 1337 | 1337 | "<span class='name'></span>" |
| 1338 | 1338 | "<span class='links'></span>" |
| 1339 | 1339 | "</div>"); |
| 1340 | 1340 | |
| 1341 | 1341 | /* Main tab container... */ |
| 1342 | 1342 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -622,14 +622,14 @@ | |
| 622 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 623 | blob_reset(&wiki); |
| 624 | } |
| 625 | manifest_destroy(pWiki); |
| 626 | if( !isPopup ){ |
| 627 | char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" |
| 628 | "Attachments</a>:</h2><ul>", |
| 629 | zPageName); |
| 630 | attachment_list(zPageName, zLabel); |
| 631 | fossil_free(zLabel); |
| 632 | document_emit_js(/*for optional pikchr support*/); |
| 633 | style_finish_page(); |
| 634 | } |
| 635 | } |
| @@ -1331,11 +1331,11 @@ | |
| 1331 | CX("<div id='fossil-status-bar' " |
| 1332 | "title='Status message area. Double-click to clear them.'>" |
| 1333 | "Status messages will go here.</div>\n" |
| 1334 | /* will be moved into the tab container via JS */); |
| 1335 | |
| 1336 | CX("<div id='wikiedit-edit-status''>" |
| 1337 | "<span class='name'></span>" |
| 1338 | "<span class='links'></span>" |
| 1339 | "</div>"); |
| 1340 | |
| 1341 | /* Main tab container... */ |
| 1342 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -622,14 +622,14 @@ | |
| 622 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 623 | blob_reset(&wiki); |
| 624 | } |
| 625 | manifest_destroy(pWiki); |
| 626 | if( !isPopup ){ |
| 627 | char * zLabel = mprintf("<h2><a href='%R/attachlist?page=%T'>" |
| 628 | "Attachments</a>:</h2>", |
| 629 | zPageName); |
| 630 | attachment_list(zPageName, zLabel, 1); |
| 631 | fossil_free(zLabel); |
| 632 | document_emit_js(/*for optional pikchr support*/); |
| 633 | style_finish_page(); |
| 634 | } |
| 635 | } |
| @@ -1331,11 +1331,11 @@ | |
| 1331 | CX("<div id='fossil-status-bar' " |
| 1332 | "title='Status message area. Double-click to clear them.'>" |
| 1333 | "Status messages will go here.</div>\n" |
| 1334 | /* will be moved into the tab container via JS */); |
| 1335 | |
| 1336 | CX("<div id='wikiedit-edit-status'>" |
| 1337 | "<span class='name'></span>" |
| 1338 | "<span class='links'></span>" |
| 1339 | "</div>"); |
| 1340 | |
| 1341 | /* Main tab container... */ |
| 1342 |
+229
| --- src/winfile.c | ||
| +++ src/winfile.c | ||
| @@ -290,6 +290,235 @@ | ||
| 290 | 290 | fossil_free(zWide); |
| 291 | 291 | for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; |
| 292 | 292 | strncpy(zBuf, zUtf8, nBuf); |
| 293 | 293 | fossil_path_free(zUtf8); |
| 294 | 294 | } |
| 295 | + | |
| 296 | +/* Perform case-insensitive comparison of two UTF-16 file names. Try to load the | |
| 297 | +** CompareStringOrdinal() function on Windows Vista and newer, and resort to the | |
| 298 | +** RtlEqualUnicodeString() function on Windows XP. | |
| 299 | +** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW() | |
| 300 | +** performs linguistic comparison, while the former performs binary comparison. | |
| 301 | +** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is | |
| 302 | +** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases | |
| 303 | +** that are technically impossible and contradicting all known laws of physics. | |
| 304 | +*/ | |
| 305 | +int win32_filenames_equal_nocase( | |
| 306 | + const wchar_t *fn1, | |
| 307 | + const wchar_t *fn2 | |
| 308 | +){ | |
| 309 | + static FARPROC fnCompareStringOrdinal; | |
| 310 | + static FARPROC fnRtlInitUnicodeString; | |
| 311 | + static FARPROC fnRtlEqualUnicodeString; | |
| 312 | + static int loaded_CompareStringOrdinal; | |
| 313 | + static int loaded_RtlUnicodeStringAPIs; | |
| 314 | + if( !loaded_CompareStringOrdinal ){ | |
| 315 | + fnCompareStringOrdinal = | |
| 316 | + GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); | |
| 317 | + loaded_CompareStringOrdinal = 1; | |
| 318 | + } | |
| 319 | + if( fnCompareStringOrdinal ){ | |
| 320 | + return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; | |
| 321 | + } | |
| 322 | + if( !loaded_RtlUnicodeStringAPIs ){ | |
| 323 | + fnRtlInitUnicodeString = | |
| 324 | + GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); | |
| 325 | + fnRtlEqualUnicodeString = | |
| 326 | + GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); | |
| 327 | + loaded_RtlUnicodeStringAPIs = 1; | |
| 328 | + } | |
| 329 | + if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){ | |
| 330 | + struct { /* UNICODE_STRING from <ntdef.h> */ | |
| 331 | + unsigned short Length; | |
| 332 | + unsigned short MaximumLength; | |
| 333 | + wchar_t *Buffer; | |
| 334 | + } u1, u2; | |
| 335 | + fnRtlInitUnicodeString(&u1,fn1); | |
| 336 | + fnRtlInitUnicodeString(&u2,fn2); | |
| 337 | + return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1); | |
| 338 | + } | |
| 339 | + /* In what kind of strange parallel universe are we? */ | |
| 340 | + return lstrcmpiW(fn1,fn2)==0; | |
| 341 | +} | |
| 342 | + | |
| 343 | +/* Helper macros to deal with directory separators. */ | |
| 344 | +#define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) | |
| 345 | +#define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} | |
| 346 | + | |
| 347 | +/* The Win32 version of file_case_preferred_name() from file.c, which is able to | |
| 348 | +** find case-preserved file names containing non-ASCII characters. The result is | |
| 349 | +** allocated by fossil_malloc() and *should* be free'd by the caller. While this | |
| 350 | +** function usually gets canonicalized paths, it is able to handle any input and | |
| 351 | +** figure out more cases than the original: | |
| 352 | +** | |
| 353 | +** fossil test-case-filename C:/ .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE | |
| 354 | +** → Original: .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE | |
| 355 | +** → Modified: .//..\Windows\/.//.\System32\.\notepad.exe | |
| 356 | +** | |
| 357 | +** md ÄÖÜ | |
| 358 | +** fossil test-case-filename ./\ .\äöü\/[empty]\\/ | |
| 359 | +** → Original: ./äöü\/[empty]\\/ | |
| 360 | +** → Modified: .\ÄÖÜ\/[empty]\\/ | |
| 361 | +** | |
| 362 | +** The function preserves slashes and backslashes: only single file or directory | |
| 363 | +** components without directory separators ("basenames") are converted to UTF-16 | |
| 364 | +** using fossil_utf8_to_path(), so bypassing its slash ↔ backslash translations. | |
| 365 | +** Note that the original function doesn't preserve all slashes and backslashes, | |
| 366 | +** for example in the second example above. | |
| 367 | +** | |
| 368 | +** NOTE: As of Windows 10, version 1803, case sensitivity may be enabled on a | |
| 369 | +** per-directory basis, as returned by NtQueryInformationFile() with the file | |
| 370 | +** information class FILE_CASE_SENSITIVE_INFORMATION. So this function may be | |
| 371 | +** changed to act like fossil_strdup() for files located in such directories. | |
| 372 | +*/ | |
| 373 | +char *win32_file_case_preferred_name( | |
| 374 | + const char *zBase, | |
| 375 | + const char *zPath | |
| 376 | +){ | |
| 377 | + int cchBase; | |
| 378 | + int cchPath; | |
| 379 | + int cchBuf; | |
| 380 | + int cchRes; | |
| 381 | + char *zBuf; | |
| 382 | + char *zRes; | |
| 383 | + int ncUsed; | |
| 384 | + int i, j; | |
| 385 | + if( filenames_are_case_sensitive() ){ | |
| 386 | + return fossil_strdup(zPath); | |
| 387 | + } | |
| 388 | + cchBase = strlen(zBase); | |
| 389 | + cchPath = strlen(zPath); | |
| 390 | + cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ | |
| 391 | + cchRes = cchPath + 1; /* + NULL */ | |
| 392 | + zBuf = fossil_malloc(cchBuf); | |
| 393 | + zRes = fossil_malloc(cchRes); | |
| 394 | + ncUsed = 0; | |
| 395 | + memcpy(zBuf,zBase,cchBase); | |
| 396 | + if( !IS_DIRSEP(zBuf,cchBase-1) ){ | |
| 397 | + zBuf[cchBase++]=L'/'; | |
| 398 | + } | |
| 399 | + memcpy(zBuf+cchBase,zPath,cchPath+1); | |
| 400 | + i = j = cchBase; | |
| 401 | + while( 1 ){ | |
| 402 | + WIN32_FIND_DATAW fd; | |
| 403 | + HANDLE hFind; | |
| 404 | + wchar_t *wzBuf; | |
| 405 | + char *zCompBuf = 0; | |
| 406 | + char *zComp = &zBuf[i]; | |
| 407 | + int cchComp; | |
| 408 | + char chSep; | |
| 409 | + int fDone; | |
| 410 | + if( IS_DIRSEP(zBuf,i) ){ | |
| 411 | + if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/ | |
| 412 | + cchRes += 32; /* Overprovisioning. */ | |
| 413 | + zRes = fossil_realloc(zRes,cchRes); | |
| 414 | + } | |
| 415 | + zRes[ncUsed++] = zBuf[i]; | |
| 416 | + i = j = i+1; | |
| 417 | + continue; | |
| 418 | + } | |
| 419 | + NEXT_DIRSEP(zBuf,j); | |
| 420 | + fDone = zBuf[j]==0; | |
| 421 | + chSep = zBuf[j]; | |
| 422 | + zBuf[j] = 0; /* Truncate working buffer. */ | |
| 423 | + wzBuf = fossil_utf8_to_path(zBuf,0); | |
| 424 | + hFind = FindFirstFileW(wzBuf,&fd); | |
| 425 | + if( hFind!=INVALID_HANDLE_VALUE ){ | |
| 426 | + wchar_t *wzComp = fossil_utf8_to_path(zComp,0); | |
| 427 | + FindClose(hFind); | |
| 428 | + /* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ | |
| 429 | + if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){ | |
| 430 | + zCompBuf = fossil_path_to_utf8(fd.cFileName); | |
| 431 | + zComp = zCompBuf; | |
| 432 | + } | |
| 433 | + fossil_path_free(wzComp); | |
| 434 | + } | |
| 435 | + fossil_path_free(wzBuf); | |
| 436 | + cchComp = strlen(zComp); | |
| 437 | + if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */ | |
| 438 | + cchRes += cchComp + 32; /* Overprovisioning. */ | |
| 439 | + zRes = fossil_realloc(zRes,cchRes); | |
| 440 | + } | |
| 441 | + memcpy(zRes+ncUsed,zComp,cchComp); | |
| 442 | + ncUsed += cchComp; | |
| 443 | + if( zCompBuf ){ | |
| 444 | + fossil_path_free(zCompBuf); | |
| 445 | + } | |
| 446 | + if( fDone ){ | |
| 447 | + zRes[ncUsed] = 0; | |
| 448 | + break; | |
| 449 | + } | |
| 450 | + zBuf[j] = chSep; /* Undo working buffer truncation. */ | |
| 451 | + i = j; | |
| 452 | + } | |
| 453 | + fossil_free(zBuf); | |
| 454 | + return zRes; | |
| 455 | +} | |
| 456 | + | |
| 457 | +/* Return the unique identifier (UID) for a file, made up of the file identifier | |
| 458 | +** (equal to "inode" for Unix-style file systems) plus the volume serial number. | |
| 459 | +** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort | |
| 460 | +** to the GetFileInformationByHandle() function on Windows XP. The result string | |
| 461 | +** is allocated by mprintf(), or NULL on failure. | |
| 462 | +*/ | |
| 463 | +char *win32_file_id( | |
| 464 | + const char *zFileName | |
| 465 | +){ | |
| 466 | + static FARPROC fnGetFileInformationByHandleEx; | |
| 467 | + static int loaded_fnGetFileInformationByHandleEx; | |
| 468 | + wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0); | |
| 469 | + HANDLE hFile; | |
| 470 | + char *zFileId = 0; | |
| 471 | + hFile = CreateFileW( | |
| 472 | + wzFileName, | |
| 473 | + 0, | |
| 474 | + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 475 | + NULL, | |
| 476 | + OPEN_EXISTING, | |
| 477 | + FILE_FLAG_BACKUP_SEMANTICS, | |
| 478 | + NULL); | |
| 479 | + if( hFile!=INVALID_HANDLE_VALUE ){ | |
| 480 | + BY_HANDLE_FILE_INFORMATION fi; | |
| 481 | + struct { /* FILE_ID_INFO from <winbase.h> */ | |
| 482 | + u64 VolumeSerialNumber; | |
| 483 | + unsigned char FileId[16]; | |
| 484 | + } fi2; | |
| 485 | + if( !loaded_fnGetFileInformationByHandleEx ){ | |
| 486 | + fnGetFileInformationByHandleEx = GetProcAddress( | |
| 487 | + GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx"); | |
| 488 | + loaded_fnGetFileInformationByHandleEx = 1; | |
| 489 | + } | |
| 490 | + if( fnGetFileInformationByHandleEx ){ | |
| 491 | + if( fnGetFileInformationByHandleEx( | |
| 492 | + hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){ | |
| 493 | + zFileId = mprintf( | |
| 494 | + "%016llx/" | |
| 495 | + "%02x%02x%02x%02x%02x%02x%02x%02x" | |
| 496 | + "%02x%02x%02x%02x%02x%02x%02x%02x", | |
| 497 | + fi2.VolumeSerialNumber, | |
| 498 | + fi2.FileId[15], fi2.FileId[14], | |
| 499 | + fi2.FileId[13], fi2.FileId[12], | |
| 500 | + fi2.FileId[11], fi2.FileId[10], | |
| 501 | + fi2.FileId[9], fi2.FileId[8], | |
| 502 | + fi2.FileId[7], fi2.FileId[6], | |
| 503 | + fi2.FileId[5], fi2.FileId[4], | |
| 504 | + fi2.FileId[3], fi2.FileId[2], | |
| 505 | + fi2.FileId[1], fi2.FileId[0]); | |
| 506 | + } | |
| 507 | + } | |
| 508 | + if( zFileId==0 ){ | |
| 509 | + if( GetFileInformationByHandle(hFile,&fi) ){ | |
| 510 | + ULARGE_INTEGER FileId = { | |
| 511 | + /*.LowPart = */ fi.nFileIndexLow, | |
| 512 | + /*.HighPart = */ fi.nFileIndexHigh | |
| 513 | + }; | |
| 514 | + zFileId = mprintf( | |
| 515 | + "%08x/%016llx", | |
| 516 | + fi.dwVolumeSerialNumber,(u64)FileId.QuadPart); | |
| 517 | + } | |
| 518 | + } | |
| 519 | + CloseHandle(hFile); | |
| 520 | + } | |
| 521 | + fossil_path_free(wzFileName); | |
| 522 | + return zFileId; | |
| 523 | +} | |
| 295 | 524 | #endif /* _WIN32 -- This code is for win32 only */ |
| 296 | 525 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -290,6 +290,235 @@ | |
| 290 | fossil_free(zWide); |
| 291 | for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; |
| 292 | strncpy(zBuf, zUtf8, nBuf); |
| 293 | fossil_path_free(zUtf8); |
| 294 | } |
| 295 | #endif /* _WIN32 -- This code is for win32 only */ |
| 296 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -290,6 +290,235 @@ | |
| 290 | fossil_free(zWide); |
| 291 | for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; |
| 292 | strncpy(zBuf, zUtf8, nBuf); |
| 293 | fossil_path_free(zUtf8); |
| 294 | } |
| 295 | |
| 296 | /* Perform case-insensitive comparison of two UTF-16 file names. Try to load the |
| 297 | ** CompareStringOrdinal() function on Windows Vista and newer, and resort to the |
| 298 | ** RtlEqualUnicodeString() function on Windows XP. |
| 299 | ** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW() |
| 300 | ** performs linguistic comparison, while the former performs binary comparison. |
| 301 | ** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is |
| 302 | ** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases |
| 303 | ** that are technically impossible and contradicting all known laws of physics. |
| 304 | */ |
| 305 | int win32_filenames_equal_nocase( |
| 306 | const wchar_t *fn1, |
| 307 | const wchar_t *fn2 |
| 308 | ){ |
| 309 | static FARPROC fnCompareStringOrdinal; |
| 310 | static FARPROC fnRtlInitUnicodeString; |
| 311 | static FARPROC fnRtlEqualUnicodeString; |
| 312 | static int loaded_CompareStringOrdinal; |
| 313 | static int loaded_RtlUnicodeStringAPIs; |
| 314 | if( !loaded_CompareStringOrdinal ){ |
| 315 | fnCompareStringOrdinal = |
| 316 | GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
| 317 | loaded_CompareStringOrdinal = 1; |
| 318 | } |
| 319 | if( fnCompareStringOrdinal ){ |
| 320 | return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; |
| 321 | } |
| 322 | if( !loaded_RtlUnicodeStringAPIs ){ |
| 323 | fnRtlInitUnicodeString = |
| 324 | GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); |
| 325 | fnRtlEqualUnicodeString = |
| 326 | GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); |
| 327 | loaded_RtlUnicodeStringAPIs = 1; |
| 328 | } |
| 329 | if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){ |
| 330 | struct { /* UNICODE_STRING from <ntdef.h> */ |
| 331 | unsigned short Length; |
| 332 | unsigned short MaximumLength; |
| 333 | wchar_t *Buffer; |
| 334 | } u1, u2; |
| 335 | fnRtlInitUnicodeString(&u1,fn1); |
| 336 | fnRtlInitUnicodeString(&u2,fn2); |
| 337 | return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1); |
| 338 | } |
| 339 | /* In what kind of strange parallel universe are we? */ |
| 340 | return lstrcmpiW(fn1,fn2)==0; |
| 341 | } |
| 342 | |
| 343 | /* Helper macros to deal with directory separators. */ |
| 344 | #define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) |
| 345 | #define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} |
| 346 | |
| 347 | /* The Win32 version of file_case_preferred_name() from file.c, which is able to |
| 348 | ** find case-preserved file names containing non-ASCII characters. The result is |
| 349 | ** allocated by fossil_malloc() and *should* be free'd by the caller. While this |
| 350 | ** function usually gets canonicalized paths, it is able to handle any input and |
| 351 | ** figure out more cases than the original: |
| 352 | ** |
| 353 | ** fossil test-case-filename C:/ .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE |
| 354 | ** → Original: .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE |
| 355 | ** → Modified: .//..\Windows\/.//.\System32\.\notepad.exe |
| 356 | ** |
| 357 | ** md ÄÖÜ |
| 358 | ** fossil test-case-filename ./\ .\äöü\/[empty]\\/ |
| 359 | ** → Original: ./äöü\/[empty]\\/ |
| 360 | ** → Modified: .\ÄÖÜ\/[empty]\\/ |
| 361 | ** |
| 362 | ** The function preserves slashes and backslashes: only single file or directory |
| 363 | ** components without directory separators ("basenames") are converted to UTF-16 |
| 364 | ** using fossil_utf8_to_path(), so bypassing its slash ↔ backslash translations. |
| 365 | ** Note that the original function doesn't preserve all slashes and backslashes, |
| 366 | ** for example in the second example above. |
| 367 | ** |
| 368 | ** NOTE: As of Windows 10, version 1803, case sensitivity may be enabled on a |
| 369 | ** per-directory basis, as returned by NtQueryInformationFile() with the file |
| 370 | ** information class FILE_CASE_SENSITIVE_INFORMATION. So this function may be |
| 371 | ** changed to act like fossil_strdup() for files located in such directories. |
| 372 | */ |
| 373 | char *win32_file_case_preferred_name( |
| 374 | const char *zBase, |
| 375 | const char *zPath |
| 376 | ){ |
| 377 | int cchBase; |
| 378 | int cchPath; |
| 379 | int cchBuf; |
| 380 | int cchRes; |
| 381 | char *zBuf; |
| 382 | char *zRes; |
| 383 | int ncUsed; |
| 384 | int i, j; |
| 385 | if( filenames_are_case_sensitive() ){ |
| 386 | return fossil_strdup(zPath); |
| 387 | } |
| 388 | cchBase = strlen(zBase); |
| 389 | cchPath = strlen(zPath); |
| 390 | cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ |
| 391 | cchRes = cchPath + 1; /* + NULL */ |
| 392 | zBuf = fossil_malloc(cchBuf); |
| 393 | zRes = fossil_malloc(cchRes); |
| 394 | ncUsed = 0; |
| 395 | memcpy(zBuf,zBase,cchBase); |
| 396 | if( !IS_DIRSEP(zBuf,cchBase-1) ){ |
| 397 | zBuf[cchBase++]=L'/'; |
| 398 | } |
| 399 | memcpy(zBuf+cchBase,zPath,cchPath+1); |
| 400 | i = j = cchBase; |
| 401 | while( 1 ){ |
| 402 | WIN32_FIND_DATAW fd; |
| 403 | HANDLE hFind; |
| 404 | wchar_t *wzBuf; |
| 405 | char *zCompBuf = 0; |
| 406 | char *zComp = &zBuf[i]; |
| 407 | int cchComp; |
| 408 | char chSep; |
| 409 | int fDone; |
| 410 | if( IS_DIRSEP(zBuf,i) ){ |
| 411 | if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/ |
| 412 | cchRes += 32; /* Overprovisioning. */ |
| 413 | zRes = fossil_realloc(zRes,cchRes); |
| 414 | } |
| 415 | zRes[ncUsed++] = zBuf[i]; |
| 416 | i = j = i+1; |
| 417 | continue; |
| 418 | } |
| 419 | NEXT_DIRSEP(zBuf,j); |
| 420 | fDone = zBuf[j]==0; |
| 421 | chSep = zBuf[j]; |
| 422 | zBuf[j] = 0; /* Truncate working buffer. */ |
| 423 | wzBuf = fossil_utf8_to_path(zBuf,0); |
| 424 | hFind = FindFirstFileW(wzBuf,&fd); |
| 425 | if( hFind!=INVALID_HANDLE_VALUE ){ |
| 426 | wchar_t *wzComp = fossil_utf8_to_path(zComp,0); |
| 427 | FindClose(hFind); |
| 428 | /* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ |
| 429 | if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){ |
| 430 | zCompBuf = fossil_path_to_utf8(fd.cFileName); |
| 431 | zComp = zCompBuf; |
| 432 | } |
| 433 | fossil_path_free(wzComp); |
| 434 | } |
| 435 | fossil_path_free(wzBuf); |
| 436 | cchComp = strlen(zComp); |
| 437 | if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */ |
| 438 | cchRes += cchComp + 32; /* Overprovisioning. */ |
| 439 | zRes = fossil_realloc(zRes,cchRes); |
| 440 | } |
| 441 | memcpy(zRes+ncUsed,zComp,cchComp); |
| 442 | ncUsed += cchComp; |
| 443 | if( zCompBuf ){ |
| 444 | fossil_path_free(zCompBuf); |
| 445 | } |
| 446 | if( fDone ){ |
| 447 | zRes[ncUsed] = 0; |
| 448 | break; |
| 449 | } |
| 450 | zBuf[j] = chSep; /* Undo working buffer truncation. */ |
| 451 | i = j; |
| 452 | } |
| 453 | fossil_free(zBuf); |
| 454 | return zRes; |
| 455 | } |
| 456 | |
| 457 | /* Return the unique identifier (UID) for a file, made up of the file identifier |
| 458 | ** (equal to "inode" for Unix-style file systems) plus the volume serial number. |
| 459 | ** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort |
| 460 | ** to the GetFileInformationByHandle() function on Windows XP. The result string |
| 461 | ** is allocated by mprintf(), or NULL on failure. |
| 462 | */ |
| 463 | char *win32_file_id( |
| 464 | const char *zFileName |
| 465 | ){ |
| 466 | static FARPROC fnGetFileInformationByHandleEx; |
| 467 | static int loaded_fnGetFileInformationByHandleEx; |
| 468 | wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0); |
| 469 | HANDLE hFile; |
| 470 | char *zFileId = 0; |
| 471 | hFile = CreateFileW( |
| 472 | wzFileName, |
| 473 | 0, |
| 474 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 475 | NULL, |
| 476 | OPEN_EXISTING, |
| 477 | FILE_FLAG_BACKUP_SEMANTICS, |
| 478 | NULL); |
| 479 | if( hFile!=INVALID_HANDLE_VALUE ){ |
| 480 | BY_HANDLE_FILE_INFORMATION fi; |
| 481 | struct { /* FILE_ID_INFO from <winbase.h> */ |
| 482 | u64 VolumeSerialNumber; |
| 483 | unsigned char FileId[16]; |
| 484 | } fi2; |
| 485 | if( !loaded_fnGetFileInformationByHandleEx ){ |
| 486 | fnGetFileInformationByHandleEx = GetProcAddress( |
| 487 | GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx"); |
| 488 | loaded_fnGetFileInformationByHandleEx = 1; |
| 489 | } |
| 490 | if( fnGetFileInformationByHandleEx ){ |
| 491 | if( fnGetFileInformationByHandleEx( |
| 492 | hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){ |
| 493 | zFileId = mprintf( |
| 494 | "%016llx/" |
| 495 | "%02x%02x%02x%02x%02x%02x%02x%02x" |
| 496 | "%02x%02x%02x%02x%02x%02x%02x%02x", |
| 497 | fi2.VolumeSerialNumber, |
| 498 | fi2.FileId[15], fi2.FileId[14], |
| 499 | fi2.FileId[13], fi2.FileId[12], |
| 500 | fi2.FileId[11], fi2.FileId[10], |
| 501 | fi2.FileId[9], fi2.FileId[8], |
| 502 | fi2.FileId[7], fi2.FileId[6], |
| 503 | fi2.FileId[5], fi2.FileId[4], |
| 504 | fi2.FileId[3], fi2.FileId[2], |
| 505 | fi2.FileId[1], fi2.FileId[0]); |
| 506 | } |
| 507 | } |
| 508 | if( zFileId==0 ){ |
| 509 | if( GetFileInformationByHandle(hFile,&fi) ){ |
| 510 | ULARGE_INTEGER FileId = { |
| 511 | /*.LowPart = */ fi.nFileIndexLow, |
| 512 | /*.HighPart = */ fi.nFileIndexHigh |
| 513 | }; |
| 514 | zFileId = mprintf( |
| 515 | "%08x/%016llx", |
| 516 | fi.dwVolumeSerialNumber,(u64)FileId.QuadPart); |
| 517 | } |
| 518 | } |
| 519 | CloseHandle(hFile); |
| 520 | } |
| 521 | fossil_path_free(wzFileName); |
| 522 | return zFileId; |
| 523 | } |
| 524 | #endif /* _WIN32 -- This code is for win32 only */ |
| 525 |
+40
-1
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -336,10 +336,11 @@ | ||
| 336 | 336 | || !blob_is_filename(&pXfer->aToken[1]) |
| 337 | 337 | || !blob_is_int64(&pXfer->aToken[2], &mtime) |
| 338 | 338 | || (!blob_eq(pHash,"-") && !blob_is_hname(pHash)) |
| 339 | 339 | || !blob_is_int(&pXfer->aToken[4], &sz) |
| 340 | 340 | || !blob_is_int(&pXfer->aToken[5], &flags) |
| 341 | + || (mtime<0 || sz<0 || flags<0) | |
| 341 | 342 | ){ |
| 342 | 343 | blob_appendf(&pXfer->err, "malformed uvfile line"); |
| 343 | 344 | return; |
| 344 | 345 | } |
| 345 | 346 | blob_init(&content, 0, 0); |
| @@ -1119,10 +1120,24 @@ | ||
| 1119 | 1120 | ** Return the TH1 code to evaluate when a ticket change is processed. |
| 1120 | 1121 | */ |
| 1121 | 1122 | const char *xfer_ticket_code(void){ |
| 1122 | 1123 | return db_get("xfer-ticket-script", 0); |
| 1123 | 1124 | } |
| 1125 | + | |
| 1126 | +/* | |
| 1127 | +** Reset the CGI content, roll back any pending db transaction, and | |
| 1128 | +** emit an "error" xfer message. The message text gets fossil-encoded | |
| 1129 | +** by this function. This is only intended for use with | |
| 1130 | +** fail-fast/fatal errors, not ones which can be skipped over. | |
| 1131 | +*/ | |
| 1132 | +static void xfer_fatal_error(const char *zMsg){ | |
| 1133 | + cgi_reset_content(); | |
| 1134 | + if( db_transaction_nesting_depth()>0 ){ | |
| 1135 | + db_rollback_transaction(); | |
| 1136 | + } | |
| 1137 | + @ error %F(zMsg) | |
| 1138 | +} | |
| 1124 | 1139 | |
| 1125 | 1140 | /* |
| 1126 | 1141 | ** Run the specified TH1 script, if any, and returns 1 on error. |
| 1127 | 1142 | */ |
| 1128 | 1143 | int xfer_run_script( |
| @@ -1461,10 +1476,14 @@ | ||
| 1461 | 1476 | int seqno, max; |
| 1462 | 1477 | if( iVers>=3 ){ |
| 1463 | 1478 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1464 | 1479 | } |
| 1465 | 1480 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1481 | + if( seqno<=0 ){ | |
| 1482 | + xfer_fatal_error("invalid clone sequence number"); | |
| 1483 | + return; | |
| 1484 | + } | |
| 1466 | 1485 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1467 | 1486 | while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){ |
| 1468 | 1487 | if( time(NULL) >= xfer.maxTime ) break; |
| 1469 | 1488 | if( iVers>=3 ){ |
| 1470 | 1489 | send_compressed_file(&xfer, seqno); |
| @@ -1532,10 +1551,14 @@ | ||
| 1532 | 1551 | */ |
| 1533 | 1552 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 1534 | 1553 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 1535 | 1554 | const char *zName = blob_str(&xfer.aToken[1]); |
| 1536 | 1555 | Blob content; |
| 1556 | + if( size<0 ){ | |
| 1557 | + xfer_fatal_error("invalid config record"); | |
| 1558 | + return; | |
| 1559 | + } | |
| 1537 | 1560 | blob_zero(&content); |
| 1538 | 1561 | blob_extract(xfer.pIn, size, &content); |
| 1539 | 1562 | if( !g.perm.Admin ){ |
| 1540 | 1563 | cgi_reset_content(); |
| 1541 | 1564 | @ error not\sauthorized\sto\spush\sconfiguration |
| @@ -1844,11 +1867,11 @@ | ||
| 1844 | 1867 | /* Send the server timestamp last, in case prior processing happened |
| 1845 | 1868 | ** to use up a significant fraction of our time window. |
| 1846 | 1869 | */ |
| 1847 | 1870 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 1848 | 1871 | @ # timestamp %s(zNow) errors %d(nErr) |
| 1849 | - free(zNow); | |
| 1872 | + fossil_free(zNow); | |
| 1850 | 1873 | |
| 1851 | 1874 | db_commit_transaction(); |
| 1852 | 1875 | configure_rebuild(); |
| 1853 | 1876 | } |
| 1854 | 1877 | |
| @@ -2487,10 +2510,14 @@ | ||
| 2487 | 2510 | && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3])) |
| 2488 | 2511 | ){ |
| 2489 | 2512 | const char *zName = blob_str(&xfer.aToken[1]); |
| 2490 | 2513 | const char *zHash = blob_str(&xfer.aToken[3]); |
| 2491 | 2514 | int iStatus; |
| 2515 | + if( mtime<0 || size<0 ){ | |
| 2516 | + xfer_fatal_error("invalid uvigot"); | |
| 2517 | + return ++nErr; | |
| 2518 | + } | |
| 2492 | 2519 | iStatus = unversioned_status(zName, mtime, zHash); |
| 2493 | 2520 | if( (syncFlags & SYNC_UV_REVERT)!=0 ){ |
| 2494 | 2521 | if( iStatus==4 ) iStatus = 2; |
| 2495 | 2522 | if( iStatus==5 ) iStatus = 1; |
| 2496 | 2523 | } |
| @@ -2573,10 +2600,14 @@ | ||
| 2573 | 2600 | */ |
| 2574 | 2601 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 2575 | 2602 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 2576 | 2603 | const char *zName = blob_str(&xfer.aToken[1]); |
| 2577 | 2604 | Blob content; |
| 2605 | + if( size<0 ){ | |
| 2606 | + xfer_fatal_error("invalid config record"); | |
| 2607 | + return ++nErr; | |
| 2608 | + } | |
| 2578 | 2609 | blob_zero(&content); |
| 2579 | 2610 | blob_extract(xfer.pIn, size, &content); |
| 2580 | 2611 | g.perm.Admin = g.perm.RdAddr = 1; |
| 2581 | 2612 | configure_receive(zName, &content, origConfigRcvMask); |
| 2582 | 2613 | nCardRcvd++; |
| @@ -2617,10 +2648,14 @@ | ||
| 2617 | 2648 | ** blob that needs to be sent. If N<=0 that indicates that all blobs |
| 2618 | 2649 | ** have been sent. |
| 2619 | 2650 | */ |
| 2620 | 2651 | if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ |
| 2621 | 2652 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 2653 | + if( cloneSeqno<0 ){ | |
| 2654 | + xfer_fatal_error("invalid clone_seqno"); | |
| 2655 | + return ++nErr; | |
| 2656 | + } | |
| 2622 | 2657 | }else |
| 2623 | 2658 | |
| 2624 | 2659 | /* message MESSAGE |
| 2625 | 2660 | ** |
| 2626 | 2661 | ** A message is received from the server. Print it. |
| @@ -2694,10 +2729,14 @@ | ||
| 2694 | 2729 | char *zUser = blob_terminate(&xfer.aToken[2]); |
| 2695 | 2730 | sqlite3_int64 mtime, iNow; |
| 2696 | 2731 | defossilize(zUser); |
| 2697 | 2732 | iNow = time(NULL); |
| 2698 | 2733 | if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){ |
| 2734 | + if( mtime<0 ){ | |
| 2735 | + xfer_fatal_error("invalid ci-lock-fail time"); | |
| 2736 | + return ++nErr; | |
| 2737 | + } | |
| 2699 | 2738 | iNow = time(NULL); |
| 2700 | 2739 | fossil_print("\nParent check-in locked by %s %s ago\n", |
| 2701 | 2740 | zUser, human_readable_age((iNow+1-mtime)/86400.0)); |
| 2702 | 2741 | }else{ |
| 2703 | 2742 | fossil_print("\nParent check-in locked by %s\n", zUser); |
| 2704 | 2743 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -336,10 +336,11 @@ | |
| 336 | || !blob_is_filename(&pXfer->aToken[1]) |
| 337 | || !blob_is_int64(&pXfer->aToken[2], &mtime) |
| 338 | || (!blob_eq(pHash,"-") && !blob_is_hname(pHash)) |
| 339 | || !blob_is_int(&pXfer->aToken[4], &sz) |
| 340 | || !blob_is_int(&pXfer->aToken[5], &flags) |
| 341 | ){ |
| 342 | blob_appendf(&pXfer->err, "malformed uvfile line"); |
| 343 | return; |
| 344 | } |
| 345 | blob_init(&content, 0, 0); |
| @@ -1119,10 +1120,24 @@ | |
| 1119 | ** Return the TH1 code to evaluate when a ticket change is processed. |
| 1120 | */ |
| 1121 | const char *xfer_ticket_code(void){ |
| 1122 | return db_get("xfer-ticket-script", 0); |
| 1123 | } |
| 1124 | |
| 1125 | /* |
| 1126 | ** Run the specified TH1 script, if any, and returns 1 on error. |
| 1127 | */ |
| 1128 | int xfer_run_script( |
| @@ -1461,10 +1476,14 @@ | |
| 1461 | int seqno, max; |
| 1462 | if( iVers>=3 ){ |
| 1463 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1464 | } |
| 1465 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1466 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1467 | while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){ |
| 1468 | if( time(NULL) >= xfer.maxTime ) break; |
| 1469 | if( iVers>=3 ){ |
| 1470 | send_compressed_file(&xfer, seqno); |
| @@ -1532,10 +1551,14 @@ | |
| 1532 | */ |
| 1533 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 1534 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 1535 | const char *zName = blob_str(&xfer.aToken[1]); |
| 1536 | Blob content; |
| 1537 | blob_zero(&content); |
| 1538 | blob_extract(xfer.pIn, size, &content); |
| 1539 | if( !g.perm.Admin ){ |
| 1540 | cgi_reset_content(); |
| 1541 | @ error not\sauthorized\sto\spush\sconfiguration |
| @@ -1844,11 +1867,11 @@ | |
| 1844 | /* Send the server timestamp last, in case prior processing happened |
| 1845 | ** to use up a significant fraction of our time window. |
| 1846 | */ |
| 1847 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 1848 | @ # timestamp %s(zNow) errors %d(nErr) |
| 1849 | free(zNow); |
| 1850 | |
| 1851 | db_commit_transaction(); |
| 1852 | configure_rebuild(); |
| 1853 | } |
| 1854 | |
| @@ -2487,10 +2510,14 @@ | |
| 2487 | && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3])) |
| 2488 | ){ |
| 2489 | const char *zName = blob_str(&xfer.aToken[1]); |
| 2490 | const char *zHash = blob_str(&xfer.aToken[3]); |
| 2491 | int iStatus; |
| 2492 | iStatus = unversioned_status(zName, mtime, zHash); |
| 2493 | if( (syncFlags & SYNC_UV_REVERT)!=0 ){ |
| 2494 | if( iStatus==4 ) iStatus = 2; |
| 2495 | if( iStatus==5 ) iStatus = 1; |
| 2496 | } |
| @@ -2573,10 +2600,14 @@ | |
| 2573 | */ |
| 2574 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 2575 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 2576 | const char *zName = blob_str(&xfer.aToken[1]); |
| 2577 | Blob content; |
| 2578 | blob_zero(&content); |
| 2579 | blob_extract(xfer.pIn, size, &content); |
| 2580 | g.perm.Admin = g.perm.RdAddr = 1; |
| 2581 | configure_receive(zName, &content, origConfigRcvMask); |
| 2582 | nCardRcvd++; |
| @@ -2617,10 +2648,14 @@ | |
| 2617 | ** blob that needs to be sent. If N<=0 that indicates that all blobs |
| 2618 | ** have been sent. |
| 2619 | */ |
| 2620 | if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ |
| 2621 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 2622 | }else |
| 2623 | |
| 2624 | /* message MESSAGE |
| 2625 | ** |
| 2626 | ** A message is received from the server. Print it. |
| @@ -2694,10 +2729,14 @@ | |
| 2694 | char *zUser = blob_terminate(&xfer.aToken[2]); |
| 2695 | sqlite3_int64 mtime, iNow; |
| 2696 | defossilize(zUser); |
| 2697 | iNow = time(NULL); |
| 2698 | if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){ |
| 2699 | iNow = time(NULL); |
| 2700 | fossil_print("\nParent check-in locked by %s %s ago\n", |
| 2701 | zUser, human_readable_age((iNow+1-mtime)/86400.0)); |
| 2702 | }else{ |
| 2703 | fossil_print("\nParent check-in locked by %s\n", zUser); |
| 2704 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -336,10 +336,11 @@ | |
| 336 | || !blob_is_filename(&pXfer->aToken[1]) |
| 337 | || !blob_is_int64(&pXfer->aToken[2], &mtime) |
| 338 | || (!blob_eq(pHash,"-") && !blob_is_hname(pHash)) |
| 339 | || !blob_is_int(&pXfer->aToken[4], &sz) |
| 340 | || !blob_is_int(&pXfer->aToken[5], &flags) |
| 341 | || (mtime<0 || sz<0 || flags<0) |
| 342 | ){ |
| 343 | blob_appendf(&pXfer->err, "malformed uvfile line"); |
| 344 | return; |
| 345 | } |
| 346 | blob_init(&content, 0, 0); |
| @@ -1119,10 +1120,24 @@ | |
| 1120 | ** Return the TH1 code to evaluate when a ticket change is processed. |
| 1121 | */ |
| 1122 | const char *xfer_ticket_code(void){ |
| 1123 | return db_get("xfer-ticket-script", 0); |
| 1124 | } |
| 1125 | |
| 1126 | /* |
| 1127 | ** Reset the CGI content, roll back any pending db transaction, and |
| 1128 | ** emit an "error" xfer message. The message text gets fossil-encoded |
| 1129 | ** by this function. This is only intended for use with |
| 1130 | ** fail-fast/fatal errors, not ones which can be skipped over. |
| 1131 | */ |
| 1132 | static void xfer_fatal_error(const char *zMsg){ |
| 1133 | cgi_reset_content(); |
| 1134 | if( db_transaction_nesting_depth()>0 ){ |
| 1135 | db_rollback_transaction(); |
| 1136 | } |
| 1137 | @ error %F(zMsg) |
| 1138 | } |
| 1139 | |
| 1140 | /* |
| 1141 | ** Run the specified TH1 script, if any, and returns 1 on error. |
| 1142 | */ |
| 1143 | int xfer_run_script( |
| @@ -1461,10 +1476,14 @@ | |
| 1476 | int seqno, max; |
| 1477 | if( iVers>=3 ){ |
| 1478 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1479 | } |
| 1480 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1481 | if( seqno<=0 ){ |
| 1482 | xfer_fatal_error("invalid clone sequence number"); |
| 1483 | return; |
| 1484 | } |
| 1485 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1486 | while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){ |
| 1487 | if( time(NULL) >= xfer.maxTime ) break; |
| 1488 | if( iVers>=3 ){ |
| 1489 | send_compressed_file(&xfer, seqno); |
| @@ -1532,10 +1551,14 @@ | |
| 1551 | */ |
| 1552 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 1553 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 1554 | const char *zName = blob_str(&xfer.aToken[1]); |
| 1555 | Blob content; |
| 1556 | if( size<0 ){ |
| 1557 | xfer_fatal_error("invalid config record"); |
| 1558 | return; |
| 1559 | } |
| 1560 | blob_zero(&content); |
| 1561 | blob_extract(xfer.pIn, size, &content); |
| 1562 | if( !g.perm.Admin ){ |
| 1563 | cgi_reset_content(); |
| 1564 | @ error not\sauthorized\sto\spush\sconfiguration |
| @@ -1844,11 +1867,11 @@ | |
| 1867 | /* Send the server timestamp last, in case prior processing happened |
| 1868 | ** to use up a significant fraction of our time window. |
| 1869 | */ |
| 1870 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 1871 | @ # timestamp %s(zNow) errors %d(nErr) |
| 1872 | fossil_free(zNow); |
| 1873 | |
| 1874 | db_commit_transaction(); |
| 1875 | configure_rebuild(); |
| 1876 | } |
| 1877 | |
| @@ -2487,10 +2510,14 @@ | |
| 2510 | && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3])) |
| 2511 | ){ |
| 2512 | const char *zName = blob_str(&xfer.aToken[1]); |
| 2513 | const char *zHash = blob_str(&xfer.aToken[3]); |
| 2514 | int iStatus; |
| 2515 | if( mtime<0 || size<0 ){ |
| 2516 | xfer_fatal_error("invalid uvigot"); |
| 2517 | return ++nErr; |
| 2518 | } |
| 2519 | iStatus = unversioned_status(zName, mtime, zHash); |
| 2520 | if( (syncFlags & SYNC_UV_REVERT)!=0 ){ |
| 2521 | if( iStatus==4 ) iStatus = 2; |
| 2522 | if( iStatus==5 ) iStatus = 1; |
| 2523 | } |
| @@ -2573,10 +2600,14 @@ | |
| 2600 | */ |
| 2601 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 2602 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 2603 | const char *zName = blob_str(&xfer.aToken[1]); |
| 2604 | Blob content; |
| 2605 | if( size<0 ){ |
| 2606 | xfer_fatal_error("invalid config record"); |
| 2607 | return ++nErr; |
| 2608 | } |
| 2609 | blob_zero(&content); |
| 2610 | blob_extract(xfer.pIn, size, &content); |
| 2611 | g.perm.Admin = g.perm.RdAddr = 1; |
| 2612 | configure_receive(zName, &content, origConfigRcvMask); |
| 2613 | nCardRcvd++; |
| @@ -2617,10 +2648,14 @@ | |
| 2648 | ** blob that needs to be sent. If N<=0 that indicates that all blobs |
| 2649 | ** have been sent. |
| 2650 | */ |
| 2651 | if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ |
| 2652 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 2653 | if( cloneSeqno<0 ){ |
| 2654 | xfer_fatal_error("invalid clone_seqno"); |
| 2655 | return ++nErr; |
| 2656 | } |
| 2657 | }else |
| 2658 | |
| 2659 | /* message MESSAGE |
| 2660 | ** |
| 2661 | ** A message is received from the server. Print it. |
| @@ -2694,10 +2729,14 @@ | |
| 2729 | char *zUser = blob_terminate(&xfer.aToken[2]); |
| 2730 | sqlite3_int64 mtime, iNow; |
| 2731 | defossilize(zUser); |
| 2732 | iNow = time(NULL); |
| 2733 | if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){ |
| 2734 | if( mtime<0 ){ |
| 2735 | xfer_fatal_error("invalid ci-lock-fail time"); |
| 2736 | return ++nErr; |
| 2737 | } |
| 2738 | iNow = time(NULL); |
| 2739 | fossil_print("\nParent check-in locked by %s %s ago\n", |
| 2740 | zUser, human_readable_age((iNow+1-mtime)/86400.0)); |
| 2741 | }else{ |
| 2742 | fossil_print("\nParent check-in locked by %s\n", zUser); |
| 2743 |
+20
| --- test/comment.test | ||
| +++ test/comment.test | ||
| @@ -319,8 +319,28 @@ | ||
| 319 | 319 | ############################################################################### |
| 320 | 320 | |
| 321 | 321 | fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig |
| 322 | 322 | test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
| 323 | 323 | |
| 324 | +############################################################################### | |
| 325 | + | |
| 326 | +fossil test-comment-format --width 72 --file "" [file join $testdir "utf8-comment.txt"] | |
| 327 | +test comment-61 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\] an\nd symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 328 | + | |
| 329 | +############################################################################### | |
| 330 | + | |
| 331 | +fossil test-comment-format --width 72 --wordbreak --file "" [file join $testdir "utf8-comment.txt"] | |
| 332 | +test comment-62 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 333 | + | |
| 334 | +############################################################################### | |
| 335 | + | |
| 336 | +fossil test-comment-format --width 72 --legacy --file "" [file join $testdir "utf8-comment.txt"] | |
| 337 | +test comment-63 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 338 | + | |
| 339 | +############################################################################### | |
| 340 | + | |
| 341 | +fossil test-comment-format --width 72 --legacy --wordbreak --file "" [file join $testdir "utf8-comment.txt"] | |
| 342 | +test comment-64 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 343 | + | |
| 324 | 344 | ############################################################################### |
| 325 | 345 | |
| 326 | 346 | test_cleanup |
| 327 | 347 |
| --- test/comment.test | |
| +++ test/comment.test | |
| @@ -319,8 +319,28 @@ | |
| 319 | ############################################################################### |
| 320 | |
| 321 | fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig |
| 322 | test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
| 323 | |
| 324 | ############################################################################### |
| 325 | |
| 326 | test_cleanup |
| 327 |
| --- test/comment.test | |
| +++ test/comment.test | |
| @@ -319,8 +319,28 @@ | |
| 319 | ############################################################################### |
| 320 | |
| 321 | fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig |
| 322 | test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
| 323 | |
| 324 | ############################################################################### |
| 325 | |
| 326 | fossil test-comment-format --width 72 --file "" [file join $testdir "utf8-comment.txt"] |
| 327 | test comment-61 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\] an\nd symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 328 | |
| 329 | ############################################################################### |
| 330 | |
| 331 | fossil test-comment-format --width 72 --wordbreak --file "" [file join $testdir "utf8-comment.txt"] |
| 332 | test comment-62 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 333 | |
| 334 | ############################################################################### |
| 335 | |
| 336 | fossil test-comment-format --width 72 --legacy --file "" [file join $testdir "utf8-comment.txt"] |
| 337 | test comment-63 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 338 | |
| 339 | ############################################################################### |
| 340 | |
| 341 | fossil test-comment-format --width 72 --legacy --wordbreak --file "" [file join $testdir "utf8-comment.txt"] |
| 342 | test comment-64 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 343 | |
| 344 | ############################################################################### |
| 345 | |
| 346 | test_cleanup |
| 347 |
+20
-20
| --- test/glob.test | ||
| +++ test/glob.test | ||
| @@ -30,40 +30,40 @@ | ||
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | glob-parse 100 test test [string map [list \r\n \n] \ |
| 33 | 33 | {SQL expression: (x GLOB 'test') |
| 34 | 34 | pattern[0] = [test] |
| 35 | -1 test}] | |
| 35 | +1 1 test}] | |
| 36 | 36 | |
| 37 | 37 | glob-parse 101 "one two" one [string map [list \r\n \n] \ |
| 38 | 38 | {SQL expression: (x GLOB 'one' OR x GLOB 'two') |
| 39 | 39 | pattern[0] = [one] |
| 40 | 40 | pattern[1] = [two] |
| 41 | -1 one}] | |
| 41 | +1 1 one}] | |
| 42 | 42 | |
| 43 | 43 | glob-parse 102 t* test [string map [list \r\n \n] \ |
| 44 | 44 | {SQL expression: (x GLOB 't*') |
| 45 | 45 | pattern[0] = [t*] |
| 46 | -1 test}] | |
| 46 | +1 1 test}] | |
| 47 | 47 | |
| 48 | 48 | glob-parse 103 "o* two" one [string map [list \r\n \n] \ |
| 49 | 49 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two') |
| 50 | 50 | pattern[0] = [o*] |
| 51 | 51 | pattern[1] = [two] |
| 52 | -1 one}] | |
| 52 | +1 1 one}] | |
| 53 | 53 | |
| 54 | 54 | glob-parse 104 {"o* two" "three four"} "one two" [string map [list \r\n \n] \ |
| 55 | 55 | {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') |
| 56 | 56 | pattern[0] = [o* two] |
| 57 | 57 | pattern[1] = [three four] |
| 58 | -1 one two}] | |
| 58 | +1 1 one two}] | |
| 59 | 59 | |
| 60 | 60 | glob-parse 105 {"o* two" "three four"} "two one" [string map [list \r\n \n] \ |
| 61 | 61 | {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') |
| 62 | 62 | pattern[0] = [o* two] |
| 63 | 63 | pattern[1] = [three four] |
| 64 | -0 two one}] | |
| 64 | +0 0 two one}] | |
| 65 | 65 | |
| 66 | 66 | glob-parse 106 "\"o*\ntwo\" \"three\nfour\"" "one\ntwo" \ |
| 67 | 67 | [string map [list \r\n \n] \ |
| 68 | 68 | {SQL expression: (x GLOB 'o* |
| 69 | 69 | two' OR x GLOB 'three |
| @@ -70,11 +70,11 @@ | ||
| 70 | 70 | four') |
| 71 | 71 | pattern[0] = [o* |
| 72 | 72 | two] |
| 73 | 73 | pattern[1] = [three |
| 74 | 74 | four] |
| 75 | -1 one | |
| 75 | +1 1 one | |
| 76 | 76 | two}] |
| 77 | 77 | |
| 78 | 78 | glob-parse 107 "\"o*\ntwo\" \"three\nfour\"" "two\none" \ |
| 79 | 79 | [string map [list \r\n \n] \ |
| 80 | 80 | {SQL expression: (x GLOB 'o* |
| @@ -82,11 +82,11 @@ | ||
| 82 | 82 | four') |
| 83 | 83 | pattern[0] = [o* |
| 84 | 84 | two] |
| 85 | 85 | pattern[1] = [three |
| 86 | 86 | four] |
| 87 | -0 two | |
| 87 | +0 0 two | |
| 88 | 88 | one}] |
| 89 | 89 | |
| 90 | 90 | glob-parse 108 "\"o*\rtwo\" \"three\rfour\"" "one\rtwo" \ |
| 91 | 91 | [string map [list \r\n \n] \ |
| 92 | 92 | {SQL expression: (x GLOB 'o* |
| @@ -94,11 +94,11 @@ | ||
| 94 | 94 | four') |
| 95 | 95 | pattern[0] = [o* |
| 96 | 96 | two] |
| 97 | 97 | pattern[1] = [three |
| 98 | 98 | four] |
| 99 | -1 one | |
| 99 | +1 1 one | |
| 100 | 100 | two}] |
| 101 | 101 | |
| 102 | 102 | glob-parse 109 "\"o*\rtwo\" \"three\rfour\"" "two\rone" \ |
| 103 | 103 | [string map [list \r\n \n] \ |
| 104 | 104 | {SQL expression: (x GLOB 'o* |
| @@ -106,11 +106,11 @@ | ||
| 106 | 106 | four') |
| 107 | 107 | pattern[0] = [o* |
| 108 | 108 | two] |
| 109 | 109 | pattern[1] = [three |
| 110 | 110 | four] |
| 111 | -0 two | |
| 111 | +0 0 two | |
| 112 | 112 | one}] |
| 113 | 113 | |
| 114 | 114 | glob-parse 110 "'o*\ntwo' 'three\nfour'" "one\ntwo" \ |
| 115 | 115 | [string map [list \r\n \n] \ |
| 116 | 116 | {SQL expression: (x GLOB 'o* |
| @@ -118,11 +118,11 @@ | ||
| 118 | 118 | four') |
| 119 | 119 | pattern[0] = [o* |
| 120 | 120 | two] |
| 121 | 121 | pattern[1] = [three |
| 122 | 122 | four] |
| 123 | -1 one | |
| 123 | +1 1 one | |
| 124 | 124 | two}] |
| 125 | 125 | |
| 126 | 126 | glob-parse 111 "'o*\ntwo' 'three\nfour'" "two\none" \ |
| 127 | 127 | [string map [list \r\n \n] \ |
| 128 | 128 | {SQL expression: (x GLOB 'o* |
| @@ -130,61 +130,61 @@ | ||
| 130 | 130 | four') |
| 131 | 131 | pattern[0] = [o* |
| 132 | 132 | two] |
| 133 | 133 | pattern[1] = [three |
| 134 | 134 | four] |
| 135 | -0 two | |
| 135 | +0 0 two | |
| 136 | 136 | one}] |
| 137 | 137 | |
| 138 | 138 | glob-parse 112 "\"'o*' 'two'\" \"'three' 'four'\"" "'one' 'two'" \ |
| 139 | 139 | [string map [list \r\n \n] \ |
| 140 | 140 | {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') |
| 141 | 141 | pattern[0] = ['o*' 'two'] |
| 142 | 142 | pattern[1] = ['three' 'four'] |
| 143 | -1 'one' 'two'}] | |
| 143 | +1 1 'one' 'two'}] | |
| 144 | 144 | |
| 145 | 145 | glob-parse 113 "\"'o*' 'two'\" \"'three' 'four'\"" "two one" \ |
| 146 | 146 | [string map [list \r\n \n] \ |
| 147 | 147 | {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') |
| 148 | 148 | pattern[0] = ['o*' 'two'] |
| 149 | 149 | pattern[1] = ['three' 'four'] |
| 150 | -0 two one}] | |
| 150 | +0 0 two one}] | |
| 151 | 151 | |
| 152 | 152 | glob-parse 114 o*,two one [string map [list \r\n \n] \ |
| 153 | 153 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two') |
| 154 | 154 | pattern[0] = [o*] |
| 155 | 155 | pattern[1] = [two] |
| 156 | -1 one}] | |
| 156 | +1 1 one}] | |
| 157 | 157 | |
| 158 | 158 | glob-parse 115 "o*,two three,four" "one two" [string map [list \r\n \n] \ |
| 159 | 159 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two' OR x GLOB 'three' OR x GLOB 'four') |
| 160 | 160 | pattern[0] = [o*] |
| 161 | 161 | pattern[1] = [two] |
| 162 | 162 | pattern[2] = [three] |
| 163 | 163 | pattern[3] = [four] |
| 164 | -1 one two}] | |
| 164 | +1 1 one two}] | |
| 165 | 165 | |
| 166 | 166 | glob-parse 116 'o*,two' one [string map [list \r\n \n] \ |
| 167 | 167 | {SQL expression: (x GLOB 'o*,two') |
| 168 | 168 | pattern[0] = [o*,two] |
| 169 | -0 one}] | |
| 169 | +0 0 one}] | |
| 170 | 170 | |
| 171 | 171 | glob-parse 117 'o*,two' one,two [string map [list \r\n \n] \ |
| 172 | 172 | {SQL expression: (x GLOB 'o*,two') |
| 173 | 173 | pattern[0] = [o*,two] |
| 174 | -1 one,two}] | |
| 174 | +1 1 one,two}] | |
| 175 | 175 | |
| 176 | 176 | glob-parse 118 "'o*,two three,four'" "one two three,four" \ |
| 177 | 177 | [string map [list \r\n \n] \ |
| 178 | 178 | {SQL expression: (x GLOB 'o*,two three,four') |
| 179 | 179 | pattern[0] = [o*,two three,four] |
| 180 | -0 one two three,four}] | |
| 180 | +0 0 one two three,four}] | |
| 181 | 181 | |
| 182 | 182 | glob-parse 119 "'o*,two three,four'" "one,two three,four" \ |
| 183 | 183 | [string map [list \r\n \n] \ |
| 184 | 184 | {SQL expression: (x GLOB 'o*,two three,four') |
| 185 | 185 | pattern[0] = [o*,two three,four] |
| 186 | -1 one,two three,four}] | |
| 186 | +1 1 one,two three,four}] | |
| 187 | 187 | |
| 188 | 188 | ############################################################################### |
| 189 | 189 | |
| 190 | 190 | test_cleanup |
| 191 | 191 |
| --- test/glob.test | |
| +++ test/glob.test | |
| @@ -30,40 +30,40 @@ | |
| 30 | } |
| 31 | |
| 32 | glob-parse 100 test test [string map [list \r\n \n] \ |
| 33 | {SQL expression: (x GLOB 'test') |
| 34 | pattern[0] = [test] |
| 35 | 1 test}] |
| 36 | |
| 37 | glob-parse 101 "one two" one [string map [list \r\n \n] \ |
| 38 | {SQL expression: (x GLOB 'one' OR x GLOB 'two') |
| 39 | pattern[0] = [one] |
| 40 | pattern[1] = [two] |
| 41 | 1 one}] |
| 42 | |
| 43 | glob-parse 102 t* test [string map [list \r\n \n] \ |
| 44 | {SQL expression: (x GLOB 't*') |
| 45 | pattern[0] = [t*] |
| 46 | 1 test}] |
| 47 | |
| 48 | glob-parse 103 "o* two" one [string map [list \r\n \n] \ |
| 49 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two') |
| 50 | pattern[0] = [o*] |
| 51 | pattern[1] = [two] |
| 52 | 1 one}] |
| 53 | |
| 54 | glob-parse 104 {"o* two" "three four"} "one two" [string map [list \r\n \n] \ |
| 55 | {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') |
| 56 | pattern[0] = [o* two] |
| 57 | pattern[1] = [three four] |
| 58 | 1 one two}] |
| 59 | |
| 60 | glob-parse 105 {"o* two" "three four"} "two one" [string map [list \r\n \n] \ |
| 61 | {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') |
| 62 | pattern[0] = [o* two] |
| 63 | pattern[1] = [three four] |
| 64 | 0 two one}] |
| 65 | |
| 66 | glob-parse 106 "\"o*\ntwo\" \"three\nfour\"" "one\ntwo" \ |
| 67 | [string map [list \r\n \n] \ |
| 68 | {SQL expression: (x GLOB 'o* |
| 69 | two' OR x GLOB 'three |
| @@ -70,11 +70,11 @@ | |
| 70 | four') |
| 71 | pattern[0] = [o* |
| 72 | two] |
| 73 | pattern[1] = [three |
| 74 | four] |
| 75 | 1 one |
| 76 | two}] |
| 77 | |
| 78 | glob-parse 107 "\"o*\ntwo\" \"three\nfour\"" "two\none" \ |
| 79 | [string map [list \r\n \n] \ |
| 80 | {SQL expression: (x GLOB 'o* |
| @@ -82,11 +82,11 @@ | |
| 82 | four') |
| 83 | pattern[0] = [o* |
| 84 | two] |
| 85 | pattern[1] = [three |
| 86 | four] |
| 87 | 0 two |
| 88 | one}] |
| 89 | |
| 90 | glob-parse 108 "\"o*\rtwo\" \"three\rfour\"" "one\rtwo" \ |
| 91 | [string map [list \r\n \n] \ |
| 92 | {SQL expression: (x GLOB 'o* |
| @@ -94,11 +94,11 @@ | |
| 94 | four') |
| 95 | pattern[0] = [o* |
| 96 | two] |
| 97 | pattern[1] = [three |
| 98 | four] |
| 99 | 1 one |
| 100 | two}] |
| 101 | |
| 102 | glob-parse 109 "\"o*\rtwo\" \"three\rfour\"" "two\rone" \ |
| 103 | [string map [list \r\n \n] \ |
| 104 | {SQL expression: (x GLOB 'o* |
| @@ -106,11 +106,11 @@ | |
| 106 | four') |
| 107 | pattern[0] = [o* |
| 108 | two] |
| 109 | pattern[1] = [three |
| 110 | four] |
| 111 | 0 two |
| 112 | one}] |
| 113 | |
| 114 | glob-parse 110 "'o*\ntwo' 'three\nfour'" "one\ntwo" \ |
| 115 | [string map [list \r\n \n] \ |
| 116 | {SQL expression: (x GLOB 'o* |
| @@ -118,11 +118,11 @@ | |
| 118 | four') |
| 119 | pattern[0] = [o* |
| 120 | two] |
| 121 | pattern[1] = [three |
| 122 | four] |
| 123 | 1 one |
| 124 | two}] |
| 125 | |
| 126 | glob-parse 111 "'o*\ntwo' 'three\nfour'" "two\none" \ |
| 127 | [string map [list \r\n \n] \ |
| 128 | {SQL expression: (x GLOB 'o* |
| @@ -130,61 +130,61 @@ | |
| 130 | four') |
| 131 | pattern[0] = [o* |
| 132 | two] |
| 133 | pattern[1] = [three |
| 134 | four] |
| 135 | 0 two |
| 136 | one}] |
| 137 | |
| 138 | glob-parse 112 "\"'o*' 'two'\" \"'three' 'four'\"" "'one' 'two'" \ |
| 139 | [string map [list \r\n \n] \ |
| 140 | {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') |
| 141 | pattern[0] = ['o*' 'two'] |
| 142 | pattern[1] = ['three' 'four'] |
| 143 | 1 'one' 'two'}] |
| 144 | |
| 145 | glob-parse 113 "\"'o*' 'two'\" \"'three' 'four'\"" "two one" \ |
| 146 | [string map [list \r\n \n] \ |
| 147 | {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') |
| 148 | pattern[0] = ['o*' 'two'] |
| 149 | pattern[1] = ['three' 'four'] |
| 150 | 0 two one}] |
| 151 | |
| 152 | glob-parse 114 o*,two one [string map [list \r\n \n] \ |
| 153 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two') |
| 154 | pattern[0] = [o*] |
| 155 | pattern[1] = [two] |
| 156 | 1 one}] |
| 157 | |
| 158 | glob-parse 115 "o*,two three,four" "one two" [string map [list \r\n \n] \ |
| 159 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two' OR x GLOB 'three' OR x GLOB 'four') |
| 160 | pattern[0] = [o*] |
| 161 | pattern[1] = [two] |
| 162 | pattern[2] = [three] |
| 163 | pattern[3] = [four] |
| 164 | 1 one two}] |
| 165 | |
| 166 | glob-parse 116 'o*,two' one [string map [list \r\n \n] \ |
| 167 | {SQL expression: (x GLOB 'o*,two') |
| 168 | pattern[0] = [o*,two] |
| 169 | 0 one}] |
| 170 | |
| 171 | glob-parse 117 'o*,two' one,two [string map [list \r\n \n] \ |
| 172 | {SQL expression: (x GLOB 'o*,two') |
| 173 | pattern[0] = [o*,two] |
| 174 | 1 one,two}] |
| 175 | |
| 176 | glob-parse 118 "'o*,two three,four'" "one two three,four" \ |
| 177 | [string map [list \r\n \n] \ |
| 178 | {SQL expression: (x GLOB 'o*,two three,four') |
| 179 | pattern[0] = [o*,two three,four] |
| 180 | 0 one two three,four}] |
| 181 | |
| 182 | glob-parse 119 "'o*,two three,four'" "one,two three,four" \ |
| 183 | [string map [list \r\n \n] \ |
| 184 | {SQL expression: (x GLOB 'o*,two three,four') |
| 185 | pattern[0] = [o*,two three,four] |
| 186 | 1 one,two three,four}] |
| 187 | |
| 188 | ############################################################################### |
| 189 | |
| 190 | test_cleanup |
| 191 |
| --- test/glob.test | |
| +++ test/glob.test | |
| @@ -30,40 +30,40 @@ | |
| 30 | } |
| 31 | |
| 32 | glob-parse 100 test test [string map [list \r\n \n] \ |
| 33 | {SQL expression: (x GLOB 'test') |
| 34 | pattern[0] = [test] |
| 35 | 1 1 test}] |
| 36 | |
| 37 | glob-parse 101 "one two" one [string map [list \r\n \n] \ |
| 38 | {SQL expression: (x GLOB 'one' OR x GLOB 'two') |
| 39 | pattern[0] = [one] |
| 40 | pattern[1] = [two] |
| 41 | 1 1 one}] |
| 42 | |
| 43 | glob-parse 102 t* test [string map [list \r\n \n] \ |
| 44 | {SQL expression: (x GLOB 't*') |
| 45 | pattern[0] = [t*] |
| 46 | 1 1 test}] |
| 47 | |
| 48 | glob-parse 103 "o* two" one [string map [list \r\n \n] \ |
| 49 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two') |
| 50 | pattern[0] = [o*] |
| 51 | pattern[1] = [two] |
| 52 | 1 1 one}] |
| 53 | |
| 54 | glob-parse 104 {"o* two" "three four"} "one two" [string map [list \r\n \n] \ |
| 55 | {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') |
| 56 | pattern[0] = [o* two] |
| 57 | pattern[1] = [three four] |
| 58 | 1 1 one two}] |
| 59 | |
| 60 | glob-parse 105 {"o* two" "three four"} "two one" [string map [list \r\n \n] \ |
| 61 | {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') |
| 62 | pattern[0] = [o* two] |
| 63 | pattern[1] = [three four] |
| 64 | 0 0 two one}] |
| 65 | |
| 66 | glob-parse 106 "\"o*\ntwo\" \"three\nfour\"" "one\ntwo" \ |
| 67 | [string map [list \r\n \n] \ |
| 68 | {SQL expression: (x GLOB 'o* |
| 69 | two' OR x GLOB 'three |
| @@ -70,11 +70,11 @@ | |
| 70 | four') |
| 71 | pattern[0] = [o* |
| 72 | two] |
| 73 | pattern[1] = [three |
| 74 | four] |
| 75 | 1 1 one |
| 76 | two}] |
| 77 | |
| 78 | glob-parse 107 "\"o*\ntwo\" \"three\nfour\"" "two\none" \ |
| 79 | [string map [list \r\n \n] \ |
| 80 | {SQL expression: (x GLOB 'o* |
| @@ -82,11 +82,11 @@ | |
| 82 | four') |
| 83 | pattern[0] = [o* |
| 84 | two] |
| 85 | pattern[1] = [three |
| 86 | four] |
| 87 | 0 0 two |
| 88 | one}] |
| 89 | |
| 90 | glob-parse 108 "\"o*\rtwo\" \"three\rfour\"" "one\rtwo" \ |
| 91 | [string map [list \r\n \n] \ |
| 92 | {SQL expression: (x GLOB 'o* |
| @@ -94,11 +94,11 @@ | |
| 94 | four') |
| 95 | pattern[0] = [o* |
| 96 | two] |
| 97 | pattern[1] = [three |
| 98 | four] |
| 99 | 1 1 one |
| 100 | two}] |
| 101 | |
| 102 | glob-parse 109 "\"o*\rtwo\" \"three\rfour\"" "two\rone" \ |
| 103 | [string map [list \r\n \n] \ |
| 104 | {SQL expression: (x GLOB 'o* |
| @@ -106,11 +106,11 @@ | |
| 106 | four') |
| 107 | pattern[0] = [o* |
| 108 | two] |
| 109 | pattern[1] = [three |
| 110 | four] |
| 111 | 0 0 two |
| 112 | one}] |
| 113 | |
| 114 | glob-parse 110 "'o*\ntwo' 'three\nfour'" "one\ntwo" \ |
| 115 | [string map [list \r\n \n] \ |
| 116 | {SQL expression: (x GLOB 'o* |
| @@ -118,11 +118,11 @@ | |
| 118 | four') |
| 119 | pattern[0] = [o* |
| 120 | two] |
| 121 | pattern[1] = [three |
| 122 | four] |
| 123 | 1 1 one |
| 124 | two}] |
| 125 | |
| 126 | glob-parse 111 "'o*\ntwo' 'three\nfour'" "two\none" \ |
| 127 | [string map [list \r\n \n] \ |
| 128 | {SQL expression: (x GLOB 'o* |
| @@ -130,61 +130,61 @@ | |
| 130 | four') |
| 131 | pattern[0] = [o* |
| 132 | two] |
| 133 | pattern[1] = [three |
| 134 | four] |
| 135 | 0 0 two |
| 136 | one}] |
| 137 | |
| 138 | glob-parse 112 "\"'o*' 'two'\" \"'three' 'four'\"" "'one' 'two'" \ |
| 139 | [string map [list \r\n \n] \ |
| 140 | {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') |
| 141 | pattern[0] = ['o*' 'two'] |
| 142 | pattern[1] = ['three' 'four'] |
| 143 | 1 1 'one' 'two'}] |
| 144 | |
| 145 | glob-parse 113 "\"'o*' 'two'\" \"'three' 'four'\"" "two one" \ |
| 146 | [string map [list \r\n \n] \ |
| 147 | {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') |
| 148 | pattern[0] = ['o*' 'two'] |
| 149 | pattern[1] = ['three' 'four'] |
| 150 | 0 0 two one}] |
| 151 | |
| 152 | glob-parse 114 o*,two one [string map [list \r\n \n] \ |
| 153 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two') |
| 154 | pattern[0] = [o*] |
| 155 | pattern[1] = [two] |
| 156 | 1 1 one}] |
| 157 | |
| 158 | glob-parse 115 "o*,two three,four" "one two" [string map [list \r\n \n] \ |
| 159 | {SQL expression: (x GLOB 'o*' OR x GLOB 'two' OR x GLOB 'three' OR x GLOB 'four') |
| 160 | pattern[0] = [o*] |
| 161 | pattern[1] = [two] |
| 162 | pattern[2] = [three] |
| 163 | pattern[3] = [four] |
| 164 | 1 1 one two}] |
| 165 | |
| 166 | glob-parse 116 'o*,two' one [string map [list \r\n \n] \ |
| 167 | {SQL expression: (x GLOB 'o*,two') |
| 168 | pattern[0] = [o*,two] |
| 169 | 0 0 one}] |
| 170 | |
| 171 | glob-parse 117 'o*,two' one,two [string map [list \r\n \n] \ |
| 172 | {SQL expression: (x GLOB 'o*,two') |
| 173 | pattern[0] = [o*,two] |
| 174 | 1 1 one,two}] |
| 175 | |
| 176 | glob-parse 118 "'o*,two three,four'" "one two three,four" \ |
| 177 | [string map [list \r\n \n] \ |
| 178 | {SQL expression: (x GLOB 'o*,two three,four') |
| 179 | pattern[0] = [o*,two three,four] |
| 180 | 0 0 one two three,four}] |
| 181 | |
| 182 | glob-parse 119 "'o*,two three,four'" "one,two three,four" \ |
| 183 | [string map [list \r\n \n] \ |
| 184 | {SQL expression: (x GLOB 'o*,two three,four') |
| 185 | pattern[0] = [o*,two three,four] |
| 186 | 1 1 one,two three,four}] |
| 187 | |
| 188 | ############################################################################### |
| 189 | |
| 190 | test_cleanup |
| 191 |
+6
| --- test/settings.test | ||
| +++ test/settings.test | ||
| @@ -26,10 +26,16 @@ | ||
| 26 | 26 | # |
| 27 | 27 | # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? |
| 28 | 28 | # fossil unset PROPERTY ?OPTIONS? |
| 29 | 29 | # |
| 30 | 30 | # Where the only supported options are "--global" and "--exact". |
| 31 | +# | |
| 32 | +############################################################################### | |
| 33 | +# | |
| 34 | +# NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list | |
| 35 | +# of settings to test and needs to be manually updated when new settings | |
| 36 | +# are added. | |
| 31 | 37 | # |
| 32 | 38 | ############################################################################### |
| 33 | 39 | # |
| 34 | 40 | # NOTE: The [extract_setting_names] procedure extracts the list of setting |
| 35 | 41 | # names from the line-ending normalized output of the "fossil settings" |
| 36 | 42 |
| --- test/settings.test | |
| +++ test/settings.test | |
| @@ -26,10 +26,16 @@ | |
| 26 | # |
| 27 | # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? |
| 28 | # fossil unset PROPERTY ?OPTIONS? |
| 29 | # |
| 30 | # Where the only supported options are "--global" and "--exact". |
| 31 | # |
| 32 | ############################################################################### |
| 33 | # |
| 34 | # NOTE: The [extract_setting_names] procedure extracts the list of setting |
| 35 | # names from the line-ending normalized output of the "fossil settings" |
| 36 |
| --- test/settings.test | |
| +++ test/settings.test | |
| @@ -26,10 +26,16 @@ | |
| 26 | # |
| 27 | # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? |
| 28 | # fossil unset PROPERTY ?OPTIONS? |
| 29 | # |
| 30 | # Where the only supported options are "--global" and "--exact". |
| 31 | # |
| 32 | ############################################################################### |
| 33 | # |
| 34 | # NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list |
| 35 | # of settings to test and needs to be manually updated when new settings |
| 36 | # are added. |
| 37 | # |
| 38 | ############################################################################### |
| 39 | # |
| 40 | # NOTE: The [extract_setting_names] procedure extracts the list of setting |
| 41 | # names from the line-ending normalized output of the "fossil settings" |
| 42 |
+2
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -359,10 +359,12 @@ | ||
| 359 | 359 | proxy \ |
| 360 | 360 | redirect-to-https \ |
| 361 | 361 | relative-paths \ |
| 362 | 362 | repo-cksum \ |
| 363 | 363 | repolist-skin \ |
| 364 | + robot-restrict \ | |
| 365 | + robots-txt \ | |
| 364 | 366 | safe-html \ |
| 365 | 367 | self-pw-reset \ |
| 366 | 368 | self-register \ |
| 367 | 369 | sitemap-extra \ |
| 368 | 370 | ssh-command \ |
| 369 | 371 | |
| 370 | 372 | ADDED test/utf8-comment.txt |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -359,10 +359,12 @@ | |
| 359 | proxy \ |
| 360 | redirect-to-https \ |
| 361 | relative-paths \ |
| 362 | repo-cksum \ |
| 363 | repolist-skin \ |
| 364 | safe-html \ |
| 365 | self-pw-reset \ |
| 366 | self-register \ |
| 367 | sitemap-extra \ |
| 368 | ssh-command \ |
| 369 | |
| 370 | DDED test/utf8-comment.txt |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -359,10 +359,12 @@ | |
| 359 | proxy \ |
| 360 | redirect-to-https \ |
| 361 | relative-paths \ |
| 362 | repo-cksum \ |
| 363 | repolist-skin \ |
| 364 | robot-restrict \ |
| 365 | robots-txt \ |
| 366 | safe-html \ |
| 367 | self-pw-reset \ |
| 368 | self-register \ |
| 369 | sitemap-extra \ |
| 370 | ssh-command \ |
| 371 | |
| 372 | DDED test/utf8-comment.txt |
| --- a/test/utf8-comment.txt | ||
| +++ b/test/utf8-comment.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +The comment formatter handles fullwidth and multi-byte [äöü] and symbols [☃] and emoji [💾] characters! |
| --- a/test/utf8-comment.txt | |
| +++ b/test/utf8-comment.txt | |
| @@ -0,0 +1 @@ | |
| --- a/test/utf8-comment.txt | |
| +++ b/test/utf8-comment.txt | |
| @@ -0,0 +1 @@ | |
| 1 | The comment formatter handles fullwidth and multi-byte [äöü] and symbols [☃] and emoji [💾] characters! |
+2
-1
| --- tools/makemake.tcl | ||
| +++ tools/makemake.tcl | ||
| @@ -209,10 +209,11 @@ | ||
| 209 | 209 | # Additional resource files that get built into the executable. |
| 210 | 210 | # These paths are all resolved from the src/ directory, so must |
| 211 | 211 | # be relative to that. |
| 212 | 212 | set extra_files { |
| 213 | 213 | diff.tcl |
| 214 | + merge.tcl | |
| 214 | 215 | markdown.md |
| 215 | 216 | wiki.wiki |
| 216 | 217 | *.js |
| 217 | 218 | default.css |
| 218 | 219 | style.*.css |
| @@ -445,11 +446,11 @@ | ||
| 445 | 446 | |
| 446 | 447 | # The USE_LINENOISE variable may be undefined, set to 0, or set |
| 447 | 448 | # to 1. If it is set to 0, then there is no need to build or link |
| 448 | 449 | # the linenoise.o object. |
| 449 | 450 | LINENOISE_DEF.0 = |
| 450 | -LINENOISE_DEF.1 = -DHAVE_LINENOISE | |
| 451 | +LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 | |
| 451 | 452 | LINENOISE_DEF. = $(LINENOISE_DEF.0) |
| 452 | 453 | LINENOISE_OBJ.0 = |
| 453 | 454 | LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o |
| 454 | 455 | LINENOISE_OBJ. = $(LINENOISE_OBJ.0) |
| 455 | 456 | |
| 456 | 457 |
| --- tools/makemake.tcl | |
| +++ tools/makemake.tcl | |
| @@ -209,10 +209,11 @@ | |
| 209 | # Additional resource files that get built into the executable. |
| 210 | # These paths are all resolved from the src/ directory, so must |
| 211 | # be relative to that. |
| 212 | set extra_files { |
| 213 | diff.tcl |
| 214 | markdown.md |
| 215 | wiki.wiki |
| 216 | *.js |
| 217 | default.css |
| 218 | style.*.css |
| @@ -445,11 +446,11 @@ | |
| 445 | |
| 446 | # The USE_LINENOISE variable may be undefined, set to 0, or set |
| 447 | # to 1. If it is set to 0, then there is no need to build or link |
| 448 | # the linenoise.o object. |
| 449 | LINENOISE_DEF.0 = |
| 450 | LINENOISE_DEF.1 = -DHAVE_LINENOISE |
| 451 | LINENOISE_DEF. = $(LINENOISE_DEF.0) |
| 452 | LINENOISE_OBJ.0 = |
| 453 | LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o |
| 454 | LINENOISE_OBJ. = $(LINENOISE_OBJ.0) |
| 455 | |
| 456 |
| --- tools/makemake.tcl | |
| +++ tools/makemake.tcl | |
| @@ -209,10 +209,11 @@ | |
| 209 | # Additional resource files that get built into the executable. |
| 210 | # These paths are all resolved from the src/ directory, so must |
| 211 | # be relative to that. |
| 212 | set extra_files { |
| 213 | diff.tcl |
| 214 | merge.tcl |
| 215 | markdown.md |
| 216 | wiki.wiki |
| 217 | *.js |
| 218 | default.css |
| 219 | style.*.css |
| @@ -445,11 +446,11 @@ | |
| 446 | |
| 447 | # The USE_LINENOISE variable may be undefined, set to 0, or set |
| 448 | # to 1. If it is set to 0, then there is no need to build or link |
| 449 | # the linenoise.o object. |
| 450 | LINENOISE_DEF.0 = |
| 451 | LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 |
| 452 | LINENOISE_DEF. = $(LINENOISE_DEF.0) |
| 453 | LINENOISE_OBJ.0 = |
| 454 | LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o |
| 455 | LINENOISE_OBJ. = $(LINENOISE_OBJ.0) |
| 456 | |
| 457 |
+1
-1
| --- tools/man_page_command_list.tcl | ||
| +++ tools/man_page_command_list.tcl | ||
| @@ -10,11 +10,11 @@ | ||
| 10 | 10 | set file [lindex $argv 0] |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | # Get list of common commands. |
| 14 | 14 | set commands [exec fossil help] |
| 15 | -regsub -nocase {.*?\ncommon commands:.*\n} $commands {} commands | |
| 15 | +regsub -nocase {.*?\nfrequently used commands:.*\n} $commands {} commands | |
| 16 | 16 | regsub -nocase {\nthis is fossil version.*} $commands {} commands |
| 17 | 17 | regsub -all {\s+} $commands " " commands |
| 18 | 18 | set commands [lsort $commands] |
| 19 | 19 | |
| 20 | 20 | # Compute number of rows. |
| 21 | 21 |
| --- tools/man_page_command_list.tcl | |
| +++ tools/man_page_command_list.tcl | |
| @@ -10,11 +10,11 @@ | |
| 10 | set file [lindex $argv 0] |
| 11 | } |
| 12 | |
| 13 | # Get list of common commands. |
| 14 | set commands [exec fossil help] |
| 15 | regsub -nocase {.*?\ncommon commands:.*\n} $commands {} commands |
| 16 | regsub -nocase {\nthis is fossil version.*} $commands {} commands |
| 17 | regsub -all {\s+} $commands " " commands |
| 18 | set commands [lsort $commands] |
| 19 | |
| 20 | # Compute number of rows. |
| 21 |
| --- tools/man_page_command_list.tcl | |
| +++ tools/man_page_command_list.tcl | |
| @@ -10,11 +10,11 @@ | |
| 10 | set file [lindex $argv 0] |
| 11 | } |
| 12 | |
| 13 | # Get list of common commands. |
| 14 | set commands [exec fossil help] |
| 15 | regsub -nocase {.*?\nfrequently used commands:.*\n} $commands {} commands |
| 16 | regsub -nocase {\nthis is fossil version.*} $commands {} commands |
| 17 | regsub -all {\s+} $commands " " commands |
| 18 | set commands [lsort $commands] |
| 19 | |
| 20 | # Compute number of rows. |
| 21 |
+1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -634,10 +634,11 @@ | ||
| 634 | 634 | $(SRCDIR)/hbmenu.js \ |
| 635 | 635 | $(SRCDIR)/href.js \ |
| 636 | 636 | $(SRCDIR)/login.js \ |
| 637 | 637 | $(SRCDIR)/markdown.md \ |
| 638 | 638 | $(SRCDIR)/menu.js \ |
| 639 | + $(SRCDIR)/merge.tcl \ | |
| 639 | 640 | $(SRCDIR)/scroll.js \ |
| 640 | 641 | $(SRCDIR)/skin.js \ |
| 641 | 642 | $(SRCDIR)/sorttable.js \ |
| 642 | 643 | $(SRCDIR)/sounds/0.wav \ |
| 643 | 644 | $(SRCDIR)/sounds/1.wav \ |
| 644 | 645 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -634,10 +634,11 @@ | |
| 634 | $(SRCDIR)/hbmenu.js \ |
| 635 | $(SRCDIR)/href.js \ |
| 636 | $(SRCDIR)/login.js \ |
| 637 | $(SRCDIR)/markdown.md \ |
| 638 | $(SRCDIR)/menu.js \ |
| 639 | $(SRCDIR)/scroll.js \ |
| 640 | $(SRCDIR)/skin.js \ |
| 641 | $(SRCDIR)/sorttable.js \ |
| 642 | $(SRCDIR)/sounds/0.wav \ |
| 643 | $(SRCDIR)/sounds/1.wav \ |
| 644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -634,10 +634,11 @@ | |
| 634 | $(SRCDIR)/hbmenu.js \ |
| 635 | $(SRCDIR)/href.js \ |
| 636 | $(SRCDIR)/login.js \ |
| 637 | $(SRCDIR)/markdown.md \ |
| 638 | $(SRCDIR)/menu.js \ |
| 639 | $(SRCDIR)/merge.tcl \ |
| 640 | $(SRCDIR)/scroll.js \ |
| 641 | $(SRCDIR)/skin.js \ |
| 642 | $(SRCDIR)/sorttable.js \ |
| 643 | $(SRCDIR)/sounds/0.wav \ |
| 644 | $(SRCDIR)/sounds/1.wav \ |
| 645 |
+2
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -592,10 +592,11 @@ | ||
| 592 | 592 | "$(SRCDIR)\hbmenu.js" \ |
| 593 | 593 | "$(SRCDIR)\href.js" \ |
| 594 | 594 | "$(SRCDIR)\login.js" \ |
| 595 | 595 | "$(SRCDIR)\markdown.md" \ |
| 596 | 596 | "$(SRCDIR)\menu.js" \ |
| 597 | + "$(SRCDIR)\merge.tcl" \ | |
| 597 | 598 | "$(SRCDIR)\scroll.js" \ |
| 598 | 599 | "$(SRCDIR)\skin.js" \ |
| 599 | 600 | "$(SRCDIR)\sorttable.js" \ |
| 600 | 601 | "$(SRCDIR)\sounds\0.wav" \ |
| 601 | 602 | "$(SRCDIR)\sounds\1.wav" \ |
| @@ -1222,10 +1223,11 @@ | ||
| 1222 | 1223 | echo "$(SRCDIR)\hbmenu.js" >> $@ |
| 1223 | 1224 | echo "$(SRCDIR)\href.js" >> $@ |
| 1224 | 1225 | echo "$(SRCDIR)\login.js" >> $@ |
| 1225 | 1226 | echo "$(SRCDIR)\markdown.md" >> $@ |
| 1226 | 1227 | echo "$(SRCDIR)\menu.js" >> $@ |
| 1228 | + echo "$(SRCDIR)\merge.tcl" >> $@ | |
| 1227 | 1229 | echo "$(SRCDIR)\scroll.js" >> $@ |
| 1228 | 1230 | echo "$(SRCDIR)\skin.js" >> $@ |
| 1229 | 1231 | echo "$(SRCDIR)\sorttable.js" >> $@ |
| 1230 | 1232 | echo "$(SRCDIR)\sounds/0.wav" >> $@ |
| 1231 | 1233 | echo "$(SRCDIR)\sounds/1.wav" >> $@ |
| 1232 | 1234 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -592,10 +592,11 @@ | |
| 592 | "$(SRCDIR)\hbmenu.js" \ |
| 593 | "$(SRCDIR)\href.js" \ |
| 594 | "$(SRCDIR)\login.js" \ |
| 595 | "$(SRCDIR)\markdown.md" \ |
| 596 | "$(SRCDIR)\menu.js" \ |
| 597 | "$(SRCDIR)\scroll.js" \ |
| 598 | "$(SRCDIR)\skin.js" \ |
| 599 | "$(SRCDIR)\sorttable.js" \ |
| 600 | "$(SRCDIR)\sounds\0.wav" \ |
| 601 | "$(SRCDIR)\sounds\1.wav" \ |
| @@ -1222,10 +1223,11 @@ | |
| 1222 | echo "$(SRCDIR)\hbmenu.js" >> $@ |
| 1223 | echo "$(SRCDIR)\href.js" >> $@ |
| 1224 | echo "$(SRCDIR)\login.js" >> $@ |
| 1225 | echo "$(SRCDIR)\markdown.md" >> $@ |
| 1226 | echo "$(SRCDIR)\menu.js" >> $@ |
| 1227 | echo "$(SRCDIR)\scroll.js" >> $@ |
| 1228 | echo "$(SRCDIR)\skin.js" >> $@ |
| 1229 | echo "$(SRCDIR)\sorttable.js" >> $@ |
| 1230 | echo "$(SRCDIR)\sounds/0.wav" >> $@ |
| 1231 | echo "$(SRCDIR)\sounds/1.wav" >> $@ |
| 1232 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -592,10 +592,11 @@ | |
| 592 | "$(SRCDIR)\hbmenu.js" \ |
| 593 | "$(SRCDIR)\href.js" \ |
| 594 | "$(SRCDIR)\login.js" \ |
| 595 | "$(SRCDIR)\markdown.md" \ |
| 596 | "$(SRCDIR)\menu.js" \ |
| 597 | "$(SRCDIR)\merge.tcl" \ |
| 598 | "$(SRCDIR)\scroll.js" \ |
| 599 | "$(SRCDIR)\skin.js" \ |
| 600 | "$(SRCDIR)\sorttable.js" \ |
| 601 | "$(SRCDIR)\sounds\0.wav" \ |
| 602 | "$(SRCDIR)\sounds\1.wav" \ |
| @@ -1222,10 +1223,11 @@ | |
| 1223 | echo "$(SRCDIR)\hbmenu.js" >> $@ |
| 1224 | echo "$(SRCDIR)\href.js" >> $@ |
| 1225 | echo "$(SRCDIR)\login.js" >> $@ |
| 1226 | echo "$(SRCDIR)\markdown.md" >> $@ |
| 1227 | echo "$(SRCDIR)\menu.js" >> $@ |
| 1228 | echo "$(SRCDIR)\merge.tcl" >> $@ |
| 1229 | echo "$(SRCDIR)\scroll.js" >> $@ |
| 1230 | echo "$(SRCDIR)\skin.js" >> $@ |
| 1231 | echo "$(SRCDIR)\sorttable.js" >> $@ |
| 1232 | echo "$(SRCDIR)\sounds/0.wav" >> $@ |
| 1233 | echo "$(SRCDIR)\sounds/1.wav" >> $@ |
| 1234 |
+4
-4
| --- www/aboutcgi.wiki | ||
| +++ www/aboutcgi.wiki | ||
| @@ -8,30 +8,30 @@ | ||
| 8 | 8 | |
| 9 | 9 | This is a "how it works" guide. This document provides background |
| 10 | 10 | information on the CGI protocol so that you can better understand what |
| 11 | 11 | is going on behind the scenes. If you just want to set up Fossil |
| 12 | 12 | as a CGI server, see the [./server/ | Fossil Server Setup] page. Or |
| 13 | -if you want to development CGI-based extensions to Fossil, see | |
| 13 | +if you want to develop CGI-based extensions to Fossil, see | |
| 14 | 14 | the [./serverext.wiki|CGI Server Extensions] page. |
| 15 | 15 | |
| 16 | 16 | <h2>A Quick Review Of CGI</h2> |
| 17 | 17 | |
| 18 | 18 | An HTTP request is a block of text that is sent by a client application |
| 19 | 19 | (usually a web browser) and arrives at the web server over a network |
| 20 | 20 | connection. The HTTP request contains a URL that describes the information |
| 21 | 21 | being requested. The URL in the HTTP request is typically the same URL |
| 22 | 22 | that appears in the URL bar at the top of the web browser that is making |
| 23 | -the request. The URL might contain a "?" character followed | |
| 23 | +the request. The URL might contain a "?" character followed by | |
| 24 | 24 | query parameters. The HTTP will usually also contain other information |
| 25 | 25 | such as the name of the application that made the request, whether or |
| 26 | 26 | not the requesting application can accept a compressed reply, POST |
| 27 | 27 | parameters from forms, and so forth. |
| 28 | 28 | |
| 29 | 29 | The job of the web server is to interpret the HTTP request and formulate |
| 30 | 30 | an appropriate reply. |
| 31 | -The web server is free to interpret the HTTP request in any way it wants. | |
| 32 | -But most web servers follow a similar pattern, described below. | |
| 31 | +The web server is free to interpret the HTTP request in any way it wants, | |
| 32 | +but most web servers follow a similar pattern, described below. | |
| 33 | 33 | (Note: details may vary from one web server to another.) |
| 34 | 34 | |
| 35 | 35 | Suppose the filename component of the URL in the HTTP request looks like this: |
| 36 | 36 | |
| 37 | 37 | <pre>/one/two/timeline/four</pre> |
| 38 | 38 |
| --- www/aboutcgi.wiki | |
| +++ www/aboutcgi.wiki | |
| @@ -8,30 +8,30 @@ | |
| 8 | |
| 9 | This is a "how it works" guide. This document provides background |
| 10 | information on the CGI protocol so that you can better understand what |
| 11 | is going on behind the scenes. If you just want to set up Fossil |
| 12 | as a CGI server, see the [./server/ | Fossil Server Setup] page. Or |
| 13 | if you want to development CGI-based extensions to Fossil, see |
| 14 | the [./serverext.wiki|CGI Server Extensions] page. |
| 15 | |
| 16 | <h2>A Quick Review Of CGI</h2> |
| 17 | |
| 18 | An HTTP request is a block of text that is sent by a client application |
| 19 | (usually a web browser) and arrives at the web server over a network |
| 20 | connection. The HTTP request contains a URL that describes the information |
| 21 | being requested. The URL in the HTTP request is typically the same URL |
| 22 | that appears in the URL bar at the top of the web browser that is making |
| 23 | the request. The URL might contain a "?" character followed |
| 24 | query parameters. The HTTP will usually also contain other information |
| 25 | such as the name of the application that made the request, whether or |
| 26 | not the requesting application can accept a compressed reply, POST |
| 27 | parameters from forms, and so forth. |
| 28 | |
| 29 | The job of the web server is to interpret the HTTP request and formulate |
| 30 | an appropriate reply. |
| 31 | The web server is free to interpret the HTTP request in any way it wants. |
| 32 | But most web servers follow a similar pattern, described below. |
| 33 | (Note: details may vary from one web server to another.) |
| 34 | |
| 35 | Suppose the filename component of the URL in the HTTP request looks like this: |
| 36 | |
| 37 | <pre>/one/two/timeline/four</pre> |
| 38 |
| --- www/aboutcgi.wiki | |
| +++ www/aboutcgi.wiki | |
| @@ -8,30 +8,30 @@ | |
| 8 | |
| 9 | This is a "how it works" guide. This document provides background |
| 10 | information on the CGI protocol so that you can better understand what |
| 11 | is going on behind the scenes. If you just want to set up Fossil |
| 12 | as a CGI server, see the [./server/ | Fossil Server Setup] page. Or |
| 13 | if you want to develop CGI-based extensions to Fossil, see |
| 14 | the [./serverext.wiki|CGI Server Extensions] page. |
| 15 | |
| 16 | <h2>A Quick Review Of CGI</h2> |
| 17 | |
| 18 | An HTTP request is a block of text that is sent by a client application |
| 19 | (usually a web browser) and arrives at the web server over a network |
| 20 | connection. The HTTP request contains a URL that describes the information |
| 21 | being requested. The URL in the HTTP request is typically the same URL |
| 22 | that appears in the URL bar at the top of the web browser that is making |
| 23 | the request. The URL might contain a "?" character followed by |
| 24 | query parameters. The HTTP will usually also contain other information |
| 25 | such as the name of the application that made the request, whether or |
| 26 | not the requesting application can accept a compressed reply, POST |
| 27 | parameters from forms, and so forth. |
| 28 | |
| 29 | The job of the web server is to interpret the HTTP request and formulate |
| 30 | an appropriate reply. |
| 31 | The web server is free to interpret the HTTP request in any way it wants, |
| 32 | but most web servers follow a similar pattern, described below. |
| 33 | (Note: details may vary from one web server to another.) |
| 34 | |
| 35 | Suppose the filename component of the URL in the HTTP request looks like this: |
| 36 | |
| 37 | <pre>/one/two/timeline/four</pre> |
| 38 |
+3
-3
| --- www/adding_code.wiki | ||
| +++ www/adding_code.wiki | ||
| @@ -37,11 +37,11 @@ | ||
| 37 | 37 | The [./makefile.wiki|Makefile] for Fossil takes care of running these |
| 38 | 38 | preprocessors with all the right arguments and in the right order. So it is |
| 39 | 39 | not necessary to understand the details of how these preprocessors work. |
| 40 | 40 | (Though, the sources for all three preprocessors are included in the source |
| 41 | 41 | tree and are well commented, if you want to dig deeper.) It is only necessary |
| 42 | -to know that these preprocessors exist and hence will effect the way you | |
| 42 | +to know that these preprocessors exist and hence will affect the way you | |
| 43 | 43 | write code. |
| 44 | 44 | |
| 45 | 45 | <h2>3.0 Adding New Source Code Files</h2> |
| 46 | 46 | |
| 47 | 47 | New source code files are added in the "src/" subdirectory of the Fossil |
| @@ -98,11 +98,11 @@ | ||
| 98 | 98 | files. See [../tools/makeheaders.html | makeheaders.html] for additional |
| 99 | 99 | information. |
| 100 | 100 | |
| 101 | 101 | After creating a template file such as shown above, and after updating |
| 102 | 102 | the makefiles, you should be able to recompile Fossil and have it include |
| 103 | -your new source file, even before you source file contains any code. | |
| 103 | +your new source file, even before your source file contains any code. | |
| 104 | 104 | It is recommended that you try this. |
| 105 | 105 | |
| 106 | 106 | Be sure to [/help/add|fossil add] your new source file to the self-hosting |
| 107 | 107 | Fossil repository and then [/help/commit|commit] your changes! |
| 108 | 108 | |
| @@ -212,11 +212,11 @@ | ||
| 212 | 212 | non-HTML content. In the unlikely event that you need to generate |
| 213 | 213 | non-HTML content, look at existing webpage implementations |
| 214 | 214 | (ex: "logo" or "style.css") to see how that is done. |
| 215 | 215 | |
| 216 | 216 | There are lots of other things that a real web-page implementation will |
| 217 | -need to do, such verifying user credentials, parsing query parameters, | |
| 217 | +need to do, such as verifying user credentials, parsing query parameters, | |
| 218 | 218 | and interacting with the repository. But now that you have the general |
| 219 | 219 | idea of how webpages are implemented, you can look at the many other |
| 220 | 220 | webpage implementations already built into Fossil to see how all that |
| 221 | 221 | works. |
| 222 | 222 | |
| 223 | 223 |
| --- www/adding_code.wiki | |
| +++ www/adding_code.wiki | |
| @@ -37,11 +37,11 @@ | |
| 37 | The [./makefile.wiki|Makefile] for Fossil takes care of running these |
| 38 | preprocessors with all the right arguments and in the right order. So it is |
| 39 | not necessary to understand the details of how these preprocessors work. |
| 40 | (Though, the sources for all three preprocessors are included in the source |
| 41 | tree and are well commented, if you want to dig deeper.) It is only necessary |
| 42 | to know that these preprocessors exist and hence will effect the way you |
| 43 | write code. |
| 44 | |
| 45 | <h2>3.0 Adding New Source Code Files</h2> |
| 46 | |
| 47 | New source code files are added in the "src/" subdirectory of the Fossil |
| @@ -98,11 +98,11 @@ | |
| 98 | files. See [../tools/makeheaders.html | makeheaders.html] for additional |
| 99 | information. |
| 100 | |
| 101 | After creating a template file such as shown above, and after updating |
| 102 | the makefiles, you should be able to recompile Fossil and have it include |
| 103 | your new source file, even before you source file contains any code. |
| 104 | It is recommended that you try this. |
| 105 | |
| 106 | Be sure to [/help/add|fossil add] your new source file to the self-hosting |
| 107 | Fossil repository and then [/help/commit|commit] your changes! |
| 108 | |
| @@ -212,11 +212,11 @@ | |
| 212 | non-HTML content. In the unlikely event that you need to generate |
| 213 | non-HTML content, look at existing webpage implementations |
| 214 | (ex: "logo" or "style.css") to see how that is done. |
| 215 | |
| 216 | There are lots of other things that a real web-page implementation will |
| 217 | need to do, such verifying user credentials, parsing query parameters, |
| 218 | and interacting with the repository. But now that you have the general |
| 219 | idea of how webpages are implemented, you can look at the many other |
| 220 | webpage implementations already built into Fossil to see how all that |
| 221 | works. |
| 222 | |
| 223 |
| --- www/adding_code.wiki | |
| +++ www/adding_code.wiki | |
| @@ -37,11 +37,11 @@ | |
| 37 | The [./makefile.wiki|Makefile] for Fossil takes care of running these |
| 38 | preprocessors with all the right arguments and in the right order. So it is |
| 39 | not necessary to understand the details of how these preprocessors work. |
| 40 | (Though, the sources for all three preprocessors are included in the source |
| 41 | tree and are well commented, if you want to dig deeper.) It is only necessary |
| 42 | to know that these preprocessors exist and hence will affect the way you |
| 43 | write code. |
| 44 | |
| 45 | <h2>3.0 Adding New Source Code Files</h2> |
| 46 | |
| 47 | New source code files are added in the "src/" subdirectory of the Fossil |
| @@ -98,11 +98,11 @@ | |
| 98 | files. See [../tools/makeheaders.html | makeheaders.html] for additional |
| 99 | information. |
| 100 | |
| 101 | After creating a template file such as shown above, and after updating |
| 102 | the makefiles, you should be able to recompile Fossil and have it include |
| 103 | your new source file, even before your source file contains any code. |
| 104 | It is recommended that you try this. |
| 105 | |
| 106 | Be sure to [/help/add|fossil add] your new source file to the self-hosting |
| 107 | Fossil repository and then [/help/commit|commit] your changes! |
| 108 | |
| @@ -212,11 +212,11 @@ | |
| 212 | non-HTML content. In the unlikely event that you need to generate |
| 213 | non-HTML content, look at existing webpage implementations |
| 214 | (ex: "logo" or "style.css") to see how that is done. |
| 215 | |
| 216 | There are lots of other things that a real web-page implementation will |
| 217 | need to do, such as verifying user credentials, parsing query parameters, |
| 218 | and interacting with the repository. But now that you have the general |
| 219 | idea of how webpages are implemented, you can look at the many other |
| 220 | webpage implementations already built into Fossil to see how all that |
| 221 | works. |
| 222 | |
| 223 |
+2
-2
| --- www/alerts.md | ||
| +++ www/alerts.md | ||
| @@ -311,11 +311,11 @@ | ||
| 311 | 311 | ## Advanced Email Setups |
| 312 | 312 | |
| 313 | 313 | Fossil offers several methods of sending email: |
| 314 | 314 | |
| 315 | 315 | 1. Pipe the email message text into a command. |
| 316 | - 2. Store email messages as entries in a SQLite database. | |
| 316 | + 2. Store email messages as entries in an SQLite database. | |
| 317 | 317 | 3. Store email messages as individual files in a directory. |
| 318 | 318 | 4. Send emails to an SMTP relay. |
| 319 | 319 | 5. Send emails directly to the recipients via SMTP. |
| 320 | 320 | |
| 321 | 321 | This wide range of options allows Fossil to talk to pretty much any |
| @@ -390,11 +390,11 @@ | ||
| 390 | 390 | currently uses this method rather than [the pipe method](#pipe) because |
| 391 | 391 | it is running inside of a restrictive [chroot jail][cj] which is unable |
| 392 | 392 | to hand off messages to the local MTA directly. |
| 393 | 393 | |
| 394 | 394 | When you configure a Fossil server this way, it adds outgoing email |
| 395 | -messages to a SQLite database file. A separate daemon process can then | |
| 395 | +messages to an SQLite database file. A separate daemon process can then | |
| 396 | 396 | extract those messages for further disposition. |
| 397 | 397 | |
| 398 | 398 | Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl) |
| 399 | 399 | used on `fossil-scm.org`: it is just a short Tcl script that |
| 400 | 400 | continuously monitors this database for new messages and hands any that |
| 401 | 401 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -311,11 +311,11 @@ | |
| 311 | ## Advanced Email Setups |
| 312 | |
| 313 | Fossil offers several methods of sending email: |
| 314 | |
| 315 | 1. Pipe the email message text into a command. |
| 316 | 2. Store email messages as entries in a SQLite database. |
| 317 | 3. Store email messages as individual files in a directory. |
| 318 | 4. Send emails to an SMTP relay. |
| 319 | 5. Send emails directly to the recipients via SMTP. |
| 320 | |
| 321 | This wide range of options allows Fossil to talk to pretty much any |
| @@ -390,11 +390,11 @@ | |
| 390 | currently uses this method rather than [the pipe method](#pipe) because |
| 391 | it is running inside of a restrictive [chroot jail][cj] which is unable |
| 392 | to hand off messages to the local MTA directly. |
| 393 | |
| 394 | When you configure a Fossil server this way, it adds outgoing email |
| 395 | messages to a SQLite database file. A separate daemon process can then |
| 396 | extract those messages for further disposition. |
| 397 | |
| 398 | Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl) |
| 399 | used on `fossil-scm.org`: it is just a short Tcl script that |
| 400 | continuously monitors this database for new messages and hands any that |
| 401 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -311,11 +311,11 @@ | |
| 311 | ## Advanced Email Setups |
| 312 | |
| 313 | Fossil offers several methods of sending email: |
| 314 | |
| 315 | 1. Pipe the email message text into a command. |
| 316 | 2. Store email messages as entries in an SQLite database. |
| 317 | 3. Store email messages as individual files in a directory. |
| 318 | 4. Send emails to an SMTP relay. |
| 319 | 5. Send emails directly to the recipients via SMTP. |
| 320 | |
| 321 | This wide range of options allows Fossil to talk to pretty much any |
| @@ -390,11 +390,11 @@ | |
| 390 | currently uses this method rather than [the pipe method](#pipe) because |
| 391 | it is running inside of a restrictive [chroot jail][cj] which is unable |
| 392 | to hand off messages to the local MTA directly. |
| 393 | |
| 394 | When you configure a Fossil server this way, it adds outgoing email |
| 395 | messages to an SQLite database file. A separate daemon process can then |
| 396 | extract those messages for further disposition. |
| 397 | |
| 398 | Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl) |
| 399 | used on `fossil-scm.org`: it is just a short Tcl script that |
| 400 | continuously monitors this database for new messages and hands any that |
| 401 |
+2
-2
| --- www/antibot.wiki | ||
| +++ www/antibot.wiki | ||
| @@ -73,17 +73,17 @@ | ||
| 73 | 73 | In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled |
| 74 | 74 | "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>". |
| 75 | 75 | If this setting is set to "UserAgent only" or "UserAgent and Javascript", |
| 76 | 76 | and if the UserAgent string looks like a human and not a robot, then |
| 77 | 77 | Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability |
| 78 | -is omitted from the user permissions. This settingn gives humans easy | |
| 78 | +is omitted from the user permissions. This setting gives humans easy | |
| 79 | 79 | access to the hyperlinks while preventing robots |
| 80 | 80 | from walking the billions of pages on a typical Fossil site. |
| 81 | 81 | |
| 82 | 82 | If the setting is "UserAgent only", then the hyperlinks are simply |
| 83 | 83 | enabled and that is all. But if the setting is "UserAgent and Javascript", |
| 84 | -then the hyperlinks are not enabled directly . | |
| 84 | +then the hyperlinks are not enabled directly. | |
| 85 | 85 | Instead, the HTML code that is generated contains anchor tags ("<a>") |
| 86 | 86 | with "href=" attributes that point to [/honeypot] rather than the correct |
| 87 | 87 | link. JavaScript code is added to the end of the page that goes back and |
| 88 | 88 | fills in the correct "href=" attributes of |
| 89 | 89 | the anchor tags with the true hyperlink targets, thus enabling the hyperlinks. |
| 90 | 90 |
| --- www/antibot.wiki | |
| +++ www/antibot.wiki | |
| @@ -73,17 +73,17 @@ | |
| 73 | In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled |
| 74 | "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>". |
| 75 | If this setting is set to "UserAgent only" or "UserAgent and Javascript", |
| 76 | and if the UserAgent string looks like a human and not a robot, then |
| 77 | Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability |
| 78 | is omitted from the user permissions. This settingn gives humans easy |
| 79 | access to the hyperlinks while preventing robots |
| 80 | from walking the billions of pages on a typical Fossil site. |
| 81 | |
| 82 | If the setting is "UserAgent only", then the hyperlinks are simply |
| 83 | enabled and that is all. But if the setting is "UserAgent and Javascript", |
| 84 | then the hyperlinks are not enabled directly . |
| 85 | Instead, the HTML code that is generated contains anchor tags ("<a>") |
| 86 | with "href=" attributes that point to [/honeypot] rather than the correct |
| 87 | link. JavaScript code is added to the end of the page that goes back and |
| 88 | fills in the correct "href=" attributes of |
| 89 | the anchor tags with the true hyperlink targets, thus enabling the hyperlinks. |
| 90 |
| --- www/antibot.wiki | |
| +++ www/antibot.wiki | |
| @@ -73,17 +73,17 @@ | |
| 73 | In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled |
| 74 | "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>". |
| 75 | If this setting is set to "UserAgent only" or "UserAgent and Javascript", |
| 76 | and if the UserAgent string looks like a human and not a robot, then |
| 77 | Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability |
| 78 | is omitted from the user permissions. This setting gives humans easy |
| 79 | access to the hyperlinks while preventing robots |
| 80 | from walking the billions of pages on a typical Fossil site. |
| 81 | |
| 82 | If the setting is "UserAgent only", then the hyperlinks are simply |
| 83 | enabled and that is all. But if the setting is "UserAgent and Javascript", |
| 84 | then the hyperlinks are not enabled directly. |
| 85 | Instead, the HTML code that is generated contains anchor tags ("<a>") |
| 86 | with "href=" attributes that point to [/honeypot] rather than the correct |
| 87 | link. JavaScript code is added to the end of the page that goes back and |
| 88 | fills in the correct "href=" attributes of |
| 89 | the anchor tags with the true hyperlink targets, thus enabling the hyperlinks. |
| 90 |
+3
-3
| --- www/backoffice.md | ||
| +++ www/backoffice.md | ||
| @@ -115,14 +115,14 @@ | ||
| 115 | 115 | launching the backoffice for all of them. There are additional useful |
| 116 | 116 | command-line options. See the "[fossil backoffice](/help?cmd=backoffice)" |
| 117 | 117 | documentation for details. |
| 118 | 118 | |
| 119 | 119 | The backoffice processes run manually using the "fossil backoffice" |
| 120 | -command do not normally use a lease. That means that you run the | |
| 120 | +command do not normally use a lease. That means that if you run the | |
| 121 | 121 | "fossil backoffice" command with --poll and you forget to disable |
| 122 | 122 | automatic backoffice by setting the "backoffice-disable" flag, then |
| 123 | -you might have one backoffice running due command and another due | |
| 123 | +you might have one backoffice running due to a command and another due | |
| 124 | 124 | to a webpage access, both at the same time. This is harmless. The |
| 125 | 125 | only downside is that it uses extra CPU time. |
| 126 | 126 | |
| 127 | 127 | How Backoffice Is Implemented |
| 128 | 128 | ----------------------------- |
| @@ -210,11 +210,11 @@ | ||
| 210 | 210 | backoffice processes to exit either immediately or after doing whatever |
| 211 | 211 | backoffice works needs to be done. If something is going wrong and |
| 212 | 212 | backoffice leases are causing delays in webpage processing, then setting |
| 213 | 213 | "backoffice-nodelay" to true can work around the problem until the bug |
| 214 | 214 | can be fixed. The "backoffice-logfile" setting is the name of a log |
| 215 | -file onto which is appended a short message everything a backoffice | |
| 215 | +file onto which is appended a short message every time a backoffice | |
| 216 | 216 | process actually starts to do the backoffice work. This log file can |
| 217 | 217 | be used to verify that backoffice really is running, if there is any |
| 218 | 218 | doubt. The "backoffice-disable" setting prevents automatic backoffice |
| 219 | 219 | processing, if true. Use this to completely disable backoffice processing |
| 220 | 220 | that occurs automatically after each HTTP request. The "backoffice-disable" |
| 221 | 221 |
| --- www/backoffice.md | |
| +++ www/backoffice.md | |
| @@ -115,14 +115,14 @@ | |
| 115 | launching the backoffice for all of them. There are additional useful |
| 116 | command-line options. See the "[fossil backoffice](/help?cmd=backoffice)" |
| 117 | documentation for details. |
| 118 | |
| 119 | The backoffice processes run manually using the "fossil backoffice" |
| 120 | command do not normally use a lease. That means that you run the |
| 121 | "fossil backoffice" command with --poll and you forget to disable |
| 122 | automatic backoffice by setting the "backoffice-disable" flag, then |
| 123 | you might have one backoffice running due command and another due |
| 124 | to a webpage access, both at the same time. This is harmless. The |
| 125 | only downside is that it uses extra CPU time. |
| 126 | |
| 127 | How Backoffice Is Implemented |
| 128 | ----------------------------- |
| @@ -210,11 +210,11 @@ | |
| 210 | backoffice processes to exit either immediately or after doing whatever |
| 211 | backoffice works needs to be done. If something is going wrong and |
| 212 | backoffice leases are causing delays in webpage processing, then setting |
| 213 | "backoffice-nodelay" to true can work around the problem until the bug |
| 214 | can be fixed. The "backoffice-logfile" setting is the name of a log |
| 215 | file onto which is appended a short message everything a backoffice |
| 216 | process actually starts to do the backoffice work. This log file can |
| 217 | be used to verify that backoffice really is running, if there is any |
| 218 | doubt. The "backoffice-disable" setting prevents automatic backoffice |
| 219 | processing, if true. Use this to completely disable backoffice processing |
| 220 | that occurs automatically after each HTTP request. The "backoffice-disable" |
| 221 |
| --- www/backoffice.md | |
| +++ www/backoffice.md | |
| @@ -115,14 +115,14 @@ | |
| 115 | launching the backoffice for all of them. There are additional useful |
| 116 | command-line options. See the "[fossil backoffice](/help?cmd=backoffice)" |
| 117 | documentation for details. |
| 118 | |
| 119 | The backoffice processes run manually using the "fossil backoffice" |
| 120 | command do not normally use a lease. That means that if you run the |
| 121 | "fossil backoffice" command with --poll and you forget to disable |
| 122 | automatic backoffice by setting the "backoffice-disable" flag, then |
| 123 | you might have one backoffice running due to a command and another due |
| 124 | to a webpage access, both at the same time. This is harmless. The |
| 125 | only downside is that it uses extra CPU time. |
| 126 | |
| 127 | How Backoffice Is Implemented |
| 128 | ----------------------------- |
| @@ -210,11 +210,11 @@ | |
| 210 | backoffice processes to exit either immediately or after doing whatever |
| 211 | backoffice works needs to be done. If something is going wrong and |
| 212 | backoffice leases are causing delays in webpage processing, then setting |
| 213 | "backoffice-nodelay" to true can work around the problem until the bug |
| 214 | can be fixed. The "backoffice-logfile" setting is the name of a log |
| 215 | file onto which is appended a short message every time a backoffice |
| 216 | process actually starts to do the backoffice work. This log file can |
| 217 | be used to verify that backoffice really is running, if there is any |
| 218 | doubt. The "backoffice-disable" setting prevents automatic backoffice |
| 219 | processing, if true. Use this to completely disable backoffice processing |
| 220 | that occurs automatically after each HTTP request. The "backoffice-disable" |
| 221 |
+2
-2
| --- www/blame.wiki | ||
| +++ www/blame.wiki | ||
| @@ -19,11 +19,11 @@ | ||
| 19 | 19 | annotated. Call this check-in C0. |
| 20 | 20 | <li>Find all direct ancestors of C0. A direct ancestor is the closure |
| 21 | 21 | of the primary parent of C0. Merged in branches are not part of |
| 22 | 22 | the direct ancestors of C0. |
| 23 | 23 | <li>Prune the list of ancestors of C0 so that it contains only |
| 24 | - check-in in which the file to be annotated was modified. | |
| 24 | + check-ins in which the file to be annotated was modified. | |
| 25 | 25 | <li>Load the complete text of the file to be annotated from check-in C0. |
| 26 | 26 | Call this version of the file F0. |
| 27 | 27 | <li>Parse F0 into lines. Mark each line as "unchanged". |
| 28 | 28 | <li>For each ancestor of C0 on the pruned list (call the ancestor CX), |
| 29 | 29 | beginning with the most |
| @@ -45,11 +45,11 @@ | ||
| 45 | 45 | |
| 46 | 46 | The time-consuming part of this algorithm is step 6b - computing the |
| 47 | 47 | diff from all historical versions of the file to the version of the file |
| 48 | 48 | under analysis. For a large file that has many historical changes, this |
| 49 | 49 | can take several seconds. For this reason, the default |
| 50 | -[/help?cmd=/annotate|/annotate] webpage only shows those lines that where | |
| 50 | +[/help?cmd=/annotate|/annotate] webpage only shows those lines that were | |
| 51 | 51 | changed by the 20 most recent modifications to the file. This allows |
| 52 | 52 | the loop on step 6 to terminate after only 19 diffs instead of the hundreds |
| 53 | 53 | or thousands of diffs that might be required for a frequently modified file. |
| 54 | 54 | |
| 55 | 55 | As currently implemented (as of 2015-12-12) the annotate algorithm does not |
| 56 | 56 |
| --- www/blame.wiki | |
| +++ www/blame.wiki | |
| @@ -19,11 +19,11 @@ | |
| 19 | annotated. Call this check-in C0. |
| 20 | <li>Find all direct ancestors of C0. A direct ancestor is the closure |
| 21 | of the primary parent of C0. Merged in branches are not part of |
| 22 | the direct ancestors of C0. |
| 23 | <li>Prune the list of ancestors of C0 so that it contains only |
| 24 | check-in in which the file to be annotated was modified. |
| 25 | <li>Load the complete text of the file to be annotated from check-in C0. |
| 26 | Call this version of the file F0. |
| 27 | <li>Parse F0 into lines. Mark each line as "unchanged". |
| 28 | <li>For each ancestor of C0 on the pruned list (call the ancestor CX), |
| 29 | beginning with the most |
| @@ -45,11 +45,11 @@ | |
| 45 | |
| 46 | The time-consuming part of this algorithm is step 6b - computing the |
| 47 | diff from all historical versions of the file to the version of the file |
| 48 | under analysis. For a large file that has many historical changes, this |
| 49 | can take several seconds. For this reason, the default |
| 50 | [/help?cmd=/annotate|/annotate] webpage only shows those lines that where |
| 51 | changed by the 20 most recent modifications to the file. This allows |
| 52 | the loop on step 6 to terminate after only 19 diffs instead of the hundreds |
| 53 | or thousands of diffs that might be required for a frequently modified file. |
| 54 | |
| 55 | As currently implemented (as of 2015-12-12) the annotate algorithm does not |
| 56 |
| --- www/blame.wiki | |
| +++ www/blame.wiki | |
| @@ -19,11 +19,11 @@ | |
| 19 | annotated. Call this check-in C0. |
| 20 | <li>Find all direct ancestors of C0. A direct ancestor is the closure |
| 21 | of the primary parent of C0. Merged in branches are not part of |
| 22 | the direct ancestors of C0. |
| 23 | <li>Prune the list of ancestors of C0 so that it contains only |
| 24 | check-ins in which the file to be annotated was modified. |
| 25 | <li>Load the complete text of the file to be annotated from check-in C0. |
| 26 | Call this version of the file F0. |
| 27 | <li>Parse F0 into lines. Mark each line as "unchanged". |
| 28 | <li>For each ancestor of C0 on the pruned list (call the ancestor CX), |
| 29 | beginning with the most |
| @@ -45,11 +45,11 @@ | |
| 45 | |
| 46 | The time-consuming part of this algorithm is step 6b - computing the |
| 47 | diff from all historical versions of the file to the version of the file |
| 48 | under analysis. For a large file that has many historical changes, this |
| 49 | can take several seconds. For this reason, the default |
| 50 | [/help?cmd=/annotate|/annotate] webpage only shows those lines that were |
| 51 | changed by the 20 most recent modifications to the file. This allows |
| 52 | the loop on step 6 to terminate after only 19 diffs instead of the hundreds |
| 53 | or thousands of diffs that might be required for a frequently modified file. |
| 54 | |
| 55 | As currently implemented (as of 2015-12-12) the annotate algorithm does not |
| 56 |
+6
-6
| --- www/bugtheory.wiki | ||
| +++ www/bugtheory.wiki | ||
| @@ -112,13 +112,13 @@ | ||
| 112 | 112 | and is not shared with other repositories during a sync, push, or pull. |
| 113 | 113 | |
| 114 | 114 | Each repository also defines scripts used to generate web pages for |
| 115 | 115 | creating new tickets, viewing existing tickets, and modifying an |
| 116 | 116 | existing ticket. These scripts consist of HTML with an embedded |
| 117 | -scripts written a Tcl-like language called "[./th1.md|TH1]". Every new fossil | |
| 118 | -repository is created with default scripts. Administrators wishing to | |
| 119 | -customize their ticket entry, viewing, and editing screens should | |
| 120 | -modify the default scripts to suit their needs. These screen generator | |
| 121 | -scripts are part of the local state of a repository and are not shared | |
| 122 | -with other repositories during a sync, push, or pull. | |
| 117 | +scripts written in a Tcl-like language called "[./th1.md|TH1]". Every | |
| 118 | +new fossil repository is created with default scripts. Administrators | |
| 119 | +wishing to customize their ticket entry, viewing, and editing screens | |
| 120 | +should modify the default scripts to suit their needs. These screen | |
| 121 | +generator scripts are part of the local state of a repository and are | |
| 122 | +not shared with other repositories during a sync, push, or pull. | |
| 123 | 123 | |
| 124 | 124 | <i>To be continued...</i> |
| 125 | 125 |
| --- www/bugtheory.wiki | |
| +++ www/bugtheory.wiki | |
| @@ -112,13 +112,13 @@ | |
| 112 | and is not shared with other repositories during a sync, push, or pull. |
| 113 | |
| 114 | Each repository also defines scripts used to generate web pages for |
| 115 | creating new tickets, viewing existing tickets, and modifying an |
| 116 | existing ticket. These scripts consist of HTML with an embedded |
| 117 | scripts written a Tcl-like language called "[./th1.md|TH1]". Every new fossil |
| 118 | repository is created with default scripts. Administrators wishing to |
| 119 | customize their ticket entry, viewing, and editing screens should |
| 120 | modify the default scripts to suit their needs. These screen generator |
| 121 | scripts are part of the local state of a repository and are not shared |
| 122 | with other repositories during a sync, push, or pull. |
| 123 | |
| 124 | <i>To be continued...</i> |
| 125 |
| --- www/bugtheory.wiki | |
| +++ www/bugtheory.wiki | |
| @@ -112,13 +112,13 @@ | |
| 112 | and is not shared with other repositories during a sync, push, or pull. |
| 113 | |
| 114 | Each repository also defines scripts used to generate web pages for |
| 115 | creating new tickets, viewing existing tickets, and modifying an |
| 116 | existing ticket. These scripts consist of HTML with an embedded |
| 117 | scripts written in a Tcl-like language called "[./th1.md|TH1]". Every |
| 118 | new fossil repository is created with default scripts. Administrators |
| 119 | wishing to customize their ticket entry, viewing, and editing screens |
| 120 | should modify the default scripts to suit their needs. These screen |
| 121 | generator scripts are part of the local state of a repository and are |
| 122 | not shared with other repositories during a sync, push, or pull. |
| 123 | |
| 124 | <i>To be continued...</i> |
| 125 |
+2
-2
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -55,11 +55,11 @@ | ||
| 55 | 55 | <h2>Aside: Is it really safe to use an unreleased development version of |
| 56 | 56 | the Fossil source code?</h2> |
| 57 | 57 | |
| 58 | 58 | Yes! Any check-in on the |
| 59 | 59 | [/timeline?t=trunk | trunk branch] of the Fossil |
| 60 | -[https://fossil-scm.org/ | Fossil self-hosting repository] | |
| 60 | +[https://fossil-scm.org/ | self-hosting repository] | |
| 61 | 61 | will work fine. (Dodgy code is always on a branch.) In the unlikely |
| 62 | 62 | event that you pick a version with a serious bug, it still won't |
| 63 | 63 | clobber your files. Fossil uses several |
| 64 | 64 | [./selfcheck.wiki | self-checks] prior to committing any |
| 65 | 65 | repository change that prevent loss-of-work due to bugs. |
| @@ -475,11 +475,11 @@ | ||
| 475 | 475 | environment variables which are required by <tt>emcc</tt> and will |
| 476 | 476 | fail if it cannot do so. Once it's set up the environment, it passes |
| 477 | 477 | on all of its arguments to <tt>emcc</tt>. |
| 478 | 478 | |
| 479 | 479 | The WASM-related build parts are set up such that none of them should |
| 480 | -ever trigger implicity (e.g. via dependencies resolution) in a normal | |
| 480 | +ever trigger implicitly (e.g. via dependencies resolution) in a normal | |
| 481 | 481 | build cycle. They are instead explicitly built as described below. |
| 482 | 482 | |
| 483 | 483 | From the top of the source tree, all WASM-related components can be |
| 484 | 484 | built with: |
| 485 | 485 | |
| 486 | 486 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -55,11 +55,11 @@ | |
| 55 | <h2>Aside: Is it really safe to use an unreleased development version of |
| 56 | the Fossil source code?</h2> |
| 57 | |
| 58 | Yes! Any check-in on the |
| 59 | [/timeline?t=trunk | trunk branch] of the Fossil |
| 60 | [https://fossil-scm.org/ | Fossil self-hosting repository] |
| 61 | will work fine. (Dodgy code is always on a branch.) In the unlikely |
| 62 | event that you pick a version with a serious bug, it still won't |
| 63 | clobber your files. Fossil uses several |
| 64 | [./selfcheck.wiki | self-checks] prior to committing any |
| 65 | repository change that prevent loss-of-work due to bugs. |
| @@ -475,11 +475,11 @@ | |
| 475 | environment variables which are required by <tt>emcc</tt> and will |
| 476 | fail if it cannot do so. Once it's set up the environment, it passes |
| 477 | on all of its arguments to <tt>emcc</tt>. |
| 478 | |
| 479 | The WASM-related build parts are set up such that none of them should |
| 480 | ever trigger implicity (e.g. via dependencies resolution) in a normal |
| 481 | build cycle. They are instead explicitly built as described below. |
| 482 | |
| 483 | From the top of the source tree, all WASM-related components can be |
| 484 | built with: |
| 485 | |
| 486 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -55,11 +55,11 @@ | |
| 55 | <h2>Aside: Is it really safe to use an unreleased development version of |
| 56 | the Fossil source code?</h2> |
| 57 | |
| 58 | Yes! Any check-in on the |
| 59 | [/timeline?t=trunk | trunk branch] of the Fossil |
| 60 | [https://fossil-scm.org/ | self-hosting repository] |
| 61 | will work fine. (Dodgy code is always on a branch.) In the unlikely |
| 62 | event that you pick a version with a serious bug, it still won't |
| 63 | clobber your files. Fossil uses several |
| 64 | [./selfcheck.wiki | self-checks] prior to committing any |
| 65 | repository change that prevent loss-of-work due to bugs. |
| @@ -475,11 +475,11 @@ | |
| 475 | environment variables which are required by <tt>emcc</tt> and will |
| 476 | fail if it cannot do so. Once it's set up the environment, it passes |
| 477 | on all of its arguments to <tt>emcc</tt>. |
| 478 | |
| 479 | The WASM-related build parts are set up such that none of them should |
| 480 | ever trigger implicitly (e.g. via dependencies resolution) in a normal |
| 481 | build cycle. They are instead explicitly built as described below. |
| 482 | |
| 483 | From the top of the source tree, all WASM-related components can be |
| 484 | built with: |
| 485 | |
| 486 |
+1
-1
| --- www/caps/index.md | ||
| +++ www/caps/index.md | ||
| @@ -294,11 +294,11 @@ | ||
| 294 | 294 | Fossil requires write access to a repo DB while cloning from it, so you |
| 295 | 295 | can’t clone from a read-only repo DB file over a local file path. |
| 296 | 296 | |
| 297 | 297 | Even more surprising to you may be the fact that user caps do not affect |
| 298 | 298 | cloning and syncing over SSH! (Not unless you go [out of your way][sshfc] |
| 299 | -patch around it, at any rate.) When you make a change to such a | |
| 299 | +to patch around it, at any rate.) When you make a change to such a | |
| 300 | 300 | repository, the stock Fossil behavior is that the change first goes to the |
| 301 | 301 | local repo clone where file system |
| 302 | 302 | permissions are all that matter, but then upon sync, the situation is |
| 303 | 303 | effectively the same as when the parent repo is on the local file |
| 304 | 304 | system. The reason behind this is that if you can log into the remote |
| 305 | 305 |
| --- www/caps/index.md | |
| +++ www/caps/index.md | |
| @@ -294,11 +294,11 @@ | |
| 294 | Fossil requires write access to a repo DB while cloning from it, so you |
| 295 | can’t clone from a read-only repo DB file over a local file path. |
| 296 | |
| 297 | Even more surprising to you may be the fact that user caps do not affect |
| 298 | cloning and syncing over SSH! (Not unless you go [out of your way][sshfc] |
| 299 | patch around it, at any rate.) When you make a change to such a |
| 300 | repository, the stock Fossil behavior is that the change first goes to the |
| 301 | local repo clone where file system |
| 302 | permissions are all that matter, but then upon sync, the situation is |
| 303 | effectively the same as when the parent repo is on the local file |
| 304 | system. The reason behind this is that if you can log into the remote |
| 305 |
| --- www/caps/index.md | |
| +++ www/caps/index.md | |
| @@ -294,11 +294,11 @@ | |
| 294 | Fossil requires write access to a repo DB while cloning from it, so you |
| 295 | can’t clone from a read-only repo DB file over a local file path. |
| 296 | |
| 297 | Even more surprising to you may be the fact that user caps do not affect |
| 298 | cloning and syncing over SSH! (Not unless you go [out of your way][sshfc] |
| 299 | to patch around it, at any rate.) When you make a change to such a |
| 300 | repository, the stock Fossil behavior is that the change first goes to the |
| 301 | local repo clone where file system |
| 302 | permissions are all that matter, but then upon sync, the situation is |
| 303 | effectively the same as when the parent repo is on the local file |
| 304 | system. The reason behind this is that if you can log into the remote |
| 305 |
+1
-1
| --- www/caps/ref.html | ||
| +++ www/caps/ref.html | ||
| @@ -252,11 +252,11 @@ | ||
| 252 | 252 | Create new ticket report formats. Note that although this allows |
| 253 | 253 | the user to provide SQL code to be run in the server’s context, |
| 254 | 254 | and this capability is given to the untrusted “anonymous” user |
| 255 | 255 | category by default, this is a safe capability to give to users |
| 256 | 256 | because it is internally restricted to read-only queries on the |
| 257 | - tickets table only. (This restriction is done with a SQLite | |
| 257 | + tickets table only. (This restriction is done with an SQLite | |
| 258 | 258 | authorization hook, not by any method so weak as SQL text |
| 259 | 259 | filtering.) Mnemonic: new <b>t</b>icket report. |
| 260 | 260 | </td> |
| 261 | 261 | </tr> |
| 262 | 262 | |
| 263 | 263 |
| --- www/caps/ref.html | |
| +++ www/caps/ref.html | |
| @@ -252,11 +252,11 @@ | |
| 252 | Create new ticket report formats. Note that although this allows |
| 253 | the user to provide SQL code to be run in the server’s context, |
| 254 | and this capability is given to the untrusted “anonymous” user |
| 255 | category by default, this is a safe capability to give to users |
| 256 | because it is internally restricted to read-only queries on the |
| 257 | tickets table only. (This restriction is done with a SQLite |
| 258 | authorization hook, not by any method so weak as SQL text |
| 259 | filtering.) Mnemonic: new <b>t</b>icket report. |
| 260 | </td> |
| 261 | </tr> |
| 262 | |
| 263 |
| --- www/caps/ref.html | |
| +++ www/caps/ref.html | |
| @@ -252,11 +252,11 @@ | |
| 252 | Create new ticket report formats. Note that although this allows |
| 253 | the user to provide SQL code to be run in the server’s context, |
| 254 | and this capability is given to the untrusted “anonymous” user |
| 255 | category by default, this is a safe capability to give to users |
| 256 | because it is internally restricted to read-only queries on the |
| 257 | tickets table only. (This restriction is done with an SQLite |
| 258 | authorization hook, not by any method so weak as SQL text |
| 259 | filtering.) Mnemonic: new <b>t</b>icket report. |
| 260 | </td> |
| 261 | </tr> |
| 262 | |
| 263 |
+2
-2
| --- www/cgi.wiki | ||
| +++ www/cgi.wiki | ||
| @@ -13,11 +13,11 @@ | ||
| 13 | 13 | setting up a Fossil server, one of which is [./server/any/cgi.md | as a CGI |
| 14 | 14 | script]. CGI is the technique that the three |
| 15 | 15 | [./selfhost.wiki|self-hosting Fossil repositories] all use. |
| 16 | 16 | |
| 17 | 17 | Setting up a Fossil server using CGI is mostly about writing a short |
| 18 | -script (usually just 2 lines line) in the cgi-bin folder of an ordinary | |
| 18 | +script (usually just 2 lines) in the cgi-bin folder of an ordinary | |
| 19 | 19 | web-server. But there are a lot of extra options that can be added |
| 20 | 20 | to this script, to customize the configuration. This article describes |
| 21 | 21 | those options. |
| 22 | 22 | |
| 23 | 23 | <h1>CGI Script Options</h1> |
| @@ -191,11 +191,11 @@ | ||
| 191 | 191 | |
| 192 | 192 | |
| 193 | 193 | <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> |
| 194 | 194 | |
| 195 | 195 | This parameter causes the contents of the given file to override the |
| 196 | -site's <tt>mainmenu</tt> configuration setting, much in the same way | |
| 196 | +site's <tt>mainmenu</tt> configuration setting, in much the same way | |
| 197 | 197 | that the <tt>skin</tt> setting overrides the skin. This can be used to |
| 198 | 198 | apply a common main menu to a number of sites, and centrally maintain |
| 199 | 199 | it, without having to copy its contents into each site. Note, however, |
| 200 | 200 | that the contents of this setting are not stored in the repository and |
| 201 | 201 | will not be cloned along with the repository. |
| 202 | 202 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -13,11 +13,11 @@ | |
| 13 | setting up a Fossil server, one of which is [./server/any/cgi.md | as a CGI |
| 14 | script]. CGI is the technique that the three |
| 15 | [./selfhost.wiki|self-hosting Fossil repositories] all use. |
| 16 | |
| 17 | Setting up a Fossil server using CGI is mostly about writing a short |
| 18 | script (usually just 2 lines line) in the cgi-bin folder of an ordinary |
| 19 | web-server. But there are a lot of extra options that can be added |
| 20 | to this script, to customize the configuration. This article describes |
| 21 | those options. |
| 22 | |
| 23 | <h1>CGI Script Options</h1> |
| @@ -191,11 +191,11 @@ | |
| 191 | |
| 192 | |
| 193 | <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> |
| 194 | |
| 195 | This parameter causes the contents of the given file to override the |
| 196 | site's <tt>mainmenu</tt> configuration setting, much in the same way |
| 197 | that the <tt>skin</tt> setting overrides the skin. This can be used to |
| 198 | apply a common main menu to a number of sites, and centrally maintain |
| 199 | it, without having to copy its contents into each site. Note, however, |
| 200 | that the contents of this setting are not stored in the repository and |
| 201 | will not be cloned along with the repository. |
| 202 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -13,11 +13,11 @@ | |
| 13 | setting up a Fossil server, one of which is [./server/any/cgi.md | as a CGI |
| 14 | script]. CGI is the technique that the three |
| 15 | [./selfhost.wiki|self-hosting Fossil repositories] all use. |
| 16 | |
| 17 | Setting up a Fossil server using CGI is mostly about writing a short |
| 18 | script (usually just 2 lines) in the cgi-bin folder of an ordinary |
| 19 | web-server. But there are a lot of extra options that can be added |
| 20 | to this script, to customize the configuration. This article describes |
| 21 | those options. |
| 22 | |
| 23 | <h1>CGI Script Options</h1> |
| @@ -191,11 +191,11 @@ | |
| 191 | |
| 192 | |
| 193 | <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> |
| 194 | |
| 195 | This parameter causes the contents of the given file to override the |
| 196 | site's <tt>mainmenu</tt> configuration setting, in much the same way |
| 197 | that the <tt>skin</tt> setting overrides the skin. This can be used to |
| 198 | apply a common main menu to a number of sites, and centrally maintain |
| 199 | it, without having to copy its contents into each site. Note, however, |
| 200 | that the contents of this setting are not stored in the repository and |
| 201 | will not be cloned along with the repository. |
| 202 |
+26
-4
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,8 +1,22 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | -<h2 id='v2_25'>Changes for version 2.25 (pending)</h2> | |
| 3 | +<h2 id='v2_26'>Changes for version 2.26 (pending)</h2> | |
| 4 | + | |
| 5 | + * Added the [/help?cmd=/ckout|/ckout web page] to provide information | |
| 6 | + about pending changes in a working check-out | |
| 7 | + * The [/help?cmd=ui|fossil ui] command defaults to using the /ckout | |
| 8 | + page as its start page. | |
| 9 | + * Added the [/help?cmd=merge-info|fossil merge-info] command and especially | |
| 10 | + the --tk option to that command, to provide analysis of the most recent | |
| 11 | + merge or update operation. | |
| 12 | + * Issue a warning if a user tries to commit on a check-in where the | |
| 13 | + branch has been changed. | |
| 14 | + * When a merge conflict occurs, a new section is added to the conflict | |
| 15 | + text that shows Fossil's suggested resolution to the conflict. | |
| 16 | + | |
| 17 | +<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> | |
| 4 | 18 | |
| 5 | 19 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 6 | 20 | that have non-ASCII filenames |
| 7 | 21 | * Add the [/help?cmd=tree|fossil tree] command. |
| 8 | 22 | * On case-insensitive filesystems, store files using the filesystem's |
| @@ -11,12 +25,21 @@ | ||
| 11 | 25 | which is more familiar to Git users. Retain the legacy name for |
| 12 | 26 | compatibility. |
| 13 | 27 | * Add new query parameters to the [/help?cmd=/timeline|/timeline page]: |
| 14 | 28 | d2=, p2=, and dp2=. |
| 15 | 29 | * Add options to the [/help?cmd=tag|fossil tag] command that will list tag values. |
| 30 | + * Add the -b|--brief option to the [/help?cmd=status|fossil status] command. | |
| 16 | 31 | * Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page]. |
| 17 | 32 | * Add history search to the [/help?cmd=/chat|/chat page]. |
| 33 | + * Add Unix socket support to the [/help?cmd=server|server command]. | |
| 34 | + * On Windows, use the root certificates managed by the operating system | |
| 35 | + (requires OpenSSL 3.2.0 or greater). | |
| 36 | + * Take into account zero-width and double-width unicode characters when | |
| 37 | + formatting the command-line timeline. | |
| 38 | + * Update the built-in SQLite to version 3.47.0. Precompiled binaries are | |
| 39 | + linked against OpenSSL 3.4.0. | |
| 40 | + * Numerous minor fixes and additions. | |
| 18 | 41 | |
| 19 | 42 | |
| 20 | 43 | <h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2> |
| 21 | 44 | |
| 22 | 45 | * Apache change work-around → As part of a security fix, the Apache webserver |
| @@ -85,11 +108,11 @@ | ||
| 85 | 108 | * Add ability to "close" forum threads, such that unprivileged users |
| 86 | 109 | may no longer respond to them. Only administrators can close |
| 87 | 110 | threads or respond to them by default, and the |
| 88 | 111 | [/help?cmd=forum-close-policy|forum-close-policy setting] can be |
| 89 | 112 | used to add that capability to moderators. |
| 90 | - * Add the [/help?cmd=all|fossil all whatis] command. | |
| 113 | + * Add the [/help?cmd=all|fossil all whatis] command. | |
| 91 | 114 | * The [/help?cmd=status|fossil status] command and relevant UI pages now |
| 92 | 115 | correctly report files which were both renamed <b>and</b> edited as such. |
| 93 | 116 | * Show default value of settings that have a default in |
| 94 | 117 | [/help?cmd=help|fossil help SETTING] output. |
| 95 | 118 | * On timeline graphs, show closed check-ins using an X in the middle of the |
| @@ -127,11 +150,10 @@ | ||
| 127 | 150 | they work when the "remote" machine is a Mac and the "fossil" |
| 128 | 151 | executable is in the $HOME/bin directory. |
| 129 | 152 | </ul> |
| 130 | 153 | * Update built-in libraries SQLite, ZLib, Pikchr to their latest versions. |
| 131 | 154 | * Documentation enhancements and typo fixes. |
| 132 | - | |
| 133 | 155 | |
| 134 | 156 | |
| 135 | 157 | <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2> |
| 136 | 158 | * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a"> |
| 137 | 159 | <li> Add the ft=TAG query parameter which in combination with d=Y |
| @@ -155,11 +177,11 @@ | ||
| 155 | 177 | frees us from needing to build and install the container as root, |
| 156 | 178 | since it no longer has to create a private <tt>/dev</tt> tree |
| 157 | 179 | inside the jail for Fossil's use. |
| 158 | 180 | * Add support for the trigram tokenizer for FTS5 search to enable |
| 159 | 181 | searching in Chinese. |
| 160 | - * Comment lines (starting with a '#') are now supported inside | |
| 182 | + * Comment lines (starting with a '#') are now supported inside | |
| 161 | 183 | [./settings.wiki#versionable|versioned settings]. |
| 162 | 184 | * Default permissions for anonymous users in new repositories are |
| 163 | 185 | changed to "hz". |
| 164 | 186 | * The [/help?cmd=status|fossil status] command now detects when a |
| 165 | 187 | file used to be a symlink and has been replaced by a regular file. |
| 166 | 188 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,8 +1,22 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_25'>Changes for version 2.25 (pending)</h2> |
| 4 | |
| 5 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 6 | that have non-ASCII filenames |
| 7 | * Add the [/help?cmd=tree|fossil tree] command. |
| 8 | * On case-insensitive filesystems, store files using the filesystem's |
| @@ -11,12 +25,21 @@ | |
| 11 | which is more familiar to Git users. Retain the legacy name for |
| 12 | compatibility. |
| 13 | * Add new query parameters to the [/help?cmd=/timeline|/timeline page]: |
| 14 | d2=, p2=, and dp2=. |
| 15 | * Add options to the [/help?cmd=tag|fossil tag] command that will list tag values. |
| 16 | * Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page]. |
| 17 | * Add history search to the [/help?cmd=/chat|/chat page]. |
| 18 | |
| 19 | |
| 20 | <h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2> |
| 21 | |
| 22 | * Apache change work-around → As part of a security fix, the Apache webserver |
| @@ -85,11 +108,11 @@ | |
| 85 | * Add ability to "close" forum threads, such that unprivileged users |
| 86 | may no longer respond to them. Only administrators can close |
| 87 | threads or respond to them by default, and the |
| 88 | [/help?cmd=forum-close-policy|forum-close-policy setting] can be |
| 89 | used to add that capability to moderators. |
| 90 | * Add the [/help?cmd=all|fossil all whatis] command. |
| 91 | * The [/help?cmd=status|fossil status] command and relevant UI pages now |
| 92 | correctly report files which were both renamed <b>and</b> edited as such. |
| 93 | * Show default value of settings that have a default in |
| 94 | [/help?cmd=help|fossil help SETTING] output. |
| 95 | * On timeline graphs, show closed check-ins using an X in the middle of the |
| @@ -127,11 +150,10 @@ | |
| 127 | they work when the "remote" machine is a Mac and the "fossil" |
| 128 | executable is in the $HOME/bin directory. |
| 129 | </ul> |
| 130 | * Update built-in libraries SQLite, ZLib, Pikchr to their latest versions. |
| 131 | * Documentation enhancements and typo fixes. |
| 132 | |
| 133 | |
| 134 | |
| 135 | <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2> |
| 136 | * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a"> |
| 137 | <li> Add the ft=TAG query parameter which in combination with d=Y |
| @@ -155,11 +177,11 @@ | |
| 155 | frees us from needing to build and install the container as root, |
| 156 | since it no longer has to create a private <tt>/dev</tt> tree |
| 157 | inside the jail for Fossil's use. |
| 158 | * Add support for the trigram tokenizer for FTS5 search to enable |
| 159 | searching in Chinese. |
| 160 | * Comment lines (starting with a '#') are now supported inside |
| 161 | [./settings.wiki#versionable|versioned settings]. |
| 162 | * Default permissions for anonymous users in new repositories are |
| 163 | changed to "hz". |
| 164 | * The [/help?cmd=status|fossil status] command now detects when a |
| 165 | file used to be a symlink and has been replaced by a regular file. |
| 166 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,8 +1,22 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | |
| 5 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 6 | about pending changes in a working check-out |
| 7 | * The [/help?cmd=ui|fossil ui] command defaults to using the /ckout |
| 8 | page as its start page. |
| 9 | * Added the [/help?cmd=merge-info|fossil merge-info] command and especially |
| 10 | the --tk option to that command, to provide analysis of the most recent |
| 11 | merge or update operation. |
| 12 | * Issue a warning if a user tries to commit on a check-in where the |
| 13 | branch has been changed. |
| 14 | * When a merge conflict occurs, a new section is added to the conflict |
| 15 | text that shows Fossil's suggested resolution to the conflict. |
| 16 | |
| 17 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 18 | |
| 19 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 20 | that have non-ASCII filenames |
| 21 | * Add the [/help?cmd=tree|fossil tree] command. |
| 22 | * On case-insensitive filesystems, store files using the filesystem's |
| @@ -11,12 +25,21 @@ | |
| 25 | which is more familiar to Git users. Retain the legacy name for |
| 26 | compatibility. |
| 27 | * Add new query parameters to the [/help?cmd=/timeline|/timeline page]: |
| 28 | d2=, p2=, and dp2=. |
| 29 | * Add options to the [/help?cmd=tag|fossil tag] command that will list tag values. |
| 30 | * Add the -b|--brief option to the [/help?cmd=status|fossil status] command. |
| 31 | * Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page]. |
| 32 | * Add history search to the [/help?cmd=/chat|/chat page]. |
| 33 | * Add Unix socket support to the [/help?cmd=server|server command]. |
| 34 | * On Windows, use the root certificates managed by the operating system |
| 35 | (requires OpenSSL 3.2.0 or greater). |
| 36 | * Take into account zero-width and double-width unicode characters when |
| 37 | formatting the command-line timeline. |
| 38 | * Update the built-in SQLite to version 3.47.0. Precompiled binaries are |
| 39 | linked against OpenSSL 3.4.0. |
| 40 | * Numerous minor fixes and additions. |
| 41 | |
| 42 | |
| 43 | <h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2> |
| 44 | |
| 45 | * Apache change work-around → As part of a security fix, the Apache webserver |
| @@ -85,11 +108,11 @@ | |
| 108 | * Add ability to "close" forum threads, such that unprivileged users |
| 109 | may no longer respond to them. Only administrators can close |
| 110 | threads or respond to them by default, and the |
| 111 | [/help?cmd=forum-close-policy|forum-close-policy setting] can be |
| 112 | used to add that capability to moderators. |
| 113 | * Add the [/help?cmd=all|fossil all whatis] command. |
| 114 | * The [/help?cmd=status|fossil status] command and relevant UI pages now |
| 115 | correctly report files which were both renamed <b>and</b> edited as such. |
| 116 | * Show default value of settings that have a default in |
| 117 | [/help?cmd=help|fossil help SETTING] output. |
| 118 | * On timeline graphs, show closed check-ins using an X in the middle of the |
| @@ -127,11 +150,10 @@ | |
| 150 | they work when the "remote" machine is a Mac and the "fossil" |
| 151 | executable is in the $HOME/bin directory. |
| 152 | </ul> |
| 153 | * Update built-in libraries SQLite, ZLib, Pikchr to their latest versions. |
| 154 | * Documentation enhancements and typo fixes. |
| 155 | |
| 156 | |
| 157 | <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2> |
| 158 | * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a"> |
| 159 | <li> Add the ft=TAG query parameter which in combination with d=Y |
| @@ -155,11 +177,11 @@ | |
| 177 | frees us from needing to build and install the container as root, |
| 178 | since it no longer has to create a private <tt>/dev</tt> tree |
| 179 | inside the jail for Fossil's use. |
| 180 | * Add support for the trigram tokenizer for FTS5 search to enable |
| 181 | searching in Chinese. |
| 182 | * Comment lines (starting with a '#') are now supported inside |
| 183 | [./settings.wiki#versionable|versioned settings]. |
| 184 | * Default permissions for anonymous users in new repositories are |
| 185 | changed to "hz". |
| 186 | * The [/help?cmd=status|fossil status] command now detects when a |
| 187 | file used to be a symlink and has been replaced by a regular file. |
| 188 |
+2
-2
| --- www/chat.md | ||
| +++ www/chat.md | ||
| @@ -155,11 +155,11 @@ | ||
| 155 | 155 | Substitute the appropriate project URL, robot account |
| 156 | 156 | name and password, message text and file attachment, of course. |
| 157 | 157 | |
| 158 | 158 | ### <a id="chat-robot"></a> Chat Messages For Timeline Events |
| 159 | 159 | |
| 160 | -If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not a | |
| 160 | +If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not an | |
| 161 | 161 | empty string, then any change to the repository that would normally result |
| 162 | 162 | in a new timeline entry is announced in the chatroom. The announcement |
| 163 | 163 | appears to come from a user whose name is given by the chat-timeline-user |
| 164 | 164 | setting. |
| 165 | 165 | |
| @@ -193,11 +193,11 @@ | ||
| 193 | 193 | * [/chat-delete](/help?name=/chat-delete) → |
| 194 | 194 | Deletes a chat message. |
| 195 | 195 | |
| 196 | 196 | Fossil chat uses the venerable "hanging GET" or |
| 197 | 197 | "[long polling](wikipedia:/wiki/Push_technology#Long_polling)" |
| 198 | -technique to recieve asynchronous notification of new messages. | |
| 198 | +technique to receive asynchronous notification of new messages. | |
| 199 | 199 | This is done because long polling works well with CGI and SCGI, |
| 200 | 200 | which are the usual mechanisms for setting up a Fossil server. |
| 201 | 201 | More advanced notification techniques such as |
| 202 | 202 | [Server-sent events](wikipedia:/wiki/Server-sent_events) and especially |
| 203 | 203 | [WebSockets](wikipedia:/wiki/WebSocket) might seem more appropriate for |
| 204 | 204 |
| --- www/chat.md | |
| +++ www/chat.md | |
| @@ -155,11 +155,11 @@ | |
| 155 | Substitute the appropriate project URL, robot account |
| 156 | name and password, message text and file attachment, of course. |
| 157 | |
| 158 | ### <a id="chat-robot"></a> Chat Messages For Timeline Events |
| 159 | |
| 160 | If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not a |
| 161 | empty string, then any change to the repository that would normally result |
| 162 | in a new timeline entry is announced in the chatroom. The announcement |
| 163 | appears to come from a user whose name is given by the chat-timeline-user |
| 164 | setting. |
| 165 | |
| @@ -193,11 +193,11 @@ | |
| 193 | * [/chat-delete](/help?name=/chat-delete) → |
| 194 | Deletes a chat message. |
| 195 | |
| 196 | Fossil chat uses the venerable "hanging GET" or |
| 197 | "[long polling](wikipedia:/wiki/Push_technology#Long_polling)" |
| 198 | technique to recieve asynchronous notification of new messages. |
| 199 | This is done because long polling works well with CGI and SCGI, |
| 200 | which are the usual mechanisms for setting up a Fossil server. |
| 201 | More advanced notification techniques such as |
| 202 | [Server-sent events](wikipedia:/wiki/Server-sent_events) and especially |
| 203 | [WebSockets](wikipedia:/wiki/WebSocket) might seem more appropriate for |
| 204 |
| --- www/chat.md | |
| +++ www/chat.md | |
| @@ -155,11 +155,11 @@ | |
| 155 | Substitute the appropriate project URL, robot account |
| 156 | name and password, message text and file attachment, of course. |
| 157 | |
| 158 | ### <a id="chat-robot"></a> Chat Messages For Timeline Events |
| 159 | |
| 160 | If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not an |
| 161 | empty string, then any change to the repository that would normally result |
| 162 | in a new timeline entry is announced in the chatroom. The announcement |
| 163 | appears to come from a user whose name is given by the chat-timeline-user |
| 164 | setting. |
| 165 | |
| @@ -193,11 +193,11 @@ | |
| 193 | * [/chat-delete](/help?name=/chat-delete) → |
| 194 | Deletes a chat message. |
| 195 | |
| 196 | Fossil chat uses the venerable "hanging GET" or |
| 197 | "[long polling](wikipedia:/wiki/Push_technology#Long_polling)" |
| 198 | technique to receive asynchronous notification of new messages. |
| 199 | This is done because long polling works well with CGI and SCGI, |
| 200 | which are the usual mechanisms for setting up a Fossil server. |
| 201 | More advanced notification techniques such as |
| 202 | [Server-sent events](wikipedia:/wiki/Server-sent_events) and especially |
| 203 | [WebSockets](wikipedia:/wiki/WebSocket) might seem more appropriate for |
| 204 |
+1
-1
| --- www/checkin_names.wiki | ||
| +++ www/checkin_names.wiki | ||
| @@ -246,11 +246,11 @@ | ||
| 246 | 246 | |
| 247 | 247 | <pre> |
| 248 | 248 | fossil info merge-in:xyzzy:2022-03-01 |
| 249 | 249 | </pre> |
| 250 | 250 | |
| 251 | -to get informations about the most recent merge-in point on the branch | |
| 251 | +to get information about the most recent merge-in point on the branch | |
| 252 | 252 | "xyzzy" that happened on or before March 1, 2022. |
| 253 | 253 | |
| 254 | 254 | <h2 id="special">Special Tags</h2> |
| 255 | 255 | |
| 256 | 256 | The tag "tip" means the most recent check-in. The "tip" tag is practically |
| 257 | 257 |
| --- www/checkin_names.wiki | |
| +++ www/checkin_names.wiki | |
| @@ -246,11 +246,11 @@ | |
| 246 | |
| 247 | <pre> |
| 248 | fossil info merge-in:xyzzy:2022-03-01 |
| 249 | </pre> |
| 250 | |
| 251 | to get informations about the most recent merge-in point on the branch |
| 252 | "xyzzy" that happened on or before March 1, 2022. |
| 253 | |
| 254 | <h2 id="special">Special Tags</h2> |
| 255 | |
| 256 | The tag "tip" means the most recent check-in. The "tip" tag is practically |
| 257 |
| --- www/checkin_names.wiki | |
| +++ www/checkin_names.wiki | |
| @@ -246,11 +246,11 @@ | |
| 246 | |
| 247 | <pre> |
| 248 | fossil info merge-in:xyzzy:2022-03-01 |
| 249 | </pre> |
| 250 | |
| 251 | to get information about the most recent merge-in point on the branch |
| 252 | "xyzzy" that happened on or before March 1, 2022. |
| 253 | |
| 254 | <h2 id="special">Special Tags</h2> |
| 255 | |
| 256 | The tag "tip" means the most recent check-in. The "tip" tag is practically |
| 257 |
+4
-4
| --- www/concepts.wiki | ||
| +++ www/concepts.wiki | ||
| @@ -163,11 +163,11 @@ | ||
| 163 | 163 | The manifest file is not normally a real file on disk. Instead, |
| 164 | 164 | the manifest is computed in memory by Fossil whenever it needs it. |
| 165 | 165 | However, the "fossil setting manifest on" command will cause the |
| 166 | 166 | manifest file to be materialized to disk, if desired. Both Fossil |
| 167 | 167 | itself, and SQLite cause the manifest file to be materialized to disk |
| 168 | -so that the makefiles for these project can read the manifest and | |
| 168 | +so that the makefiles for these projects can read the manifest and | |
| 169 | 169 | embed version information in generated binaries. |
| 170 | 170 | |
| 171 | 171 | Fossil automatically generates a manifest whenever you "commit" |
| 172 | 172 | a new check-in. So this is not something that you, the developer, |
| 173 | 173 | need to worry with. The format of a manifest is intentionally |
| @@ -178,11 +178,11 @@ | ||
| 178 | 178 | |
| 179 | 179 | In addition to identifying all files in the check-in, a |
| 180 | 180 | manifest also contains a check-in comment, the date and time |
| 181 | 181 | when the check-in was established, who created the check-in, |
| 182 | 182 | and links to other check-ins from which the current check-in |
| 183 | -is derived. There is also a couple of checksums used to verify | |
| 183 | +is derived. There are also a couple of checksums used to verify | |
| 184 | 184 | the integrity of the check-in. And the whole manifest might |
| 185 | 185 | be PGP clearsigned. |
| 186 | 186 | |
| 187 | 187 | <h3 id="keyconc">2.3 Key concepts</h3> |
| 188 | 188 | |
| @@ -217,11 +217,11 @@ | ||
| 217 | 217 | SQLite, patch, or any similar software on your system in order to use |
| 218 | 218 | Fossil effectively. You will want to have some kind of text editor |
| 219 | 219 | for entering check-in comments. Fossil will use whatever text editor |
| 220 | 220 | is identified by your VISUAL environment variable. Fossil will also |
| 221 | 221 | use GPG to clearsign your manifests if you happen to have it installed, |
| 222 | -but Fossil will skip that step if GPG missing from your system. | |
| 222 | +but Fossil will skip that step if GPG is missing from your system. | |
| 223 | 223 | You can optionally set up Fossil to use external "diff" programs, |
| 224 | 224 | though Fossil has an excellent built-in "diff" algorithm that works |
| 225 | 225 | fine for most people. If you happen to have Tcl/Tk installed on your |
| 226 | 226 | system, Fossil will use it to generate a graphical "diff" display when |
| 227 | 227 | you use the --tk option to the "diff" command, but this too is entirely |
| @@ -286,11 +286,11 @@ | ||
| 286 | 286 | fossil setting autosync off |
| 287 | 287 | fossil settings |
| 288 | 288 | </pre> |
| 289 | 289 | |
| 290 | 290 | By default, Fossil runs with autosync mode turned on. The |
| 291 | -authors finds that projects run more smoothly in autosync mode since | |
| 291 | +authors find that projects run more smoothly in autosync mode since | |
| 292 | 292 | autosync helps to prevent pointless forking and merging and helps keeps |
| 293 | 293 | all collaborators working on exactly the same code rather than on their |
| 294 | 294 | own personal forks of the code. In the author's view, manual-merge mode |
| 295 | 295 | should be reserved for disconnected operation. |
| 296 | 296 | |
| 297 | 297 |
| --- www/concepts.wiki | |
| +++ www/concepts.wiki | |
| @@ -163,11 +163,11 @@ | |
| 163 | The manifest file is not normally a real file on disk. Instead, |
| 164 | the manifest is computed in memory by Fossil whenever it needs it. |
| 165 | However, the "fossil setting manifest on" command will cause the |
| 166 | manifest file to be materialized to disk, if desired. Both Fossil |
| 167 | itself, and SQLite cause the manifest file to be materialized to disk |
| 168 | so that the makefiles for these project can read the manifest and |
| 169 | embed version information in generated binaries. |
| 170 | |
| 171 | Fossil automatically generates a manifest whenever you "commit" |
| 172 | a new check-in. So this is not something that you, the developer, |
| 173 | need to worry with. The format of a manifest is intentionally |
| @@ -178,11 +178,11 @@ | |
| 178 | |
| 179 | In addition to identifying all files in the check-in, a |
| 180 | manifest also contains a check-in comment, the date and time |
| 181 | when the check-in was established, who created the check-in, |
| 182 | and links to other check-ins from which the current check-in |
| 183 | is derived. There is also a couple of checksums used to verify |
| 184 | the integrity of the check-in. And the whole manifest might |
| 185 | be PGP clearsigned. |
| 186 | |
| 187 | <h3 id="keyconc">2.3 Key concepts</h3> |
| 188 | |
| @@ -217,11 +217,11 @@ | |
| 217 | SQLite, patch, or any similar software on your system in order to use |
| 218 | Fossil effectively. You will want to have some kind of text editor |
| 219 | for entering check-in comments. Fossil will use whatever text editor |
| 220 | is identified by your VISUAL environment variable. Fossil will also |
| 221 | use GPG to clearsign your manifests if you happen to have it installed, |
| 222 | but Fossil will skip that step if GPG missing from your system. |
| 223 | You can optionally set up Fossil to use external "diff" programs, |
| 224 | though Fossil has an excellent built-in "diff" algorithm that works |
| 225 | fine for most people. If you happen to have Tcl/Tk installed on your |
| 226 | system, Fossil will use it to generate a graphical "diff" display when |
| 227 | you use the --tk option to the "diff" command, but this too is entirely |
| @@ -286,11 +286,11 @@ | |
| 286 | fossil setting autosync off |
| 287 | fossil settings |
| 288 | </pre> |
| 289 | |
| 290 | By default, Fossil runs with autosync mode turned on. The |
| 291 | authors finds that projects run more smoothly in autosync mode since |
| 292 | autosync helps to prevent pointless forking and merging and helps keeps |
| 293 | all collaborators working on exactly the same code rather than on their |
| 294 | own personal forks of the code. In the author's view, manual-merge mode |
| 295 | should be reserved for disconnected operation. |
| 296 | |
| 297 |
| --- www/concepts.wiki | |
| +++ www/concepts.wiki | |
| @@ -163,11 +163,11 @@ | |
| 163 | The manifest file is not normally a real file on disk. Instead, |
| 164 | the manifest is computed in memory by Fossil whenever it needs it. |
| 165 | However, the "fossil setting manifest on" command will cause the |
| 166 | manifest file to be materialized to disk, if desired. Both Fossil |
| 167 | itself, and SQLite cause the manifest file to be materialized to disk |
| 168 | so that the makefiles for these projects can read the manifest and |
| 169 | embed version information in generated binaries. |
| 170 | |
| 171 | Fossil automatically generates a manifest whenever you "commit" |
| 172 | a new check-in. So this is not something that you, the developer, |
| 173 | need to worry with. The format of a manifest is intentionally |
| @@ -178,11 +178,11 @@ | |
| 178 | |
| 179 | In addition to identifying all files in the check-in, a |
| 180 | manifest also contains a check-in comment, the date and time |
| 181 | when the check-in was established, who created the check-in, |
| 182 | and links to other check-ins from which the current check-in |
| 183 | is derived. There are also a couple of checksums used to verify |
| 184 | the integrity of the check-in. And the whole manifest might |
| 185 | be PGP clearsigned. |
| 186 | |
| 187 | <h3 id="keyconc">2.3 Key concepts</h3> |
| 188 | |
| @@ -217,11 +217,11 @@ | |
| 217 | SQLite, patch, or any similar software on your system in order to use |
| 218 | Fossil effectively. You will want to have some kind of text editor |
| 219 | for entering check-in comments. Fossil will use whatever text editor |
| 220 | is identified by your VISUAL environment variable. Fossil will also |
| 221 | use GPG to clearsign your manifests if you happen to have it installed, |
| 222 | but Fossil will skip that step if GPG is missing from your system. |
| 223 | You can optionally set up Fossil to use external "diff" programs, |
| 224 | though Fossil has an excellent built-in "diff" algorithm that works |
| 225 | fine for most people. If you happen to have Tcl/Tk installed on your |
| 226 | system, Fossil will use it to generate a graphical "diff" display when |
| 227 | you use the --tk option to the "diff" command, but this too is entirely |
| @@ -286,11 +286,11 @@ | |
| 286 | fossil setting autosync off |
| 287 | fossil settings |
| 288 | </pre> |
| 289 | |
| 290 | By default, Fossil runs with autosync mode turned on. The |
| 291 | authors find that projects run more smoothly in autosync mode since |
| 292 | autosync helps to prevent pointless forking and merging and helps keeps |
| 293 | all collaborators working on exactly the same code rather than on their |
| 294 | own personal forks of the code. In the author's view, manual-merge mode |
| 295 | should be reserved for disconnected operation. |
| 296 | |
| 297 |
+1
-1
| --- www/contribute.wiki | ||
| +++ www/contribute.wiki | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | <title>Contributing To Fossil</title> |
| 2 | 2 | |
| 3 | -Fossil users are encouraged to contributed enhancements back to the | |
| 3 | +Fossil users are encouraged to contribute enhancements back to the | |
| 4 | 4 | project. This note outlines some of the procedures for making |
| 5 | 5 | useful contributions. |
| 6 | 6 | |
| 7 | 7 | <h2>1.0 Contributor Agreement</h2> |
| 8 | 8 | |
| 9 | 9 |
| --- www/contribute.wiki | |
| +++ www/contribute.wiki | |
| @@ -1,8 +1,8 @@ | |
| 1 | <title>Contributing To Fossil</title> |
| 2 | |
| 3 | Fossil users are encouraged to contributed enhancements back to the |
| 4 | project. This note outlines some of the procedures for making |
| 5 | useful contributions. |
| 6 | |
| 7 | <h2>1.0 Contributor Agreement</h2> |
| 8 | |
| 9 |
| --- www/contribute.wiki | |
| +++ www/contribute.wiki | |
| @@ -1,8 +1,8 @@ | |
| 1 | <title>Contributing To Fossil</title> |
| 2 | |
| 3 | Fossil users are encouraged to contribute enhancements back to the |
| 4 | project. This note outlines some of the procedures for making |
| 5 | useful contributions. |
| 6 | |
| 7 | <h2>1.0 Contributor Agreement</h2> |
| 8 | |
| 9 |
+10
-8
| --- www/custom_ticket.wiki | ||
| +++ www/custom_ticket.wiki | ||
| @@ -82,16 +82,18 @@ | ||
| 82 | 82 | |
| 83 | 83 | Look for the text "Contact:" (about halfway through). Then insert these lines |
| 84 | 84 | after the closing tr tag and before the "enable_output" line: |
| 85 | 85 | |
| 86 | 86 | <verbatim> |
| 87 | -<td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> | |
| 88 | - $<assigned_to> | |
| 89 | -</td> | |
| 90 | -<td align="right">Opened by:</td><td bgcolor="#d0d0d0"> | |
| 91 | - $<opened_by> | |
| 92 | -</td> | |
| 87 | +<tr> | |
| 88 | + <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> | |
| 89 | + $<assigned_to> | |
| 90 | + </td> | |
| 91 | + <td align="right">Opened by:</td><td bgcolor="#d0d0d0"> | |
| 92 | + $<opened_by> | |
| 93 | + </td> | |
| 94 | +</tr> | |
| 93 | 95 | </verbatim> |
| 94 | 96 | |
| 95 | 97 | This will add a row which displays these two fields, in the event the user has |
| 96 | 98 | <a href="./caps/ref.html#w">ticket "edit" capability</a>. |
| 97 | 99 | |
| @@ -109,12 +111,12 @@ | ||
| 109 | 111 | </verbatim> |
| 110 | 112 | |
| 111 | 113 | That will give you a drop-down list of assignees. The first argument to the TH1 |
| 112 | 114 | command 'combobox' is the database field which the combobox is associated to. |
| 113 | 115 | The next argument is the list of choices you want to show in the combobox (and |
| 114 | -that you specified in the second step above. The last argument should be 1 for a | |
| 115 | -true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for | |
| 116 | +that you specified in the second step above.) The last argument should be 1 for | |
| 117 | +a true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for | |
| 116 | 118 | details). |
| 117 | 119 | |
| 118 | 120 | Now, similar to the previous |
| 119 | 121 | section, look for "Contact:" and add this: |
| 120 | 122 | |
| 121 | 123 |
| --- www/custom_ticket.wiki | |
| +++ www/custom_ticket.wiki | |
| @@ -82,16 +82,18 @@ | |
| 82 | |
| 83 | Look for the text "Contact:" (about halfway through). Then insert these lines |
| 84 | after the closing tr tag and before the "enable_output" line: |
| 85 | |
| 86 | <verbatim> |
| 87 | <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> |
| 88 | $<assigned_to> |
| 89 | </td> |
| 90 | <td align="right">Opened by:</td><td bgcolor="#d0d0d0"> |
| 91 | $<opened_by> |
| 92 | </td> |
| 93 | </verbatim> |
| 94 | |
| 95 | This will add a row which displays these two fields, in the event the user has |
| 96 | <a href="./caps/ref.html#w">ticket "edit" capability</a>. |
| 97 | |
| @@ -109,12 +111,12 @@ | |
| 109 | </verbatim> |
| 110 | |
| 111 | That will give you a drop-down list of assignees. The first argument to the TH1 |
| 112 | command 'combobox' is the database field which the combobox is associated to. |
| 113 | The next argument is the list of choices you want to show in the combobox (and |
| 114 | that you specified in the second step above. The last argument should be 1 for a |
| 115 | true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for |
| 116 | details). |
| 117 | |
| 118 | Now, similar to the previous |
| 119 | section, look for "Contact:" and add this: |
| 120 | |
| 121 |
| --- www/custom_ticket.wiki | |
| +++ www/custom_ticket.wiki | |
| @@ -82,16 +82,18 @@ | |
| 82 | |
| 83 | Look for the text "Contact:" (about halfway through). Then insert these lines |
| 84 | after the closing tr tag and before the "enable_output" line: |
| 85 | |
| 86 | <verbatim> |
| 87 | <tr> |
| 88 | <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> |
| 89 | $<assigned_to> |
| 90 | </td> |
| 91 | <td align="right">Opened by:</td><td bgcolor="#d0d0d0"> |
| 92 | $<opened_by> |
| 93 | </td> |
| 94 | </tr> |
| 95 | </verbatim> |
| 96 | |
| 97 | This will add a row which displays these two fields, in the event the user has |
| 98 | <a href="./caps/ref.html#w">ticket "edit" capability</a>. |
| 99 | |
| @@ -109,12 +111,12 @@ | |
| 111 | </verbatim> |
| 112 | |
| 113 | That will give you a drop-down list of assignees. The first argument to the TH1 |
| 114 | command 'combobox' is the database field which the combobox is associated to. |
| 115 | The next argument is the list of choices you want to show in the combobox (and |
| 116 | that you specified in the second step above.) The last argument should be 1 for |
| 117 | a true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for |
| 118 | details). |
| 119 | |
| 120 | Now, similar to the previous |
| 121 | section, look for "Contact:" and add this: |
| 122 | |
| 123 |
+13
-5
| --- www/embeddeddoc.wiki | ||
| +++ www/embeddeddoc.wiki | ||
| @@ -178,17 +178,25 @@ | ||
| 178 | 178 | As with "$ROOT", this substitution only works for Markdown and HTML |
| 179 | 179 | documents. For Wiki documents, you would need to use a relative URL. |
| 180 | 180 | |
| 181 | 181 | <h2 id="th1">2.3 TH1 Documents</h2> |
| 182 | 182 | |
| 183 | -Fossil will substitute the value of [./th1.md | TH1 expressions] within | |
| 184 | -<tt>{</tt> curly braces <tt>}</tt> into the output HTML if you have | |
| 185 | -configured it with the <tt>--with-th1-docs</tt> option, which is | |
| 186 | -disabled by default. | |
| 183 | +Enabling TH1 document support requires the following: | |
| 184 | + | |
| 185 | + * Configure the build with the <code>--with-th1-docs</code> flag. | |
| 186 | + * Enable the <code>th1-docs</code> setting, which is only available | |
| 187 | + after building with <code>--with-th1-docs</code>. | |
| 188 | + * Affected files must have a <code>.th1</code> file extension. | |
| 189 | + * The code to run must be embedded in blocks of | |
| 190 | + <code><th1>...</th1></code>. | |
| 191 | + | |
| 192 | +Fossil will substitute the value of [./th1.md | TH1 expressions] | |
| 193 | +within the <code><th1>...</th1></code> blocks into | |
| 194 | +the output HTML. | |
| 187 | 195 | |
| 188 | 196 | Since TH1 is a full scripting language, this feature essential grants |
| 189 | -the ability to execute code on the server to anyone with check-in | |
| 197 | +the ability to execute code on the server to anyone with check-in | |
| 190 | 198 | privilege for the project. |
| 191 | 199 | This is a security risk that needs to be carefully managed. |
| 192 | 200 | The feature is off by default. |
| 193 | 201 | Administrators should understand and carefully assess the risks |
| 194 | 202 | before enabling the use of TH1 within embedded documentation. |
| 195 | 203 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -178,17 +178,25 @@ | |
| 178 | As with "$ROOT", this substitution only works for Markdown and HTML |
| 179 | documents. For Wiki documents, you would need to use a relative URL. |
| 180 | |
| 181 | <h2 id="th1">2.3 TH1 Documents</h2> |
| 182 | |
| 183 | Fossil will substitute the value of [./th1.md | TH1 expressions] within |
| 184 | <tt>{</tt> curly braces <tt>}</tt> into the output HTML if you have |
| 185 | configured it with the <tt>--with-th1-docs</tt> option, which is |
| 186 | disabled by default. |
| 187 | |
| 188 | Since TH1 is a full scripting language, this feature essential grants |
| 189 | the ability to execute code on the server to anyone with check-in |
| 190 | privilege for the project. |
| 191 | This is a security risk that needs to be carefully managed. |
| 192 | The feature is off by default. |
| 193 | Administrators should understand and carefully assess the risks |
| 194 | before enabling the use of TH1 within embedded documentation. |
| 195 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -178,17 +178,25 @@ | |
| 178 | As with "$ROOT", this substitution only works for Markdown and HTML |
| 179 | documents. For Wiki documents, you would need to use a relative URL. |
| 180 | |
| 181 | <h2 id="th1">2.3 TH1 Documents</h2> |
| 182 | |
| 183 | Enabling TH1 document support requires the following: |
| 184 | |
| 185 | * Configure the build with the <code>--with-th1-docs</code> flag. |
| 186 | * Enable the <code>th1-docs</code> setting, which is only available |
| 187 | after building with <code>--with-th1-docs</code>. |
| 188 | * Affected files must have a <code>.th1</code> file extension. |
| 189 | * The code to run must be embedded in blocks of |
| 190 | <code><th1>...</th1></code>. |
| 191 | |
| 192 | Fossil will substitute the value of [./th1.md | TH1 expressions] |
| 193 | within the <code><th1>...</th1></code> blocks into |
| 194 | the output HTML. |
| 195 | |
| 196 | Since TH1 is a full scripting language, this feature essential grants |
| 197 | the ability to execute code on the server to anyone with check-in |
| 198 | privilege for the project. |
| 199 | This is a security risk that needs to be carefully managed. |
| 200 | The feature is off by default. |
| 201 | Administrators should understand and carefully assess the risks |
| 202 | before enabling the use of TH1 within embedded documentation. |
| 203 |
+3
-3
| --- www/env-opts.md | ||
| +++ www/env-opts.md | ||
| @@ -468,21 +468,21 @@ | ||
| 468 | 468 | obviously as part of the `fossil ui` command. In that specific case, |
| 469 | 469 | the browser is launched pointing at the web server started by `fossil |
| 470 | 470 | ui` listening on a private TCP port. |
| 471 | 471 | |
| 472 | 472 | On all platforms, if the local or global settings `web-browser` is |
| 473 | -set, that is the command used to open an URL. | |
| 473 | +set, that is the command used to open a URL. | |
| 474 | 474 | |
| 475 | 475 | Otherwise, the specific actions vary by platform. |
| 476 | 476 | |
| 477 | 477 | On Unix-like platforms other than Apple's, it looks for the first |
| 478 | 478 | program from the list `xdg-open`, `gnome-open`, `firefox`, and |
| 479 | 479 | `google-chrome` that it can find on the `PATH`. |
| 480 | 480 | |
| 481 | -On Apple platforms, it assumes that `open` is the command to open an | |
| 481 | +On Apple platforms, it assumes that `open` is the command to open a | |
| 482 | 482 | URL in the user's configured default browser. |
| 483 | 483 | |
| 484 | 484 | On Windows platforms, it assumes that `start` is the command to open |
| 485 | -an URL in the user's configured default browser. | |
| 485 | +a URL in the user's configured default browser. | |
| 486 | 486 | |
| 487 | 487 | [configdb]: ./tech_overview.wiki#configdb |
| 488 | 488 | [configloc]: ./tech_overview.wiki#configloc |
| 489 | 489 |
| --- www/env-opts.md | |
| +++ www/env-opts.md | |
| @@ -468,21 +468,21 @@ | |
| 468 | obviously as part of the `fossil ui` command. In that specific case, |
| 469 | the browser is launched pointing at the web server started by `fossil |
| 470 | ui` listening on a private TCP port. |
| 471 | |
| 472 | On all platforms, if the local or global settings `web-browser` is |
| 473 | set, that is the command used to open an URL. |
| 474 | |
| 475 | Otherwise, the specific actions vary by platform. |
| 476 | |
| 477 | On Unix-like platforms other than Apple's, it looks for the first |
| 478 | program from the list `xdg-open`, `gnome-open`, `firefox`, and |
| 479 | `google-chrome` that it can find on the `PATH`. |
| 480 | |
| 481 | On Apple platforms, it assumes that `open` is the command to open an |
| 482 | URL in the user's configured default browser. |
| 483 | |
| 484 | On Windows platforms, it assumes that `start` is the command to open |
| 485 | an URL in the user's configured default browser. |
| 486 | |
| 487 | [configdb]: ./tech_overview.wiki#configdb |
| 488 | [configloc]: ./tech_overview.wiki#configloc |
| 489 |
| --- www/env-opts.md | |
| +++ www/env-opts.md | |
| @@ -468,21 +468,21 @@ | |
| 468 | obviously as part of the `fossil ui` command. In that specific case, |
| 469 | the browser is launched pointing at the web server started by `fossil |
| 470 | ui` listening on a private TCP port. |
| 471 | |
| 472 | On all platforms, if the local or global settings `web-browser` is |
| 473 | set, that is the command used to open a URL. |
| 474 | |
| 475 | Otherwise, the specific actions vary by platform. |
| 476 | |
| 477 | On Unix-like platforms other than Apple's, it looks for the first |
| 478 | program from the list `xdg-open`, `gnome-open`, `firefox`, and |
| 479 | `google-chrome` that it can find on the `PATH`. |
| 480 | |
| 481 | On Apple platforms, it assumes that `open` is the command to open a |
| 482 | URL in the user's configured default browser. |
| 483 | |
| 484 | On Windows platforms, it assumes that `start` is the command to open |
| 485 | a URL in the user's configured default browser. |
| 486 | |
| 487 | [configdb]: ./tech_overview.wiki#configdb |
| 488 | [configloc]: ./tech_overview.wiki#configloc |
| 489 |
+1
-1
| --- www/event.wiki | ||
| +++ www/event.wiki | ||
| @@ -115,6 +115,6 @@ | ||
| 115 | 115 | create or edit technotes. In addition, users must have create-wiki |
| 116 | 116 | privilege (permission "f") to create new technotes and edit-wiki |
| 117 | 117 | privilege (permission "k") in order to edit existing technotes. |
| 118 | 118 | |
| 119 | 119 | Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| 120 | -[/md_rules | Markdown], or a plain text. | |
| 120 | +[/md_rules | Markdown], or plain text. | |
| 121 | 121 |
| --- www/event.wiki | |
| +++ www/event.wiki | |
| @@ -115,6 +115,6 @@ | |
| 115 | create or edit technotes. In addition, users must have create-wiki |
| 116 | privilege (permission "f") to create new technotes and edit-wiki |
| 117 | privilege (permission "k") in order to edit existing technotes. |
| 118 | |
| 119 | Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| 120 | [/md_rules | Markdown], or a plain text. |
| 121 |
| --- www/event.wiki | |
| +++ www/event.wiki | |
| @@ -115,6 +115,6 @@ | |
| 115 | create or edit technotes. In addition, users must have create-wiki |
| 116 | privilege (permission "f") to create new technotes and edit-wiki |
| 117 | privilege (permission "k") in order to edit existing technotes. |
| 118 | |
| 119 | Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| 120 | [/md_rules | Markdown], or plain text. |
| 121 |
+3
-3
| --- www/fileedit-page.md | ||
| +++ www/fileedit-page.md | ||
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | # The fileedit Page |
| 2 | 2 | |
| 3 | 3 | This document describes the limitations of, caveats for, and |
| 4 | 4 | disclaimers for the [](/fileedit) page, which provides users with |
| 5 | -[checkin privileges](./caps/index.md) basic editing features for files | |
| 6 | -via the web interface. | |
| 5 | + basic editing features for files via the web interface when they | |
| 6 | + have [checkin privileges](./caps/index.md). | |
| 7 | 7 | |
| 8 | 8 | # Important Caveats and Disclaimers |
| 9 | 9 | |
| 10 | 10 | Predictably, the ability to edit files in a repository from a web |
| 11 | 11 | browser halfway around the world comes with several obligatory caveats |
| @@ -219,11 +219,11 @@ | ||
| 219 | 219 | - `element`: the DOM element in which the preview is rendered. |
| 220 | 220 | - `mimetype`: the mimetype of the being-previewed content, as determined |
| 221 | 221 | by Fossil (by its file extension). |
| 222 | 222 | |
| 223 | 223 | The event listener callback shown above doesn't use the `mimetype`, |
| 224 | -but makes used of the other two. It fishes all `code` blocks out of | |
| 224 | +but makes use of the other two. It fishes all `code` blocks out of | |
| 225 | 225 | the preview which explicitly have a CSS class named |
| 226 | 226 | `language-`something, and then asks highlightjs to highlight them. |
| 227 | 227 | |
| 228 | 228 | ## <a id="editor"></a> Integrating a Custom Editor Widget |
| 229 | 229 | |
| 230 | 230 |
| --- www/fileedit-page.md | |
| +++ www/fileedit-page.md | |
| @@ -1,11 +1,11 @@ | |
| 1 | # The fileedit Page |
| 2 | |
| 3 | This document describes the limitations of, caveats for, and |
| 4 | disclaimers for the [](/fileedit) page, which provides users with |
| 5 | [checkin privileges](./caps/index.md) basic editing features for files |
| 6 | via the web interface. |
| 7 | |
| 8 | # Important Caveats and Disclaimers |
| 9 | |
| 10 | Predictably, the ability to edit files in a repository from a web |
| 11 | browser halfway around the world comes with several obligatory caveats |
| @@ -219,11 +219,11 @@ | |
| 219 | - `element`: the DOM element in which the preview is rendered. |
| 220 | - `mimetype`: the mimetype of the being-previewed content, as determined |
| 221 | by Fossil (by its file extension). |
| 222 | |
| 223 | The event listener callback shown above doesn't use the `mimetype`, |
| 224 | but makes used of the other two. It fishes all `code` blocks out of |
| 225 | the preview which explicitly have a CSS class named |
| 226 | `language-`something, and then asks highlightjs to highlight them. |
| 227 | |
| 228 | ## <a id="editor"></a> Integrating a Custom Editor Widget |
| 229 | |
| 230 |
| --- www/fileedit-page.md | |
| +++ www/fileedit-page.md | |
| @@ -1,11 +1,11 @@ | |
| 1 | # The fileedit Page |
| 2 | |
| 3 | This document describes the limitations of, caveats for, and |
| 4 | disclaimers for the [](/fileedit) page, which provides users with |
| 5 | basic editing features for files via the web interface when they |
| 6 | have [checkin privileges](./caps/index.md). |
| 7 | |
| 8 | # Important Caveats and Disclaimers |
| 9 | |
| 10 | Predictably, the ability to edit files in a repository from a web |
| 11 | browser halfway around the world comes with several obligatory caveats |
| @@ -219,11 +219,11 @@ | |
| 219 | - `element`: the DOM element in which the preview is rendered. |
| 220 | - `mimetype`: the mimetype of the being-previewed content, as determined |
| 221 | by Fossil (by its file extension). |
| 222 | |
| 223 | The event listener callback shown above doesn't use the `mimetype`, |
| 224 | but makes use of the other two. It fishes all `code` blocks out of |
| 225 | the preview which explicitly have a CSS class named |
| 226 | `language-`something, and then asks highlightjs to highlight them. |
| 227 | |
| 228 | ## <a id="editor"></a> Integrating a Custom Editor Widget |
| 229 | |
| 230 |
+6
-6
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -360,11 +360,11 @@ | ||
| 360 | 360 | of text in the wiki page. That text follows the newline character |
| 361 | 361 | that terminates the <b>W</b> card. The wiki text is always followed by one |
| 362 | 362 | extra newline. |
| 363 | 363 | |
| 364 | 364 | The <b>C</b> card on a wiki page is optional. The argument is a comment |
| 365 | -that explains why the changes was made. The ability to have a <b>C</b> | |
| 365 | +that explains why the changes were made. The ability to have a <b>C</b> | |
| 366 | 366 | card on a wiki page artifact was added on 2019-12-02 at the suggestion |
| 367 | 367 | of user George Krivov and is not currently used or generated by the |
| 368 | 368 | implementation. Older versions of Fossil will reject a wiki-page |
| 369 | 369 | artifact that includes a <b>C</b> card. |
| 370 | 370 | |
| @@ -396,14 +396,14 @@ | ||
| 396 | 396 | ticket into existence. |
| 397 | 397 | |
| 398 | 398 | <b>J</b> cards specify changes to the "value" of "fields" in the ticket. |
| 399 | 399 | If the <i>value</i> parameter of the <b>J</b> card is omitted, then the |
| 400 | 400 | field is set to an empty string. |
| 401 | -Each fossil server has a ticket configuration which specifies the fields its | |
| 401 | +Each fossil server has a ticket configuration which specifies the fields it | |
| 402 | 402 | understands. The ticket configuration is part of the local state for |
| 403 | 403 | the repository and thus can vary from one repository to another. |
| 404 | -Hence a <b>J</b> card might specify a <i>field</i> that do not exist in the | |
| 404 | +Hence a <b>J</b> card might specify a <i>field</i> that does not exist in the | |
| 405 | 405 | local ticket configuration. If a <b>J</b> card specifies a <i>field</i> that |
| 406 | 406 | is not in the local configuration, then that <b>J</b> card |
| 407 | 407 | is simply ignored. |
| 408 | 408 | |
| 409 | 409 | The first argument of the <b>J</b> card is the field name. The second |
| @@ -462,11 +462,11 @@ | ||
| 462 | 462 | A technical note or "technote" artifact (formerly known as an "event" artifact) |
| 463 | 463 | associates a timeline comment and a page of text |
| 464 | 464 | (similar to a wiki page) with a point in time. Technotes can be used |
| 465 | 465 | to record project milestones, release notes, blog entries, process |
| 466 | 466 | checkpoints, or news articles. |
| 467 | -The following cards are allowed on an technote artifact: | |
| 467 | +The following cards are allowed on a technote artifact: | |
| 468 | 468 | |
| 469 | 469 | <div class="indent"> |
| 470 | 470 | <b>C</b> <i>comment</i><br> |
| 471 | 471 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 472 | 472 | <b>E</b> <i>technote-time</i> <i>technote-id</i><br /> |
| @@ -510,15 +510,15 @@ | ||
| 510 | 510 | The <b>*</b> in place of the artifact ID indicates that |
| 511 | 511 | the tag or property applies to the current artifact. It is not |
| 512 | 512 | possible to encode the current artifact ID as part of an artifact, |
| 513 | 513 | since the act of inserting the artifact ID would change the artifact ID, |
| 514 | 514 | hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the |
| 515 | -name means that tags can only be add and they can only be non-propagating | |
| 515 | +name means that tags can only be "add" and they can only be non-propagating | |
| 516 | 516 | tags. In a technote, <b>T</b> cards are normally used to set the background |
| 517 | 517 | display color for timelines. |
| 518 | 518 | |
| 519 | -The optional <b>U</b> card gives name of the user who entered the technote. | |
| 519 | +The optional <b>U</b> card gives the name of the user who entered the technote. | |
| 520 | 520 | |
| 521 | 521 | A single <b>W</b> card provides wiki text for the document associated with the |
| 522 | 522 | technote. The format of the <b>W</b> card is exactly the same as for a |
| 523 | 523 | [#wikichng | wiki artifact]. |
| 524 | 524 | |
| 525 | 525 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -360,11 +360,11 @@ | |
| 360 | of text in the wiki page. That text follows the newline character |
| 361 | that terminates the <b>W</b> card. The wiki text is always followed by one |
| 362 | extra newline. |
| 363 | |
| 364 | The <b>C</b> card on a wiki page is optional. The argument is a comment |
| 365 | that explains why the changes was made. The ability to have a <b>C</b> |
| 366 | card on a wiki page artifact was added on 2019-12-02 at the suggestion |
| 367 | of user George Krivov and is not currently used or generated by the |
| 368 | implementation. Older versions of Fossil will reject a wiki-page |
| 369 | artifact that includes a <b>C</b> card. |
| 370 | |
| @@ -396,14 +396,14 @@ | |
| 396 | ticket into existence. |
| 397 | |
| 398 | <b>J</b> cards specify changes to the "value" of "fields" in the ticket. |
| 399 | If the <i>value</i> parameter of the <b>J</b> card is omitted, then the |
| 400 | field is set to an empty string. |
| 401 | Each fossil server has a ticket configuration which specifies the fields its |
| 402 | understands. The ticket configuration is part of the local state for |
| 403 | the repository and thus can vary from one repository to another. |
| 404 | Hence a <b>J</b> card might specify a <i>field</i> that do not exist in the |
| 405 | local ticket configuration. If a <b>J</b> card specifies a <i>field</i> that |
| 406 | is not in the local configuration, then that <b>J</b> card |
| 407 | is simply ignored. |
| 408 | |
| 409 | The first argument of the <b>J</b> card is the field name. The second |
| @@ -462,11 +462,11 @@ | |
| 462 | A technical note or "technote" artifact (formerly known as an "event" artifact) |
| 463 | associates a timeline comment and a page of text |
| 464 | (similar to a wiki page) with a point in time. Technotes can be used |
| 465 | to record project milestones, release notes, blog entries, process |
| 466 | checkpoints, or news articles. |
| 467 | The following cards are allowed on an technote artifact: |
| 468 | |
| 469 | <div class="indent"> |
| 470 | <b>C</b> <i>comment</i><br> |
| 471 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 472 | <b>E</b> <i>technote-time</i> <i>technote-id</i><br /> |
| @@ -510,15 +510,15 @@ | |
| 510 | The <b>*</b> in place of the artifact ID indicates that |
| 511 | the tag or property applies to the current artifact. It is not |
| 512 | possible to encode the current artifact ID as part of an artifact, |
| 513 | since the act of inserting the artifact ID would change the artifact ID, |
| 514 | hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the |
| 515 | name means that tags can only be add and they can only be non-propagating |
| 516 | tags. In a technote, <b>T</b> cards are normally used to set the background |
| 517 | display color for timelines. |
| 518 | |
| 519 | The optional <b>U</b> card gives name of the user who entered the technote. |
| 520 | |
| 521 | A single <b>W</b> card provides wiki text for the document associated with the |
| 522 | technote. The format of the <b>W</b> card is exactly the same as for a |
| 523 | [#wikichng | wiki artifact]. |
| 524 | |
| 525 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -360,11 +360,11 @@ | |
| 360 | of text in the wiki page. That text follows the newline character |
| 361 | that terminates the <b>W</b> card. The wiki text is always followed by one |
| 362 | extra newline. |
| 363 | |
| 364 | The <b>C</b> card on a wiki page is optional. The argument is a comment |
| 365 | that explains why the changes were made. The ability to have a <b>C</b> |
| 366 | card on a wiki page artifact was added on 2019-12-02 at the suggestion |
| 367 | of user George Krivov and is not currently used or generated by the |
| 368 | implementation. Older versions of Fossil will reject a wiki-page |
| 369 | artifact that includes a <b>C</b> card. |
| 370 | |
| @@ -396,14 +396,14 @@ | |
| 396 | ticket into existence. |
| 397 | |
| 398 | <b>J</b> cards specify changes to the "value" of "fields" in the ticket. |
| 399 | If the <i>value</i> parameter of the <b>J</b> card is omitted, then the |
| 400 | field is set to an empty string. |
| 401 | Each fossil server has a ticket configuration which specifies the fields it |
| 402 | understands. The ticket configuration is part of the local state for |
| 403 | the repository and thus can vary from one repository to another. |
| 404 | Hence a <b>J</b> card might specify a <i>field</i> that does not exist in the |
| 405 | local ticket configuration. If a <b>J</b> card specifies a <i>field</i> that |
| 406 | is not in the local configuration, then that <b>J</b> card |
| 407 | is simply ignored. |
| 408 | |
| 409 | The first argument of the <b>J</b> card is the field name. The second |
| @@ -462,11 +462,11 @@ | |
| 462 | A technical note or "technote" artifact (formerly known as an "event" artifact) |
| 463 | associates a timeline comment and a page of text |
| 464 | (similar to a wiki page) with a point in time. Technotes can be used |
| 465 | to record project milestones, release notes, blog entries, process |
| 466 | checkpoints, or news articles. |
| 467 | The following cards are allowed on a technote artifact: |
| 468 | |
| 469 | <div class="indent"> |
| 470 | <b>C</b> <i>comment</i><br> |
| 471 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 472 | <b>E</b> <i>technote-time</i> <i>technote-id</i><br /> |
| @@ -510,15 +510,15 @@ | |
| 510 | The <b>*</b> in place of the artifact ID indicates that |
| 511 | the tag or property applies to the current artifact. It is not |
| 512 | possible to encode the current artifact ID as part of an artifact, |
| 513 | since the act of inserting the artifact ID would change the artifact ID, |
| 514 | hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the |
| 515 | name means that tags can only be "add" and they can only be non-propagating |
| 516 | tags. In a technote, <b>T</b> cards are normally used to set the background |
| 517 | display color for timelines. |
| 518 | |
| 519 | The optional <b>U</b> card gives the name of the user who entered the technote. |
| 520 | |
| 521 | A single <b>W</b> card provides wiki text for the document associated with the |
| 522 | technote. The format of the <b>W</b> card is exactly the same as for a |
| 523 | [#wikichng | wiki artifact]. |
| 524 | |
| 525 |
+1
-1
| --- www/forum.wiki | ||
| +++ www/forum.wiki | ||
| @@ -405,7 +405,7 @@ | ||
| 405 | 405 | |
| 406 | 406 | Though forum users are permitted to delete their own posts, they are |
| 407 | 407 | not permitted, without appropriate permissions, to close their own |
| 408 | 408 | posts. This is intentional, as closing one's own post can be used to |
| 409 | 409 | antagonize other forum users. For example, by posting something |
| 410 | -trollish or highly contraversial in nature and closing the post to | |
| 410 | +trollish or highly controversial in nature and closing the post to | |
| 411 | 411 | further responses. |
| 412 | 412 |
| --- www/forum.wiki | |
| +++ www/forum.wiki | |
| @@ -405,7 +405,7 @@ | |
| 405 | |
| 406 | Though forum users are permitted to delete their own posts, they are |
| 407 | not permitted, without appropriate permissions, to close their own |
| 408 | posts. This is intentional, as closing one's own post can be used to |
| 409 | antagonize other forum users. For example, by posting something |
| 410 | trollish or highly contraversial in nature and closing the post to |
| 411 | further responses. |
| 412 |
| --- www/forum.wiki | |
| +++ www/forum.wiki | |
| @@ -405,7 +405,7 @@ | |
| 405 | |
| 406 | Though forum users are permitted to delete their own posts, they are |
| 407 | not permitted, without appropriate permissions, to close their own |
| 408 | posts. This is intentional, as closing one's own post can be used to |
| 409 | antagonize other forum users. For example, by posting something |
| 410 | trollish or highly controversial in nature and closing the post to |
| 411 | further responses. |
| 412 |
+1
-1
| --- www/fossil-is-not-relational.md | ||
| +++ www/fossil-is-not-relational.md | ||
| @@ -131,11 +131,11 @@ | ||
| 131 | 131 | metadata. |
| 132 | 132 | |
| 133 | 133 | - Raw file content of versioned files. These data are external to |
| 134 | 134 | artifacts, which refer to them by their hashes. How they are stored |
| 135 | 135 | is not the concern of the data model, but (spoiler alert!) Fossil |
| 136 | - stores in them an sqlite database, one record per distinct hash, in | |
| 136 | + stores them in an SQLite database, one record per distinct hash, in | |
| 137 | 137 | its `blob` table (which we will cover more very soon). |
| 138 | 138 | |
| 139 | 139 | Non-SCM-relevant state includes: |
| 140 | 140 | |
| 141 | 141 | - Fossil's list of users and their metadata (permissions, email |
| 142 | 142 |
| --- www/fossil-is-not-relational.md | |
| +++ www/fossil-is-not-relational.md | |
| @@ -131,11 +131,11 @@ | |
| 131 | metadata. |
| 132 | |
| 133 | - Raw file content of versioned files. These data are external to |
| 134 | artifacts, which refer to them by their hashes. How they are stored |
| 135 | is not the concern of the data model, but (spoiler alert!) Fossil |
| 136 | stores in them an sqlite database, one record per distinct hash, in |
| 137 | its `blob` table (which we will cover more very soon). |
| 138 | |
| 139 | Non-SCM-relevant state includes: |
| 140 | |
| 141 | - Fossil's list of users and their metadata (permissions, email |
| 142 |
| --- www/fossil-is-not-relational.md | |
| +++ www/fossil-is-not-relational.md | |
| @@ -131,11 +131,11 @@ | |
| 131 | metadata. |
| 132 | |
| 133 | - Raw file content of versioned files. These data are external to |
| 134 | artifacts, which refer to them by their hashes. How they are stored |
| 135 | is not the concern of the data model, but (spoiler alert!) Fossil |
| 136 | stores them in an SQLite database, one record per distinct hash, in |
| 137 | its `blob` table (which we will cover more very soon). |
| 138 | |
| 139 | Non-SCM-relevant state includes: |
| 140 | |
| 141 | - Fossil's list of users and their metadata (permissions, email |
| 142 |
+9
-10
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -70,18 +70,13 @@ | ||
| 70 | 70 | <td>Designed for Linux kernel development</td> |
| 71 | 71 | <td>Designed for SQLite development</td> |
| 72 | 72 | <td><a href="#scale">2.5.2 ↓</a></td> |
| 73 | 73 | </tr> |
| 74 | 74 | <tr> |
| 75 | - <td>Many contributors</td> | |
| 76 | - <td>Select contributors</td> | |
| 77 | - <td><a href="#contrib">2.5.3 ↓</a></td> | |
| 78 | -</tr> | |
| 79 | -<tr> | |
| 80 | 75 | <td>Focus on individual branches</td> |
| 81 | 76 | <td>Focus on the entire tree of changes</td> |
| 82 | - <td><a href="#branches">2.5.4 ↓</a></td> | |
| 77 | + <td><a href="#branches">2.5.3 ↓</a></td> | |
| 83 | 78 | </tr> |
| 84 | 79 | <tr> |
| 85 | 80 | <td>One check-out per repository</td> |
| 86 | 81 | <td>Many check-outs per repository</td> |
| 87 | 82 | <td><a href="#checkouts">2.6 ↓</a></td> |
| @@ -591,18 +586,21 @@ | ||
| 591 | 586 | and its handful of active committers. Seeing all |
| 592 | 587 | changes on all branches all at once helps keep the whole team |
| 593 | 588 | up-to-date with what everybody else is doing, resulting in a more |
| 594 | 589 | tightly focused and cohesive implementation. |
| 595 | 590 | |
| 591 | +Parts of this section are [https://fossil-scm.org/forum/forumpost/5961e969fa|disputed] | |
| 592 | +by [https://github.com/olorin37|Jakub A. G.]. | |
| 593 | + | |
| 596 | 594 | |
| 597 | 595 | <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> |
| 598 | 596 | |
| 599 | 597 | Because Git commingles the repository data with the initial checkout of |
| 600 | 598 | that repository, the default mode of operation in Git is to stick to that |
| 601 | 599 | single work/repo tree, even when that's a shortsighted way of working. |
| 602 | 600 | |
| 603 | -Fossil doesn't work that way. A Fossil repository is a SQLite database | |
| 601 | +Fossil doesn't work that way. A Fossil repository is an SQLite database | |
| 604 | 602 | file which is normally stored outside the working checkout directory. You can |
| 605 | 603 | [/help?cmd=open | open] a Fossil repository any number of times into |
| 606 | 604 | any number of working directories. A common usage pattern is to have one |
| 607 | 605 | working directory per active working branch, so that switching branches |
| 608 | 606 | is done with a <tt>cd</tt> command rather than by checking out the |
| @@ -660,10 +658,12 @@ | ||
| 660 | 658 | |
| 661 | 659 | Plus, |
| 662 | 660 | <tt>cd</tt> is faster to type than <tt>git checkout</tt> or <tt>fossil |
| 663 | 661 | update</tt>. |
| 664 | 662 | |
| 663 | +Parts of this section are [https://fossil-scm.org/forum/forumpost/5961e969fa|disputed] | |
| 664 | +by [https://github.com/olorin37|Jakub A. G.]. | |
| 665 | 665 | |
| 666 | 666 | <h3 id="history">2.7 What you should have done vs. What you actually did</h3> |
| 667 | 667 | |
| 668 | 668 | Git puts a lot of emphasis on maintaining |
| 669 | 669 | a "clean" check-in history. Extraneous and experimental branches by |
| @@ -861,13 +861,12 @@ | ||
| 861 | 861 | as this author is aware, but there is now |
| 862 | 862 | [https://lwn.net/ml/git/[email protected]/ |
| 863 | 863 | | a competing SHA-256 based plan] which requires complete repository |
| 864 | 864 | conversion from SHA-1 to SHA-256, breaking all public hashes in the |
| 865 | 865 | repo. One way to characterize such a massive upheaval in Git terms is a |
| 866 | -whole-project rebase, which violates | |
| 867 | -[https://blog.axosoft.com/golden-rule-of-rebasing-in-git/ | Git's own | |
| 868 | -Golden Rule of Rebasing]. | |
| 866 | +whole-project rebase, which violates the | |
| 867 | +[https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing|Golden Rule of Rebasing]. | |
| 869 | 868 | |
| 870 | 869 | Regardless of the eventual implementation details, we fully expect Git |
| 871 | 870 | to move off SHA-1 eventually and for the changes to take years more to |
| 872 | 871 | percolate through the community. |
| 873 | 872 | |
| 874 | 873 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -70,18 +70,13 @@ | |
| 70 | <td>Designed for Linux kernel development</td> |
| 71 | <td>Designed for SQLite development</td> |
| 72 | <td><a href="#scale">2.5.2 ↓</a></td> |
| 73 | </tr> |
| 74 | <tr> |
| 75 | <td>Many contributors</td> |
| 76 | <td>Select contributors</td> |
| 77 | <td><a href="#contrib">2.5.3 ↓</a></td> |
| 78 | </tr> |
| 79 | <tr> |
| 80 | <td>Focus on individual branches</td> |
| 81 | <td>Focus on the entire tree of changes</td> |
| 82 | <td><a href="#branches">2.5.4 ↓</a></td> |
| 83 | </tr> |
| 84 | <tr> |
| 85 | <td>One check-out per repository</td> |
| 86 | <td>Many check-outs per repository</td> |
| 87 | <td><a href="#checkouts">2.6 ↓</a></td> |
| @@ -591,18 +586,21 @@ | |
| 591 | and its handful of active committers. Seeing all |
| 592 | changes on all branches all at once helps keep the whole team |
| 593 | up-to-date with what everybody else is doing, resulting in a more |
| 594 | tightly focused and cohesive implementation. |
| 595 | |
| 596 | |
| 597 | <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> |
| 598 | |
| 599 | Because Git commingles the repository data with the initial checkout of |
| 600 | that repository, the default mode of operation in Git is to stick to that |
| 601 | single work/repo tree, even when that's a shortsighted way of working. |
| 602 | |
| 603 | Fossil doesn't work that way. A Fossil repository is a SQLite database |
| 604 | file which is normally stored outside the working checkout directory. You can |
| 605 | [/help?cmd=open | open] a Fossil repository any number of times into |
| 606 | any number of working directories. A common usage pattern is to have one |
| 607 | working directory per active working branch, so that switching branches |
| 608 | is done with a <tt>cd</tt> command rather than by checking out the |
| @@ -660,10 +658,12 @@ | |
| 660 | |
| 661 | Plus, |
| 662 | <tt>cd</tt> is faster to type than <tt>git checkout</tt> or <tt>fossil |
| 663 | update</tt>. |
| 664 | |
| 665 | |
| 666 | <h3 id="history">2.7 What you should have done vs. What you actually did</h3> |
| 667 | |
| 668 | Git puts a lot of emphasis on maintaining |
| 669 | a "clean" check-in history. Extraneous and experimental branches by |
| @@ -861,13 +861,12 @@ | |
| 861 | as this author is aware, but there is now |
| 862 | [https://lwn.net/ml/git/[email protected]/ |
| 863 | | a competing SHA-256 based plan] which requires complete repository |
| 864 | conversion from SHA-1 to SHA-256, breaking all public hashes in the |
| 865 | repo. One way to characterize such a massive upheaval in Git terms is a |
| 866 | whole-project rebase, which violates |
| 867 | [https://blog.axosoft.com/golden-rule-of-rebasing-in-git/ | Git's own |
| 868 | Golden Rule of Rebasing]. |
| 869 | |
| 870 | Regardless of the eventual implementation details, we fully expect Git |
| 871 | to move off SHA-1 eventually and for the changes to take years more to |
| 872 | percolate through the community. |
| 873 | |
| 874 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -70,18 +70,13 @@ | |
| 70 | <td>Designed for Linux kernel development</td> |
| 71 | <td>Designed for SQLite development</td> |
| 72 | <td><a href="#scale">2.5.2 ↓</a></td> |
| 73 | </tr> |
| 74 | <tr> |
| 75 | <td>Focus on individual branches</td> |
| 76 | <td>Focus on the entire tree of changes</td> |
| 77 | <td><a href="#branches">2.5.3 ↓</a></td> |
| 78 | </tr> |
| 79 | <tr> |
| 80 | <td>One check-out per repository</td> |
| 81 | <td>Many check-outs per repository</td> |
| 82 | <td><a href="#checkouts">2.6 ↓</a></td> |
| @@ -591,18 +586,21 @@ | |
| 586 | and its handful of active committers. Seeing all |
| 587 | changes on all branches all at once helps keep the whole team |
| 588 | up-to-date with what everybody else is doing, resulting in a more |
| 589 | tightly focused and cohesive implementation. |
| 590 | |
| 591 | Parts of this section are [https://fossil-scm.org/forum/forumpost/5961e969fa|disputed] |
| 592 | by [https://github.com/olorin37|Jakub A. G.]. |
| 593 | |
| 594 | |
| 595 | <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> |
| 596 | |
| 597 | Because Git commingles the repository data with the initial checkout of |
| 598 | that repository, the default mode of operation in Git is to stick to that |
| 599 | single work/repo tree, even when that's a shortsighted way of working. |
| 600 | |
| 601 | Fossil doesn't work that way. A Fossil repository is an SQLite database |
| 602 | file which is normally stored outside the working checkout directory. You can |
| 603 | [/help?cmd=open | open] a Fossil repository any number of times into |
| 604 | any number of working directories. A common usage pattern is to have one |
| 605 | working directory per active working branch, so that switching branches |
| 606 | is done with a <tt>cd</tt> command rather than by checking out the |
| @@ -660,10 +658,12 @@ | |
| 658 | |
| 659 | Plus, |
| 660 | <tt>cd</tt> is faster to type than <tt>git checkout</tt> or <tt>fossil |
| 661 | update</tt>. |
| 662 | |
| 663 | Parts of this section are [https://fossil-scm.org/forum/forumpost/5961e969fa|disputed] |
| 664 | by [https://github.com/olorin37|Jakub A. G.]. |
| 665 | |
| 666 | <h3 id="history">2.7 What you should have done vs. What you actually did</h3> |
| 667 | |
| 668 | Git puts a lot of emphasis on maintaining |
| 669 | a "clean" check-in history. Extraneous and experimental branches by |
| @@ -861,13 +861,12 @@ | |
| 861 | as this author is aware, but there is now |
| 862 | [https://lwn.net/ml/git/[email protected]/ |
| 863 | | a competing SHA-256 based plan] which requires complete repository |
| 864 | conversion from SHA-1 to SHA-256, breaking all public hashes in the |
| 865 | repo. One way to characterize such a massive upheaval in Git terms is a |
| 866 | whole-project rebase, which violates the |
| 867 | [https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing|Golden Rule of Rebasing]. |
| 868 | |
| 869 | Regardless of the eventual implementation details, we fully expect Git |
| 870 | to move off SHA-1 eventually and for the changes to take years more to |
| 871 | percolate through the community. |
| 872 | |
| 873 |
+1
-1
| --- www/fossil_prompt.wiki | ||
| +++ www/fossil_prompt.wiki | ||
| @@ -17,6 +17,6 @@ | ||
| 17 | 17 | |
| 18 | 18 | For a permanent installation, you can graft the code into your |
| 19 | 19 | <tt>.bashrc</tt> file in your home directory. |
| 20 | 20 | |
| 21 | 21 | The code is very simple (only 32 non-comment lines, as of this writing) |
| 22 | -and hence easy to customized. | |
| 22 | +and hence easy to customize. | |
| 23 | 23 |
| --- www/fossil_prompt.wiki | |
| +++ www/fossil_prompt.wiki | |
| @@ -17,6 +17,6 @@ | |
| 17 | |
| 18 | For a permanent installation, you can graft the code into your |
| 19 | <tt>.bashrc</tt> file in your home directory. |
| 20 | |
| 21 | The code is very simple (only 32 non-comment lines, as of this writing) |
| 22 | and hence easy to customized. |
| 23 |
| --- www/fossil_prompt.wiki | |
| +++ www/fossil_prompt.wiki | |
| @@ -17,6 +17,6 @@ | |
| 17 | |
| 18 | For a permanent installation, you can graft the code into your |
| 19 | <tt>.bashrc</tt> file in your home directory. |
| 20 | |
| 21 | The code is very simple (only 32 non-comment lines, as of this writing) |
| 22 | and hence easy to customize. |
| 23 |
+3
-3
| --- www/gitusers.md | ||
| +++ www/gitusers.md | ||
| @@ -56,11 +56,11 @@ | ||
| 56 | 56 | |
| 57 | 57 | |
| 58 | 58 | |
| 59 | 59 | #### <a id="cwork" name="scw"></a> Checkout Workflows |
| 60 | 60 | |
| 61 | -A Fossil repository is a SQLite database storing the entire history of a | |
| 61 | +A Fossil repository is an SQLite database storing the entire history of a | |
| 62 | 62 | project. It is not normally stored inside the working tree. |
| 63 | 63 | A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory |
| 64 | 64 | that contains a snapshot of your project that you are currently working |
| 65 | 65 | on, extracted for you from the repository database file by the `fossil` |
| 66 | 66 | program. |
| @@ -148,11 +148,11 @@ | ||
| 148 | 148 | option, it won’t let you close the check-out with uncommitted changes to |
| 149 | 149 | those managed files. |
| 150 | 150 | |
| 151 | 151 | The `close` command also refuses to run without `--force` when you have |
| 152 | 152 | certain other precious per-checkout data that Fossil stores in the |
| 153 | -`.fslckout` file at the root of a check-out directory. This is a SQLite | |
| 153 | +`.fslckout` file at the root of a check-out directory. This is an SQLite | |
| 154 | 154 | database that keeps track of local state such as what version you have |
| 155 | 155 | checked out, the contents of the [stash] for that working directory, the |
| 156 | 156 | [undo] buffers, per-checkout [settings][set], and so forth. The stash |
| 157 | 157 | and undo buffers are considered precious uncommitted changes, |
| 158 | 158 | so you have to force Fossil to discard these as part of closing the |
| @@ -773,11 +773,11 @@ | ||
| 773 | 773 | hashes.) |
| 774 | 774 | |
| 775 | 775 | In this scheme, Alice then needs to say “`fossil update trunk`” in order |
| 776 | 776 | to return her check-out’s parent commit to the previous version lest her |
| 777 | 777 | next attempted commit land atop this mistake branch. The fact that Bob |
| 778 | -marked the branch as closed will prevent that from going thru, cluing | |
| 778 | +marked the branch as closed will prevent that from going through, cluing | |
| 779 | 779 | Alice into what she needs to do to remedy the situation, but that merely |
| 780 | 780 | shows why it’s a better workflow if Alice makes the amendment herself: |
| 781 | 781 | |
| 782 | 782 | ``` |
| 783 | 783 | fossil amend --branch MISTAKE --hide --close \ |
| 784 | 784 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -56,11 +56,11 @@ | |
| 56 | |
| 57 | |
| 58 | |
| 59 | #### <a id="cwork" name="scw"></a> Checkout Workflows |
| 60 | |
| 61 | A Fossil repository is a SQLite database storing the entire history of a |
| 62 | project. It is not normally stored inside the working tree. |
| 63 | A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory |
| 64 | that contains a snapshot of your project that you are currently working |
| 65 | on, extracted for you from the repository database file by the `fossil` |
| 66 | program. |
| @@ -148,11 +148,11 @@ | |
| 148 | option, it won’t let you close the check-out with uncommitted changes to |
| 149 | those managed files. |
| 150 | |
| 151 | The `close` command also refuses to run without `--force` when you have |
| 152 | certain other precious per-checkout data that Fossil stores in the |
| 153 | `.fslckout` file at the root of a check-out directory. This is a SQLite |
| 154 | database that keeps track of local state such as what version you have |
| 155 | checked out, the contents of the [stash] for that working directory, the |
| 156 | [undo] buffers, per-checkout [settings][set], and so forth. The stash |
| 157 | and undo buffers are considered precious uncommitted changes, |
| 158 | so you have to force Fossil to discard these as part of closing the |
| @@ -773,11 +773,11 @@ | |
| 773 | hashes.) |
| 774 | |
| 775 | In this scheme, Alice then needs to say “`fossil update trunk`” in order |
| 776 | to return her check-out’s parent commit to the previous version lest her |
| 777 | next attempted commit land atop this mistake branch. The fact that Bob |
| 778 | marked the branch as closed will prevent that from going thru, cluing |
| 779 | Alice into what she needs to do to remedy the situation, but that merely |
| 780 | shows why it’s a better workflow if Alice makes the amendment herself: |
| 781 | |
| 782 | ``` |
| 783 | fossil amend --branch MISTAKE --hide --close \ |
| 784 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -56,11 +56,11 @@ | |
| 56 | |
| 57 | |
| 58 | |
| 59 | #### <a id="cwork" name="scw"></a> Checkout Workflows |
| 60 | |
| 61 | A Fossil repository is an SQLite database storing the entire history of a |
| 62 | project. It is not normally stored inside the working tree. |
| 63 | A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory |
| 64 | that contains a snapshot of your project that you are currently working |
| 65 | on, extracted for you from the repository database file by the `fossil` |
| 66 | program. |
| @@ -148,11 +148,11 @@ | |
| 148 | option, it won’t let you close the check-out with uncommitted changes to |
| 149 | those managed files. |
| 150 | |
| 151 | The `close` command also refuses to run without `--force` when you have |
| 152 | certain other precious per-checkout data that Fossil stores in the |
| 153 | `.fslckout` file at the root of a check-out directory. This is an SQLite |
| 154 | database that keeps track of local state such as what version you have |
| 155 | checked out, the contents of the [stash] for that working directory, the |
| 156 | [undo] buffers, per-checkout [settings][set], and so forth. The stash |
| 157 | and undo buffers are considered precious uncommitted changes, |
| 158 | so you have to force Fossil to discard these as part of closing the |
| @@ -773,11 +773,11 @@ | |
| 773 | hashes.) |
| 774 | |
| 775 | In this scheme, Alice then needs to say “`fossil update trunk`” in order |
| 776 | to return her check-out’s parent commit to the previous version lest her |
| 777 | next attempted commit land atop this mistake branch. The fact that Bob |
| 778 | marked the branch as closed will prevent that from going through, cluing |
| 779 | Alice into what she needs to do to remedy the situation, but that merely |
| 780 | shows why it’s a better workflow if Alice makes the amendment herself: |
| 781 | |
| 782 | ``` |
| 783 | fossil amend --branch MISTAKE --hide --close \ |
| 784 |
+2
-2
| --- www/glossary.md | ||
| +++ www/glossary.md | ||
| @@ -73,11 +73,11 @@ | ||
| 73 | 73 | because you’ll have all of your OS’s *other* files intermixed. |
| 74 | 74 | Worse, Fossil doesn’t track OS permissions, so even if you were to |
| 75 | 75 | try to use Fossil as a system deployment tool by archiving versions |
| 76 | 76 | of the OS configuration files and then unpacking them on a new |
| 77 | 77 | system, the extracted project files would have read/write access by |
| 78 | - the user who did the extraction, which probably isn’t want you were | |
| 78 | + the user who did the extraction, which probably isn’t what you were | |
| 79 | 79 | wanting. |
| 80 | 80 | |
| 81 | 81 | Even with these problems aside, do you really want a `.fslckout` |
| 82 | 82 | SQLite database at the root of your filesystem? Are you prepared for |
| 83 | 83 | the consequences of saying `fossil clean --verily` on such a system? |
| @@ -348,11 +348,11 @@ | ||
| 348 | 348 | |
| 349 | 349 | * In the same way that one cannot extract files from a zip archive |
| 350 | 350 | without having a copy of that zip file, one cannot make check-outs |
| 351 | 351 | without access to the repository file or a clone thereof. |
| 352 | 352 | |
| 353 | -* Because a Fossil repository is a SQLite database file, the same | |
| 353 | +* Because a Fossil repository is an SQLite database file, the same | |
| 354 | 354 | rules for avoiding data corruption apply to it. In particular, it is |
| 355 | 355 | [nearly a hard requirement][h2cflp] that the repository clone be on |
| 356 | 356 | the same machine as the one where you make check-outs and the |
| 357 | 357 | subsequent check-ins. |
| 358 | 358 | |
| 359 | 359 |
| --- www/glossary.md | |
| +++ www/glossary.md | |
| @@ -73,11 +73,11 @@ | |
| 73 | because you’ll have all of your OS’s *other* files intermixed. |
| 74 | Worse, Fossil doesn’t track OS permissions, so even if you were to |
| 75 | try to use Fossil as a system deployment tool by archiving versions |
| 76 | of the OS configuration files and then unpacking them on a new |
| 77 | system, the extracted project files would have read/write access by |
| 78 | the user who did the extraction, which probably isn’t want you were |
| 79 | wanting. |
| 80 | |
| 81 | Even with these problems aside, do you really want a `.fslckout` |
| 82 | SQLite database at the root of your filesystem? Are you prepared for |
| 83 | the consequences of saying `fossil clean --verily` on such a system? |
| @@ -348,11 +348,11 @@ | |
| 348 | |
| 349 | * In the same way that one cannot extract files from a zip archive |
| 350 | without having a copy of that zip file, one cannot make check-outs |
| 351 | without access to the repository file or a clone thereof. |
| 352 | |
| 353 | * Because a Fossil repository is a SQLite database file, the same |
| 354 | rules for avoiding data corruption apply to it. In particular, it is |
| 355 | [nearly a hard requirement][h2cflp] that the repository clone be on |
| 356 | the same machine as the one where you make check-outs and the |
| 357 | subsequent check-ins. |
| 358 | |
| 359 |
| --- www/glossary.md | |
| +++ www/glossary.md | |
| @@ -73,11 +73,11 @@ | |
| 73 | because you’ll have all of your OS’s *other* files intermixed. |
| 74 | Worse, Fossil doesn’t track OS permissions, so even if you were to |
| 75 | try to use Fossil as a system deployment tool by archiving versions |
| 76 | of the OS configuration files and then unpacking them on a new |
| 77 | system, the extracted project files would have read/write access by |
| 78 | the user who did the extraction, which probably isn’t what you were |
| 79 | wanting. |
| 80 | |
| 81 | Even with these problems aside, do you really want a `.fslckout` |
| 82 | SQLite database at the root of your filesystem? Are you prepared for |
| 83 | the consequences of saying `fossil clean --verily` on such a system? |
| @@ -348,11 +348,11 @@ | |
| 348 | |
| 349 | * In the same way that one cannot extract files from a zip archive |
| 350 | without having a copy of that zip file, one cannot make check-outs |
| 351 | without access to the repository file or a clone thereof. |
| 352 | |
| 353 | * Because a Fossil repository is an SQLite database file, the same |
| 354 | rules for avoiding data corruption apply to it. In particular, it is |
| 355 | [nearly a hard requirement][h2cflp] that the repository clone be on |
| 356 | the same machine as the one where you make check-outs and the |
| 357 | subsequent check-ins. |
| 358 | |
| 359 |
+1
-1
| --- www/grep.md | ||
| +++ www/grep.md | ||
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | |
| 50 | 50 | $ fossil grep COMMAND: $(fossil ls src) |
| 51 | 51 | |
| 52 | 52 | If you run that in a check-out of the [Fossil self-hosting source |
| 53 | 53 | repository][fshsr], that returns the first line of the built-in |
| 54 | -documentation for each Fossil command, across all historical verisons. | |
| 54 | +documentation for each Fossil command, across all historical versions. | |
| 55 | 55 | |
| 56 | 56 | Fossil `grep` has extensions relative to these other `grep` standards, |
| 57 | 57 | such as `--verbose` to print each checkin ID considered, regardless of |
| 58 | 58 | whether it matches. This one is noteworthy here because the behavior |
| 59 | 59 | used to be under `-v` before we reassigned it to give POSIX-like `grep |
| 60 | 60 |
| --- www/grep.md | |
| +++ www/grep.md | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $ fossil grep COMMAND: $(fossil ls src) |
| 51 | |
| 52 | If you run that in a check-out of the [Fossil self-hosting source |
| 53 | repository][fshsr], that returns the first line of the built-in |
| 54 | documentation for each Fossil command, across all historical verisons. |
| 55 | |
| 56 | Fossil `grep` has extensions relative to these other `grep` standards, |
| 57 | such as `--verbose` to print each checkin ID considered, regardless of |
| 58 | whether it matches. This one is noteworthy here because the behavior |
| 59 | used to be under `-v` before we reassigned it to give POSIX-like `grep |
| 60 |
| --- www/grep.md | |
| +++ www/grep.md | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $ fossil grep COMMAND: $(fossil ls src) |
| 51 | |
| 52 | If you run that in a check-out of the [Fossil self-hosting source |
| 53 | repository][fshsr], that returns the first line of the built-in |
| 54 | documentation for each Fossil command, across all historical versions. |
| 55 | |
| 56 | Fossil `grep` has extensions relative to these other `grep` standards, |
| 57 | such as `--verbose` to print each checkin ID considered, regardless of |
| 58 | whether it matches. This one is noteworthy here because the behavior |
| 59 | used to be under `-v` before we reassigned it to give POSIX-like `grep |
| 60 |
+1
-1
| --- www/hashpolicy.wiki | ||
| +++ www/hashpolicy.wiki | ||
| @@ -78,11 +78,11 @@ | ||
| 78 | 78 | Version 2.0 extended the [./fileformat.wiki|Fossil file format] |
| 79 | 79 | to allow artifacts to be named by either SHA1 or SHA3-256 hashes. |
| 80 | 80 | (SHA3-256 is the only variant of SHA3 that |
| 81 | 81 | Fossil uses for artifact naming, so for the remainder of this article |
| 82 | 82 | it will be called simply "SHA3". Similarly, "Hardened SHA1" will |
| 83 | -shortened to "SHA1" in the remaining text.) | |
| 83 | +be shortened to "SHA1" in the remaining text.) | |
| 84 | 84 | |
| 85 | 85 | To be clear: Fossil (version 2.0 and later) |
| 86 | 86 | allows the SHA1 and SHA3 hashes to be mixed within |
| 87 | 87 | the same repository. Older check-ins, created years ago, |
| 88 | 88 | continue to be named using their legacy SHA1 hashes while |
| 89 | 89 |
| --- www/hashpolicy.wiki | |
| +++ www/hashpolicy.wiki | |
| @@ -78,11 +78,11 @@ | |
| 78 | Version 2.0 extended the [./fileformat.wiki|Fossil file format] |
| 79 | to allow artifacts to be named by either SHA1 or SHA3-256 hashes. |
| 80 | (SHA3-256 is the only variant of SHA3 that |
| 81 | Fossil uses for artifact naming, so for the remainder of this article |
| 82 | it will be called simply "SHA3". Similarly, "Hardened SHA1" will |
| 83 | shortened to "SHA1" in the remaining text.) |
| 84 | |
| 85 | To be clear: Fossil (version 2.0 and later) |
| 86 | allows the SHA1 and SHA3 hashes to be mixed within |
| 87 | the same repository. Older check-ins, created years ago, |
| 88 | continue to be named using their legacy SHA1 hashes while |
| 89 |
| --- www/hashpolicy.wiki | |
| +++ www/hashpolicy.wiki | |
| @@ -78,11 +78,11 @@ | |
| 78 | Version 2.0 extended the [./fileformat.wiki|Fossil file format] |
| 79 | to allow artifacts to be named by either SHA1 or SHA3-256 hashes. |
| 80 | (SHA3-256 is the only variant of SHA3 that |
| 81 | Fossil uses for artifact naming, so for the remainder of this article |
| 82 | it will be called simply "SHA3". Similarly, "Hardened SHA1" will |
| 83 | be shortened to "SHA1" in the remaining text.) |
| 84 | |
| 85 | To be clear: Fossil (version 2.0 and later) |
| 86 | allows the SHA1 and SHA3 hashes to be mixed within |
| 87 | the same repository. Older check-ins, created years ago, |
| 88 | continue to be named using their legacy SHA1 hashes while |
| 89 |
+1
-1
| --- www/hints.wiki | ||
| +++ www/hints.wiki | ||
| @@ -8,11 +8,11 @@ | ||
| 8 | 8 | 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands |
| 9 | 9 | to get a pop-up |
| 10 | 10 | window containing a complete side-by-side diff. (NB: The pop-up |
| 11 | 11 | window is run as a separate Tcl/Tk process, so you will need to |
| 12 | 12 | have Tcl/Tk installed on your machine for this to work. Visit |
| 13 | - [http://www.activestate.com/activetcl] to for a quick download of | |
| 13 | + [http://www.activestate.com/activetcl] for a quick download of | |
| 14 | 14 | Tcl/Tk if you do not already have it on your system.) |
| 15 | 15 | |
| 16 | 16 | 3. The "[/help/clean | fossil clean -x]" command is a great |
| 17 | 17 | alternative to "make clean". You can use "[/help/clean | fossil clean -f]" |
| 18 | 18 | as a slightly safer alternative if the "ignore-glob" setting is |
| 19 | 19 |
| --- www/hints.wiki | |
| +++ www/hints.wiki | |
| @@ -8,11 +8,11 @@ | |
| 8 | 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands |
| 9 | to get a pop-up |
| 10 | window containing a complete side-by-side diff. (NB: The pop-up |
| 11 | window is run as a separate Tcl/Tk process, so you will need to |
| 12 | have Tcl/Tk installed on your machine for this to work. Visit |
| 13 | [http://www.activestate.com/activetcl] to for a quick download of |
| 14 | Tcl/Tk if you do not already have it on your system.) |
| 15 | |
| 16 | 3. The "[/help/clean | fossil clean -x]" command is a great |
| 17 | alternative to "make clean". You can use "[/help/clean | fossil clean -f]" |
| 18 | as a slightly safer alternative if the "ignore-glob" setting is |
| 19 |
| --- www/hints.wiki | |
| +++ www/hints.wiki | |
| @@ -8,11 +8,11 @@ | |
| 8 | 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands |
| 9 | to get a pop-up |
| 10 | window containing a complete side-by-side diff. (NB: The pop-up |
| 11 | window is run as a separate Tcl/Tk process, so you will need to |
| 12 | have Tcl/Tk installed on your machine for this to work. Visit |
| 13 | [http://www.activestate.com/activetcl] for a quick download of |
| 14 | Tcl/Tk if you do not already have it on your system.) |
| 15 | |
| 16 | 3. The "[/help/clean | fossil clean -x]" command is a great |
| 17 | alternative to "make clean". You can use "[/help/clean | fossil clean -f]" |
| 18 | as a slightly safer alternative if the "ignore-glob" setting is |
| 19 |
+1
-1
| --- www/history.md | ||
| +++ www/history.md | ||
| @@ -16,11 +16,11 @@ | ||
| 16 | 16 | |
| 17 | 17 | [120]: /reports?type=ci&view=byuser |
| 18 | 18 | |
| 19 | 19 | ## History |
| 20 | 20 | |
| 21 | -The SQLite project start out using [CVS][300], as CVS was the most | |
| 21 | +The SQLite project started out using [CVS][300], as CVS was the most | |
| 22 | 22 | commonly used version control system in that era (circa 2000). CVS |
| 23 | 23 | was an amazing version control system for its day in that it allowed |
| 24 | 24 | multiple developers to be editing the same file at the same time. |
| 25 | 25 | |
| 26 | 26 | [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System |
| 27 | 27 |
| --- www/history.md | |
| +++ www/history.md | |
| @@ -16,11 +16,11 @@ | |
| 16 | |
| 17 | [120]: /reports?type=ci&view=byuser |
| 18 | |
| 19 | ## History |
| 20 | |
| 21 | The SQLite project start out using [CVS][300], as CVS was the most |
| 22 | commonly used version control system in that era (circa 2000). CVS |
| 23 | was an amazing version control system for its day in that it allowed |
| 24 | multiple developers to be editing the same file at the same time. |
| 25 | |
| 26 | [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System |
| 27 |
| --- www/history.md | |
| +++ www/history.md | |
| @@ -16,11 +16,11 @@ | |
| 16 | |
| 17 | [120]: /reports?type=ci&view=byuser |
| 18 | |
| 19 | ## History |
| 20 | |
| 21 | The SQLite project started out using [CVS][300], as CVS was the most |
| 22 | commonly used version control system in that era (circa 2000). CVS |
| 23 | was an amazing version control system for its day in that it allowed |
| 24 | multiple developers to be editing the same file at the same time. |
| 25 | |
| 26 | [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System |
| 27 |
+4
-4
| --- www/hooks.md | ||
| +++ www/hooks.md | ||
| @@ -5,11 +5,11 @@ | ||
| 5 | 5 | a continuous integration (CI) system. |
| 6 | 6 | |
| 7 | 7 | ## Interim Documentation. |
| 8 | 8 | |
| 9 | 9 | * This is a work-in-progress. The interface is in flux. |
| 10 | - For the time being, the documentation as a list of | |
| 10 | + For the time being, the documentation is a list of | |
| 11 | 11 | bullet points. We hope to transform this into a proper document |
| 12 | 12 | later, after things settle down. |
| 13 | 13 | |
| 14 | 14 | * Contributions and suggestions to the hook system and/or the |
| 15 | 15 | documentation are welcomed. |
| @@ -35,14 +35,14 @@ | ||
| 35 | 35 | that the original script can return. |
| 36 | 36 | |
| 37 | 37 | * The "%F" sequence inside the script is translated into the |
| 38 | 38 | name of the fossil executable. |
| 39 | 39 | |
| 40 | - * The "%R" sequence in the script is translated in to the name of | |
| 40 | + * The "%R" sequence in the script is translated into the name of | |
| 41 | 41 | the repository. |
| 42 | 42 | |
| 43 | - * The "%A" sequence becomes the name of an auxiliary input files, | |
| 43 | + * The "%A" sequence becomes the name of an auxiliary input file, | |
| 44 | 44 | the meaning of which depends on the hook type. The auxiliary filename |
| 45 | 45 | might be an empty string. Take care to use appropriate quoting! |
| 46 | 46 | |
| 47 | 47 | ## Disabled Hooks |
| 48 | 48 | |
| @@ -144,11 +144,11 @@ | ||
| 144 | 144 | ## Commit-Msg Hooks |
| 145 | 145 | |
| 146 | 146 | * Commit-msg hooks are not yet implemented. |
| 147 | 147 | |
| 148 | 148 | * The commit-msg hooks run during "fossil commit" after the check-in |
| 149 | - messages has been entered by the user. The "%A" argument to the | |
| 149 | + message has been entered by the user. The "%A" argument to the | |
| 150 | 150 | commit-msg hook is the text of the commit message. The intent |
| 151 | 151 | of the commit-msg hook is to validate the text of the commit |
| 152 | 152 | message to (for example) check for typos or ensure that it |
| 153 | 153 | conforms to standards. |
| 154 | 154 | |
| 155 | 155 |
| --- www/hooks.md | |
| +++ www/hooks.md | |
| @@ -5,11 +5,11 @@ | |
| 5 | a continuous integration (CI) system. |
| 6 | |
| 7 | ## Interim Documentation. |
| 8 | |
| 9 | * This is a work-in-progress. The interface is in flux. |
| 10 | For the time being, the documentation as a list of |
| 11 | bullet points. We hope to transform this into a proper document |
| 12 | later, after things settle down. |
| 13 | |
| 14 | * Contributions and suggestions to the hook system and/or the |
| 15 | documentation are welcomed. |
| @@ -35,14 +35,14 @@ | |
| 35 | that the original script can return. |
| 36 | |
| 37 | * The "%F" sequence inside the script is translated into the |
| 38 | name of the fossil executable. |
| 39 | |
| 40 | * The "%R" sequence in the script is translated in to the name of |
| 41 | the repository. |
| 42 | |
| 43 | * The "%A" sequence becomes the name of an auxiliary input files, |
| 44 | the meaning of which depends on the hook type. The auxiliary filename |
| 45 | might be an empty string. Take care to use appropriate quoting! |
| 46 | |
| 47 | ## Disabled Hooks |
| 48 | |
| @@ -144,11 +144,11 @@ | |
| 144 | ## Commit-Msg Hooks |
| 145 | |
| 146 | * Commit-msg hooks are not yet implemented. |
| 147 | |
| 148 | * The commit-msg hooks run during "fossil commit" after the check-in |
| 149 | messages has been entered by the user. The "%A" argument to the |
| 150 | commit-msg hook is the text of the commit message. The intent |
| 151 | of the commit-msg hook is to validate the text of the commit |
| 152 | message to (for example) check for typos or ensure that it |
| 153 | conforms to standards. |
| 154 | |
| 155 |
| --- www/hooks.md | |
| +++ www/hooks.md | |
| @@ -5,11 +5,11 @@ | |
| 5 | a continuous integration (CI) system. |
| 6 | |
| 7 | ## Interim Documentation. |
| 8 | |
| 9 | * This is a work-in-progress. The interface is in flux. |
| 10 | For the time being, the documentation is a list of |
| 11 | bullet points. We hope to transform this into a proper document |
| 12 | later, after things settle down. |
| 13 | |
| 14 | * Contributions and suggestions to the hook system and/or the |
| 15 | documentation are welcomed. |
| @@ -35,14 +35,14 @@ | |
| 35 | that the original script can return. |
| 36 | |
| 37 | * The "%F" sequence inside the script is translated into the |
| 38 | name of the fossil executable. |
| 39 | |
| 40 | * The "%R" sequence in the script is translated into the name of |
| 41 | the repository. |
| 42 | |
| 43 | * The "%A" sequence becomes the name of an auxiliary input file, |
| 44 | the meaning of which depends on the hook type. The auxiliary filename |
| 45 | might be an empty string. Take care to use appropriate quoting! |
| 46 | |
| 47 | ## Disabled Hooks |
| 48 | |
| @@ -144,11 +144,11 @@ | |
| 144 | ## Commit-Msg Hooks |
| 145 | |
| 146 | * Commit-msg hooks are not yet implemented. |
| 147 | |
| 148 | * The commit-msg hooks run during "fossil commit" after the check-in |
| 149 | message has been entered by the user. The "%A" argument to the |
| 150 | commit-msg hook is the text of the commit message. The intent |
| 151 | of the commit-msg hook is to validate the text of the commit |
| 152 | message to (for example) check for typos or ensure that it |
| 153 | conforms to standards. |
| 154 | |
| 155 |
+4
-4
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -84,16 +84,16 @@ | ||
| 84 | 84 | the repository are consistent prior to each commit. |
| 85 | 85 | |
| 86 | 86 | 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 87 | 87 | |
| 88 | 88 | <hr> |
| 89 | -<h3>Latest Release: 2.24 ([/timeline?c=version-2.24|2024-04-23])</h3> | |
| 89 | +<h3>Latest Release: 2.25 ([/timeline?c=version-2.25|2024-11-06])</h3> | |
| 90 | 90 | |
| 91 | 91 | * [/uv/download.html|Download] |
| 92 | - * [./changes.wiki#v2_24|Change Summary] | |
| 93 | - * [/timeline?p=version-2.24&bt=version-2.23&y=ci|Check-ins in version 2.24] | |
| 94 | - * [/timeline?df=version-2.24&y=ci|Check-ins derived from the 2.24 release] | |
| 92 | + * [./changes.wiki#v2_25|Change Summary] | |
| 93 | + * [/timeline?p=version-2.25&bt=version-2.24&y=ci|Check-ins in version 2.25] | |
| 94 | + * [/timeline?df=version-2.25&y=ci|Check-ins derived from the 2.25 release] | |
| 95 | 95 | * [/timeline?t=release|Timeline of all past releases] |
| 96 | 96 | |
| 97 | 97 | <hr> |
| 98 | 98 | <h3>Quick Start</h3> |
| 99 | 99 | |
| 100 | 100 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -84,16 +84,16 @@ | |
| 84 | the repository are consistent prior to each commit. |
| 85 | |
| 86 | 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 87 | |
| 88 | <hr> |
| 89 | <h3>Latest Release: 2.24 ([/timeline?c=version-2.24|2024-04-23])</h3> |
| 90 | |
| 91 | * [/uv/download.html|Download] |
| 92 | * [./changes.wiki#v2_24|Change Summary] |
| 93 | * [/timeline?p=version-2.24&bt=version-2.23&y=ci|Check-ins in version 2.24] |
| 94 | * [/timeline?df=version-2.24&y=ci|Check-ins derived from the 2.24 release] |
| 95 | * [/timeline?t=release|Timeline of all past releases] |
| 96 | |
| 97 | <hr> |
| 98 | <h3>Quick Start</h3> |
| 99 | |
| 100 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -84,16 +84,16 @@ | |
| 84 | the repository are consistent prior to each commit. |
| 85 | |
| 86 | 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 87 | |
| 88 | <hr> |
| 89 | <h3>Latest Release: 2.25 ([/timeline?c=version-2.25|2024-11-06])</h3> |
| 90 | |
| 91 | * [/uv/download.html|Download] |
| 92 | * [./changes.wiki#v2_25|Change Summary] |
| 93 | * [/timeline?p=version-2.25&bt=version-2.24&y=ci|Check-ins in version 2.25] |
| 94 | * [/timeline?df=version-2.25&y=ci|Check-ins derived from the 2.25 release] |
| 95 | * [/timeline?t=release|Timeline of all past releases] |
| 96 | |
| 97 | <hr> |
| 98 | <h3>Quick Start</h3> |
| 99 | |
| 100 |
+2
-2
| --- www/inout.wiki | ||
| +++ www/inout.wiki | ||
| @@ -48,11 +48,11 @@ | ||
| 48 | 48 | emitted by "<tt>git fast-export</tt>" before sending it along to Fossil, |
| 49 | 49 | but we've seen the problem recur on multiple machines. |
| 50 | 50 | |
| 51 | 51 | While one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't |
| 52 | 52 | seem to be affected by this problem — we instead recommend using |
| 53 | -Mirosoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows | |
| 53 | +Microsoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows | |
| 54 | 54 | Subsystem for Linux] or either of the two popular "Git for Windows" |
| 55 | 55 | distributions based on MSYS2. They handle pipes the POSIX way, avoiding |
| 56 | 56 | any dependency on the amount of data involved. |
| 57 | 57 | |
| 58 | 58 | <h2>Fossil → Git</h2> |
| @@ -76,11 +76,11 @@ | ||
| 76 | 76 | As with the "import" command, the --git option is not required |
| 77 | 77 | since the git-fast-export file format is currently the only VCS interchange |
| 78 | 78 | format that Fossil will generate. However, |
| 79 | 79 | future versions of Fossil might add the ability to generate other |
| 80 | 80 | VCS interchange formats, and so for compatibility, the use of the --git |
| 81 | -option recommended. | |
| 81 | +option is recommended. | |
| 82 | 82 | |
| 83 | 83 | <h2>Mirror A Fossil Repository In Git</h2> |
| 84 | 84 | |
| 85 | 85 | Fossil version 2.9 and later supports a simple mechanism for |
| 86 | 86 | doing a Git or |
| 87 | 87 |
| --- www/inout.wiki | |
| +++ www/inout.wiki | |
| @@ -48,11 +48,11 @@ | |
| 48 | emitted by "<tt>git fast-export</tt>" before sending it along to Fossil, |
| 49 | but we've seen the problem recur on multiple machines. |
| 50 | |
| 51 | While one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't |
| 52 | seem to be affected by this problem — we instead recommend using |
| 53 | Mirosoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows |
| 54 | Subsystem for Linux] or either of the two popular "Git for Windows" |
| 55 | distributions based on MSYS2. They handle pipes the POSIX way, avoiding |
| 56 | any dependency on the amount of data involved. |
| 57 | |
| 58 | <h2>Fossil → Git</h2> |
| @@ -76,11 +76,11 @@ | |
| 76 | As with the "import" command, the --git option is not required |
| 77 | since the git-fast-export file format is currently the only VCS interchange |
| 78 | format that Fossil will generate. However, |
| 79 | future versions of Fossil might add the ability to generate other |
| 80 | VCS interchange formats, and so for compatibility, the use of the --git |
| 81 | option recommended. |
| 82 | |
| 83 | <h2>Mirror A Fossil Repository In Git</h2> |
| 84 | |
| 85 | Fossil version 2.9 and later supports a simple mechanism for |
| 86 | doing a Git or |
| 87 |
| --- www/inout.wiki | |
| +++ www/inout.wiki | |
| @@ -48,11 +48,11 @@ | |
| 48 | emitted by "<tt>git fast-export</tt>" before sending it along to Fossil, |
| 49 | but we've seen the problem recur on multiple machines. |
| 50 | |
| 51 | While one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't |
| 52 | seem to be affected by this problem — we instead recommend using |
| 53 | Microsoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows |
| 54 | Subsystem for Linux] or either of the two popular "Git for Windows" |
| 55 | distributions based on MSYS2. They handle pipes the POSIX way, avoiding |
| 56 | any dependency on the amount of data involved. |
| 57 | |
| 58 | <h2>Fossil → Git</h2> |
| @@ -76,11 +76,11 @@ | |
| 76 | As with the "import" command, the --git option is not required |
| 77 | since the git-fast-export file format is currently the only VCS interchange |
| 78 | format that Fossil will generate. However, |
| 79 | future versions of Fossil might add the ability to generate other |
| 80 | VCS interchange formats, and so for compatibility, the use of the --git |
| 81 | option is recommended. |
| 82 | |
| 83 | <h2>Mirror A Fossil Repository In Git</h2> |
| 84 | |
| 85 | Fossil version 2.9 and later supports a simple mechanism for |
| 86 | doing a Git or |
| 87 |
+5
-5
| --- www/interwiki.md | ||
| +++ www/interwiki.md | ||
| @@ -50,11 +50,11 @@ | ||
| 50 | 50 | or is an empty string. |
| 51 | 51 | |
| 52 | 52 | 2. <b>Hash Links</b> → the PageName is a hexadecimal number with |
| 53 | 53 | at least four digits. |
| 54 | 54 | |
| 55 | - 3. <b>Wiki Links</b> → An PageName that is not a Path or Hash. | |
| 55 | + 3. <b>Wiki Links</b> → A PageName that is not a Path or Hash. | |
| 56 | 56 | |
| 57 | 57 | The Intermap defines a base URL for each Tag. Path links are appended |
| 58 | 58 | directly to the URL contained in the Intermap. The Intermap can define |
| 59 | 59 | additional text to put in between the base URL and the PageName for |
| 60 | 60 | Hash and Wiki links, respectively. |
| @@ -62,11 +62,11 @@ | ||
| 62 | 62 | <a id="intermap"></a> |
| 63 | 63 | ## Intermap |
| 64 | 64 | |
| 65 | 65 | The intermap defines a mapping from interwiki Tags to full URLs. The |
| 66 | 66 | Intermap can be viewed and managed using the [fossil interwiki][iwiki] |
| 67 | -command or the [/intermap][imap] webpages. | |
| 67 | +command or the [/intermap][imap] webpage. | |
| 68 | 68 | |
| 69 | 69 | [iwiki]: /help?cmd=interwiki |
| 70 | 70 | [imap]: /intermap |
| 71 | 71 | |
| 72 | 72 | The current intermap for a server is seen on the [/intermap][imap] page |
| @@ -77,19 +77,19 @@ | ||
| 77 | 77 | Each intermap entry stores, at a minimum, the base URL for the remote |
| 78 | 78 | wiki. The intermap entry might also store additional path text that |
| 79 | 79 | is used for Hash and Wiki links. If only the base URL is provided, |
| 80 | 80 | then the intermap will only allow Path style interwiki links. The |
| 81 | 81 | Hash and Wiki style interwiki links are only allowed if the necessary |
| 82 | -extensions for provided in the intermap. | |
| 82 | +extensions are provided in the intermap. | |
| 83 | 83 | |
| 84 | 84 | |
| 85 | 85 | ## Disadvantages and Limitations |
| 86 | 86 | |
| 87 | 87 | * Configuration is required. The intermap must be set up correctly |
| 88 | 88 | before interwiki links will work. This contrasts with ordinary |
| 89 | 89 | links that just work without any configuration. Cloning a repository |
| 90 | - copies the intermap, but normal syncs to not keep the intermap in | |
| 90 | + copies the intermap, but normal syncs do not keep the intermap in | |
| 91 | 91 | sync. Use the "[fossil config pull interwiki][fcfg]" command to |
| 92 | 92 | synchronize the intermap. |
| 93 | 93 | |
| 94 | 94 | * The is no backlink tracking. For ordinary intrawiki links, Fossil keeps |
| 95 | 95 | track of both the source and target, and when displaying targets it |
| @@ -96,11 +96,11 @@ | ||
| 96 | 96 | commonly shows links to that target. For example, if you mention a |
| 97 | 97 | check-in as part of a comment of another check-in, that new check-in |
| 98 | 98 | shows up in the "References" section of the target check-in. |
| 99 | 99 | ([example](31af805348690958). In other words, Fossil tracks not just |
| 100 | 100 | "_source→target_", but it also tracks "_target→source_". |
| 101 | - But backtracking do not work for interwiki links, since the Fossil | |
| 101 | + But backtracking does not work for interwiki links, since the Fossil | |
| 102 | 102 | running on the target has no way of scanning the source text and |
| 103 | 103 | hence has no way of knowing that it is a target of a link from the source. |
| 104 | 104 | |
| 105 | 105 | [fcfg]: /help?cmd=config |
| 106 | 106 | |
| 107 | 107 |
| --- www/interwiki.md | |
| +++ www/interwiki.md | |
| @@ -50,11 +50,11 @@ | |
| 50 | or is an empty string. |
| 51 | |
| 52 | 2. <b>Hash Links</b> → the PageName is a hexadecimal number with |
| 53 | at least four digits. |
| 54 | |
| 55 | 3. <b>Wiki Links</b> → An PageName that is not a Path or Hash. |
| 56 | |
| 57 | The Intermap defines a base URL for each Tag. Path links are appended |
| 58 | directly to the URL contained in the Intermap. The Intermap can define |
| 59 | additional text to put in between the base URL and the PageName for |
| 60 | Hash and Wiki links, respectively. |
| @@ -62,11 +62,11 @@ | |
| 62 | <a id="intermap"></a> |
| 63 | ## Intermap |
| 64 | |
| 65 | The intermap defines a mapping from interwiki Tags to full URLs. The |
| 66 | Intermap can be viewed and managed using the [fossil interwiki][iwiki] |
| 67 | command or the [/intermap][imap] webpages. |
| 68 | |
| 69 | [iwiki]: /help?cmd=interwiki |
| 70 | [imap]: /intermap |
| 71 | |
| 72 | The current intermap for a server is seen on the [/intermap][imap] page |
| @@ -77,19 +77,19 @@ | |
| 77 | Each intermap entry stores, at a minimum, the base URL for the remote |
| 78 | wiki. The intermap entry might also store additional path text that |
| 79 | is used for Hash and Wiki links. If only the base URL is provided, |
| 80 | then the intermap will only allow Path style interwiki links. The |
| 81 | Hash and Wiki style interwiki links are only allowed if the necessary |
| 82 | extensions for provided in the intermap. |
| 83 | |
| 84 | |
| 85 | ## Disadvantages and Limitations |
| 86 | |
| 87 | * Configuration is required. The intermap must be set up correctly |
| 88 | before interwiki links will work. This contrasts with ordinary |
| 89 | links that just work without any configuration. Cloning a repository |
| 90 | copies the intermap, but normal syncs to not keep the intermap in |
| 91 | sync. Use the "[fossil config pull interwiki][fcfg]" command to |
| 92 | synchronize the intermap. |
| 93 | |
| 94 | * The is no backlink tracking. For ordinary intrawiki links, Fossil keeps |
| 95 | track of both the source and target, and when displaying targets it |
| @@ -96,11 +96,11 @@ | |
| 96 | commonly shows links to that target. For example, if you mention a |
| 97 | check-in as part of a comment of another check-in, that new check-in |
| 98 | shows up in the "References" section of the target check-in. |
| 99 | ([example](31af805348690958). In other words, Fossil tracks not just |
| 100 | "_source→target_", but it also tracks "_target→source_". |
| 101 | But backtracking do not work for interwiki links, since the Fossil |
| 102 | running on the target has no way of scanning the source text and |
| 103 | hence has no way of knowing that it is a target of a link from the source. |
| 104 | |
| 105 | [fcfg]: /help?cmd=config |
| 106 | |
| 107 |
| --- www/interwiki.md | |
| +++ www/interwiki.md | |
| @@ -50,11 +50,11 @@ | |
| 50 | or is an empty string. |
| 51 | |
| 52 | 2. <b>Hash Links</b> → the PageName is a hexadecimal number with |
| 53 | at least four digits. |
| 54 | |
| 55 | 3. <b>Wiki Links</b> → A PageName that is not a Path or Hash. |
| 56 | |
| 57 | The Intermap defines a base URL for each Tag. Path links are appended |
| 58 | directly to the URL contained in the Intermap. The Intermap can define |
| 59 | additional text to put in between the base URL and the PageName for |
| 60 | Hash and Wiki links, respectively. |
| @@ -62,11 +62,11 @@ | |
| 62 | <a id="intermap"></a> |
| 63 | ## Intermap |
| 64 | |
| 65 | The intermap defines a mapping from interwiki Tags to full URLs. The |
| 66 | Intermap can be viewed and managed using the [fossil interwiki][iwiki] |
| 67 | command or the [/intermap][imap] webpage. |
| 68 | |
| 69 | [iwiki]: /help?cmd=interwiki |
| 70 | [imap]: /intermap |
| 71 | |
| 72 | The current intermap for a server is seen on the [/intermap][imap] page |
| @@ -77,19 +77,19 @@ | |
| 77 | Each intermap entry stores, at a minimum, the base URL for the remote |
| 78 | wiki. The intermap entry might also store additional path text that |
| 79 | is used for Hash and Wiki links. If only the base URL is provided, |
| 80 | then the intermap will only allow Path style interwiki links. The |
| 81 | Hash and Wiki style interwiki links are only allowed if the necessary |
| 82 | extensions are provided in the intermap. |
| 83 | |
| 84 | |
| 85 | ## Disadvantages and Limitations |
| 86 | |
| 87 | * Configuration is required. The intermap must be set up correctly |
| 88 | before interwiki links will work. This contrasts with ordinary |
| 89 | links that just work without any configuration. Cloning a repository |
| 90 | copies the intermap, but normal syncs do not keep the intermap in |
| 91 | sync. Use the "[fossil config pull interwiki][fcfg]" command to |
| 92 | synchronize the intermap. |
| 93 | |
| 94 | * The is no backlink tracking. For ordinary intrawiki links, Fossil keeps |
| 95 | track of both the source and target, and when displaying targets it |
| @@ -96,11 +96,11 @@ | |
| 96 | commonly shows links to that target. For example, if you mention a |
| 97 | check-in as part of a comment of another check-in, that new check-in |
| 98 | shows up in the "References" section of the target check-in. |
| 99 | ([example](31af805348690958). In other words, Fossil tracks not just |
| 100 | "_source→target_", but it also tracks "_target→source_". |
| 101 | But backtracking does not work for interwiki links, since the Fossil |
| 102 | running on the target has no way of scanning the source text and |
| 103 | hence has no way of knowing that it is a target of a link from the source. |
| 104 | |
| 105 | [fcfg]: /help?cmd=config |
| 106 | |
| 107 |
+1
-1
| --- www/json-api/api-artifact.md | ||
| +++ www/json-api/api-artifact.md | ||
| @@ -71,11 +71,11 @@ | ||
| 71 | 71 | # File Artifacts |
| 72 | 72 | |
| 73 | 73 | Fetches information about file artifacts. |
| 74 | 74 | |
| 75 | 75 | **FIXME:** the content type guessing is currently very primitive, and |
| 76 | -may (but i haven't seen this) mis-diagnose some non-binary files as | |
| 76 | +may (but I haven't seen this) mis-diagnose some non-binary files as | |
| 77 | 77 | binary. Fossil doesn't yet have a mechanism for mime-type mappings. |
| 78 | 78 | |
| 79 | 79 | **Status:** implemented 20111020 |
| 80 | 80 | |
| 81 | 81 | **Required permissions:** "o" |
| 82 | 82 |
| --- www/json-api/api-artifact.md | |
| +++ www/json-api/api-artifact.md | |
| @@ -71,11 +71,11 @@ | |
| 71 | # File Artifacts |
| 72 | |
| 73 | Fetches information about file artifacts. |
| 74 | |
| 75 | **FIXME:** the content type guessing is currently very primitive, and |
| 76 | may (but i haven't seen this) mis-diagnose some non-binary files as |
| 77 | binary. Fossil doesn't yet have a mechanism for mime-type mappings. |
| 78 | |
| 79 | **Status:** implemented 20111020 |
| 80 | |
| 81 | **Required permissions:** "o" |
| 82 |
| --- www/json-api/api-artifact.md | |
| +++ www/json-api/api-artifact.md | |
| @@ -71,11 +71,11 @@ | |
| 71 | # File Artifacts |
| 72 | |
| 73 | Fetches information about file artifacts. |
| 74 | |
| 75 | **FIXME:** the content type guessing is currently very primitive, and |
| 76 | may (but I haven't seen this) mis-diagnose some non-binary files as |
| 77 | binary. Fossil doesn't yet have a mechanism for mime-type mappings. |
| 78 | |
| 79 | **Status:** implemented 20111020 |
| 80 | |
| 81 | **Required permissions:** "o" |
| 82 |
+3
-3
| --- www/json-api/api-auth.md | ||
| +++ www/json-api/api-auth.md | ||
| @@ -33,11 +33,11 @@ | ||
| 33 | 33 | purposes, the "auth token" and the "login cookie" are the same thing (or |
| 34 | 34 | serve the same purpose), and the auth token is in fact just the value |
| 35 | 35 | part of the login cookie (which has a project-specific key). |
| 36 | 36 | |
| 37 | 37 | Note that fossil has two conventional user names which can show up in |
| 38 | -various response but do not refer to specific people: nobody and | |
| 38 | +various responses but do not refer to specific people: nobody and | |
| 39 | 39 | anonymous. The nobody user is anyone who is not logged in. The anonymous |
| 40 | 40 | user is logged in but has no persistent user data (no associated user |
| 41 | 41 | name, email address, or similar). Normally the guest (nobody) user has |
| 42 | 42 | more access restrictions. The distinction between the two is largely |
| 43 | 43 | historical - it is a mechanism to keep bots from following the |
| @@ -158,11 +158,11 @@ | ||
| 158 | 158 | tickets is disabled for the guest user then all non-guest users must |
| 159 | 159 | send authentication info in their requests in order to be able to fetch |
| 160 | 160 | ticket info. |
| 161 | 161 | |
| 162 | 162 | Cookie-aware clients should send the login-generated cookie with each |
| 163 | -request, in which case they do not need explicitly include the | |
| 163 | +request, in which case they do not need to explicitly include the | |
| 164 | 164 | `authToken` in the JSON envelope/GET arguments. If submitted, the |
| 165 | 165 | `authToken` is used, otherwise the cookie, if set, is used. Note that |
| 166 | 166 | fossil uses a project-dependent cookie name in order to help thwart |
| 167 | 167 | attacks, so there is no simple mapping of cookie *name* to auth |
| 168 | 168 | token. That said, the cookie's *value* is also the auth token's value. |
| @@ -200,11 +200,11 @@ | ||
| 200 | 200 | |
| 201 | 201 | The password value *may* be time-limited, and *may* eventually become |
| 202 | 202 | invalidated due to old age. This is unspecified. |
| 203 | 203 | |
| 204 | 204 | ***Potential***** (low-probability) bug regarding the seed value:** from |
| 205 | -what i hear, some unusual JSON platforms don't support full 32-bit | |
| 205 | +what I hear, some unusual JSON platforms don't support full 32-bit | |
| 206 | 206 | precision. If absolutely necessary we could chop off a bit or two from |
| 207 | 207 | the seed value (*if* it ever becomes a problem and if DRH blesses it). |
| 208 | 208 | Or we could just make it a double. |
| 209 | 209 | |
| 210 | 210 | |
| 211 | 211 |
| --- www/json-api/api-auth.md | |
| +++ www/json-api/api-auth.md | |
| @@ -33,11 +33,11 @@ | |
| 33 | purposes, the "auth token" and the "login cookie" are the same thing (or |
| 34 | serve the same purpose), and the auth token is in fact just the value |
| 35 | part of the login cookie (which has a project-specific key). |
| 36 | |
| 37 | Note that fossil has two conventional user names which can show up in |
| 38 | various response but do not refer to specific people: nobody and |
| 39 | anonymous. The nobody user is anyone who is not logged in. The anonymous |
| 40 | user is logged in but has no persistent user data (no associated user |
| 41 | name, email address, or similar). Normally the guest (nobody) user has |
| 42 | more access restrictions. The distinction between the two is largely |
| 43 | historical - it is a mechanism to keep bots from following the |
| @@ -158,11 +158,11 @@ | |
| 158 | tickets is disabled for the guest user then all non-guest users must |
| 159 | send authentication info in their requests in order to be able to fetch |
| 160 | ticket info. |
| 161 | |
| 162 | Cookie-aware clients should send the login-generated cookie with each |
| 163 | request, in which case they do not need explicitly include the |
| 164 | `authToken` in the JSON envelope/GET arguments. If submitted, the |
| 165 | `authToken` is used, otherwise the cookie, if set, is used. Note that |
| 166 | fossil uses a project-dependent cookie name in order to help thwart |
| 167 | attacks, so there is no simple mapping of cookie *name* to auth |
| 168 | token. That said, the cookie's *value* is also the auth token's value. |
| @@ -200,11 +200,11 @@ | |
| 200 | |
| 201 | The password value *may* be time-limited, and *may* eventually become |
| 202 | invalidated due to old age. This is unspecified. |
| 203 | |
| 204 | ***Potential***** (low-probability) bug regarding the seed value:** from |
| 205 | what i hear, some unusual JSON platforms don't support full 32-bit |
| 206 | precision. If absolutely necessary we could chop off a bit or two from |
| 207 | the seed value (*if* it ever becomes a problem and if DRH blesses it). |
| 208 | Or we could just make it a double. |
| 209 | |
| 210 | |
| 211 |
| --- www/json-api/api-auth.md | |
| +++ www/json-api/api-auth.md | |
| @@ -33,11 +33,11 @@ | |
| 33 | purposes, the "auth token" and the "login cookie" are the same thing (or |
| 34 | serve the same purpose), and the auth token is in fact just the value |
| 35 | part of the login cookie (which has a project-specific key). |
| 36 | |
| 37 | Note that fossil has two conventional user names which can show up in |
| 38 | various responses but do not refer to specific people: nobody and |
| 39 | anonymous. The nobody user is anyone who is not logged in. The anonymous |
| 40 | user is logged in but has no persistent user data (no associated user |
| 41 | name, email address, or similar). Normally the guest (nobody) user has |
| 42 | more access restrictions. The distinction between the two is largely |
| 43 | historical - it is a mechanism to keep bots from following the |
| @@ -158,11 +158,11 @@ | |
| 158 | tickets is disabled for the guest user then all non-guest users must |
| 159 | send authentication info in their requests in order to be able to fetch |
| 160 | ticket info. |
| 161 | |
| 162 | Cookie-aware clients should send the login-generated cookie with each |
| 163 | request, in which case they do not need to explicitly include the |
| 164 | `authToken` in the JSON envelope/GET arguments. If submitted, the |
| 165 | `authToken` is used, otherwise the cookie, if set, is used. Note that |
| 166 | fossil uses a project-dependent cookie name in order to help thwart |
| 167 | attacks, so there is no simple mapping of cookie *name* to auth |
| 168 | token. That said, the cookie's *value* is also the auth token's value. |
| @@ -200,11 +200,11 @@ | |
| 200 | |
| 201 | The password value *may* be time-limited, and *may* eventually become |
| 202 | invalidated due to old age. This is unspecified. |
| 203 | |
| 204 | ***Potential***** (low-probability) bug regarding the seed value:** from |
| 205 | what I hear, some unusual JSON platforms don't support full 32-bit |
| 206 | precision. If absolutely necessary we could chop off a bit or two from |
| 207 | the seed value (*if* it ever becomes a problem and if DRH blesses it). |
| 208 | Or we could just make it a double. |
| 209 | |
| 210 | |
| 211 |
+1
-1
| --- www/json-api/api-checkout.md | ||
| +++ www/json-api/api-checkout.md | ||
| @@ -7,11 +7,11 @@ | ||
| 7 | 7 | |
| 8 | 8 | **Required permissions:** n/a (local access only) |
| 9 | 9 | |
| 10 | 10 | **Request:** `/json/status` |
| 11 | 11 | |
| 12 | -This command requires a local checkout and is analog to the "fossil | |
| 12 | +This command requires a local checkout and is the analog to the "fossil | |
| 13 | 13 | status" command. |
| 14 | 14 | |
| 15 | 15 | **Request Options:** currently none. |
| 16 | 16 | |
| 17 | 17 | Payload example: |
| 18 | 18 |
| --- www/json-api/api-checkout.md | |
| +++ www/json-api/api-checkout.md | |
| @@ -7,11 +7,11 @@ | |
| 7 | |
| 8 | **Required permissions:** n/a (local access only) |
| 9 | |
| 10 | **Request:** `/json/status` |
| 11 | |
| 12 | This command requires a local checkout and is analog to the "fossil |
| 13 | status" command. |
| 14 | |
| 15 | **Request Options:** currently none. |
| 16 | |
| 17 | Payload example: |
| 18 |
| --- www/json-api/api-checkout.md | |
| +++ www/json-api/api-checkout.md | |
| @@ -7,11 +7,11 @@ | |
| 7 | |
| 8 | **Required permissions:** n/a (local access only) |
| 9 | |
| 10 | **Request:** `/json/status` |
| 11 | |
| 12 | This command requires a local checkout and is the analog to the "fossil |
| 13 | status" command. |
| 14 | |
| 15 | **Request Options:** currently none. |
| 16 | |
| 17 | Payload example: |
| 18 |
+1
-1
| --- www/json-api/api-finfo.md | ||
| +++ www/json-api/api-finfo.md | ||
| @@ -20,11 +20,11 @@ | ||
| 20 | 20 | as opposed to listing all checkins. If set, neither "before" nor |
| 21 | 21 | "after" have any effect.\ |
| 22 | 22 | CLI mode: `--checkin|-ci` |
| 23 | 23 | - `before=DATETIME` only lists checkins from on or before this time.\ |
| 24 | 24 | CLI mode: `--before|-b` |
| 25 | -- `after=DATETIME` only lists checkins from on or before this time. | |
| 25 | +- `after=DATETIME` only lists checkins from on or after this time. | |
| 26 | 26 | Using this option swaps the sort order, to provide reasonable |
| 27 | 27 | behaviour in conjunction with the limit option.\ |
| 28 | 28 | Only one of "before" and "after" may be specified, and if both are |
| 29 | 29 | specified then which one takes precedence is unspecified.\ |
| 30 | 30 | CLI mode: `--after|-a` |
| 31 | 31 |
| --- www/json-api/api-finfo.md | |
| +++ www/json-api/api-finfo.md | |
| @@ -20,11 +20,11 @@ | |
| 20 | as opposed to listing all checkins. If set, neither "before" nor |
| 21 | "after" have any effect.\ |
| 22 | CLI mode: `--checkin|-ci` |
| 23 | - `before=DATETIME` only lists checkins from on or before this time.\ |
| 24 | CLI mode: `--before|-b` |
| 25 | - `after=DATETIME` only lists checkins from on or before this time. |
| 26 | Using this option swaps the sort order, to provide reasonable |
| 27 | behaviour in conjunction with the limit option.\ |
| 28 | Only one of "before" and "after" may be specified, and if both are |
| 29 | specified then which one takes precedence is unspecified.\ |
| 30 | CLI mode: `--after|-a` |
| 31 |
| --- www/json-api/api-finfo.md | |
| +++ www/json-api/api-finfo.md | |
| @@ -20,11 +20,11 @@ | |
| 20 | as opposed to listing all checkins. If set, neither "before" nor |
| 21 | "after" have any effect.\ |
| 22 | CLI mode: `--checkin|-ci` |
| 23 | - `before=DATETIME` only lists checkins from on or before this time.\ |
| 24 | CLI mode: `--before|-b` |
| 25 | - `after=DATETIME` only lists checkins from on or after this time. |
| 26 | Using this option swaps the sort order, to provide reasonable |
| 27 | behaviour in conjunction with the limit option.\ |
| 28 | Only one of "before" and "after" may be specified, and if both are |
| 29 | specified then which one takes precedence is unspecified.\ |
| 30 | CLI mode: `--after|-a` |
| 31 |
+2
-2
| --- www/json-api/api-query.md | ||
| +++ www/json-api/api-query.md | ||
| @@ -60,11 +60,11 @@ | ||
| 60 | 60 | … |
| 61 | 61 | ] |
| 62 | 62 | } |
| 63 | 63 | ``` |
| 64 | 64 | |
| 65 | -The column names are provided in a separate field is because their order | |
| 65 | +The column names are provided in a separate field because their order | |
| 66 | 66 | is guaranteed to match the order of the query columns, whereas object |
| 67 | 67 | key/value pairs might get reordered (typically sorted by key) when |
| 68 | 68 | travelling through different JSON implementations. In this manner, |
| 69 | 69 | clients can e.g. be sure to render the columns in the proper |
| 70 | 70 | (query-specified) order. |
| @@ -76,10 +76,10 @@ | ||
| 76 | 76 | |
| 77 | 77 | Note the column *names* are never *guaranteed* to be exactly as they |
| 78 | 78 | appear in the SQL *unless* they are qualified with an AS, e.g. `SELECT |
| 79 | 79 | foo AS foo...`. When generating reports which need fixed column names, it |
| 80 | 80 | is highly recommended to use an AS qualifier for every column, even if |
| 81 | -they use the same name as the column. This is the only way to guaranty | |
| 81 | +they use the same name as the column. This is the only way to guarantee | |
| 82 | 82 | that the result column names will be stable. (FYI: that behaviour comes |
| 83 | 83 | from sqlite3, not the JSON bits, and this behaviour *has* been known to |
| 84 | 84 | change between sqlite3 versions (so this is not just an idle threat of |
| 85 | 85 | *potential* future incompatibility).) |
| 86 | 86 |
| --- www/json-api/api-query.md | |
| +++ www/json-api/api-query.md | |
| @@ -60,11 +60,11 @@ | |
| 60 | … |
| 61 | ] |
| 62 | } |
| 63 | ``` |
| 64 | |
| 65 | The column names are provided in a separate field is because their order |
| 66 | is guaranteed to match the order of the query columns, whereas object |
| 67 | key/value pairs might get reordered (typically sorted by key) when |
| 68 | travelling through different JSON implementations. In this manner, |
| 69 | clients can e.g. be sure to render the columns in the proper |
| 70 | (query-specified) order. |
| @@ -76,10 +76,10 @@ | |
| 76 | |
| 77 | Note the column *names* are never *guaranteed* to be exactly as they |
| 78 | appear in the SQL *unless* they are qualified with an AS, e.g. `SELECT |
| 79 | foo AS foo...`. When generating reports which need fixed column names, it |
| 80 | is highly recommended to use an AS qualifier for every column, even if |
| 81 | they use the same name as the column. This is the only way to guaranty |
| 82 | that the result column names will be stable. (FYI: that behaviour comes |
| 83 | from sqlite3, not the JSON bits, and this behaviour *has* been known to |
| 84 | change between sqlite3 versions (so this is not just an idle threat of |
| 85 | *potential* future incompatibility).) |
| 86 |
| --- www/json-api/api-query.md | |
| +++ www/json-api/api-query.md | |
| @@ -60,11 +60,11 @@ | |
| 60 | … |
| 61 | ] |
| 62 | } |
| 63 | ``` |
| 64 | |
| 65 | The column names are provided in a separate field because their order |
| 66 | is guaranteed to match the order of the query columns, whereas object |
| 67 | key/value pairs might get reordered (typically sorted by key) when |
| 68 | travelling through different JSON implementations. In this manner, |
| 69 | clients can e.g. be sure to render the columns in the proper |
| 70 | (query-specified) order. |
| @@ -76,10 +76,10 @@ | |
| 76 | |
| 77 | Note the column *names* are never *guaranteed* to be exactly as they |
| 78 | appear in the SQL *unless* they are qualified with an AS, e.g. `SELECT |
| 79 | foo AS foo...`. When generating reports which need fixed column names, it |
| 80 | is highly recommended to use an AS qualifier for every column, even if |
| 81 | they use the same name as the column. This is the only way to guarantee |
| 82 | that the result column names will be stable. (FYI: that behaviour comes |
| 83 | from sqlite3, not the JSON bits, and this behaviour *has* been known to |
| 84 | change between sqlite3 versions (so this is not just an idle threat of |
| 85 | *potential* future incompatibility).) |
| 86 |
+2
-2
| --- www/json-api/api-tag.md | ||
| +++ www/json-api/api-tag.md | ||
| @@ -98,15 +98,15 @@ | ||
| 98 | 98 | path element. |
| 99 | 99 | - `limit=int` (defalt=0) Limits the number of results (0=no limit). |
| 100 | 100 | Since they are ordered from oldest to newest, the newest N results |
| 101 | 101 | will be returned. |
| 102 | 102 | - `type=string` (default=`*`) Searches only for the given type of |
| 103 | - artifact (using fossil's conventional type naming: ci, e, t, w. | |
| 103 | + artifact (using fossil's conventional type naming: ci, e, t, w.) | |
| 104 | 104 | - `raw=bool` (=false) If enabled, the response is an array of hashes |
| 105 | 105 | of the requested artifact type; otherwise, |
| 106 | 106 | it is an array of higher-level objects. If this is |
| 107 | - true, the "name" property is interpretted as-is. If it is false, the | |
| 107 | + true, the "name" property is interpreted as-is. If it is false, the | |
| 108 | 108 | name is automatically prepended with "sym-" (meaning a branch). |
| 109 | 109 | (FIXME: the current semantics are confusing and hard to remember. |
| 110 | 110 | Re-do them.) |
| 111 | 111 | |
| 112 | 112 | **Response payload example, in RAW mode: (expect this format to change |
| 113 | 113 |
| --- www/json-api/api-tag.md | |
| +++ www/json-api/api-tag.md | |
| @@ -98,15 +98,15 @@ | |
| 98 | path element. |
| 99 | - `limit=int` (defalt=0) Limits the number of results (0=no limit). |
| 100 | Since they are ordered from oldest to newest, the newest N results |
| 101 | will be returned. |
| 102 | - `type=string` (default=`*`) Searches only for the given type of |
| 103 | artifact (using fossil's conventional type naming: ci, e, t, w. |
| 104 | - `raw=bool` (=false) If enabled, the response is an array of hashes |
| 105 | of the requested artifact type; otherwise, |
| 106 | it is an array of higher-level objects. If this is |
| 107 | true, the "name" property is interpretted as-is. If it is false, the |
| 108 | name is automatically prepended with "sym-" (meaning a branch). |
| 109 | (FIXME: the current semantics are confusing and hard to remember. |
| 110 | Re-do them.) |
| 111 | |
| 112 | **Response payload example, in RAW mode: (expect this format to change |
| 113 |
| --- www/json-api/api-tag.md | |
| +++ www/json-api/api-tag.md | |
| @@ -98,15 +98,15 @@ | |
| 98 | path element. |
| 99 | - `limit=int` (defalt=0) Limits the number of results (0=no limit). |
| 100 | Since they are ordered from oldest to newest, the newest N results |
| 101 | will be returned. |
| 102 | - `type=string` (default=`*`) Searches only for the given type of |
| 103 | artifact (using fossil's conventional type naming: ci, e, t, w.) |
| 104 | - `raw=bool` (=false) If enabled, the response is an array of hashes |
| 105 | of the requested artifact type; otherwise, |
| 106 | it is an array of higher-level objects. If this is |
| 107 | true, the "name" property is interpreted as-is. If it is false, the |
| 108 | name is automatically prepended with "sym-" (meaning a branch). |
| 109 | (FIXME: the current semantics are confusing and hard to remember. |
| 110 | Re-do them.) |
| 111 | |
| 112 | **Response payload example, in RAW mode: (expect this format to change |
| 113 |
+8
-8
| --- www/json-api/conventions.md | ||
| +++ www/json-api/conventions.md | ||
| @@ -100,11 +100,11 @@ | ||
| 100 | 100 | |
| 101 | 101 | The API allows most of those (normally all but the payload) to come in |
| 102 | 102 | as either GET parameters or properties of the top-level POSTed request |
| 103 | 103 | JSON envelope, with GET taking priority over POST. (Reminder to self: we |
| 104 | 104 | could potentially also use values from cookies. Fossil currently only |
| 105 | -uses 1 cookie (the login token), and i'd prefer to keep it that way.) | |
| 105 | +uses 1 cookie (the login token), and I'd prefer to keep it that way.) | |
| 106 | 106 | |
| 107 | 107 | POST requests without such an envelope will be rejected, generating a |
| 108 | 108 | Fossil/JSON error response (as opposed to an HTTP error response). GET |
| 109 | 109 | requests, by definition, never have an envelope. |
| 110 | 110 | |
| @@ -169,11 +169,11 @@ | ||
| 169 | 169 | No higher-level constructs, e.g. JSON **arrays** or **objects**, are |
| 170 | 170 | accepted in string form. Such parameters must be set in the POST |
| 171 | 171 | envelope or payload, as specified by the specific API. |
| 172 | 172 | |
| 173 | 173 | This API does not currently use any **floating-point** parameters, but |
| 174 | -does return floating-point results in a couple places. | |
| 174 | +does return floating-point results in a couple of places. | |
| 175 | 175 | |
| 176 | 176 | For **integer** parameters we use a conventional string-to-int algorithm |
| 177 | 177 | and assume base 10 (analog to atoi(3)). The API may err on the side of |
| 178 | 178 | usability when given technically invalid values. e.g. "123abc" will |
| 179 | 179 | likely be interpreted as the integer 123. No APIs currently rely on |
| @@ -205,11 +205,11 @@ | ||
| 205 | 205 | cause inconsistencies vis-a-vis the HTML interface). |
| 206 | 206 | |
| 207 | 207 | <a id="response-envelope"></a> |
| 208 | 208 | # Response Envelope |
| 209 | 209 | |
| 210 | -Every response comes in the form of a HTTP response or (in CLI mode) | |
| 210 | +Every response comes in the form of an HTTP response or (in CLI mode) | |
| 211 | 211 | JSON sent to stdout. The body of the response is a JSON object following |
| 212 | 212 | a common envelope format. The envelope has the following properties: |
| 213 | 213 | |
| 214 | 214 | |
| 215 | 215 | - `fossil`: Fossil server version string. This property is basically |
| @@ -216,11 +216,11 @@ | ||
| 216 | 216 | "the official response envelope marker" - if it is set, clients can |
| 217 | 217 | "probably safely assume" that the object indeed came from one of the |
| 218 | 218 | Fossil/JSON APIs. This API never creates responses which do not |
| 219 | 219 | contain this property. |
| 220 | 220 | - `requestId`: Only set if the request contained it, and then it is |
| 221 | - echoed back to the caller as-is. This can be use to determine | |
| 221 | + echoed back to the caller as-is. This can be used to determine | |
| 222 | 222 | (client-side) which request a given response is coming in for |
| 223 | 223 | (assuming multiple asynchronous requests are pending). In practice |
| 224 | 224 | this generally isn’t needed because response handling tends to be |
| 225 | 225 | done by closures associated with the original request object (at |
| 226 | 226 | least in JavaScript code). In languages without closures it might |
| @@ -235,11 +235,11 @@ | ||
| 235 | 235 | - `payload`: Request-specific response payload (data type/structure is |
| 236 | 236 | request-specific). The payload is never set for error responses, |
| 237 | 237 | only for success responses (and only those which actually have a |
| 238 | 238 | payload - not all do). |
| 239 | 239 | - `timestamp`: Response timestamp (GMT Unix Epoch). We use seconds |
| 240 | - precision because i did not know at the time that Fossil actually | |
| 240 | + precision because I did not know at the time that Fossil actually | |
| 241 | 241 | records millisecond precision. |
| 242 | 242 | - `payloadVersion`: Not initially needed, but reserved for future use |
| 243 | 243 | in maintaining version compatibility when the format of a given |
| 244 | 244 | response type's payload changes. If needed, the "first version" |
| 245 | 245 | value is assumed to be 0, for semantic [near-]compatibility with the |
| @@ -402,11 +402,11 @@ | ||
| 402 | 402 | (they are represented by `\t`) there is no chance that such a global |
| 403 | 403 | replacement will corrupt JSON string contents - only the formatting will |
| 404 | 404 | be affected. |
| 405 | 405 | |
| 406 | 406 | Potential TODO: because extraneous indention "could potentially" be used |
| 407 | -as a form DoS, the option *might* be subject to later removed from HTTP | |
| 407 | +as a form of DoS, the option *might* be subject to later removal from HTTP | |
| 408 | 408 | mode (in CLI it's fine). |
| 409 | 409 | |
| 410 | 410 | In HTTP mode no trailing newline is added to the output, whereas in CLI |
| 411 | 411 | mode one is normally appended (exception: in JSONP mode no newline is |
| 412 | 412 | appended, to (rather pedantically and arbitraily) allow the client to |
| @@ -482,11 +482,11 @@ | ||
| 482 | 482 | normally want to see very specific error codes when tracking down a |
| 483 | 483 | problem. We can offer a configuration option to "dumb down" error |
| 484 | 484 | codes to their generic category by simply doing a modulo 100 |
| 485 | 485 | (or 1000) against the native error code number. e.g. FOSSIL-1271 |
| 486 | 486 | could (via a simple modulo) be reduced to FOSSIL-1200 or |
| 487 | - FOSSIL-1000, depending on the paranoia level of the sysadmin. i have | |
| 487 | + FOSSIL-1000, depending on the paranoia level of the sysadmin. I have | |
| 488 | 488 | tried to order the result code numbers so that a dumb-down level of |
| 489 | 489 | 2 provides reasonably usable results without giving away too much |
| 490 | 490 | detail to malicious clients.\ |
| 491 | 491 | (**TODO:** `g.json.errorDetailParanoia` is used to set the |
| 492 | 492 | default dumb-down level, but it is currently set at compile-time. |
| @@ -533,11 +533,11 @@ | ||
| 533 | 533 | can actually implement this one, though.) |
| 534 | 534 | - `FOSSIL-1106`: Assertion failed (or would have had we |
| 535 | 535 | continued). Note: if an `assert()` fails in CGI/server modes, the HTTP |
| 536 | 536 | response will be code 500 (Internal Server Error). We want to avoid |
| 537 | 537 | that and return a JSON response instead. All of that said - there seems |
| 538 | - to be little reason to implementi this, since assertions are "truly | |
| 538 | + to be little reason to implement this, since assertions are "truly | |
| 539 | 539 | serious" errors. |
| 540 | 540 | - `FOSSIL-1107`: Allocation/out of memory error. This cannot be reasonably |
| 541 | 541 | reported because fossil aborts if an allocation fails. |
| 542 | 542 | - `FOSSIL-1108`: Requested API is not yet implemented. |
| 543 | 543 | - `FOSSIL-1109`: Panic! Fossil's `fossil_panic()` or `cgi_panic()` was |
| 544 | 544 |
| --- www/json-api/conventions.md | |
| +++ www/json-api/conventions.md | |
| @@ -100,11 +100,11 @@ | |
| 100 | |
| 101 | The API allows most of those (normally all but the payload) to come in |
| 102 | as either GET parameters or properties of the top-level POSTed request |
| 103 | JSON envelope, with GET taking priority over POST. (Reminder to self: we |
| 104 | could potentially also use values from cookies. Fossil currently only |
| 105 | uses 1 cookie (the login token), and i'd prefer to keep it that way.) |
| 106 | |
| 107 | POST requests without such an envelope will be rejected, generating a |
| 108 | Fossil/JSON error response (as opposed to an HTTP error response). GET |
| 109 | requests, by definition, never have an envelope. |
| 110 | |
| @@ -169,11 +169,11 @@ | |
| 169 | No higher-level constructs, e.g. JSON **arrays** or **objects**, are |
| 170 | accepted in string form. Such parameters must be set in the POST |
| 171 | envelope or payload, as specified by the specific API. |
| 172 | |
| 173 | This API does not currently use any **floating-point** parameters, but |
| 174 | does return floating-point results in a couple places. |
| 175 | |
| 176 | For **integer** parameters we use a conventional string-to-int algorithm |
| 177 | and assume base 10 (analog to atoi(3)). The API may err on the side of |
| 178 | usability when given technically invalid values. e.g. "123abc" will |
| 179 | likely be interpreted as the integer 123. No APIs currently rely on |
| @@ -205,11 +205,11 @@ | |
| 205 | cause inconsistencies vis-a-vis the HTML interface). |
| 206 | |
| 207 | <a id="response-envelope"></a> |
| 208 | # Response Envelope |
| 209 | |
| 210 | Every response comes in the form of a HTTP response or (in CLI mode) |
| 211 | JSON sent to stdout. The body of the response is a JSON object following |
| 212 | a common envelope format. The envelope has the following properties: |
| 213 | |
| 214 | |
| 215 | - `fossil`: Fossil server version string. This property is basically |
| @@ -216,11 +216,11 @@ | |
| 216 | "the official response envelope marker" - if it is set, clients can |
| 217 | "probably safely assume" that the object indeed came from one of the |
| 218 | Fossil/JSON APIs. This API never creates responses which do not |
| 219 | contain this property. |
| 220 | - `requestId`: Only set if the request contained it, and then it is |
| 221 | echoed back to the caller as-is. This can be use to determine |
| 222 | (client-side) which request a given response is coming in for |
| 223 | (assuming multiple asynchronous requests are pending). In practice |
| 224 | this generally isn’t needed because response handling tends to be |
| 225 | done by closures associated with the original request object (at |
| 226 | least in JavaScript code). In languages without closures it might |
| @@ -235,11 +235,11 @@ | |
| 235 | - `payload`: Request-specific response payload (data type/structure is |
| 236 | request-specific). The payload is never set for error responses, |
| 237 | only for success responses (and only those which actually have a |
| 238 | payload - not all do). |
| 239 | - `timestamp`: Response timestamp (GMT Unix Epoch). We use seconds |
| 240 | precision because i did not know at the time that Fossil actually |
| 241 | records millisecond precision. |
| 242 | - `payloadVersion`: Not initially needed, but reserved for future use |
| 243 | in maintaining version compatibility when the format of a given |
| 244 | response type's payload changes. If needed, the "first version" |
| 245 | value is assumed to be 0, for semantic [near-]compatibility with the |
| @@ -402,11 +402,11 @@ | |
| 402 | (they are represented by `\t`) there is no chance that such a global |
| 403 | replacement will corrupt JSON string contents - only the formatting will |
| 404 | be affected. |
| 405 | |
| 406 | Potential TODO: because extraneous indention "could potentially" be used |
| 407 | as a form DoS, the option *might* be subject to later removed from HTTP |
| 408 | mode (in CLI it's fine). |
| 409 | |
| 410 | In HTTP mode no trailing newline is added to the output, whereas in CLI |
| 411 | mode one is normally appended (exception: in JSONP mode no newline is |
| 412 | appended, to (rather pedantically and arbitraily) allow the client to |
| @@ -482,11 +482,11 @@ | |
| 482 | normally want to see very specific error codes when tracking down a |
| 483 | problem. We can offer a configuration option to "dumb down" error |
| 484 | codes to their generic category by simply doing a modulo 100 |
| 485 | (or 1000) against the native error code number. e.g. FOSSIL-1271 |
| 486 | could (via a simple modulo) be reduced to FOSSIL-1200 or |
| 487 | FOSSIL-1000, depending on the paranoia level of the sysadmin. i have |
| 488 | tried to order the result code numbers so that a dumb-down level of |
| 489 | 2 provides reasonably usable results without giving away too much |
| 490 | detail to malicious clients.\ |
| 491 | (**TODO:** `g.json.errorDetailParanoia` is used to set the |
| 492 | default dumb-down level, but it is currently set at compile-time. |
| @@ -533,11 +533,11 @@ | |
| 533 | can actually implement this one, though.) |
| 534 | - `FOSSIL-1106`: Assertion failed (or would have had we |
| 535 | continued). Note: if an `assert()` fails in CGI/server modes, the HTTP |
| 536 | response will be code 500 (Internal Server Error). We want to avoid |
| 537 | that and return a JSON response instead. All of that said - there seems |
| 538 | to be little reason to implementi this, since assertions are "truly |
| 539 | serious" errors. |
| 540 | - `FOSSIL-1107`: Allocation/out of memory error. This cannot be reasonably |
| 541 | reported because fossil aborts if an allocation fails. |
| 542 | - `FOSSIL-1108`: Requested API is not yet implemented. |
| 543 | - `FOSSIL-1109`: Panic! Fossil's `fossil_panic()` or `cgi_panic()` was |
| 544 |
| --- www/json-api/conventions.md | |
| +++ www/json-api/conventions.md | |
| @@ -100,11 +100,11 @@ | |
| 100 | |
| 101 | The API allows most of those (normally all but the payload) to come in |
| 102 | as either GET parameters or properties of the top-level POSTed request |
| 103 | JSON envelope, with GET taking priority over POST. (Reminder to self: we |
| 104 | could potentially also use values from cookies. Fossil currently only |
| 105 | uses 1 cookie (the login token), and I'd prefer to keep it that way.) |
| 106 | |
| 107 | POST requests without such an envelope will be rejected, generating a |
| 108 | Fossil/JSON error response (as opposed to an HTTP error response). GET |
| 109 | requests, by definition, never have an envelope. |
| 110 | |
| @@ -169,11 +169,11 @@ | |
| 169 | No higher-level constructs, e.g. JSON **arrays** or **objects**, are |
| 170 | accepted in string form. Such parameters must be set in the POST |
| 171 | envelope or payload, as specified by the specific API. |
| 172 | |
| 173 | This API does not currently use any **floating-point** parameters, but |
| 174 | does return floating-point results in a couple of places. |
| 175 | |
| 176 | For **integer** parameters we use a conventional string-to-int algorithm |
| 177 | and assume base 10 (analog to atoi(3)). The API may err on the side of |
| 178 | usability when given technically invalid values. e.g. "123abc" will |
| 179 | likely be interpreted as the integer 123. No APIs currently rely on |
| @@ -205,11 +205,11 @@ | |
| 205 | cause inconsistencies vis-a-vis the HTML interface). |
| 206 | |
| 207 | <a id="response-envelope"></a> |
| 208 | # Response Envelope |
| 209 | |
| 210 | Every response comes in the form of an HTTP response or (in CLI mode) |
| 211 | JSON sent to stdout. The body of the response is a JSON object following |
| 212 | a common envelope format. The envelope has the following properties: |
| 213 | |
| 214 | |
| 215 | - `fossil`: Fossil server version string. This property is basically |
| @@ -216,11 +216,11 @@ | |
| 216 | "the official response envelope marker" - if it is set, clients can |
| 217 | "probably safely assume" that the object indeed came from one of the |
| 218 | Fossil/JSON APIs. This API never creates responses which do not |
| 219 | contain this property. |
| 220 | - `requestId`: Only set if the request contained it, and then it is |
| 221 | echoed back to the caller as-is. This can be used to determine |
| 222 | (client-side) which request a given response is coming in for |
| 223 | (assuming multiple asynchronous requests are pending). In practice |
| 224 | this generally isn’t needed because response handling tends to be |
| 225 | done by closures associated with the original request object (at |
| 226 | least in JavaScript code). In languages without closures it might |
| @@ -235,11 +235,11 @@ | |
| 235 | - `payload`: Request-specific response payload (data type/structure is |
| 236 | request-specific). The payload is never set for error responses, |
| 237 | only for success responses (and only those which actually have a |
| 238 | payload - not all do). |
| 239 | - `timestamp`: Response timestamp (GMT Unix Epoch). We use seconds |
| 240 | precision because I did not know at the time that Fossil actually |
| 241 | records millisecond precision. |
| 242 | - `payloadVersion`: Not initially needed, but reserved for future use |
| 243 | in maintaining version compatibility when the format of a given |
| 244 | response type's payload changes. If needed, the "first version" |
| 245 | value is assumed to be 0, for semantic [near-]compatibility with the |
| @@ -402,11 +402,11 @@ | |
| 402 | (they are represented by `\t`) there is no chance that such a global |
| 403 | replacement will corrupt JSON string contents - only the formatting will |
| 404 | be affected. |
| 405 | |
| 406 | Potential TODO: because extraneous indention "could potentially" be used |
| 407 | as a form of DoS, the option *might* be subject to later removal from HTTP |
| 408 | mode (in CLI it's fine). |
| 409 | |
| 410 | In HTTP mode no trailing newline is added to the output, whereas in CLI |
| 411 | mode one is normally appended (exception: in JSONP mode no newline is |
| 412 | appended, to (rather pedantically and arbitraily) allow the client to |
| @@ -482,11 +482,11 @@ | |
| 482 | normally want to see very specific error codes when tracking down a |
| 483 | problem. We can offer a configuration option to "dumb down" error |
| 484 | codes to their generic category by simply doing a modulo 100 |
| 485 | (or 1000) against the native error code number. e.g. FOSSIL-1271 |
| 486 | could (via a simple modulo) be reduced to FOSSIL-1200 or |
| 487 | FOSSIL-1000, depending on the paranoia level of the sysadmin. I have |
| 488 | tried to order the result code numbers so that a dumb-down level of |
| 489 | 2 provides reasonably usable results without giving away too much |
| 490 | detail to malicious clients.\ |
| 491 | (**TODO:** `g.json.errorDetailParanoia` is used to set the |
| 492 | default dumb-down level, but it is currently set at compile-time. |
| @@ -533,11 +533,11 @@ | |
| 533 | can actually implement this one, though.) |
| 534 | - `FOSSIL-1106`: Assertion failed (or would have had we |
| 535 | continued). Note: if an `assert()` fails in CGI/server modes, the HTTP |
| 536 | response will be code 500 (Internal Server Error). We want to avoid |
| 537 | that and return a JSON response instead. All of that said - there seems |
| 538 | to be little reason to implement this, since assertions are "truly |
| 539 | serious" errors. |
| 540 | - `FOSSIL-1107`: Allocation/out of memory error. This cannot be reasonably |
| 541 | reported because fossil aborts if an allocation fails. |
| 542 | - `FOSSIL-1108`: Requested API is not yet implemented. |
| 543 | - `FOSSIL-1109`: Panic! Fossil's `fossil_panic()` or `cgi_panic()` was |
| 544 |
+7
-7
| --- www/json-api/hacking.md | ||
| +++ www/json-api/hacking.md | ||
| @@ -79,11 +79,11 @@ | ||
| 79 | 79 | core. The disadvantages of this are that we lose fossil's conventional |
| 80 | 80 | help text mechanism (which is based on code comments in the |
| 81 | 81 | command/path's dispatcher impl) and the ability to write abbreviated |
| 82 | 82 | command names in CLI mode ("json" itself may be abbreviated, but not the |
| 83 | 83 | subcommands). The advantages are that we can handle CLI/HTTP modes |
| 84 | -almost identically (there are a couple minor differences) by unifying | |
| 84 | +almost identically (there are a couple of minor differences) by unifying | |
| 85 | 85 | them under the same callback functions much more easily. |
| 86 | 86 | |
| 87 | 87 | The top-level "json" command/path uses its own dispatching mechanism |
| 88 | 88 | which uses either the path (in HTTP mode) or CLI positional arguments to |
| 89 | 89 | dispatch commands (stopping at the first "flag option" (e.g. -foo) in |
| @@ -125,11 +125,11 @@ | ||
| 125 | 125 | (e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` |
| 126 | 126 | if they really want to). One exception is `fossil_exit()`, which does |
| 127 | 127 | _not_ generate any extra output and will `exit()` the app. In the JSON |
| 128 | 128 | API, as a rule of thumb, `fossil_exit()` is only used when we *want* a |
| 129 | 129 | failed request to cause an HTTP 500 error, and it is reserved for |
| 130 | -allocation errors and similar truly catostrophic failures. That said... | |
| 130 | +allocation errors and similar truly catastrophic failures. That said... | |
| 131 | 131 | libcson has been hacked to use `fossil_alloc()` and friends for memory |
| 132 | 132 | management, and those routines exit on error, so alloc error handling in |
| 133 | 133 | the JSON command handler code can afford to be a little lax (the |
| 134 | 134 | majority of *potential* errors clients get from the cson API have |
| 135 | 135 | allocation failure as their root cause). |
| @@ -259,11 +259,11 @@ | ||
| 259 | 259 | - `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON |
| 260 | 260 | environment," which is a superset of the GET/POST/`POST.payload` (if |
| 261 | 261 | `POST.payload` is-a Object). |
| 262 | 262 | - `json_find_option_TYPE()`: searches the CLI args (only when in CLI |
| 263 | 263 | mode) and the JSON environment. |
| 264 | -- The use of fossil's `P()` and `PD()` macros is discourages in JSON | |
| 264 | +- The use of fossil's `P()` and `PD()` macros is discouraged in JSON | |
| 265 | 265 | callbacks because they can only handle String data from the CLI or |
| 266 | 266 | GET parameters (not POST/`POST.payload`). (Note that `P()` and `PD()` |
| 267 | 267 | *normally* also handle POSTed keys, but they only "see" values |
| 268 | 268 | posted as form-urlencoded fields, and not JSON format.) |
| 269 | 269 | - `find_option()` (from `src/main.c`) "should" also be avoided in |
| @@ -280,11 +280,11 @@ | ||
| 280 | 280 | |
| 281 | 281 | <a href="creating-json-values"></a> |
| 282 | 282 | ## Creating JSON Values |
| 283 | 283 | |
| 284 | 284 | cson has a fairly rich API for creating and manipulating the various |
| 285 | -JSON-defined value types. For a detailed overview and demonstration i | |
| 285 | +JSON-defined value types. For a detailed overview and demonstration I | |
| 286 | 286 | recommend reading: |
| 287 | 287 | |
| 288 | 288 | [](https://fossil.wanderinghorse.net/wikis/cson/?page=HowTo) |
| 289 | 289 | |
| 290 | 290 | That said, the Fossil/JSON API has several convenience wrappers to save |
| @@ -310,15 +310,15 @@ | ||
| 310 | 310 | to Arrays or Objects, or convert single columns to a JSON-compatible |
| 311 | 311 | form. See `json_stmt_to_array_of_obj()`, |
| 312 | 312 | `json_stmt_to_array_of_array()` (both in `src/json.c`), and |
| 313 | 313 | `cson_sqlite3_column_to_value()` and friends (in |
| 314 | 314 | `extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric |
| 315 | -types, but they optimistically/natively *assume* that any fields of type | |
| 315 | +types, but they optimistically/naively *assume* that any fields of type | |
| 316 | 316 | TEXT or BLOB are actually UTF8 data, and treat them as such. cson's |
| 317 | 317 | string class only handles UTF8 data and it is semantically illegal to |
| 318 | 318 | feed them anything but UTF8. Violating this will likely result in |
| 319 | -down-stream errors (e.g. when emiting the JSON string output). **The | |
| 319 | +down-stream errors (e.g. when emitting the JSON string output). **The | |
| 320 | 320 | moral of this story is:** *do not use these APIs to fetch binary data*. |
| 321 | 321 | JSON doesn't do binary and the `cson_string` class does not |
| 322 | 322 | protect itself against clients feeding it non-UTF8 data. |
| 323 | 323 | |
| 324 | 324 | Here's a basic example of using these features: |
| @@ -347,7 +347,7 @@ | ||
| 347 | 347 | AS clauses to be guaranteed that the db driver will return the column |
| 348 | 348 | names we want. Note that the AS clause is often used to translate column |
| 349 | 349 | names into something more JSON-conventional or user-friendly, e.g. |
| 350 | 350 | "SELECT cap AS capabilities...". Alternately, we can convert the |
| 351 | 351 | individual `sqlite3_stmt` column values to JSON using |
| 352 | -`cson_sqlite3_column_to_value()`, without refering directly to the | |
| 352 | +`cson_sqlite3_column_to_value()`, without referring directly to the | |
| 353 | 353 | db-reported column name. |
| 354 | 354 |
| --- www/json-api/hacking.md | |
| +++ www/json-api/hacking.md | |
| @@ -79,11 +79,11 @@ | |
| 79 | core. The disadvantages of this are that we lose fossil's conventional |
| 80 | help text mechanism (which is based on code comments in the |
| 81 | command/path's dispatcher impl) and the ability to write abbreviated |
| 82 | command names in CLI mode ("json" itself may be abbreviated, but not the |
| 83 | subcommands). The advantages are that we can handle CLI/HTTP modes |
| 84 | almost identically (there are a couple minor differences) by unifying |
| 85 | them under the same callback functions much more easily. |
| 86 | |
| 87 | The top-level "json" command/path uses its own dispatching mechanism |
| 88 | which uses either the path (in HTTP mode) or CLI positional arguments to |
| 89 | dispatch commands (stopping at the first "flag option" (e.g. -foo) in |
| @@ -125,11 +125,11 @@ | |
| 125 | (e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` |
| 126 | if they really want to). One exception is `fossil_exit()`, which does |
| 127 | _not_ generate any extra output and will `exit()` the app. In the JSON |
| 128 | API, as a rule of thumb, `fossil_exit()` is only used when we *want* a |
| 129 | failed request to cause an HTTP 500 error, and it is reserved for |
| 130 | allocation errors and similar truly catostrophic failures. That said... |
| 131 | libcson has been hacked to use `fossil_alloc()` and friends for memory |
| 132 | management, and those routines exit on error, so alloc error handling in |
| 133 | the JSON command handler code can afford to be a little lax (the |
| 134 | majority of *potential* errors clients get from the cson API have |
| 135 | allocation failure as their root cause). |
| @@ -259,11 +259,11 @@ | |
| 259 | - `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON |
| 260 | environment," which is a superset of the GET/POST/`POST.payload` (if |
| 261 | `POST.payload` is-a Object). |
| 262 | - `json_find_option_TYPE()`: searches the CLI args (only when in CLI |
| 263 | mode) and the JSON environment. |
| 264 | - The use of fossil's `P()` and `PD()` macros is discourages in JSON |
| 265 | callbacks because they can only handle String data from the CLI or |
| 266 | GET parameters (not POST/`POST.payload`). (Note that `P()` and `PD()` |
| 267 | *normally* also handle POSTed keys, but they only "see" values |
| 268 | posted as form-urlencoded fields, and not JSON format.) |
| 269 | - `find_option()` (from `src/main.c`) "should" also be avoided in |
| @@ -280,11 +280,11 @@ | |
| 280 | |
| 281 | <a href="creating-json-values"></a> |
| 282 | ## Creating JSON Values |
| 283 | |
| 284 | cson has a fairly rich API for creating and manipulating the various |
| 285 | JSON-defined value types. For a detailed overview and demonstration i |
| 286 | recommend reading: |
| 287 | |
| 288 | [](https://fossil.wanderinghorse.net/wikis/cson/?page=HowTo) |
| 289 | |
| 290 | That said, the Fossil/JSON API has several convenience wrappers to save |
| @@ -310,15 +310,15 @@ | |
| 310 | to Arrays or Objects, or convert single columns to a JSON-compatible |
| 311 | form. See `json_stmt_to_array_of_obj()`, |
| 312 | `json_stmt_to_array_of_array()` (both in `src/json.c`), and |
| 313 | `cson_sqlite3_column_to_value()` and friends (in |
| 314 | `extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric |
| 315 | types, but they optimistically/natively *assume* that any fields of type |
| 316 | TEXT or BLOB are actually UTF8 data, and treat them as such. cson's |
| 317 | string class only handles UTF8 data and it is semantically illegal to |
| 318 | feed them anything but UTF8. Violating this will likely result in |
| 319 | down-stream errors (e.g. when emiting the JSON string output). **The |
| 320 | moral of this story is:** *do not use these APIs to fetch binary data*. |
| 321 | JSON doesn't do binary and the `cson_string` class does not |
| 322 | protect itself against clients feeding it non-UTF8 data. |
| 323 | |
| 324 | Here's a basic example of using these features: |
| @@ -347,7 +347,7 @@ | |
| 347 | AS clauses to be guaranteed that the db driver will return the column |
| 348 | names we want. Note that the AS clause is often used to translate column |
| 349 | names into something more JSON-conventional or user-friendly, e.g. |
| 350 | "SELECT cap AS capabilities...". Alternately, we can convert the |
| 351 | individual `sqlite3_stmt` column values to JSON using |
| 352 | `cson_sqlite3_column_to_value()`, without refering directly to the |
| 353 | db-reported column name. |
| 354 |
| --- www/json-api/hacking.md | |
| +++ www/json-api/hacking.md | |
| @@ -79,11 +79,11 @@ | |
| 79 | core. The disadvantages of this are that we lose fossil's conventional |
| 80 | help text mechanism (which is based on code comments in the |
| 81 | command/path's dispatcher impl) and the ability to write abbreviated |
| 82 | command names in CLI mode ("json" itself may be abbreviated, but not the |
| 83 | subcommands). The advantages are that we can handle CLI/HTTP modes |
| 84 | almost identically (there are a couple of minor differences) by unifying |
| 85 | them under the same callback functions much more easily. |
| 86 | |
| 87 | The top-level "json" command/path uses its own dispatching mechanism |
| 88 | which uses either the path (in HTTP mode) or CLI positional arguments to |
| 89 | dispatch commands (stopping at the first "flag option" (e.g. -foo) in |
| @@ -125,11 +125,11 @@ | |
| 125 | (e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` |
| 126 | if they really want to). One exception is `fossil_exit()`, which does |
| 127 | _not_ generate any extra output and will `exit()` the app. In the JSON |
| 128 | API, as a rule of thumb, `fossil_exit()` is only used when we *want* a |
| 129 | failed request to cause an HTTP 500 error, and it is reserved for |
| 130 | allocation errors and similar truly catastrophic failures. That said... |
| 131 | libcson has been hacked to use `fossil_alloc()` and friends for memory |
| 132 | management, and those routines exit on error, so alloc error handling in |
| 133 | the JSON command handler code can afford to be a little lax (the |
| 134 | majority of *potential* errors clients get from the cson API have |
| 135 | allocation failure as their root cause). |
| @@ -259,11 +259,11 @@ | |
| 259 | - `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON |
| 260 | environment," which is a superset of the GET/POST/`POST.payload` (if |
| 261 | `POST.payload` is-a Object). |
| 262 | - `json_find_option_TYPE()`: searches the CLI args (only when in CLI |
| 263 | mode) and the JSON environment. |
| 264 | - The use of fossil's `P()` and `PD()` macros is discouraged in JSON |
| 265 | callbacks because they can only handle String data from the CLI or |
| 266 | GET parameters (not POST/`POST.payload`). (Note that `P()` and `PD()` |
| 267 | *normally* also handle POSTed keys, but they only "see" values |
| 268 | posted as form-urlencoded fields, and not JSON format.) |
| 269 | - `find_option()` (from `src/main.c`) "should" also be avoided in |
| @@ -280,11 +280,11 @@ | |
| 280 | |
| 281 | <a href="creating-json-values"></a> |
| 282 | ## Creating JSON Values |
| 283 | |
| 284 | cson has a fairly rich API for creating and manipulating the various |
| 285 | JSON-defined value types. For a detailed overview and demonstration I |
| 286 | recommend reading: |
| 287 | |
| 288 | [](https://fossil.wanderinghorse.net/wikis/cson/?page=HowTo) |
| 289 | |
| 290 | That said, the Fossil/JSON API has several convenience wrappers to save |
| @@ -310,15 +310,15 @@ | |
| 310 | to Arrays or Objects, or convert single columns to a JSON-compatible |
| 311 | form. See `json_stmt_to_array_of_obj()`, |
| 312 | `json_stmt_to_array_of_array()` (both in `src/json.c`), and |
| 313 | `cson_sqlite3_column_to_value()` and friends (in |
| 314 | `extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric |
| 315 | types, but they optimistically/naively *assume* that any fields of type |
| 316 | TEXT or BLOB are actually UTF8 data, and treat them as such. cson's |
| 317 | string class only handles UTF8 data and it is semantically illegal to |
| 318 | feed them anything but UTF8. Violating this will likely result in |
| 319 | down-stream errors (e.g. when emitting the JSON string output). **The |
| 320 | moral of this story is:** *do not use these APIs to fetch binary data*. |
| 321 | JSON doesn't do binary and the `cson_string` class does not |
| 322 | protect itself against clients feeding it non-UTF8 data. |
| 323 | |
| 324 | Here's a basic example of using these features: |
| @@ -347,7 +347,7 @@ | |
| 347 | AS clauses to be guaranteed that the db driver will return the column |
| 348 | names we want. Note that the AS clause is often used to translate column |
| 349 | names into something more JSON-conventional or user-friendly, e.g. |
| 350 | "SELECT cap AS capabilities...". Alternately, we can convert the |
| 351 | individual `sqlite3_stmt` column values to JSON using |
| 352 | `cson_sqlite3_column_to_value()`, without referring directly to the |
| 353 | db-reported column name. |
| 354 |
+10
-9
| --- www/makefile.wiki | ||
| +++ www/makefile.wiki | ||
| @@ -7,11 +7,11 @@ | ||
| 7 | 7 | before it is compiled. Most users will download a |
| 8 | 8 | [https://fossil-scm.org/home/uv/download.html | precompiled binary] |
| 9 | 9 | so this is of no consequence to them, and even those who |
| 10 | 10 | want to compile the code themselves can use one of the |
| 11 | 11 | [./build.wiki | existing makefiles]. |
| 12 | -So must people do not need to be concerned with the | |
| 12 | +So most people do not need to be concerned with the | |
| 13 | 13 | build complexities of Fossil. But hard-core developers who desire |
| 14 | 14 | a deep understanding of how Fossil is put together can benefit |
| 15 | 15 | from reviewing this article. |
| 16 | 16 | |
| 17 | 17 | <h1 id="srctour">2.0 Source Code Tour</h1> |
| @@ -56,21 +56,21 @@ | ||
| 56 | 56 | The TH1 script engine is implemented using files: |
| 57 | 57 | |
| 58 | 58 | 9. th.c |
| 59 | 59 | 10. th.h |
| 60 | 60 | |
| 61 | -The proprocessing steps are omitted for all of these imported | |
| 61 | +The preprocessing steps are omitted for all of these imported | |
| 62 | 62 | files. |
| 63 | 63 | |
| 64 | 64 | The VERSION.h header file is generated from other information sources |
| 65 | 65 | using a small program called: |
| 66 | 66 | |
| 67 | 67 | 11. [/file/tools/mkversion.c | mkversion.c] |
| 68 | 68 | |
| 69 | 69 | The builtin_data.h header file contains the definitions of C-language |
| 70 | 70 | byte-array constants that contain various resources such as scripts and |
| 71 | -images. The builtin_data.h header file is generate from the original | |
| 71 | +images. The builtin_data.h header file is generated from the original | |
| 72 | 72 | resource files using a small program called: |
| 73 | 73 | |
| 74 | 74 | 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] |
| 75 | 75 | |
| 76 | 76 | Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] |
| @@ -132,11 +132,11 @@ | ||
| 132 | 132 | the exceptions described above. |
| 133 | 133 | |
| 134 | 134 | <h1>3.0 Automatically generated files</h1> |
| 135 | 135 | |
| 136 | 136 | The "VERSION.h" header file contains some C preprocessor macros that |
| 137 | -identify the version of Fossil that is to be build. The VERSION.h file is | |
| 137 | +identify the version of Fossil that is to be built. The VERSION.h file is | |
| 138 | 138 | generated automatically from information extracted from the "manifest", |
| 139 | 139 | "manifest.uuid", and "VERSION" source files in the root directory of the |
| 140 | 140 | source tree. |
| 141 | 141 | (The "manifest" and "manifest.uuid" files are automatically generated and |
| 142 | 142 | updated by Fossil itself. See the [/help/setting | fossil set manifest] |
| @@ -294,12 +294,12 @@ | ||
| 294 | 294 | link against the standard C library. No other libraries or external |
| 295 | 295 | dependences are used. |
| 296 | 296 | |
| 297 | 297 | <h1>7.0 Debugging</h1> |
| 298 | 298 | |
| 299 | -Debug mode is controlled via FOSSIL_DEBUG preprocessor macro which could be | |
| 300 | -set explicitly at the make command for the target platform. | |
| 299 | +Debug mode is controlled via a FOSSIL_DEBUG preprocessor macro. This can be | |
| 300 | +set explicitly with the make command for the target platform. | |
| 301 | 301 | |
| 302 | 302 | However, in practice it is instead recommended to add a respective configure |
| 303 | 303 | option for the target platform and then perform a clean build. This way the |
| 304 | 304 | Debug flags are consistently applied across the whole build process. For |
| 305 | 305 | example, use these Debug flags in addition to other flags passed to the |
| @@ -313,14 +313,15 @@ | ||
| 313 | 313 | On Windows: |
| 314 | 314 | <pre> |
| 315 | 315 | win\buildmsvc.bat FOSSIL_DEBUG=1 |
| 316 | 316 | </pre> |
| 317 | 317 | |
| 318 | -The resulting fossil binary could then be loaded into a platform-specific | |
| 318 | +The resulting fossil binary can then be loaded into a platform-specific | |
| 319 | 319 | debugger. Source files displayed in the debugger correspond to the ones |
| 320 | -generated from the translation stage of the build process, that is what was | |
| 321 | -actually compiled into the object files. | |
| 320 | +generated from the translation stage of the build process, as that is what | |
| 321 | +was actually compiled into the respective object files that make up the | |
| 322 | +fossil binary. | |
| 322 | 323 | |
| 323 | 324 | <h1>8.0 See Also</h1> |
| 324 | 325 | |
| 325 | 326 | * [./tech_overview.wiki | A Technical Overview Of Fossil] |
| 326 | 327 | * [./adding_code.wiki | How To Add Features To Fossil] |
| 327 | 328 |
| --- www/makefile.wiki | |
| +++ www/makefile.wiki | |
| @@ -7,11 +7,11 @@ | |
| 7 | before it is compiled. Most users will download a |
| 8 | [https://fossil-scm.org/home/uv/download.html | precompiled binary] |
| 9 | so this is of no consequence to them, and even those who |
| 10 | want to compile the code themselves can use one of the |
| 11 | [./build.wiki | existing makefiles]. |
| 12 | So must people do not need to be concerned with the |
| 13 | build complexities of Fossil. But hard-core developers who desire |
| 14 | a deep understanding of how Fossil is put together can benefit |
| 15 | from reviewing this article. |
| 16 | |
| 17 | <h1 id="srctour">2.0 Source Code Tour</h1> |
| @@ -56,21 +56,21 @@ | |
| 56 | The TH1 script engine is implemented using files: |
| 57 | |
| 58 | 9. th.c |
| 59 | 10. th.h |
| 60 | |
| 61 | The proprocessing steps are omitted for all of these imported |
| 62 | files. |
| 63 | |
| 64 | The VERSION.h header file is generated from other information sources |
| 65 | using a small program called: |
| 66 | |
| 67 | 11. [/file/tools/mkversion.c | mkversion.c] |
| 68 | |
| 69 | The builtin_data.h header file contains the definitions of C-language |
| 70 | byte-array constants that contain various resources such as scripts and |
| 71 | images. The builtin_data.h header file is generate from the original |
| 72 | resource files using a small program called: |
| 73 | |
| 74 | 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] |
| 75 | |
| 76 | Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] |
| @@ -132,11 +132,11 @@ | |
| 132 | the exceptions described above. |
| 133 | |
| 134 | <h1>3.0 Automatically generated files</h1> |
| 135 | |
| 136 | The "VERSION.h" header file contains some C preprocessor macros that |
| 137 | identify the version of Fossil that is to be build. The VERSION.h file is |
| 138 | generated automatically from information extracted from the "manifest", |
| 139 | "manifest.uuid", and "VERSION" source files in the root directory of the |
| 140 | source tree. |
| 141 | (The "manifest" and "manifest.uuid" files are automatically generated and |
| 142 | updated by Fossil itself. See the [/help/setting | fossil set manifest] |
| @@ -294,12 +294,12 @@ | |
| 294 | link against the standard C library. No other libraries or external |
| 295 | dependences are used. |
| 296 | |
| 297 | <h1>7.0 Debugging</h1> |
| 298 | |
| 299 | Debug mode is controlled via FOSSIL_DEBUG preprocessor macro which could be |
| 300 | set explicitly at the make command for the target platform. |
| 301 | |
| 302 | However, in practice it is instead recommended to add a respective configure |
| 303 | option for the target platform and then perform a clean build. This way the |
| 304 | Debug flags are consistently applied across the whole build process. For |
| 305 | example, use these Debug flags in addition to other flags passed to the |
| @@ -313,14 +313,15 @@ | |
| 313 | On Windows: |
| 314 | <pre> |
| 315 | win\buildmsvc.bat FOSSIL_DEBUG=1 |
| 316 | </pre> |
| 317 | |
| 318 | The resulting fossil binary could then be loaded into a platform-specific |
| 319 | debugger. Source files displayed in the debugger correspond to the ones |
| 320 | generated from the translation stage of the build process, that is what was |
| 321 | actually compiled into the object files. |
| 322 | |
| 323 | <h1>8.0 See Also</h1> |
| 324 | |
| 325 | * [./tech_overview.wiki | A Technical Overview Of Fossil] |
| 326 | * [./adding_code.wiki | How To Add Features To Fossil] |
| 327 |
| --- www/makefile.wiki | |
| +++ www/makefile.wiki | |
| @@ -7,11 +7,11 @@ | |
| 7 | before it is compiled. Most users will download a |
| 8 | [https://fossil-scm.org/home/uv/download.html | precompiled binary] |
| 9 | so this is of no consequence to them, and even those who |
| 10 | want to compile the code themselves can use one of the |
| 11 | [./build.wiki | existing makefiles]. |
| 12 | So most people do not need to be concerned with the |
| 13 | build complexities of Fossil. But hard-core developers who desire |
| 14 | a deep understanding of how Fossil is put together can benefit |
| 15 | from reviewing this article. |
| 16 | |
| 17 | <h1 id="srctour">2.0 Source Code Tour</h1> |
| @@ -56,21 +56,21 @@ | |
| 56 | The TH1 script engine is implemented using files: |
| 57 | |
| 58 | 9. th.c |
| 59 | 10. th.h |
| 60 | |
| 61 | The preprocessing steps are omitted for all of these imported |
| 62 | files. |
| 63 | |
| 64 | The VERSION.h header file is generated from other information sources |
| 65 | using a small program called: |
| 66 | |
| 67 | 11. [/file/tools/mkversion.c | mkversion.c] |
| 68 | |
| 69 | The builtin_data.h header file contains the definitions of C-language |
| 70 | byte-array constants that contain various resources such as scripts and |
| 71 | images. The builtin_data.h header file is generated from the original |
| 72 | resource files using a small program called: |
| 73 | |
| 74 | 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] |
| 75 | |
| 76 | Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] |
| @@ -132,11 +132,11 @@ | |
| 132 | the exceptions described above. |
| 133 | |
| 134 | <h1>3.0 Automatically generated files</h1> |
| 135 | |
| 136 | The "VERSION.h" header file contains some C preprocessor macros that |
| 137 | identify the version of Fossil that is to be built. The VERSION.h file is |
| 138 | generated automatically from information extracted from the "manifest", |
| 139 | "manifest.uuid", and "VERSION" source files in the root directory of the |
| 140 | source tree. |
| 141 | (The "manifest" and "manifest.uuid" files are automatically generated and |
| 142 | updated by Fossil itself. See the [/help/setting | fossil set manifest] |
| @@ -294,12 +294,12 @@ | |
| 294 | link against the standard C library. No other libraries or external |
| 295 | dependences are used. |
| 296 | |
| 297 | <h1>7.0 Debugging</h1> |
| 298 | |
| 299 | Debug mode is controlled via a FOSSIL_DEBUG preprocessor macro. This can be |
| 300 | set explicitly with the make command for the target platform. |
| 301 | |
| 302 | However, in practice it is instead recommended to add a respective configure |
| 303 | option for the target platform and then perform a clean build. This way the |
| 304 | Debug flags are consistently applied across the whole build process. For |
| 305 | example, use these Debug flags in addition to other flags passed to the |
| @@ -313,14 +313,15 @@ | |
| 313 | On Windows: |
| 314 | <pre> |
| 315 | win\buildmsvc.bat FOSSIL_DEBUG=1 |
| 316 | </pre> |
| 317 | |
| 318 | The resulting fossil binary can then be loaded into a platform-specific |
| 319 | debugger. Source files displayed in the debugger correspond to the ones |
| 320 | generated from the translation stage of the build process, as that is what |
| 321 | was actually compiled into the respective object files that make up the |
| 322 | fossil binary. |
| 323 | |
| 324 | <h1>8.0 See Also</h1> |
| 325 | |
| 326 | * [./tech_overview.wiki | A Technical Overview Of Fossil] |
| 327 | * [./adding_code.wiki | How To Add Features To Fossil] |
| 328 |
+1
-1
| --- www/mdtest/test1.md | ||
| +++ www/mdtest/test1.md | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | # Markdown Link-test |
| 2 | 2 | |
| 3 | -This document exist solely as a test for some of the hyperlinking | |
| 3 | +This document exists solely as a test for some of the hyperlinking | |
| 4 | 4 | capabilities of Markdown as implemented by Fossil. |
| 5 | 5 | |
| 6 | 6 | ## Relative-Path Links |
| 7 | 7 | |
| 8 | 8 | * The index: [](../index.wiki) |
| 9 | 9 |
| --- www/mdtest/test1.md | |
| +++ www/mdtest/test1.md | |
| @@ -1,8 +1,8 @@ | |
| 1 | # Markdown Link-test |
| 2 | |
| 3 | This document exist solely as a test for some of the hyperlinking |
| 4 | capabilities of Markdown as implemented by Fossil. |
| 5 | |
| 6 | ## Relative-Path Links |
| 7 | |
| 8 | * The index: [](../index.wiki) |
| 9 |
| --- www/mdtest/test1.md | |
| +++ www/mdtest/test1.md | |
| @@ -1,8 +1,8 @@ | |
| 1 | # Markdown Link-test |
| 2 | |
| 3 | This document exists solely as a test for some of the hyperlinking |
| 4 | capabilities of Markdown as implemented by Fossil. |
| 5 | |
| 6 | ## Relative-Path Links |
| 7 | |
| 8 | * The index: [](../index.wiki) |
| 9 |
+2
-1
| --- www/newrepo.wiki | ||
| +++ www/newrepo.wiki | ||
| @@ -57,11 +57,12 @@ | ||
| 57 | 57 | $ fossil open ../demo.fossil |
| 58 | 58 | </verbatim> |
| 59 | 59 | |
| 60 | 60 | That creates a file called <tt>_FOSSIL_</tt> in the current |
| 61 | 61 | directory, and this file contains all kinds of fossil-related |
| 62 | -information about your local repository. You can ignore it | |
| 62 | +information about your local repository. Under Linux, the BSDs or | |
| 63 | +macOS, this will instead be called <tt>.fslckout</tt>. You can ignore it | |
| 63 | 64 | for all purposes, but be sure not to accidentally remove it |
| 64 | 65 | or otherwise damage it - it belongs to fossil, not you. |
| 65 | 66 | |
| 66 | 67 | The next thing we need to do is add files to our repository. As it |
| 67 | 68 | happens, we have a few C source files lying around, which we'll |
| 68 | 69 |
| --- www/newrepo.wiki | |
| +++ www/newrepo.wiki | |
| @@ -57,11 +57,12 @@ | |
| 57 | $ fossil open ../demo.fossil |
| 58 | </verbatim> |
| 59 | |
| 60 | That creates a file called <tt>_FOSSIL_</tt> in the current |
| 61 | directory, and this file contains all kinds of fossil-related |
| 62 | information about your local repository. You can ignore it |
| 63 | for all purposes, but be sure not to accidentally remove it |
| 64 | or otherwise damage it - it belongs to fossil, not you. |
| 65 | |
| 66 | The next thing we need to do is add files to our repository. As it |
| 67 | happens, we have a few C source files lying around, which we'll |
| 68 |
| --- www/newrepo.wiki | |
| +++ www/newrepo.wiki | |
| @@ -57,11 +57,12 @@ | |
| 57 | $ fossil open ../demo.fossil |
| 58 | </verbatim> |
| 59 | |
| 60 | That creates a file called <tt>_FOSSIL_</tt> in the current |
| 61 | directory, and this file contains all kinds of fossil-related |
| 62 | information about your local repository. Under Linux, the BSDs or |
| 63 | macOS, this will instead be called <tt>.fslckout</tt>. You can ignore it |
| 64 | for all purposes, but be sure not to accidentally remove it |
| 65 | or otherwise damage it - it belongs to fossil, not you. |
| 66 | |
| 67 | The next thing we need to do is add files to our repository. As it |
| 68 | happens, we have a few C source files lying around, which we'll |
| 69 |
+6
-2
| --- www/password.wiki | ||
| +++ www/password.wiki | ||
| @@ -63,17 +63,21 @@ | ||
| 63 | 63 | for "anonymous" uses one-time captchas not persistent passwords. |
| 64 | 64 | |
| 65 | 65 | <h2>Web Interface Authentication</h2> |
| 66 | 66 | |
| 67 | 67 | When a user logs into Fossil using the web interface, the login name |
| 68 | -and password are sent in the clear to the server. The server then | |
| 68 | +and password are sent in the clear to the server. For most modern fossil | |
| 69 | +server setups with [/help?cmd=redirect-to-https|redirect-to-https] enabled, | |
| 70 | +this will be protected by the | |
| 71 | +SSL connection over HTTPS so it cannot be easily viewed. The server then | |
| 69 | 72 | hashes the password and compares it against the value stored in USER.PW. |
| 70 | 73 | If they match, the server sets a cookie on the client to record the |
| 71 | 74 | login. This cookie contains a large amount of high-quality randomness |
| 72 | 75 | and is thus intractable to guess. The value of the cookie and the IP |
| 73 | 76 | address of the client is stored in the USER.COOKIE and USER.IPADDR fields |
| 74 | 77 | of the USER table on the server. |
| 78 | + | |
| 75 | 79 | The USER.CEXPIRE field holds an expiration date |
| 76 | 80 | for the cookie, encoded as a Julian day number. On all subsequent |
| 77 | 81 | HTTP requests, the cookie value is matched against the USER table to |
| 78 | 82 | enable access to the repository. |
| 79 | 83 | |
| @@ -123,11 +127,11 @@ | ||
| 123 | 127 | </pre> |
| 124 | 128 | |
| 125 | 129 | For older clients, the password is used for the shared secret as stated |
| 126 | 130 | in the URL and with no encoding. |
| 127 | 131 | For newer clients, the shared secret is derived from the password |
| 128 | -by transformed the password using the SHA1 hash encoding | |
| 132 | +by transforming the password using the SHA1 hash encoding | |
| 129 | 133 | described above. However, if the first character of the password is |
| 130 | 134 | "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password |
| 131 | 135 | is used directly as the share secret without the SHA1 encoding. |
| 132 | 136 | |
| 133 | 137 | <pre> |
| 134 | 138 |
| --- www/password.wiki | |
| +++ www/password.wiki | |
| @@ -63,17 +63,21 @@ | |
| 63 | for "anonymous" uses one-time captchas not persistent passwords. |
| 64 | |
| 65 | <h2>Web Interface Authentication</h2> |
| 66 | |
| 67 | When a user logs into Fossil using the web interface, the login name |
| 68 | and password are sent in the clear to the server. The server then |
| 69 | hashes the password and compares it against the value stored in USER.PW. |
| 70 | If they match, the server sets a cookie on the client to record the |
| 71 | login. This cookie contains a large amount of high-quality randomness |
| 72 | and is thus intractable to guess. The value of the cookie and the IP |
| 73 | address of the client is stored in the USER.COOKIE and USER.IPADDR fields |
| 74 | of the USER table on the server. |
| 75 | The USER.CEXPIRE field holds an expiration date |
| 76 | for the cookie, encoded as a Julian day number. On all subsequent |
| 77 | HTTP requests, the cookie value is matched against the USER table to |
| 78 | enable access to the repository. |
| 79 | |
| @@ -123,11 +127,11 @@ | |
| 123 | </pre> |
| 124 | |
| 125 | For older clients, the password is used for the shared secret as stated |
| 126 | in the URL and with no encoding. |
| 127 | For newer clients, the shared secret is derived from the password |
| 128 | by transformed the password using the SHA1 hash encoding |
| 129 | described above. However, if the first character of the password is |
| 130 | "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password |
| 131 | is used directly as the share secret without the SHA1 encoding. |
| 132 | |
| 133 | <pre> |
| 134 |
| --- www/password.wiki | |
| +++ www/password.wiki | |
| @@ -63,17 +63,21 @@ | |
| 63 | for "anonymous" uses one-time captchas not persistent passwords. |
| 64 | |
| 65 | <h2>Web Interface Authentication</h2> |
| 66 | |
| 67 | When a user logs into Fossil using the web interface, the login name |
| 68 | and password are sent in the clear to the server. For most modern fossil |
| 69 | server setups with [/help?cmd=redirect-to-https|redirect-to-https] enabled, |
| 70 | this will be protected by the |
| 71 | SSL connection over HTTPS so it cannot be easily viewed. The server then |
| 72 | hashes the password and compares it against the value stored in USER.PW. |
| 73 | If they match, the server sets a cookie on the client to record the |
| 74 | login. This cookie contains a large amount of high-quality randomness |
| 75 | and is thus intractable to guess. The value of the cookie and the IP |
| 76 | address of the client is stored in the USER.COOKIE and USER.IPADDR fields |
| 77 | of the USER table on the server. |
| 78 | |
| 79 | The USER.CEXPIRE field holds an expiration date |
| 80 | for the cookie, encoded as a Julian day number. On all subsequent |
| 81 | HTTP requests, the cookie value is matched against the USER table to |
| 82 | enable access to the repository. |
| 83 | |
| @@ -123,11 +127,11 @@ | |
| 127 | </pre> |
| 128 | |
| 129 | For older clients, the password is used for the shared secret as stated |
| 130 | in the URL and with no encoding. |
| 131 | For newer clients, the shared secret is derived from the password |
| 132 | by transforming the password using the SHA1 hash encoding |
| 133 | described above. However, if the first character of the password is |
| 134 | "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password |
| 135 | is used directly as the share secret without the SHA1 encoding. |
| 136 | |
| 137 | <pre> |
| 138 |
+4
-4
| --- www/patchcmd.md | ||
| +++ www/patchcmd.md | ||
| @@ -9,11 +9,11 @@ | ||
| 9 | 9 | "fossil patch push" command to make a copy of all your changes on the |
| 10 | 10 | remote Linux server: |
| 11 | 11 | |
| 12 | 12 | fossil patch push linuxserver:/path/to/checkout |
| 13 | 13 | |
| 14 | -In the previous "linuxserver" is the name of the remote machine and | |
| 14 | +In the previous line "linuxserver" is the name of the remote machine and | |
| 15 | 15 | "/path/to/checkout" is an existing checkout directory for the same project |
| 16 | 16 | on the remote machine. |
| 17 | 17 | |
| 18 | 18 | The "fossil patch push" command works by first creating a patch file, |
| 19 | 19 | then transfering that patch file to the remote machine using "ssh", then |
| @@ -85,11 +85,11 @@ | ||
| 85 | 85 | The "fossil patch create" command records all of the local, uncommitted |
| 86 | 86 | changes in an SQLite database file. If the argument to "fossil patch create" |
| 87 | 87 | is a filename, then the patch-file database is written into that file. |
| 88 | 88 | If the argument is "-" then the database is written on standard output. |
| 89 | 89 | |
| 90 | -The "fossil patch apply" command reads the database that is the patch file | |
| 90 | +The "fossil patch apply" command reads the patch-file database | |
| 91 | 91 | and applies it to the local check-out. If a filename is given as an |
| 92 | 92 | argument, then the database is read from that file. If the argument is "-" |
| 93 | 93 | then the database is read from standard input. |
| 94 | 94 | |
| 95 | 95 | Hence the command: |
| @@ -102,11 +102,11 @@ | ||
| 102 | 102 | |
| 103 | 103 | Likewise, a command like this: |
| 104 | 104 | |
| 105 | 105 | fossil patch pull remote:projB |
| 106 | 106 | |
| 107 | -Could be entered like this: | |
| 107 | +could be entered like this: | |
| 108 | 108 | |
| 109 | 109 | ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply - |
| 110 | 110 | |
| 111 | -The "fossil patch view" command just opens the database file and prints | |
| 111 | +The "fossil patch view" command just opens the patch-file database and prints | |
| 112 | 112 | a summary of its contents on standard output. |
| 113 | 113 |
| --- www/patchcmd.md | |
| +++ www/patchcmd.md | |
| @@ -9,11 +9,11 @@ | |
| 9 | "fossil patch push" command to make a copy of all your changes on the |
| 10 | remote Linux server: |
| 11 | |
| 12 | fossil patch push linuxserver:/path/to/checkout |
| 13 | |
| 14 | In the previous "linuxserver" is the name of the remote machine and |
| 15 | "/path/to/checkout" is an existing checkout directory for the same project |
| 16 | on the remote machine. |
| 17 | |
| 18 | The "fossil patch push" command works by first creating a patch file, |
| 19 | then transfering that patch file to the remote machine using "ssh", then |
| @@ -85,11 +85,11 @@ | |
| 85 | The "fossil patch create" command records all of the local, uncommitted |
| 86 | changes in an SQLite database file. If the argument to "fossil patch create" |
| 87 | is a filename, then the patch-file database is written into that file. |
| 88 | If the argument is "-" then the database is written on standard output. |
| 89 | |
| 90 | The "fossil patch apply" command reads the database that is the patch file |
| 91 | and applies it to the local check-out. If a filename is given as an |
| 92 | argument, then the database is read from that file. If the argument is "-" |
| 93 | then the database is read from standard input. |
| 94 | |
| 95 | Hence the command: |
| @@ -102,11 +102,11 @@ | |
| 102 | |
| 103 | Likewise, a command like this: |
| 104 | |
| 105 | fossil patch pull remote:projB |
| 106 | |
| 107 | Could be entered like this: |
| 108 | |
| 109 | ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply - |
| 110 | |
| 111 | The "fossil patch view" command just opens the database file and prints |
| 112 | a summary of its contents on standard output. |
| 113 |
| --- www/patchcmd.md | |
| +++ www/patchcmd.md | |
| @@ -9,11 +9,11 @@ | |
| 9 | "fossil patch push" command to make a copy of all your changes on the |
| 10 | remote Linux server: |
| 11 | |
| 12 | fossil patch push linuxserver:/path/to/checkout |
| 13 | |
| 14 | In the previous line "linuxserver" is the name of the remote machine and |
| 15 | "/path/to/checkout" is an existing checkout directory for the same project |
| 16 | on the remote machine. |
| 17 | |
| 18 | The "fossil patch push" command works by first creating a patch file, |
| 19 | then transfering that patch file to the remote machine using "ssh", then |
| @@ -85,11 +85,11 @@ | |
| 85 | The "fossil patch create" command records all of the local, uncommitted |
| 86 | changes in an SQLite database file. If the argument to "fossil patch create" |
| 87 | is a filename, then the patch-file database is written into that file. |
| 88 | If the argument is "-" then the database is written on standard output. |
| 89 | |
| 90 | The "fossil patch apply" command reads the patch-file database |
| 91 | and applies it to the local check-out. If a filename is given as an |
| 92 | argument, then the database is read from that file. If the argument is "-" |
| 93 | then the database is read from standard input. |
| 94 | |
| 95 | Hence the command: |
| @@ -102,11 +102,11 @@ | |
| 102 | |
| 103 | Likewise, a command like this: |
| 104 | |
| 105 | fossil patch pull remote:projB |
| 106 | |
| 107 | could be entered like this: |
| 108 | |
| 109 | ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply - |
| 110 | |
| 111 | The "fossil patch view" command just opens the patch-file database and prints |
| 112 | a summary of its contents on standard output. |
| 113 |
+2
-2
| --- www/pikchr.md | ||
| +++ www/pikchr.md | ||
| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | # The Pikchr Diagram Language |
| 2 | 2 | |
| 3 | 3 | Pikchr (pronounced "picture") is a [PIC][1]-like markup language for creating |
| 4 | -diagrams in technical documentation. Pikchr diagrams source text | |
| 4 | +diagrams in technical documentation. Source text for Pikchr diagrams | |
| 5 | 5 | can be embedded directly in either [Markdown][2] or [Fossil Wiki][3]. |
| 6 | 6 | Fossil translates the Pikchr source text into SVG which is displayed as |
| 7 | 7 | part of the rendered wiki. |
| 8 | 8 | |
| 9 | 9 | [1]: wikipedia:/wiki/Pic_language |
| @@ -134,6 +134,6 @@ | ||
| 134 | 134 | |
| 135 | 135 | * **float-right** → The diagram is shown at the right margin and |
| 136 | 136 | text fills in around the diagram. |
| 137 | 137 | |
| 138 | 138 | * **source** → The display starts out showing the Pikchr source text. |
| 139 | - The reader must click (or Alt-click or Ctrl-click) to set the diagram. | |
| 139 | + The reader must click (or Alt-click or Ctrl-click) to show the diagram. | |
| 140 | 140 |
| --- www/pikchr.md | |
| +++ www/pikchr.md | |
| @@ -1,9 +1,9 @@ | |
| 1 | # The Pikchr Diagram Language |
| 2 | |
| 3 | Pikchr (pronounced "picture") is a [PIC][1]-like markup language for creating |
| 4 | diagrams in technical documentation. Pikchr diagrams source text |
| 5 | can be embedded directly in either [Markdown][2] or [Fossil Wiki][3]. |
| 6 | Fossil translates the Pikchr source text into SVG which is displayed as |
| 7 | part of the rendered wiki. |
| 8 | |
| 9 | [1]: wikipedia:/wiki/Pic_language |
| @@ -134,6 +134,6 @@ | |
| 134 | |
| 135 | * **float-right** → The diagram is shown at the right margin and |
| 136 | text fills in around the diagram. |
| 137 | |
| 138 | * **source** → The display starts out showing the Pikchr source text. |
| 139 | The reader must click (or Alt-click or Ctrl-click) to set the diagram. |
| 140 |
| --- www/pikchr.md | |
| +++ www/pikchr.md | |
| @@ -1,9 +1,9 @@ | |
| 1 | # The Pikchr Diagram Language |
| 2 | |
| 3 | Pikchr (pronounced "picture") is a [PIC][1]-like markup language for creating |
| 4 | diagrams in technical documentation. Source text for Pikchr diagrams |
| 5 | can be embedded directly in either [Markdown][2] or [Fossil Wiki][3]. |
| 6 | Fossil translates the Pikchr source text into SVG which is displayed as |
| 7 | part of the rendered wiki. |
| 8 | |
| 9 | [1]: wikipedia:/wiki/Pic_language |
| @@ -134,6 +134,6 @@ | |
| 134 | |
| 135 | * **float-right** → The diagram is shown at the right margin and |
| 136 | text fills in around the diagram. |
| 137 | |
| 138 | * **source** → The display starts out showing the Pikchr source text. |
| 139 | The reader must click (or Alt-click or Ctrl-click) to show the diagram. |
| 140 |
+1
-1
| --- www/qandc.wiki | ||
| +++ www/qandc.wiki | ||
| @@ -142,11 +142,11 @@ | ||
| 142 | 142 | fossil supports disconnected operation. |
| 143 | 143 | |
| 144 | 144 | As for bloat: Fossil is a single self-contained executable. |
| 145 | 145 | You do not need any other packages |
| 146 | 146 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 147 | -sqlite, and so forth) | |
| 147 | +SQLite, and so forth) | |
| 148 | 148 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 149 | 149 | by itself. And the self-contained fossil |
| 150 | 150 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 151 | 151 | grown in the years since the previous sentence was written but is still |
| 152 | 152 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 153 | 153 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -142,11 +142,11 @@ | |
| 142 | fossil supports disconnected operation. |
| 143 | |
| 144 | As for bloat: Fossil is a single self-contained executable. |
| 145 | You do not need any other packages |
| 146 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 147 | sqlite, and so forth) |
| 148 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 149 | by itself. And the self-contained fossil |
| 150 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 151 | grown in the years since the previous sentence was written but is still |
| 152 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 153 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -142,11 +142,11 @@ | |
| 142 | fossil supports disconnected operation. |
| 143 | |
| 144 | As for bloat: Fossil is a single self-contained executable. |
| 145 | You do not need any other packages |
| 146 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 147 | SQLite, and so forth) |
| 148 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 149 | by itself. And the self-contained fossil |
| 150 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 151 | grown in the years since the previous sentence was written but is still |
| 152 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 153 |
+1
-1
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -364,11 +364,11 @@ | ||
| 364 | 364 | abbreviation to the 40-character |
| 365 | 365 | artifact identifier for a particular check-in, or it can be a |
| 366 | 366 | date/time stamp. ([./checkin_names.wiki | more info]) |
| 367 | 367 | If you omit |
| 368 | 368 | the <i>VERSION</i>, then fossil moves you to the |
| 369 | -latest version of the branch your are currently on. | |
| 369 | +latest version of the branch you are currently on. | |
| 370 | 370 | |
| 371 | 371 | The default behavior is for [./concepts.wiki#workflow|autosync] to |
| 372 | 372 | be turned on. That means that a [/help/pull|pull] automatically occurs |
| 373 | 373 | when you run [/help/update|update] and a [/help/push|push] happens |
| 374 | 374 | automatically after you [/help/commit|commit]. So in normal practice, |
| 375 | 375 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -364,11 +364,11 @@ | |
| 364 | abbreviation to the 40-character |
| 365 | artifact identifier for a particular check-in, or it can be a |
| 366 | date/time stamp. ([./checkin_names.wiki | more info]) |
| 367 | If you omit |
| 368 | the <i>VERSION</i>, then fossil moves you to the |
| 369 | latest version of the branch your are currently on. |
| 370 | |
| 371 | The default behavior is for [./concepts.wiki#workflow|autosync] to |
| 372 | be turned on. That means that a [/help/pull|pull] automatically occurs |
| 373 | when you run [/help/update|update] and a [/help/push|push] happens |
| 374 | automatically after you [/help/commit|commit]. So in normal practice, |
| 375 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -364,11 +364,11 @@ | |
| 364 | abbreviation to the 40-character |
| 365 | artifact identifier for a particular check-in, or it can be a |
| 366 | date/time stamp. ([./checkin_names.wiki | more info]) |
| 367 | If you omit |
| 368 | the <i>VERSION</i>, then fossil moves you to the |
| 369 | latest version of the branch you are currently on. |
| 370 | |
| 371 | The default behavior is for [./concepts.wiki#workflow|autosync] to |
| 372 | be turned on. That means that a [/help/pull|pull] automatically occurs |
| 373 | when you run [/help/update|update] and a [/help/push|push] happens |
| 374 | automatically after you [/help/commit|commit]. So in normal practice, |
| 375 |
+2
-2
| --- www/rebaseharm.md | ||
| +++ www/rebaseharm.md | ||
| @@ -224,16 +224,16 @@ | ||
| 224 | 224 | you are keeping private branches. Or, to put it another way, you are |
| 225 | 225 | doing siloed development. You are not sharing your intermediate work |
| 226 | 226 | with collaborators. This is not good for product quality. |
| 227 | 227 | |
| 228 | 228 | [Nagappan, et. al][nagappan] studied bugs in Windows Vista and found |
| 229 | -that best predictor of bugs is the distance on the org-chart between | |
| 229 | +that the best predictor of bugs is the distance on the org-chart between | |
| 230 | 230 | the stake-holders. The bug rate is inversely related to the |
| 231 | 231 | amount of communication among the engineers. |
| 232 | 232 | Similar findings arise in other disciplines. Keeping |
| 233 | 233 | private branches does not prove that developers are communicating |
| 234 | -insufficiently, but it is a key symptom that problem. | |
| 234 | +insufficiently, but it is a key symptom of that problem. | |
| 235 | 235 | |
| 236 | 236 | [Weinberg][weinberg] argues programming should be "egoless." That |
| 237 | 237 | is to say, programmers should avoid linking their code with their sense of |
| 238 | 238 | self, as that makes it more difficult for them to find and respond |
| 239 | 239 | to bugs, and hence makes them less productive. Many developers are |
| 240 | 240 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -224,16 +224,16 @@ | |
| 224 | you are keeping private branches. Or, to put it another way, you are |
| 225 | doing siloed development. You are not sharing your intermediate work |
| 226 | with collaborators. This is not good for product quality. |
| 227 | |
| 228 | [Nagappan, et. al][nagappan] studied bugs in Windows Vista and found |
| 229 | that best predictor of bugs is the distance on the org-chart between |
| 230 | the stake-holders. The bug rate is inversely related to the |
| 231 | amount of communication among the engineers. |
| 232 | Similar findings arise in other disciplines. Keeping |
| 233 | private branches does not prove that developers are communicating |
| 234 | insufficiently, but it is a key symptom that problem. |
| 235 | |
| 236 | [Weinberg][weinberg] argues programming should be "egoless." That |
| 237 | is to say, programmers should avoid linking their code with their sense of |
| 238 | self, as that makes it more difficult for them to find and respond |
| 239 | to bugs, and hence makes them less productive. Many developers are |
| 240 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -224,16 +224,16 @@ | |
| 224 | you are keeping private branches. Or, to put it another way, you are |
| 225 | doing siloed development. You are not sharing your intermediate work |
| 226 | with collaborators. This is not good for product quality. |
| 227 | |
| 228 | [Nagappan, et. al][nagappan] studied bugs in Windows Vista and found |
| 229 | that the best predictor of bugs is the distance on the org-chart between |
| 230 | the stake-holders. The bug rate is inversely related to the |
| 231 | amount of communication among the engineers. |
| 232 | Similar findings arise in other disciplines. Keeping |
| 233 | private branches does not prove that developers are communicating |
| 234 | insufficiently, but it is a key symptom of that problem. |
| 235 | |
| 236 | [Weinberg][weinberg] argues programming should be "egoless." That |
| 237 | is to say, programmers should avoid linking their code with their sense of |
| 238 | self, as that makes it more difficult for them to find and respond |
| 239 | to bugs, and hence makes them less productive. Many developers are |
| 240 |
+1
-1
| --- www/selfcheck.wiki | ||
| +++ www/selfcheck.wiki | ||
| @@ -13,11 +13,11 @@ | ||
| 13 | 13 | |
| 14 | 14 | <h2>Atomic Check-ins With Rollback</h2> |
| 15 | 15 | |
| 16 | 16 | The Fossil repository is stored in an |
| 17 | 17 | <a href="http://www.sqlite.org/">SQLite</a> database file. |
| 18 | -([./tech_overview.wiki | Addition information] about the repository | |
| 18 | +([./tech_overview.wiki | Additional information] about the repository | |
| 19 | 19 | file format.) |
| 20 | 20 | SQLite is very mature and stable and has been in wide-spread use for many |
| 21 | 21 | years, so we are confident it will not cause repository |
| 22 | 22 | corruption. SQLite |
| 23 | 23 | databases do not corrupt even if a program or system crash or power |
| 24 | 24 |
| --- www/selfcheck.wiki | |
| +++ www/selfcheck.wiki | |
| @@ -13,11 +13,11 @@ | |
| 13 | |
| 14 | <h2>Atomic Check-ins With Rollback</h2> |
| 15 | |
| 16 | The Fossil repository is stored in an |
| 17 | <a href="http://www.sqlite.org/">SQLite</a> database file. |
| 18 | ([./tech_overview.wiki | Addition information] about the repository |
| 19 | file format.) |
| 20 | SQLite is very mature and stable and has been in wide-spread use for many |
| 21 | years, so we are confident it will not cause repository |
| 22 | corruption. SQLite |
| 23 | databases do not corrupt even if a program or system crash or power |
| 24 |
| --- www/selfcheck.wiki | |
| +++ www/selfcheck.wiki | |
| @@ -13,11 +13,11 @@ | |
| 13 | |
| 14 | <h2>Atomic Check-ins With Rollback</h2> |
| 15 | |
| 16 | The Fossil repository is stored in an |
| 17 | <a href="http://www.sqlite.org/">SQLite</a> database file. |
| 18 | ([./tech_overview.wiki | Additional information] about the repository |
| 19 | file format.) |
| 20 | SQLite is very mature and stable and has been in wide-spread use for many |
| 21 | years, so we are confident it will not cause repository |
| 22 | corruption. SQLite |
| 23 | databases do not corrupt even if a program or system crash or power |
| 24 |
+1
-1
| --- www/selfhost.wiki | ||
| +++ www/selfhost.wiki | ||
| @@ -27,11 +27,11 @@ | ||
| 27 | 27 | hosts <a href="http://www.sqlite.org/">SQLite</a> and over a |
| 28 | 28 | dozen other smaller projects. This demonstrates that Fossil can run on |
| 29 | 29 | a low-power host processor. |
| 30 | 30 | Multiple fossil-based projects can easily be hosted on the same machine, |
| 31 | 31 | even if that machine is itself one of several dozen virtual machines on |
| 32 | -single physical box. The CGI script that runs the canonical Fossil | |
| 32 | +a single physical box. The CGI script that runs the canonical Fossil | |
| 33 | 33 | self-hosting repository is as follows: |
| 34 | 34 | |
| 35 | 35 | <pre> |
| 36 | 36 | #!/usr/bin/fossil |
| 37 | 37 | repository: /fossil/fossil.fossil |
| 38 | 38 |
| --- www/selfhost.wiki | |
| +++ www/selfhost.wiki | |
| @@ -27,11 +27,11 @@ | |
| 27 | hosts <a href="http://www.sqlite.org/">SQLite</a> and over a |
| 28 | dozen other smaller projects. This demonstrates that Fossil can run on |
| 29 | a low-power host processor. |
| 30 | Multiple fossil-based projects can easily be hosted on the same machine, |
| 31 | even if that machine is itself one of several dozen virtual machines on |
| 32 | single physical box. The CGI script that runs the canonical Fossil |
| 33 | self-hosting repository is as follows: |
| 34 | |
| 35 | <pre> |
| 36 | #!/usr/bin/fossil |
| 37 | repository: /fossil/fossil.fossil |
| 38 |
| --- www/selfhost.wiki | |
| +++ www/selfhost.wiki | |
| @@ -27,11 +27,11 @@ | |
| 27 | hosts <a href="http://www.sqlite.org/">SQLite</a> and over a |
| 28 | dozen other smaller projects. This demonstrates that Fossil can run on |
| 29 | a low-power host processor. |
| 30 | Multiple fossil-based projects can easily be hosted on the same machine, |
| 31 | even if that machine is itself one of several dozen virtual machines on |
| 32 | a single physical box. The CGI script that runs the canonical Fossil |
| 33 | self-hosting repository is as follows: |
| 34 | |
| 35 | <pre> |
| 36 | #!/usr/bin/fossil |
| 37 | repository: /fossil/fossil.fossil |
| 38 |
+1
-1
| --- www/server/debian/service.md | ||
| +++ www/server/debian/service.md | ||
| @@ -183,11 +183,11 @@ | ||
| 183 | 183 | to the system level. We need to start this socket listener at the root |
| 184 | 184 | level because of the low-numbered TCP port restriction we brought up |
| 185 | 185 | above. |
| 186 | 186 | |
| 187 | 187 | This configuration says more or less the same thing as the socket part |
| 188 | -of an `inted` entry [exemplified elsewhere in this | |
| 188 | +of an `inetd` entry [exemplified elsewhere in this | |
| 189 | 189 | documentation](../any/inetd.md). |
| 190 | 190 | |
| 191 | 191 | Next, create the service definition file in that same directory as |
| 192 | 192 | `[email protected]`: |
| 193 | 193 | |
| 194 | 194 |
| --- www/server/debian/service.md | |
| +++ www/server/debian/service.md | |
| @@ -183,11 +183,11 @@ | |
| 183 | to the system level. We need to start this socket listener at the root |
| 184 | level because of the low-numbered TCP port restriction we brought up |
| 185 | above. |
| 186 | |
| 187 | This configuration says more or less the same thing as the socket part |
| 188 | of an `inted` entry [exemplified elsewhere in this |
| 189 | documentation](../any/inetd.md). |
| 190 | |
| 191 | Next, create the service definition file in that same directory as |
| 192 | `[email protected]`: |
| 193 | |
| 194 |
| --- www/server/debian/service.md | |
| +++ www/server/debian/service.md | |
| @@ -183,11 +183,11 @@ | |
| 183 | to the system level. We need to start this socket listener at the root |
| 184 | level because of the low-numbered TCP port restriction we brought up |
| 185 | above. |
| 186 | |
| 187 | This configuration says more or less the same thing as the socket part |
| 188 | of an `inetd` entry [exemplified elsewhere in this |
| 189 | documentation](../any/inetd.md). |
| 190 | |
| 191 | Next, create the service definition file in that same directory as |
| 192 | `[email protected]`: |
| 193 | |
| 194 |
+1
-1
| --- www/server/openbsd/fastcgi.md | ||
| +++ www/server/openbsd/fastcgi.md | ||
| @@ -172,11 +172,11 @@ | ||
| 172 | 172 | setting, raising the limit to 100 MiB. |
| 173 | 173 | |
| 174 | 174 | [dlim]: https://man.openbsd.org/httpd.conf.5#connection |
| 175 | 175 | [uv]: ../../unvers.wiki |
| 176 | 176 | |
| 177 | -**NOTE:** If not already in possession of a HTTPS certificate, comment | |
| 177 | +**NOTE:** If not already in possession of an HTTPS certificate, comment | |
| 178 | 178 | out the `https` server block and proceed to securing a free |
| 179 | 179 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 180 | 180 | [Start `httpd`](#starthttpd). |
| 181 | 181 | |
| 182 | 182 | |
| 183 | 183 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -172,11 +172,11 @@ | |
| 172 | setting, raising the limit to 100 MiB. |
| 173 | |
| 174 | [dlim]: https://man.openbsd.org/httpd.conf.5#connection |
| 175 | [uv]: ../../unvers.wiki |
| 176 | |
| 177 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 178 | out the `https` server block and proceed to securing a free |
| 179 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 180 | [Start `httpd`](#starthttpd). |
| 181 | |
| 182 | |
| 183 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -172,11 +172,11 @@ | |
| 172 | setting, raising the limit to 100 MiB. |
| 173 | |
| 174 | [dlim]: https://man.openbsd.org/httpd.conf.5#connection |
| 175 | [uv]: ../../unvers.wiki |
| 176 | |
| 177 | **NOTE:** If not already in possession of an HTTPS certificate, comment |
| 178 | out the `https` server block and proceed to securing a free |
| 179 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 180 | [Start `httpd`](#starthttpd). |
| 181 | |
| 182 | |
| 183 |
+1
-1
| --- www/server/windows/service.md | ||
| +++ www/server/windows/service.md | ||
| @@ -39,11 +39,11 @@ | ||
| 39 | 39 | This will create a windows service named 'Fossil-DSCM' running under the local |
| 40 | 40 | system account and accessible on port 8080 by default. `fossil winsrv` can also |
| 41 | 41 | start, stop, and delete the service. For all available options, please execute |
| 42 | 42 | `fossil help winsrv` on a windows install of Fossil. |
| 43 | 43 | |
| 44 | -If you wish to server a directory of repositories, the `fossil winsrv` command | |
| 44 | +If you wish to serve a directory of repositories, the `fossil winsrv` command | |
| 45 | 45 | requires a slightly different set of options vs. `fossil server`: |
| 46 | 46 | |
| 47 | 47 | ``` |
| 48 | 48 | fossil winsrv create --repository D:/Path/to/Repos --repolist |
| 49 | 49 | ``` |
| 50 | 50 |
| --- www/server/windows/service.md | |
| +++ www/server/windows/service.md | |
| @@ -39,11 +39,11 @@ | |
| 39 | This will create a windows service named 'Fossil-DSCM' running under the local |
| 40 | system account and accessible on port 8080 by default. `fossil winsrv` can also |
| 41 | start, stop, and delete the service. For all available options, please execute |
| 42 | `fossil help winsrv` on a windows install of Fossil. |
| 43 | |
| 44 | If you wish to server a directory of repositories, the `fossil winsrv` command |
| 45 | requires a slightly different set of options vs. `fossil server`: |
| 46 | |
| 47 | ``` |
| 48 | fossil winsrv create --repository D:/Path/to/Repos --repolist |
| 49 | ``` |
| 50 |
| --- www/server/windows/service.md | |
| +++ www/server/windows/service.md | |
| @@ -39,11 +39,11 @@ | |
| 39 | This will create a windows service named 'Fossil-DSCM' running under the local |
| 40 | system account and accessible on port 8080 by default. `fossil winsrv` can also |
| 41 | start, stop, and delete the service. For all available options, please execute |
| 42 | `fossil help winsrv` on a windows install of Fossil. |
| 43 | |
| 44 | If you wish to serve a directory of repositories, the `fossil winsrv` command |
| 45 | requires a slightly different set of options vs. `fossil server`: |
| 46 | |
| 47 | ``` |
| 48 | fossil winsrv create --repository D:/Path/to/Repos --repolist |
| 49 | ``` |
| 50 |
+2
-3
| --- www/settings.wiki | ||
| +++ www/settings.wiki | ||
| @@ -44,13 +44,12 @@ | ||
| 44 | 44 | Because these options can change over time, and the inconvenience of |
| 45 | 45 | replicating changes, these settings are "versionable". As well as being |
| 46 | 46 | able to be set using the <tt>settings</tt> command or the web interface, |
| 47 | 47 | you can create versioned files in the <tt>.fossil-settings</tt> |
| 48 | 48 | subdirectory of the check-out root, named with the setting name. |
| 49 | -The contents of the file is the | |
| 50 | -value of the setting, and these files are checked in, committed, merged, | |
| 51 | -and so on, as with any other file. | |
| 49 | +Each file holds the value of a setting, and these files are checked in, | |
| 50 | +committed, merged, and so on, as with any other file. | |
| 52 | 51 | |
| 53 | 52 | Where a setting is a list of values, such as <tt>ignore-glob</tt>, you |
| 54 | 53 | can use a newline as a separator as well as a comma. |
| 55 | 54 | |
| 56 | 55 | For example, to set the list of ignored files, create a |
| 57 | 56 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -44,13 +44,12 @@ | |
| 44 | Because these options can change over time, and the inconvenience of |
| 45 | replicating changes, these settings are "versionable". As well as being |
| 46 | able to be set using the <tt>settings</tt> command or the web interface, |
| 47 | you can create versioned files in the <tt>.fossil-settings</tt> |
| 48 | subdirectory of the check-out root, named with the setting name. |
| 49 | The contents of the file is the |
| 50 | value of the setting, and these files are checked in, committed, merged, |
| 51 | and so on, as with any other file. |
| 52 | |
| 53 | Where a setting is a list of values, such as <tt>ignore-glob</tt>, you |
| 54 | can use a newline as a separator as well as a comma. |
| 55 | |
| 56 | For example, to set the list of ignored files, create a |
| 57 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -44,13 +44,12 @@ | |
| 44 | Because these options can change over time, and the inconvenience of |
| 45 | replicating changes, these settings are "versionable". As well as being |
| 46 | able to be set using the <tt>settings</tt> command or the web interface, |
| 47 | you can create versioned files in the <tt>.fossil-settings</tt> |
| 48 | subdirectory of the check-out root, named with the setting name. |
| 49 | Each file holds the value of a setting, and these files are checked in, |
| 50 | committed, merged, and so on, as with any other file. |
| 51 | |
| 52 | Where a setting is a list of values, such as <tt>ignore-glob</tt>, you |
| 53 | can use a newline as a separator as well as a comma. |
| 54 | |
| 55 | For example, to set the list of ignored files, create a |
| 56 |
+2
-2
| --- www/shunning.wiki | ||
| +++ www/shunning.wiki | ||
| @@ -33,11 +33,11 @@ | ||
| 33 | 33 | |
| 34 | 34 | All of these are rare cases: Fossil is [./antibot.wiki | designed to |
| 35 | 35 | foil spammers up front], legally problematic check-ins should range from |
| 36 | 36 | rare to nonexistent, and you have to go way out of your way to force |
| 37 | 37 | Fossil to insert bad control artifacts. Therefore, before we get to |
| 38 | -methods of permanently deleting content from a Fossil repos, let's give | |
| 38 | +methods of permanently deleting content from a Fossil repo, let's give | |
| 39 | 39 | some alternatives that usually suffice, which don't damage the project's |
| 40 | 40 | fossil record: |
| 41 | 41 | |
| 42 | 42 | * When a forum post or wiki article is "deleted," what actually |
| 43 | 43 | happens is that a new empty version is added to the Fossil repository. |
| @@ -133,11 +133,11 @@ | ||
| 133 | 133 | using the "configuration" command: |
| 134 | 134 | |
| 135 | 135 | <b>fossil configuration pull shun</b> <i>remote-url</i><br> |
| 136 | 136 | <b>fossil configuration push shun</b> <i>remote-url</i> |
| 137 | 137 | |
| 138 | -The two command above will pull or push shunning lists from or to | |
| 138 | +The two commands above will pull or push shunning lists from or to | |
| 139 | 139 | the <i>remote-url</i> indicated and merge the lists on the receiving |
| 140 | 140 | end. "Admin" privilege on the remote server is required in order to |
| 141 | 141 | push a shun list. In contrast, the shunning list will be automatically |
| 142 | 142 | received by default as part of a normal client "pull" operation unless |
| 143 | 143 | disabled by the "<tt>auto-shun</tt>" setting. |
| 144 | 144 |
| --- www/shunning.wiki | |
| +++ www/shunning.wiki | |
| @@ -33,11 +33,11 @@ | |
| 33 | |
| 34 | All of these are rare cases: Fossil is [./antibot.wiki | designed to |
| 35 | foil spammers up front], legally problematic check-ins should range from |
| 36 | rare to nonexistent, and you have to go way out of your way to force |
| 37 | Fossil to insert bad control artifacts. Therefore, before we get to |
| 38 | methods of permanently deleting content from a Fossil repos, let's give |
| 39 | some alternatives that usually suffice, which don't damage the project's |
| 40 | fossil record: |
| 41 | |
| 42 | * When a forum post or wiki article is "deleted," what actually |
| 43 | happens is that a new empty version is added to the Fossil repository. |
| @@ -133,11 +133,11 @@ | |
| 133 | using the "configuration" command: |
| 134 | |
| 135 | <b>fossil configuration pull shun</b> <i>remote-url</i><br> |
| 136 | <b>fossil configuration push shun</b> <i>remote-url</i> |
| 137 | |
| 138 | The two command above will pull or push shunning lists from or to |
| 139 | the <i>remote-url</i> indicated and merge the lists on the receiving |
| 140 | end. "Admin" privilege on the remote server is required in order to |
| 141 | push a shun list. In contrast, the shunning list will be automatically |
| 142 | received by default as part of a normal client "pull" operation unless |
| 143 | disabled by the "<tt>auto-shun</tt>" setting. |
| 144 |
| --- www/shunning.wiki | |
| +++ www/shunning.wiki | |
| @@ -33,11 +33,11 @@ | |
| 33 | |
| 34 | All of these are rare cases: Fossil is [./antibot.wiki | designed to |
| 35 | foil spammers up front], legally problematic check-ins should range from |
| 36 | rare to nonexistent, and you have to go way out of your way to force |
| 37 | Fossil to insert bad control artifacts. Therefore, before we get to |
| 38 | methods of permanently deleting content from a Fossil repo, let's give |
| 39 | some alternatives that usually suffice, which don't damage the project's |
| 40 | fossil record: |
| 41 | |
| 42 | * When a forum post or wiki article is "deleted," what actually |
| 43 | happens is that a new empty version is added to the Fossil repository. |
| @@ -133,11 +133,11 @@ | |
| 133 | using the "configuration" command: |
| 134 | |
| 135 | <b>fossil configuration pull shun</b> <i>remote-url</i><br> |
| 136 | <b>fossil configuration push shun</b> <i>remote-url</i> |
| 137 | |
| 138 | The two commands above will pull or push shunning lists from or to |
| 139 | the <i>remote-url</i> indicated and merge the lists on the receiving |
| 140 | end. "Admin" privilege on the remote server is required in order to |
| 141 | push a shun list. In contrast, the shunning list will be automatically |
| 142 | received by default as part of a normal client "pull" operation unless |
| 143 | disabled by the "<tt>auto-shun</tt>" setting. |
| 144 |
+2
-2
| --- www/stats.wiki | ||
| +++ www/stats.wiki | ||
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | <title>Fossil Performance</title> |
| 2 | 2 | |
| 3 | 3 | The questions will inevitably arise: How does Fossil perform? |
| 4 | 4 | Does it use a lot of disk space or bandwidth? Is it scalable? |
| 5 | 5 | |
| 6 | -In an attempt to answers these questions, this report looks at several | |
| 6 | +In an attempt to answer these questions, this report looks at several | |
| 7 | 7 | projects that use fossil for configuration management and examines how |
| 8 | 8 | well they are working. The following table is a summary of the results. |
| 9 | 9 | (Last updated on 2018-06-04.) |
| 10 | 10 | Explanation and analysis follows the table. |
| 11 | 11 | |
| @@ -100,11 +100,11 @@ | ||
| 100 | 100 | is the unordered collection of artifacts. In fact, one of the key |
| 101 | 101 | characteristics of Fossil is that the entire project history can be |
| 102 | 102 | reconstructed simply by scanning the artifacts in an arbitrary order. |
| 103 | 103 | |
| 104 | 104 | The number of check-ins is the number of times that the "commit" command |
| 105 | -has been run. A single check-in might change a 3 or 4 files, or it might | |
| 105 | +has been run. A single check-in might change 3 or 4 files, or it might | |
| 106 | 106 | change dozens or hundreds of files. Regardless of the number of files |
| 107 | 107 | changed, it still only counts as one check-in. |
| 108 | 108 | |
| 109 | 109 | The "Uncompressed Size" is the total size of all the artifacts within |
| 110 | 110 | the repository assuming they were all uncompressed and stored |
| 111 | 111 |
| --- www/stats.wiki | |
| +++ www/stats.wiki | |
| @@ -1,11 +1,11 @@ | |
| 1 | <title>Fossil Performance</title> |
| 2 | |
| 3 | The questions will inevitably arise: How does Fossil perform? |
| 4 | Does it use a lot of disk space or bandwidth? Is it scalable? |
| 5 | |
| 6 | In an attempt to answers these questions, this report looks at several |
| 7 | projects that use fossil for configuration management and examines how |
| 8 | well they are working. The following table is a summary of the results. |
| 9 | (Last updated on 2018-06-04.) |
| 10 | Explanation and analysis follows the table. |
| 11 | |
| @@ -100,11 +100,11 @@ | |
| 100 | is the unordered collection of artifacts. In fact, one of the key |
| 101 | characteristics of Fossil is that the entire project history can be |
| 102 | reconstructed simply by scanning the artifacts in an arbitrary order. |
| 103 | |
| 104 | The number of check-ins is the number of times that the "commit" command |
| 105 | has been run. A single check-in might change a 3 or 4 files, or it might |
| 106 | change dozens or hundreds of files. Regardless of the number of files |
| 107 | changed, it still only counts as one check-in. |
| 108 | |
| 109 | The "Uncompressed Size" is the total size of all the artifacts within |
| 110 | the repository assuming they were all uncompressed and stored |
| 111 |
| --- www/stats.wiki | |
| +++ www/stats.wiki | |
| @@ -1,11 +1,11 @@ | |
| 1 | <title>Fossil Performance</title> |
| 2 | |
| 3 | The questions will inevitably arise: How does Fossil perform? |
| 4 | Does it use a lot of disk space or bandwidth? Is it scalable? |
| 5 | |
| 6 | In an attempt to answer these questions, this report looks at several |
| 7 | projects that use fossil for configuration management and examines how |
| 8 | well they are working. The following table is a summary of the results. |
| 9 | (Last updated on 2018-06-04.) |
| 10 | Explanation and analysis follows the table. |
| 11 | |
| @@ -100,11 +100,11 @@ | |
| 100 | is the unordered collection of artifacts. In fact, one of the key |
| 101 | characteristics of Fossil is that the entire project history can be |
| 102 | reconstructed simply by scanning the artifacts in an arbitrary order. |
| 103 | |
| 104 | The number of check-ins is the number of times that the "commit" command |
| 105 | has been run. A single check-in might change 3 or 4 files, or it might |
| 106 | change dozens or hundreds of files. Regardless of the number of files |
| 107 | changed, it still only counts as one check-in. |
| 108 | |
| 109 | The "Uncompressed Size" is the total size of all the artifacts within |
| 110 | the repository assuming they were all uncompressed and stored |
| 111 |
+6
-5
| --- www/style.wiki | ||
| +++ www/style.wiki | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | <title>Coding Style</title> |
| 2 | 2 | |
| 3 | -Fossil source code should following the style guidelines below. | |
| 3 | +Fossil source code should follow the style guidelines below. | |
| 4 | 4 | |
| 5 | 5 | <em> The Fossil source tree includes a few files taken from external |
| 6 | 6 | sources |
| 7 | 7 | (examples: [https://github.com/antirez/linenoise|linenoise] and |
| 8 | 8 | [http://zlib.net/|zLib]) |
| @@ -38,13 +38,14 @@ | ||
| 38 | 38 | |
| 39 | 39 | <li> -Wno-long-long: Fossil uses the 'long long' integer type, which is not strictly ANSI C-89 (defined in C99). |
| 40 | 40 | The use of 'long long' resolves many problems with 64-bit arithmetics, especially on 32-bit machines. |
| 41 | 41 | (http_ssl.c, sha3.c, shell.c, util.c) |
| 42 | 42 | |
| 43 | - <li> alloca(): By default, sqlite3.c is compiled with the -DSQLITE_USE_ALLOCA flag to use the alloca() function. | |
| 44 | - alloca() is not considered ANSI C, and normally not recommended due to portability issues, but | |
| 45 | - performance and/or memory consumption improvement may be a stronger argument in favor of its usage. | |
| 43 | + <li> alloca(): By default, sqlite3.c was compiled with the -DSQLITE_USE_ALLOCA flag to use the alloca() function. | |
| 44 | + This is no longer the case as of 20220119. alloca() is not considered ANSI C, and normally not | |
| 45 | + recommended due to portability issues, but performance and/or memory consumption | |
| 46 | + improvement may have been a stronger argument in favor of its usage. | |
| 46 | 47 | (sqlite3.c) |
| 47 | 48 | </ol> |
| 48 | 49 | |
| 49 | 50 | <li> All comments and identifiers are in English. |
| 50 | 51 | |
| @@ -75,11 +76,11 @@ | ||
| 75 | 76 | |
| 76 | 77 | <ol> |
| 77 | 78 | <li value=30> Every function has a header comment describing the purpose and use |
| 78 | 79 | of the function. |
| 79 | 80 | |
| 80 | - <li> Function header comment defines the behavior of the function in | |
| 81 | + <li> A function header comment defines the behavior of the function in | |
| 81 | 82 | sufficient detail to allow the function to be re-implemented from |
| 82 | 83 | scratch without reference to the original code. |
| 83 | 84 | |
| 84 | 85 | <li> Functions that perform dynamic memory allocation (either directly |
| 85 | 86 | or indirectly via subfunctions) say so in their header comments. |
| 86 | 87 |
| --- www/style.wiki | |
| +++ www/style.wiki | |
| @@ -1,8 +1,8 @@ | |
| 1 | <title>Coding Style</title> |
| 2 | |
| 3 | Fossil source code should following the style guidelines below. |
| 4 | |
| 5 | <em> The Fossil source tree includes a few files taken from external |
| 6 | sources |
| 7 | (examples: [https://github.com/antirez/linenoise|linenoise] and |
| 8 | [http://zlib.net/|zLib]) |
| @@ -38,13 +38,14 @@ | |
| 38 | |
| 39 | <li> -Wno-long-long: Fossil uses the 'long long' integer type, which is not strictly ANSI C-89 (defined in C99). |
| 40 | The use of 'long long' resolves many problems with 64-bit arithmetics, especially on 32-bit machines. |
| 41 | (http_ssl.c, sha3.c, shell.c, util.c) |
| 42 | |
| 43 | <li> alloca(): By default, sqlite3.c is compiled with the -DSQLITE_USE_ALLOCA flag to use the alloca() function. |
| 44 | alloca() is not considered ANSI C, and normally not recommended due to portability issues, but |
| 45 | performance and/or memory consumption improvement may be a stronger argument in favor of its usage. |
| 46 | (sqlite3.c) |
| 47 | </ol> |
| 48 | |
| 49 | <li> All comments and identifiers are in English. |
| 50 | |
| @@ -75,11 +76,11 @@ | |
| 75 | |
| 76 | <ol> |
| 77 | <li value=30> Every function has a header comment describing the purpose and use |
| 78 | of the function. |
| 79 | |
| 80 | <li> Function header comment defines the behavior of the function in |
| 81 | sufficient detail to allow the function to be re-implemented from |
| 82 | scratch without reference to the original code. |
| 83 | |
| 84 | <li> Functions that perform dynamic memory allocation (either directly |
| 85 | or indirectly via subfunctions) say so in their header comments. |
| 86 |
| --- www/style.wiki | |
| +++ www/style.wiki | |
| @@ -1,8 +1,8 @@ | |
| 1 | <title>Coding Style</title> |
| 2 | |
| 3 | Fossil source code should follow the style guidelines below. |
| 4 | |
| 5 | <em> The Fossil source tree includes a few files taken from external |
| 6 | sources |
| 7 | (examples: [https://github.com/antirez/linenoise|linenoise] and |
| 8 | [http://zlib.net/|zLib]) |
| @@ -38,13 +38,14 @@ | |
| 38 | |
| 39 | <li> -Wno-long-long: Fossil uses the 'long long' integer type, which is not strictly ANSI C-89 (defined in C99). |
| 40 | The use of 'long long' resolves many problems with 64-bit arithmetics, especially on 32-bit machines. |
| 41 | (http_ssl.c, sha3.c, shell.c, util.c) |
| 42 | |
| 43 | <li> alloca(): By default, sqlite3.c was compiled with the -DSQLITE_USE_ALLOCA flag to use the alloca() function. |
| 44 | This is no longer the case as of 20220119. alloca() is not considered ANSI C, and normally not |
| 45 | recommended due to portability issues, but performance and/or memory consumption |
| 46 | improvement may have been a stronger argument in favor of its usage. |
| 47 | (sqlite3.c) |
| 48 | </ol> |
| 49 | |
| 50 | <li> All comments and identifiers are in English. |
| 51 | |
| @@ -75,11 +76,11 @@ | |
| 76 | |
| 77 | <ol> |
| 78 | <li value=30> Every function has a header comment describing the purpose and use |
| 79 | of the function. |
| 80 | |
| 81 | <li> A function header comment defines the behavior of the function in |
| 82 | sufficient detail to allow the function to be re-implemented from |
| 83 | scratch without reference to the original code. |
| 84 | |
| 85 | <li> Functions that perform dynamic memory allocation (either directly |
| 86 | or indirectly via subfunctions) say so in their header comments. |
| 87 |
+33
-13
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -518,11 +518,11 @@ | ||
| 518 | 518 | pronounced as if it were a single word "gimme" in some dialects of |
| 519 | 519 | English (including the dialect spoken by the original author of Fossil). |
| 520 | 520 | |
| 521 | 521 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 522 | 522 | |
| 523 | -Sync synchronizing unversioned content, the client may send "uvgimme" | |
| 523 | +When synchronizing unversioned content, the client may send "uvgimme" | |
| 524 | 524 | cards to the server. A uvgimme card requests that the server send |
| 525 | 525 | unversioned content to the client. The format of a uvgimme card is |
| 526 | 526 | as follows: |
| 527 | 527 | |
| 528 | 528 | <pre> |
| @@ -570,77 +570,97 @@ | ||
| 570 | 570 | |
| 571 | 571 | <pre> |
| 572 | 572 | <b>reqconfig</b> <i>configuration-name</i> |
| 573 | 573 | </pre> |
| 574 | 574 | |
| 575 | -As of 2018-06-04, the configuration-name must be one of the | |
| 575 | +As of 2024-10-22, the configuration-name must be one of the | |
| 576 | 576 | following values: |
| 577 | 577 | |
| 578 | 578 | <table border=0 align="center"> |
| 579 | 579 | <tr><td valign="top"> |
| 580 | 580 | <ul> |
| 581 | 581 | <li> css |
| 582 | 582 | <li> header |
| 583 | +<li> mainmenu | |
| 583 | 584 | <li> footer |
| 584 | 585 | <li> details |
| 586 | +<li> js | |
| 587 | +<li> default-skin | |
| 585 | 588 | <li> logo-mimetype |
| 586 | 589 | <li> logo-image |
| 587 | 590 | <li> background-mimetype |
| 588 | 591 | <li> background-image |
| 589 | -<li> index-page | |
| 592 | +<li> icon-mimetype | |
| 593 | +<li> icon-image | |
| 590 | 594 | <li> timeline-block-markup |
| 595 | +<li> timeline-date-format | |
| 596 | +<li> timeline-default-style | |
| 597 | +<ul></td><td valign="top"><ul> | |
| 598 | +<li> timeline-dwelltime | |
| 599 | +<li> timeline-closetime | |
| 600 | +<li> timeline-hard-newlines | |
| 591 | 601 | <li> timeline-max-comment |
| 592 | 602 | <li> timeline-plaintext |
| 603 | +<li> timeline-truncate-at-blank | |
| 604 | +<li> timeline-tslink-info | |
| 605 | +<li> timeline-utc | |
| 593 | 606 | <li> adunit |
| 594 | 607 | <li> adunit-omit-if-admin |
| 595 | 608 | <li> adunit-omit-if-user |
| 596 | -<ul></td><td valign="top"><ul> | |
| 597 | -<li> th1-docs | |
| 609 | +<li> default-csp | |
| 610 | +<li> sitemap-extra | |
| 611 | +<li> safe-html | |
| 598 | 612 | <li> th1-hooks |
| 599 | -<li> th1-setup | |
| 600 | -<li> tcl | |
| 601 | -<li> tcl-setup | |
| 613 | +<li> th1-uri-regexp | |
| 602 | 614 | <li> project-name |
| 615 | +<ul></td><td valign="top"><ul> | |
| 603 | 616 | <li> short-project-name |
| 604 | 617 | <li> project-description |
| 605 | 618 | <li> index-page |
| 606 | 619 | <li> manifest |
| 607 | 620 | <li> binary-glob |
| 608 | 621 | <li> clean-glob |
| 609 | 622 | <li> ignore-glob |
| 610 | 623 | <li> keep-glob |
| 611 | 624 | <li> crlf-glob |
| 612 | -<ul></td><td valign="top"><ul> | |
| 613 | 625 | <li> crnl-glob |
| 614 | 626 | <li> encoding-glob |
| 615 | 627 | <li> empty-dirs |
| 616 | 628 | <li> <s title="removed 2020-08, version 2.12.1">allow-symlinks</s> |
| 617 | 629 | <li> dotfiles |
| 618 | 630 | <li> parent-project-code |
| 619 | -<li> parent-projet-name | |
| 631 | +<li> parent-project-name | |
| 632 | +<ul></td><td valign="top"><ul> | |
| 620 | 633 | <li> hash-policy |
| 634 | +<li> comment-format | |
| 635 | +<li> mimetypes | |
| 636 | +<li> forbid-delta-manifests | |
| 621 | 637 | <li> mv-rm-files |
| 622 | 638 | <li> ticket-table |
| 623 | 639 | <li> ticket-common |
| 624 | 640 | <li> ticket-change |
| 625 | 641 | <li> ticket-newpage |
| 626 | 642 | <li> ticket-viewpage |
| 627 | 643 | <li> ticket-editpage |
| 628 | -<ul></td><td valign="top"><ul> | |
| 629 | 644 | <li> ticket-reportlist |
| 630 | 645 | <li> ticket-report-template |
| 631 | 646 | <li> ticket-key-template |
| 632 | 647 | <li> ticket-title-expr |
| 633 | 648 | <li> ticket-closed-expr |
| 649 | +<ul></td><td valign="top"><ul> | |
| 650 | +<li> user-color-map | |
| 634 | 651 | <li> xfer-common-script |
| 635 | 652 | <li> xfer-push-script |
| 636 | 653 | <li> xfer-commit-script |
| 637 | 654 | <li> xfer-ticket-script |
| 638 | 655 | <li> @reportfmt |
| 639 | 656 | <li> @user |
| 640 | 657 | <li> @concealed |
| 641 | 658 | <li> @shun |
| 659 | +<li> @alias | |
| 660 | +<li> @subscriber | |
| 661 | +<li> @interwiki | |
| 642 | 662 | </ul></td></tr> |
| 643 | 663 | </table> |
| 644 | 664 | |
| 645 | 665 | New configuration-names are likely to be added in future releases of |
| 646 | 666 | Fossil. If the server receives a configuration-name that it does not |
| @@ -861,11 +881,11 @@ | ||
| 861 | 881 | <h2 id="strategies">5.0 Synchronization Strategies</h2> |
| 862 | 882 | |
| 863 | 883 | <h3 id="pull-strategy">5.1 Pull</h3> |
| 864 | 884 | |
| 865 | 885 | A typical pull operation proceeds as shown below. Details |
| 866 | -of the actual implementation may very slightly but the gist of | |
| 886 | +of the actual implementation may vary slightly but the gist of | |
| 867 | 887 | a pull is captured in the following steps: |
| 868 | 888 | |
| 869 | 889 | <ol> |
| 870 | 890 | <li>The client sends login and pull cards. |
| 871 | 891 | <li>The client sends a cookie card if it has previously received a cookie. |
| @@ -1071,6 +1091,6 @@ | ||
| 1071 | 1091 | </ol> |
| 1072 | 1092 | |
| 1073 | 1093 | In a complex debugging situation, you can run the command |
| 1074 | 1094 | "fossil sync --transport-command ./debugging_script" where |
| 1075 | 1095 | "debugging_script" is some script of your own that invokes |
| 1076 | -the anomolous behavior your are trying to debug. | |
| 1096 | +the anomalous behavior you are trying to debug. | |
| 1077 | 1097 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -518,11 +518,11 @@ | |
| 518 | pronounced as if it were a single word "gimme" in some dialects of |
| 519 | English (including the dialect spoken by the original author of Fossil). |
| 520 | |
| 521 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 522 | |
| 523 | Sync synchronizing unversioned content, the client may send "uvgimme" |
| 524 | cards to the server. A uvgimme card requests that the server send |
| 525 | unversioned content to the client. The format of a uvgimme card is |
| 526 | as follows: |
| 527 | |
| 528 | <pre> |
| @@ -570,77 +570,97 @@ | |
| 570 | |
| 571 | <pre> |
| 572 | <b>reqconfig</b> <i>configuration-name</i> |
| 573 | </pre> |
| 574 | |
| 575 | As of 2018-06-04, the configuration-name must be one of the |
| 576 | following values: |
| 577 | |
| 578 | <table border=0 align="center"> |
| 579 | <tr><td valign="top"> |
| 580 | <ul> |
| 581 | <li> css |
| 582 | <li> header |
| 583 | <li> footer |
| 584 | <li> details |
| 585 | <li> logo-mimetype |
| 586 | <li> logo-image |
| 587 | <li> background-mimetype |
| 588 | <li> background-image |
| 589 | <li> index-page |
| 590 | <li> timeline-block-markup |
| 591 | <li> timeline-max-comment |
| 592 | <li> timeline-plaintext |
| 593 | <li> adunit |
| 594 | <li> adunit-omit-if-admin |
| 595 | <li> adunit-omit-if-user |
| 596 | <ul></td><td valign="top"><ul> |
| 597 | <li> th1-docs |
| 598 | <li> th1-hooks |
| 599 | <li> th1-setup |
| 600 | <li> tcl |
| 601 | <li> tcl-setup |
| 602 | <li> project-name |
| 603 | <li> short-project-name |
| 604 | <li> project-description |
| 605 | <li> index-page |
| 606 | <li> manifest |
| 607 | <li> binary-glob |
| 608 | <li> clean-glob |
| 609 | <li> ignore-glob |
| 610 | <li> keep-glob |
| 611 | <li> crlf-glob |
| 612 | <ul></td><td valign="top"><ul> |
| 613 | <li> crnl-glob |
| 614 | <li> encoding-glob |
| 615 | <li> empty-dirs |
| 616 | <li> <s title="removed 2020-08, version 2.12.1">allow-symlinks</s> |
| 617 | <li> dotfiles |
| 618 | <li> parent-project-code |
| 619 | <li> parent-projet-name |
| 620 | <li> hash-policy |
| 621 | <li> mv-rm-files |
| 622 | <li> ticket-table |
| 623 | <li> ticket-common |
| 624 | <li> ticket-change |
| 625 | <li> ticket-newpage |
| 626 | <li> ticket-viewpage |
| 627 | <li> ticket-editpage |
| 628 | <ul></td><td valign="top"><ul> |
| 629 | <li> ticket-reportlist |
| 630 | <li> ticket-report-template |
| 631 | <li> ticket-key-template |
| 632 | <li> ticket-title-expr |
| 633 | <li> ticket-closed-expr |
| 634 | <li> xfer-common-script |
| 635 | <li> xfer-push-script |
| 636 | <li> xfer-commit-script |
| 637 | <li> xfer-ticket-script |
| 638 | <li> @reportfmt |
| 639 | <li> @user |
| 640 | <li> @concealed |
| 641 | <li> @shun |
| 642 | </ul></td></tr> |
| 643 | </table> |
| 644 | |
| 645 | New configuration-names are likely to be added in future releases of |
| 646 | Fossil. If the server receives a configuration-name that it does not |
| @@ -861,11 +881,11 @@ | |
| 861 | <h2 id="strategies">5.0 Synchronization Strategies</h2> |
| 862 | |
| 863 | <h3 id="pull-strategy">5.1 Pull</h3> |
| 864 | |
| 865 | A typical pull operation proceeds as shown below. Details |
| 866 | of the actual implementation may very slightly but the gist of |
| 867 | a pull is captured in the following steps: |
| 868 | |
| 869 | <ol> |
| 870 | <li>The client sends login and pull cards. |
| 871 | <li>The client sends a cookie card if it has previously received a cookie. |
| @@ -1071,6 +1091,6 @@ | |
| 1071 | </ol> |
| 1072 | |
| 1073 | In a complex debugging situation, you can run the command |
| 1074 | "fossil sync --transport-command ./debugging_script" where |
| 1075 | "debugging_script" is some script of your own that invokes |
| 1076 | the anomolous behavior your are trying to debug. |
| 1077 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -518,11 +518,11 @@ | |
| 518 | pronounced as if it were a single word "gimme" in some dialects of |
| 519 | English (including the dialect spoken by the original author of Fossil). |
| 520 | |
| 521 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 522 | |
| 523 | When synchronizing unversioned content, the client may send "uvgimme" |
| 524 | cards to the server. A uvgimme card requests that the server send |
| 525 | unversioned content to the client. The format of a uvgimme card is |
| 526 | as follows: |
| 527 | |
| 528 | <pre> |
| @@ -570,77 +570,97 @@ | |
| 570 | |
| 571 | <pre> |
| 572 | <b>reqconfig</b> <i>configuration-name</i> |
| 573 | </pre> |
| 574 | |
| 575 | As of 2024-10-22, the configuration-name must be one of the |
| 576 | following values: |
| 577 | |
| 578 | <table border=0 align="center"> |
| 579 | <tr><td valign="top"> |
| 580 | <ul> |
| 581 | <li> css |
| 582 | <li> header |
| 583 | <li> mainmenu |
| 584 | <li> footer |
| 585 | <li> details |
| 586 | <li> js |
| 587 | <li> default-skin |
| 588 | <li> logo-mimetype |
| 589 | <li> logo-image |
| 590 | <li> background-mimetype |
| 591 | <li> background-image |
| 592 | <li> icon-mimetype |
| 593 | <li> icon-image |
| 594 | <li> timeline-block-markup |
| 595 | <li> timeline-date-format |
| 596 | <li> timeline-default-style |
| 597 | <ul></td><td valign="top"><ul> |
| 598 | <li> timeline-dwelltime |
| 599 | <li> timeline-closetime |
| 600 | <li> timeline-hard-newlines |
| 601 | <li> timeline-max-comment |
| 602 | <li> timeline-plaintext |
| 603 | <li> timeline-truncate-at-blank |
| 604 | <li> timeline-tslink-info |
| 605 | <li> timeline-utc |
| 606 | <li> adunit |
| 607 | <li> adunit-omit-if-admin |
| 608 | <li> adunit-omit-if-user |
| 609 | <li> default-csp |
| 610 | <li> sitemap-extra |
| 611 | <li> safe-html |
| 612 | <li> th1-hooks |
| 613 | <li> th1-uri-regexp |
| 614 | <li> project-name |
| 615 | <ul></td><td valign="top"><ul> |
| 616 | <li> short-project-name |
| 617 | <li> project-description |
| 618 | <li> index-page |
| 619 | <li> manifest |
| 620 | <li> binary-glob |
| 621 | <li> clean-glob |
| 622 | <li> ignore-glob |
| 623 | <li> keep-glob |
| 624 | <li> crlf-glob |
| 625 | <li> crnl-glob |
| 626 | <li> encoding-glob |
| 627 | <li> empty-dirs |
| 628 | <li> <s title="removed 2020-08, version 2.12.1">allow-symlinks</s> |
| 629 | <li> dotfiles |
| 630 | <li> parent-project-code |
| 631 | <li> parent-project-name |
| 632 | <ul></td><td valign="top"><ul> |
| 633 | <li> hash-policy |
| 634 | <li> comment-format |
| 635 | <li> mimetypes |
| 636 | <li> forbid-delta-manifests |
| 637 | <li> mv-rm-files |
| 638 | <li> ticket-table |
| 639 | <li> ticket-common |
| 640 | <li> ticket-change |
| 641 | <li> ticket-newpage |
| 642 | <li> ticket-viewpage |
| 643 | <li> ticket-editpage |
| 644 | <li> ticket-reportlist |
| 645 | <li> ticket-report-template |
| 646 | <li> ticket-key-template |
| 647 | <li> ticket-title-expr |
| 648 | <li> ticket-closed-expr |
| 649 | <ul></td><td valign="top"><ul> |
| 650 | <li> user-color-map |
| 651 | <li> xfer-common-script |
| 652 | <li> xfer-push-script |
| 653 | <li> xfer-commit-script |
| 654 | <li> xfer-ticket-script |
| 655 | <li> @reportfmt |
| 656 | <li> @user |
| 657 | <li> @concealed |
| 658 | <li> @shun |
| 659 | <li> @alias |
| 660 | <li> @subscriber |
| 661 | <li> @interwiki |
| 662 | </ul></td></tr> |
| 663 | </table> |
| 664 | |
| 665 | New configuration-names are likely to be added in future releases of |
| 666 | Fossil. If the server receives a configuration-name that it does not |
| @@ -861,11 +881,11 @@ | |
| 881 | <h2 id="strategies">5.0 Synchronization Strategies</h2> |
| 882 | |
| 883 | <h3 id="pull-strategy">5.1 Pull</h3> |
| 884 | |
| 885 | A typical pull operation proceeds as shown below. Details |
| 886 | of the actual implementation may vary slightly but the gist of |
| 887 | a pull is captured in the following steps: |
| 888 | |
| 889 | <ol> |
| 890 | <li>The client sends login and pull cards. |
| 891 | <li>The client sends a cookie card if it has previously received a cookie. |
| @@ -1071,6 +1091,6 @@ | |
| 1091 | </ol> |
| 1092 | |
| 1093 | In a complex debugging situation, you can run the command |
| 1094 | "fossil sync --transport-command ./debugging_script" where |
| 1095 | "debugging_script" is some script of your own that invokes |
| 1096 | the anomalous behavior you are trying to debug. |
| 1097 |
+1
-1
| --- www/tech_overview.wiki | ||
| +++ www/tech_overview.wiki | ||
| @@ -142,11 +142,11 @@ | ||
| 142 | 142 | if the ~/.fossil file does not already exist |
| 143 | 143 | * Otherwise, use the traditional unix name of "~/.fossil" |
| 144 | 144 | |
| 145 | 145 | This algorithm is complex due to the need for historical compatibility. |
| 146 | 146 | Originally, the database was always just "~/.fossil". Then support |
| 147 | -for the FOSSIL_HOME environment variable as added. Later, support for the | |
| 147 | +for the FOSSIL_HOME environment variable was added. Later, support for the | |
| 148 | 148 | [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames] |
| 149 | 149 | was added. Each of these changes needed to continue to support legacy |
| 150 | 150 | installations. |
| 151 | 151 | |
| 152 | 152 | On Windows, the configuration database is the first of the following |
| 153 | 153 |
| --- www/tech_overview.wiki | |
| +++ www/tech_overview.wiki | |
| @@ -142,11 +142,11 @@ | |
| 142 | if the ~/.fossil file does not already exist |
| 143 | * Otherwise, use the traditional unix name of "~/.fossil" |
| 144 | |
| 145 | This algorithm is complex due to the need for historical compatibility. |
| 146 | Originally, the database was always just "~/.fossil". Then support |
| 147 | for the FOSSIL_HOME environment variable as added. Later, support for the |
| 148 | [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames] |
| 149 | was added. Each of these changes needed to continue to support legacy |
| 150 | installations. |
| 151 | |
| 152 | On Windows, the configuration database is the first of the following |
| 153 |
| --- www/tech_overview.wiki | |
| +++ www/tech_overview.wiki | |
| @@ -142,11 +142,11 @@ | |
| 142 | if the ~/.fossil file does not already exist |
| 143 | * Otherwise, use the traditional unix name of "~/.fossil" |
| 144 | |
| 145 | This algorithm is complex due to the need for historical compatibility. |
| 146 | Originally, the database was always just "~/.fossil". Then support |
| 147 | for the FOSSIL_HOME environment variable was added. Later, support for the |
| 148 | [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames] |
| 149 | was added. Each of these changes needed to continue to support legacy |
| 150 | installations. |
| 151 | |
| 152 | On Windows, the configuration database is the first of the following |
| 153 |
+5
-5
| --- www/th1.md | ||
| +++ www/th1.md | ||
| @@ -32,11 +32,11 @@ | ||
| 32 | 32 | the computation, then converting the result back into a string. (This might |
| 33 | 33 | seem inefficient, but it is faster than people imagine, and numeric |
| 34 | 34 | computations do not come up very often for the kinds of work that TH1 does, |
| 35 | 35 | so it has never been a factor.) |
| 36 | 36 | |
| 37 | -A TH1 script consist of a sequence of commands. | |
| 37 | +A TH1 script consists of a sequence of commands. | |
| 38 | 38 | Each command is terminated by the first *unescaped* newline or ";" character. |
| 39 | 39 | The text of the command (excluding the newline or semicolon terminator) |
| 40 | 40 | is broken into space-separated tokens. The first token is the command |
| 41 | 41 | name and subsequent tokens are the arguments. In this sense, TH1 syntax |
| 42 | 42 | is similar to the familiar command-line shell syntax. |
| @@ -288,11 +288,11 @@ | ||
| 288 | 288 | The capability expression is a list. Each term of the list is a |
| 289 | 289 | cluster of [capability letters](./caps/ref.html). |
| 290 | 290 | The overall expression is true if any |
| 291 | 291 | one term is true. A single term is true if all letters within that |
| 292 | 292 | term are true. Or, if the term begins with "!", then the term is true |
| 293 | -if none of the terms or true. Or, if the term begins with "@" then | |
| 293 | +if none of the terms are true. Or, if the term begins with "@" then | |
| 294 | 294 | the term is true if all of the capability letters in that term are |
| 295 | 295 | available to the "anonymous" user. Or, if the term is "*" then it is |
| 296 | 296 | always true. |
| 297 | 297 | |
| 298 | 298 | Examples: |
| @@ -383,11 +383,11 @@ | ||
| 383 | 383 | <a id="date"></a>TH1 date Command |
| 384 | 384 | ----------------------------------- |
| 385 | 385 | |
| 386 | 386 | * date ?-local? |
| 387 | 387 | |
| 388 | -Return a strings which is the current time and date. If the -local | |
| 388 | +Return a string which is the current time and date. If the -local | |
| 389 | 389 | option is used, the date appears using localtime instead of UTC. |
| 390 | 390 | |
| 391 | 391 | <a id="decorate"></a>TH1 decorate Command |
| 392 | 392 | ------------------------------------------- |
| 393 | 393 | |
| @@ -570,11 +570,11 @@ | ||
| 570 | 570 | <a id="linecount"></a>TH1 linecount Command |
| 571 | 571 | --------------------------------------------- |
| 572 | 572 | |
| 573 | 573 | * linecount STRING MAX MIN |
| 574 | 574 | |
| 575 | -Returns one more than the number of \n characters in STRING. But | |
| 575 | +Returns one more than the number of `\n` characters in STRING. But | |
| 576 | 576 | never returns less than MIN or more than MAX. |
| 577 | 577 | |
| 578 | 578 | <a id="markdown"></a>TH1 markdown Command |
| 579 | 579 | ------------------------------------------- |
| 580 | 580 | |
| @@ -842,11 +842,11 @@ | ||
| 842 | 842 | ----------------------------------------------- |
| 843 | 843 | |
| 844 | 844 | * verifyCsrf |
| 845 | 845 | |
| 846 | 846 | Before using the results of a form, first call this command to verify |
| 847 | -that this Anti-CSRF token is present and is valid. If the Anti-CSRF token | |
| 847 | +that the Anti-CSRF token is present and is valid. If the Anti-CSRF token | |
| 848 | 848 | is missing or is incorrect, that indicates a cross-site scripting attack. |
| 849 | 849 | If the event of an attack is detected, an error message is generated and |
| 850 | 850 | all further processing is aborted. |
| 851 | 851 | |
| 852 | 852 | <a id="verifyLogin"></a>TH1 verifyLogin Command |
| 853 | 853 |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -32,11 +32,11 @@ | |
| 32 | the computation, then converting the result back into a string. (This might |
| 33 | seem inefficient, but it is faster than people imagine, and numeric |
| 34 | computations do not come up very often for the kinds of work that TH1 does, |
| 35 | so it has never been a factor.) |
| 36 | |
| 37 | A TH1 script consist of a sequence of commands. |
| 38 | Each command is terminated by the first *unescaped* newline or ";" character. |
| 39 | The text of the command (excluding the newline or semicolon terminator) |
| 40 | is broken into space-separated tokens. The first token is the command |
| 41 | name and subsequent tokens are the arguments. In this sense, TH1 syntax |
| 42 | is similar to the familiar command-line shell syntax. |
| @@ -288,11 +288,11 @@ | |
| 288 | The capability expression is a list. Each term of the list is a |
| 289 | cluster of [capability letters](./caps/ref.html). |
| 290 | The overall expression is true if any |
| 291 | one term is true. A single term is true if all letters within that |
| 292 | term are true. Or, if the term begins with "!", then the term is true |
| 293 | if none of the terms or true. Or, if the term begins with "@" then |
| 294 | the term is true if all of the capability letters in that term are |
| 295 | available to the "anonymous" user. Or, if the term is "*" then it is |
| 296 | always true. |
| 297 | |
| 298 | Examples: |
| @@ -383,11 +383,11 @@ | |
| 383 | <a id="date"></a>TH1 date Command |
| 384 | ----------------------------------- |
| 385 | |
| 386 | * date ?-local? |
| 387 | |
| 388 | Return a strings which is the current time and date. If the -local |
| 389 | option is used, the date appears using localtime instead of UTC. |
| 390 | |
| 391 | <a id="decorate"></a>TH1 decorate Command |
| 392 | ------------------------------------------- |
| 393 | |
| @@ -570,11 +570,11 @@ | |
| 570 | <a id="linecount"></a>TH1 linecount Command |
| 571 | --------------------------------------------- |
| 572 | |
| 573 | * linecount STRING MAX MIN |
| 574 | |
| 575 | Returns one more than the number of \n characters in STRING. But |
| 576 | never returns less than MIN or more than MAX. |
| 577 | |
| 578 | <a id="markdown"></a>TH1 markdown Command |
| 579 | ------------------------------------------- |
| 580 | |
| @@ -842,11 +842,11 @@ | |
| 842 | ----------------------------------------------- |
| 843 | |
| 844 | * verifyCsrf |
| 845 | |
| 846 | Before using the results of a form, first call this command to verify |
| 847 | that this Anti-CSRF token is present and is valid. If the Anti-CSRF token |
| 848 | is missing or is incorrect, that indicates a cross-site scripting attack. |
| 849 | If the event of an attack is detected, an error message is generated and |
| 850 | all further processing is aborted. |
| 851 | |
| 852 | <a id="verifyLogin"></a>TH1 verifyLogin Command |
| 853 |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -32,11 +32,11 @@ | |
| 32 | the computation, then converting the result back into a string. (This might |
| 33 | seem inefficient, but it is faster than people imagine, and numeric |
| 34 | computations do not come up very often for the kinds of work that TH1 does, |
| 35 | so it has never been a factor.) |
| 36 | |
| 37 | A TH1 script consists of a sequence of commands. |
| 38 | Each command is terminated by the first *unescaped* newline or ";" character. |
| 39 | The text of the command (excluding the newline or semicolon terminator) |
| 40 | is broken into space-separated tokens. The first token is the command |
| 41 | name and subsequent tokens are the arguments. In this sense, TH1 syntax |
| 42 | is similar to the familiar command-line shell syntax. |
| @@ -288,11 +288,11 @@ | |
| 288 | The capability expression is a list. Each term of the list is a |
| 289 | cluster of [capability letters](./caps/ref.html). |
| 290 | The overall expression is true if any |
| 291 | one term is true. A single term is true if all letters within that |
| 292 | term are true. Or, if the term begins with "!", then the term is true |
| 293 | if none of the terms are true. Or, if the term begins with "@" then |
| 294 | the term is true if all of the capability letters in that term are |
| 295 | available to the "anonymous" user. Or, if the term is "*" then it is |
| 296 | always true. |
| 297 | |
| 298 | Examples: |
| @@ -383,11 +383,11 @@ | |
| 383 | <a id="date"></a>TH1 date Command |
| 384 | ----------------------------------- |
| 385 | |
| 386 | * date ?-local? |
| 387 | |
| 388 | Return a string which is the current time and date. If the -local |
| 389 | option is used, the date appears using localtime instead of UTC. |
| 390 | |
| 391 | <a id="decorate"></a>TH1 decorate Command |
| 392 | ------------------------------------------- |
| 393 | |
| @@ -570,11 +570,11 @@ | |
| 570 | <a id="linecount"></a>TH1 linecount Command |
| 571 | --------------------------------------------- |
| 572 | |
| 573 | * linecount STRING MAX MIN |
| 574 | |
| 575 | Returns one more than the number of `\n` characters in STRING. But |
| 576 | never returns less than MIN or more than MAX. |
| 577 | |
| 578 | <a id="markdown"></a>TH1 markdown Command |
| 579 | ------------------------------------------- |
| 580 | |
| @@ -842,11 +842,11 @@ | |
| 842 | ----------------------------------------------- |
| 843 | |
| 844 | * verifyCsrf |
| 845 | |
| 846 | Before using the results of a form, first call this command to verify |
| 847 | that the Anti-CSRF token is present and is valid. If the Anti-CSRF token |
| 848 | is missing or is incorrect, that indicates a cross-site scripting attack. |
| 849 | If the event of an attack is detected, an error message is generated and |
| 850 | all further processing is aborted. |
| 851 | |
| 852 | <a id="verifyLogin"></a>TH1 verifyLogin Command |
| 853 |
+2
-2
| --- www/theory1.wiki | ||
| +++ www/theory1.wiki | ||
| @@ -73,14 +73,14 @@ | ||
| 73 | 73 | implementation detail which currently happens to use SQLite. |
| 74 | 74 | |
| 75 | 75 | Another way to think of the relational tables in a Fossil repository is |
| 76 | 76 | as an index for the artifacts. Without the relational tables, |
| 77 | 77 | to generate a report like a timeline would require scanning every artifact - |
| 78 | -the equivalent of a full table scan. The relational tables hold pointers | |
| 78 | +the equivalent of a full table scan. The relational tables hold pointers to | |
| 79 | 79 | the relevant artifacts in presorted order so that generating a timeline |
| 80 | 80 | is much more efficient. So like an index in a relational database, the |
| 81 | -relational tables in an Fossil repository do not add any new information, | |
| 81 | +relational tables in a Fossil repository do not add any new information, | |
| 82 | 82 | they merely make the information in the artifacts faster and easier to |
| 83 | 83 | look up. |
| 84 | 84 | |
| 85 | 85 | Fossil is not "based" on SQLite. Fossil simply exploits SQLite as |
| 86 | 86 | a powerful tool to make the implementation easier. |
| 87 | 87 |
| --- www/theory1.wiki | |
| +++ www/theory1.wiki | |
| @@ -73,14 +73,14 @@ | |
| 73 | implementation detail which currently happens to use SQLite. |
| 74 | |
| 75 | Another way to think of the relational tables in a Fossil repository is |
| 76 | as an index for the artifacts. Without the relational tables, |
| 77 | to generate a report like a timeline would require scanning every artifact - |
| 78 | the equivalent of a full table scan. The relational tables hold pointers |
| 79 | the relevant artifacts in presorted order so that generating a timeline |
| 80 | is much more efficient. So like an index in a relational database, the |
| 81 | relational tables in an Fossil repository do not add any new information, |
| 82 | they merely make the information in the artifacts faster and easier to |
| 83 | look up. |
| 84 | |
| 85 | Fossil is not "based" on SQLite. Fossil simply exploits SQLite as |
| 86 | a powerful tool to make the implementation easier. |
| 87 |
| --- www/theory1.wiki | |
| +++ www/theory1.wiki | |
| @@ -73,14 +73,14 @@ | |
| 73 | implementation detail which currently happens to use SQLite. |
| 74 | |
| 75 | Another way to think of the relational tables in a Fossil repository is |
| 76 | as an index for the artifacts. Without the relational tables, |
| 77 | to generate a report like a timeline would require scanning every artifact - |
| 78 | the equivalent of a full table scan. The relational tables hold pointers to |
| 79 | the relevant artifacts in presorted order so that generating a timeline |
| 80 | is much more efficient. So like an index in a relational database, the |
| 81 | relational tables in a Fossil repository do not add any new information, |
| 82 | they merely make the information in the artifacts faster and easier to |
| 83 | look up. |
| 84 | |
| 85 | Fossil is not "based" on SQLite. Fossil simply exploits SQLite as |
| 86 | a powerful tool to make the implementation easier. |
| 87 |
+2
-2
| --- www/tickets.wiki | ||
| +++ www/tickets.wiki | ||
| @@ -27,11 +27,11 @@ | ||
| 27 | 27 | that key, or the value may be appended to the prior value. |
| 28 | 28 | |
| 29 | 29 | <h2>2.0 Ticket Tables</h2> |
| 30 | 30 | |
| 31 | 31 | The low-level artifact format for ticket content is tedious and |
| 32 | -cumbersome to access in real time. To facility reporting and display | |
| 32 | +cumbersome to access in real time. To facilitate reporting and display | |
| 33 | 33 | of tickets, the low-level artifact information is collected and |
| 34 | 34 | summarized in a pair of SQL tables in each local repository. Display |
| 35 | 35 | and reporting of tickets is accomplished by querying these two tables. |
| 36 | 36 | |
| 37 | 37 | Note that only the low-level ticket change artifacts are synced. The |
| @@ -191,9 +191,9 @@ | ||
| 191 | 191 | comment according to its chosen format. Hence, Fossil was enhanced to |
| 192 | 192 | support the "new-style" tickets. |
| 193 | 193 | |
| 194 | 194 | The TICKETCHNG table was added to support new-style tickets. In the new |
| 195 | 195 | style, comment text is stored with the "icomment" (for "Incremental Comment") |
| 196 | -key and appears separately, and with its on mimetype, in multiple rows | |
| 196 | +key and appears separately, and with its own mimetype, in multiple rows | |
| 197 | 197 | of the TICKETCHNG table. It then falls to the TH1 script code on the |
| 198 | 198 | View Ticket Page to query the TICKETCHNG table and extract and format |
| 199 | 199 | the various comments in time stamp order. |
| 200 | 200 |
| --- www/tickets.wiki | |
| +++ www/tickets.wiki | |
| @@ -27,11 +27,11 @@ | |
| 27 | that key, or the value may be appended to the prior value. |
| 28 | |
| 29 | <h2>2.0 Ticket Tables</h2> |
| 30 | |
| 31 | The low-level artifact format for ticket content is tedious and |
| 32 | cumbersome to access in real time. To facility reporting and display |
| 33 | of tickets, the low-level artifact information is collected and |
| 34 | summarized in a pair of SQL tables in each local repository. Display |
| 35 | and reporting of tickets is accomplished by querying these two tables. |
| 36 | |
| 37 | Note that only the low-level ticket change artifacts are synced. The |
| @@ -191,9 +191,9 @@ | |
| 191 | comment according to its chosen format. Hence, Fossil was enhanced to |
| 192 | support the "new-style" tickets. |
| 193 | |
| 194 | The TICKETCHNG table was added to support new-style tickets. In the new |
| 195 | style, comment text is stored with the "icomment" (for "Incremental Comment") |
| 196 | key and appears separately, and with its on mimetype, in multiple rows |
| 197 | of the TICKETCHNG table. It then falls to the TH1 script code on the |
| 198 | View Ticket Page to query the TICKETCHNG table and extract and format |
| 199 | the various comments in time stamp order. |
| 200 |
| --- www/tickets.wiki | |
| +++ www/tickets.wiki | |
| @@ -27,11 +27,11 @@ | |
| 27 | that key, or the value may be appended to the prior value. |
| 28 | |
| 29 | <h2>2.0 Ticket Tables</h2> |
| 30 | |
| 31 | The low-level artifact format for ticket content is tedious and |
| 32 | cumbersome to access in real time. To facilitate reporting and display |
| 33 | of tickets, the low-level artifact information is collected and |
| 34 | summarized in a pair of SQL tables in each local repository. Display |
| 35 | and reporting of tickets is accomplished by querying these two tables. |
| 36 | |
| 37 | Note that only the low-level ticket change artifacts are synced. The |
| @@ -191,9 +191,9 @@ | |
| 191 | comment according to its chosen format. Hence, Fossil was enhanced to |
| 192 | support the "new-style" tickets. |
| 193 | |
| 194 | The TICKETCHNG table was added to support new-style tickets. In the new |
| 195 | style, comment text is stored with the "icomment" (for "Incremental Comment") |
| 196 | key and appears separately, and with its own mimetype, in multiple rows |
| 197 | of the TICKETCHNG table. It then falls to the TH1 script code on the |
| 198 | View Ticket Page to query the TICKETCHNG table and extract and format |
| 199 | the various comments in time stamp order. |
| 200 |
+2
-2
| --- www/unvers.wiki | ||
| +++ www/unvers.wiki | ||
| @@ -53,12 +53,12 @@ | ||
| 53 | 53 | [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. |
| 54 | 54 | The "-u" option is only available on "sync" and "clone". |
| 55 | 55 | A rough equivalent of an unversioned pull would be the |
| 56 | 56 | [/help?cmd=unversioned|fossil unversioned revert] command. The |
| 57 | 57 | "unversioned revert" |
| 58 | -command causes the unversioned content on the local repository to overwritten | |
| 59 | -by the unversioned content found on the remote repository. | |
| 58 | +command causes the unversioned content on the local repository to be | |
| 59 | +overwritten by the unversioned content found on the remote repository. | |
| 60 | 60 | |
| 61 | 61 | Beware that because unversioned file sync is an uncommonly dangerous |
| 62 | 62 | capability — there being no history to revert to in the case of human |
| 63 | 63 | error — even the all-powerful Fossil "setup" user does not get |
| 64 | 64 | unversioned file sync capability by default. See |
| 65 | 65 |
| --- www/unvers.wiki | |
| +++ www/unvers.wiki | |
| @@ -53,12 +53,12 @@ | |
| 53 | [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. |
| 54 | The "-u" option is only available on "sync" and "clone". |
| 55 | A rough equivalent of an unversioned pull would be the |
| 56 | [/help?cmd=unversioned|fossil unversioned revert] command. The |
| 57 | "unversioned revert" |
| 58 | command causes the unversioned content on the local repository to overwritten |
| 59 | by the unversioned content found on the remote repository. |
| 60 | |
| 61 | Beware that because unversioned file sync is an uncommonly dangerous |
| 62 | capability — there being no history to revert to in the case of human |
| 63 | error — even the all-powerful Fossil "setup" user does not get |
| 64 | unversioned file sync capability by default. See |
| 65 |
| --- www/unvers.wiki | |
| +++ www/unvers.wiki | |
| @@ -53,12 +53,12 @@ | |
| 53 | [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. |
| 54 | The "-u" option is only available on "sync" and "clone". |
| 55 | A rough equivalent of an unversioned pull would be the |
| 56 | [/help?cmd=unversioned|fossil unversioned revert] command. The |
| 57 | "unversioned revert" |
| 58 | command causes the unversioned content on the local repository to be |
| 59 | overwritten by the unversioned content found on the remote repository. |
| 60 | |
| 61 | Beware that because unversioned file sync is an uncommonly dangerous |
| 62 | capability — there being no history to revert to in the case of human |
| 63 | error — even the all-powerful Fossil "setup" user does not get |
| 64 | unversioned file sync capability by default. See |
| 65 |
+1
-1
| --- www/userlinks.wiki | ||
| +++ www/userlinks.wiki | ||
| @@ -23,11 +23,11 @@ | ||
| 23 | 23 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 24 | 24 | * An [./event.wiki | Event] is a special kind of wiki page associated |
| 25 | 25 | with a point in time rather than a name. |
| 26 | 26 | * [./settings.wiki | Settings] control the behaviour of Fossil. |
| 27 | 27 | * [./ssl.wiki | Use SSL] to encrypt communication with the server. |
| 28 | - * The [https://fossil-scm.org/forum|Fossil forum] is, as of mid-2018, | |
| 28 | + * The [https://fossil-scm.org/forum|Fossil forum] is, as of late-2024, | |
| 29 | 29 | the project's central communication channel. The |
| 30 | 30 | [https://www.mail-archive.com/[email protected] |
| 31 | 31 | | read-only mailing list archives] house discussions spanning Fossil's |
| 32 | 32 | first decade. |
| 33 | 33 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 34 | 34 |
| --- www/userlinks.wiki | |
| +++ www/userlinks.wiki | |
| @@ -23,11 +23,11 @@ | |
| 23 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 24 | * An [./event.wiki | Event] is a special kind of wiki page associated |
| 25 | with a point in time rather than a name. |
| 26 | * [./settings.wiki | Settings] control the behaviour of Fossil. |
| 27 | * [./ssl.wiki | Use SSL] to encrypt communication with the server. |
| 28 | * The [https://fossil-scm.org/forum|Fossil forum] is, as of mid-2018, |
| 29 | the project's central communication channel. The |
| 30 | [https://www.mail-archive.com/[email protected] |
| 31 | | read-only mailing list archives] house discussions spanning Fossil's |
| 32 | first decade. |
| 33 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 34 |
| --- www/userlinks.wiki | |
| +++ www/userlinks.wiki | |
| @@ -23,11 +23,11 @@ | |
| 23 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 24 | * An [./event.wiki | Event] is a special kind of wiki page associated |
| 25 | with a point in time rather than a name. |
| 26 | * [./settings.wiki | Settings] control the behaviour of Fossil. |
| 27 | * [./ssl.wiki | Use SSL] to encrypt communication with the server. |
| 28 | * The [https://fossil-scm.org/forum|Fossil forum] is, as of late-2024, |
| 29 | the project's central communication channel. The |
| 30 | [https://www.mail-archive.com/[email protected] |
| 31 | | read-only mailing list archives] house discussions spanning Fossil's |
| 32 | first decade. |
| 33 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 34 |
+5
-5
| --- www/webui.wiki | ||
| +++ www/webui.wiki | ||
| @@ -116,15 +116,15 @@ | ||
| 116 | 116 | The "Files" link on the menu allows you to browse through the <b>file |
| 117 | 117 | hierarchy</b> of the project and to view complete changes histories on |
| 118 | 118 | individual files, with hyperlinks to the check-ins that made those |
| 119 | 119 | changes, and with diffs and annotated diffs between versions. |
| 120 | 120 | |
| 121 | -The web interface supports [./embeddeddoc.wiki | embedded documentation]. | |
| 122 | -Embedded documentation is documentation files (usually in wiki format) | |
| 123 | -that are checked into project as part of the source tree. Such files | |
| 124 | -can be viewed as if they were ordinary web pages. This document that | |
| 125 | -you are now reading is an example of embedded documentation. | |
| 121 | +The web interface supports [./embeddeddoc.wiki | embedded documentation] | |
| 122 | +files (usually in wiki format) that are checked into the project as | |
| 123 | +part of the source tree. Such files can be viewed as if they were | |
| 124 | +ordinary web pages. This document that you are now reading is an | |
| 125 | +example of embedded documentation. | |
| 126 | 126 | |
| 127 | 127 | <h2>Customizing The Web Interface Appearance</h2> |
| 128 | 128 | |
| 129 | 129 | Users with appropriate permissions can customize the look and feel of |
| 130 | 130 | the web interface using the "Admin" link on the main menu of the web |
| 131 | 131 |
| --- www/webui.wiki | |
| +++ www/webui.wiki | |
| @@ -116,15 +116,15 @@ | |
| 116 | The "Files" link on the menu allows you to browse through the <b>file |
| 117 | hierarchy</b> of the project and to view complete changes histories on |
| 118 | individual files, with hyperlinks to the check-ins that made those |
| 119 | changes, and with diffs and annotated diffs between versions. |
| 120 | |
| 121 | The web interface supports [./embeddeddoc.wiki | embedded documentation]. |
| 122 | Embedded documentation is documentation files (usually in wiki format) |
| 123 | that are checked into project as part of the source tree. Such files |
| 124 | can be viewed as if they were ordinary web pages. This document that |
| 125 | you are now reading is an example of embedded documentation. |
| 126 | |
| 127 | <h2>Customizing The Web Interface Appearance</h2> |
| 128 | |
| 129 | Users with appropriate permissions can customize the look and feel of |
| 130 | the web interface using the "Admin" link on the main menu of the web |
| 131 |
| --- www/webui.wiki | |
| +++ www/webui.wiki | |
| @@ -116,15 +116,15 @@ | |
| 116 | The "Files" link on the menu allows you to browse through the <b>file |
| 117 | hierarchy</b> of the project and to view complete changes histories on |
| 118 | individual files, with hyperlinks to the check-ins that made those |
| 119 | changes, and with diffs and annotated diffs between versions. |
| 120 | |
| 121 | The web interface supports [./embeddeddoc.wiki | embedded documentation] |
| 122 | files (usually in wiki format) that are checked into the project as |
| 123 | part of the source tree. Such files can be viewed as if they were |
| 124 | ordinary web pages. This document that you are now reading is an |
| 125 | example of embedded documentation. |
| 126 | |
| 127 | <h2>Customizing The Web Interface Appearance</h2> |
| 128 | |
| 129 | Users with appropriate permissions can customize the look and feel of |
| 130 | the web interface using the "Admin" link on the main menu of the web |
| 131 |
+7
-4
| --- www/whyallinone.md | ||
| +++ www/whyallinone.md | ||
| @@ -141,11 +141,11 @@ | ||
| 141 | 141 | |
| 142 | 142 | 7. Hosting all of these elements within a single service gives a |
| 143 | 143 | consistent look-and-feel across all aspects of the project. |
| 144 | 144 | |
| 145 | 145 | Skinning independent software packages’ web interfaces to make |
| 146 | - them appear unified is more work than skinning everything once, as | |
| 146 | + them appear unified is more work than [skinning] everything once, as | |
| 147 | 147 | in Fossil, and even then, you can’t make independently-developed |
| 148 | 148 | software look like it was produced by a single entity without |
| 149 | 149 | resorting to heroic levels of customization. If you use a separate |
| 150 | 150 | DVCS web front end, chat system, forum manager, documentation |
| 151 | 151 | system, ticket tracker, and so on, you are likely to be relegated |
| @@ -217,16 +217,19 @@ | ||
| 217 | 217 | configuration change to its [role-based access control system](./caps/). |
| 218 | 218 | When you’re ready to turn these additional features on, you can do so |
| 219 | 219 | with a few mouse clicks. |
| 220 | 220 | |
| 221 | 221 | Because Fossil is web-native out of the box, if you’ve delegated these |
| 222 | -features to outside systems to flesh out Git’s DVCS-only nature, Fossil | |
| 223 | -can link out to these systems, and they back into Fossil, letting you | |
| 224 | -use Fossil in the same DVCS-only mode. | |
| 222 | +features to outside systems to flesh out Git’s DVCS-only nature, you are | |
| 223 | +free to do the same with Fossil. One of the many things the [skinning] | |
| 224 | +facility allows is replacing the built-in links to the wiki, forum, | |
| 225 | +ticket system, etc. with links to external systems. How easy those | |
| 226 | +systems make it to link back into Fossil is up to their developers. | |
| 225 | 227 | |
| 226 | 228 | [Discord]: https://discord.com/ |
| 227 | 229 | [edoc]: ./embeddeddoc.wiki |
| 228 | 230 | [Jira]: https://www.atlassian.com/software/jira |
| 229 | 231 | [MediaWiki]: https://www.mediawiki.org/ |
| 232 | +[skinning]: ./customskin.md | |
| 230 | 233 | [Sphinx]: https://www.sphinx-doc.org/en/master/ |
| 231 | 234 | [SSO]: https://en.wikipedia.org/wiki/Single_sign-on |
| 232 | 235 | [srckl]: https://www.sqlite.org/src/ext/checklist/top/index |
| 233 | 236 |
| --- www/whyallinone.md | |
| +++ www/whyallinone.md | |
| @@ -141,11 +141,11 @@ | |
| 141 | |
| 142 | 7. Hosting all of these elements within a single service gives a |
| 143 | consistent look-and-feel across all aspects of the project. |
| 144 | |
| 145 | Skinning independent software packages’ web interfaces to make |
| 146 | them appear unified is more work than skinning everything once, as |
| 147 | in Fossil, and even then, you can’t make independently-developed |
| 148 | software look like it was produced by a single entity without |
| 149 | resorting to heroic levels of customization. If you use a separate |
| 150 | DVCS web front end, chat system, forum manager, documentation |
| 151 | system, ticket tracker, and so on, you are likely to be relegated |
| @@ -217,16 +217,19 @@ | |
| 217 | configuration change to its [role-based access control system](./caps/). |
| 218 | When you’re ready to turn these additional features on, you can do so |
| 219 | with a few mouse clicks. |
| 220 | |
| 221 | Because Fossil is web-native out of the box, if you’ve delegated these |
| 222 | features to outside systems to flesh out Git’s DVCS-only nature, Fossil |
| 223 | can link out to these systems, and they back into Fossil, letting you |
| 224 | use Fossil in the same DVCS-only mode. |
| 225 | |
| 226 | [Discord]: https://discord.com/ |
| 227 | [edoc]: ./embeddeddoc.wiki |
| 228 | [Jira]: https://www.atlassian.com/software/jira |
| 229 | [MediaWiki]: https://www.mediawiki.org/ |
| 230 | [Sphinx]: https://www.sphinx-doc.org/en/master/ |
| 231 | [SSO]: https://en.wikipedia.org/wiki/Single_sign-on |
| 232 | [srckl]: https://www.sqlite.org/src/ext/checklist/top/index |
| 233 |
| --- www/whyallinone.md | |
| +++ www/whyallinone.md | |
| @@ -141,11 +141,11 @@ | |
| 141 | |
| 142 | 7. Hosting all of these elements within a single service gives a |
| 143 | consistent look-and-feel across all aspects of the project. |
| 144 | |
| 145 | Skinning independent software packages’ web interfaces to make |
| 146 | them appear unified is more work than [skinning] everything once, as |
| 147 | in Fossil, and even then, you can’t make independently-developed |
| 148 | software look like it was produced by a single entity without |
| 149 | resorting to heroic levels of customization. If you use a separate |
| 150 | DVCS web front end, chat system, forum manager, documentation |
| 151 | system, ticket tracker, and so on, you are likely to be relegated |
| @@ -217,16 +217,19 @@ | |
| 217 | configuration change to its [role-based access control system](./caps/). |
| 218 | When you’re ready to turn these additional features on, you can do so |
| 219 | with a few mouse clicks. |
| 220 | |
| 221 | Because Fossil is web-native out of the box, if you’ve delegated these |
| 222 | features to outside systems to flesh out Git’s DVCS-only nature, you are |
| 223 | free to do the same with Fossil. One of the many things the [skinning] |
| 224 | facility allows is replacing the built-in links to the wiki, forum, |
| 225 | ticket system, etc. with links to external systems. How easy those |
| 226 | systems make it to link back into Fossil is up to their developers. |
| 227 | |
| 228 | [Discord]: https://discord.com/ |
| 229 | [edoc]: ./embeddeddoc.wiki |
| 230 | [Jira]: https://www.atlassian.com/software/jira |
| 231 | [MediaWiki]: https://www.mediawiki.org/ |
| 232 | [skinning]: ./customskin.md |
| 233 | [Sphinx]: https://www.sphinx-doc.org/en/master/ |
| 234 | [SSO]: https://en.wikipedia.org/wiki/Single_sign-on |
| 235 | [srckl]: https://www.sqlite.org/src/ext/checklist/top/index |
| 236 |
+1
-1
| --- www/wikitheory.wiki | ||
| +++ www/wikitheory.wiki | ||
| @@ -84,7 +84,7 @@ | ||
| 84 | 84 | wiki page at the top of the timeline |
| 85 | 85 | * [/info/19c60b7fc9e2] shows the text of the |
| 86 | 86 | [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...] |
| 87 | 87 | wiki page in the "About" section. |
| 88 | 88 | |
| 89 | -This special wiki pages are very useful for recording historical | |
| 89 | +These special wiki pages are very useful for recording historical | |
| 90 | 90 | notes. |
| 91 | 91 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -84,7 +84,7 @@ | |
| 84 | wiki page at the top of the timeline |
| 85 | * [/info/19c60b7fc9e2] shows the text of the |
| 86 | [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...] |
| 87 | wiki page in the "About" section. |
| 88 | |
| 89 | This special wiki pages are very useful for recording historical |
| 90 | notes. |
| 91 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -84,7 +84,7 @@ | |
| 84 | wiki page at the top of the timeline |
| 85 | * [/info/19c60b7fc9e2] shows the text of the |
| 86 | [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...] |
| 87 | wiki page in the "About" section. |
| 88 | |
| 89 | These special wiki pages are very useful for recording historical |
| 90 | notes. |
| 91 |