Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Sync with trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | timeline-keyboard-navigation |
| Files: | files | file ages | folders |
| SHA3-256: |
611f20e81708ee77c0d6a7b6103cb072 |
| User & Date: | florian 2024-12-12 16:58:00.000 |
Context
|
2024-12-17
| ||
| 06:24 | Take note of possibly useful shortcuts. ... (check-in: 88cba5fbfb user: florian tags: timeline-keyboard-navigation) | |
|
2024-12-12
| ||
| 16:58 | Sync with trunk. ... (check-in: 611f20e817 user: florian tags: timeline-keyboard-navigation) | |
| 11:36 | On the /ckout page, omit the diff if the diff parameter is 0 or smaller. ... (check-in: 0654e8459a user: drh tags: trunk) | |
|
2024-09-06
| ||
| 10:47 | Sync with trunk. ... (check-in: 00f7466add user: florian tags: timeline-keyboard-navigation) | |
Changes
Changes to BUILD.txt.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | For example: mkdir build cd build ../configure make | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | For example: mkdir build cd build ../configure make This will now keep all generated files separate from the maintained source code. -------------------------------------------------------------------------- Here are some notes on what is happening behind the scenes: * The configure script (if used) examines the options given |
| ︙ | ︙ |
Changes to Makefile.in.
| ︙ | ︙ | |||
49 50 51 52 53 54 55 | CFLAGS_INCLUDE = @CFLAGS_INCLUDE@ LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ BCCFLAGS = @CPPFLAGS@ $(CFLAGS) TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H # # Fuzzing may be enable by appending -fsanitize=fuzzer -DFOSSIL_FUZZ # to the TCCFLAGS variable. | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | CFLAGS_INCLUDE = @CFLAGS_INCLUDE@ LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ BCCFLAGS = @CPPFLAGS@ $(CFLAGS) TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H # # Fuzzing may be enable by appending -fsanitize=fuzzer -DFOSSIL_FUZZ # to the TCCFLAGS variable. # For more thorough (but also slower) investigation # -fsanitize=fuzzer,undefined,address # might be more useful. INSTALLDIR = $(DESTDIR)@prefix@/bin USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ SQLITE3_SRC.2 = @SQLITE3_SRC.2@ SQLITE3_OBJ.2 = @SQLITE3_OBJ.2@ |
| ︙ | ︙ | |||
90 91 92 93 94 95 96 97 98 99 100 101 102 103 | # MAKE_COMPILATION_DB (yes/no) determines whether or not the # compile_commands.json file will be generated. MAKE_COMPILATION_DB = @MAKE_COMPILATION_DB@ .PHONY: all tags include $(SRCDIR)/main.mk distclean: clean -rm -f autoconfig.h config.log Makefile -rm -f cscope.out tags reconfig: @AUTOREMAKE@ | > > > | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | # MAKE_COMPILATION_DB (yes/no) determines whether or not the # compile_commands.json file will be generated. MAKE_COMPILATION_DB = @MAKE_COMPILATION_DB@ .PHONY: all tags include $(SRCDIR)/main.mk SQLITE_OPTIONS += @SQLITE_OPTIONS_EXT@ SHELL_OPTIONS += @SQLITE_OPTIONS_EXT@ # ^^^ must come after main.mk is included distclean: clean -rm -f autoconfig.h config.log Makefile -rm -f cscope.out tags reconfig: @AUTOREMAKE@ |
| ︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 2.26 |
Changes to auto.def.
| ︙ | ︙ | |||
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
msg-result "Using Homebrew Tcl/Tk version $path."
} else {
msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'."
define TCLSH false ;# force "make test" failure via /usr/bin/false
}
}
define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define USE_MMAN_H 0
define USE_SEE 0
define SQLITE3_ORIGIN 0
# SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided
# Maintain the C89/C90-style order of variable declarations before statements.
# Check if the compiler supports the respective warning flag.
if {[cctest -cflags -Wdeclaration-after-statement]} {
define-append EXTRA_CFLAGS -Wdeclaration-after-statement
}
| > > > | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
msg-result "Using Homebrew Tcl/Tk version $path."
} else {
msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'."
define TCLSH false ;# force "make test" failure via /usr/bin/false
}
}
define CFLAGS [get-env CFLAGS "-g -O2"]
define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define USE_MMAN_H 0
define USE_SEE 0
define SQLITE3_ORIGIN 0
# SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided
define SQLITE_OPTIONS_EXT ""
# SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c
# Maintain the C89/C90-style order of variable declarations before statements.
# Check if the compiler supports the respective warning flag.
if {[cctest -cflags -Wdeclaration-after-statement]} {
define-append EXTRA_CFLAGS -Wdeclaration-after-statement
}
|
| ︙ | ︙ | |||
399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} {
if {$lcheck eq ""} {
msg-result "Linked to zlib via default library path"
} else {
define-append EXTRA_LDFLAGS "$lcheck"
msg-result "Linked to zlib via $lcheck"
}
break
}
}
set ::zlib_lib -lz
}
set ssldirs [opt-val with-openssl]
| > > > > | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} {
if {$lcheck eq ""} {
msg-result "Linked to zlib via default library path"
} else {
define-append EXTRA_LDFLAGS "$lcheck"
msg-result "Linked to zlib via $lcheck"
}
if {![check-function-in-lib compressBound z]} {
puts "Notice: disabling zlib compression in the SQL shell"
define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB}
}
break
}
}
set ::zlib_lib -lz
}
set ssldirs [opt-val with-openssl]
|
| ︙ | ︙ |
Changes to autosetup/autosetup.
| ︙ | ︙ | |||
562 563 564 565 566 567 568 |
continue
}
set max [max $max [string length $opt]]
}
set indent [string repeat " " [expr {$max+4}]]
set cols [getenv COLUMNS 80]
catch {
| | > > > | 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
continue
}
set max [max $max [string length $opt]]
}
set indent [string repeat " " [expr {$max+4}]]
set cols [getenv COLUMNS 80]
catch {
lassign [exec stty size] _ sttycols
if {[string is integer -strict $sttycols]} {
set cols $sttycols
}
}
incr cols -1
# Now output
foreach help $::autosetup(optionhelp) {
lassign $help opt module desc
if {![string match $what $module]} {
continue
|
| ︙ | ︙ | |||
906 907 908 909 910 911 912 | # @section Paths, Searching # @find-executable-path name # # Searches the path for an executable with the given name. # Note that the name may include some parameters, e.g. 'cc -mbig-endian', # in which case the parameters are ignored. | | < | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 |
# @section Paths, Searching
# @find-executable-path name
#
# Searches the path for an executable with the given name.
# Note that the name may include some parameters, e.g. 'cc -mbig-endian',
# in which case the parameters are ignored.
# Returns the full path to the executable if found, or "" if not found.
#
proc find-executable-path {name} {
# Ignore any parameters
set name [lindex $name 0]
# The empty string is never a valid executable
if {$name ne ""} {
foreach p [split-path] {
|
| ︙ | ︙ |
Changes to autosetup/autosetup-config.guess.
1 2 | #! /bin/sh # Attempt to guess a canonical system name. | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2024 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2024-07-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # |
| ︙ | ︙ | |||
43 44 45 46 47 48 49 | me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] | | | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system '$me' is run on.
Options:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
Copyright 1992-2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try '$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
--version | -v )
|
| ︙ | ︙ | |||
98 99 100 101 102 103 104 | GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. | | | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
GUESS=
# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.
# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
# use 'HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team.
tmp=
# shellcheck disable=SC2172
trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
set_cc_for_build() {
# prevent multiple calls if $tmp is already set
test "$tmp" && return 0
: "${TMPDIR=/tmp}"
# shellcheck disable=SC2039,SC3028
{ tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
dummy=$tmp/dummy
case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
,,) echo "int x;" > "$dummy.c"
for driver in cc gcc c17 c99 c89 ; do
if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
CC_FOR_BUILD=$driver
break
fi
done
if test x"$CC_FOR_BUILD" = x ; then
CC_FOR_BUILD=no_compiler_found
|
| ︙ | ︙ | |||
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include <features.h> #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include <stdarg.h> /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. | > > > > > > | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #if defined(__ANDROID__) LIBC=android #else #include <features.h> #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #elif defined(__LLVM_LIBC__) LIBC=llvm #else #include <stdarg.h> /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. |
| ︙ | ︙ | |||
433 434 435 436 437 438 439 | set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ | | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL |
| ︙ | ︙ | |||
455 456 457 458 459 460 461 |
;;
sun4*:SunOS:*:*)
case `/usr/bin/arch -k` in
Series*|S4*)
UNAME_RELEASE=`uname -v`
;;
esac
| | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
;;
sun4*:SunOS:*:*)
case `/usr/bin/arch -k` in
Series*|S4*)
UNAME_RELEASE=`uname -v`
;;
esac
# Japanese Language versions have a version number like '4.1.3-JL'.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
GUESS=sparc-sun-sunos$SUN_REL
;;
sun3*:SunOS:*:*)
GUESS=m68k-sun-sunos$UNAME_RELEASE
;;
sun*:*:4.2BSD:*)
|
| ︙ | ︙ | |||
624 625 626 627 628 629 630 |
;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
| > | | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
int
main ()
{
if (!__power_pc())
exit(1);
puts("powerpc-ibm-aix3.2.5");
exit(0);
}
EOF
|
| ︙ | ︙ | |||
708 709 710 711 712 713 714 | set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include <stdlib.h> #include <unistd.h> | > | | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#define _HPUX_SOURCE
#include <stdlib.h>
#include <unistd.h>
int
main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
#endif
long cpu = sysconf (_SC_CPU_VERSION);
switch (cpu)
|
| ︙ | ︙ | |||
900 901 902 903 904 905 906 |
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
else
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
fi
;;
*:FreeBSD:*:*)
| | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 |
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
else
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
fi
;;
*:FreeBSD:*:*)
UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in
amd64)
UNAME_PROCESSOR=x86_64 ;;
i386)
UNAME_PROCESSOR=i586 ;;
esac
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
|
| ︙ | ︙ | |||
925 926 927 928 929 930 931 932 933 934 935 936 937 938 |
;;
*:MSYS*:*)
GUESS=$UNAME_MACHINE-pc-msys
;;
i*:PW*:*)
GUESS=$UNAME_MACHINE-pc-pw32
;;
*:Interix*:*)
case $UNAME_MACHINE in
x86)
GUESS=i586-pc-interix$UNAME_RELEASE
;;
authenticamd | genuineintel | EM64T)
GUESS=x86_64-unknown-interix$UNAME_RELEASE
| > > > | 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 |
;;
*:MSYS*:*)
GUESS=$UNAME_MACHINE-pc-msys
;;
i*:PW*:*)
GUESS=$UNAME_MACHINE-pc-pw32
;;
*:SerenityOS:*:*)
GUESS=$UNAME_MACHINE-pc-serenity
;;
*:Interix*:*)
case $UNAME_MACHINE in
x86)
GUESS=i586-pc-interix$UNAME_RELEASE
;;
authenticamd | genuineintel | EM64T)
GUESS=x86_64-unknown-interix$UNAME_RELEASE
|
| ︙ | ︙ | |||
959 960 961 962 963 964 965 966 967 968 969 |
;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
;;
*:Minix:*:*)
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
| > > > > > > > > > > > > > > > > > > > > > > > > > > | | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 |
;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
;;
x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
;;
*:[Mm]anagarm:*:*)
GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
;;
*:Minix:*:*)
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
set_cc_for_build
CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
ABI=64
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __ARM_EABI__
#ifdef __ARM_PCS_VFP
ABI=eabihf
#else
ABI=eabi
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
esac
fi
GUESS=$CPU-unknown-linux-$LIBCABI
;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
|
| ︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 |
;;
ia64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
| > > > > > > > > > | | 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 |
;;
ia64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:cos:*:*)
GUESS=$UNAME_MACHINE-unknown-cos
;;
kvx:mbr:*:*)
GUESS=$UNAME_MACHINE-unknown-mbr
;;
loongarch32:Linux:*:* | loongarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
m32r*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
m68*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
|
| ︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 1151 1152 |
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
vax:Linux:*:*)
GUESS=$UNAME_MACHINE-dec-linux-$LIBC
;;
x86_64:Linux:*:*)
set_cc_for_build
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
| > > > > > > | > > > > | | | > | > | < | | | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 |
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
vax:Linux:*:*)
GUESS=$UNAME_MACHINE-dec-linux-$LIBC
;;
x86_64:Linux:*:*)
set_cc_for_build
CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
ABI=64
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __i386__
ABI=x86
#else
#ifdef __ILP32__
ABI=x32
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
x86) CPU=i686 ;;
x32) LIBCABI=${LIBC}x32 ;;
esac
fi
GUESS=$CPU-pc-linux-$LIBCABI
;;
xtensa*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
# earlier versions are messed up and put the nodename in both
# sysname and nodename.
GUESS=i386-sequent-sysv4
;;
i*86:UNIX_SV:4.2MP:2.*)
# Unixware is an offshoot of SVR4, but it has its own version
# number series starting with 2...
# I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
# Use sysv4.2uw... so that sysv4* matches it.
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;;
i*86:OS/2:*:*)
# If we were able to find 'uname', then EMX Unix compatibility
# is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx
;;
i*86:XTS-300:*:STOP)
GUESS=$UNAME_MACHINE-unknown-stop
;;
i*86:atheos:*:*)
|
| ︙ | ︙ | |||
1314 1315 1316 1317 1318 1319 1320 | if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; | | | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 |
if uname -p 2>/dev/null >/dev/null ; then
UNAME_MACHINE=`(uname -p) 2>/dev/null`
GUESS=$UNAME_MACHINE-sni-sysv4
else
GUESS=ns32k-sni-sysv
fi
;;
PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4
;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes <hewes@openmarket.com>.
# How about differentiating between stratus architectures? -djm
GUESS=hppa1.1-stratus-sysv4
|
| ︙ | ︙ | |||
1360 1361 1362 1363 1364 1365 1366 |
;;
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
GUESS=i586-pc-beos
;;
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
GUESS=i586-pc-haiku
;;
| | > > > | | 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 |
;;
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
GUESS=i586-pc-beos
;;
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
GUESS=i586-pc-haiku
;;
ppc:Haiku:*:*) # Haiku running on Apple PowerPC
GUESS=powerpc-apple-haiku
;;
*:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat)
GUESS=$UNAME_MACHINE-unknown-haiku
;;
SX-4:SUPER-UX:*:*)
GUESS=sx4-nec-superux$UNAME_RELEASE
;;
SX-5:SUPER-UX:*:*)
GUESS=sx5-nec-superux$UNAME_RELEASE
;;
|
| ︙ | ︙ | |||
1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 |
i*86:skyos:*:*)
SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
;;
i*86:rdos:*:*)
GUESS=$UNAME_MACHINE-pc-rdos
;;
*:AROS:*:*)
GUESS=$UNAME_MACHINE-unknown-aros
;;
x86_64:VMkernel:*:*)
GUESS=$UNAME_MACHINE-unknown-esx
;;
amd64:Isilon\ OneFS:*:*)
GUESS=x86_64-unknown-onefs
;;
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
esac
# Do we have a guess based on uname results?
if test "x$GUESS" != x; then
echo "$GUESS"
exit
fi
| > > > > > > | 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 |
i*86:skyos:*:*)
SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
;;
i*86:rdos:*:*)
GUESS=$UNAME_MACHINE-pc-rdos
;;
i*86:Fiwix:*:*)
GUESS=$UNAME_MACHINE-pc-fiwix
;;
*:AROS:*:*)
GUESS=$UNAME_MACHINE-unknown-aros
;;
x86_64:VMkernel:*:*)
GUESS=$UNAME_MACHINE-unknown-esx
;;
amd64:Isilon\ OneFS:*:*)
GUESS=x86_64-unknown-onefs
;;
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
*:Ironclad:*:*)
GUESS=$UNAME_MACHINE-unknown-ironclad
;;
esac
# Do we have a guess based on uname results?
if test "x$GUESS" != x; then
echo "$GUESS"
exit
fi
|
| ︙ | ︙ | |||
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 |
#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#include <signal.h>
#if defined(_SIZE_T_) || defined(SIGLOST)
#include <sys/utsname.h>
#endif
#endif
#endif
main ()
{
#if defined (sony)
#if defined (MIPSEB)
/* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
I don't know.... */
printf ("mips-sony-bsd\n"); exit (0);
| > | 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 |
#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#include <signal.h>
#if defined(_SIZE_T_) || defined(SIGLOST)
#include <sys/utsname.h>
#endif
#endif
#endif
int
main ()
{
#if defined (sony)
#if defined (MIPSEB)
/* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
I don't know.... */
printf ("mips-sony-bsd\n"); exit (0);
|
| ︙ | ︙ |
Changes to autosetup/autosetup-config.sub.
1 2 | #! /bin/sh # Configuration validation subroutine script. | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2024 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale timestamp='2024-05-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # |
| ︙ | ︙ | |||
72 73 74 75 76 77 78 | -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) | | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
-v, --version print version number, then exit
Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
Copyright 1992-2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try '$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
--version | -v )
|
| ︙ | ︙ | |||
116 117 118 119 120 121 122 |
exit 1;;
1) ;;
*) echo "$me: too many arguments$help" >&2
exit 1;;
esac
# Split fields of configuration type
| | > | | | > > > > | > > > > | > > > < > > > > > | | > | > > > | > > > > > > > > > > > | | > > > > > > > > > > | > > > > > > | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
exit 1;;
1) ;;
*) echo "$me: too many arguments$help" >&2
exit 1;;
esac
# Split fields of configuration type
saved_IFS=$IFS
IFS="-" read field1 field2 field3 field4 <<EOF
$1
EOF
IFS=$saved_IFS
# Separate into logical components for further validation
case $1 in
*-*-*-*-*)
echo "Invalid configuration '$1': more than four components" >&2
exit 1
;;
*-*-*-*)
basic_machine=$field1-$field2
basic_os=$field3-$field4
;;
*-*-*)
# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
# parts
maybe_os=$field2-$field3
case $maybe_os in
cloudabi*-eabi* \
| kfreebsd*-gnu* \
| knetbsd*-gnu* \
| kopensolaris*-gnu* \
| linux-* \
| managarm-* \
| netbsd*-eabi* \
| netbsd*-gnu* \
| nto-qnx* \
| os2-emx* \
| rtmk-nova* \
| storm-chaos* \
| uclinux-gnu* \
| uclinux-uclibc* \
| windows-* )
basic_machine=$field1
basic_os=$maybe_os
;;
android-linux)
basic_machine=$field1-unknown
basic_os=linux-android
;;
*)
basic_machine=$field1-$field2
basic_os=$field3
;;
esac
;;
*-*)
case $field1-$field2 in
# Shorthands that happen to contain a single dash
convex-c[12] | convex-c3[248])
basic_machine=$field2-convex
basic_os=
;;
decstation-3100)
basic_machine=mips-dec
basic_os=
;;
*-*)
# Second component is usually, but not always the OS
case $field2 in
# Do not treat sunos as a manufacturer
sun*os*)
basic_machine=$field1
basic_os=$field2
;;
# Manufacturers
3100* \
| 32* \
| 3300* \
| 3600* \
| 7300* \
| acorn \
| altos* \
| apollo \
| apple \
| atari \
| att* \
| axis \
| be \
| bull \
| cbm \
| ccur \
| cisco \
| commodore \
| convergent* \
| convex* \
| cray \
| crds \
| dec* \
| delta* \
| dg \
| digital \
| dolphin \
| encore* \
| gould \
| harris \
| highlevel \
| hitachi* \
| hp \
| ibm* \
| intergraph \
| isi* \
| knuth \
| masscomp \
| microblaze* \
| mips* \
| motorola* \
| ncr* \
| news \
| next \
| ns \
| oki \
| omron* \
| pc533* \
| rebel \
| rom68k \
| rombug \
| semi \
| sequent* \
| siemens \
| sgi* \
| siemens \
| sim \
| sni \
| sony* \
| stratus \
| sun \
| sun[234]* \
| tektronix \
| tti* \
| ultra \
| unicom* \
| wec \
| winbond \
| wrs)
basic_machine=$field1-$field2
basic_os=
;;
zephyr*)
basic_machine=$field1-unknown
basic_os=$field2
;;
*)
basic_machine=$field1
basic_os=$field2
;;
esac
;;
esac
|
| ︙ | ︙ | |||
261 262 263 264 265 266 267 | basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; | < < < < < < < < < < < < < < < < < < < < | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= |
| ︙ | ︙ | |||
703 704 705 706 707 708 709 | basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; | | < > > > > > | > > > > < > > > > | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 | basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) cpu=m68k vendor=motorola ;; # This used to be dpx2*, but that gets the RS6000-based # DPX/20 and the x86-based DPX/2-100 wrong. See # https://oldskool.silicium.org/stations/bull_dpx20.htm # https://www.feb-patrimoine.com/english/bull_dpx2.htm # https://www.feb-patrimoine.com/english/unix_and_bull.htm dpx2 | dpx2[23]00 | dpx2[23]xx) cpu=m68k vendor=bull ;; dpx2100 | dpx21xx) cpu=i386 vendor=bull ;; dpx20) cpu=rs6000 vendor=bull ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi |
| ︙ | ︙ | |||
826 827 828 829 830 831 832 | cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next | < < < < < < < < < < < < | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 | cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 |
| ︙ | ︙ | |||
926 927 928 929 930 931 932 | ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) | | > | | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 | ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) saved_IFS=$IFS IFS="-" read cpu vendor <<EOF $basic_machine EOF IFS=$saved_IFS ;; # We use 'pc' rather than 'unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) cpu=$basic_machine vendor=pc ;; # These rules are duplicated from below for sake of the special case above; |
| ︙ | ︙ | |||
959 960 961 962 963 964 965 | ;; esac unset -v basic_machine # Decode basic machines in the full and proper CPU-Company form. case $cpu-$vendor in | | | > > > > | | 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 |
;;
esac
unset -v basic_machine
# Decode basic machines in the full and proper CPU-Company form.
case $cpu-$vendor in
# Here we handle the default manufacturer of certain CPU types in canonical form.
# It is in some cases the only manufacturer, in others, it is the most popular.
c[12]-convex | c[12]-unknown | c3[248]-convex | c3[248]-unknown)
vendor=convex
basic_os=${basic_os:-bsd}
;;
craynv-unknown)
vendor=cray
basic_os=${basic_os:-unicosmp}
;;
c90-unknown | c90-cray)
vendor=cray
basic_os=${basic_os:-unicos}
;;
fx80-unknown)
vendor=alliant
;;
romp-unknown)
vendor=ibm
;;
|
| ︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 | ;; xps-unknown | xps100-unknown) cpu=xps100 vendor=honeywell ;; # Here we normalize CPU types with a missing or matching vendor | | | | > > > > > > > > > > > > > > > > > > > > > > > | | 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 |
;;
xps-unknown | xps100-unknown)
cpu=xps100
vendor=honeywell
;;
# Here we normalize CPU types with a missing or matching vendor
armh-unknown | armh-alt)
cpu=armv7l
vendor=alt
basic_os=${basic_os:-linux-gnueabihf}
;;
# Normalized CPU+vendor pairs that imply an OS, if not otherwise specified
m68k-isi)
basic_os=${basic_os:-sysv}
;;
m68k-sony)
basic_os=${basic_os:-newsos}
;;
m68k-tektronix)
basic_os=${basic_os:-bsd}
;;
m88k-harris)
basic_os=${basic_os:-sysv3}
;;
i386-bull | m68k-bull)
basic_os=${basic_os:-sysv3}
;;
rs6000-bull)
basic_os=${basic_os:-bosx}
;;
mips-sni)
basic_os=${basic_os:-sysv4}
;;
# Here we normalize CPU types irrespective of the vendor
amd64-*)
cpu=x86_64
;;
blackfin-*)
cpu=bfin
basic_os=${basic_os:-linux}
;;
c54x-*)
cpu=tic54x
;;
c55x-*)
cpu=tic55x
;;
|
| ︙ | ︙ | |||
1043 1044 1045 1046 1047 1048 1049 | cpu=mips64 ;; ms1-*) cpu=mt ;; m68knommu-*) cpu=m68k | | | | < < < | 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 |
cpu=mips64
;;
ms1-*)
cpu=mt
;;
m68knommu-*)
cpu=m68k
basic_os=${basic_os:-linux}
;;
m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
cpu=s12z
;;
openrisc-*)
cpu=or32
;;
parisc-*)
cpu=hppa
basic_os=${basic_os:-linux}
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
cpu=i586
;;
pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
cpu=i686
;;
pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
cpu=i686
;;
pentium4-*)
cpu=i786
;;
ppc-* | ppcbe-*)
cpu=powerpc
;;
ppcle-* | powerpclittle-*)
cpu=powerpcle
;;
ppc64-*)
|
| ︙ | ︙ | |||
1103 1104 1105 1106 1107 1108 1109 | ;; tx39-*) cpu=mipstx39 ;; tx39el-*) cpu=mipstx39el ;; | < < < | | 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 |
;;
tx39-*)
cpu=mipstx39
;;
tx39el-*)
cpu=mipstx39el
;;
xscale-* | xscalee[bl]-*)
cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
;;
arm64-* | aarch64le-*)
cpu=aarch64
;;
# Recognize the canonical CPU Types that limit and/or modify the
# company names they are paired with.
cr16-*)
basic_os=${basic_os:-elf}
|
| ︙ | ︙ | |||
1161 1162 1163 1164 1165 1166 1167 |
basic_os=${basic_os:-linux-gnu}
;;
*)
# Recognize the canonical CPU types that are allowed with any
# company name.
case $cpu in
| | > > | > > | > > | > > > > > | | > > > > > > | > | > > | > | > > > > > | | > > > | > > > > > | | > > > > > > | | > > > > > > | > > > > | > > > | > | > | < < < | | | | | | | | | | | | | | | | < | | | | | < < | | | > < > > | > > > > > | | > > > > > > > > > | | > > > > > > > > | | > > > | > | > > > | | > > > > > > > | > > > > > | > > > > > | > > > > > > > > > > > | > > | > > > > > | | > | > | | | > | > > > > > | 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 |
basic_os=${basic_os:-linux-gnu}
;;
*)
# Recognize the canonical CPU types that are allowed with any
# company name.
case $cpu in
1750a \
| 580 \
| [cjt]90 \
| a29k \
| aarch64 \
| aarch64_be \
| aarch64c \
| abacus \
| alpha \
| alpha64 \
| alpha64ev56 \
| alpha64ev6[78] \
| alpha64ev[4-8] \
| alpha64pca5[67] \
| alphaev56 \
| alphaev6[78] \
| alphaev[4-8] \
| alphapca5[67] \
| am33_2.0 \
| amdgcn \
| arc \
| arc32 \
| arc64 \
| arceb \
| arm \
| arm64e \
| arm64ec \
| arm[lb]e \
| arme[lb] \
| armv* \
| asmjs \
| avr \
| avr32 \
| ba \
| be32 \
| be64 \
| bfin \
| bpf \
| bs2000 \
| c30 \
| c4x \
| c8051 \
| c[123]* \
| clipper \
| craynv \
| csky \
| cydra \
| d10v \
| d30v \
| dlx \
| dsp16xx \
| e2k \
| elxsi \
| epiphany \
| f30[01] \
| f700 \
| fido \
| fr30 \
| frv \
| ft32 \
| fx80 \
| h8300 \
| h8500 \
| hexagon \
| hppa \
| hppa1.[01] \
| hppa2.0 \
| hppa2.0[nw] \
| hppa64 \
| i*86 \
| i370 \
| i860 \
| i960 \
| ia16 \
| ia64 \
| ip2k \
| iq2000 \
| javascript \
| k1om \
| kvx \
| le32 \
| le64 \
| lm32 \
| loongarch32 \
| loongarch64 \
| m32c \
| m32r \
| m32rle \
| m5200 \
| m68000 \
| m680[012346]0 \
| m6811 \
| m6812 \
| m68360 \
| m683?2 \
| m68hc11 \
| m68hc12 \
| m68hcs12x \
| m68k \
| m88110 \
| m88k \
| maxq \
| mb \
| mcore \
| mep \
| metag \
| microblaze \
| microblazeel \
| mips* \
| mmix \
| mn10200 \
| mn10300 \
| moxie \
| msp430 \
| mt \
| nanomips* \
| nds32 \
| nds32be \
| nds32le \
| nfp \
| nios \
| nios2 \
| nios2eb \
| nios2el \
| none \
| np1 \
| ns16k \
| ns32k \
| nvptx \
| open8 \
| or1k* \
| or32 \
| orion \
| pdp10 \
| pdp11 \
| picochip \
| pj \
| pjl \
| pn \
| power \
| powerpc \
| powerpc64 \
| powerpc64le \
| powerpcle \
| powerpcspe \
| pru \
| pyramid \
| riscv \
| riscv32 \
| riscv32be \
| riscv64 \
| riscv64be \
| rl78 \
| romp \
| rs6000 \
| rx \
| s390 \
| s390x \
| score \
| sh \
| sh64 \
| sh64le \
| sh[12345][lb]e \
| sh[1234] \
| sh[1234]e[lb] \
| sh[23]e \
| sh[23]ele \
| sh[24]a \
| sh[24]ae[lb] \
| sh[lb]e \
| she[lb] \
| shl \
| sparc \
| sparc64 \
| sparc64b \
| sparc64v \
| sparc86x \
| sparclet \
| sparclite \
| sparcv8 \
| sparcv9 \
| sparcv9b \
| sparcv9v \
| spu \
| sv1 \
| sx* \
| tahoe \
| thumbv7* \
| tic30 \
| tic4x \
| tic54x \
| tic55x \
| tic6x \
| tic80 \
| tron \
| ubicom32 \
| v70 \
| v810 \
| v850 \
| v850e \
| v850e1 \
| v850e2 \
| v850e2v3 \
| v850es \
| vax \
| vc4 \
| visium \
| w65 \
| wasm32 \
| wasm64 \
| we32k \
| x86 \
| x86_64 \
| xc16x \
| xgate \
| xps100 \
| xstormy16 \
| xtensa* \
| ymp \
| z80 \
| z8k)
;;
*)
echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
exit 1
;;
esac
;;
esac
# Here we canonicalize certain aliases for manufacturers.
case $vendor in
digital*)
vendor=dec
;;
commodore*)
vendor=cbm
;;
*)
;;
esac
# Decode manufacturer-specific aliases for certain operating systems.
if test x"$basic_os" != x
then
# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
# set os.
obj=
case $basic_os in
gnu/linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
;;
os2-emx)
kernel=os2
os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
;;
nto-qnx*)
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
;;
*-*)
saved_IFS=$IFS
IFS="-" read kernel os <<EOF
$basic_os
EOF
IFS=$saved_IFS
;;
# Default OS when just kernel was specified
nto*)
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
;;
linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
;;
managarm*)
kernel=managarm
os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'`
;;
*)
kernel=
os=$basic_os
;;
esac
# Now, normalize the OS (knowing we just have one component, it's not a kernel,
|
| ︙ | ︙ | |||
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 | os=`echo "$os" | sed -e 's|solaris1|sunos4|'` ;; solaris) os=solaris2 ;; unixware*) os=sysv4.2uw ;; # es1800 is here to avoid being matched by es* (a different OS) es1800*) os=ose ;; # Some version numbers need modification chorusos*) | > > > > > > > > > > > > > > > > > | 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 | os=`echo "$os" | sed -e 's|solaris1|sunos4|'` ;; solaris) os=solaris2 ;; unixware*) os=sysv4.2uw ;; # The marketing names for NeXT's operating systems were # NeXTSTEP, NeXTSTEP 2, OpenSTEP 3, OpenSTEP 4. 'openstep' is # mapped to 'openstep3', but 'openstep1' and 'openstep2' are # mapped to 'nextstep' and 'nextstep2', consistent with the # treatment of SunOS/Solaris. ns | ns1 | nextstep | nextstep1 | openstep1) os=nextstep ;; ns2 | nextstep2 | openstep2) os=nextstep2 ;; ns3 | nextstep3 | openstep | openstep3) os=openstep3 ;; ns4 | nextstep4 | openstep4) os=openstep4 ;; # es1800 is here to avoid being matched by es* (a different OS) es1800*) os=ose ;; # Some version numbers need modification chorusos*) |
| ︙ | ︙ | |||
1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 | os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; wince*) os=wince ;; utek*) os=bsd ;; dynix*) os=bsd ;; acis*) os=aos ;; atheos*) os=atheos ;; syllable*) os=syllable ;; 386bsd) os=bsd ;; | > | > > > > | < < | > > | 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 | os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; wince*) os=wince ;; utek*) os=bsd vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'` ;; dynix*) os=bsd ;; acis*) os=aos ;; atheos*) os=atheos ;; syllable*) os=syllable ;; 386bsd) os=bsd ;; ctix*) os=sysv vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'` ;; uts*) os=sysv ;; nova*) kernel=rtmk os=nova ;; # Preserve the version number of sinix5. sinix5.*) os=`echo "$os" | sed -e 's|sinix|sysv|'` vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` ;; sinix*) os=sysv4 vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'` ;; tpf*) os=tpf ;; triton*) os=sysv3 ;; |
| ︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 | # particular features comes up, bare metal # configurations are quite functional. case $cpu in arm*) os=eabi ;; *) | | > > > > > > > | > | > > | > | | > | > > | > | > | | 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 | # particular features comes up, bare metal # configurations are quite functional. case $cpu in arm*) os=eabi ;; *) os= obj=elf ;; esac ;; aout* | coff* | elf* | pe*) # These are machine code file formats, not OSes obj=$os os= ;; *) # No normalization, but not necessarily accepted, that comes below. ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. kernel= obj= case $cpu-$vendor in score-*) os= obj=elf ;; spu-*) os= obj=elf ;; *-acorn) os=riscix1.2 ;; arm*-rebel) kernel=linux os=gnu ;; arm*-semi) os= obj=aout ;; c4x-* | tic4x-*) os= obj=coff ;; c8051-*) os= obj=elf ;; clipper-intergraph) os=clix ;; hexagon-*) os= obj=elf ;; tic54x-*) os= obj=coff ;; tic55x-*) os= obj=coff ;; tic6x-*) os= obj=coff ;; # This must come before the *-dec entry. pdp10-*) os=tops20 ;; pdp11-*) os=none |
| ︙ | ︙ | |||
1567 1568 1569 1570 1571 1572 1573 | i386-sun) os=sunos4.0.2 ;; m68000-sun) os=sunos3 ;; m68*-cisco) | > | | > > > > > > > > > | > | | > > | | > | > | 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 | i386-sun) os=sunos4.0.2 ;; m68000-sun) os=sunos3 ;; m68*-cisco) os= obj=aout ;; mep-*) os= obj=elf ;; # The -sgi and -siemens entries must be before the mips- entry # or we get the wrong os. *-sgi) os=irix ;; *-siemens) os=sysv4 ;; mips*-cisco) os= obj=elf ;; mips*-*|nanomips*-*) os= obj=elf ;; or32-*) os= obj=coff ;; # This must be before the sparc-* entry or we get the wrong os. *-tti) os=sysv3 ;; sparc-* | *-sun) os=sunos4.1.1 ;; pru-*) os= obj=elf ;; *-be) os=beos ;; *-ibm) os=aix ;; |
| ︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 | *-oki) os=proelf ;; *-hp) os=hpux ;; *-hitachi) | | | 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 | *-oki) os=proelf ;; *-hp) os=hpux ;; *-hitachi) os=hiuxwe2 ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=sysv ;; *-cbm) os=amigaos ;; |
| ︙ | ︙ | |||
1656 1657 1658 1659 1660 1661 1662 | ;; *-highlevel) os=bsd ;; *-encore) os=bsd ;; | < < < < < < > | > | | > | > > > < > > > > > | > > > > > > > > > > > > > > > > | > > > > > | < > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > | | | | > > > > > > > > > | > | > | > > > > > | > > > > > > > > | > > > > > > | > > > > > > | | < | > | | | | > | > | > > > | > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > > > | > > > > | > > > > > > > > | | | | > > | > > > > | | > > > | | | 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 | ;; *-highlevel) os=bsd ;; *-encore) os=bsd ;; *-masscomp) os=rtu ;; f30[01]-fujitsu | f700-fujitsu) os=uxpv ;; *-rom68k) os= obj=coff ;; *-*bug) os= obj=coff ;; *-apple) os=macos ;; *-atari*) os=mint ;; *-wrs) os=vxworks ;; *) os=none ;; esac fi # Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). case $os in # Sometimes we do "kernel-libc", so those need to count as OSes. llvm* | musl* | newlib* | relibc* | uclibc*) ;; # Likewise for "kernel-abi" eabi* | gnueabi*) ;; # VxWorks passes extra cpu info in the 4th filed. simlinux | simwindows | spe) ;; # See `case $cpu-$os` validation below ghcjs) ;; # Now accept the basic system types. # Each alternative MUST end in a * to match a version number. abug \ | aix* \ | amdhsa* \ | amigados* \ | amigaos* \ | android* \ | aof* \ | aos* \ | aros* \ | atheos* \ | auroraux* \ | aux* \ | beos* \ | bitrig* \ | bme* \ | bosx* \ | bsd* \ | cegcc* \ | chorusos* \ | chorusrdb* \ | clix* \ | cloudabi* \ | cnk* \ | conix* \ | cos* \ | cxux* \ | cygwin* \ | darwin* \ | dgux* \ | dicos* \ | dnix* \ | domain* \ | dragonfly* \ | drops* \ | ebmon* \ | ecoff* \ | ekkobsd* \ | emscripten* \ | emx* \ | es* \ | fiwix* \ | freebsd* \ | fuchsia* \ | genix* \ | genode* \ | glidix* \ | gnu* \ | go32* \ | haiku* \ | hcos* \ | hiux* \ | hms* \ | hpux* \ | ieee* \ | interix* \ | ios* \ | iris* \ | irix* \ | ironclad* \ | isc* \ | its* \ | l4re* \ | libertybsd* \ | lites* \ | lnews* \ | luna* \ | lynxos* \ | mach* \ | macos* \ | magic* \ | mbr* \ | midipix* \ | midnightbsd* \ | mingw32* \ | mingw64* \ | minix* \ | mint* \ | mirbsd* \ | mks* \ | mlibc* \ | mmixware* \ | mon960* \ | morphos* \ | moss* \ | moxiebox* \ | mpeix* \ | mpw* \ | msdos* \ | msys* \ | mvs* \ | nacl* \ | netbsd* \ | netware* \ | newsos* \ | nextstep* \ | nindy* \ | nonstopux* \ | nova* \ | nsk* \ | nucleus* \ | nx6 \ | nx7 \ | oabi* \ | ohos* \ | onefs* \ | openbsd* \ | openedition* \ | openstep* \ | os108* \ | os2* \ | os400* \ | os68k* \ | os9* \ | ose* \ | osf* \ | oskit* \ | osx* \ | palmos* \ | phoenix* \ | plan9* \ | powermax* \ | powerunix* \ | proelf* \ | psos* \ | psp* \ | ptx* \ | pw32* \ | qnx* \ | rdos* \ | redox* \ | rhapsody* \ | riscix* \ | riscos* \ | rtems* \ | rtmk* \ | rtu* \ | scout* \ | secbsd* \ | sei* \ | serenity* \ | sim* \ | skyos* \ | solaris* \ | solidbsd* \ | sortix* \ | storm-chaos* \ | sunos \ | sunos[34]* \ | superux* \ | syllable* \ | sym* \ | sysv* \ | tenex* \ | tirtos* \ | toppers* \ | tops10* \ | tops20* \ | tpf* \ | tvos* \ | twizzler* \ | uclinux* \ | udi* \ | udk* \ | ultrix* \ | unicos* \ | uniplus* \ | unleashed* \ | unos* \ | uwin* \ | uxpv* \ | v88r* \ |*vms* \ | vos* \ | vsta* \ | vxsim* \ | vxworks* \ | wasi* \ | watchos* \ | wince* \ | windiss* \ | windows* \ | winnt* \ | xenix* \ | xray* \ | zephyr* \ | zvmoe* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) # Don't forget version if it is 3.2v4 or newer. ;; # This refers to builds using the UEFI calling convention # (which depends on the architecture) and PE file format. # Note that this is both a different calling convention and # different file format than that of GNU-EFI # (x86_64-w64-mingw32). uefi) ;; none) ;; kernel* | msvc* ) # Restricted further below ;; '') if test x"$obj" = x then echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 fi ;; *) echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 exit 1 ;; esac case $obj in aout* | coff* | elf* | pe*) ;; '') # empty is fine ;; *) echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 exit 1 ;; esac # Here we handle the constraint that a (synthetic) cpu and os are # valid only in combination with each other and nowhere else. case $cpu-$os in # The "javascript-unknown-ghcjs" triple is used by GHC; we # accept it here in order to tolerate that, but reject any # variations. javascript-ghcjs) ;; javascript-* | *-ghcjs) echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os-$obj in linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ | linux-mlibc*- | linux-musl*- | linux-newlib*- \ | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) ;; uclinux-uclibc*- | uclinux-gnu*- ) ;; managarm-mlibc*- | managarm-kernel*- ) ;; windows*-msvc*-) ;; -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ | -uclibc*- ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 exit 1 ;; -kernel*- ) echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 exit 1 ;; *-kernel*- ) echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 exit 1 ;; *-msvc*- ) echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 exit 1 ;; kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) ;; vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) ;; nto-qnx*-) ;; os2-emx-) ;; rtmk-nova-) ;; *-eabi*- | *-gnueabi*-) ;; none--*) # None (no kernel, i.e. freestanding / bare metal), # can be paired with an machine code file format ;; -*-) # Blank kernel with real OS is always fine. ;; --*) # Blank kernel and OS with real machine code file format is always fine. ;; *-*-*) echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos* | *-solaris*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be |
| ︙ | ︙ | |||
1858 1859 1860 1861 1862 1863 1864 | *-vos*) vendor=stratus ;; esac ;; esac | | | 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 |
*-vos*)
vendor=stratus
;;
esac
;;
esac
echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}"
exit
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
|
Changes to autosetup/autosetup-find-tclsh.
1 2 3 4 5 6 7 | #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh in current dir from source # Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works) # If an argument is given, use that as the test instead of autosetup-test-tclsh d="`dirname "$0"`" for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/bin/sh
# Looks for a suitable tclsh or jimsh in the PATH
# If not found, builds a bootstrap jimsh in current dir from source
# Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works)
# If an argument is given, use that as the test instead of autosetup-test-tclsh
d="`dirname "$0"`"
for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do
{ $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0
done
echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0"
for cc in ${CC_FOR_BUILD:-cc} gcc; do
{ $cc -o jimsh0 "$d/jimsh0.c"; } >/dev/null 2>&1 || continue
./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0
done
echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
echo false
|
Changes to autosetup/cc.tcl.
1 2 3 4 5 6 7 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc' module supports checking various 'features' of the C or C++ # compiler/linker environment. Common commands are 'cc-check-includes', | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc' module supports checking various 'features' of the C or C++ # compiler/linker environment. Common commands are 'cc-check-includes', # 'cc-check-types', 'cc-check-functions', 'cc-with' and 'make-config-header' # # The following environment variables are used if set: # ## CC - C compiler ## CXX - C++ compiler ## CPP - C preprocessor ## CCACHE - Set to "none" to disable automatic use of ccache |
| ︙ | ︙ |
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
118 119 120 121 122 123 124 | #include <stdio.h> #include <assert.h> #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; | < < < | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #include <stdio.h> #include <assert.h> #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) # include <pwd.h> |
| ︙ | ︙ | |||
206 207 208 209 210 211 212 | # endif # ifndef unlink # define unlink _unlink # endif # ifndef strdup # define strdup _strdup # endif | < < | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | # endif # ifndef unlink # define unlink _unlink # endif # ifndef strdup # define strdup _strdup # endif # undef pclose # define pclose _pclose # endif #else /* Make sure isatty() has a prototype. */ extern int isatty(int); |
| ︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | #include <windows.h> /* string conversion routines only needed on Win32 */ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif /* Use console I/O package as a direct INCLUDE. */ #define SQLITE_INTERNAL_LINKAGE static #ifdef SQLITE_SHELL_FIDDLE /* Deselect most features from the console I/O package for Fiddle. */ # define SQLITE_CIO_NO_REDIRECT # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE # define SQLITE_CIO_NO_FLUSH #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < | < < < | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 |
#include <windows.h>
/* string conversion routines only needed on Win32 */
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
#endif
/************************* Begin ../ext/misc/sqlite3_stdio.h ******************/
/*
** 2024-09-24
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This header file contains definitions of interfaces that provide
** cross-platform I/O for UTF-8 content.
**
** On most platforms, the interfaces definitions in this file are
** just #defines. For example sqlite3_fopen() is a macro that resolves
** to the standard fopen() in the C-library.
**
** But Windows does not have a standard C-library, at least not one that
** can handle UTF-8. So for windows build, the interfaces resolve to new
** C-language routines contained in the separate sqlite3_stdio.c source file.
**
** So on all non-Windows platforms, simply #include this header file and
** use the interfaces defined herein. Then to run your application on Windows,
** also link in the accompanying sqlite3_stdio.c source file when compiling
** to get compatible interfaces.
*/
#ifndef _SQLITE3_STDIO_H_
#define _SQLITE3_STDIO_H_ 1
#ifdef _WIN32
/**** Definitions For Windows ****/
#include <stdio.h>
#include <windows.h>
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
FILE *sqlite3_popen(const char *zCommand, const char *type);
char *sqlite3_fgets(char *s, int size, FILE *stream);
int sqlite3_fputs(const char *s, FILE *stream);
int sqlite3_fprintf(FILE *stream, const char *format, ...);
void sqlite3_fsetmode(FILE *stream, int mode);
#else
/**** Definitions For All Other Platforms ****/
#include <stdio.h>
#define sqlite3_fopen fopen
#define sqlite3_popen popen
#define sqlite3_fgets fgets
#define sqlite3_fputs fputs
#define sqlite3_fprintf fprintf
#define sqlite3_fsetmode(F,X) /*no-op*/
#endif
#endif /* _SQLITE3_STDIO_H_ */
/************************* End ../ext/misc/sqlite3_stdio.h ********************/
/************************* Begin ../ext/misc/sqlite3_stdio.c ******************/
/*
** 2024-09-24
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** Implementation of standard I/O interfaces for UTF-8 that are missing
** on Windows.
*/
#ifdef _WIN32 /* This file is a no-op on all platforms except Windows */
#ifndef _SQLITE3_STDIO_H_
/* #include "sqlite3_stdio.h" */
#endif
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
/* #include "sqlite3.h" */
#include <ctype.h>
#include <stdarg.h>
#include <io.h>
#include <fcntl.h>
/*
** If the SQLITE_U8TEXT_ONLY option is defined, then use O_U8TEXT
** when appropriate on all output. (Sometimes use O_BINARY when
** rendering ASCII text in cases where NL-to-CRLF expansion would
** not be correct.)
**
** If the SQLITE_U8TEXT_STDIO option is defined, then use O_U8TEXT
** when appropriate when writing to stdout or stderr. Use O_BINARY
** or O_TEXT (depending on things like the .mode and the .crlf setting
** in the CLI, or other context clues in other applications) for all
** other output channels.
**
** The default behavior, if neither of the above is defined is to
** use O_U8TEXT when writing to the Windows console (or anything
** else for which _isatty() returns true) and to use O_BINARY or O_TEXT
** for all other output channels.
*/
#if defined(SQLITE_U8TEXT_ONLY)
# define UseWtextForOutput(fd) 1
# define UseWtextForInput(fd) 1
# define IsConsole(fd) _isatty(_fileno(fd))
#elif defined(SQLITE_U8TEXT_STDIO)
# define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr)
# define UseWtextForInput(fd) ((fd)==stdin)
# define IsConsole(fd) _isatty(_fileno(fd))
#else
# define UseWtextForOutput(fd) _isatty(_fileno(fd))
# define UseWtextForInput(fd) _isatty(_fileno(fd))
# define IsConsole(fd) 1
#endif
/*
** Global variables determine if simulated O_BINARY mode is to be
** used for stdout or other, respectively. Simulated O_BINARY mode
** means the mode is usually O_BINARY, but switches to O_U8TEXT for
** unicode characters U+0080 or greater (any character that has a
** multi-byte representation in UTF-8). This is the only way we
** have found to render Unicode characters on a Windows console while
** at the same time avoiding undesirable \n to \r\n translation.
*/
static int simBinaryStdout = 0;
static int simBinaryOther = 0;
/*
** Determine if simulated binary mode should be used for output to fd
*/
static int UseBinaryWText(FILE *fd){
if( fd==stdout || fd==stderr ){
return simBinaryStdout;
}else{
return simBinaryOther;
}
}
/*
** Work-alike for the fopen() routine from the standard C library.
*/
FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
FILE *fp = 0;
wchar_t *b1, *b2;
int sz1, sz2;
sz1 = (int)strlen(zFilename);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
b1[sz1] = 0;
sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
b2[sz2] = 0;
fp = _wfopen(b1, b2);
}
sqlite3_free(b1);
sqlite3_free(b2);
simBinaryOther = 0;
return fp;
}
/*
** Work-alike for the popen() routine from the standard C library.
*/
FILE *sqlite3_popen(const char *zCommand, const char *zMode){
FILE *fp = 0;
wchar_t *b1, *b2;
int sz1, sz2;
sz1 = (int)strlen(zCommand);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
b1[sz1] = 0;
sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
b2[sz2] = 0;
fp = _wpopen(b1, b2);
}
sqlite3_free(b1);
sqlite3_free(b2);
return fp;
}
/*
** Work-alike for fgets() from the standard C library.
*/
char *sqlite3_fgets(char *buf, int sz, FILE *in){
if( UseWtextForInput(in) ){
/* When reading from the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode to read UTF-16 characters, then translate
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifndef SQLITE_USE_STDIO_FOR_CONSOLE
DWORD nRead = 0;
if( IsConsole(in)
&& ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0)
){
b1[nRead] = 0;
}else
#endif
{
_setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT);
if( fgetws(b1, sz/4, in)==0 ){
sqlite3_free(b1);
return 0;
}
}
WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0);
sqlite3_free(b1);
return buf;
}else{
/* Reading from a file or other input source, just read bytes without
** any translation. */
return fgets(buf, sz, in);
}
}
/*
** Send ASCII text as O_BINARY. But for Unicode characters U+0080 and
** greater, switch to O_U8TEXT.
*/
static void piecemealOutput(wchar_t *b1, int sz, FILE *out){
int i;
wchar_t c;
while( sz>0 ){
for(i=0; i<sz && b1[i]>=0x80; i++){}
if( i>0 ){
c = b1[i];
b1[i] = 0;
fflush(out);
_setmode(_fileno(out), _O_U8TEXT);
fputws(b1, out);
fflush(out);
b1 += i;
b1[0] = c;
sz -= i;
}else{
fflush(out);
_setmode(_fileno(out), _O_TEXT);
_setmode(_fileno(out), _O_BINARY);
fwrite(&b1[0], 1, 1, out);
for(i=1; i<sz && b1[i]<0x80; i++){
fwrite(&b1[i], 1, 1, out);
}
fflush(out);
_setmode(_fileno(out), _O_U8TEXT);
b1 += i;
sz -= i;
}
}
}
/*
** Work-alike for fputs() from the standard C library.
*/
int sqlite3_fputs(const char *z, FILE *out){
if( !UseWtextForOutput(out) ){
/* Writing to a file or other destination, just write bytes without
** any translation. */
return fputs(z, out);
}else{
/* One must use UTF16 in order to get unicode support when writing
** to the console on Windows.
*/
int sz = (int)strlen(z);
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
#ifndef SQLITE_STDIO_FOR_CONSOLE
DWORD nWr = 0;
if( IsConsole(out)
&& WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0)
){
/* If writing to the console, then the WriteConsoleW() is all we
** need to do. */
}else
#endif
{
/* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined
** then write using the standard library. */
_setmode(_fileno(out), _O_U8TEXT);
if( UseBinaryWText(out) ){
piecemealOutput(b1, sz, out);
}else{
fputws(b1, out);
}
}
sqlite3_free(b1);
return 0;
}
}
/*
** Work-alike for fprintf() from the standard C library.
*/
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
int rc;
if( UseWtextForOutput(out) ){
/* When writing to the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode and write UTF-16 characters.
*/
char *z;
va_list ap;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
sqlite3_fputs(z, out);
rc = (int)strlen(z);
sqlite3_free(z);
}else{
/* Writing to a file or other destination, just write bytes without
** any translation. */
va_list ap;
va_start(ap, zFormat);
rc = vfprintf(out, zFormat, ap);
va_end(ap);
}
return rc;
}
/*
** Set the mode for an output stream. mode argument is typically _O_BINARY or
** _O_TEXT.
*/
void sqlite3_fsetmode(FILE *fp, int mode){
if( !UseWtextForOutput(fp) ){
fflush(fp);
_setmode(_fileno(fp), mode);
}else if( fp==stdout || fp==stderr ){
simBinaryStdout = (mode==_O_BINARY);
}else{
simBinaryOther = (mode==_O_BINARY);
}
}
#endif /* defined(_WIN32) */
/************************* End ../ext/misc/sqlite3_stdio.c ********************/
/* Use console I/O package as a direct INCLUDE. */
#define SQLITE_INTERNAL_LINKAGE static
#ifdef SQLITE_SHELL_FIDDLE
/* Deselect most features from the console I/O package for Fiddle. */
# define SQLITE_CIO_NO_REDIRECT
# define SQLITE_CIO_NO_CLASSIFY
# define SQLITE_CIO_NO_TRANSLATE
# define SQLITE_CIO_NO_SETMODE
# define SQLITE_CIO_NO_FLUSH
#endif
#define eputz(z) sqlite3_fputs(z,stderr)
#define sputz(fp,z) sqlite3_fputs(z,fp)
/* True if the timer is enabled */
static int enableTimer = 0;
/* A version of strcmp() that works with NULL values */
static int cli_strcmp(const char *a, const char *b){
if( a==0 ) a = "";
|
| ︙ | ︙ | |||
1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 |
#if defined(_WRS_KERNEL) || defined(__RTP__)
struct rusage {
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
};
#define getrusage(A,B) memset(B,0,sizeof(*B))
#endif
/* Saved resource information for the beginning of an operation */
static struct rusage sBegin; /* CPU time at start */
static sqlite3_int64 iBegin; /* Wall-clock time at start */
/*
** Begin timing an operation
| > | 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
#if defined(_WRS_KERNEL) || defined(__RTP__)
struct rusage {
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
};
#define getrusage(A,B) memset(B,0,sizeof(*B))
#endif
/* Saved resource information for the beginning of an operation */
static struct rusage sBegin; /* CPU time at start */
static sqlite3_int64 iBegin; /* Wall-clock time at start */
/*
** Begin timing an operation
|
| ︙ | ︙ | |||
1438 1439 1440 1441 1442 1443 1444 |
return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
(double)(pEnd->tv_sec - pStart->tv_sec);
}
/*
** Print the timing results.
*/
| | | | | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 |
return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
(double)(pEnd->tv_sec - pStart->tv_sec);
}
/*
** Print the timing results.
*/
static void endTimer(FILE *out){
if( enableTimer ){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
(iEnd - iBegin)*0.001,
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
}
#define BEGIN_TIMER beginTimer()
#define END_TIMER(X) endTimer(X)
#define HAS_TIMER 1
#elif (defined(_WIN32) || defined(WIN32))
/* Saved resource information for the beginning of an operation */
static HANDLE hProcess;
static FILETIME ftKernelBegin;
|
| ︙ | ︙ | |||
1517 1518 1519 1520 1521 1522 1523 | sqlite_int64 i64End = *((sqlite_int64 *) pEnd); return (double) ((i64End - i64Start) / 10000000.0); } /* ** Print the timing results. */ | | | | | | 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
return (double) ((i64End - i64Start) / 10000000.0);
}
/*
** Print the timing results.
*/
static void endTimer(FILE *out){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
(ftWallEnd - ftWallBegin)*0.001,
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
}
}
#define BEGIN_TIMER beginTimer()
#define END_TIMER(X) endTimer(X)
#define HAS_TIMER hasTimer()
#else
#define BEGIN_TIMER
#define END_TIMER(X) /*no-op*/
#define HAS_TIMER 0
#endif
/*
** Used to prevent warnings about unused parameters
*/
#define UNUSED_PARAMETER(x) (void)(x)
|
| ︙ | ︙ | |||
1736 1737 1738 1739 1740 1741 1742 |
static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
va_list ap;
char *z;
if( iotrace==0 ) return;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > | | | | | > > | < > > > > > > > | | | | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 |
static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
va_list ap;
char *z;
if( iotrace==0 ) return;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
sqlite3_fprintf(iotrace, "%s", z);
sqlite3_free(z);
}
#endif
/* Lookup table to estimate the number of columns consumed by a Unicode
** character.
*/
static const struct {
unsigned char w; /* Width of the character in columns */
int iFirst; /* First character in a span having this width */
} aUWidth[] = {
/* {1, 0x00000}, */
{0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488},
{1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0},
{0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7},
{1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616},
{0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6},
{1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee},
{0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730},
{1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4},
{0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941},
{1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955},
{0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc},
{1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce},
{0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c},
{1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49},
{0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81},
{1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6},
{0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2},
{1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d},
{0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d},
{1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83},
{0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e},
{1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e},
{0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf},
{1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce},
{0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d},
{1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5},
{0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34},
{1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2},
{0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8},
{1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36},
{0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71},
{1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88},
{0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6},
{1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033},
{0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058},
{1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f},
{1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735},
{0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4},
{1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7},
{0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b},
{1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923},
{0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939},
{1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04},
{0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c},
{1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74},
{0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b},
{1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064},
{0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329},
{1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f},
{2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806},
{1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827},
{2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e},
{1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20},
{1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00},
{1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc},
{0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c},
{1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40},
{0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185},
{1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245},
{2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001},
{1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0}
};
/*
** Return an estimate of the width, in columns, for the single Unicode
** character c. For normal characters, the answer is always 1. But the
** estimate might be 0 or 2 for zero-width and double-width characters.
**
** Different display devices display unicode using different widths. So
** it is impossible to know that true display width with 100% accuracy.
** Inaccuracies in the width estimates might cause columns to be misaligned.
** Unfortunately, there is nothing we can do about that.
*/
int cli_wcwidth(int c){
int iFirst, iLast;
/* Fast path for common characters */
if( c<=0x300 ) return 1;
/* The general case */
iFirst = 0;
iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1;
while( iFirst<iLast-1 ){
int iMid = (iFirst+iLast)/2;
int cMid = aUWidth[iMid].iFirst;
if( cMid < c ){
iFirst = iMid;
}else if( cMid > c ){
iLast = iMid - 1;
}else{
return aUWidth[iMid].w;
}
}
if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w;
return aUWidth[iLast].w;
}
/*
** Compute the value and length of a multi-byte UTF-8 character that
** begins at z[0]. Return the length. Write the Unicode value into *pU.
**
** This routine only works for *multi-byte* UTF-8 characters.
*/
static int decodeUtf8(const unsigned char *z, int *pU){
if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){
*pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f);
return 2;
}
if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){
*pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f);
return 3;
}
if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80
&& (z[3] & 0xc0)==0x80
){
*pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
| (z[4] & 0x3f);
return 4;
}
*pU = 0;
return 1;
}
#if 0 /* NOT USED */
/*
** Return the width, in display columns, of a UTF-8 string.
**
** Each normal character counts as 1. Zero-width characters count
** as zero, and double-width characters count as 2.
*/
int cli_wcswidth(const char *z){
const unsigned char *a = (const unsigned char*)z;
int n = 0;
int i = 0;
unsigned char c;
while( (c = a[i])!=0 ){
if( c>=0xc0 ){
int u;
int len = decodeUtf8(&a[i], &u);
i += len;
n += cli_wcwidth(u);
}else if( c>=' ' ){
n++;
i++;
}else{
i++;
}
}
return n;
}
#endif
/*
** Output string zUtf to stdout as w characters. If w is negative,
** then right-justify the text. W is the width in UTF-8 characters, not
** in bytes. This is different from the %*.*s specification in printf
** since with %*.*s the width is measured in bytes, not characters.
**
** Take into account zero-width and double-width Unicode characters.
** In other words, a zero-width character does not count toward the
** the w limit. A double-width character counts as two.
*/
static void utf8_width_print(FILE *out, int w, const char *zUtf){
const unsigned char *a = (const unsigned char*)zUtf;
unsigned char c;
int i = 0;
int n = 0;
int aw = w<0 ? -w : w;
if( zUtf==0 ) zUtf = "";
while( (c = a[i])!=0 ){
if( (c&0xc0)==0xc0 ){
int u;
int len = decodeUtf8(a+i, &u);
int x = cli_wcwidth(u);
if( x+n>aw ){
break;
}
i += len;
n += x;
}else if( n>=aw ){
break;
}else{
n++;
i++;
}
}
if( n>=aw ){
sqlite3_fprintf(out, "%.*s", i, zUtf);
}else if( w<0 ){
sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf);
}else{
sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, "");
}
}
/*
** Determines if a string is a number of not.
*/
|
| ︙ | ︙ | |||
1832 1833 1834 1835 1836 1837 1838 |
static FILE * openChrSource(const char *zFile){
#if defined(_WIN32) || defined(WIN32)
struct __stat64 x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
/* On Windows, open first, then check the stream nature. This order
** is necessary because _stat() and sibs, when checking a named pipe,
** effectively break the pipe as its supplier sees it. */
| | | | 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 |
static FILE * openChrSource(const char *zFile){
#if defined(_WIN32) || defined(WIN32)
struct __stat64 x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
/* On Windows, open first, then check the stream nature. This order
** is necessary because _stat() and sibs, when checking a named pipe,
** effectively break the pipe as its supplier sees it. */
FILE *rv = sqlite3_fopen(zFile, "rb");
if( rv==0 ) return 0;
if( _fstat64(_fileno(rv), &x) != 0
|| !STAT_CHR_SRC(x.st_mode)){
fclose(rv);
rv = 0;
}
return rv;
#else
struct stat x = {0};
int rc = stat(zFile, &x);
# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode))
if( rc!=0 ) return 0;
if( STAT_CHR_SRC(x.st_mode) ){
return sqlite3_fopen(zFile, "rb");
}else{
return 0;
}
#endif
#undef STAT_CHR_SRC
}
|
| ︙ | ︙ | |||
1873 1874 1875 1876 1877 1878 1879 |
while( 1 ){
if( n+100>nLine ){
nLine = nLine*2 + 100;
zLine = realloc(zLine, nLine);
shell_check_oom(zLine);
}
| | | 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
while( 1 ){
if( n+100>nLine ){
nLine = nLine*2 + 100;
zLine = realloc(zLine, nLine);
shell_check_oom(zLine);
}
if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
if( n==0 ){
free(zLine);
return 0;
}
zLine[n] = 0;
break;
}
|
| ︙ | ︙ | |||
2939 2940 2941 2942 2943 2944 2945 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This SQLite extension implements functions that compute SHA3 hashes ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. | | | 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This SQLite extension implements functions that compute SHA3 hashes ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. ** Three SQL functions are implemented: ** ** sha3(X,SIZE) ** sha3_agg(Y,SIZE) ** sha3_query(Z,SIZE) ** ** The sha3(X) function computes the SHA3 hash of the input X, or NULL if ** X is NULL. If inputs X is text, the UTF-8 rendering of that text is |
| ︙ | ︙ | |||
3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 |
SQLITE_UTF8 | SQLITE_DIRECTONLY,
0, sha3QueryFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/shathree.c ********************/
/************************* Begin ../ext/misc/uint.c ******************/
/*
** 2020-04-14
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 |
SQLITE_UTF8 | SQLITE_DIRECTONLY,
0, sha3QueryFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/shathree.c ********************/
/************************* Begin ../ext/misc/sha1.c ******************/
/*
** 2017-01-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This SQLite extension implements functions that compute SHA1 hashes.
** Two SQL functions are implemented:
**
** sha1(X)
** sha1_query(Y)
**
** The sha1(X) function computes the SHA1 hash of the input X, or NULL if
** X is NULL.
**
** The sha1_query(Y) function evalutes all queries in the SQL statements of Y
** and returns a hash of their results.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <stdarg.h>
/******************************************************************************
** The Hash Engine
*/
/* Context for the SHA1 hash */
typedef struct SHA1Context SHA1Context;
struct SHA1Context {
unsigned int state[5];
unsigned int count[2];
unsigned char buffer[64];
};
#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r))
#define rol(x,k) SHA_ROT(x,k,32-(k))
#define ror(x,k) SHA_ROT(x,32-(k),k)
#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \
|(rol(block[i],8)&0x00FF00FF))
#define blk0be(i) block[i]
#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
^block[(i+2)&15]^block[i&15],1))
/*
* (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
*
* Rl0() for little-endian and Rb0() for big-endian. Endianness is
* determined at run-time.
*/
#define Rl0(v,w,x,y,z,i) \
z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2);
#define Rb0(v,w,x,y,z,i) \
z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2);
#define R1(v,w,x,y,z,i) \
z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2);
#define R2(v,w,x,y,z,i) \
z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2);
#define R3(v,w,x,y,z,i) \
z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2);
#define R4(v,w,x,y,z,i) \
z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2);
/*
* Hash a single 512-bit block. This is the core of the algorithm.
*/
static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){
unsigned int qq[5]; /* a, b, c, d, e; */
static int one = 1;
unsigned int block[16];
memcpy(block, buffer, 64);
memcpy(qq,state,5*sizeof(unsigned int));
#define a qq[0]
#define b qq[1]
#define c qq[2]
#define d qq[3]
#define e qq[4]
/* Copy p->state[] to working vars */
/*
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
*/
/* 4 rounds of 20 operations each. Loop unrolled. */
if( 1 == *(unsigned char*)&one ){
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);
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);
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);
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);
}else{
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);
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);
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);
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);
}
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
#undef a
#undef b
#undef c
#undef d
#undef e
}
/* Initialize a SHA1 context */
static void hash_init(SHA1Context *p){
/* SHA1 initialization constants */
p->state[0] = 0x67452301;
p->state[1] = 0xEFCDAB89;
p->state[2] = 0x98BADCFE;
p->state[3] = 0x10325476;
p->state[4] = 0xC3D2E1F0;
p->count[0] = p->count[1] = 0;
}
/* Add new content to the SHA1 hash */
static void hash_step(
SHA1Context *p, /* Add content to this context */
const unsigned char *data, /* Data to be added */
unsigned int len /* Number of bytes in data */
){
unsigned int i, j;
j = p->count[0];
if( (p->count[0] += len << 3) < j ){
p->count[1] += (len>>29)+1;
}
j = (j >> 3) & 63;
if( (j + len) > 63 ){
(void)memcpy(&p->buffer[j], data, (i = 64-j));
SHA1Transform(p->state, p->buffer);
for(; i + 63 < len; i += 64){
SHA1Transform(p->state, &data[i]);
}
j = 0;
}else{
i = 0;
}
(void)memcpy(&p->buffer[j], &data[i], len - i);
}
/* Compute a string using sqlite3_vsnprintf() and hash it */
static void hash_step_vformat(
SHA1Context *p, /* Add content to this context */
const char *zFormat,
...
){
va_list ap;
int n;
char zBuf[50];
va_start(ap, zFormat);
sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap);
va_end(ap);
n = (int)strlen(zBuf);
hash_step(p, (unsigned char*)zBuf, n);
}
/* Add padding and compute the message digest. Render the
** message digest as lower-case hexadecimal and put it into
** zOut[]. zOut[] must be at least 41 bytes long. */
static void hash_finish(
SHA1Context *p, /* The SHA1 context to finish and render */
char *zOut, /* Store hex or binary hash here */
int bAsBinary /* 1 for binary hash, 0 for hex hash */
){
unsigned int i;
unsigned char finalcount[8];
unsigned char digest[20];
static const char zEncode[] = "0123456789abcdef";
for (i = 0; i < 8; i++){
finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
hash_step(p, (const unsigned char *)"\200", 1);
while ((p->count[0] & 504) != 448){
hash_step(p, (const unsigned char *)"\0", 1);
}
hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */
for (i = 0; i < 20; i++){
digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
if( bAsBinary ){
memcpy(zOut, digest, 20);
}else{
for(i=0; i<20; i++){
zOut[i*2] = zEncode[(digest[i]>>4)&0xf];
zOut[i*2+1] = zEncode[digest[i] & 0xf];
}
zOut[i*2]= 0;
}
}
/* End of the hashing logic
*****************************************************************************/
/*
** Implementation of the sha1(X) function.
**
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
** argument X. If X is a BLOB, it is hashed as is. For all other
** types of input, X is converted into a UTF-8 string and the string
** is hash without the trailing 0x00 terminator. The hash of a NULL
** value is NULL.
*/
static void sha1Func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
SHA1Context cx;
int eType = sqlite3_value_type(argv[0]);
int nByte = sqlite3_value_bytes(argv[0]);
char zOut[44];
assert( argc==1 );
if( eType==SQLITE_NULL ) return;
hash_init(&cx);
if( eType==SQLITE_BLOB ){
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
}else{
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
}
if( sqlite3_user_data(context)!=0 ){
hash_finish(&cx, zOut, 1);
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
}else{
hash_finish(&cx, zOut, 0);
sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
}
}
/*
** Implementation of the sha1_query(SQL) function.
**
** This function compiles and runs the SQL statement(s) given in the
** argument. The results are hashed using SHA1 and that hash is returned.
**
** The original SQL text is included as part of the hash.
**
** The hash is not just a concatenation of the outputs. Each query
** is delimited and each row and value within the query is delimited,
** with all values being marked with their datatypes.
*/
static void sha1QueryFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
const char *zSql = (const char*)sqlite3_value_text(argv[0]);
sqlite3_stmt *pStmt = 0;
int nCol; /* Number of columns in the result set */
int i; /* Loop counter */
int rc;
int n;
const char *z;
SHA1Context cx;
char zOut[44];
assert( argc==1 );
if( zSql==0 ) return;
hash_init(&cx);
while( zSql[0] ){
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
if( rc ){
char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s",
zSql, sqlite3_errmsg(db));
sqlite3_finalize(pStmt);
sqlite3_result_error(context, zMsg, -1);
sqlite3_free(zMsg);
return;
}
if( !sqlite3_stmt_readonly(pStmt) ){
char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt));
sqlite3_finalize(pStmt);
sqlite3_result_error(context, zMsg, -1);
sqlite3_free(zMsg);
return;
}
nCol = sqlite3_column_count(pStmt);
z = sqlite3_sql(pStmt);
n = (int)strlen(z);
hash_step_vformat(&cx,"S%d:",n);
hash_step(&cx,(unsigned char*)z,n);
/* Compute a hash over the result of the query */
while( SQLITE_ROW==sqlite3_step(pStmt) ){
hash_step(&cx,(const unsigned char*)"R",1);
for(i=0; i<nCol; i++){
switch( sqlite3_column_type(pStmt,i) ){
case SQLITE_NULL: {
hash_step(&cx, (const unsigned char*)"N",1);
break;
}
case SQLITE_INTEGER: {
sqlite3_uint64 u;
int j;
unsigned char x[9];
sqlite3_int64 v = sqlite3_column_int64(pStmt,i);
memcpy(&u, &v, 8);
for(j=8; j>=1; j--){
x[j] = u & 0xff;
u >>= 8;
}
x[0] = 'I';
hash_step(&cx, x, 9);
break;
}
case SQLITE_FLOAT: {
sqlite3_uint64 u;
int j;
unsigned char x[9];
double r = sqlite3_column_double(pStmt,i);
memcpy(&u, &r, 8);
for(j=8; j>=1; j--){
x[j] = u & 0xff;
u >>= 8;
}
x[0] = 'F';
hash_step(&cx,x,9);
break;
}
case SQLITE_TEXT: {
int n2 = sqlite3_column_bytes(pStmt, i);
const unsigned char *z2 = sqlite3_column_text(pStmt, i);
hash_step_vformat(&cx,"T%d:",n2);
hash_step(&cx, z2, n2);
break;
}
case SQLITE_BLOB: {
int n2 = sqlite3_column_bytes(pStmt, i);
const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
hash_step_vformat(&cx,"B%d:",n2);
hash_step(&cx, z2, n2);
break;
}
}
}
}
sqlite3_finalize(pStmt);
}
hash_finish(&cx, zOut, 0);
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
}
#ifdef _WIN32
#endif
int sqlite3_sha_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
static int one = 1;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "sha1", 1,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, sha1Func, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha1b", 1,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
(void*)&one, sha1Func, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha1_query", 1,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
sha1QueryFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/sha1.c ********************/
/************************* Begin ../ext/misc/uint.c ******************/
/*
** 2020-04-14
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
|
| ︙ | ︙ | |||
5073 5074 5075 5076 5077 5078 5079 |
p->a[p->nUsed++] = y;
p->bSorted = 1;
}else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
p->a[p->nUsed++] = y;
}else if( p->bKeepSorted ){
int i;
i = percentBinarySearch(p, y, 0);
| | | 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 |
p->a[p->nUsed++] = y;
p->bSorted = 1;
}else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
p->a[p->nUsed++] = y;
}else if( p->bKeepSorted ){
int i;
i = percentBinarySearch(p, y, 0);
if( i<(int)p->nUsed ){
memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
}
p->a[i] = y;
p->nUsed++;
}else{
p->a[p->nUsed++] = y;
p->bSorted = 0;
|
| ︙ | ︙ | |||
5189 5190 5191 5192 5193 5194 5195 |
}
p->bKeepSorted = 1;
/* Find and remove the row */
i = percentBinarySearch(p, y, 1);
if( i>=0 ){
p->nUsed--;
| | | 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 |
}
p->bKeepSorted = 1;
/* Find and remove the row */
i = percentBinarySearch(p, y, 1);
if( i>=0 ){
p->nUsed--;
if( i<(int)p->nUsed ){
memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
}
}
}
/*
** Compute the final output of percentile(). Clean up all allocated
|
| ︙ | ︙ | |||
5249 5250 5251 5252 5253 5254 5255 |
#endif
int sqlite3_percentile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
| | | | | | 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 |
#endif
int sqlite3_percentile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
unsigned int i;
#ifdef SQLITE3EXT_H
SQLITE_EXTENSION_INIT2(pApi);
#else
(void)pApi; /* Unused parameter */
#endif
(void)pzErrMsg; /* Unused parameter */
for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
rc = sqlite3_create_window_function(db,
aPercentFunc[i].zName,
aPercentFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
|
| ︙ | ︙ | |||
7010 7011 7012 7013 7014 7015 7016 |
}else{
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
| > | | | | | | | | > | > | | | | | | | > | > | | | | | | | > | > | | | | | | | | | | | | | | | > | 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 |
}else{
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
if( pConstraint->iColumn<SERIES_COLUMN_START ){
if( pConstraint->iColumn==SERIES_COLUMN_VALUE && pConstraint->usable ){
switch( op ){
case SQLITE_INDEX_CONSTRAINT_EQ:
case SQLITE_INDEX_CONSTRAINT_IS: {
idxNum |= 0x0080;
idxNum &= ~0x3300;
aIdx[5] = i;
aIdx[6] = -1;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1;
#endif
break;
}
case SQLITE_INDEX_CONSTRAINT_GE: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x0100;
idxNum &= ~0x0200;
aIdx[5] = i;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1;
#endif
break;
}
case SQLITE_INDEX_CONSTRAINT_GT: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x0200;
idxNum &= ~0x0100;
aIdx[5] = i;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1;
#endif
break;
}
case SQLITE_INDEX_CONSTRAINT_LE: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x1000;
idxNum &= ~0x2000;
aIdx[6] = i;
break;
}
case SQLITE_INDEX_CONSTRAINT_LT: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x2000;
idxNum &= ~0x1000;
aIdx[6] = i;
break;
}
}
}
continue;
}
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
assert( iCol>=0 && iCol<=2 );
iMask = 1 << iCol;
|
| ︙ | ︙ | |||
8113 8114 8115 8116 8117 8118 8119 | ** set to (mode & 0777) before returning. ** ** If the optional MTIME argument is present, then it is interpreted ** as an integer - the number of seconds since the unix epoch. The ** modification-time of the target file is set to this value before ** returning. ** | | | 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 | ** set to (mode & 0777) before returning. ** ** If the optional MTIME argument is present, then it is interpreted ** as an integer - the number of seconds since the unix epoch. The ** modification-time of the target file is set to this value before ** returning. ** ** If five or more arguments are passed to this function and an ** error is encountered, an exception is raised. ** ** READFILE(FILE): ** ** Read and return the contents of file FILE (type blob) from disk. ** ** FSDIR: |
| ︙ | ︙ | |||
8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 |
# endif
# define mkdir(path,mode) _mkdir(path)
# define lstat(path,buf) stat(path,buf)
#endif
#include <time.h>
#include <errno.h>
/*
** Structure of the fsdir() table-valued function
*/
/* 0 1 2 3 4 5 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
| > > > > > > > | 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 |
# endif
# define mkdir(path,mode) _mkdir(path)
# define lstat(path,buf) stat(path,buf)
#endif
#include <time.h>
#include <errno.h>
/* When used as part of the CLI, the sqlite3_stdio.h module will have
** been included before this one. In that case use the sqlite3_stdio.h
** #defines. If not, create our own for fopen().
*/
#ifndef _SQLITE3_STDIO_H_
# define sqlite3_fopen fopen
#endif
/*
** Structure of the fsdir() table-valued function
*/
/* 0 1 2 3 4 5 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
|
| ︙ | ︙ | |||
8215 8216 8217 8218 8219 8220 8221 |
static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in;
sqlite3_int64 nIn;
void *pBuf;
sqlite3 *db;
int mxBlob;
| | | 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 |
static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in;
sqlite3_int64 nIn;
void *pBuf;
sqlite3 *db;
int mxBlob;
in = sqlite3_fopen(zName, "rb");
if( in==0 ){
/* File does not exist or is unreadable. Leave the result set to NULL. */
return;
}
fseek(in, 0, SEEK_END);
nIn = ftell(in);
rewind(in);
|
| ︙ | ︙ | |||
8470 8471 8472 8473 8474 8475 8476 |
return 1;
}
}
}else{
sqlite3_int64 nWrite = 0;
const char *z;
int rc = 0;
| | | 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 |
return 1;
}
}
}else{
sqlite3_int64 nWrite = 0;
const char *z;
int rc = 0;
FILE *out = sqlite3_fopen(zFile, "wb");
if( out==0 ) return 1;
z = (const char*)sqlite3_value_blob(pData);
if( z ){
sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out);
nWrite = sqlite3_value_bytes(pData);
if( nWrite!=n ){
rc = 1;
|
| ︙ | ︙ | |||
10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 | #include <string.h> #include <assert.h> #ifndef SQLITE_NO_STDINT # include <stdint.h> #endif #include <zlib.h> #ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_AMALGAMATION #ifndef UINT32_TYPE # ifdef HAVE_UINT32_T | > > > > > > > > | 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 | #include <string.h> #include <assert.h> #ifndef SQLITE_NO_STDINT # include <stdint.h> #endif #include <zlib.h> /* When used as part of the CLI, the sqlite3_stdio.h module will have ** been included before this one. In that case use the sqlite3_stdio.h ** #defines. If not, create our own for fopen(). */ #ifndef _SQLITE3_STDIO_H_ # define sqlite3_fopen fopen #endif #ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_AMALGAMATION #ifndef UINT32_TYPE # ifdef HAVE_UINT32_T |
| ︙ | ︙ | |||
11588 11589 11590 11591 11592 11593 11594 |
if( rc!=SQLITE_OK ) return rc;
bInMemory = 1;
}else{
zFile = (const char*)sqlite3_value_text(argv[0]);
}
if( 0==pTab->pWriteFd && 0==bInMemory ){
| | | 11449 11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 |
if( rc!=SQLITE_OK ) return rc;
bInMemory = 1;
}else{
zFile = (const char*)sqlite3_value_text(argv[0]);
}
if( 0==pTab->pWriteFd && 0==bInMemory ){
pCsr->pFile = zFile ? sqlite3_fopen(zFile, "rb") : 0;
if( pCsr->pFile==0 ){
zipfileCursorErr(pCsr, "cannot open file: %s", zFile);
rc = SQLITE_ERROR;
}else{
rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd);
if( rc==SQLITE_OK ){
if( pCsr->eocd.nEntry==0 ){
|
| ︙ | ︙ | |||
11778 11779 11780 11781 11782 11783 11784 |
return SQLITE_ERROR;
}
/* Open a write fd on the file. Also load the entire central directory
** structure into memory. During the transaction any new file data is
** appended to the archive file, but the central directory is accumulated
** in main-memory until the transaction is committed. */
| | | 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 |
return SQLITE_ERROR;
}
/* Open a write fd on the file. Also load the entire central directory
** structure into memory. During the transaction any new file data is
** appended to the archive file, but the central directory is accumulated
** in main-memory until the transaction is committed. */
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+");
if( pTab->pWriteFd==0 ){
pTab->base.zErrMsg = sqlite3_mprintf(
"zipfile: failed to open file %s for writing", pTab->zFile
);
rc = SQLITE_ERROR;
}else{
fseek(pTab->pWriteFd, 0, SEEK_END);
|
| ︙ | ︙ | |||
14231 14232 14233 14234 14235 14236 14237 14238 14239 14240 14241 14242 14243 14244 14245 14246 14247 14248 |
pEnd = pFirst;
pFirst = p->pWrite;
}
return rc;
}
static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
int rc = idxRegisterVtab(p);
sqlite3_stmt *pSchema = 0;
/* For each table in the main db schema:
**
** 1) Add an entry to the p->pTable list, and
** 2) Create the equivalent virtual table in dbv.
*/
rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | | > > > > > | > | | | 14092 14093 14094 14095 14096 14097 14098 14099 14100 14101 14102 14103 14104 14105 14106 14107 14108 14109 14110 14111 14112 14113 14114 14115 14116 14117 14118 14119 14120 14121 14122 14123 14124 14125 14126 14127 14128 14129 14130 14131 14132 14133 14134 14135 14136 14137 14138 14139 14140 14141 14142 14143 14144 14145 14146 14147 14148 14149 14150 14151 14152 14153 14154 14155 14156 14157 14158 14159 14160 14161 14162 14163 14164 14165 14166 14167 14168 14169 14170 14171 14172 14173 14174 14175 14176 14177 14178 14179 14180 14181 14182 14183 14184 14185 14186 14187 14188 14189 14190 14191 14192 14193 14194 14195 14196 14197 14198 14199 14200 14201 14202 14203 14204 14205 |
pEnd = pFirst;
pFirst = p->pWrite;
}
return rc;
}
/*
** This function tests if the schema of the main database of database handle
** db contains an object named zTab. Assuming no error occurs, output parameter
** (*pbContains) is set to true if zTab exists, or false if it does not.
**
** Or, if an error occurs, an SQLite error code is returned. The final value
** of (*pbContains) is undefined in this case.
*/
static int expertDbContainsObject(
sqlite3 *db,
const char *zTab,
int *pbContains /* OUT: True if object exists */
){
const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?";
sqlite3_stmt *pSql = 0;
int rc = SQLITE_OK;
int ret = 0;
rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_text(pSql, 1, zTab, -1, SQLITE_STATIC);
if( SQLITE_ROW==sqlite3_step(pSql) ){
ret = 1;
}
rc = sqlite3_finalize(pSql);
}
*pbContains = ret;
return rc;
}
/*
** Execute SQL command zSql using database handle db. If no error occurs,
** set (*pzErr) to NULL and return SQLITE_OK.
**
** If an error does occur, return an SQLite error code and set (*pzErr) to
** point to a buffer containing an English language error message. Except,
** if the error message begins with "no such module:", then ignore the
** error and return as if the SQL statement had succeeded.
**
** This is used to copy as much of the database schema as possible while
** ignoring any errors related to missing virtual table modules.
*/
static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){
int rc = SQLITE_OK;
char *zErr = 0;
rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
if( rc!=SQLITE_OK && zErr ){
int nErr = STRLEN(zErr);
if( nErr>=15 && memcmp(zErr, "no such module:", 15)==0 ){
sqlite3_free(zErr);
rc = SQLITE_OK;
zErr = 0;
}
}
*pzErr = zErr;
return rc;
}
static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
int rc = idxRegisterVtab(p);
sqlite3_stmt *pSchema = 0;
/* For each table in the main db schema:
**
** 1) Add an entry to the p->pTable list, and
** 2) Create the equivalent virtual table in dbv.
*/
rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
"SELECT type, name, sql, 1, "
" substr(sql,1,14)=='create virtual' COLLATE nocase "
"FROM sqlite_schema "
"WHERE type IN ('table','view') AND "
" substr(name,1,7)!='sqlite_' COLLATE nocase "
" UNION ALL "
"SELECT type, name, sql, 2, 0 FROM sqlite_schema "
"WHERE type = 'trigger'"
" AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
"ORDER BY 4, 5 DESC, 1"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
const char *zType = (const char*)sqlite3_column_text(pSchema, 0);
const char *zName = (const char*)sqlite3_column_text(pSchema, 1);
const char *zSql = (const char*)sqlite3_column_text(pSchema, 2);
int bVirtual = sqlite3_column_int(pSchema, 4);
int bExists = 0;
if( zType==0 || zName==0 ) continue;
rc = expertDbContainsObject(p->dbv, zName, &bExists);
if( rc || bExists ) continue;
if( zType[0]=='v' || zType[1]=='r' || bVirtual ){
/* A view. Or a trigger on a view. */
if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
}else{
IdxTable *pTab;
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){
int i;
char *zInner = 0;
char *zOuter = 0;
pTab->pNext = p->pTable;
p->pTable = pTab;
/* The statement the vtab will pass to sqlite3_declare_vtab() */
|
| ︙ | ︙ | |||
14462 14463 14464 14465 14466 14467 14468 14469 14470 14471 14472 14473 14474 14475 |
/* Formulate the query text */
sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
const char *zComma = zCols==0 ? "" : ", ";
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
zCols = idxAppendText(&rc, zCols,
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
zComma, zName, nCol, zName, zColl
);
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
}
sqlite3_reset(pIndexXInfo);
| > > > > > > | 14392 14393 14394 14395 14396 14397 14398 14399 14400 14401 14402 14403 14404 14405 14406 14407 14408 14409 14410 14411 |
/* Formulate the query text */
sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
const char *zComma = zCols==0 ? "" : ", ";
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
if( zName==0 ){
/* This index contains an expression. Ignore it. */
sqlite3_free(zCols);
sqlite3_free(zOrder);
return sqlite3_reset(pIndexXInfo);
}
zCols = idxAppendText(&rc, zCols,
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
zComma, zName, nCol, zName, zColl
);
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
}
sqlite3_reset(pIndexXInfo);
|
| ︙ | ︙ | |||
14790 14791 14792 14793 14794 14795 14796 |
}
#endif
/* Copy the entire schema of database [db] into [dbm]. */
if( rc==SQLITE_OK ){
sqlite3_stmt *pSql = 0;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
| > | | > > > > | > | 14726 14727 14728 14729 14730 14731 14732 14733 14734 14735 14736 14737 14738 14739 14740 14741 14742 14743 14744 14745 14746 14747 14748 14749 14750 14751 |
}
#endif
/* Copy the entire schema of database [db] into [dbm]. */
if( rc==SQLITE_OK ){
sqlite3_stmt *pSql = 0;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql, name, substr(sql,1,14)=='create virtual' COLLATE nocase"
" FROM sqlite_schema WHERE substr(name,1,7)!='sqlite_' COLLATE nocase"
" ORDER BY 3 DESC, rowid"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
const char *zName = (const char*)sqlite3_column_text(pSql, 1);
int bExists = 0;
rc = expertDbContainsObject(pNew->dbm, zName, &bExists);
if( rc==SQLITE_OK && zSql && bExists==0 ){
rc = expertSchemaSql(pNew->dbm, zSql, pzErrmsg);
}
}
idxFinalize(&rc, pSql);
}
/* Create the vtab schema */
if( rc==SQLITE_OK ){
rc = idxCreateVtabSchema(pNew, pzErrmsg);
|
| ︙ | ︙ | |||
16209 16210 16211 16212 16213 16214 16215 16216 16217 16218 16219 16220 16221 16222 |
rc = sqlite3_create_function(db, "stmtrand", 0, SQLITE_UTF8, 0,
stmtrandFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/stmtrand.c ********************/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
#else
#define SQLITE_SHELL_HAVE_RECOVER 0
#endif
#if SQLITE_SHELL_HAVE_RECOVER
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 16217 16218 16219 16220 16221 16222 16223 16224 16225 16226 16227 16228 16229 16230 16231 16232 16233 16234 16235 16236 16237 16238 16239 16240 16241 16242 16243 16244 16245 16246 16247 16248 16249 16250 16251 16252 16253 16254 16255 16256 16257 16258 16259 16260 16261 16262 16263 16264 16265 16266 16267 16268 16269 16270 16271 16272 16273 16274 16275 16276 16277 16278 16279 16280 16281 16282 16283 16284 16285 16286 16287 16288 16289 16290 16291 16292 16293 16294 16295 16296 16297 16298 16299 16300 16301 16302 16303 16304 16305 16306 16307 16308 16309 16310 16311 16312 16313 16314 16315 16316 16317 16318 16319 16320 16321 16322 16323 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 16380 16381 16382 16383 16384 16385 16386 16387 16388 16389 16390 16391 16392 16393 16394 16395 16396 16397 16398 16399 16400 16401 16402 16403 16404 16405 16406 16407 16408 16409 16410 16411 16412 16413 16414 16415 16416 16417 16418 16419 16420 16421 16422 16423 16424 16425 16426 16427 16428 16429 16430 16431 16432 16433 16434 16435 16436 16437 16438 16439 16440 16441 16442 16443 16444 16445 16446 16447 16448 16449 16450 16451 16452 16453 16454 16455 16456 16457 16458 16459 16460 16461 16462 16463 16464 16465 16466 16467 16468 16469 16470 16471 16472 16473 16474 16475 16476 16477 16478 16479 16480 16481 16482 16483 16484 16485 16486 16487 16488 16489 16490 16491 16492 16493 16494 16495 16496 16497 16498 16499 16500 16501 16502 16503 16504 16505 16506 16507 16508 16509 16510 16511 16512 16513 16514 16515 16516 16517 16518 16519 16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 16590 16591 16592 16593 16594 16595 16596 16597 16598 16599 16600 16601 16602 16603 16604 16605 16606 16607 16608 16609 16610 16611 16612 16613 16614 16615 16616 16617 16618 16619 16620 16621 16622 16623 16624 16625 16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 16645 16646 16647 16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 16680 16681 16682 16683 16684 16685 16686 16687 16688 16689 16690 16691 16692 16693 16694 16695 16696 16697 16698 16699 16700 16701 16702 16703 16704 16705 16706 16707 16708 16709 16710 16711 16712 16713 16714 16715 16716 16717 16718 16719 16720 16721 16722 16723 16724 16725 16726 16727 16728 16729 16730 16731 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 16747 16748 16749 16750 16751 16752 16753 16754 16755 16756 16757 16758 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 16772 16773 16774 16775 16776 16777 16778 16779 16780 16781 16782 16783 16784 16785 16786 16787 16788 16789 16790 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 16809 16810 16811 16812 16813 16814 16815 16816 16817 16818 16819 16820 16821 16822 16823 16824 16825 16826 16827 16828 16829 16830 16831 16832 16833 16834 16835 16836 16837 16838 16839 16840 16841 16842 16843 16844 16845 16846 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 16862 16863 16864 16865 16866 16867 16868 16869 16870 16871 16872 16873 16874 16875 16876 16877 16878 16879 16880 16881 16882 16883 16884 16885 16886 16887 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 16903 16904 16905 16906 16907 16908 16909 16910 16911 16912 16913 16914 16915 16916 16917 16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 16951 16952 16953 16954 16955 16956 16957 16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 17006 17007 17008 17009 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 17034 17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 17063 17064 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 17124 17125 17126 17127 17128 17129 17130 17131 17132 17133 17134 17135 17136 17137 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 17151 17152 17153 17154 17155 17156 17157 17158 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 17197 17198 17199 17200 17201 17202 17203 17204 17205 17206 17207 17208 17209 17210 17211 17212 17213 17214 17215 17216 17217 17218 17219 17220 17221 17222 17223 17224 17225 17226 17227 17228 17229 17230 17231 17232 17233 17234 17235 17236 17237 17238 17239 17240 17241 17242 17243 17244 17245 17246 17247 17248 17249 17250 17251 17252 17253 17254 17255 17256 17257 17258 17259 17260 17261 17262 17263 17264 17265 17266 17267 17268 17269 17270 17271 17272 17273 17274 17275 17276 17277 17278 17279 17280 17281 17282 17283 17284 17285 17286 17287 17288 17289 17290 17291 17292 17293 17294 17295 17296 17297 17298 17299 17300 17301 17302 17303 17304 17305 17306 17307 17308 17309 17310 17311 17312 17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 17339 17340 17341 17342 17343 17344 17345 17346 17347 17348 17349 17350 17351 |
rc = sqlite3_create_function(db, "stmtrand", 0, SQLITE_UTF8, 0,
stmtrandFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/stmtrand.c ********************/
/************************* Begin ../ext/misc/vfstrace.c ******************/
/*
** 2011 March 16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains code implements a VFS shim that writes diagnostic
** output for each VFS call, similar to "strace".
**
** USAGE:
**
** This source file exports a single symbol which is the name of a
** function:
**
** int vfstrace_register(
** const char *zTraceName, // Name of the newly constructed VFS
** const char *zOldVfsName, // Name of the underlying VFS
** int (*xOut)(const char*,void*), // Output routine. ex: fputs
** void *pOutArg, // 2nd argument to xOut. ex: stderr
** int makeDefault // Make the new VFS the default
** );
**
** Applications that want to trace their VFS usage must provide a callback
** function with this prototype:
**
** int traceOutput(const char *zMessage, void *pAppData);
**
** This function will "output" the trace messages, where "output" can
** mean different things to different applications. The traceOutput function
** for the command-line shell (see shell.c) is "fputs" from the standard
** library, which means that all trace output is written on the stream
** specified by the second argument. In the case of the command-line shell
** the second argument is stderr. Other applications might choose to output
** trace information to a file, over a socket, or write it into a buffer.
**
** The vfstrace_register() function creates a new "shim" VFS named by
** the zTraceName parameter. A "shim" VFS is an SQLite backend that does
** not really perform the duties of a true backend, but simply filters or
** interprets VFS calls before passing them off to another VFS which does
** the actual work. In this case the other VFS - the one that does the
** real work - is identified by the second parameter, zOldVfsName. If
** the 2nd parameter is NULL then the default VFS is used. The common
** case is for the 2nd parameter to be NULL.
**
** The third and fourth parameters are the pointer to the output function
** and the second argument to the output function. For the SQLite
** command-line shell, when the -vfstrace option is used, these parameters
** are fputs and stderr, respectively.
**
** The fifth argument is true (non-zero) to cause the newly created VFS
** to become the default VFS. The common case is for the fifth parameter
** to be true.
**
** The call to vfstrace_register() simply creates the shim VFS that does
** tracing. The application must also arrange to use the new VFS for
** all database connections that are created and for which tracing is
** desired. This can be done by specifying the trace VFS using URI filename
** notation, or by specifying the trace VFS as the 4th parameter to
** sqlite3_open_v2() or by making the trace VFS be the default (by setting
** the 5th parameter of vfstrace_register() to 1).
**
**
** ENABLING VFSTRACE IN A COMMAND-LINE SHELL
**
** The SQLite command line shell implemented by the shell.c source file
** can be used with this module. To compile in -vfstrace support, first
** gather this file (test_vfstrace.c), the shell source file (shell.c),
** and the SQLite amalgamation source files (sqlite3.c, sqlite3.h) into
** the working directory. Then compile using a command like the following:
**
** gcc -o sqlite3 -Os -I. -DSQLITE_ENABLE_VFSTRACE \
** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE \
** -DHAVE_READLINE -DHAVE_USLEEP=1 \
** shell.c test_vfstrace.c sqlite3.c -ldl -lreadline -lncurses
**
** The gcc command above works on Linux and provides (in addition to the
** -vfstrace option) support for FTS3 and FTS4, RTREE, and command-line
** editing using the readline library. The command-line shell does not
** use threads so we added -DSQLITE_THREADSAFE=0 just to make the code
** run a little faster. For compiling on a Mac, you'll probably need
** to omit the -DHAVE_READLINE, the -lreadline, and the -lncurses options.
** The compilation could be simplified to just this:
**
** gcc -DSQLITE_ENABLE_VFSTRACE \
** shell.c test_vfstrace.c sqlite3.c -ldl -lpthread
**
** In this second example, all unnecessary options have been removed
** Note that since the code is now threadsafe, we had to add the -lpthread
** option to pull in the pthreads library.
**
** To cross-compile for windows using MinGW, a command like this might
** work:
**
** /opt/mingw/bin/i386-mingw32msvc-gcc -o sqlite3.exe -Os -I \
** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_VFSTRACE \
** shell.c test_vfstrace.c sqlite3.c
**
** Similar compiler commands will work on different systems. The key
** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that
** the shell.c source file will know to include the -vfstrace command-line
** option and (2) you must compile and link the three source files
** shell,c, test_vfstrace.c, and sqlite3.c.
**
** RUNTIME CONTROL OF VFSTRACE OUTPUT
**
** The application can use the "vfstrace" pragma to control which VFS
** APIs are traced. To disable all output:
**
** PRAGMA vfstrace('-all');
**
** To enable all output (which is the default setting):
**
** PRAGMA vfstrace('+all');
**
** Individual APIs can be enabled or disabled by name, with or without
** the initial "x" character. For example, to set up for tracing lock
** primatives only:
**
** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
**
** The argument to the vfstrace pragma ignores capitalization and any
** characters other than alphabetics, '+', and '-'.
*/
#include <stdlib.h>
#include <string.h>
/* #include "sqlite3.h" */
/*
** An instance of this structure is attached to the each trace VFS to
** provide auxiliary information.
*/
typedef struct vfstrace_info vfstrace_info;
struct vfstrace_info {
sqlite3_vfs *pRootVfs; /* The underlying real VFS */
int (*xOut)(const char*, void*); /* Send output here */
unsigned int mTrace; /* Mask of interfaces to trace */
u8 bOn; /* Tracing on/off */
void *pOutArg; /* First argument to xOut */
const char *zVfsName; /* Name of this trace-VFS */
sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */
};
/*
** The sqlite3_file object for the trace VFS
*/
typedef struct vfstrace_file vfstrace_file;
struct vfstrace_file {
sqlite3_file base; /* Base class. Must be first */
vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */
const char *zFName; /* Base name of the file */
sqlite3_file *pReal; /* The real underlying file */
};
/*
** Bit values for vfstrace_info.mTrace.
*/
#define VTR_CLOSE 0x00000001
#define VTR_READ 0x00000002
#define VTR_WRITE 0x00000004
#define VTR_TRUNC 0x00000008
#define VTR_SYNC 0x00000010
#define VTR_FSIZE 0x00000020
#define VTR_LOCK 0x00000040
#define VTR_UNLOCK 0x00000080
#define VTR_CRL 0x00000100
#define VTR_FCTRL 0x00000200
#define VTR_SECSZ 0x00000400
#define VTR_DEVCHAR 0x00000800
#define VTR_SHMLOCK 0x00001000
#define VTR_SHMMAP 0x00002000
#define VTR_SHMBAR 0x00004000
#define VTR_SHMUNMAP 0x00008000
#define VTR_OPEN 0x00010000
#define VTR_DELETE 0x00020000
#define VTR_ACCESS 0x00040000
#define VTR_FULLPATH 0x00080000
#define VTR_DLOPEN 0x00100000
#define VTR_DLERR 0x00200000
#define VTR_DLSYM 0x00400000
#define VTR_DLCLOSE 0x00800000
#define VTR_RAND 0x01000000
#define VTR_SLEEP 0x02000000
#define VTR_CURTIME 0x04000000
#define VTR_LASTERR 0x08000000
/*
** Method declarations for vfstrace_file.
*/
static int vfstraceClose(sqlite3_file*);
static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size);
static int vfstraceSync(sqlite3_file*, int flags);
static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int vfstraceLock(sqlite3_file*, int);
static int vfstraceUnlock(sqlite3_file*, int);
static int vfstraceCheckReservedLock(sqlite3_file*, int *);
static int vfstraceFileControl(sqlite3_file*, int op, void *pArg);
static int vfstraceSectorSize(sqlite3_file*);
static int vfstraceDeviceCharacteristics(sqlite3_file*);
static int vfstraceShmLock(sqlite3_file*,int,int,int);
static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **);
static void vfstraceShmBarrier(sqlite3_file*);
static int vfstraceShmUnmap(sqlite3_file*,int);
/*
** Method declarations for vfstrace_vfs.
*/
static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *);
static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename);
static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
static void vfstraceDlClose(sqlite3_vfs*, void*);
static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int vfstraceSleep(sqlite3_vfs*, int microseconds);
static int vfstraceCurrentTime(sqlite3_vfs*, double*);
static int vfstraceGetLastError(sqlite3_vfs*, int, char*);
static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr);
static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *);
static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName);
/*
** Return a pointer to the tail of the pathname. Examples:
**
** /home/drh/xyzzy.txt -> xyzzy.txt
** xyzzy.txt -> xyzzy.txt
*/
static const char *fileTail(const char *z){
size_t i;
if( z==0 ) return 0;
i = strlen(z)-1;
while( i>0 && z[i-1]!='/' ){ i--; }
return &z[i];
}
/*
** Send trace output defined by zFormat and subsequent arguments.
*/
static void vfstrace_printf(
vfstrace_info *pInfo,
const char *zFormat,
...
){
va_list ap;
char *zMsg;
if( pInfo->bOn ){
va_start(ap, zFormat);
zMsg = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
pInfo->xOut(zMsg, pInfo->pOutArg);
sqlite3_free(zMsg);
}
}
/*
** Try to convert an error code into a symbolic name for that error code.
*/
static const char *vfstrace_errcode_name(int rc ){
const char *zVal = 0;
switch( rc ){
case SQLITE_OK: zVal = "SQLITE_OK"; break;
case SQLITE_INTERNAL: zVal = "SQLITE_INTERNAL"; break;
case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break;
case SQLITE_PERM: zVal = "SQLITE_PERM"; break;
case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break;
case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break;
case SQLITE_LOCKED: zVal = "SQLITE_LOCKED"; break;
case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break;
case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break;
case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break;
case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break;
case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break;
case SQLITE_NOTFOUND: zVal = "SQLITE_NOTFOUND"; break;
case SQLITE_FULL: zVal = "SQLITE_FULL"; break;
case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break;
case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break;
case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break;
case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break;
case SQLITE_TOOBIG: zVal = "SQLITE_TOOBIG"; break;
case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break;
case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break;
case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break;
case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break;
case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break;
case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break;
case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break;
case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break;
case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break;
case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break;
case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break;
case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break;
case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break;
case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break;
case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break;
case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break;
case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break;
case SQLITE_IOERR_CHECKRESERVEDLOCK:
zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break;
case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break;
case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break;
case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break;
case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break;
case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break;
case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break;
case SQLITE_IOERR_SHMMAP: zVal = "SQLITE_IOERR_SHMMAP"; break;
case SQLITE_IOERR_SEEK: zVal = "SQLITE_IOERR_SEEK"; break;
case SQLITE_IOERR_GETTEMPPATH: zVal = "SQLITE_IOERR_GETTEMPPATH"; break;
case SQLITE_IOERR_CONVPATH: zVal = "SQLITE_IOERR_CONVPATH"; break;
case SQLITE_READONLY_DBMOVED: zVal = "SQLITE_READONLY_DBMOVED"; break;
case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break;
case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
}
return zVal;
}
/*
** Convert value rc into a string and print it using zFormat. zFormat
** should have exactly one %s
*/
static void vfstrace_print_errcode(
vfstrace_info *pInfo,
const char *zFormat,
int rc
){
const char *zVal;
char zBuf[50];
zVal = vfstrace_errcode_name(rc);
if( zVal==0 ){
zVal = vfstrace_errcode_name(rc&0xff);
if( zVal ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "%s | 0x%x", zVal, rc&0xffff00);
}else{
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d (0x%x)", rc, rc);
}
zVal = zBuf;
}
vfstrace_printf(pInfo, zFormat, zVal);
}
/*
** Append to a buffer.
*/
static void strappend(char *z, int *pI, const char *zAppend){
int i = *pI;
while( zAppend[0] ){ z[i++] = *(zAppend++); }
z[i] = 0;
*pI = i;
}
/*
** Turn tracing output on or off according to mMask.
*/
static void vfstraceOnOff(vfstrace_info *pInfo, unsigned int mMask){
pInfo->bOn = (pInfo->mTrace & mMask)!=0;
}
/*
** Close an vfstrace-file.
*/
static int vfstraceClose(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_CLOSE);
vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xClose(p->pReal);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
if( rc==SQLITE_OK ){
sqlite3_free((void*)p->base.pMethods);
p->base.pMethods = 0;
}
return rc;
}
/*
** Read data from an vfstrace-file.
*/
static int vfstraceRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_READ);
vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Write data to an vfstrace-file.
*/
static int vfstraceWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_WRITE);
vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Truncate an vfstrace-file.
*/
static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_TRUNC);
vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
size);
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Sync an vfstrace-file.
*/
static int vfstraceSync(sqlite3_file *pFile, int flags){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
int i;
char zBuf[100];
memcpy(zBuf, "|0", 3);
i = 0;
if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL");
else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
}
vfstraceOnOff(pInfo, VTR_SYNC);
vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
&zBuf[1]);
rc = p->pReal->pMethods->xSync(p->pReal, flags);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the current file-size of an vfstrace-file.
*/
static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_FSIZE);
vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
vfstrace_print_errcode(pInfo, " -> %s,", rc);
vfstrace_printf(pInfo, " size=%lld\n", *pSize);
return rc;
}
/*
** Return the name of a lock.
*/
static const char *lockName(int eLock){
const char *azLockNames[] = {
"NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE"
};
if( eLock<0 || eLock>=(int)(sizeof(azLockNames)/sizeof(azLockNames[0])) ){
return "???";
}else{
return azLockNames[eLock];
}
}
/*
** Lock an vfstrace-file.
*/
static int vfstraceLock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_LOCK);
vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Unlock an vfstrace-file.
*/
static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_UNLOCK);
vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Check if another file-handle holds a RESERVED lock on an vfstrace-file.
*/
static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_CRL);
vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
/*
** File control method. For custom operations on an vfstrace-file.
*/
static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zBuf[100];
char zBuf2[100];
char *zOp;
char *zRVal = 0;
vfstraceOnOff(pInfo, VTR_FCTRL);
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
case SQLITE_FCNTL_SIZE_HINT: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld",
*(sqlite3_int64*)pArg);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break;
case SQLITE_FCNTL_PERSIST_WAL: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "PERSIST_WAL,%d", *(int*)pArg);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
case SQLITE_FCNTL_PRAGMA: {
const char *const* a = (const char*const*)pArg;
if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
const u8 *zArg = (const u8*)a[2];
if( zArg[0]>='0' && zArg[0]<=9 ){
pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
}else{
static const struct {
const char *z;
unsigned int m;
} aKw[] = {
{ "all", 0xffffffff },
{ "close", VTR_CLOSE },
{ "read", VTR_READ },
{ "write", VTR_WRITE },
{ "truncate", VTR_TRUNC },
{ "sync", VTR_SYNC },
{ "filesize", VTR_FSIZE },
{ "lock", VTR_LOCK },
{ "unlock", VTR_UNLOCK },
{ "checkreservedlock", VTR_CRL },
{ "filecontrol", VTR_FCTRL },
{ "sectorsize", VTR_SECSZ },
{ "devicecharacteristics", VTR_DEVCHAR },
{ "shmlock", VTR_SHMLOCK },
{ "shmmap", VTR_SHMMAP },
{ "shmummap", VTR_SHMUNMAP },
{ "shmbarrier", VTR_SHMBAR },
{ "open", VTR_OPEN },
{ "delete", VTR_DELETE },
{ "access", VTR_ACCESS },
{ "fullpathname", VTR_FULLPATH },
{ "dlopen", VTR_DLOPEN },
{ "dlerror", VTR_DLERR },
{ "dlsym", VTR_DLSYM },
{ "dlclose", VTR_DLCLOSE },
{ "randomness", VTR_RAND },
{ "sleep", VTR_SLEEP },
{ "currenttime", VTR_CURTIME },
{ "currenttimeint64", VTR_CURTIME },
{ "getlasterror", VTR_LASTERR },
};
int onOff = 1;
while( zArg[0] ){
int jj, n;
while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+'
&& !isalpha(zArg[0]) ) zArg++;
if( zArg[0]==0 ) break;
if( zArg[0]=='-' ){
onOff = 0;
zArg++;
}else if( zArg[0]=='+' ){
onOff = 1;
zArg++;
}
while( !isalpha(zArg[0]) ){
if( zArg[0]==0 ) break;
zArg++;
}
if( zArg[0]=='x' && isalpha(zArg[1]) ) zArg++;
for(n=0; isalpha(zArg[n]); n++){}
for(jj=0; jj<(int)(sizeof(aKw)/sizeof(aKw[0])); jj++){
if( sqlite3_strnicmp(aKw[jj].z,(const char*)zArg,n)==0 ){
if( onOff ){
pInfo->mTrace |= aKw[jj].m;
}else{
pInfo->mTrace &= ~aKw[jj].m;
}
break;
}
}
zArg += n;
}
}
}
sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break;
case SQLITE_FCNTL_MMAP_SIZE: {
sqlite3_int64 iMMap = *(sqlite3_int64*)pArg;
sqlite3_snprintf(sizeof(zBuf), zBuf, "MMAP_SIZE,%lld",iMMap);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_TRACE: zOp = "TRACE"; break;
case SQLITE_FCNTL_HAS_MOVED: zOp = "HAS_MOVED"; break;
case SQLITE_FCNTL_SYNC: zOp = "SYNC"; break;
case SQLITE_FCNTL_COMMIT_PHASETWO: zOp = "COMMIT_PHASETWO"; break;
case SQLITE_FCNTL_WIN32_SET_HANDLE: zOp = "WIN32_SET_HANDLE"; break;
case SQLITE_FCNTL_WAL_BLOCK: zOp = "WAL_BLOCK"; break;
case SQLITE_FCNTL_ZIPVFS: zOp = "ZIPVFS"; break;
case SQLITE_FCNTL_RBU: zOp = "RBU"; break;
case SQLITE_FCNTL_VFS_POINTER: zOp = "VFS_POINTER"; break;
case SQLITE_FCNTL_JOURNAL_POINTER: zOp = "JOURNAL_POINTER"; break;
case SQLITE_FCNTL_WIN32_GET_HANDLE: zOp = "WIN32_GET_HANDLE"; break;
case SQLITE_FCNTL_PDB: zOp = "PDB"; break;
case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: zOp = "BEGIN_ATOMIC_WRITE"; break;
case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: zOp = "COMMIT_ATOMIC_WRITE"; break;
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
zOp = "ROLLBACK_ATOMIC_WRITE";
break;
}
case SQLITE_FCNTL_LOCK_TIMEOUT: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "LOCK_TIMEOUT,%d", *(int*)pArg);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_DATA_VERSION: zOp = "DATA_VERSION"; break;
case SQLITE_FCNTL_SIZE_LIMIT: zOp = "SIZE_LIMIT"; break;
case SQLITE_FCNTL_CKPT_DONE: zOp = "CKPT_DONE"; break;
case SQLITE_FCNTL_RESERVE_BYTES: zOp = "RESERVED_BYTES"; break;
case SQLITE_FCNTL_CKPT_START: zOp = "CKPT_START"; break;
case SQLITE_FCNTL_EXTERNAL_READER: zOp = "EXTERNAL_READER"; break;
case SQLITE_FCNTL_CKSM_FILE: zOp = "CKSM_FILE"; break;
case SQLITE_FCNTL_RESET_CACHE: zOp = "RESET_CACHE"; break;
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
default: {
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
zOp = zBuf;
break;
}
}
vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)",
pInfo->zVfsName, p->zFName, zOp);
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
if( rc==SQLITE_OK ){
switch( op ){
case SQLITE_FCNTL_VFSNAME: {
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
pInfo->zVfsName, *(char**)pArg);
zRVal = *(char**)pArg;
break;
}
case SQLITE_FCNTL_MMAP_SIZE: {
sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%lld", *(sqlite3_int64*)pArg);
zRVal = zBuf2;
break;
}
case SQLITE_FCNTL_HAS_MOVED:
case SQLITE_FCNTL_PERSIST_WAL: {
sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%d", *(int*)pArg);
zRVal = zBuf2;
break;
}
case SQLITE_FCNTL_PRAGMA:
case SQLITE_FCNTL_TEMPFILENAME: {
zRVal = *(char**)pArg;
break;
}
}
}
if( zRVal ){
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", %s\n", zRVal);
}else{
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
}
return rc;
}
/*
** Return the sector-size in bytes for an vfstrace-file.
*/
static int vfstraceSectorSize(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_SECSZ);
vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xSectorSize(p->pReal);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the device characteristic flags supported by an vfstrace-file.
*/
static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_DEVCHAR);
vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
return rc;
}
/*
** Shared-memory operations.
*/
static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
static const char *azLockName[] = {
"WRITE",
"CKPT",
"RECOVER",
"READ0",
"READ1",
"READ2",
"READ3",
"READ4",
};
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zLck[100];
int i = 0;
vfstraceOnOff(pInfo, VTR_SHMLOCK);
memcpy(zLck, "|0", 3);
if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
if( flags & ~(0xf) ){
sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
}
if( ofst>=0 && ofst<(int)(sizeof(azLockName)/sizeof(azLockName[0])) ){
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)",
pInfo->zVfsName, p->zFName, ofst, azLockName[ofst],
n, &zLck[1]);
}else{
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)",
pInfo->zVfsName, p->zFName, ofst,
n, &zLck[1]);
}
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static int vfstraceShmMap(
sqlite3_file *pFile,
int iRegion,
int szRegion,
int isWrite,
void volatile **pp
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_SHMMAP);
vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)",
pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite);
rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static void vfstraceShmBarrier(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
vfstraceOnOff(pInfo, VTR_SHMBAR);
vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName);
p->pReal->pMethods->xShmBarrier(p->pReal);
}
static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_SHMUNMAP);
vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
pInfo->zVfsName, p->zFName, delFlag);
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Open an vfstrace file handle.
*/
static int vfstraceOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
int rc;
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
p->pInfo = pInfo;
p->zFName = zName ? fileTail(zName) : "<temp>";
p->pReal = (sqlite3_file *)&p[1];
rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
vfstraceOnOff(pInfo, VTR_OPEN);
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
pNew->xClose = vfstraceClose;
pNew->xRead = vfstraceRead;
pNew->xWrite = vfstraceWrite;
pNew->xTruncate = vfstraceTruncate;
pNew->xSync = vfstraceSync;
pNew->xFileSize = vfstraceFileSize;
pNew->xLock = vfstraceLock;
pNew->xUnlock = vfstraceUnlock;
pNew->xCheckReservedLock = vfstraceCheckReservedLock;
pNew->xFileControl = vfstraceFileControl;
pNew->xSectorSize = vfstraceSectorSize;
pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics;
if( pNew->iVersion>=2 ){
pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0;
pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0;
pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0;
pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0;
}
pFile->pMethods = pNew;
}
vfstrace_print_errcode(pInfo, " -> %s", rc);
if( pOutFlags ){
vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags);
}else{
vfstrace_printf(pInfo, "\n");
}
return rc;
}
/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_DELETE);
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
pInfo->zVfsName, zPath, dirSync);
rc = pRoot->xDelete(pRoot, zPath, dirSync);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int vfstraceAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_ACCESS);
vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
pInfo->zVfsName, zPath, flags);
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
/*
** Populate buffer zOut with the full canonical pathname corresponding
** to the pathname in zPath. zOut is guaranteed to point to a buffer
** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
*/
static int vfstraceFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_FULLPATH);
vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
pInfo->zVfsName, zPath);
rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
return rc;
}
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLOPEN);
vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
return pRoot->xDlOpen(pRoot, zPath);
}
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLERR);
vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
pRoot->xDlError(pRoot, nByte, zErrMsg);
vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
}
/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym);
return pRoot->xDlSym(pRoot, p, zSym);
}
/*
** Close the dynamic library handle pHandle.
*/
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLCLOSE);
vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
pRoot->xDlClose(pRoot, pHandle);
}
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_RAND);
vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
return pRoot->xRandomness(pRoot, nByte, zBufOut);
}
/*
** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_SLEEP);
vfstrace_printf(pInfo, "%s.xSleep(%d)\n", pInfo->zVfsName, nMicro);
return pRoot->xSleep(pRoot, nMicro);
}
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_CURTIME);
vfstrace_printf(pInfo, "%s.xCurrentTime()", pInfo->zVfsName);
rc = pRoot->xCurrentTime(pRoot, pTimeOut);
vfstrace_printf(pInfo, " -> %.17g\n", *pTimeOut);
return rc;
}
static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_CURTIME);
vfstrace_printf(pInfo, "%s.xCurrentTimeInt64()", pInfo->zVfsName);
rc = pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
vfstrace_printf(pInfo, " -> %lld\n", *pTimeOut);
return rc;
}
/*
** Return the most recent error code and message
*/
static int vfstraceGetLastError(sqlite3_vfs *pVfs, int nErr, char *zErr){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_LASTERR);
vfstrace_printf(pInfo, "%s.xGetLastError(%d,zBuf)", pInfo->zVfsName, nErr);
if( nErr ) zErr[0] = 0;
rc = pRoot->xGetLastError(pRoot, nErr, zErr);
vfstrace_printf(pInfo, " -> zBuf[] = \"%s\", rc = %d\n", nErr?zErr:"", rc);
return rc;
}
/*
** Override system calls.
*/
static int vfstraceSetSystemCall(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_syscall_ptr pFunc
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xSetSystemCall(pRoot, zName, pFunc);
}
static sqlite3_syscall_ptr vfstraceGetSystemCall(
sqlite3_vfs *pVfs,
const char *zName
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xGetSystemCall(pRoot, zName);
}
static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xNextSystemCall(pRoot, zName);
}
/*
** Clients invoke this routine to construct a new trace-vfs shim.
**
** Return SQLITE_OK on success.
**
** SQLITE_NOMEM is returned in the case of a memory allocation error.
** SQLITE_NOTFOUND is returned if zOldVfsName does not exist.
*/
int vfstrace_register(
const char *zTraceName, /* Name of the newly constructed VFS */
const char *zOldVfsName, /* Name of the underlying VFS */
int (*xOut)(const char*,void*), /* Output routine. ex: fputs */
void *pOutArg, /* 2nd argument to xOut. ex: stderr */
int makeDefault /* True to make the new VFS the default */
){
sqlite3_vfs *pNew;
sqlite3_vfs *pRoot;
vfstrace_info *pInfo;
size_t nName;
size_t nByte;
pRoot = sqlite3_vfs_find(zOldVfsName);
if( pRoot==0 ) return SQLITE_NOTFOUND;
nName = strlen(zTraceName);
nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1;
pNew = sqlite3_malloc64( nByte );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, nByte);
pInfo = (vfstrace_info*)&pNew[1];
pNew->iVersion = pRoot->iVersion;
pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file);
pNew->mxPathname = pRoot->mxPathname;
pNew->zName = (char*)&pInfo[1];
memcpy((char*)&pInfo[1], zTraceName, nName+1);
pNew->pAppData = pInfo;
pNew->xOpen = vfstraceOpen;
pNew->xDelete = vfstraceDelete;
pNew->xAccess = vfstraceAccess;
pNew->xFullPathname = vfstraceFullPathname;
pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen;
pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError;
pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym;
pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose;
pNew->xRandomness = vfstraceRandomness;
pNew->xSleep = vfstraceSleep;
pNew->xCurrentTime = vfstraceCurrentTime;
pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError;
if( pNew->iVersion>=2 ){
pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 :
vfstraceCurrentTimeInt64;
if( pNew->iVersion>=3 ){
pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 :
vfstraceSetSystemCall;
pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 :
vfstraceGetSystemCall;
pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 :
vfstraceNextSystemCall;
}
}
pInfo->pRootVfs = pRoot;
pInfo->xOut = xOut;
pInfo->pOutArg = pOutArg;
pInfo->zVfsName = pNew->zName;
pInfo->pTraceVfs = pNew;
pInfo->mTrace = 0xffffffff;
pInfo->bOn = 1;
vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
pInfo->zVfsName, pRoot->zName);
return sqlite3_vfs_register(pNew, makeDefault);
}
/*
** Look for the named VFS. If it is a TRACEVFS, then unregister it
** and delete it.
*/
void vfstrace_unregister(const char *zTraceName){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zTraceName);
if( pVfs==0 ) return;
if( pVfs->xOpen!=vfstraceOpen ) return;
sqlite3_vfs_unregister(pVfs);
sqlite3_free(pVfs);
}
/************************* End ../ext/misc/vfstrace.c ********************/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
#else
#define SQLITE_SHELL_HAVE_RECOVER 0
#endif
#if SQLITE_SHELL_HAVE_RECOVER
|
| ︙ | ︙ | |||
18239 18240 18241 18242 18243 18244 18245 |
do{
sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++);
}while( strstr(z,zBuf)!=0 );
return zBuf;
}
/*
| | | | 19368 19369 19370 19371 19372 19373 19374 19375 19376 19377 19378 19379 19380 19381 19382 19383 19384 19385 19386 19387 19388 19389 19390 19391 19392 19393 |
do{
sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++);
}while( strstr(z,zBuf)!=0 );
return zBuf;
}
/*
** Implementation of scalar SQL function "escape_crlf". The argument passed to
** this function is the output of built-in function quote(). If the first
** character of the input is "'", indicating that the value passed to quote()
** was a text value, then this function searches the input for "\n" and "\r"
** characters and adds a wrapper similar to the following:
**
** replace(replace(<input>, '\n', char(10), '\r', char(13));
**
** Or, if the first character of the input is not "'", then a copy of the input
** is returned.
*/
static void recoverEscapeCrlf(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zText = (const char*)sqlite3_value_text(argv[0]);
(void)argc;
if( zText && zText[0]=='\'' ){
|
| ︙ | ︙ | |||
18465 18466 18467 18468 18469 18470 18471 |
const char *zName;
int nArg;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
} aFunc[] = {
{ "getpage", 1, recoverGetPage },
{ "page_is_used", 1, recoverPageIsUsed },
{ "read_i32", 2, recoverReadI32 },
| | | 19594 19595 19596 19597 19598 19599 19600 19601 19602 19603 19604 19605 19606 19607 19608 |
const char *zName;
int nArg;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
} aFunc[] = {
{ "getpage", 1, recoverGetPage },
{ "page_is_used", 1, recoverPageIsUsed },
{ "read_i32", 2, recoverReadI32 },
{ "escape_crlf", 1, recoverEscapeCrlf },
};
const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
sqlite3 *db = 0; /* New database handle */
int ii; /* For iterating through aFunc[] */
assert( p->dbOut==0 );
|
| ︙ | ︙ | |||
18818 18819 18820 18821 18822 18823 18824 |
&& eHidden!=RECOVER_EHIDDEN_STORED
){
assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 );
zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol);
if( bSql ){
zBind = recoverMPrintf(p,
| | | 19947 19948 19949 19950 19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 |
&& eHidden!=RECOVER_EHIDDEN_STORED
){
assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 );
zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol);
if( bSql ){
zBind = recoverMPrintf(p,
"%z%sescape_crlf(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind
);
zSqlSep = "||', '||";
}else{
zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind);
}
zSep = ", ";
}
|
| ︙ | ︙ | |||
19320 19321 19322 19323 19324 19325 19326 19327 19328 19329 19330 19331 19332 19333 |
}else if( iField<pTab->nCol ){
assert( apVal[iField]==0 );
apVal[iField] = sqlite3_value_dup( pVal );
if( apVal[iField]==0 ){
recoverError(p, SQLITE_NOMEM, 0);
}
p1->nVal = iField+1;
}
p1->iPrevCell = iCell;
p1->iPrevPage = iPage;
}
}else{
recoverReset(p, pSel);
p1->pTab = 0;
| > > | 20449 20450 20451 20452 20453 20454 20455 20456 20457 20458 20459 20460 20461 20462 20463 20464 |
}else if( iField<pTab->nCol ){
assert( apVal[iField]==0 );
apVal[iField] = sqlite3_value_dup( pVal );
if( apVal[iField]==0 ){
recoverError(p, SQLITE_NOMEM, 0);
}
p1->nVal = iField+1;
}else if( pTab->nCol==0 ){
p1->nVal = pTab->nCol;
}
p1->iPrevCell = iCell;
p1->iPrevPage = iPage;
}
}else{
recoverReset(p, pSel);
p1->pTab = 0;
|
| ︙ | ︙ | |||
20435 20436 20437 20438 20439 20440 20441 20442 20443 20444 20445 20446 20447 20448 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ | > | 21566 21567 21568 21569 21570 21571 21572 21573 21574 21575 21576 21577 21578 21579 21580 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ |
| ︙ | ︙ | |||
20580 20581 20582 20583 20584 20585 20586 20587 20588 20589 20590 20591 20592 20593 20594 20595 20596 20597 20598 20599 20600 20601 20602 20603 20604 20605 20606 |
#define MODE_Json 13 /* Output JSON */
#define MODE_Markdown 14 /* Markdown formatting */
#define MODE_Table 15 /* MySQL-style table formatting */
#define MODE_Box 16 /* Unicode box-drawing characters */
#define MODE_Count 17 /* Output only a count of the rows of output */
#define MODE_Off 18 /* No query output shown */
#define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */
static const char *modeDescr[] = {
"line",
"column",
"list",
"semi",
"html",
"insert",
"quote",
"tcl",
"csv",
"explain",
"ascii",
"prettyprint",
"eqp",
"json",
"markdown",
"table",
"box",
"count",
| > | > > | 21712 21713 21714 21715 21716 21717 21718 21719 21720 21721 21722 21723 21724 21725 21726 21727 21728 21729 21730 21731 21732 21733 21734 21735 21736 21737 21738 21739 21740 21741 21742 21743 21744 21745 21746 21747 21748 21749 |
#define MODE_Json 13 /* Output JSON */
#define MODE_Markdown 14 /* Markdown formatting */
#define MODE_Table 15 /* MySQL-style table formatting */
#define MODE_Box 16 /* Unicode box-drawing characters */
#define MODE_Count 17 /* Output only a count of the rows of output */
#define MODE_Off 18 /* No query output shown */
#define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */
#define MODE_Www 20 /* Full web-page output */
static const char *modeDescr[] = {
"line",
"column",
"list",
"semi",
"html",
"insert",
"quote",
"tcl",
"csv",
"explain",
"ascii",
"prettyprint",
"eqp",
"json",
"markdown",
"table",
"box",
"count",
"off",
"scanexp",
"www",
};
/*
** These are the column/row/line separators used by the various
** import/export modes.
*/
#define SEP_Column "|"
|
| ︙ | ︙ | |||
20628 20629 20630 20631 20632 20633 20634 |
/*
** A callback for the sqlite3_log() interface.
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
ShellState *p = (ShellState*)pArg;
if( p->pLog==0 ) return;
| | | | | | 21763 21764 21765 21766 21767 21768 21769 21770 21771 21772 21773 21774 21775 21776 21777 21778 21779 21780 21781 21782 21783 21784 21785 21786 21787 21788 21789 21790 21791 21792 21793 21794 21795 21796 21797 21798 21799 21800 21801 21802 21803 21804 21805 21806 21807 21808 21809 21810 21811 21812 21813 |
/*
** A callback for the sqlite3_log() interface.
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
ShellState *p = (ShellState*)pArg;
if( p->pLog==0 ) return;
sqlite3_fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
fflush(p->pLog);
}
/*
** SQL function: shell_putsnl(X)
**
** Write the text X to the screen (or whatever output is being directed)
** adding a newline at the end, and then return X.
*/
static void shellPutsFunc(
sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
(void)nVal;
sqlite3_fprintf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
sqlite3_result_value(pCtx, apVal[0]);
}
/*
** If in safe mode, print an error message described by the arguments
** and exit immediately.
*/
static void failIfSafeMode(
ShellState *p,
const char *zErrMsg,
...
){
if( p->bSafeMode ){
va_list ap;
char *zMsg;
va_start(ap, zErrMsg);
zMsg = sqlite3_vmprintf(zErrMsg, ap);
va_end(ap);
sqlite3_fprintf(stderr, "line %d: %s\n", p->lineno, zMsg);
exit(1);
}
}
/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
|
| ︙ | ︙ | |||
20697 20698 20699 20700 20701 20702 20703 |
){
const char *zEditor;
char *zTempFile = 0;
sqlite3 *db;
char *zCmd = 0;
int bBin;
int rc;
| | | 21832 21833 21834 21835 21836 21837 21838 21839 21840 21841 21842 21843 21844 21845 21846 |
){
const char *zEditor;
char *zTempFile = 0;
sqlite3 *db;
char *zCmd = 0;
int bBin;
int rc;
int hasCRLF = 0;
FILE *f = 0;
sqlite3_int64 sz;
sqlite3_int64 x;
unsigned char *p = 0;
if( argc==2 ){
zEditor = (const char*)sqlite3_value_text(argv[1]);
|
| ︙ | ︙ | |||
20731 20732 20733 20734 20735 20736 20737 |
sqlite3_result_error_nomem(context);
return;
}
}
bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB;
/* When writing the file to be edited, do \n to \r\n conversions on systems
** that want \r\n line endings */
| | | | | 21866 21867 21868 21869 21870 21871 21872 21873 21874 21875 21876 21877 21878 21879 21880 21881 21882 21883 21884 21885 21886 21887 21888 21889 21890 21891 21892 21893 21894 21895 21896 21897 21898 21899 21900 21901 21902 21903 21904 21905 21906 21907 21908 21909 21910 21911 |
sqlite3_result_error_nomem(context);
return;
}
}
bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB;
/* When writing the file to be edited, do \n to \r\n conversions on systems
** that want \r\n line endings */
f = sqlite3_fopen(zTempFile, bBin ? "wb" : "w");
if( f==0 ){
sqlite3_result_error(context, "edit() cannot open temp file", -1);
goto edit_func_end;
}
sz = sqlite3_value_bytes(argv[0]);
if( bBin ){
x = fwrite(sqlite3_value_blob(argv[0]), 1, (size_t)sz, f);
}else{
const char *z = (const char*)sqlite3_value_text(argv[0]);
/* Remember whether or not the value originally contained \r\n */
if( z && strstr(z,"\r\n")!=0 ) hasCRLF = 1;
x = fwrite(sqlite3_value_text(argv[0]), 1, (size_t)sz, f);
}
fclose(f);
f = 0;
if( x!=sz ){
sqlite3_result_error(context, "edit() could not write the whole file", -1);
goto edit_func_end;
}
zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile);
if( zCmd==0 ){
sqlite3_result_error_nomem(context);
goto edit_func_end;
}
rc = system(zCmd);
sqlite3_free(zCmd);
if( rc ){
sqlite3_result_error(context, "EDITOR returned non-zero", -1);
goto edit_func_end;
}
f = sqlite3_fopen(zTempFile, "rb");
if( f==0 ){
sqlite3_result_error(context,
"edit() cannot reopen temp file after edit", -1);
goto edit_func_end;
}
fseek(f, 0, SEEK_END);
sz = ftell(f);
|
| ︙ | ︙ | |||
20787 20788 20789 20790 20791 20792 20793 |
sqlite3_result_error(context, "could not read back the whole file", -1);
goto edit_func_end;
}
if( bBin ){
sqlite3_result_blob64(context, p, sz, sqlite3_free);
}else{
sqlite3_int64 i, j;
| | | 21922 21923 21924 21925 21926 21927 21928 21929 21930 21931 21932 21933 21934 21935 21936 |
sqlite3_result_error(context, "could not read back the whole file", -1);
goto edit_func_end;
}
if( bBin ){
sqlite3_result_blob64(context, p, sz, sqlite3_free);
}else{
sqlite3_int64 i, j;
if( hasCRLF ){
/* If the original contains \r\n then do no conversions back to \n */
}else{
/* If the file did not originally contain \r\n then convert any new
** \r\n back into \n */
p[sz] = 0;
for(i=j=0; i<sz; i++){
if( p[i]=='\r' && p[i+1]=='\n' ) i++;
|
| ︙ | ︙ | |||
20828 20829 20830 20831 20832 20833 20834 20835 20836 20837 20838 |
}
static void outputModePop(ShellState *p){
p->mode = p->modePrior;
p->shellFlgs = p->priorShFlgs;
memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
}
/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
| > > > > > > > > > > > > > > > | | | 21963 21964 21965 21966 21967 21968 21969 21970 21971 21972 21973 21974 21975 21976 21977 21978 21979 21980 21981 21982 21983 21984 21985 21986 21987 21988 21989 21990 21991 21992 21993 21994 21995 21996 21997 21998 21999 22000 22001 22002 22003 22004 22005 22006 22007 22008 22009 22010 22011 22012 22013 |
}
static void outputModePop(ShellState *p){
p->mode = p->modePrior;
p->shellFlgs = p->priorShFlgs;
memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
}
/*
** Set output mode to text or binary for Windows.
*/
static void setCrlfMode(ShellState *p){
#ifdef _WIN32
if( p->crlfMode ){
sqlite3_fsetmode(p->out, _O_TEXT);
}else{
sqlite3_fsetmode(p->out, _O_BINARY);
}
#else
UNUSED_PARAMETER(p);
#endif
}
/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i;
unsigned char *aBlob = (unsigned char*)pBlob;
char *zStr = sqlite3_malloc(nBlob*2 + 1);
shell_check_oom(zStr);
for(i=0; i<nBlob; i++){
static const char aHex[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
zStr[i*2] = aHex[ (aBlob[i] >> 4) ];
zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ];
}
zStr[i*2] = '\0';
sqlite3_fprintf(out, "X'%s'", zStr);
sqlite3_free(zStr);
}
/*
** Find a string that is not found anywhere in z[]. Return a pointer
** to that string.
**
|
| ︙ | ︙ | |||
20879 20880 20881 20882 20883 20884 20885 | } /* ** Output the given string as a quoted string using SQL quoting conventions. ** ** See also: output_quoted_escaped_string() */ | | < | | < | | | | | < | < < < | < | | < | | | | | | | | | | | < | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | | | | | | | | | | | | | | | | | | | | | | | 22029 22030 22031 22032 22033 22034 22035 22036 22037 22038 22039 22040 22041 22042 22043 22044 22045 22046 22047 22048 22049 22050 22051 22052 22053 22054 22055 22056 22057 22058 22059 22060 22061 22062 22063 22064 22065 22066 22067 22068 22069 22070 22071 22072 22073 22074 22075 22076 22077 22078 22079 22080 22081 22082 22083 22084 22085 22086 22087 22088 22089 22090 22091 22092 22093 22094 22095 22096 22097 22098 22099 22100 22101 22102 22103 22104 22105 22106 22107 22108 22109 22110 22111 22112 22113 22114 22115 22116 22117 22118 22119 22120 22121 22122 22123 22124 22125 22126 22127 22128 22129 22130 22131 22132 22133 22134 22135 22136 22137 22138 22139 22140 22141 22142 22143 22144 22145 22146 22147 22148 22149 22150 22151 22152 22153 22154 22155 22156 22157 22158 22159 22160 22161 22162 22163 22164 22165 22166 22167 22168 22169 22170 22171 22172 22173 22174 22175 22176 22177 22178 22179 22180 22181 22182 22183 22184 22185 22186 22187 22188 22189 22190 22191 22192 22193 22194 22195 22196 22197 22198 22199 22200 22201 22202 22203 22204 22205 22206 22207 22208 22209 22210 22211 22212 22213 22214 22215 22216 22217 22218 22219 22220 22221 22222 22223 22224 22225 22226 22227 22228 22229 22230 22231 22232 22233 22234 22235 22236 22237 22238 22239 22240 22241 22242 22243 22244 22245 22246 22247 22248 22249 22250 22251 22252 22253 22254 22255 22256 22257 22258 22259 22260 22261 22262 22263 22264 22265 22266 22267 22268 22269 22270 22271 22272 22273 22274 22275 22276 22277 22278 22279 22280 22281 22282 22283 22284 22285 22286 22287 22288 22289 22290 22291 22292 22293 22294 22295 22296 22297 22298 22299 22300 22301 22302 22303 22304 22305 22306 22307 22308 22309 22310 22311 22312 22313 22314 22315 22316 |
}
/*
** Output the given string as a quoted string using SQL quoting conventions.
**
** See also: output_quoted_escaped_string()
*/
static void output_quoted_string(ShellState *p, const char *z){
int i;
char c;
FILE *out = p->out;
sqlite3_fsetmode(out, _O_BINARY);
if( z==0 ) return;
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c==0 ){
sqlite3_fprintf(out, "'%s'",z);
}else{
sqlite3_fputs("'", out);
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c=='\'' ) i++;
if( i ){
sqlite3_fprintf(out, "%.*s", i, z);
z += i;
}
if( c=='\'' ){
sqlite3_fputs("'", out);
continue;
}
if( c==0 ){
break;
}
z++;
}
sqlite3_fputs("'", out);
}
setCrlfMode(p);
}
/*
** Output the given string as a quoted string using SQL quoting conventions.
** Additionallly , escape the "\n" and "\r" characters so that they do not
** get corrupted by end-of-line translation facilities in some operating
** systems.
**
** This is like output_quoted_string() but with the addition of the \r\n
** escape mechanism.
*/
static void output_quoted_escaped_string(ShellState *p, const char *z){
int i;
char c;
FILE *out = p->out;
sqlite3_fsetmode(out, _O_BINARY);
for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
if( c==0 ){
sqlite3_fprintf(out, "'%s'",z);
}else{
const char *zNL = 0;
const char *zCR = 0;
int nNL = 0;
int nCR = 0;
char zBuf1[20], zBuf2[20];
for(i=0; z[i]; i++){
if( z[i]=='\n' ) nNL++;
if( z[i]=='\r' ) nCR++;
}
if( nNL ){
sqlite3_fputs("replace(", out);
zNL = unused_string(z, "\\n", "\\012", zBuf1);
}
if( nCR ){
sqlite3_fputs("replace(", out);
zCR = unused_string(z, "\\r", "\\015", zBuf2);
}
sqlite3_fputs("'", out);
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){}
if( c=='\'' ) i++;
if( i ){
sqlite3_fprintf(out, "%.*s", i, z);
z += i;
}
if( c=='\'' ){
sqlite3_fputs("'", out);
continue;
}
if( c==0 ){
break;
}
z++;
if( c=='\n' ){
sqlite3_fputs(zNL, out);
continue;
}
sqlite3_fputs(zCR, out);
}
sqlite3_fputs("'", out);
if( nCR ){
sqlite3_fprintf(out, ",'%s',char(13))", zCR);
}
if( nNL ){
sqlite3_fprintf(out, ",'%s',char(10))", zNL);
}
}
setCrlfMode(p);
}
/*
** Find earliest of chars within s specified in zAny.
** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated.
*/
static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){
const char *pcFirst = 0;
if( ns == ~(size_t)0 ) ns = strlen(s);
while(*zAny){
const char *pc = (const char*)memchr(s, *zAny&0xff, ns);
if( pc ){
pcFirst = pc;
ns = pcFirst - s;
}
++zAny;
}
return pcFirst;
}
/* Skip over as much z[] input char sequence as is valid UTF-8,
** limited per nAccept char's or whole characters and containing
** no char cn such that ((1<<cn) & ccm)!=0. On return, the
** sequence z:return (inclusive:exclusive) is validated UTF-8.
** Limit: nAccept>=0 => char count, nAccept<0 => character
*/
const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){
int ng = (nAccept<0)? -nAccept : 0;
const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
assert(z!=0);
while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){
unsigned char c = *(u8*)z;
if( c<0x7f ){
if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z;
++z; /* ASCII */
}else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */
else{
const char *zt = z+1; /* Got lead byte, look at trail bytes.*/
do{
if( pcLimit && zt >= pcLimit ) return z;
else{
char ct = *zt++;
if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){
/* Trailing bytes are too few, too many, or invalid. */
return z;
}
}
} while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */
z = zt;
}
}
return z;
}
/*
** Output the given string as a quoted according to C or TCL quoting rules.
*/
static void output_c_string(FILE *out, const char *z){
char c;
static const char *zq = "\"";
static long ctrlMask = ~0L;
static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */
char ace[3] = "\\?";
char cbsSay;
sqlite3_fputs(zq, out);
while( *z!=0 ){
const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0);
const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask);
const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast;
if( pcEnd > z ){
sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z);
}
if( (c = *pcEnd)==0 ) break;
++pcEnd;
switch( c ){
case '\\': case '"':
cbsSay = (char)c;
break;
case '\t': cbsSay = 't'; break;
case '\n': cbsSay = 'n'; break;
case '\r': cbsSay = 'r'; break;
case '\f': cbsSay = 'f'; break;
default: cbsSay = 0; break;
}
if( cbsSay ){
ace[1] = cbsSay;
sqlite3_fputs(ace, out);
}else if( !isprint(c&0xff) ){
sqlite3_fprintf(out, "\\%03o", c&0xff);
}else{
ace[1] = (char)c;
sqlite3_fputs(ace+1, out);
}
z = pcEnd;
}
sqlite3_fputs(zq, out);
}
/*
** Output the given string as quoted according to JSON quoting rules.
*/
static void output_json_string(FILE *out, const char *z, i64 n){
unsigned char c;
static const char *zq = "\"";
static long ctrlMask = ~0L;
static const char *zDQBS = "\"\\";
const char *pcLimit;
char ace[3] = "\\?";
char cbsSay;
if( z==0 ) z = "";
pcLimit = z + ((n<0)? strlen(z) : (size_t)n);
sqlite3_fputs(zq, out);
while( z < pcLimit ){
const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z);
const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask);
const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast;
if( pcEnd > z ){
sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z);
z = pcEnd;
}
if( z >= pcLimit ) break;
c = (unsigned char)*(z++);
switch( c ){
case '"': case '\\':
cbsSay = (char)c;
break;
case '\b': cbsSay = 'b'; break;
case '\f': cbsSay = 'f'; break;
case '\n': cbsSay = 'n'; break;
case '\r': cbsSay = 'r'; break;
case '\t': cbsSay = 't'; break;
default: cbsSay = 0; break;
}
if( cbsSay ){
ace[1] = cbsSay;
sqlite3_fputs(ace, out);
}else if( c<=0x1f || c>=0x7f ){
sqlite3_fprintf(out, "\\u%04x", c);
}else{
ace[1] = (char)c;
sqlite3_fputs(ace+1, out);
}
}
sqlite3_fputs(zq, out);
}
/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
int i;
if( z==0 ) z = "";
while( *z ){
for(i=0; z[i]
&& z[i]!='<'
&& z[i]!='&'
&& z[i]!='>'
&& z[i]!='\"'
&& z[i]!='\'';
i++){}
if( i>0 ){
sqlite3_fprintf(out, "%.*s",i,z);
}
if( z[i]=='<' ){
sqlite3_fputs("<", out);
}else if( z[i]=='&' ){
sqlite3_fputs("&", out);
}else if( z[i]=='>' ){
sqlite3_fputs(">", out);
}else if( z[i]=='\"' ){
sqlite3_fputs(""", out);
}else if( z[i]=='\'' ){
sqlite3_fputs("'", out);
}else{
break;
}
z += i + 1;
}
}
|
| ︙ | ︙ | |||
21165 21166 21167 21168 21169 21170 21171 |
** Output a single term of CSV. Actually, p->colSeparator is used for
** the separator, which may or may not be a comma. p->nullValue is
** the null value. Strings are quoted if necessary. The separator
** is only issued if bSep is true.
*/
static void output_csv(ShellState *p, const char *z, int bSep){
if( z==0 ){
| | | | | | 22341 22342 22343 22344 22345 22346 22347 22348 22349 22350 22351 22352 22353 22354 22355 22356 22357 22358 22359 22360 22361 22362 22363 22364 22365 22366 22367 22368 22369 22370 22371 22372 22373 22374 |
** Output a single term of CSV. Actually, p->colSeparator is used for
** the separator, which may or may not be a comma. p->nullValue is
** the null value. Strings are quoted if necessary. The separator
** is only issued if bSep is true.
*/
static void output_csv(ShellState *p, const char *z, int bSep){
if( z==0 ){
sqlite3_fprintf(p->out, "%s",p->nullValue);
}else{
unsigned i;
for(i=0; z[i]; i++){
if( needCsvQuote[((unsigned char*)z)[i]] ){
i = 0;
break;
}
}
if( i==0 || strstr(z, p->colSeparator)!=0 ){
char *zQuoted = sqlite3_mprintf("\"%w\"", z);
shell_check_oom(zQuoted);
sqlite3_fputs(zQuoted, p->out);
sqlite3_free(zQuoted);
}else{
sqlite3_fputs(z, p->out);
}
}
if( bSep ){
sqlite3_fputs(p->colSeparator, p->out);
}
}
/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){
|
| ︙ | ︙ | |||
21292 21293 21294 21295 21296 21297 21298 | }; int i; const char *az[4]; az[0] = zA1; az[1] = zA2; az[2] = zA3; az[3] = zA4; | | | | | | | | 22468 22469 22470 22471 22472 22473 22474 22475 22476 22477 22478 22479 22480 22481 22482 22483 22484 22485 22486 22487 22488 22489 22490 22491 22492 22493 22494 22495 22496 22497 22498 22499 22500 22501 22502 22503 22504 22505 22506 22507 |
};
int i;
const char *az[4];
az[0] = zA1;
az[1] = zA2;
az[2] = zA3;
az[3] = zA4;
sqlite3_fprintf(p->out, "authorizer: %s", azAction[op]);
for(i=0; i<4; i++){
sqlite3_fputs(" ", p->out);
if( az[i] ){
output_c_string(p->out, az[i]);
}else{
sqlite3_fputs("NULL", p->out);
}
}
sqlite3_fputs("\n", p->out);
if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
#endif
/*
** Print a schema statement. Part of MODE_Semi and MODE_Pretty output.
**
** This routine converts some CREATE TABLE statements for shadow tables
** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements.
**
** If the schema statement in z[] contains a start-of-comment and if
** sqlite3_complete() returns false, try to terminate the comment before
** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c
*/
static void printSchemaLine(FILE *out, const char *z, const char *zTail){
char *zToFree = 0;
if( z==0 ) return;
if( zTail==0 ) return;
if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){
const char *zOrig = z;
static const char *azTerm[] = { "", "*/", "\n" };
int i;
|
| ︙ | ︙ | |||
21339 21340 21341 21342 21343 21344 21345 |
z = zNew;
break;
}
sqlite3_free(zNew);
}
}
if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){
| | | | | | 22515 22516 22517 22518 22519 22520 22521 22522 22523 22524 22525 22526 22527 22528 22529 22530 22531 22532 22533 22534 22535 22536 22537 22538 |
z = zNew;
break;
}
sqlite3_free(zNew);
}
}
if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){
sqlite3_fprintf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
}else{
sqlite3_fprintf(out, "%s%s", z, zTail);
}
sqlite3_free(zToFree);
}
static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){
char c = z[n];
z[n] = 0;
printSchemaLine(out, z, zTail);
z[n] = c;
}
/*
** Return true if string z[] has nothing but whitespace and comments to the
** end of the first line.
*/
|
| ︙ | ︙ | |||
21376 21377 21378 21379 21380 21381 21382 |
*/
static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
EQPGraphRow *pNew;
i64 nText;
if( zText==0 ) return;
nText = strlen(zText);
if( p->autoEQPtest ){
| | | 22552 22553 22554 22555 22556 22557 22558 22559 22560 22561 22562 22563 22564 22565 22566 |
*/
static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
EQPGraphRow *pNew;
i64 nText;
if( zText==0 ) return;
nText = strlen(zText);
if( p->autoEQPtest ){
sqlite3_fprintf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
}
pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
shell_check_oom(pNew);
pNew->iEqpId = iEqpId;
pNew->iParentId = p2;
memcpy(pNew->zText, zText, nText+1);
pNew->pNext = 0;
|
| ︙ | ︙ | |||
21424 21425 21426 21427 21428 21429 21430 |
static void eqp_render_level(ShellState *p, int iEqpId){
EQPGraphRow *pRow, *pNext;
i64 n = strlen(p->sGraph.zPrefix);
char *z;
for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
pNext = eqp_next_row(p, iEqpId, pRow);
z = pRow->zText;
| > | | | | | | | | | | | | | | | | 22600 22601 22602 22603 22604 22605 22606 22607 22608 22609 22610 22611 22612 22613 22614 22615 22616 22617 22618 22619 22620 22621 22622 22623 22624 22625 22626 22627 22628 22629 22630 22631 22632 22633 22634 22635 22636 22637 22638 22639 22640 22641 22642 22643 22644 22645 22646 22647 22648 22649 22650 22651 22652 22653 22654 22655 22656 22657 22658 22659 22660 22661 22662 22663 22664 22665 22666 22667 22668 22669 22670 22671 22672 22673 22674 22675 22676 22677 22678 22679 22680 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 22693 22694 22695 22696 22697 22698 22699 22700 |
static void eqp_render_level(ShellState *p, int iEqpId){
EQPGraphRow *pRow, *pNext;
i64 n = strlen(p->sGraph.zPrefix);
char *z;
for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
pNext = eqp_next_row(p, iEqpId, pRow);
z = pRow->zText;
sqlite3_fprintf(p->out, "%s%s%s\n", p->sGraph.zPrefix,
pNext ? "|--" : "`--", z);
if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){
memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
eqp_render_level(p, pRow->iEqpId);
p->sGraph.zPrefix[n] = 0;
}
}
}
/*
** Display and reset the EXPLAIN QUERY PLAN data
*/
static void eqp_render(ShellState *p, i64 nCycle){
EQPGraphRow *pRow = p->sGraph.pRow;
if( pRow ){
if( pRow->zText[0]=='-' ){
if( pRow->pNext==0 ){
eqp_reset(p);
return;
}
sqlite3_fprintf(p->out, "%s\n", pRow->zText+3);
p->sGraph.pRow = pRow->pNext;
sqlite3_free(pRow);
}else if( nCycle>0 ){
sqlite3_fprintf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle);
}else{
sqlite3_fputs("QUERY PLAN\n", p->out);
}
p->sGraph.zPrefix[0] = 0;
eqp_render_level(p, 0);
eqp_reset(p);
}
}
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/*
** Progress handler callback.
*/
static int progress_handler(void *pClientData) {
ShellState *p = (ShellState*)pClientData;
p->nProgress++;
if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){
sqlite3_fprintf(p->out, "Progress limit reached (%u)\n", p->nProgress);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0;
return 1;
}
if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
sqlite3_fprintf(p->out, "Progress %u\n", p->nProgress);
}
return 0;
}
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
/*
** Print N dashes
*/
static void print_dashes(FILE *out, int N){
const char zDash[] = "--------------------------------------------------";
const int nDash = sizeof(zDash) - 1;
while( N>nDash ){
sqlite3_fputs(zDash, out);
N -= nDash;
}
sqlite3_fprintf(out, "%.*s", N, zDash);
}
/*
** Print a markdown or table-style row separator using ascii-art
*/
static void print_row_separator(
ShellState *p,
int nArg,
const char *zSep
){
int i;
if( nArg>0 ){
sqlite3_fputs(zSep, p->out);
print_dashes(p->out, p->actualWidth[0]+2);
for(i=1; i<nArg; i++){
sqlite3_fputs(zSep, p->out);
print_dashes(p->out, p->actualWidth[i]+2);
}
sqlite3_fputs(zSep, p->out);
}
sqlite3_fputs("\n", p->out);
}
/*
** This is the callback routine that the shell
** invokes for each row of a query result.
*/
static int shell_callback(
|
| ︙ | ︙ | |||
21539 21540 21541 21542 21543 21544 21545 |
case MODE_Line: {
int w = 5;
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
| | | | 22716 22717 22718 22719 22720 22721 22722 22723 22724 22725 22726 22727 22728 22729 22730 22731 22732 |
case MODE_Line: {
int w = 5;
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
for(i=0; i<nArg; i++){
sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i],
azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);
}
break;
}
case MODE_ScanExp:
case MODE_Explain: {
static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13};
|
| ︙ | ︙ | |||
21569 21570 21571 21572 21573 21574 21575 |
iIndent = 3;
}
if( nArg>nWidth ) nArg = nWidth;
/* If this is the first row seen, print out the headers */
if( p->cnt++==0 ){
for(i=0; i<nArg; i++){
| | | | | | | | | | | 22746 22747 22748 22749 22750 22751 22752 22753 22754 22755 22756 22757 22758 22759 22760 22761 22762 22763 22764 22765 22766 22767 22768 22769 22770 22771 22772 22773 22774 22775 22776 22777 22778 22779 22780 22781 22782 22783 22784 22785 22786 22787 22788 22789 22790 22791 22792 22793 22794 22795 22796 22797 22798 22799 22800 22801 22802 22803 22804 22805 22806 22807 22808 |
iIndent = 3;
}
if( nArg>nWidth ) nArg = nWidth;
/* If this is the first row seen, print out the headers */
if( p->cnt++==0 ){
for(i=0; i<nArg; i++){
utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]);
sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out);
}
for(i=0; i<nArg; i++){
print_dashes(p->out, aWidth[i]);
sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out);
}
}
/* If there is no data, exit early. */
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
const char *zSep = " ";
int w = aWidth[i];
const char *zVal = azArg[ aMap[i] ];
if( i==nArg-1 ) w = 0;
if( zVal && strlenChar(zVal)>w ){
w = strlenChar(zVal);
zSep = " ";
}
if( i==iIndent && p->aiIndent && p->pStmt ){
if( p->iIndent<p->nIndent ){
sqlite3_fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
}
p->iIndent++;
}
utf8_width_print(p->out, w, zVal ? zVal : p->nullValue);
sqlite3_fputs(i==nArg-1 ? "\n" : zSep, p->out);
}
break;
}
case MODE_Semi: { /* .schema and .fullschema output */
printSchemaLine(p->out, azArg[0], ";\n");
break;
}
case MODE_Pretty: { /* .schema and .fullschema with --indent */
char *z;
int j;
int nParen = 0;
char cEnd = 0;
char c;
int nLine = 0;
assert( nArg==1 );
if( azArg[0]==0 ) break;
if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
|| sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
){
sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
break;
}
z = sqlite3_mprintf("%s", azArg[0]);
shell_check_oom(z);
j = 0;
for(i=0; IsSpace(z[i]); i++){}
for(; (c = z[i])!=0; i++){
|
| ︙ | ︙ | |||
21650 21651 21652 21653 21654 21655 21656 |
}else if( c=='-' && z[i+1]=='-' ){
cEnd = '\n';
}else if( c=='(' ){
nParen++;
}else if( c==')' ){
nParen--;
if( nLine>0 && nParen==0 && j>0 ){
| | | | > | | | > | > > > > > > > | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 22827 22828 22829 22830 22831 22832 22833 22834 22835 22836 22837 22838 22839 22840 22841 22842 22843 22844 22845 22846 22847 22848 22849 22850 22851 22852 22853 22854 22855 22856 22857 22858 22859 22860 22861 22862 22863 22864 22865 22866 22867 22868 22869 22870 22871 22872 22873 22874 22875 22876 22877 22878 22879 22880 22881 22882 22883 22884 22885 22886 22887 22888 22889 22890 22891 22892 22893 22894 22895 22896 22897 22898 22899 22900 22901 22902 22903 22904 22905 22906 22907 22908 22909 22910 22911 22912 22913 22914 22915 22916 22917 22918 22919 22920 22921 22922 22923 22924 22925 22926 22927 22928 22929 22930 22931 22932 22933 22934 22935 22936 22937 22938 22939 22940 22941 22942 22943 22944 22945 22946 22947 22948 22949 22950 22951 22952 22953 22954 22955 22956 22957 22958 22959 22960 22961 22962 22963 22964 22965 22966 22967 22968 22969 22970 22971 22972 22973 22974 22975 22976 22977 22978 22979 22980 22981 22982 22983 22984 22985 22986 22987 22988 22989 22990 22991 22992 22993 22994 22995 22996 22997 22998 22999 23000 23001 23002 23003 23004 23005 23006 23007 23008 23009 23010 23011 23012 23013 23014 23015 23016 23017 23018 23019 23020 23021 23022 23023 23024 23025 23026 23027 23028 23029 23030 23031 23032 23033 23034 23035 23036 23037 23038 23039 23040 23041 23042 23043 23044 23045 23046 23047 23048 23049 23050 23051 23052 23053 23054 23055 23056 23057 23058 23059 23060 23061 23062 23063 23064 23065 23066 23067 23068 23069 23070 23071 23072 23073 23074 23075 23076 23077 23078 23079 23080 23081 23082 23083 23084 23085 23086 23087 23088 23089 23090 23091 23092 23093 23094 23095 |
}else if( c=='-' && z[i+1]=='-' ){
cEnd = '\n';
}else if( c=='(' ){
nParen++;
}else if( c==')' ){
nParen--;
if( nLine>0 && nParen==0 && j>0 ){
printSchemaLineN(p->out, z, j, "\n");
j = 0;
}
}
z[j++] = c;
if( nParen==1 && cEnd==0
&& (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
){
if( c=='\n' ) j--;
printSchemaLineN(p->out, z, j, "\n ");
j = 0;
nLine++;
while( IsSpace(z[i+1]) ){ i++; }
}
}
z[j] = 0;
}
printSchemaLine(p->out, z, ";\n");
sqlite3_free(z);
break;
}
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
sqlite3_fprintf(p->out, "%s%s", azCol[i],
i==nArg-1 ? p->rowSeparator : p->colSeparator);
}
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
char *z = azArg[i];
if( z==0 ) z = p->nullValue;
sqlite3_fputs(z, p->out);
sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out);
}
break;
}
case MODE_Www:
case MODE_Html: {
if( p->cnt==0 && p->cMode==MODE_Www ){
sqlite3_fputs(
"</PRE>\n"
"<TABLE border='1' cellspacing='0' cellpadding='2'>\n"
,p->out
);
}
if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){
sqlite3_fputs("<TR>", p->out);
for(i=0; i<nArg; i++){
sqlite3_fputs("<TH>", p->out);
output_html_string(p->out, azCol[i]);
sqlite3_fputs("</TH>\n", p->out);
}
sqlite3_fputs("</TR>\n", p->out);
}
p->cnt++;
if( azArg==0 ) break;
sqlite3_fputs("<TR>", p->out);
for(i=0; i<nArg; i++){
sqlite3_fputs("<TD>", p->out);
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
sqlite3_fputs("</TD>\n", p->out);
}
sqlite3_fputs("</TR>\n", p->out);
break;
}
case MODE_Tcl: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
output_c_string(p->out, azCol[i] ? azCol[i] : "");
if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out);
}
sqlite3_fputs(p->rowSeparator, p->out);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out);
}
sqlite3_fputs(p->rowSeparator, p->out);
break;
}
case MODE_Csv: {
sqlite3_fsetmode(p->out, _O_BINARY);
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
}
sqlite3_fputs(p->rowSeparator, p->out);
}
if( nArg>0 ){
for(i=0; i<nArg; i++){
output_csv(p, azArg[i], i<nArg-1);
}
sqlite3_fputs(p->rowSeparator, p->out);
}
setCrlfMode(p);
break;
}
case MODE_Insert: {
if( azArg==0 ) break;
sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable);
if( p->showHeader ){
sqlite3_fputs("(", p->out);
for(i=0; i<nArg; i++){
if( i>0 ) sqlite3_fputs(",", p->out);
if( quoteChar(azCol[i]) ){
char *z = sqlite3_mprintf("\"%w\"", azCol[i]);
shell_check_oom(z);
sqlite3_fputs(z, p->out);
sqlite3_free(z);
}else{
sqlite3_fprintf(p->out, "%s", azCol[i]);
}
}
sqlite3_fputs(")", p->out);
}
p->cnt++;
for(i=0; i<nArg; i++){
sqlite3_fputs(i>0 ? "," : " VALUES(", p->out);
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
sqlite3_fputs("NULL", p->out);
}else if( aiType && aiType[i]==SQLITE_TEXT ){
if( ShellHasFlag(p, SHFLG_Newlines) ){
output_quoted_string(p, azArg[i]);
}else{
output_quoted_escaped_string(p, azArg[i]);
}
}else if( aiType && aiType[i]==SQLITE_INTEGER ){
sqlite3_fputs(azArg[i], p->out);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
sqlite3_fputs("9.0e+999", p->out);
}else if( ur==0xfff0000000000000LL ){
sqlite3_fputs("-9.0e+999", p->out);
}else{
sqlite3_int64 ir = (sqlite3_int64)r;
if( r==(double)ir ){
sqlite3_snprintf(50,z,"%lld.0", ir);
}else{
sqlite3_snprintf(50,z,"%!.20g", r);
}
sqlite3_fputs(z, p->out);
}
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
output_hex_blob(p->out, pBlob, nBlob);
}else if( isNumber(azArg[i], 0) ){
sqlite3_fputs(azArg[i], p->out);
}else if( ShellHasFlag(p, SHFLG_Newlines) ){
output_quoted_string(p, azArg[i]);
}else{
output_quoted_escaped_string(p, azArg[i]);
}
}
sqlite3_fputs(");\n", p->out);
break;
}
case MODE_Json: {
if( azArg==0 ) break;
if( p->cnt==0 ){
sqlite3_fputs("[{", p->out);
}else{
sqlite3_fputs(",\n{", p->out);
}
p->cnt++;
for(i=0; i<nArg; i++){
output_json_string(p->out, azCol[i], -1);
sqlite3_fputs(":", p->out);
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
sqlite3_fputs("null", p->out);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
sqlite3_fputs("9.0e+999", p->out);
}else if( ur==0xfff0000000000000LL ){
sqlite3_fputs("-9.0e+999", p->out);
}else{
sqlite3_snprintf(50,z,"%!.20g", r);
sqlite3_fputs(z, p->out);
}
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
output_json_string(p->out, pBlob, nBlob);
}else if( aiType && aiType[i]==SQLITE_TEXT ){
output_json_string(p->out, azArg[i], -1);
}else{
sqlite3_fputs(azArg[i], p->out);
}
if( i<nArg-1 ){
sqlite3_fputs(",", p->out);
}
}
sqlite3_fputs("}", p->out);
break;
}
case MODE_Quote: {
if( azArg==0 ) break;
if( p->cnt==0 && p->showHeader ){
for(i=0; i<nArg; i++){
if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
output_quoted_string(p, azCol[i]);
}
sqlite3_fputs(p->rowSeparator, p->out);
}
p->cnt++;
for(i=0; i<nArg; i++){
if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
sqlite3_fputs("NULL", p->out);
}else if( aiType && aiType[i]==SQLITE_TEXT ){
output_quoted_string(p, azArg[i]);
}else if( aiType && aiType[i]==SQLITE_INTEGER ){
sqlite3_fputs(azArg[i], p->out);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_snprintf(50,z,"%!.20g", r);
sqlite3_fputs(z, p->out);
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
output_hex_blob(p->out, pBlob, nBlob);
}else if( isNumber(azArg[i], 0) ){
sqlite3_fputs(azArg[i], p->out);
}else{
output_quoted_string(p, azArg[i]);
}
}
sqlite3_fputs(p->rowSeparator, p->out);
break;
}
case MODE_Ascii: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out);
}
sqlite3_fputs(p->rowSeparator, p->out);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out);
}
sqlite3_fputs(p->rowSeparator, p->out);
break;
}
case MODE_EQP: {
eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
break;
}
}
|
| ︙ | ︙ | |||
21973 21974 21975 21976 21977 21978 21979 |
"INSERT INTO [_shell$self]\n"
" VALUES('run','PRAGMA integrity_check','ok');\n"
"INSERT INTO selftest(tno,op,cmd,ans)"
" SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n"
"DROP TABLE [_shell$self];"
,0,0,&zErrMsg);
if( zErrMsg ){
| | | 23160 23161 23162 23163 23164 23165 23166 23167 23168 23169 23170 23171 23172 23173 23174 |
"INSERT INTO [_shell$self]\n"
" VALUES('run','PRAGMA integrity_check','ok');\n"
"INSERT INTO selftest(tno,op,cmd,ans)"
" SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n"
"DROP TABLE [_shell$self];"
,0,0,&zErrMsg);
if( zErrMsg ){
sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0);
}
/*
|
| ︙ | ︙ | |||
22076 22077 22078 22079 22080 22081 22082 |
int rc;
int nResult;
int i;
const char *z;
rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
char *zContext = shell_error_context(zSelect, p->db);
| | | | | | > | | 23263 23264 23265 23266 23267 23268 23269 23270 23271 23272 23273 23274 23275 23276 23277 23278 23279 23280 23281 23282 23283 23284 23285 23286 23287 23288 23289 23290 23291 23292 23293 23294 23295 23296 23297 23298 23299 23300 23301 23302 23303 |
int rc;
int nResult;
int i;
const char *z;
rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
char *zContext = shell_error_context(zSelect, p->db);
sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s",
rc, sqlite3_errmsg(p->db), zContext);
sqlite3_free(zContext);
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
return rc;
}
rc = sqlite3_step(pSelect);
nResult = sqlite3_column_count(pSelect);
while( rc==SQLITE_ROW ){
z = (const char*)sqlite3_column_text(pSelect, 0);
sqlite3_fprintf(p->out, "%s", z);
for(i=1; i<nResult; i++){
sqlite3_fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i));
}
if( z==0 ) z = "";
while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
if( z[0] ){
sqlite3_fputs("\n;\n", p->out);
}else{
sqlite3_fputs(";\n", p->out);
}
rc = sqlite3_step(pSelect);
}
rc = sqlite3_finalize(pSelect);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n",
rc, sqlite3_errmsg(p->db));
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
}
return rc;
}
/*
** Allocate space and save off string indicating current error.
|
| ︙ | ︙ | |||
22137 22138 22139 22140 22141 22142 22143 | return zErr; } #ifdef __linux__ /* ** Attempt to display I/O stats on Linux using /proc/PID/io */ | | | | | > | > > | | | > | | | | | | | | | | | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | | | 23325 23326 23327 23328 23329 23330 23331 23332 23333 23334 23335 23336 23337 23338 23339 23340 23341 23342 23343 23344 23345 23346 23347 23348 23349 23350 23351 23352 23353 23354 23355 23356 23357 23358 23359 23360 23361 23362 23363 23364 23365 23366 23367 23368 23369 23370 23371 23372 23373 23374 23375 23376 23377 23378 23379 23380 23381 23382 23383 23384 23385 23386 23387 23388 23389 23390 23391 23392 23393 23394 23395 23396 23397 23398 23399 23400 23401 23402 23403 23404 23405 23406 23407 23408 23409 23410 23411 23412 23413 23414 23415 23416 23417 23418 23419 23420 23421 23422 23423 23424 23425 23426 23427 23428 23429 23430 23431 23432 23433 23434 23435 23436 23437 23438 23439 23440 23441 23442 23443 23444 23445 23446 23447 23448 23449 23450 23451 23452 23453 23454 23455 23456 23457 23458 23459 23460 23461 23462 23463 23464 23465 23466 23467 23468 23469 23470 23471 23472 23473 23474 23475 23476 23477 23478 23479 23480 23481 23482 23483 23484 23485 23486 23487 23488 23489 23490 23491 23492 23493 23494 23495 23496 23497 23498 23499 23500 23501 23502 23503 23504 23505 23506 23507 23508 23509 23510 23511 23512 23513 23514 23515 23516 23517 23518 23519 23520 23521 23522 23523 23524 23525 23526 23527 23528 23529 23530 23531 23532 23533 23534 23535 23536 23537 23538 23539 23540 23541 23542 23543 23544 23545 23546 23547 23548 |
return zErr;
}
#ifdef __linux__
/*
** Attempt to display I/O stats on Linux using /proc/PID/io
*/
static void displayLinuxIoStats(FILE *out){
FILE *in;
char z[200];
sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
in = sqlite3_fopen(z, "rb");
if( in==0 ) return;
while( sqlite3_fgets(z, sizeof(z), in)!=0 ){
static const struct {
const char *zPattern;
const char *zDesc;
} aTrans[] = {
{ "rchar: ", "Bytes received by read():" },
{ "wchar: ", "Bytes sent to write():" },
{ "syscr: ", "Read() system calls:" },
{ "syscw: ", "Write() system calls:" },
{ "read_bytes: ", "Bytes read from storage:" },
{ "write_bytes: ", "Bytes written to storage:" },
{ "cancelled_write_bytes: ", "Cancelled write bytes:" },
};
int i;
for(i=0; i<ArraySize(aTrans); i++){
int n = strlen30(aTrans[i].zPattern);
if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){
sqlite3_fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
break;
}
}
}
fclose(in);
}
#endif
/*
** Display a single line of status using 64-bit values.
*/
static void displayStatLine(
FILE *out, /* Write to this channel */
char *zLabel, /* Label for this one line */
char *zFormat, /* Format for the result */
int iStatusCtrl, /* Which status to display */
int bReset /* True to reset the stats */
){
sqlite3_int64 iCur = -1;
sqlite3_int64 iHiwtr = -1;
int i, nPercent;
char zLine[200];
sqlite3_status64(iStatusCtrl, &iCur, &iHiwtr, bReset);
for(i=0, nPercent=0; zFormat[i]; i++){
if( zFormat[i]=='%' ) nPercent++;
}
if( nPercent>1 ){
sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr);
}else{
sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr);
}
sqlite3_fprintf(out, "%-36s %s\n", zLabel, zLine);
}
/*
** Display memory stats.
*/
static int display_stats(
sqlite3 *db, /* Database to query */
ShellState *pArg, /* Pointer to ShellState */
int bReset /* True to reset the stats */
){
int iCur;
int iHiwtr;
FILE *out;
if( pArg==0 || pArg->out==0 ) return 0;
out = pArg->out;
if( pArg->pStmt && pArg->statsOn==2 ){
int nCol, i, x;
sqlite3_stmt *pStmt = pArg->pStmt;
char z[100];
nCol = sqlite3_column_count(pStmt);
sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol);
for(i=0; i<nCol; i++){
sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);
sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i));
#ifndef SQLITE_OMIT_DECLTYPE
sqlite3_snprintf(30, z+x, "declared type:");
sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
#endif
#ifdef SQLITE_ENABLE_COLUMN_METADATA
sqlite3_snprintf(30, z+x, "database name:");
sqlite3_fprintf(out, "%-36s %s\n", z,
sqlite3_column_database_name(pStmt,i));
sqlite3_snprintf(30, z+x, "table name:");
sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
sqlite3_snprintf(30, z+x, "origin name:");
sqlite3_fprintf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i));
#endif
}
}
if( pArg->statsOn==3 ){
if( pArg->pStmt ){
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset);
sqlite3_fprintf(out, "VM-steps: %d\n", iCur);
}
return 0;
}
displayStatLine(out, "Memory Used:",
"%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
displayStatLine(out, "Number of Outstanding Allocations:",
"%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset);
if( pArg->shellFlgs & SHFLG_Pagecache ){
displayStatLine(out, "Number of Pcache Pages Used:",
"%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset);
}
displayStatLine(out, "Number of Pcache Overflow Bytes:",
"%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset);
displayStatLine(out, "Largest Allocation:",
"%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset);
displayStatLine(out, "Largest Pcache Allocation:",
"%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset);
#ifdef YYTRACKMAXSTACKDEPTH
displayStatLine(out, "Deepest Parser Stack:",
"%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset);
#endif
if( db ){
if( pArg->shellFlgs & SHFLG_Lookaside ){
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED,
&iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT,
&iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Successful lookaside attempts: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE,
&iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Lookaside failures due to size: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL,
&iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Lookaside failures due to OOM: %d\n", iHiwtr);
}
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Pager Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache spills: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Statement Heap/Lookaside Usage: %d bytes\n", iCur);
}
if( pArg->pStmt ){
int iHit, iMiss;
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
bReset);
sqlite3_fprintf(out,
"Fullscan Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
sqlite3_fprintf(out,
"Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
sqlite3_fprintf(out,
"Autoindex Inserts: %d\n", iCur);
iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT,
bReset);
iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS,
bReset);
if( iHit || iMiss ){
sqlite3_fprintf(out,
"Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss);
}
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
sqlite3_fprintf(out,
"Virtual Machine Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset);
sqlite3_fprintf(out,
"Reprepare operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset);
sqlite3_fprintf(out,
"Number of times run: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset);
sqlite3_fprintf(out,
"Memory used by prepared stmt: %d\n", iCur);
}
#ifdef __linux__
displayLinuxIoStats(pArg->out);
#endif
/* Do not remove this machine readable comment: extra-stats-output-here */
return 0;
}
|
| ︙ | ︙ | |||
22719 22720 22721 22722 22723 22724 22725 | #define BOX_234 "\342\224\254" /* U+252c -,- */ #define BOX_124 "\342\224\264" /* U+2534 -'- */ #define BOX_1234 "\342\224\274" /* U+253c -|- */ /* Draw horizontal line N characters long using unicode box ** characters */ | | | | | | | | | | | 23930 23931 23932 23933 23934 23935 23936 23937 23938 23939 23940 23941 23942 23943 23944 23945 23946 23947 23948 23949 23950 23951 23952 23953 23954 23955 23956 23957 23958 23959 23960 23961 23962 23963 23964 23965 23966 23967 23968 23969 23970 23971 23972 23973 23974 23975 23976 23977 |
#define BOX_234 "\342\224\254" /* U+252c -,- */
#define BOX_124 "\342\224\264" /* U+2534 -'- */
#define BOX_1234 "\342\224\274" /* U+253c -|- */
/* Draw horizontal line N characters long using unicode box
** characters
*/
static void print_box_line(FILE *out, int N){
const char zDash[] =
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24;
const int nDash = sizeof(zDash) - 1;
N *= 3;
while( N>nDash ){
sqlite3_fputs(zDash, out);
N -= nDash;
}
sqlite3_fprintf(out, "%.*s", N, zDash);
}
/*
** Draw a horizontal separator for a MODE_Box table.
*/
static void print_box_row_separator(
ShellState *p,
int nArg,
const char *zSep1,
const char *zSep2,
const char *zSep3
){
int i;
if( nArg>0 ){
sqlite3_fputs(zSep1, p->out);
print_box_line(p->out, p->actualWidth[0]+2);
for(i=1; i<nArg; i++){
sqlite3_fputs(zSep2, p->out);
print_box_line(p->out, p->actualWidth[i]+2);
}
sqlite3_fputs(zSep3, p->out);
}
sqlite3_fputs("\n", p->out);
}
/*
** z[] is a line of text that is to be displayed the .mode box or table or
** similar tabular formats. z[] might contain control characters such
** as \n, \t, \f, or \r.
**
|
| ︙ | ︙ | |||
22786 22787 22788 22789 22790 22791 22792 |
*pzTail = 0;
return 0;
}
if( mxWidth<0 ) mxWidth = -mxWidth;
if( mxWidth==0 ) mxWidth = 1000000;
i = j = n = 0;
while( n<mxWidth ){
| > > > > > > > > > | | > | | 23997 23998 23999 24000 24001 24002 24003 24004 24005 24006 24007 24008 24009 24010 24011 24012 24013 24014 24015 24016 24017 24018 24019 24020 24021 24022 24023 24024 24025 24026 |
*pzTail = 0;
return 0;
}
if( mxWidth<0 ) mxWidth = -mxWidth;
if( mxWidth==0 ) mxWidth = 1000000;
i = j = n = 0;
while( n<mxWidth ){
unsigned char c = z[i];
if( c>=0xc0 ){
int u;
int len = decodeUtf8(&z[i], &u);
i += len;
j += len;
n += cli_wcwidth(u);
continue;
}
if( c>=' ' ){
n++;
i++;
j++;
continue;
}
if( c=='\t' ){
do{
n++;
j++;
}while( (n&7)!=0 && n<mxWidth );
i++;
continue;
}
|
| ︙ | ︙ | |||
22833 22834 22835 22836 22837 22838 22839 |
}else{
*pzTail = &z[i+1];
}
zOut = malloc( j+1 );
shell_check_oom(zOut);
i = j = n = 0;
while( i<k ){
| > > > > > > > > | | | 24054 24055 24056 24057 24058 24059 24060 24061 24062 24063 24064 24065 24066 24067 24068 24069 24070 24071 24072 24073 24074 24075 24076 24077 24078 |
}else{
*pzTail = &z[i+1];
}
zOut = malloc( j+1 );
shell_check_oom(zOut);
i = j = n = 0;
while( i<k ){
unsigned char c = z[i];
if( c>=0xc0 ){
int u;
int len = decodeUtf8(&z[i], &u);
do{ zOut[j++] = z[i++]; }while( (--len)>0 );
n += cli_wcwidth(u);
continue;
}
if( c>=' ' ){
n++;
zOut[j++] = z[i++];
continue;
}
if( z[i]=='\t' ){
do{
n++;
zOut[j++] = ' ';
}while( (n&7)!=0 && n<mxWidth );
|
| ︙ | ︙ | |||
23015 23016 23017 23018 23019 23020 23021 |
case MODE_Column: {
colSep = " ";
rowSep = "\n";
if( p->showHeader ){
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
if( p->colWidth[i]<0 ) w = -w;
| | | | | | > | | | > | | | | | | | | | | | 24244 24245 24246 24247 24248 24249 24250 24251 24252 24253 24254 24255 24256 24257 24258 24259 24260 24261 24262 24263 24264 24265 24266 24267 24268 24269 24270 24271 24272 24273 24274 24275 24276 24277 24278 24279 24280 24281 24282 24283 24284 24285 24286 24287 24288 24289 24290 24291 24292 24293 24294 24295 24296 24297 24298 24299 24300 24301 24302 24303 24304 24305 24306 24307 24308 24309 24310 24311 24312 24313 24314 24315 24316 24317 24318 24319 24320 24321 24322 24323 24324 24325 24326 24327 24328 24329 24330 24331 24332 24333 24334 24335 24336 24337 24338 24339 24340 24341 24342 24343 24344 24345 24346 |
case MODE_Column: {
colSep = " ";
rowSep = "\n";
if( p->showHeader ){
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
if( p->colWidth[i]<0 ) w = -w;
utf8_width_print(p->out, w, azData[i]);
sqlite3_fputs(i==nColumn-1?"\n":" ", p->out);
}
for(i=0; i<nColumn; i++){
print_dashes(p->out, p->actualWidth[i]);
sqlite3_fputs(i==nColumn-1?"\n":" ", p->out);
}
}
break;
}
case MODE_Table: {
colSep = " | ";
rowSep = " |\n";
print_row_separator(p, nColumn, "+");
sqlite3_fputs("| ", p->out);
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
n = strlenChar(azData[i]);
sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "",
azData[i], (w-n+1)/2, "");
sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out);
}
print_row_separator(p, nColumn, "+");
break;
}
case MODE_Markdown: {
colSep = " | ";
rowSep = " |\n";
sqlite3_fputs("| ", p->out);
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
n = strlenChar(azData[i]);
sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "",
azData[i], (w-n+1)/2, "");
sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out);
}
print_row_separator(p, nColumn, "|");
break;
}
case MODE_Box: {
colSep = " " BOX_13 " ";
rowSep = " " BOX_13 "\n";
print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34);
sqlite3_fputs(BOX_13 " ", p->out);
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
n = strlenChar(azData[i]);
sqlite3_fprintf(p->out, "%*s%s%*s%s",
(w-n)/2, "", azData[i], (w-n+1)/2, "",
i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
}
print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
break;
}
}
for(i=nColumn, j=0; i<nTotal; i++, j++){
if( j==0 && p->cMode!=MODE_Column ){
sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out);
}
z = azData[i];
if( z==0 ) z = p->nullValue;
w = p->actualWidth[j];
if( p->colWidth[j]<0 ) w = -w;
utf8_width_print(p->out, w, z);
if( j==nColumn-1 ){
sqlite3_fputs(rowSep, p->out);
if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){
if( p->cMode==MODE_Table ){
print_row_separator(p, nColumn, "+");
}else if( p->cMode==MODE_Box ){
print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
}else if( p->cMode==MODE_Column ){
sqlite3_fputs("\n", p->out);
}
}
j = -1;
if( seenInterrupt ) goto columnar_end;
}else{
sqlite3_fputs(colSep, p->out);
}
}
if( p->cMode==MODE_Table ){
print_row_separator(p, nColumn, "+");
}else if( p->cMode==MODE_Box ){
print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14);
}
columnar_end:
if( seenInterrupt ){
sqlite3_fputs("Interrupt\n", p->out);
}
nData = (nRow+1)*nColumn;
for(i=0; i<nData; i++){
z = azData[i];
if( z!=zEmpty && z!=zShowNull ) free(azData[i]);
}
sqlite3_free(azData);
|
| ︙ | ︙ | |||
23188 23189 23190 23191 23192 23193 23194 |
}else{
rc = sqlite3_step(pStmt);
}
}
} while( SQLITE_ROW == rc );
sqlite3_free(pData);
if( pArg->cMode==MODE_Json ){
| | > > | 24419 24420 24421 24422 24423 24424 24425 24426 24427 24428 24429 24430 24431 24432 24433 24434 24435 |
}else{
rc = sqlite3_step(pStmt);
}
}
} while( SQLITE_ROW == rc );
sqlite3_free(pData);
if( pArg->cMode==MODE_Json ){
sqlite3_fputs("]\n", pArg->out);
}else if( pArg->cMode==MODE_Www ){
sqlite3_fputs("</TABLE>\n<PRE>\n", pArg->out);
}else if( pArg->cMode==MODE_Count ){
char zBuf[200];
sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n",
nRow, nRow!=1 ? "s" : "");
printf("%s", zBuf);
}
}
|
| ︙ | ︙ | |||
23237 23238 23239 23240 23241 23242 23243 23244 23245 23246 23247 23248 23249 23250 23251 23252 23253 23254 23255 |
static int expertFinish(
ShellState *pState,
int bCancel,
char **pzErr
){
int rc = SQLITE_OK;
sqlite3expert *p = pState->expert.pExpert;
assert( p );
assert( bCancel || pzErr==0 || *pzErr==0 );
if( bCancel==0 ){
int bVerbose = pState->expert.bVerbose;
rc = sqlite3_expert_analyze(p, pzErr);
if( rc==SQLITE_OK ){
int nQuery = sqlite3_expert_count(p);
int i;
if( bVerbose ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
| > | | > | | > | < | 24470 24471 24472 24473 24474 24475 24476 24477 24478 24479 24480 24481 24482 24483 24484 24485 24486 24487 24488 24489 24490 24491 24492 24493 24494 24495 24496 24497 24498 24499 24500 24501 24502 24503 24504 24505 24506 24507 24508 24509 24510 24511 |
static int expertFinish(
ShellState *pState,
int bCancel,
char **pzErr
){
int rc = SQLITE_OK;
sqlite3expert *p = pState->expert.pExpert;
FILE *out = pState->out;
assert( p );
assert( bCancel || pzErr==0 || *pzErr==0 );
if( bCancel==0 ){
int bVerbose = pState->expert.bVerbose;
rc = sqlite3_expert_analyze(p, pzErr);
if( rc==SQLITE_OK ){
int nQuery = sqlite3_expert_count(p);
int i;
if( bVerbose ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
sqlite3_fputs("-- Candidates -----------------------------\n", out);
sqlite3_fprintf(out, "%s\n", zCand);
}
for(i=0; i<nQuery; i++){
const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL);
const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES);
const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN);
if( zIdx==0 ) zIdx = "(no new indexes)\n";
if( bVerbose ){
sqlite3_fprintf(out,
"-- Query %d --------------------------------\n"
"%s\n\n"
,i+1, zSql);
}
sqlite3_fprintf(out, "%s\n%s\n", zIdx, zEQP);
}
}
}
sqlite3_expert_destroy(p);
pState->expert.pExpert = 0;
return rc;
}
|
| ︙ | ︙ | |||
23297 23298 23299 23300 23301 23302 23303 |
if( z[0]=='-' && z[1]=='-' ) z++;
n = strlen30(z);
if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){
pState->expert.bVerbose = 1;
}
else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
| | | | > | | 24532 24533 24534 24535 24536 24537 24538 24539 24540 24541 24542 24543 24544 24545 24546 24547 24548 24549 24550 24551 24552 24553 24554 24555 24556 24557 24558 24559 24560 24561 24562 24563 24564 24565 24566 |
if( z[0]=='-' && z[1]=='-' ) z++;
n = strlen30(z);
if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){
pState->expert.bVerbose = 1;
}
else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
sqlite3_fprintf(stderr, "option requires an argument: %s\n", z);
rc = SQLITE_ERROR;
}else{
iSample = (int)integerValue(azArg[++i]);
if( iSample<0 || iSample>100 ){
sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]);
rc = SQLITE_ERROR;
}
}
}
else{
sqlite3_fprintf(stderr,"unknown option: %s\n", z);
rc = SQLITE_ERROR;
}
}
if( rc==SQLITE_OK ){
pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
if( pState->expert.pExpert==0 ){
sqlite3_fprintf(stderr,
"sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory");
rc = SQLITE_ERROR;
}else{
sqlite3_expert_config(
pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample
);
}
}
|
| ︙ | ︙ | |||
23645 23646 23647 23648 23649 23650 23651 |
zSql = azArg[2];
if( zTable==0 ) return 0;
if( zType==0 ) return 0;
dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0;
noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0;
if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
| | | | | | | 24881 24882 24883 24884 24885 24886 24887 24888 24889 24890 24891 24892 24893 24894 24895 24896 24897 24898 24899 24900 24901 24902 24903 24904 24905 24906 24907 24908 24909 24910 24911 24912 24913 24914 24915 24916 24917 |
zSql = azArg[2];
if( zTable==0 ) return 0;
if( zType==0 ) return 0;
dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0;
noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0;
if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
/* no-op */
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){
if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
}else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( dataOnly ){
/* no-op */
}else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
char *zIns;
if( !p->writableSchema ){
sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out);
p->writableSchema = 1;
}
zIns = sqlite3_mprintf(
"INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)"
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
shell_check_oom(zIns);
sqlite3_fprintf(p->out, "%s\n", zIns);
sqlite3_free(zIns);
return 0;
}else{
printSchemaLine(p->out, zSql, ";\n");
}
if( cli_strcmp(zType, "table")==0 ){
ShellText sSelect;
ShellText sTable;
char **azCol;
int i;
|
| ︙ | ︙ | |||
23725 23726 23727 23728 23729 23730 23731 |
savedDestTable = p->zDestTable;
savedMode = p->mode;
p->zDestTable = sTable.z;
p->mode = p->cMode = MODE_Insert;
rc = shell_exec(p, sSelect.z, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
| | | 24961 24962 24963 24964 24965 24966 24967 24968 24969 24970 24971 24972 24973 24974 24975 |
savedDestTable = p->zDestTable;
savedMode = p->mode;
p->zDestTable = sTable.z;
p->mode = p->cMode = MODE_Insert;
rc = shell_exec(p, sSelect.z, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
toggleSelectOrder(p->db);
shell_exec(p, sSelect.z, 0);
toggleSelectOrder(p->db);
}
p->zDestTable = savedDestTable;
p->mode = savedMode;
freeText(&sTable);
|
| ︙ | ︙ | |||
23756 23757 23758 23759 23760 23761 23762 |
){
int rc;
char *zErr = 0;
rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr);
if( rc==SQLITE_CORRUPT ){
char *zQ2;
int len = strlen30(zQuery);
| | | | < > | 24992 24993 24994 24995 24996 24997 24998 24999 25000 25001 25002 25003 25004 25005 25006 25007 25008 25009 25010 25011 25012 25013 25014 25015 25016 25017 25018 25019 25020 25021 25022 25023 |
){
int rc;
char *zErr = 0;
rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr);
if( rc==SQLITE_CORRUPT ){
char *zQ2;
int len = strlen30(zQuery);
sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
if( zErr ){
sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr);
sqlite3_free(zErr);
zErr = 0;
}
zQ2 = malloc( len+100 );
if( zQ2==0 ) return rc;
sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
}else{
rc = SQLITE_CORRUPT;
}
free(zQ2);
}
sqlite3_free(zErr);
return rc;
}
/*
** Text of help messages.
**
** The help text for each individual command begins with a line that starts
|
| ︙ | ︙ | |||
23830 23831 23832 23833 23834 23835 23836 | #endif ".changes on|off Show number of rows changed by SQL", #ifndef SQLITE_SHELL_FIDDLE ".check GLOB Fail if output since .testcase does not match", ".clone NEWDB Clone data into NEWDB from the existing database", #endif ".connection [close] [#] Open or close an auxiliary database connection", | < < | > | 25066 25067 25068 25069 25070 25071 25072 25073 25074 25075 25076 25077 25078 25079 25080 25081 25082 25083 25084 25085 25086 | #endif ".changes on|off Show number of rows changed by SQL", #ifndef SQLITE_SHELL_FIDDLE ".check GLOB Fail if output since .testcase does not match", ".clone NEWDB Clone data into NEWDB from the existing database", #endif ".connection [close] [#] Open or close an auxiliary database connection", ".crlf ?on|off? Whether or not to use \\r\\n line endings", ".databases List names and files of attached databases", ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", #if SQLITE_SHELL_HAVE_RECOVER ".dbinfo ?DB? Show status information about the database", #endif ".dbtotxt Hex dump of the database file", ".dump ?OBJECTS? Render database content as SQL", " Options:", " --data-only Output only INSERT statements", " --newlines Allow unescaped newline characters in output", " --nosys Omit system tables (ex: \"sqlite_stat1\")", " --preserve-rowids Include ROWID values in the output", " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", |
| ︙ | ︙ | |||
23938 23939 23940 23941 23942 23943 23944 | #ifndef SQLITE_SHELL_FIDDLE ".nonce STRING Suspend safe mode for one command if nonce matches", #endif ".nullvalue STRING Use STRING in place of NULL values", #ifndef SQLITE_SHELL_FIDDLE ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", | | | > > | | 25173 25174 25175 25176 25177 25178 25179 25180 25181 25182 25183 25184 25185 25186 25187 25188 25189 25190 25191 | #ifndef SQLITE_SHELL_FIDDLE ".nonce STRING Suspend safe mode for one command if nonce matches", #endif ".nullvalue STRING Use STRING in place of NULL values", #ifndef SQLITE_SHELL_FIDDLE ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", " --bom Put a UTF8 byte-order mark at the beginning", " -e Send output to the system text editor", " --plain Use text/plain output instead of HTML for -w option", " -w Send output as HTML to a web browser (same as \".www\")", " -x Send output as CSV to a spreadsheet (same as \".excel\")", /* Note that .open is (partially) available in WASM builds but is ** currently only intended to be used by the fiddle tool, not ** end users, so is "undocumented." */ ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #endif |
| ︙ | ︙ | |||
23963 23964 23965 23966 23967 23968 23969 23970 23971 23972 23973 23974 23975 23976 | " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_FIDDLE ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet", #endif ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", " init Initialize the TEMP table that holds bindings", " list List the current parameter bindings", " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", | > > | 25200 25201 25202 25203 25204 25205 25206 25207 25208 25209 25210 25211 25212 25213 25214 25215 | " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_FIDDLE ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", " --plain Use text/plain for -w option", " -w Send output to a web browser", " -x Send output as CSV to a spreadsheet", #endif ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", " init Initialize the TEMP table that holds bindings", " list List the current parameter bindings", " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", |
| ︙ | ︙ | |||
24076 24077 24078 24079 24080 24081 24082 24083 24084 24085 24086 24087 24088 24089 | #endif ".version Show source, library and compiler versions", ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", }; /* ** Output help text. ** ** zPattern describes the set of commands for which help text is provided. ** If zPattern is NULL, then show all commands, but only give a one-line | > > > > | 25315 25316 25317 25318 25319 25320 25321 25322 25323 25324 25325 25326 25327 25328 25329 25330 25331 25332 | #endif ".version Show source, library and compiler versions", ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", #ifndef SQLITE_SHELL_FIDDLE ".www Display output of the next command in web browser", " --plain Show results as text/plain, not as HTML", #endif }; /* ** Output help text. ** ** zPattern describes the set of commands for which help text is provided. ** If zPattern is NULL, then show all commands, but only give a one-line |
| ︙ | ︙ | |||
24124 24125 24126 24127 24128 24129 24130 |
break;
default:
hh &= ~HH_Summary;
break;
}
if( ((hw^hh)&HH_Undoc)==0 ){
if( (hh&HH_Summary)!=0 ){
| | | | | | | | 25367 25368 25369 25370 25371 25372 25373 25374 25375 25376 25377 25378 25379 25380 25381 25382 25383 25384 25385 25386 25387 25388 25389 25390 25391 25392 25393 25394 25395 25396 25397 25398 25399 25400 25401 25402 25403 25404 25405 25406 25407 25408 25409 25410 25411 25412 25413 25414 25415 25416 25417 25418 25419 25420 25421 25422 25423 25424 25425 |
break;
default:
hh &= ~HH_Summary;
break;
}
if( ((hw^hh)&HH_Undoc)==0 ){
if( (hh&HH_Summary)!=0 ){
sqlite3_fprintf(out, ".%s\n", azHelp[i]+1);
++n;
}else if( (hw&HW_SummaryOnly)==0 ){
sqlite3_fprintf(out, "%s\n", azHelp[i]);
}
}
}
}else{
/* Seek documented commands for which zPattern is an exact prefix */
zPat = sqlite3_mprintf(".%s*", zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
sqlite3_fprintf(out, "%s\n", azHelp[i]);
j = i+1;
n++;
}
}
sqlite3_free(zPat);
if( n ){
if( n==1 ){
/* when zPattern is a prefix of exactly one command, then include
** the details of that command, which should begin at offset j */
while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
sqlite3_fprintf(out, "%s\n", azHelp[j]);
j++;
}
}
return n;
}
/* Look for documented commands that contain zPattern anywhere.
** Show complete text of all documented commands that match. */
zPat = sqlite3_mprintf("%%%s%%", zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( azHelp[i][0]==',' ){
while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
continue;
}
if( azHelp[i][0]=='.' ) j = i;
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
sqlite3_fprintf(out, "%s\n", azHelp[j]);
while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
j++;
sqlite3_fprintf(out, "%s\n", azHelp[j]);
}
i = j;
n++;
}
}
sqlite3_free(zPat);
}
|
| ︙ | ︙ | |||
24198 24199 24200 24201 24202 24203 24204 |
** from the file before the buffer is returned. This byte is not included in
** the final value of (*pnByte), if applicable.
**
** NULL is returned if any error is encountered. The final value of *pnByte
** is undefined in this case.
*/
static char *readFile(const char *zName, int *pnByte){
| | | | | | 25441 25442 25443 25444 25445 25446 25447 25448 25449 25450 25451 25452 25453 25454 25455 25456 25457 25458 25459 25460 25461 25462 25463 25464 25465 25466 25467 25468 25469 25470 25471 25472 25473 25474 25475 25476 25477 25478 25479 |
** from the file before the buffer is returned. This byte is not included in
** the final value of (*pnByte), if applicable.
**
** NULL is returned if any error is encountered. The final value of *pnByte
** is undefined in this case.
*/
static char *readFile(const char *zName, int *pnByte){
FILE *in = sqlite3_fopen(zName, "rb");
long nIn;
size_t nRead;
char *pBuf;
int rc;
if( in==0 ) return 0;
rc = fseek(in, 0, SEEK_END);
if( rc!=0 ){
sqlite3_fprintf(stderr,"Error: '%s' not seekable\n", zName);
fclose(in);
return 0;
}
nIn = ftell(in);
rewind(in);
pBuf = sqlite3_malloc64( nIn+1 );
if( pBuf==0 ){
sqlite3_fputs("Error: out of memory\n", stderr);
fclose(in);
return 0;
}
nRead = fread(pBuf, nIn, 1, in);
fclose(in);
if( nRead!=1 ){
sqlite3_free(pBuf);
sqlite3_fprintf(stderr,"Error: cannot read '%s'\n", zName);
return 0;
}
pBuf[nIn] = 0;
if( pnByte ) *pnByte = nIn;
return pBuf;
}
|
| ︙ | ︙ | |||
24288 24289 24290 24291 24292 24293 24294 |
**
** If the file does not exist or is empty but its name looks like a ZIP
** archive and the dfltZip flag is true, then assume it is a ZIP archive.
** Otherwise, assume an ordinary database regardless of the filename if
** the type cannot be determined from content.
*/
int deduceDatabaseType(const char *zName, int dfltZip){
| | | 25531 25532 25533 25534 25535 25536 25537 25538 25539 25540 25541 25542 25543 25544 25545 |
**
** If the file does not exist or is empty but its name looks like a ZIP
** archive and the dfltZip flag is true, then assume it is a ZIP archive.
** Otherwise, assume an ordinary database regardless of the filename if
** the type cannot be determined from content.
*/
int deduceDatabaseType(const char *zName, int dfltZip){
FILE *f = sqlite3_fopen(zName, "rb");
size_t n;
int rc = SHELL_OPEN_UNSPEC;
char zBuf[100];
if( f==0 ){
if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
return SHELL_OPEN_ZIPFILE;
}else{
|
| ︙ | ︙ | |||
24341 24342 24343 24344 24345 24346 24347 |
int j, k;
int rc;
FILE *in;
const char *zDbFilename = p->pAuxDb->zDbFilename;
unsigned int x[16];
char zLine[1000];
if( zDbFilename ){
| | | | | | | 25584 25585 25586 25587 25588 25589 25590 25591 25592 25593 25594 25595 25596 25597 25598 25599 25600 25601 25602 25603 25604 25605 25606 25607 25608 25609 25610 25611 25612 25613 25614 25615 25616 25617 25618 25619 25620 25621 25622 25623 25624 |
int j, k;
int rc;
FILE *in;
const char *zDbFilename = p->pAuxDb->zDbFilename;
unsigned int x[16];
char zLine[1000];
if( zDbFilename ){
in = sqlite3_fopen(zDbFilename, "r");
if( in==0 ){
sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename);
return 0;
}
nLine = 0;
}else{
in = p->in;
nLine = p->lineno;
if( in==0 ) in = stdin;
}
*pnData = 0;
nLine++;
if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<0 ) goto readHexDb_error;
if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error;
n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
a = sqlite3_malloc( n ? n : 1 );
shell_check_oom(a);
memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
sqlite3_fputs("invalid pagesize\n", stderr);
goto readHexDb_error;
}
for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
continue;
}
if( cli_strncmp(zLine, "| end ", 6)==0 ){
break;
|
| ︙ | ︙ | |||
24399 24400 24401 24402 24403 24404 24405 |
}
return a;
readHexDb_error:
if( in!=p->in ){
fclose(in);
}else{
| | | | 25642 25643 25644 25645 25646 25647 25648 25649 25650 25651 25652 25653 25654 25655 25656 25657 25658 25659 25660 25661 25662 25663 |
}
return a;
readHexDb_error:
if( in!=p->in ){
fclose(in);
}else{
while( sqlite3_fgets(zLine, sizeof(zLine), p->in)!=0 ){
nLine++;
if(cli_strncmp(zLine, "| end ", 6)==0 ) break;
}
p->lineno = nLine;
}
sqlite3_free(a);
sqlite3_fprintf(stderr,"Error on line %d of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_OMIT_DESERIALIZE */
/*
** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
*/
|
| ︙ | ︙ | |||
24481 24482 24483 24484 24485 24486 24487 |
case SHELL_OPEN_NORMAL: {
sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
break;
}
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
| | | > > | > | 25724 25725 25726 25727 25728 25729 25730 25731 25732 25733 25734 25735 25736 25737 25738 25739 25740 25741 25742 25743 25744 25745 25746 25747 25748 25749 25750 25751 25752 25753 25754 25755 25756 25757 25758 25759 25760 25761 25762 25763 25764 25765 25766 25767 25768 |
case SHELL_OPEN_NORMAL: {
sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
break;
}
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
exit(1);
}
sqlite3_close(p->db);
sqlite3_open(":memory:", &p->db);
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
sqlite3_fputs("Also: unable to open substitute in-memory database.\n",
stderr);
exit(1);
}else{
sqlite3_fprintf(stderr,
"Notice: using substitute in-memory database instead of \"%s\"\n",
zDbFilename);
}
}
globalDb = p->db;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0);
/* Reflect the use or absence of --unsafe-testing invocation. */
{
int testmode_on = ShellHasFlag(p,SHFLG_TestingMode);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_sha_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
sqlite3_percentile_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
|
| ︙ | ︙ | |||
24603 24604 24605 24606 24607 24608 24609 |
if( aData==0 ){
return;
}
rc = sqlite3_deserialize(p->db, "main", aData, nData, nData,
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
| | | 25849 25850 25851 25852 25853 25854 25855 25856 25857 25858 25859 25860 25861 25862 25863 |
if( aData==0 ){
return;
}
rc = sqlite3_deserialize(p->db, "main", aData, nData, nData,
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc);
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
|
| ︙ | ︙ | |||
24627 24628 24629 24630 24631 24632 24633 |
/*
** Attempt to close the database connection. Report errors.
*/
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
if( rc ){
| > | | > | 25873 25874 25875 25876 25877 25878 25879 25880 25881 25882 25883 25884 25885 25886 25887 25888 25889 25890 25891 25892 25893 |
/*
** Attempt to close the database connection. Report errors.
*/
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
if( rc ){
sqlite3_fprintf(stderr,
"Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db));
}
}
#if (HAVE_READLINE || HAVE_EDITLINE) \
&& !defined(SQLITE_OMIT_READLINE_COMPLETION)
/*
** Readline completion callbacks
*/
static char *readline_completion_generator(const char *text, int state){
static sqlite3_stmt *pStmt = 0;
char *zRet;
if( state==0 ){
|
| ︙ | ︙ | |||
24669 24670 24671 24672 24673 24674 24675 | } #elif HAVE_LINENOISE /* ** Linenoise completion callback. Note that the 3rd argument is from ** the "msteveb" version of linenoise, not the "antirez" version. */ | | > > > | > > > > | 25917 25918 25919 25920 25921 25922 25923 25924 25925 25926 25927 25928 25929 25930 25931 25932 25933 25934 25935 25936 25937 25938 25939 25940 25941 25942 25943 25944 25945 25946 |
}
#elif HAVE_LINENOISE
/*
** Linenoise completion callback. Note that the 3rd argument is from
** the "msteveb" version of linenoise, not the "antirez" version.
*/
static void linenoise_completion(
const char *zLine,
linenoiseCompletions *lc
#if HAVE_LINENOISE==2
,void *pUserData
#endif
){
i64 nLine = strlen(zLine);
i64 i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
#if HAVE_LINENOISE==2
UNUSED_PARAMETER(pUserData);
#endif
if( nLine>(i64)sizeof(zBuf)-30 ) return;
if( zLine[0]=='.' || zLine[0]=='#') return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
memcpy(zBuf, zLine, iStart);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
|
| ︙ | ︙ | |||
24791 24792 24793 24794 24795 24796 24797 |
if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff);
if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
return 1;
}
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
return 0;
}
| > | | 26046 26047 26048 26049 26050 26051 26052 26053 26054 26055 26056 26057 26058 26059 26060 26061 |
if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff);
if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
return 1;
}
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
return 0;
}
sqlite3_fprintf(stderr,
"ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg);
return 0;
}
/*
** Set or clear a shell flag according to a boolean value.
*/
static void setOrClearFlag(ShellState *p, unsigned mFlag, const char *zArg){
|
| ︙ | ︙ | |||
24818 24819 24820 24821 24822 24823 24824 | } /* ** Try to open an output file. The names "stdout" and "stderr" are ** recognized and do the right thing. NULL is returned if the output ** filename is "off". */ | | | | | 26074 26075 26076 26077 26078 26079 26080 26081 26082 26083 26084 26085 26086 26087 26088 26089 26090 26091 26092 26093 26094 26095 26096 26097 26098 26099 |
}
/*
** Try to open an output file. The names "stdout" and "stderr" are
** recognized and do the right thing. NULL is returned if the output
** filename is "off".
*/
static FILE *output_file_open(const char *zFile){
FILE *f;
if( cli_strcmp(zFile,"stdout")==0 ){
f = stdout;
}else if( cli_strcmp(zFile, "stderr")==0 ){
f = stderr;
}else if( cli_strcmp(zFile, "off")==0 ){
f = 0;
}else{
f = sqlite3_fopen(zFile, "w");
if( f==0 ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile);
}
}
return f;
}
#ifndef SQLITE_OMIT_TRACE
/*
|
| ︙ | ︙ | |||
24882 24883 24884 24885 24886 24887 24888 |
if( zSql==0 ) return 0;
nSql = strlen(zSql);
if( nSql>1000000000 ) nSql = 1000000000;
while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; }
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
| | > | | 26138 26139 26140 26141 26142 26143 26144 26145 26146 26147 26148 26149 26150 26151 26152 26153 26154 26155 26156 26157 26158 |
if( zSql==0 ) return 0;
nSql = strlen(zSql);
if( nSql>1000000000 ) nSql = 1000000000;
while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; }
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0;
sqlite3_fprintf(p->traceOut,
"%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
break;
}
}
return 0;
}
#endif
|
| ︙ | ︙ | |||
24994 24995 24996 24997 24998 24999 25000 |
|| (c==EOF && pc==cQuote)
){
do{ p->n--; }while( p->z[p->n]!=cQuote );
p->cTerm = c;
break;
}
if( pc==cQuote && c!='\r' ){
| > | | | 26251 26252 26253 26254 26255 26256 26257 26258 26259 26260 26261 26262 26263 26264 26265 26266 26267 26268 26269 |
|| (c==EOF && pc==cQuote)
){
do{ p->n--; }while( p->z[p->n]!=cQuote );
p->cTerm = c;
break;
}
if( pc==cQuote && c!='\r' ){
sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n",
p->zFile, p->nLine, cQuote);
}
if( c==EOF ){
sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n",
p->zFile, startLine, cQuote);
p->cTerm = c;
break;
}
import_append_char(p, c);
ppc = pc;
pc = c;
|
| ︙ | ︙ | |||
25096 25097 25098 25099 25100 25101 25102 |
int cnt = 0;
const int spinRate = 10000;
zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
| | | | 26354 26355 26356 26357 26358 26359 26360 26361 26362 26363 26364 26365 26366 26367 26368 26369 26370 26371 26372 26373 26374 26375 26376 26377 26378 26379 26380 26381 26382 26383 26384 26385 |
int cnt = 0;
const int spinRate = 10000;
zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_data_xfer;
}
n = sqlite3_column_count(pQuery);
zInsert = sqlite3_malloc64(200 + nTable + n*3);
shell_check_oom(zInsert);
sqlite3_snprintf(200+nTable,zInsert,
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
i = strlen30(zInsert);
for(j=1; j<n; j++){
memcpy(zInsert+i, ",?", 2);
i += 2;
}
memcpy(zInsert+i, ");", 3);
rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0);
if( rc ){
sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n",
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert);
goto end_data_xfer;
}
for(k=0; k<2; k++){
while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
for(i=0; i<n; i++){
switch( sqlite3_column_type(pQuery, i) ){
|
| ︙ | ︙ | |||
25149 25150 25151 25152 25153 25154 25155 |
SQLITE_STATIC);
break;
}
}
} /* End for */
rc = sqlite3_step(pInsert);
if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
| | | | 26407 26408 26409 26410 26411 26412 26413 26414 26415 26416 26417 26418 26419 26420 26421 26422 26423 26424 26425 26426 26427 26428 26429 26430 26431 26432 26433 26434 26435 26436 26437 26438 26439 |
SQLITE_STATIC);
break;
}
}
} /* End for */
rc = sqlite3_step(pInsert);
if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
sqlite3_fprintf(stderr,"Error %d: %s\n",
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb));
}
sqlite3_reset(pInsert);
cnt++;
if( (cnt%spinRate)==0 ){
printf("%c\b", "|/-\\"[(cnt/spinRate)%4]);
fflush(stdout);
}
} /* End while */
if( rc==SQLITE_DONE ) break;
sqlite3_finalize(pQuery);
sqlite3_free(zQuery);
zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;",
zTable);
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable);
break;
}
} /* End for(k=0...) */
end_data_xfer:
sqlite3_finalize(pQuery);
sqlite3_finalize(pInsert);
|
| ︙ | ︙ | |||
25204 25205 25206 25207 25208 25209 25210 |
char *zErrMsg = 0;
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
" WHERE %s ORDER BY rowid ASC", zWhere);
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
| > | | | | | | | 26462 26463 26464 26465 26466 26467 26468 26469 26470 26471 26472 26473 26474 26475 26476 26477 26478 26479 26480 26481 26482 26483 26484 26485 26486 26487 26488 26489 26490 26491 26492 26493 26494 26495 26496 26497 26498 26499 26500 26501 26502 26503 26504 26505 26506 26507 26508 26509 26510 26511 26512 26513 26514 26515 26516 26517 26518 26519 |
char *zErrMsg = 0;
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
" WHERE %s ORDER BY rowid ASC", zWhere);
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
sqlite3_fprintf(stderr,
"Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db),
sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
}
while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){
sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
}
if( xForEach ){
xForEach(p, newDb, (const char*)zName);
}
sputz(stdout, "done\n");
}
if( rc!=SQLITE_DONE ){
sqlite3_finalize(pQuery);
sqlite3_free(zQuery);
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
" WHERE %s ORDER BY rowid DESC", zWhere);
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
}
while( sqlite3_step(pQuery)==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue;
sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
if( xForEach ){
xForEach(p, newDb, (const char*)zName);
}
sputz(stdout, "done\n");
|
| ︙ | ︙ | |||
25270 25271 25272 25273 25274 25275 25276 |
** as possible out of the main database (which might be corrupt) and write it
** into zNewDb.
*/
static void tryToClone(ShellState *p, const char *zNewDb){
int rc;
sqlite3 *newDb = 0;
if( access(zNewDb,0)==0 ){
| | > | | > | > > | > > > > > > > > | | | > > > > > | > | 26529 26530 26531 26532 26533 26534 26535 26536 26537 26538 26539 26540 26541 26542 26543 26544 26545 26546 26547 26548 26549 26550 26551 26552 26553 26554 26555 26556 26557 26558 26559 26560 26561 26562 26563 26564 26565 26566 26567 26568 26569 26570 26571 26572 26573 26574 26575 26576 26577 26578 26579 26580 26581 26582 26583 26584 26585 26586 26587 26588 26589 26590 26591 26592 26593 26594 26595 26596 26597 26598 26599 26600 26601 26602 26603 26604 26605 26606 26607 26608 26609 26610 26611 26612 26613 26614 26615 26616 26617 26618 26619 26620 26621 26622 26623 26624 26625 26626 26627 26628 26629 26630 26631 26632 26633 26634 26635 26636 26637 26638 26639 26640 26641 26642 26643 26644 26645 26646 26647 26648 26649 |
** as possible out of the main database (which might be corrupt) and write it
** into zNewDb.
*/
static void tryToClone(ShellState *p, const char *zNewDb){
int rc;
sqlite3 *newDb = 0;
if( access(zNewDb,0)==0 ){
sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb);
return;
}
rc = sqlite3_open(zNewDb, &newDb);
if( rc ){
sqlite3_fprintf(stderr,
"Cannot create output database: %s\n", sqlite3_errmsg(newDb));
}else{
sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0);
sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0);
tryToCloneSchema(p, newDb, "type='table'", tryToCloneData);
tryToCloneSchema(p, newDb, "type!='table'", 0);
sqlite3_exec(newDb, "COMMIT;", 0, 0, 0);
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
}
close_db(newDb);
}
#ifndef SQLITE_SHELL_FIDDLE
/*
** Change the output stream (file or pipe or console) to something else.
*/
static void output_redir(ShellState *p, FILE *pfNew){
if( p->out != stdout ){
sqlite3_fputs("Output already redirected.\n", stderr);
}else{
p->out = pfNew;
setCrlfMode(p);
if( p->mode==MODE_Www ){
sqlite3_fputs(
"<!DOCTYPE html>\n"
"<HTML><BODY><PRE>\n",
p->out
);
}
}
}
/*
** Change the output file back to stdout.
**
** If the p->doXdgOpen flag is set, that means the output was being
** redirected to a temporary file named by p->zTempFile. In that case,
** launch start/open/xdg-open on that temporary file.
*/
static void output_reset(ShellState *p){
if( p->outfile[0]=='|' ){
#ifndef SQLITE_OMIT_POPEN
pclose(p->out);
#endif
}else{
if( p->mode==MODE_Www ){
sqlite3_fputs("</PRE></BODY></HTML>\n", p->out);
}
output_file_close(p->out);
#ifndef SQLITE_NOHAVE_SYSTEM
if( p->doXdgOpen ){
const char *zXdgOpenCmd =
#if defined(_WIN32)
"start";
#elif defined(__APPLE__)
"open";
#else
"xdg-open";
#endif
char *zCmd;
zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
if( system(zCmd) ){
sqlite3_fprintf(stderr,"Failed: [%s]\n", zCmd);
}else{
/* Give the start/open/xdg-open command some time to get
** going before we continue, and potential delete the
** p->zTempFile data file out from under it */
sqlite3_sleep(2000);
}
sqlite3_free(zCmd);
outputModePop(p);
p->doXdgOpen = 0;
}
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
}
p->outfile[0] = 0;
p->out = stdout;
setCrlfMode(p);
}
#else
# define output_redir(SS,pfO)
# define output_reset(SS)
#endif
/*
** Run an SQL command and return the single integer result.
*/
static int db_int(sqlite3 *db, const char *zSql, ...){
sqlite3_stmt *pStmt;
int res = 0;
char *z;
va_list ap;
va_start(ap, zSql);
z = sqlite3_vmprintf(zSql, ap);
va_end(ap);
sqlite3_prepare_v2(db, z, -1, &pStmt, 0);
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
res = sqlite3_column_int(pStmt,0);
}
sqlite3_finalize(pStmt);
sqlite3_free(z);
return res;
}
#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
|
| ︙ | ︙ | |||
25417 25418 25419 25420 25421 25422 25423 |
unsigned char aHdr[100];
open_db(p, 0);
if( p->db==0 ) return 1;
rc = sqlite3_prepare_v2(p->db,
"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
-1, &pStmt, 0);
if( rc ){
| | | | | | | | | | | | < | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 26694 26695 26696 26697 26698 26699 26700 26701 26702 26703 26704 26705 26706 26707 26708 26709 26710 26711 26712 26713 26714 26715 26716 26717 26718 26719 26720 26721 26722 26723 26724 26725 26726 26727 26728 26729 26730 26731 26732 26733 26734 26735 26736 26737 26738 26739 26740 26741 26742 26743 26744 26745 26746 26747 26748 26749 26750 26751 26752 26753 26754 26755 26756 26757 26758 26759 26760 26761 26762 26763 26764 26765 26766 26767 26768 26769 26770 26771 26772 26773 26774 26775 26776 26777 26778 26779 26780 26781 26782 26783 26784 26785 26786 26787 26788 26789 26790 26791 26792 26793 26794 26795 26796 26797 26798 26799 26800 26801 26802 26803 26804 26805 26806 26807 26808 26809 26810 26811 26812 26813 26814 26815 26816 26817 26818 26819 26820 26821 26822 26823 26824 26825 26826 26827 26828 26829 26830 26831 26832 26833 26834 26835 26836 26837 26838 26839 26840 26841 26842 26843 26844 26845 26846 26847 26848 26849 26850 26851 26852 26853 26854 26855 26856 26857 26858 26859 |
unsigned char aHdr[100];
open_db(p, 0);
if( p->db==0 ) return 1;
rc = sqlite3_prepare_v2(p->db,
"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
-1, &pStmt, 0);
if( rc ){
sqlite3_fprintf(stderr,"error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
return 1;
}
sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
if( sqlite3_step(pStmt)==SQLITE_ROW
&& sqlite3_column_bytes(pStmt,0)>100
){
const u8 *pb = sqlite3_column_blob(pStmt,0);
shell_check_oom(pb);
memcpy(aHdr, pb, 100);
sqlite3_finalize(pStmt);
}else{
sqlite3_fputs("unable to read database header\n", stderr);
sqlite3_finalize(pStmt);
return 1;
}
i = get2byteInt(aHdr+16);
if( i==1 ) i = 65536;
sqlite3_fprintf(p->out, "%-20s %d\n", "database page size:", i);
sqlite3_fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
sqlite3_fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]);
sqlite3_fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]);
for(i=0; i<ArraySize(aField); i++){
int ofst = aField[i].ofst;
unsigned int val = get4byteInt(aHdr + ofst);
sqlite3_fprintf(p->out, "%-20s %u", aField[i].zName, val);
switch( ofst ){
case 56: {
if( val==1 ) sqlite3_fputs(" (utf8)", p->out);
if( val==2 ) sqlite3_fputs(" (utf16le)", p->out);
if( val==3 ) sqlite3_fputs(" (utf16be)", p->out);
}
}
sqlite3_fputs("\n", p->out);
}
if( zDb==0 ){
zSchemaTab = sqlite3_mprintf("main.sqlite_schema");
}else if( cli_strcmp(zDb,"temp")==0 ){
zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
}else{
zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
}
for(i=0; i<ArraySize(aQuery); i++){
int val = db_int(p->db, aQuery[i].zSql, zSchemaTab);
sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
}
sqlite3_free(zSchemaTab);
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
** Implementation of the ".dbtotxt" command.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
sqlite3_stmt *pStmt = 0;
sqlite3_int64 nPage = 0;
int pgSz = 0;
const char *zTail;
char *zName = 0;
int rc, i, j;
unsigned char bShow[256]; /* Characters ok to display */
UNUSED_PARAMETER(nArg);
UNUSED_PARAMETER(azArg);
memset(bShow, '.', sizeof(bShow));
for(i=' '; i<='~'; i++){
if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
}
rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
rc = 0;
if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
pgSz = sqlite3_column_int(pStmt, 0);
sqlite3_finalize(pStmt);
pStmt = 0;
if( pgSz<512 || pgSz>65536 || (pgSz&(pgSz-1))!=0 ) goto dbtotxt_error;
rc = sqlite3_prepare_v2(p->db, "PRAGMA page_count", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
rc = 0;
if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
nPage = sqlite3_column_int64(pStmt, 0);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nPage<1 ) goto dbtotxt_error;
rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
zTail = "unk.db";
}else{
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
zTail = strrchr(zFilename, '/');
#if defined(_WIN32)
if( zTail==0 ) zTail = strrchr(zFilename, '\\');
#endif
}
zName = strdup(zTail);
shell_check_oom(zName);
sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
nPage*pgSz, pgSz, zName);
sqlite3_finalize(pStmt);
pStmt = 0;
rc = sqlite3_prepare_v2(p->db,
"SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0);
const u8 *aData = sqlite3_column_blob(pStmt, 1);
int seenPageLabel = 0;
for(i=0; i<pgSz; i+=16){
const u8 *aLine = aData+i;
for(j=0; j<16 && aLine[j]==0; j++){}
if( j==16 ) continue;
if( !seenPageLabel ){
sqlite3_fprintf(p->out, "| page %lld offset %lld\n", pgno, pgno*pgSz);
seenPageLabel = 1;
}
sqlite3_fprintf(p->out, "| %5d:", i);
for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
sqlite3_fprintf(p->out, " ");
for(j=0; j<16; j++){
unsigned char c = (unsigned char)aLine[j];
sqlite3_fprintf(p->out, "%c", bShow[c]);
}
sqlite3_fprintf(p->out, "\n");
}
}
sqlite3_finalize(pStmt);
sqlite3_fprintf(p->out, "| end %s\n", zName);
free(zName);
return 0;
dbtotxt_error:
if( rc ){
sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db));
}
sqlite3_finalize(pStmt);
free(zName);
return 1;
}
/*
** Print the given string as an error message.
*/
static void shellEmitError(const char *zErr){
sqlite3_fprintf(stderr,"Error: %s\n", zErr);
}
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
*/
static int shellDatabaseError(sqlite3 *db){
shellEmitError(sqlite3_errmsg(db));
return 1;
|
| ︙ | ︙ | |||
25724 25725 25726 25727 25728 25729 25730 25731 25732 25733 25734 25735 25736 25737 | sqlite3 *db = pState->db; /* Database handle to query "main" db of */ int bVerbose = 0; /* If -verbose is present */ int bGroupByParent = 0; /* If -groupbyparent is present */ int i; /* To iterate through azArg[] */ const char *zIndent = ""; /* How much to indent CREATE INDEX by */ int rc; /* Return code */ sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ /* ** This SELECT statement returns one row for each foreign key constraint ** in the schema of the main database. The column values are: ** ** 0. The text of an SQL statement similar to: ** | > | 27092 27093 27094 27095 27096 27097 27098 27099 27100 27101 27102 27103 27104 27105 27106 | sqlite3 *db = pState->db; /* Database handle to query "main" db of */ int bVerbose = 0; /* If -verbose is present */ int bGroupByParent = 0; /* If -groupbyparent is present */ int i; /* To iterate through azArg[] */ const char *zIndent = ""; /* How much to indent CREATE INDEX by */ int rc; /* Return code */ sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ FILE *out = pState->out; /* Send output here */ /* ** This SELECT statement returns one row for each foreign key constraint ** in the schema of the main database. The column values are: ** ** 0. The text of an SQL statement similar to: ** |
| ︙ | ︙ | |||
25799 25800 25801 25802 25803 25804 25805 |
bVerbose = 1;
}
else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){
bGroupByParent = 1;
zIndent = " ";
}
else{
| > | | 27168 27169 27170 27171 27172 27173 27174 27175 27176 27177 27178 27179 27180 27181 27182 27183 |
bVerbose = 1;
}
else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){
bGroupByParent = 1;
zIndent = " ";
}
else{
sqlite3_fprintf(stderr,
"Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]);
return SQLITE_ERROR;
}
}
/* Register the fkey_collate_clause() SQL function */
rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8,
0, shellFkeyCollateClause, 0, 0
|
| ︙ | ︙ | |||
25843 25844 25845 25846 25847 25848 25849 |
res = zPlan!=0 && ( 0==sqlite3_strglob(zGlob, zPlan)
|| 0==sqlite3_strglob(zGlobIPK, zPlan));
}
rc = sqlite3_finalize(pExplain);
if( rc!=SQLITE_OK ) break;
if( res<0 ){
| | | | > | | | | | | | > | | 27213 27214 27215 27216 27217 27218 27219 27220 27221 27222 27223 27224 27225 27226 27227 27228 27229 27230 27231 27232 27233 27234 27235 27236 27237 27238 27239 27240 27241 27242 27243 27244 27245 27246 27247 27248 27249 27250 27251 27252 27253 27254 27255 27256 27257 27258 27259 27260 27261 27262 27263 27264 27265 27266 27267 27268 27269 27270 27271 27272 27273 27274 27275 27276 27277 27278 27279 27280 27281 27282 27283 27284 27285 27286 27287 27288 27289 27290 27291 27292 27293 27294 27295 27296 27297 27298 |
res = zPlan!=0 && ( 0==sqlite3_strglob(zGlob, zPlan)
|| 0==sqlite3_strglob(zGlobIPK, zPlan));
}
rc = sqlite3_finalize(pExplain);
if( rc!=SQLITE_OK ) break;
if( res<0 ){
sqlite3_fputs("Error: internal error", stderr);
break;
}else{
if( bGroupByParent
&& (bVerbose || res==0)
&& (zPrev==0 || sqlite3_stricmp(zParent, zPrev))
){
sqlite3_fprintf(out, "-- Parent table %s\n", zParent);
sqlite3_free(zPrev);
zPrev = sqlite3_mprintf("%s", zParent);
}
if( res==0 ){
sqlite3_fprintf(out, "%s%s --> %s\n", zIndent, zCI, zTarget);
}else if( bVerbose ){
sqlite3_fprintf(out,
"%s/* no extra indexes required for %s -> %s */\n",
zIndent, zFrom, zTarget
);
}
}
}
sqlite3_free(zPrev);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db));
}
rc2 = sqlite3_finalize(pSql);
if( rc==SQLITE_OK && rc2!=SQLITE_OK ){
rc = rc2;
sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db));
}
}else{
sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db));
}
return rc;
}
/*
** Implementation of ".lint" dot command.
*/
static int lintDotCommand(
ShellState *pState, /* Current shell tool state */
char **azArg, /* Array of arguments passed to dot command */
int nArg /* Number of entries in azArg[] */
){
int n;
n = (nArg>=2 ? strlen30(azArg[1]) : 0);
if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage;
return lintFkeyIndexes(pState, azArg, nArg);
usage:
sqlite3_fprintf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]);
sqlite3_fprintf(stderr, "Where sub-commands are:\n");
sqlite3_fprintf(stderr, " fkey-indexes\n");
return SQLITE_ERROR;
}
static void shellPrepare(
sqlite3 *db,
int *pRc,
const char *zSql,
sqlite3_stmt **ppStmt
){
*ppStmt = 0;
if( *pRc==SQLITE_OK ){
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr,
"sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
*pRc = rc;
}
}
}
/*
** Create a prepared statement using printf-style arguments for the SQL.
|
| ︙ | ︙ | |||
25956 25957 25958 25959 25960 25961 25962 |
sqlite3_stmt *pStmt
){
if( pStmt ){
sqlite3 *db = sqlite3_db_handle(pStmt);
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
| | | 27328 27329 27330 27331 27332 27333 27334 27335 27336 27337 27338 27339 27340 27341 27342 |
sqlite3_stmt *pStmt
){
if( pStmt ){
sqlite3 *db = sqlite3_db_handle(pStmt);
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
}
}
#if !defined SQLITE_OMIT_VIRTUALTABLE
|
| ︙ | ︙ | |||
25978 25979 25980 25981 25982 25983 25984 |
int *pRc,
sqlite3_stmt *pStmt
){
int rc = sqlite3_reset(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
sqlite3 *db = sqlite3_db_handle(pStmt);
| | | 27350 27351 27352 27353 27354 27355 27356 27357 27358 27359 27360 27361 27362 27363 27364 |
int *pRc,
sqlite3_stmt *pStmt
){
int rc = sqlite3_reset(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
sqlite3 *db = sqlite3_db_handle(pStmt);
sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
}
#endif /* !defined SQLITE_OMIT_VIRTUALTABLE */
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
| ︙ | ︙ | |||
26007 26008 26009 26010 26011 26012 26013 26014 26015 26016 26017 26018 26019 26020 |
u8 fromCmdLine; /* Run from -A instead of .archive */
int nArg; /* Number of command arguments */
char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
const char *zFile; /* --file argument, or NULL */
const char *zDir; /* --directory argument, or NULL */
char **azArg; /* Array of command arguments */
ShellState *p; /* Shell state */
sqlite3 *db; /* Database containing the archive */
};
/*
** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
*/
static int arUsage(FILE *f){
| > | 27379 27380 27381 27382 27383 27384 27385 27386 27387 27388 27389 27390 27391 27392 27393 |
u8 fromCmdLine; /* Run from -A instead of .archive */
int nArg; /* Number of command arguments */
char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
const char *zFile; /* --file argument, or NULL */
const char *zDir; /* --directory argument, or NULL */
char **azArg; /* Array of command arguments */
ShellState *p; /* Shell state */
FILE *out; /* Output to this stream */
sqlite3 *db; /* Database containing the archive */
};
/*
** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
*/
static int arUsage(FILE *f){
|
| ︙ | ︙ | |||
26030 26031 26032 26033 26034 26035 26036 |
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
shellEmitError(z);
if( pAr->fromCmdLine ){
| | | | 27403 27404 27405 27406 27407 27408 27409 27410 27411 27412 27413 27414 27415 27416 27417 27418 27419 |
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
shellEmitError(z);
if( pAr->fromCmdLine ){
sqlite3_fputs("Use \"-A\" for more help\n", stderr);
}else{
sqlite3_fputs("Use \".archive --help\" for more help\n", stderr);
}
sqlite3_free(z);
return SQLITE_ERROR;
}
/*
** Values for ArCommand.eCmd.
|
| ︙ | ︙ | |||
26132 26133 26134 26135 26136 26137 26138 |
{ "dryrun", 'n', AR_SWITCH_DRYRUN, 0 },
{ "glob", 'g', AR_SWITCH_GLOB, 0 },
};
int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
struct ArSwitch *pEnd = &aSwitch[nSwitch];
if( nArg<=1 ){
| | | 27505 27506 27507 27508 27509 27510 27511 27512 27513 27514 27515 27516 27517 27518 27519 |
{ "dryrun", 'n', AR_SWITCH_DRYRUN, 0 },
{ "glob", 'g', AR_SWITCH_GLOB, 0 },
};
int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
struct ArSwitch *pEnd = &aSwitch[nSwitch];
if( nArg<=1 ){
sqlite3_fprintf(stderr, "Wrong number of arguments. Usage:\n");
return arUsage(stderr);
}else{
char *z = azArg[1];
if( z[0]!='-' ){
/* Traditional style [tar] invocation */
int i;
int iArg = 2;
|
| ︙ | ︙ | |||
26238 26239 26240 26241 26242 26243 26244 |
}
if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
}
}
}
}
if( pAr->eCmd==0 ){
| | | 27611 27612 27613 27614 27615 27616 27617 27618 27619 27620 27621 27622 27623 27624 27625 |
}
if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
}
}
}
}
if( pAr->eCmd==0 ){
sqlite3_fprintf(stderr, "Required argument missing. Usage:\n");
return arUsage(stderr);
}
return SQLITE_OK;
}
/*
** This function assumes that all arguments within the ArCommand.azArg[]
|
| ︙ | ︙ | |||
26281 26282 26283 26284 26285 26286 26287 |
z[n] = '\0';
sqlite3_bind_text(pTest, j, z, -1, SQLITE_STATIC);
if( SQLITE_ROW==sqlite3_step(pTest) ){
bOk = 1;
}
shellReset(&rc, pTest);
if( rc==SQLITE_OK && bOk==0 ){
| | | 27654 27655 27656 27657 27658 27659 27660 27661 27662 27663 27664 27665 27666 27667 27668 |
z[n] = '\0';
sqlite3_bind_text(pTest, j, z, -1, SQLITE_STATIC);
if( SQLITE_ROW==sqlite3_step(pTest) ){
bOk = 1;
}
shellReset(&rc, pTest);
if( rc==SQLITE_OK && bOk==0 ){
sqlite3_fprintf(stderr,"not found in archive: %s\n", z);
rc = SQLITE_ERROR;
}
}
shellFinalize(&rc, pTest);
}
return rc;
}
|
| ︙ | ︙ | |||
26348 26349 26350 26351 26352 26353 26354 |
rc = arCheckEntries(pAr);
arWhereClause(&rc, pAr, &zWhere);
shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose],
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
| | | | | 27721 27722 27723 27724 27725 27726 27727 27728 27729 27730 27731 27732 27733 27734 27735 27736 27737 27738 27739 27740 27741 27742 27743 |
rc = arCheckEntries(pAr);
arWhereClause(&rc, pAr, &zWhere);
shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose],
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql));
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
if( pAr->bVerbose ){
sqlite3_fprintf(pAr->out, "%s % 10d %s %s\n",
sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1),
sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3));
}else{
sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0));
}
}
}
shellFinalize(&rc, pSql);
sqlite3_free(zWhere);
return rc;
}
|
| ︙ | ︙ | |||
26383 26384 26385 26386 26387 26388 26389 |
rc = arCheckEntries(pAr);
arWhereClause(&rc, pAr, &zWhere);
}
if( rc==SQLITE_OK ){
zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;",
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
| | | | 27756 27757 27758 27759 27760 27761 27762 27763 27764 27765 27766 27767 27768 27769 27770 27771 27772 27773 27774 27775 27776 27777 27778 27779 27780 27781 27782 27783 |
rc = arCheckEntries(pAr);
arWhereClause(&rc, pAr, &zWhere);
}
if( rc==SQLITE_OK ){
zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;",
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
sqlite3_fprintf(pAr->out, "%s\n", zSql);
}else{
char *zErr = 0;
rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
if( rc!=SQLITE_OK ){
sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
}else{
rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0);
}
}
if( zErr ){
sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); /* stdout? */
sqlite3_free(zErr);
}
}
}
sqlite3_free(zWhere);
sqlite3_free(zSql);
return rc;
|
| ︙ | ︙ | |||
26460 26461 26462 26463 26464 26465 26466 |
** only for the directories. This is because the timestamps for
** extracted directories must be reset after they are populated (as
** populating them changes the timestamp). */
for(i=0; i<2; i++){
j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
sqlite3_bind_int(pSql, j, i);
if( pAr->bDryRun ){
| | | | | | 27833 27834 27835 27836 27837 27838 27839 27840 27841 27842 27843 27844 27845 27846 27847 27848 27849 27850 27851 27852 27853 27854 27855 27856 27857 27858 27859 27860 27861 27862 27863 27864 27865 27866 27867 27868 27869 27870 27871 27872 27873 27874 27875 27876 27877 |
** only for the directories. This is because the timestamps for
** extracted directories must be reset after they are populated (as
** populating them changes the timestamp). */
for(i=0; i<2; i++){
j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
sqlite3_bind_int(pSql, j, i);
if( pAr->bDryRun ){
sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql));
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
if( i==0 && pAr->bVerbose ){
sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0));
}
}
}
shellReset(&rc, pSql);
}
shellFinalize(&rc, pSql);
}
sqlite3_free(zDir);
sqlite3_free(zWhere);
return rc;
}
/*
** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out.
*/
static int arExecSql(ArCommand *pAr, const char *zSql){
int rc;
if( pAr->bDryRun ){
sqlite3_fprintf(pAr->out, "%s\n", zSql);
rc = SQLITE_OK;
}else{
char *zErr = 0;
rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
if( zErr ){
sqlite3_fprintf(stdout, "ERROR: %s\n", zErr);
sqlite3_free(zErr);
}
}
return rc;
}
|
| ︙ | ︙ | |||
26639 26640 26641 26642 26643 26644 26645 26646 26647 26648 26649 26650 26651 26652 |
int rc;
memset(&cmd, 0, sizeof(cmd));
cmd.fromCmdLine = fromCmdLine;
rc = arParseCommand(azArg, nArg, &cmd);
if( rc==SQLITE_OK ){
int eDbType = SHELL_OPEN_UNSPEC;
cmd.p = pState;
cmd.db = pState->db;
if( cmd.zFile ){
eDbType = deduceDatabaseType(cmd.zFile, 1);
}else{
eDbType = pState->openMode;
}
if( eDbType==SHELL_OPEN_ZIPFILE ){
| > | 28012 28013 28014 28015 28016 28017 28018 28019 28020 28021 28022 28023 28024 28025 28026 |
int rc;
memset(&cmd, 0, sizeof(cmd));
cmd.fromCmdLine = fromCmdLine;
rc = arParseCommand(azArg, nArg, &cmd);
if( rc==SQLITE_OK ){
int eDbType = SHELL_OPEN_UNSPEC;
cmd.p = pState;
cmd.out = pState->out;
cmd.db = pState->db;
if( cmd.zFile ){
eDbType = deduceDatabaseType(cmd.zFile, 1);
}else{
eDbType = pState->openMode;
}
if( eDbType==SHELL_OPEN_ZIPFILE ){
|
| ︙ | ︙ | |||
26665 26666 26667 26668 26669 26670 26671 |
|| cmd.eCmd==AR_CMD_REMOVE || cmd.eCmd==AR_CMD_UPDATE ){
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
}else{
flags = SQLITE_OPEN_READONLY;
}
cmd.db = 0;
if( cmd.bDryRun ){
| | > | | | 28039 28040 28041 28042 28043 28044 28045 28046 28047 28048 28049 28050 28051 28052 28053 28054 28055 28056 28057 28058 28059 28060 28061 28062 28063 28064 28065 28066 28067 28068 28069 28070 28071 28072 28073 |
|| cmd.eCmd==AR_CMD_REMOVE || cmd.eCmd==AR_CMD_UPDATE ){
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
}else{
flags = SQLITE_OPEN_READONLY;
}
cmd.db = 0;
if( cmd.bDryRun ){
sqlite3_fprintf(cmd.out, "-- open database '%s'%s\n", cmd.zFile,
eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
}
rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr, "cannot open file: %s (%s)\n",
cmd.zFile, sqlite3_errmsg(cmd.db));
goto end_ar_command;
}
sqlite3_fileio_init(cmd.db, 0, 0);
sqlite3_sqlar_init(cmd.db, 0, 0);
sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p,
shellPutsFunc, 0, 0);
}
if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){
if( cmd.eCmd!=AR_CMD_CREATE
&& sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
){
sqlite3_fprintf(stderr, "database does not contain an 'sqlar' table\n");
rc = SQLITE_ERROR;
goto end_ar_command;
}
cmd.zSrcTable = sqlite3_mprintf("sqlar");
}
switch( cmd.eCmd ){
|
| ︙ | ︙ | |||
26742 26743 26744 26745 26746 26747 26748 |
/*
** This function is used as a callback by the recover extension. Simply
** print the supplied SQL statement to stdout.
*/
static int recoverSqlCb(void *pCtx, const char *zSql){
ShellState *pState = (ShellState*)pCtx;
| | | 28117 28118 28119 28120 28121 28122 28123 28124 28125 28126 28127 28128 28129 28130 28131 |
/*
** This function is used as a callback by the recover extension. Simply
** print the supplied SQL statement to stdout.
*/
static int recoverSqlCb(void *pCtx, const char *zSql){
ShellState *pState = (ShellState*)pCtx;
sqlite3_fprintf(pState->out, "%s;\n", zSql);
return SQLITE_OK;
}
/*
** This function is called to recover data from the database. A script
** to construct a new database containing all recovered data is output
** on stream pState->out.
|
| ︙ | ︙ | |||
26785 26786 26787 26788 26789 26790 26791 |
i++;
zLAF = azArg[i];
}else
if( n<=10 && memcmp("-no-rowids", z, n)==0 ){
bRowids = 0;
}
else{
| | | | 28160 28161 28162 28163 28164 28165 28166 28167 28168 28169 28170 28171 28172 28173 28174 28175 28176 28177 28178 28179 28180 28181 28182 28183 28184 28185 28186 28187 28188 28189 28190 28191 28192 28193 |
i++;
zLAF = azArg[i];
}else
if( n<=10 && memcmp("-no-rowids", z, n)==0 ){
bRowids = 0;
}
else{
sqlite3_fprintf(stderr,"unexpected option: %s\n", azArg[i]);
showHelp(pState->out, azArg[0]);
return 1;
}
}
p = sqlite3_recover_init_sql(
pState->db, "main", recoverSqlCb, (void*)pState
);
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
sqlite3_recover_run(p);
if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
const char *zErr = sqlite3_recover_errmsg(p);
int errCode = sqlite3_recover_errcode(p);
sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
return rc;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
|
| ︙ | ︙ | |||
26826 26827 26828 26829 26830 26831 26832 |
if( rc==SQLITE_OK ){
i64 nStep = 0;
i64 nError = 0;
const char *zErr = 0;
while( SQLITE_OK==sqlite3_intck_step(p) ){
const char *zMsg = sqlite3_intck_message(p);
if( zMsg ){
| | | | | 28201 28202 28203 28204 28205 28206 28207 28208 28209 28210 28211 28212 28213 28214 28215 28216 28217 28218 28219 28220 28221 28222 28223 28224 28225 28226 28227 28228 28229 |
if( rc==SQLITE_OK ){
i64 nStep = 0;
i64 nError = 0;
const char *zErr = 0;
while( SQLITE_OK==sqlite3_intck_step(p) ){
const char *zMsg = sqlite3_intck_message(p);
if( zMsg ){
sqlite3_fprintf(pState->out, "%s\n", zMsg);
nError++;
}
nStep++;
if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){
sqlite3_intck_unlock(p);
}
}
rc = sqlite3_intck_error(p, &zErr);
if( zErr ){
sqlite3_fprintf(stderr,"%s\n", zErr);
}
sqlite3_intck_close(p);
sqlite3_fprintf(pState->out, "%lld steps, %lld errors\n", nStep, nError);
}
return rc;
}
/*
* zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
|
| ︙ | ︙ | |||
26863 26864 26865 26866 26867 26868 26869 | * done if renaming was necessary, or set to 0 if none was done. The out * string (if any) must be sqlite3_free()'ed by the caller. */ #ifdef SHELL_DEBUG #define rc_err_oom_die(rc) \ if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ | | | 28238 28239 28240 28241 28242 28243 28244 28245 28246 28247 28248 28249 28250 28251 28252 |
* done if renaming was necessary, or set to 0 if none was done. The out
* string (if any) must be sqlite3_free()'ed by the caller.
*/
#ifdef SHELL_DEBUG
#define rc_err_oom_die(rc) \
if( rc==SQLITE_NOMEM ) shell_check_oom(0); \
else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \
sqlite3_fprintf(stderr,"E:%d\n",rc), assert(0)
#else
static void rc_err_oom_die(int rc){
if( rc==SQLITE_NOMEM ) shell_check_oom(0);
assert(rc==SQLITE_OK||rc==SQLITE_DONE);
}
#endif
|
| ︙ | ︙ | |||
27021 27022 27023 27024 27025 27026 27027 |
sqlite3_finalize(pStmt);
return 0;
}else if( *pDb==0 ){
return 0;
}else{
/* Formulate the columns spec, close the DB, zero *pDb. */
char *zColsSpec = 0;
| | | | | 28396 28397 28398 28399 28400 28401 28402 28403 28404 28405 28406 28407 28408 28409 28410 28411 28412 28413 28414 28415 28416 28417 28418 28419 28420 28421 28422 28423 28424 28425 28426 |
sqlite3_finalize(pStmt);
return 0;
}else if( *pDb==0 ){
return 0;
}else{
/* Formulate the columns spec, close the DB, zero *pDb. */
char *zColsSpec = 0;
int hasDupes = db_int(*pDb, "%s", zHasDupes);
int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0;
if( hasDupes ){
#ifdef SHELL_COLUMN_RENAME_CLEAN
rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
rc_err_oom_die(rc);
#endif
rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
rc_err_oom_die(rc);
rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
rc_err_oom_die(rc);
sqlite3_bind_int(pStmt, 1, nDigits);
rc = sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
}
assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */
rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
rc_err_oom_die(rc);
rc = sqlite3_step(pStmt);
if( rc==SQLITE_ROW ){
zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
}else{
zColsSpec = 0;
|
| ︙ | ︙ | |||
27080 27081 27082 27083 27084 27085 27086 |
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
shellPreparePrintf(p->db, &rc, &pStmt,
"SELECT 1 FROM sqlite_schema o WHERE "
"sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
| | | > | 28455 28456 28457 28458 28459 28460 28461 28462 28463 28464 28465 28466 28467 28468 28469 28470 28471 |
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
shellPreparePrintf(p->db, &rc, &pStmt,
"SELECT 1 FROM sqlite_schema o WHERE "
"sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
sqlite3_fputs("/* WARNING: "
"Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n",
p->out
);
}
shellFinalize(&rc, pStmt);
return rc;
}
/*
|
| ︙ | ︙ | |||
27112 27113 27114 27115 27116 27117 27118 |
static int faultsim_callback(int iArg){
if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){
return SQLITE_OK;
}
if( faultsim_state.iCnt ){
if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--;
if( faultsim_state.eVerbose>=2 ){
| > | > | | 28488 28489 28490 28491 28492 28493 28494 28495 28496 28497 28498 28499 28500 28501 28502 28503 28504 28505 28506 28507 28508 28509 |
static int faultsim_callback(int iArg){
if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){
return SQLITE_OK;
}
if( faultsim_state.iCnt ){
if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--;
if( faultsim_state.eVerbose>=2 ){
sqlite3_fprintf(stdout,
"FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt);
}
return SQLITE_OK;
}
if( faultsim_state.eVerbose>=1 ){
sqlite3_fprintf(stdout,
"FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr);
}
faultsim_state.iCnt = faultsim_state.iInterval;
faultsim_state.nHit++;
if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){
faultsim_state.iCnt = -1;
}
return faultsim_state.iErr;
|
| ︙ | ︙ | |||
27180 27181 27182 27183 27184 27185 27186 |
n = strlen30(azArg[0]);
c = azArg[0][0];
clearTempFile(p);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){
if( nArg!=2 ){
| | | 28558 28559 28560 28561 28562 28563 28564 28565 28566 28567 28568 28569 28570 28571 28572 |
n = strlen30(azArg[0]);
c = azArg[0][0];
clearTempFile(p);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){
if( nArg!=2 ){
sqlite3_fprintf(stderr, "Usage: .auth ON|OFF\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
if( booleanValue(azArg[1]) ){
sqlite3_set_authorizer(p->db, shellAuth, p);
}else if( p->bSafeModePersist ){
|
| ︙ | ︙ | |||
27227 27228 27229 27230 27231 27232 27233 |
if( cli_strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
}else
if( cli_strcmp(z, "-async")==0 ){
bAsync = 1;
}else
{
| | | | | | 28605 28606 28607 28608 28609 28610 28611 28612 28613 28614 28615 28616 28617 28618 28619 28620 28621 28622 28623 28624 28625 28626 28627 28628 28629 28630 28631 28632 28633 28634 28635 28636 28637 28638 28639 28640 |
if( cli_strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
}else
if( cli_strcmp(z, "-async")==0 ){
bAsync = 1;
}else
{
sqlite3_fprintf(stderr,"unknown option: %s\n", azArg[j]);
return 1;
}
}else if( zDestFile==0 ){
zDestFile = azArg[j];
}else if( zDb==0 ){
zDb = zDestFile;
zDestFile = azArg[j];
}else{
sqlite3_fprintf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
return 1;
}
}
if( zDestFile==0 ){
sqlite3_fprintf(stderr, "missing FILENAME argument on .backup\n");
return 1;
}
if( zDb==0 ) zDb = "main";
rc = sqlite3_open_v2(zDestFile, &pDest,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zDestFile);
close_db(pDest);
return 1;
}
if( bAsync ){
sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;",
0, 0, 0);
}
|
| ︙ | ︙ | |||
27284 27285 27286 27287 27288 27289 27290 |
bail_on_error = booleanValue(azArg[1]);
}else{
eputz("Usage: .bail on|off\n");
rc = 1;
}
}else
| | < < < < < < < | < | < | 28662 28663 28664 28665 28666 28667 28668 28669 28670 28671 28672 28673 28674 28675 28676 28677 28678 28679 |
bail_on_error = booleanValue(azArg[1]);
}else{
eputz("Usage: .bail on|off\n");
rc = 1;
}
}else
/* Undocumented. Legacy only. See "crlf" below */
if( c=='b' && n>=3 && cli_strncmp(azArg[0], "binary", n)==0 ){
eputz("The \".binary\" command is deprecated.\n");
rc = 1;
}else
/* The undocumented ".breakpoint" command causes a call to the no-op
** routine named test_breakpoint().
*/
if( c=='b' && n>=3 && cli_strncmp(azArg[0], "breakpoint", n)==0 ){
test_breakpoint();
|
| ︙ | ︙ | |||
27318 27319 27320 27321 27322 27323 27324 |
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
rc = !SetCurrentDirectoryW(z);
sqlite3_free(z);
#else
rc = chdir(azArg[1]);
#endif
if( rc ){
| | | 28687 28688 28689 28690 28691 28692 28693 28694 28695 28696 28697 28698 28699 28700 28701 |
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
rc = !SetCurrentDirectoryW(z);
sqlite3_free(z);
#else
rc = chdir(azArg[1]);
#endif
if( rc ){
sqlite3_fprintf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]);
rc = 1;
}
}else{
eputz("Usage: .cd DIRECTORY\n");
rc = 1;
}
}else
|
| ︙ | ︙ | |||
27351 27352 27353 27354 27355 27356 27357 |
output_reset(p);
if( nArg!=2 ){
eputz("Usage: .check GLOB-PATTERN\n");
rc = 2;
}else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
rc = 2;
}else if( testcase_glob(azArg[1],zRes)==0 ){
| > | | | 28720 28721 28722 28723 28724 28725 28726 28727 28728 28729 28730 28731 28732 28733 28734 28735 28736 28737 28738 28739 |
output_reset(p);
if( nArg!=2 ){
eputz("Usage: .check GLOB-PATTERN\n");
rc = 2;
}else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
rc = 2;
}else if( testcase_glob(azArg[1],zRes)==0 ){
sqlite3_fprintf(stderr,
"testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n",
p->zTestcase, azArg[1], zRes);
rc = 1;
}else{
sqlite3_fprintf(p->out, "testcase-%s ok\n", p->zTestcase);
p->nCheck++;
}
sqlite3_free(zRes);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
#ifndef SQLITE_SHELL_FIDDLE
|
| ︙ | ︙ | |||
27388 27389 27390 27391 27392 27393 27394 |
zFile = "(not open)";
}else if( zFile==0 ){
zFile = "(memory)";
}else if( zFile[0]==0 ){
zFile = "(temporary-file)";
}
if( p->pAuxDb == &p->aAuxDb[i] ){
| | | | 28758 28759 28760 28761 28762 28763 28764 28765 28766 28767 28768 28769 28770 28771 28772 28773 28774 |
zFile = "(not open)";
}else if( zFile==0 ){
zFile = "(memory)";
}else if( zFile[0]==0 ){
zFile = "(temporary-file)";
}
if( p->pAuxDb == &p->aAuxDb[i] ){
sqlite3_fprintf(stdout, "ACTIVE %d: %s\n", i, zFile);
}else if( p->aAuxDb[i].db!=0 ){
sqlite3_fprintf(stdout, " %d: %s\n", i, zFile);
}
}
}else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
int i = azArg[1][0] - '0';
if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){
p->pAuxDb->db = p->db;
p->pAuxDb = &p->aAuxDb[i];
|
| ︙ | ︙ | |||
27420 27421 27422 27423 27424 27425 27426 |
}
}else{
eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n");
rc = 1;
}
}else
| | > > > > | < | | < < < < < < > | 28790 28791 28792 28793 28794 28795 28796 28797 28798 28799 28800 28801 28802 28803 28804 28805 28806 28807 28808 28809 28810 28811 28812 28813 28814 28815 |
}
}else{
eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n");
rc = 1;
}
}else
if( c=='c' && n==4
&& (cli_strncmp(azArg[0], "crlf", n)==0
|| cli_strncmp(azArg[0], "crnl",n)==0)
){
if( nArg==2 ){
#ifdef _WIN32
p->crlfMode = booleanValue(azArg[1]);
#else
p->crlfMode = 0;
#endif
}
sqlite3_fprintf(stderr, "crlf is %s\n", p->crlfMode ? "ON" : "OFF");
}else
if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){
char **azName = 0;
int nName = 0;
sqlite3_stmt *pStmt;
int i;
|
| ︙ | ︙ | |||
27463 27464 27465 27466 27467 27468 27469 |
}
}
sqlite3_finalize(pStmt);
for(i=0; i<nName; i++){
int eTxn = sqlite3_txn_state(p->db, azName[i*2]);
int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]);
const char *z = azName[i*2+1];
| | | 28831 28832 28833 28834 28835 28836 28837 28838 28839 28840 28841 28842 28843 28844 28845 |
}
}
sqlite3_finalize(pStmt);
for(i=0; i<nName; i++){
int eTxn = sqlite3_txn_state(p->db, azName[i*2]);
int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]);
const char *z = azName[i*2+1];
sqlite3_fprintf(p->out, "%s: %s %s%s\n",
azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w",
eTxn==SQLITE_TXN_NONE ? "" :
eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn");
free(azName[i*2]);
free(azName[i*2+1]);
}
sqlite3_free(azName);
|
| ︙ | ︙ | |||
27505 27506 27507 27508 27509 27510 27511 |
open_db(p, 0);
for(ii=0; ii<ArraySize(aDbConfig); ii++){
if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
if( nArg>=3 ){
sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
}
sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v);
| > | | | 28873 28874 28875 28876 28877 28878 28879 28880 28881 28882 28883 28884 28885 28886 28887 28888 28889 28890 28891 28892 |
open_db(p, 0);
for(ii=0; ii<ArraySize(aDbConfig); ii++){
if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
if( nArg>=3 ){
sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
}
sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v);
sqlite3_fprintf(p->out, "%19s %s\n",
aDbConfig[ii].zName, v ? "on" : "off");
if( nArg>1 ) break;
}
if( nArg>1 && ii==ArraySize(aDbConfig) ){
sqlite3_fprintf(stderr,"Error: unknown dbconfig \"%s\"\n", azArg[1]);
eputz("Enter \".dbconfig\" with no arguments for a list\n");
}
}else
#if SQLITE_SHELL_HAVE_RECOVER
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbinfo", n)==0 ){
rc = shell_dbinfo_command(p, nArg, azArg);
|
| ︙ | ︙ | |||
27559 27560 27561 27562 27563 27564 27565 |
if( cli_strcmp(z,"data-only")==0 ){
ShellSetFlag(p, SHFLG_DumpDataOnly);
}else
if( cli_strcmp(z,"nosys")==0 ){
ShellSetFlag(p, SHFLG_DumpNoSys);
}else
{
| > | | 28928 28929 28930 28931 28932 28933 28934 28935 28936 28937 28938 28939 28940 28941 28942 28943 |
if( cli_strcmp(z,"data-only")==0 ){
ShellSetFlag(p, SHFLG_DumpDataOnly);
}else
if( cli_strcmp(z,"nosys")==0 ){
ShellSetFlag(p, SHFLG_DumpNoSys);
}else
{
sqlite3_fprintf(stderr,
"Unknown option \"%s\" on \".dump\"\n", azArg[i]);
rc = 1;
sqlite3_free(zLike);
goto meta_command_exit;
}
}else{
/* azArg[i] contains a LIKE pattern. This ".dump" request should
** only dump data for tables for which either the table name matches
|
| ︙ | ︙ | |||
27594 27595 27596 27597 27598 27599 27600 |
open_db(p, 0);
outputDumpWarning(p, zLike);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
| | | | 28964 28965 28966 28967 28968 28969 28970 28971 28972 28973 28974 28975 28976 28977 28978 28979 |
open_db(p, 0);
outputDumpWarning(p, zLike);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
sqlite3_fputs("PRAGMA foreign_keys=OFF;\n", p->out);
sqlite3_fputs("BEGIN TRANSACTION;\n", p->out);
}
p->writableSchema = 0;
p->showHeader = 0;
/* Set writable_schema=ON since doing so forces SQLite to initialize
** as much of the schema as it can even if the sqlite_schema table is
** corrupt. */
sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
|
| ︙ | ︙ | |||
27627 27628 27629 27630 27631 27632 27633 |
zLike
);
run_table_dump_query(p, zSql);
sqlite3_free(zSql);
}
sqlite3_free(zLike);
if( p->writableSchema ){
| | | > > > > | 28997 28998 28999 29000 29001 29002 29003 29004 29005 29006 29007 29008 29009 29010 29011 29012 29013 29014 29015 29016 29017 29018 29019 29020 29021 29022 29023 29024 29025 29026 29027 29028 29029 29030 29031 29032 29033 29034 |
zLike
);
run_table_dump_query(p, zSql);
sqlite3_free(zSql);
}
sqlite3_free(zLike);
if( p->writableSchema ){
sqlite3_fputs("PRAGMA writable_schema=OFF;\n", p->out);
p->writableSchema = 0;
}
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
sqlite3_fputs(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out);
}
p->showHeader = savedShowHeader;
p->shellFlgs = savedShellFlags;
}else
if( c=='e' && cli_strncmp(azArg[0], "echo", n)==0 ){
if( nArg==2 ){
setOrClearFlag(p, SHFLG_Echo, azArg[1]);
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
}
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( p->autoEQPtrace ){
if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0);
p->autoEQPtrace = 0;
|
| ︙ | ︙ | |||
27713 27714 27715 27716 27717 27718 27719 |
p->autoExplain = 1;
}
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
if( p->bSafeMode ){
| > | | 29087 29088 29089 29090 29091 29092 29093 29094 29095 29096 29097 29098 29099 29100 29101 29102 |
p->autoExplain = 1;
}
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
if( p->bSafeMode ){
sqlite3_fprintf(stderr,
"Cannot run experimental commands such as \"%s\" in safe mode\n",
azArg[0]);
rc = 1;
}else{
open_db(p, 0);
expertDotCommand(p, azArg, nArg);
}
}else
|
| ︙ | ︙ | |||
27770 27771 27772 27773 27774 27775 27776 |
if( zCmd[0]=='-' && zCmd[1] ){
zCmd++;
if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
}
/* --help lists all file-controls */
if( cli_strcmp(zCmd,"help")==0 ){
| | > | | | | 29145 29146 29147 29148 29149 29150 29151 29152 29153 29154 29155 29156 29157 29158 29159 29160 29161 29162 29163 29164 29165 29166 29167 29168 29169 29170 29171 29172 29173 29174 29175 29176 29177 29178 29179 29180 29181 29182 29183 29184 29185 |
if( zCmd[0]=='-' && zCmd[1] ){
zCmd++;
if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
}
/* --help lists all file-controls */
if( cli_strcmp(zCmd,"help")==0 ){
sqlite3_fputs("Available file-controls:\n", p->out);
for(i=0; i<ArraySize(aCtrl); i++){
sqlite3_fprintf(p->out,
" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage);
}
rc = 1;
goto meta_command_exit;
}
/* convert filectrl text option to value. allow any unique prefix
** of the option name, or a numerical value. */
n2 = strlen30(zCmd);
for(i=0; i<ArraySize(aCtrl); i++){
if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
if( filectrl<0 ){
filectrl = aCtrl[i].ctrlCode;
iCtrl = i;
}else{
sqlite3_fprintf(stderr,"Error: ambiguous file-control: \"%s\"\n"
"Use \".filectrl --help\" for help\n", zCmd);
rc = 1;
goto meta_command_exit;
}
}
}
if( filectrl<0 ){
sqlite3_fprintf(stderr,"Error: unknown file-control: %s\n"
"Use \".filectrl --help\" for help\n", zCmd);
}else{
switch(filectrl){
case SQLITE_FCNTL_SIZE_LIMIT: {
if( nArg!=2 && nArg!=3 ) break;
iRes = nArg==3 ? integerValue(azArg[2]) : -1;
sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes);
|
| ︙ | ︙ | |||
27839 27840 27841 27842 27843 27844 27845 |
break;
}
case SQLITE_FCNTL_TEMPFILENAME: {
char *z = 0;
if( nArg!=2 ) break;
sqlite3_file_control(p->db, zSchema, filectrl, &z);
if( z ){
| | | | > | | 29215 29216 29217 29218 29219 29220 29221 29222 29223 29224 29225 29226 29227 29228 29229 29230 29231 29232 29233 29234 29235 29236 29237 29238 29239 29240 29241 29242 29243 29244 29245 29246 29247 29248 29249 29250 29251 29252 29253 29254 29255 29256 |
break;
}
case SQLITE_FCNTL_TEMPFILENAME: {
char *z = 0;
if( nArg!=2 ) break;
sqlite3_file_control(p->db, zSchema, filectrl, &z);
if( z ){
sqlite3_fprintf(p->out, "%s\n", z);
sqlite3_free(z);
}
isOk = 2;
break;
}
case SQLITE_FCNTL_RESERVE_BYTES: {
int x;
if( nArg>=3 ){
x = atoi(azArg[2]);
sqlite3_file_control(p->db, zSchema, filectrl, &x);
}
x = -1;
sqlite3_file_control(p->db, zSchema, filectrl, &x);
sqlite3_fprintf(p->out, "%d\n", x);
isOk = 2;
break;
}
}
}
if( isOk==0 && iCtrl>=0 ){
sqlite3_fprintf(p->out, "Usage: .filectrl %s %s\n",
zCmd, aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
char zBuf[100];
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes);
sqlite3_fprintf(p->out, "%s\n", zBuf);
}
}else
if( c=='f' && cli_strncmp(azArg[0], "fullschema", n)==0 ){
ShellState data;
int doStats = 0;
memcpy(&data, p, sizeof(data));
|
| ︙ | ︙ | |||
27906 27907 27908 27909 27910 27911 27912 |
-1, &pStmt, 0);
if( rc==SQLITE_OK ){
doStats = sqlite3_step(pStmt)==SQLITE_ROW;
sqlite3_finalize(pStmt);
}
}
if( doStats==0 ){
| | | | | | 29283 29284 29285 29286 29287 29288 29289 29290 29291 29292 29293 29294 29295 29296 29297 29298 29299 29300 29301 29302 29303 29304 29305 29306 29307 29308 29309 29310 29311 29312 29313 29314 29315 29316 29317 29318 29319 29320 29321 29322 29323 |
-1, &pStmt, 0);
if( rc==SQLITE_OK ){
doStats = sqlite3_step(pStmt)==SQLITE_ROW;
sqlite3_finalize(pStmt);
}
}
if( doStats==0 ){
sqlite3_fputs("/* No STAT tables available */\n", p->out);
}else{
sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
data.cMode = data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1";
shell_exec(&data, "SELECT * FROM sqlite_stat1", 0);
data.zDestTable = "sqlite_stat4";
shell_exec(&data, "SELECT * FROM sqlite_stat4", 0);
sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
}
}else
if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){
if( nArg==2 ){
p->showHeader = booleanValue(azArg[1]);
p->shellFlgs |= SHFLG_HeaderSet;
}else{
eputz("Usage: .headers on|off\n");
rc = 1;
}
}else
if( c=='h' && cli_strncmp(azArg[0], "help", n)==0 ){
if( nArg>=2 ){
n = showHelp(p->out, azArg[1]);
if( n==0 ){
sqlite3_fprintf(p->out, "Nothing matches '%s'\n", azArg[1]);
}
}else{
showHelp(p->out, 0);
}
}else
#ifndef SQLITE_SHELL_FIDDLE
|
| ︙ | ︙ | |||
27975 27976 27977 27978 27979 27980 27981 |
if( z[0]=='-' && z[1]=='-' ) z++;
if( z[0]!='-' ){
if( zFile==0 ){
zFile = z;
}else if( zTable==0 ){
zTable = z;
}else{
| | | | | 29352 29353 29354 29355 29356 29357 29358 29359 29360 29361 29362 29363 29364 29365 29366 29367 29368 29369 29370 29371 29372 29373 29374 29375 29376 29377 29378 29379 29380 29381 29382 29383 29384 29385 29386 29387 29388 29389 29390 29391 29392 29393 |
if( z[0]=='-' && z[1]=='-' ) z++;
if( z[0]!='-' ){
if( zFile==0 ){
zFile = z;
}else if( zTable==0 ){
zTable = z;
}else{
sqlite3_fprintf(p->out, "ERROR: extra argument: \"%s\". Usage:\n",z);
showHelp(p->out, "import");
goto meta_command_exit;
}
}else if( cli_strcmp(z,"-v")==0 ){
eVerbose++;
}else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){
zSchema = azArg[++i];
}else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){
nSkip = integerValue(azArg[++i]);
}else if( cli_strcmp(z,"-ascii")==0 ){
sCtx.cColSep = SEP_Unit[0];
sCtx.cRowSep = SEP_Record[0];
xRead = ascii_read_one_field;
useOutputMode = 0;
}else if( cli_strcmp(z,"-csv")==0 ){
sCtx.cColSep = ',';
sCtx.cRowSep = '\n';
xRead = csv_read_one_field;
useOutputMode = 0;
}else{
sqlite3_fprintf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z);
showHelp(p->out, "import");
goto meta_command_exit;
}
}
if( zTable==0 ){
sqlite3_fprintf(p->out, "ERROR: missing %s argument. Usage:\n",
zFile==0 ? "FILE" : "TABLE");
showHelp(p->out, "import");
goto meta_command_exit;
}
seenInterrupt = 0;
open_db(p, 0);
if( useOutputMode ){
|
| ︙ | ︙ | |||
28052 28053 28054 28055 28056 28057 28058 |
sCtx.zFile = zFile;
sCtx.nLine = 1;
if( sCtx.zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
goto meta_command_exit;
#else
| | | | | | | | | | > > > > | | | > > > > < | 29429 29430 29431 29432 29433 29434 29435 29436 29437 29438 29439 29440 29441 29442 29443 29444 29445 29446 29447 29448 29449 29450 29451 29452 29453 29454 29455 29456 29457 29458 29459 29460 29461 29462 29463 29464 29465 29466 29467 29468 29469 29470 29471 29472 29473 29474 29475 29476 29477 29478 29479 29480 29481 29482 29483 29484 29485 29486 29487 29488 29489 29490 29491 29492 29493 29494 29495 29496 29497 29498 29499 29500 29501 29502 29503 29504 29505 29506 29507 29508 29509 29510 29511 29512 29513 29514 29515 29516 29517 29518 29519 29520 29521 |
sCtx.zFile = zFile;
sCtx.nLine = 1;
if( sCtx.zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
goto meta_command_exit;
#else
sCtx.in = sqlite3_popen(sCtx.zFile+1, "r");
sCtx.zFile = "<pipe>";
sCtx.xCloser = pclose;
#endif
}else{
sCtx.in = sqlite3_fopen(sCtx.zFile, "rb");
sCtx.xCloser = fclose;
}
if( sCtx.in==0 ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile);
goto meta_command_exit;
}
if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
char zSep[2];
zSep[1] = 0;
zSep[0] = sCtx.cColSep;
sqlite3_fputs("Column separator ", p->out);
output_c_string(p->out, zSep);
sqlite3_fputs(", row separator ", p->out);
zSep[0] = sCtx.cRowSep;
output_c_string(p->out, zSep);
sqlite3_fputs("\n", p->out);
}
sCtx.z = sqlite3_malloc64(120);
if( sCtx.z==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
/* Below, resources must be freed before exit. */
while( (nSkip--)>0 ){
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
}
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
&& 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
" WHERE name=%Q AND type='view'",
zSchema ? zSchema : "main", zTable)
){
/* Table does not exist. Create it. */
sqlite3 *dbCols = 0;
char *zRenames = 0;
char *zColDefs;
zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
zSchema ? zSchema : "main", zTable);
while( xRead(&sCtx) ){
zAutoColumn(sCtx.z, &dbCols, 0);
if( sCtx.cTerm!=sCtx.cColSep ) break;
}
zColDefs = zAutoColumn(0, &dbCols, &zRenames);
if( zRenames!=0 ){
sqlite3_fprintf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
"Columns renamed during .import %s due to duplicates:\n"
"%s\n", sCtx.zFile, zRenames);
sqlite3_free(zRenames);
}
assert(dbCols==0);
if( zColDefs==0 ){
sqlite3_fprintf(stderr,"%s: empty file\n", sCtx.zFile);
import_cleanup(&sCtx);
rc = 1;
sqlite3_free(zCreate);
goto meta_command_exit;
}
zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
if( zCreate==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
if( eVerbose>=1 ){
sqlite3_fprintf(p->out, "%s\n", zCreate);
}
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
if( rc ){
sqlite3_fprintf(stderr,
"%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
}
sqlite3_free(zCreate);
zCreate = 0;
if( rc ){
import_cleanup(&sCtx);
rc = 1;
goto meta_command_exit;
}
}
zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
zTable, zSchema);
|
| ︙ | ︙ | |||
28178 28179 28180 28181 28182 28183 28184 |
zSql[j++] = ',';
zSql[j++] = '?';
}
zSql[j++] = ')';
zSql[j] = 0;
assert( j<nByte );
if( eVerbose>=2 ){
| | | 29562 29563 29564 29565 29566 29567 29568 29569 29570 29571 29572 29573 29574 29575 29576 |
zSql[j++] = ',';
zSql[j++] = '?';
}
zSql[j++] = ')';
zSql[j] = 0;
assert( j<nByte );
if( eVerbose>=2 ){
sqlite3_fprintf(p->out, "Insert using: %s\n", zSql);
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
zSql = 0;
if( rc ){
shellDatabaseError(p->db);
if (pStmt) sqlite3_finalize(pStmt);
|
| ︙ | ︙ | |||
28217 28218 28219 28220 28221 28222 28223 |
** (If there are too few fields, it's not valid CSV anyway.)
*/
if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
z = "";
}
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
| | > | | > | | | 29601 29602 29603 29604 29605 29606 29607 29608 29609 29610 29611 29612 29613 29614 29615 29616 29617 29618 29619 29620 29621 29622 29623 29624 29625 29626 29627 29628 29629 29630 29631 29632 29633 29634 29635 29636 29637 29638 29639 29640 29641 29642 29643 29644 29645 29646 29647 29648 29649 29650 29651 29652 29653 29654 29655 29656 29657 29658 29659 29660 29661 29662 29663 29664 29665 |
** (If there are too few fields, it's not valid CSV anyway.)
*/
if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
z = "";
}
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
sqlite3_fprintf(stderr,"%s:%d: expected %d columns but found %d"
" - filling the rest with NULL\n",
sCtx.zFile, startLine, nCol, i+1);
i += 2;
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
}
}
if( sCtx.cTerm==sCtx.cColSep ){
do{
xRead(&sCtx);
i++;
}while( sCtx.cTerm==sCtx.cColSep );
sqlite3_fprintf(stderr,
"%s:%d: expected %d columns but found %d - extras ignored\n",
sCtx.zFile, startLine, nCol, i);
}
if( i>=nCol ){
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr,"%s:%d: INSERT failed: %s\n",
sCtx.zFile, startLine, sqlite3_errmsg(p->db));
sCtx.nErr++;
}else{
sCtx.nRow++;
}
}
}while( sCtx.cTerm!=EOF );
import_cleanup(&sCtx);
sqlite3_finalize(pStmt);
if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
if( eVerbose>0 ){
sqlite3_fprintf(p->out,
"Added %d rows with %d errors using %d lines of input\n",
sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
}
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
#ifndef SQLITE_UNTESTABLE
if( c=='i' && cli_strncmp(azArg[0], "imposter", n)==0 ){
char *zSql;
char *zCollist = 0;
sqlite3_stmt *pStmt;
int tnum = 0;
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
if( !ShellHasFlag(p,SHFLG_TestingMode) ){
sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n",
"imposter");
rc = 1;
goto meta_command_exit;
}
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
eputz("Usage: .imposter INDEX IMPOSTER\n"
" .imposter off\n");
|
| ︙ | ︙ | |||
28331 28332 28333 28334 28335 28336 28337 |
zCollist = sqlite3_mprintf("\"%w\"", zCol);
}else{
zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol);
}
}
sqlite3_finalize(pStmt);
if( i==0 || tnum==0 ){
| | > | | > | | | | | | 29717 29718 29719 29720 29721 29722 29723 29724 29725 29726 29727 29728 29729 29730 29731 29732 29733 29734 29735 29736 29737 29738 29739 29740 29741 29742 29743 29744 29745 29746 29747 29748 29749 29750 29751 29752 29753 29754 29755 29756 29757 29758 29759 29760 29761 29762 29763 29764 29765 29766 29767 29768 29769 29770 29771 29772 29773 29774 29775 29776 29777 29778 29779 29780 29781 29782 29783 29784 29785 29786 29787 29788 29789 29790 |
zCollist = sqlite3_mprintf("\"%w\"", zCol);
}else{
zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol);
}
}
sqlite3_finalize(pStmt);
if( i==0 || tnum==0 ){
sqlite3_fprintf(stderr,"no such index: \"%s\"\n", azArg[1]);
rc = 1;
sqlite3_free(zCollist);
goto meta_command_exit;
}
if( lenPK==0 ) lenPK = 100000;
zSql = sqlite3_mprintf(
"CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
azArg[2], zCollist, lenPK, zCollist);
sqlite3_free(zCollist);
rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
if( rc ){
sqlite3_fprintf(stderr,
"Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
}else{
sqlite3_fprintf(stdout, "%s;\n", zSql);
sqlite3_fprintf(stdout,
"WARNING: writing to an imposter table will corrupt"
" the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index");
}
}else{
sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1;
}
sqlite3_free(zSql);
}else
#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */
if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){
i64 iArg = 0;
if( nArg==2 ){
iArg = integerValue(azArg[1]);
if( iArg==0 ) iArg = -1;
}
if( (nArg!=1 && nArg!=2) || iArg<0 ){
sqlite3_fprintf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
rc = intckDatabaseCmd(p, iArg);
}else
#ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){
SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...);
if( iotrace && iotrace!=stdout ) fclose(iotrace);
iotrace = 0;
if( nArg<2 ){
sqlite3IoTrace = 0;
}else if( cli_strcmp(azArg[1], "-")==0 ){
sqlite3IoTrace = iotracePrintf;
iotrace = stdout;
}else{
iotrace = sqlite3_fopen(azArg[1], "w");
if( iotrace==0 ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
sqlite3IoTrace = 0;
rc = 1;
}else{
sqlite3IoTrace = iotracePrintf;
}
}
}else
|
| ︙ | ︙ | |||
28420 28421 28422 28423 28424 28425 28426 |
{ "trigger_depth", SQLITE_LIMIT_TRIGGER_DEPTH },
{ "worker_threads", SQLITE_LIMIT_WORKER_THREADS },
};
int i, n2;
open_db(p, 0);
if( nArg==1 ){
for(i=0; i<ArraySize(aLimit); i++){
| | | | | | 29808 29809 29810 29811 29812 29813 29814 29815 29816 29817 29818 29819 29820 29821 29822 29823 29824 29825 29826 29827 29828 29829 29830 29831 29832 29833 29834 29835 29836 29837 29838 29839 29840 29841 29842 29843 29844 29845 29846 29847 29848 29849 29850 29851 29852 29853 29854 |
{ "trigger_depth", SQLITE_LIMIT_TRIGGER_DEPTH },
{ "worker_threads", SQLITE_LIMIT_WORKER_THREADS },
};
int i, n2;
open_db(p, 0);
if( nArg==1 ){
for(i=0; i<ArraySize(aLimit); i++){
sqlite3_fprintf(stdout, "%20s %d\n", aLimit[i].zLimitName,
sqlite3_limit(p->db, aLimit[i].limitCode, -1));
}
}else if( nArg>3 ){
eputz("Usage: .limit NAME ?NEW-VALUE?\n");
rc = 1;
goto meta_command_exit;
}else{
int iLimit = -1;
n2 = strlen30(azArg[1]);
for(i=0; i<ArraySize(aLimit); i++){
if( sqlite3_strnicmp(aLimit[i].zLimitName, azArg[1], n2)==0 ){
if( iLimit<0 ){
iLimit = i;
}else{
sqlite3_fprintf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]);
rc = 1;
goto meta_command_exit;
}
}
}
if( iLimit<0 ){
sqlite3_fprintf(stderr,"unknown limit: \"%s\"\n"
"enter \".limits\" with no arguments for a list.\n",
azArg[1]);
rc = 1;
goto meta_command_exit;
}
if( nArg==3 ){
sqlite3_limit(p->db, aLimit[iLimit].limitCode,
(int)integerValue(azArg[2]));
}
sqlite3_fprintf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName,
sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1));
}
}else
if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){
open_db(p, 0);
lintDotCommand(p, azArg, nArg);
|
| ︙ | ︙ | |||
28501 28502 28503 28504 28505 28506 28507 |
){
sputz(stdout, "cannot set .log to anything other"
" than \"on\" or \"off\"\n");
zFile = "off";
}
output_file_close(p->pLog);
if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout";
| | | 29889 29890 29891 29892 29893 29894 29895 29896 29897 29898 29899 29900 29901 29902 29903 |
){
sputz(stdout, "cannot set .log to anything other"
" than \"on\" or \"off\"\n");
zFile = "off";
}
output_file_close(p->pLog);
if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout";
p->pLog = output_file_open(zFile);
}
}else
if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
const char *zMode = 0;
const char *zTabname = 0;
int i, n2;
|
| ︙ | ︙ | |||
28535 28536 28537 28538 28539 28540 28541 |
ColModeOpts cmo = ColModeOpts_default_qbox;
zMode = "box";
cmOpts = cmo;
}
}else if( zTabname==0 ){
zTabname = z;
}else if( z[0]=='-' ){
| | | > | > | | 29923 29924 29925 29926 29927 29928 29929 29930 29931 29932 29933 29934 29935 29936 29937 29938 29939 29940 29941 29942 29943 29944 29945 29946 29947 29948 29949 29950 29951 29952 29953 29954 29955 29956 29957 29958 29959 29960 29961 29962 29963 |
ColModeOpts cmo = ColModeOpts_default_qbox;
zMode = "box";
cmOpts = cmo;
}
}else if( zTabname==0 ){
zTabname = z;
}else if( z[0]=='-' ){
sqlite3_fprintf(stderr,"unknown option: %s\n", z);
eputz("options:\n"
" --noquote\n"
" --quote\n"
" --wordwrap on/off\n"
" --wrap N\n"
" --ww\n");
rc = 1;
goto meta_command_exit;
}else{
sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}
}
if( zMode==0 ){
if( p->mode==MODE_Column
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
){
sqlite3_fprintf(p->out,
"current output mode: %s --wrap %d --wordwrap %s --%squote\n",
modeDescr[p->mode], p->cmOpts.iWrap,
p->cmOpts.bWordWrap ? "on" : "off",
p->cmOpts.bQuote ? "" : "no");
}else{
sqlite3_fprintf(p->out,
"current output mode: %s\n", modeDescr[p->mode]);
}
zMode = modeDescr[p->mode];
}
n2 = strlen30(zMode);
if( cli_strncmp(zMode,"lines",n2)==0 ){
p->mode = MODE_Line;
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
| ︙ | ︙ | |||
28632 28633 28634 28635 28636 28637 28638 |
#ifndef SQLITE_SHELL_FIDDLE
if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){
if( nArg!=2 ){
eputz("Usage: .nonce NONCE\n");
rc = 1;
}else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){
| | | 30022 30023 30024 30025 30026 30027 30028 30029 30030 30031 30032 30033 30034 30035 30036 |
#ifndef SQLITE_SHELL_FIDDLE
if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){
if( nArg!=2 ){
eputz("Usage: .nonce NONCE\n");
rc = 1;
}else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){
sqlite3_fprintf(stderr,"line %d: incorrect nonce: \"%s\"\n",
p->lineno, azArg[1]);
exit(1);
}else{
p->bSafeMode = 0;
return 0; /* Return immediately to bypass the safe mode reset
** at the end of this procedure */
}
|
| ︙ | ︙ | |||
28687 28688 28689 28690 28691 28692 28693 |
openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_OMIT_DESERIALIZE */
}else
#endif /* !SQLITE_SHELL_FIDDLE */
if( z[0]=='-' ){
| | | | 30077 30078 30079 30080 30081 30082 30083 30084 30085 30086 30087 30088 30089 30090 30091 30092 30093 30094 30095 |
openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_OMIT_DESERIALIZE */
}else
#endif /* !SQLITE_SHELL_FIDDLE */
if( z[0]=='-' ){
sqlite3_fprintf(stderr,"unknown option: %s\n", z);
rc = 1;
goto meta_command_exit;
}else if( zFN ){
sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}else{
zFN = z;
}
}
|
| ︙ | ︙ | |||
28733 28734 28735 28736 28737 28738 28739 |
shell_check_oom(zNewFilename);
}else{
zNewFilename = 0;
}
p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
| | > < | > | > > > > > | | > > > | | > | | > > > > > > > > < | | | | | | > > > > > > | 30123 30124 30125 30126 30127 30128 30129 30130 30131 30132 30133 30134 30135 30136 30137 30138 30139 30140 30141 30142 30143 30144 30145 30146 30147 30148 30149 30150 30151 30152 30153 30154 30155 30156 30157 30158 30159 30160 30161 30162 30163 30164 30165 30166 30167 30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 30183 30184 30185 30186 30187 30188 30189 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 30209 30210 30211 30212 30213 30214 30215 30216 30217 30218 30219 30220 30221 30222 30223 30224 30225 30226 30227 30228 30229 30230 30231 30232 30233 30234 30235 30236 30237 30238 30239 30240 30241 30242 30243 30244 30245 30246 30247 30248 30249 30250 30251 30252 30253 30254 30255 30256 30257 30258 30259 30260 30261 30262 30263 30264 30265 30266 30267 30268 30269 30270 30271 30272 30273 30274 30275 30276 30277 30278 30279 |
shell_check_oom(zNewFilename);
}else{
zNewFilename = 0;
}
p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
sqlite3_fprintf(stderr,"Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
p->pAuxDb->zFreeOnClose = zNewFilename;
}
}
if( p->db==0 ){
/* As a fall-back open a TEMP database */
p->pAuxDb->zDbFilename = 0;
open_db(p, 0);
}
}else
#ifndef SQLITE_SHELL_FIDDLE
if( (c=='o'
&& (cli_strncmp(azArg[0], "output", n)==0
|| cli_strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0)
|| (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0)
){
char *zFile = 0;
int i;
int eMode = 0;
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */
int bPlain = 0; /* --plain option */
static const char *zBomUtf8 = "\357\273\277";
const char *zBom = 0;
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
bOnce = 2;
}else if( c=='w' ){
eMode = 'w';
bOnce = 2;
}else if( cli_strncmp(azArg[0],"once",n)==0 ){
bOnce = 1;
}
for(i=1; i<nArg; i++){
char *z = azArg[i];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( cli_strcmp(z,"-bom")==0 ){
zBom = zBomUtf8;
}else if( cli_strcmp(z,"-plain")==0 ){
bPlain = 1;
}else if( c=='o' && cli_strcmp(z,"-x")==0 ){
eMode = 'x'; /* spreadsheet */
}else if( c=='o' && cli_strcmp(z,"-e")==0 ){
eMode = 'e'; /* text editor */
}else if( c=='o' && cli_strcmp(z,"-w")==0 ){
eMode = 'w'; /* Web browser */
}else{
sqlite3_fprintf(p->out,
"ERROR: unknown option: \"%s\". Usage:\n", azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
goto meta_command_exit;
}
}else if( zFile==0 && eMode==0 ){
zFile = sqlite3_mprintf("%s", z);
if( zFile && zFile[0]=='|' ){
while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]);
break;
}
}else{
sqlite3_fprintf(p->out,
"ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
sqlite3_free(zFile);
goto meta_command_exit;
}
}
if( zFile==0 ){
zFile = sqlite3_mprintf("stdout");
}
if( bOnce ){
p->outCount = 2;
}else{
p->outCount = 0;
}
output_reset(p);
#ifndef SQLITE_NOHAVE_SYSTEM
if( eMode=='e' || eMode=='x' || eMode=='w' ){
p->doXdgOpen = 1;
outputModePush(p);
if( eMode=='x' ){
/* spreadsheet mode. Output as CSV. */
newTempFile(p, "csv");
ShellClearFlag(p, SHFLG_Echo);
p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
#ifdef _WIN32
zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does
** not work without it. */
#endif
}else if( eMode=='w' ){
/* web-browser mode. */
newTempFile(p, "html");
if( !bPlain ) p->mode = MODE_Www;
}else{
/* text editor mode */
newTempFile(p, "txt");
}
sqlite3_free(zFile);
zFile = sqlite3_mprintf("%s", p->zTempFile);
}
#endif /* SQLITE_NOHAVE_SYSTEM */
shell_check_oom(zFile);
if( zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
rc = 1;
output_redir(p, stdout);
#else
FILE *pfPipe = sqlite3_popen(zFile + 1, "w");
if( pfPipe==0 ){
sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
rc = 1;
}else{
output_redir(p, pfPipe);
if( zBom ) sqlite3_fputs(zBom, pfPipe);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
#endif
}else{
FILE *pfFile = output_file_open(zFile);
if( pfFile==0 ){
if( cli_strcmp(zFile,"off")!=0 ){
sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile);
}
rc = 1;
} else {
output_redir(p, pfFile);
if( zBom ) sqlite3_fputs(zBom, pfFile);
if( bPlain && eMode=='w' ){
sqlite3_fputs(
"<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n",
pfFile
);
}
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
sqlite3_free(zFile);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
|
| ︙ | ︙ | |||
28893 28894 28895 28896 28897 28898 28899 |
sqlite3_finalize(pStmt);
pStmt = 0;
if( len ){
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
| > | | 30306 30307 30308 30309 30310 30311 30312 30313 30314 30315 30316 30317 30318 30319 30320 30321 |
sqlite3_finalize(pStmt);
pStmt = 0;
if( len ){
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
sqlite3_fprintf(p->out,
"%-*s %s\n", len, sqlite3_column_text(pStmt,0),
sqlite3_column_text(pStmt,1));
}
sqlite3_finalize(pStmt);
}
}else
/* .parameter init
|
| ︙ | ︙ | |||
28938 28939 28940 28941 28942 28943 28944 |
zSql = sqlite3_mprintf(
"REPLACE INTO temp.sqlite_parameters(key,value)"
"VALUES(%Q,%Q);", zKey, zValue);
shell_check_oom(zSql);
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
| | | 30352 30353 30354 30355 30356 30357 30358 30359 30360 30361 30362 30363 30364 30365 30366 |
zSql = sqlite3_mprintf(
"REPLACE INTO temp.sqlite_parameters(key,value)"
"VALUES(%Q,%Q);", zKey, zValue);
shell_check_oom(zSql);
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
pStmt = 0;
rc = 1;
}
}
sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
|
| ︙ | ︙ | |||
28967 28968 28969 28970 28971 28972 28973 |
parameter_syntax_error:
showHelp(p->out, "parameter");
}else
if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){
int i;
for(i=1; i<nArg; i++){
| | | | | 30381 30382 30383 30384 30385 30386 30387 30388 30389 30390 30391 30392 30393 30394 30395 30396 30397 30398 |
parameter_syntax_error:
showHelp(p->out, "parameter");
}else
if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){
int i;
for(i=1; i<nArg; i++){
if( i>1 ) sqlite3_fputs(" ", p->out);
sqlite3_fputs(azArg[i], p->out);
}
sqlite3_fputs("\n", p->out);
}else
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( c=='p' && n>=3 && cli_strncmp(azArg[0], "progress", n)==0 ){
int i;
int nn = 0;
p->flgProgress = 0;
|
| ︙ | ︙ | |||
29007 29008 29009 29010 29011 29012 29013 |
rc = 1;
goto meta_command_exit;
}else{
p->mxProgress = (int)integerValue(azArg[++i]);
}
continue;
}
| | | 30421 30422 30423 30424 30425 30426 30427 30428 30429 30430 30431 30432 30433 30434 30435 |
rc = 1;
goto meta_command_exit;
}else{
p->mxProgress = (int)integerValue(azArg[++i]);
}
continue;
}
sqlite3_fprintf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]);
rc = 1;
goto meta_command_exit;
}else{
nn = (int)integerValue(z);
}
}
open_db(p, 0);
|
| ︙ | ︙ | |||
29048 29049 29050 29051 29052 29053 29054 |
rc = 1;
goto meta_command_exit;
}
if( azArg[1][0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
rc = 1;
| < | | | | 30462 30463 30464 30465 30466 30467 30468 30469 30470 30471 30472 30473 30474 30475 30476 30477 30478 30479 30480 30481 30482 30483 30484 30485 30486 30487 |
rc = 1;
goto meta_command_exit;
}
if( azArg[1][0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
rc = 1;
#else
p->in = sqlite3_popen(azArg[1]+1, "r");
if( p->in==0 ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
pclose(p->in);
}
#endif
}else if( (p->in = openChrSource(azArg[1]))==0 ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
fclose(p->in);
}
p->in = inSaved;
p->lineno = savedLineno;
|
| ︙ | ︙ | |||
29093 29094 29095 29096 29097 29098 29099 |
}else{
eputz("Usage: .restore ?DB? FILE\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
| | | 30506 30507 30508 30509 30510 30511 30512 30513 30514 30515 30516 30517 30518 30519 30520 |
}else{
eputz("Usage: .restore ?DB? FILE\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zSrcFile);
close_db(pSrc);
return 1;
}
open_db(p, 0);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
shellDatabaseError(p->db);
|
| ︙ | ︙ | |||
29125 29126 29127 29128 29129 29130 29131 |
shellDatabaseError(p->db);
rc = 1;
}
close_db(pSrc);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
| > | > > | 30538 30539 30540 30541 30542 30543 30544 30545 30546 30547 30548 30549 30550 30551 30552 30553 30554 30555 |
shellDatabaseError(p->db);
rc = 1;
}
close_db(pSrc);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
if( c=='s' &&
(cli_strncmp(azArg[0], "scanstats", n)==0 ||
cli_strncmp(azArg[0], "scanstatus", n)==0)
){
if( nArg==2 ){
if( cli_strcmp(azArg[1], "vm")==0 ){
p->scanstatsOn = 3;
}else
if( cli_strcmp(azArg[1], "est")==0 ){
p->scanstatsOn = 2;
}else{
|
| ︙ | ︙ | |||
29176 29177 29178 29179 29180 29181 29182 |
if( optionMatch(azArg[ii],"indent") ){
data.cMode = data.mode = MODE_Pretty;
}else if( optionMatch(azArg[ii],"debug") ){
bDebug = 1;
}else if( optionMatch(azArg[ii],"nosys") ){
bNoSystemTabs = 1;
}else if( azArg[ii][0]=='-' ){
| | | 30592 30593 30594 30595 30596 30597 30598 30599 30600 30601 30602 30603 30604 30605 30606 |
if( optionMatch(azArg[ii],"indent") ){
data.cMode = data.mode = MODE_Pretty;
}else if( optionMatch(azArg[ii],"debug") ){
bDebug = 1;
}else if( optionMatch(azArg[ii],"nosys") ){
bNoSystemTabs = 1;
}else if( azArg[ii][0]=='-' ){
sqlite3_fprintf(stderr,"Unknown option: \"%s\"\n", azArg[ii]);
rc = 1;
goto meta_command_exit;
}else if( zName==0 ){
zName = azArg[ii];
}else{
eputz("Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n");
rc = 1;
|
| ︙ | ︙ | |||
29277 29278 29279 29280 29281 29282 29283 |
}
if( bNoSystemTabs ){
appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0);
}
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
if( bDebug ){
| | | 30693 30694 30695 30696 30697 30698 30699 30700 30701 30702 30703 30704 30705 30706 30707 |
}
if( bNoSystemTabs ){
appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0);
}
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
if( bDebug ){
sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z);
}else{
rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
}
freeText(&sSelect);
}
if( zErrMsg ){
shellEmitError(zErrMsg);
|
| ︙ | ︙ | |||
29338 29339 29340 29341 29342 29343 29344 |
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ){
session_not_open:
eputz("ERROR: No sessions are open\n");
}else{
rc = sqlite3session_attach(pSession->p, azCmd[1]);
if( rc ){
| > | | | | > | | 30754 30755 30756 30757 30758 30759 30760 30761 30762 30763 30764 30765 30766 30767 30768 30769 30770 30771 30772 30773 30774 30775 30776 30777 30778 30779 30780 30781 30782 30783 30784 30785 30786 30787 30788 30789 30790 30791 30792 30793 30794 30795 30796 30797 30798 30799 30800 30801 30802 30803 30804 30805 |
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ){
session_not_open:
eputz("ERROR: No sessions are open\n");
}else{
rc = sqlite3session_attach(pSession->p, azCmd[1]);
if( rc ){
sqlite3_fprintf(stderr,
"ERROR: sqlite3session_attach() returns %d\n",rc);
rc = 0;
}
}
}else
/* .session changeset FILE
** .session patchset FILE
** Write a changeset or patchset into a file. The file is overwritten.
*/
if( cli_strcmp(azCmd[0],"changeset")==0
|| cli_strcmp(azCmd[0],"patchset")==0
){
FILE *out = 0;
failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]);
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = sqlite3_fopen(azCmd[1], "wb");
if( out==0 ){
sqlite3_fprintf(stderr,"ERROR: cannot open \"%s\" for writing\n",
azCmd[1]);
}else{
int szChng;
void *pChng;
if( azCmd[0][0]=='c' ){
rc = sqlite3session_changeset(pSession->p, &szChng, &pChng);
}else{
rc = sqlite3session_patchset(pSession->p, &szChng, &pChng);
}
if( rc ){
sqlite3_fprintf(stdout, "Error: error code %d\n", rc);
rc = 0;
}
if( pChng
&& fwrite(pChng, szChng, 1, out)!=1 ){
sqlite3_fprintf(stderr,
"ERROR: Failed to write entire %d-byte output\n", szChng);
}
sqlite3_free(pChng);
fclose(out);
}
}else
/* .session close
|
| ︙ | ︙ | |||
29400 29401 29402 29403 29404 29405 29406 |
*/
if( cli_strcmp(azCmd[0], "enable")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
| > | | 30818 30819 30820 30821 30822 30823 30824 30825 30826 30827 30828 30829 30830 30831 30832 30833 |
*/
if( cli_strcmp(azCmd[0], "enable")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
sqlite3_fprintf(p->out,
"session %s enable flag = %d\n", pSession->zName, ii);
}
}else
/* .session filter GLOB ....
** Set a list of GLOB patterns of table names to be excluded.
*/
if( cli_strcmp(azCmd[0], "filter")==0 ){
|
| ︙ | ︙ | |||
29435 29436 29437 29438 29439 29440 29441 |
*/
if( cli_strcmp(azCmd[0], "indirect")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
| > | > | | | > | | | 30854 30855 30856 30857 30858 30859 30860 30861 30862 30863 30864 30865 30866 30867 30868 30869 30870 30871 30872 30873 30874 30875 30876 30877 30878 30879 30880 30881 30882 30883 30884 30885 30886 30887 30888 30889 30890 30891 30892 30893 30894 30895 30896 30897 30898 30899 30900 30901 30902 30903 30904 30905 30906 30907 30908 30909 30910 30911 30912 30913 30914 30915 30916 30917 30918 |
*/
if( cli_strcmp(azCmd[0], "indirect")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
sqlite3_fprintf(p->out,
"session %s indirect flag = %d\n", pSession->zName, ii);
}
}else
/* .session isempty
** Determine if the session is empty
*/
if( cli_strcmp(azCmd[0], "isempty")==0 ){
int ii;
if( nCmd!=1 ) goto session_syntax_error;
if( pAuxDb->nSession ){
ii = sqlite3session_isempty(pSession->p);
sqlite3_fprintf(p->out,
"session %s isempty flag = %d\n", pSession->zName, ii);
}
}else
/* .session list
** List all currently open sessions
*/
if( cli_strcmp(azCmd[0],"list")==0 ){
for(i=0; i<pAuxDb->nSession; i++){
sqlite3_fprintf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
}
}else
/* .session open DB NAME
** Open a new session called NAME on the attached database DB.
** DB is normally "main".
*/
if( cli_strcmp(azCmd[0],"open")==0 ){
char *zName;
if( nCmd!=3 ) goto session_syntax_error;
zName = azCmd[2];
if( zName[0]==0 ) goto session_syntax_error;
for(i=0; i<pAuxDb->nSession; i++){
if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
sqlite3_fprintf(stderr,"Session \"%s\" already exists\n", zName);
goto meta_command_exit;
}
}
if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
sqlite3_fprintf(stderr,
"Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
goto meta_command_exit;
}
pSession = &pAuxDb->aSession[pAuxDb->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){
sqlite3_fprintf(stderr,"Cannot open session: error code=%d\n", rc);
rc = 0;
goto meta_command_exit;
}
pSession->nFilter = 0;
sqlite3session_table_filter(pSession->p, session_filter, pSession);
pAuxDb->nSession++;
pSession->zName = sqlite3_mprintf("%s", zName);
|
| ︙ | ︙ | |||
29506 29507 29508 29509 29510 29511 29512 |
/* Undocumented commands for internal testing. Subject to change
** without notice. */
if( c=='s' && n>=10 && cli_strncmp(azArg[0], "selftest-", 9)==0 ){
if( cli_strncmp(azArg[0]+9, "boolean", n-9)==0 ){
int i, v;
for(i=1; i<nArg; i++){
v = booleanValue(azArg[i]);
| | | | 30928 30929 30930 30931 30932 30933 30934 30935 30936 30937 30938 30939 30940 30941 30942 30943 30944 30945 30946 30947 30948 30949 30950 30951 |
/* Undocumented commands for internal testing. Subject to change
** without notice. */
if( c=='s' && n>=10 && cli_strncmp(azArg[0], "selftest-", 9)==0 ){
if( cli_strncmp(azArg[0]+9, "boolean", n-9)==0 ){
int i, v;
for(i=1; i<nArg; i++){
v = booleanValue(azArg[i]);
sqlite3_fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v);
}
}
if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){
int i; sqlite3_int64 v;
for(i=1; i<nArg; i++){
char zBuf[200];
v = integerValue(azArg[i]);
sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
sqlite3_fputs(zBuf, p->out);
}
}
}else
#endif
if( c=='s' && n>=4 && cli_strncmp(azArg[0],"selftest",n)==0 ){
int bIsInit = 0; /* True to initialize the SELFTEST table */
|
| ︙ | ︙ | |||
29542 29543 29544 29545 29546 29547 29548 |
if( cli_strcmp(z,"-init")==0 ){
bIsInit = 1;
}else
if( cli_strcmp(z,"-v")==0 ){
bVerbose++;
}else
{
| > | | | 30964 30965 30966 30967 30968 30969 30970 30971 30972 30973 30974 30975 30976 30977 30978 30979 30980 |
if( cli_strcmp(z,"-init")==0 ){
bIsInit = 1;
}else
if( cli_strcmp(z,"-v")==0 ){
bVerbose++;
}else
{
sqlite3_fprintf(stderr,
"Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
sqlite3_fputs("Should be one of: --init -v\n", stderr);
rc = 1;
goto meta_command_exit;
}
}
if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0)
!= SQLITE_OK ){
bSelftestExists = 0;
|
| ︙ | ︙ | |||
29588 29589 29590 29591 29592 29593 29594 |
const char *zAns = (const char*)sqlite3_column_text(pStmt, 3);
if( zOp==0 ) continue;
if( zSql==0 ) continue;
if( zAns==0 ) continue;
k = 0;
if( bVerbose>0 ){
| | | | | | | > | | | 31011 31012 31013 31014 31015 31016 31017 31018 31019 31020 31021 31022 31023 31024 31025 31026 31027 31028 31029 31030 31031 31032 31033 31034 31035 31036 31037 31038 31039 31040 31041 31042 31043 31044 31045 31046 31047 31048 31049 31050 31051 31052 31053 31054 31055 31056 31057 31058 31059 31060 31061 |
const char *zAns = (const char*)sqlite3_column_text(pStmt, 3);
if( zOp==0 ) continue;
if( zSql==0 ) continue;
if( zAns==0 ) continue;
k = 0;
if( bVerbose>0 ){
sqlite3_fprintf(stdout, "%d: %s %s\n", tno, zOp, zSql);
}
if( cli_strcmp(zOp,"memo")==0 ){
sqlite3_fprintf(p->out, "%s\n", zSql);
}else
if( cli_strcmp(zOp,"run")==0 ){
char *zErrMsg = 0;
str.n = 0;
str.z[0] = 0;
rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
nTest++;
if( bVerbose ){
sqlite3_fprintf(p->out, "Result: %s\n", str.z);
}
if( rc || zErrMsg ){
nErr++;
rc = 1;
sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
sqlite3_free(zErrMsg);
}else if( cli_strcmp(zAns,str.z)!=0 ){
nErr++;
rc = 1;
sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns);
sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z);
}
}
else{
sqlite3_fprintf(stderr,
"Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
rc = 1;
break;
}
} /* End loop over rows of content from SELFTEST */
sqlite3_finalize(pStmt);
} /* End loop over k */
freeText(&str);
sqlite3_fprintf(p->out, "%d errors out of %d tests\n", nErr, nTest);
}else
if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){
if( nArg<2 || nArg>3 ){
eputz("Usage: .separator COL ?ROW?\n");
rc = 1;
}
|
| ︙ | ︙ | |||
29671 29672 29673 29674 29675 29676 29677 |
){
iSize = atoi(&z[5]);
}else
if( cli_strcmp(z,"debug")==0 ){
bDebug = 1;
}else
{
| > | | 31095 31096 31097 31098 31099 31100 31101 31102 31103 31104 31105 31106 31107 31108 31109 31110 |
){
iSize = atoi(&z[5]);
}else
if( cli_strcmp(z,"debug")==0 ){
bDebug = 1;
}else
{
sqlite3_fprintf(stderr,
"Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
showHelp(p->out, azArg[0]);
rc = 1;
goto meta_command_exit;
}
}else if( zLike ){
eputz("Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n");
rc = 1;
|
| ︙ | ︙ | |||
29749 29750 29751 29752 29753 29754 29755 |
" FROM [sha3sum$query]",
sSql.z, iSize);
}
shell_check_oom(zSql);
freeText(&sQuery);
freeText(&sSql);
if( bDebug ){
| | | 31174 31175 31176 31177 31178 31179 31180 31181 31182 31183 31184 31185 31186 31187 31188 |
" FROM [sha3sum$query]",
sSql.z, iSize);
}
shell_check_oom(zSql);
freeText(&sQuery);
freeText(&sSql);
if( bDebug ){
sqlite3_fprintf(p->out, "%s\n", zSql);
}else{
shell_exec(p, zSql, 0);
}
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE)
{
int lrc;
char *zRevText = /* Query for reversible to-blob-to-text check */
|
| ︙ | ︙ | |||
29779 29780 29781 29782 29783 29784 29785 |
" from (select 'SELECT COUNT(*) AS bad_text_count\n"
"FROM '||tname||' WHERE '\n"
"||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n"
"|| ' AND typeof('||cname||')=''text'' ',\n"
"' OR ') as query, tname from tabcols group by tname)"
, zRevText);
shell_check_oom(zRevText);
| | | > | | 31204 31205 31206 31207 31208 31209 31210 31211 31212 31213 31214 31215 31216 31217 31218 31219 31220 31221 31222 31223 31224 31225 31226 31227 31228 31229 31230 31231 31232 31233 31234 31235 31236 31237 31238 31239 31240 |
" from (select 'SELECT COUNT(*) AS bad_text_count\n"
"FROM '||tname||' WHERE '\n"
"||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n"
"|| ' AND typeof('||cname||')=''text'' ',\n"
"' OR ') as query, tname from tabcols group by tname)"
, zRevText);
shell_check_oom(zRevText);
if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zRevText);
lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
if( lrc!=SQLITE_OK ){
/* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the
** user does cruel and unnatural things like ".limit expr_depth 0". */
rc = 1;
}else{
if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC);
lrc = SQLITE_ROW==sqlite3_step(pStmt);
if( lrc ){
const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
sqlite3_stmt *pCheckStmt;
lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0);
if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zGenQuery);
if( lrc!=SQLITE_OK ){
rc = 1;
}else{
if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){
double countIrreversible = sqlite3_column_double(pCheckStmt, 0);
if( countIrreversible>0 ){
int sz = (int)(countIrreversible + 0.5);
sqlite3_fprintf(stderr,
"Digest includes %d invalidly encoded text field%s.\n",
sz, (sz>1)? "s": "");
}
}
sqlite3_finalize(pCheckStmt);
}
sqlite3_finalize(pStmt);
}
|
| ︙ | ︙ | |||
29834 29835 29836 29837 29838 29839 29840 |
goto meta_command_exit;
}
zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
for(i=2; i<nArg && zCmd!=0; i++){
zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
zCmd, azArg[i]);
}
| | | | | | | > | > | | | | | | | | | | | | | | | | | | 31260 31261 31262 31263 31264 31265 31266 31267 31268 31269 31270 31271 31272 31273 31274 31275 31276 31277 31278 31279 31280 31281 31282 31283 31284 31285 31286 31287 31288 31289 31290 31291 31292 31293 31294 31295 31296 31297 31298 31299 31300 31301 31302 31303 31304 31305 31306 31307 31308 31309 31310 31311 31312 31313 31314 31315 31316 31317 31318 31319 31320 31321 31322 31323 31324 31325 31326 31327 31328 31329 31330 31331 31332 |
goto meta_command_exit;
}
zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
for(i=2; i<nArg && zCmd!=0; i++){
zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
zCmd, azArg[i]);
}
/*consoleRestore();*/
x = zCmd!=0 ? system(zCmd) : 1;
/*consoleRenewSetup();*/
sqlite3_free(zCmd);
if( x ) sqlite3_fprintf(stderr,"System command returns %d\n", x);
}else
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */
if( c=='s' && cli_strncmp(azArg[0], "show", n)==0 ){
static const char *azBool[] = { "off", "on", "trigger", "full"};
const char *zOut;
int i;
if( nArg!=1 ){
eputz("Usage: .show\n");
rc = 1;
goto meta_command_exit;
}
sqlite3_fprintf(p->out, "%12.12s: %s\n","echo",
azBool[ShellHasFlag(p, SHFLG_Echo)]);
sqlite3_fprintf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]);
sqlite3_fprintf(p->out, "%12.12s: %s\n","explain",
p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off");
sqlite3_fprintf(p->out, "%12.12s: %s\n","headers",
azBool[p->showHeader!=0]);
if( p->mode==MODE_Column
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
){
sqlite3_fprintf(p->out,
"%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
modeDescr[p->mode], p->cmOpts.iWrap,
p->cmOpts.bWordWrap ? "on" : "off",
p->cmOpts.bQuote ? "" : "no");
}else{
sqlite3_fprintf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
}
sqlite3_fprintf(p->out, "%12.12s: ", "nullvalue");
output_c_string(p->out, p->nullValue);
sqlite3_fputs("\n", p->out);
sqlite3_fprintf(p->out, "%12.12s: %s\n","output",
strlen30(p->outfile) ? p->outfile : "stdout");
sqlite3_fprintf(p->out, "%12.12s: ", "colseparator");
output_c_string(p->out, p->colSeparator);
sqlite3_fputs("\n", p->out);
sqlite3_fprintf(p->out, "%12.12s: ", "rowseparator");
output_c_string(p->out, p->rowSeparator);
sqlite3_fputs("\n", p->out);
switch( p->statsOn ){
case 0: zOut = "off"; break;
default: zOut = "on"; break;
case 2: zOut = "stmt"; break;
case 3: zOut = "vmstep"; break;
}
sqlite3_fprintf(p->out, "%12.12s: %s\n","stats", zOut);
sqlite3_fprintf(p->out, "%12.12s: ", "width");
for (i=0;i<p->nWidth;i++) {
sqlite3_fprintf(p->out, "%d ", p->colWidth[i]);
}
sqlite3_fputs("\n", p->out);
sqlite3_fprintf(p->out, "%12.12s: %s\n", "filename",
p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
}else
if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){
if( nArg==2 ){
if( cli_strcmp(azArg[1],"stmt")==0 ){
p->statsOn = 2;
|
| ︙ | ︙ | |||
30008 30009 30010 30011 30012 30013 30014 |
}
nPrintCol = 80/(maxlen+2);
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){
for(j=i; j<nRow; j+=nPrintRow){
char *zSp = j<nPrintRow ? "" : " ";
| > | | | | 31436 31437 31438 31439 31440 31441 31442 31443 31444 31445 31446 31447 31448 31449 31450 31451 31452 31453 31454 31455 31456 31457 31458 31459 31460 31461 31462 31463 31464 31465 |
}
nPrintCol = 80/(maxlen+2);
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){
for(j=i; j<nRow; j+=nPrintRow){
char *zSp = j<nPrintRow ? "" : " ";
sqlite3_fprintf(p->out,
"%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:"");
}
sqlite3_fputs("\n", p->out);
}
}
for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
sqlite3_free(azResult);
}else
#ifndef SQLITE_SHELL_FIDDLE
/* Begin redirecting output to the file "testcase-out.txt" */
if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){
output_reset(p);
p->out = output_file_open("testcase-out.txt");
if( p->out==0 ){
eputz("Error: cannot open 'testcase-out.txt'\n");
}
if( nArg>=2 ){
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]);
}else{
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?");
|
| ︙ | ︙ | |||
30066 30067 30068 30069 30070 30071 30072 |
{"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,1, "OFFSET " },
{"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" },
{"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" },
{"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" },
{"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
{"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
{"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
| < | | | | | 31495 31496 31497 31498 31499 31500 31501 31502 31503 31504 31505 31506 31507 31508 31509 31510 31511 31512 31513 31514 31515 31516 31517 31518 31519 31520 31521 31522 31523 31524 31525 31526 31527 31528 31529 31530 31531 31532 31533 31534 31535 31536 31537 31538 31539 31540 31541 31542 31543 31544 31545 31546 31547 31548 31549 31550 31551 31552 31553 31554 31555 31556 |
{"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,1, "OFFSET " },
{"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" },
{"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" },
{"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" },
{"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
{"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
{"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
};
int testctrl = -1;
int iCtrl = -1;
int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
int isOk = 0;
int i, n2;
const char *zCmd = 0;
open_db(p, 0);
zCmd = nArg>=2 ? azArg[1] : "help";
/* The argument can optionally begin with "-" or "--" */
if( zCmd[0]=='-' && zCmd[1] ){
zCmd++;
if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
}
/* --help lists all test-controls */
if( cli_strcmp(zCmd,"help")==0 ){
sqlite3_fputs("Available test-controls:\n", p->out);
for(i=0; i<ArraySize(aCtrl); i++){
if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue;
sqlite3_fprintf(p->out, " .testctrl %s %s\n",
aCtrl[i].zCtrlName, aCtrl[i].zUsage);
}
rc = 1;
goto meta_command_exit;
}
/* convert testctrl text option to value. allow any unique prefix
** of the option name, or a numerical value. */
n2 = strlen30(zCmd);
for(i=0; i<ArraySize(aCtrl); i++){
if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue;
if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
if( testctrl<0 ){
testctrl = aCtrl[i].ctrlCode;
iCtrl = i;
}else{
sqlite3_fprintf(stderr,"Error: ambiguous test-control: \"%s\"\n"
"Use \".testctrl --help\" for help\n", zCmd);
rc = 1;
goto meta_command_exit;
}
}
}
if( testctrl<0 ){
sqlite3_fprintf(stderr,"Error: unknown test-control: %s\n"
"Use \".testctrl --help\" for help\n", zCmd);
}else{
switch(testctrl){
/* Special processing for .testctrl opt MASK ...
** Each MASK argument can be one of:
**
|
| ︙ | ︙ | |||
30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 |
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
unsigned int newOpt;
int ii;
sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
newOpt = curOpt;
for(ii=2; ii<nArg; ii++){
const char *z = azArg[ii];
int useLabel = 0;
const char *zLabel = 0;
if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){
| > > | 31596 31597 31598 31599 31600 31601 31602 31603 31604 31605 31606 31607 31608 31609 31610 31611 31612 |
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
unsigned int newOpt;
unsigned int m;
int ii;
int nOff;
sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
newOpt = curOpt;
for(ii=2; ii<nArg; ii++){
const char *z = azArg[ii];
int useLabel = 0;
const char *zLabel = 0;
if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){
|
| ︙ | ︙ | |||
30190 30191 30192 30193 30194 30195 30196 |
}
if( useLabel ){
int jj;
for(jj=0; jj<ArraySize(aLabel); jj++){
if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break;
}
if( jj>=ArraySize(aLabel) ){
| > | | | | < > > | | | > > | < > > > < > | < | < | | > | 31620 31621 31622 31623 31624 31625 31626 31627 31628 31629 31630 31631 31632 31633 31634 31635 31636 31637 31638 31639 31640 31641 31642 31643 31644 31645 31646 31647 31648 31649 31650 31651 31652 31653 31654 31655 31656 31657 31658 31659 31660 31661 31662 31663 31664 31665 31666 31667 31668 31669 31670 31671 31672 31673 31674 |
}
if( useLabel ){
int jj;
for(jj=0; jj<ArraySize(aLabel); jj++){
if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break;
}
if( jj>=ArraySize(aLabel) ){
sqlite3_fprintf(stderr,
"Error: no such optimization: \"%s\"\n", zLabel);
sqlite3_fputs("Should be one of:", stderr);
for(jj=0; jj<ArraySize(aLabel); jj++){
sqlite3_fprintf(stderr," %s", aLabel[jj].zLabel);
}
sqlite3_fputs("\n", stderr);
rc = 1;
goto meta_command_exit;
}
if( useLabel=='+' ){
newOpt &= ~aLabel[jj].mask;
}else{
newOpt |= aLabel[jj].mask;
}
}
}
if( curOpt!=newOpt ){
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
}
for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){
if( m & newOpt ) nOff++;
}
if( nOff<12 ){
sqlite3_fputs("+All", p->out);
for(ii=0; ii<ArraySize(aLabel); ii++){
if( !aLabel[ii].bDsply ) continue;
if( (newOpt & aLabel[ii].mask)!=0 ){
sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel);
}
}
}else{
sqlite3_fputs("-All", p->out);
for(ii=0; ii<ArraySize(aLabel); ii++){
if( !aLabel[ii].bDsply ) continue;
if( (newOpt & aLabel[ii].mask)==0 ){
sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel);
}
}
}
sqlite3_fputs("\n", p->out);
rc2 = isOk = 3;
break;
}
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_FK_NO_ACTION:
if( nArg==3 ){
|
| ︙ | ︙ | |||
30265 30266 30267 30268 30269 30270 30271 |
/* sqlite3_test_control(int, int, sqlite3*) */
case SQLITE_TESTCTRL_PRNG_SEED:
if( nArg==3 || nArg==4 ){
int ii = (int)integerValue(azArg[2]);
sqlite3 *db;
if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){
sqlite3_randomness(sizeof(ii),&ii);
| | | 31700 31701 31702 31703 31704 31705 31706 31707 31708 31709 31710 31711 31712 31713 31714 |
/* sqlite3_test_control(int, int, sqlite3*) */
case SQLITE_TESTCTRL_PRNG_SEED:
if( nArg==3 || nArg==4 ){
int ii = (int)integerValue(azArg[2]);
sqlite3 *db;
if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){
sqlite3_randomness(sizeof(ii),&ii);
sqlite3_fprintf(stdout, "-- random seed: %d\n", ii);
}
if( nArg==3 ){
db = 0;
}else{
db = p->db;
/* Make sure the schema has been loaded */
sqlite3_table_column_metadata(db, 0, "x", 0, 0, 0, 0, 0, 0);
|
| ︙ | ︙ | |||
30299 30300 30301 30302 30303 30304 30305 |
if( nArg==3 ){
int opt = booleanValue(azArg[2]);
rc2 = sqlite3_test_control(testctrl, opt);
isOk = 3;
}
break;
| < < < < < < < < < < < < < < < | | 31734 31735 31736 31737 31738 31739 31740 31741 31742 31743 31744 31745 31746 31747 31748 31749 31750 31751 31752 31753 31754 31755 31756 31757 31758 31759 31760 31761 31762 31763 31764 31765 31766 31767 |
if( nArg==3 ){
int opt = booleanValue(azArg[2]);
rc2 = sqlite3_test_control(testctrl, opt);
isOk = 3;
}
break;
/* sqlite3_test_control(sqlite3*) */
case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS:
rc2 = sqlite3_test_control(testctrl, p->db);
isOk = 3;
break;
case SQLITE_TESTCTRL_IMPOSTER:
if( nArg==5 ){
rc2 = sqlite3_test_control(testctrl, p->db,
azArg[2],
integerValue(azArg[3]),
integerValue(azArg[4]));
isOk = 3;
}
break;
case SQLITE_TESTCTRL_SEEK_COUNT: {
u64 x = 0;
rc2 = sqlite3_test_control(testctrl, p->db, &x);
sqlite3_fprintf(p->out, "%llu\n", x);
isOk = 3;
break;
}
#ifdef YYCOVERAGE
case SQLITE_TESTCTRL_PARSER_COVERAGE: {
if( nArg==2 ){
|
| ︙ | ︙ | |||
30364 30365 30366 30367 30368 30369 30370 |
isOk = 1;
}else if( nArg==2 ){
int id = 1;
while(1){
int val = 0;
rc2 = sqlite3_test_control(testctrl, -id, &val);
if( rc2!=SQLITE_OK ) break;
| | | | | 31784 31785 31786 31787 31788 31789 31790 31791 31792 31793 31794 31795 31796 31797 31798 31799 31800 31801 31802 |
isOk = 1;
}else if( nArg==2 ){
int id = 1;
while(1){
int val = 0;
rc2 = sqlite3_test_control(testctrl, -id, &val);
if( rc2!=SQLITE_OK ) break;
if( id>1 ) sqlite3_fputs(" ", p->out);
sqlite3_fprintf(p->out, "%d: %d", id, val);
id++;
}
if( id>1 ) sqlite3_fputs("\n", p->out);
isOk = 3;
}
break;
}
#endif
case SQLITE_TESTCTRL_SORTER_MMAP:
if( nArg==3 ){
|
| ︙ | ︙ | |||
30410 30411 30412 30413 30414 30415 30416 |
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"reset")==0 ){
faultsim_state.iCnt = faultsim_state.nSkip;
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"status")==0 ){
| > | > | > | > | > | > | > | > | > | | > > | | | | 31830 31831 31832 31833 31834 31835 31836 31837 31838 31839 31840 31841 31842 31843 31844 31845 31846 31847 31848 31849 31850 31851 31852 31853 31854 31855 31856 31857 31858 31859 31860 31861 31862 31863 31864 31865 31866 31867 31868 31869 31870 31871 31872 31873 31874 31875 31876 31877 31878 31879 31880 31881 31882 31883 31884 31885 31886 31887 31888 31889 31890 31891 31892 31893 31894 31895 31896 31897 31898 31899 31900 31901 31902 31903 31904 31905 31906 31907 31908 31909 31910 31911 31912 31913 31914 |
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"reset")==0 ){
faultsim_state.iCnt = faultsim_state.nSkip;
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"status")==0 ){
sqlite3_fprintf(p->out, "faultsim.iId: %d\n",
faultsim_state.iId);
sqlite3_fprintf(p->out, "faultsim.iErr: %d\n",
faultsim_state.iErr);
sqlite3_fprintf(p->out, "faultsim.iCnt: %d\n",
faultsim_state.iCnt);
sqlite3_fprintf(p->out, "faultsim.nHit: %d\n",
faultsim_state.nHit);
sqlite3_fprintf(p->out, "faultsim.iInterval: %d\n",
faultsim_state.iInterval);
sqlite3_fprintf(p->out, "faultsim.eVerbose: %d\n",
faultsim_state.eVerbose);
sqlite3_fprintf(p->out, "faultsim.nRepeat: %d\n",
faultsim_state.nRepeat);
sqlite3_fprintf(p->out, "faultsim.nSkip: %d\n",
faultsim_state.nSkip);
}else if( cli_strcmp(z,"-v")==0 ){
if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++;
}else if( cli_strcmp(z,"-q")==0 ){
if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--;
}else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){
faultsim_state.iId = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-errcode")==0 && kk+1<nArg ){
faultsim_state.iErr = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-interval")==0 && kk+1<nArg ){
faultsim_state.iInterval = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-repeat")==0 && kk+1<nArg ){
faultsim_state.nRepeat = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){
faultsim_state.nSkip = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){
bShowHelp = 1;
}else{
sqlite3_fprintf(stderr,
"Unrecognized fault_install argument: \"%s\"\n",
azArg[kk]);
rc = 1;
bShowHelp = 1;
break;
}
}
if( bShowHelp ){
sqlite3_fputs(
"Usage: .testctrl fault_install ARGS\n"
"Possible arguments:\n"
" off Disable faultsim\n"
" on Activate faultsim\n"
" reset Reset the trigger counter\n"
" status Show current status\n"
" -v Increase verbosity\n"
" -q Decrease verbosity\n"
" --errcode N When triggered, return N as error code\n"
" --id ID Trigger only for the ID specified\n"
" --interval N Trigger only after every N-th call\n"
" --repeat N Turn off after N hits. 0 means never\n"
" --skip N Skip the first N encounters\n"
,p->out
);
}
break;
}
}
}
if( isOk==0 && iCtrl>=0 ){
sqlite3_fprintf(p->out,
"Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
sqlite3_fprintf(p->out, "%d\n", rc2);
}else if( isOk==2 ){
sqlite3_fprintf(p->out, "0x%08x\n", rc2);
}
}else
#endif /* !defined(SQLITE_UNTESTABLE) */
if( c=='t' && n>4 && cli_strncmp(azArg[0], "timeout", n)==0 ){
open_db(p, 0);
sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0);
|
| ︙ | ︙ | |||
30524 30525 30526 30527 30528 30529 30530 |
else if( optionMatch(z, "stmt") ){
mType |= SQLITE_TRACE_STMT;
}
else if( optionMatch(z, "close") ){
mType |= SQLITE_TRACE_CLOSE;
}
else {
| | | | 31955 31956 31957 31958 31959 31960 31961 31962 31963 31964 31965 31966 31967 31968 31969 31970 31971 31972 31973 31974 31975 |
else if( optionMatch(z, "stmt") ){
mType |= SQLITE_TRACE_STMT;
}
else if( optionMatch(z, "close") ){
mType |= SQLITE_TRACE_CLOSE;
}
else {
sqlite3_fprintf(stderr,"Unknown option \"%s\" on \".trace\"\n", z);
rc = 1;
goto meta_command_exit;
}
}else{
output_file_close(p->traceOut);
p->traceOut = output_file_open(z);
}
}
if( p->traceOut==0 ){
sqlite3_trace_v2(p->db, 0, 0, 0);
}else{
if( mType==0 ) mType = SQLITE_TRACE_STMT;
sqlite3_trace_v2(p->db, mType, sql_trace_callback, p);
|
| ︙ | ︙ | |||
30567 30568 30569 30570 30571 30572 30573 |
for(ii=1; ii<nArg; ii++){
sqlite3_create_module(p->db, azArg[ii], 0, 0);
}
}
}else
#endif
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | 31998 31999 32000 32001 32002 32003 32004 32005 32006 32007 32008 32009 32010 32011 32012 32013 32014 32015 32016 32017 32018 32019 32020 32021 32022 32023 32024 32025 32026 32027 32028 32029 32030 32031 32032 32033 32034 32035 32036 32037 32038 32039 32040 32041 32042 32043 32044 32045 32046 32047 32048 32049 32050 32051 32052 32053 32054 32055 32056 32057 32058 32059 32060 32061 32062 32063 32064 32065 32066 32067 32068 32069 32070 |
for(ii=1; ii<nArg; ii++){
sqlite3_create_module(p->db, azArg[ii], 0, 0);
}
}
}else
#endif
if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){
char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit";
sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid());
#if SQLITE_HAVE_ZLIB
sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion());
#endif
#define CTIMEOPT_VAL_(opt) #opt
#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
#if defined(__clang__) && defined(__clang_major__)
sqlite3_fprintf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "."
CTIMEOPT_VAL(__clang_minor__) "."
CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz);
#elif defined(_MSC_VER)
sqlite3_fprintf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz);
#elif defined(__GNUC__) && defined(__VERSION__)
sqlite3_fprintf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz);
#endif
}else
if( c=='v' && cli_strncmp(azArg[0], "vfsinfo", n)==0 ){
const char *zDbName = nArg==2 ? azArg[1] : "main";
sqlite3_vfs *pVfs = 0;
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs);
if( pVfs ){
sqlite3_fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName);
sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
}
}
}else
if( c=='v' && cli_strncmp(azArg[0], "vfslist", n)==0 ){
sqlite3_vfs *pVfs;
sqlite3_vfs *pCurrent = 0;
if( p->db ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent);
}
for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){
sqlite3_fprintf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName,
pVfs==pCurrent ? " <--- CURRENT" : "");
sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
if( pVfs->pNext ){
sqlite3_fputs("-----------------------------------\n", p->out);
}
}
}else
if( c=='v' && cli_strncmp(azArg[0], "vfsname", n)==0 ){
const char *zDbName = nArg==2 ? azArg[1] : "main";
char *zVfsName = 0;
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
if( zVfsName ){
sqlite3_fprintf(p->out, "%s\n", zVfsName);
sqlite3_free(zVfsName);
}
}
}else
if( c=='w' && cli_strncmp(azArg[0], "wheretrace", n)==0 ){
unsigned int x = nArg>=2? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
|
| ︙ | ︙ | |||
30712 30713 30714 30715 30716 30717 30718 |
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
for(j=1; j<nArg; j++){
p->colWidth[j-1] = (int)integerValue(azArg[j]);
}
}else
{
| | | 32080 32081 32082 32083 32084 32085 32086 32087 32088 32089 32090 32091 32092 32093 32094 |
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
for(j=1; j<nArg; j++){
p->colWidth[j-1] = (int)integerValue(azArg[j]);
}
}else
{
sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
rc = 1;
}
meta_command_exit:
if( p->outCount ){
p->outCount--;
|
| ︙ | ︙ | |||
30753 30754 30755 30756 30757 30758 30759 |
*/
static QuickScanState quickscan(char *zLine, QuickScanState qss,
SCAN_TRACKER_REFTYPE pst){
char cin;
char cWait = (char)qss; /* intentional narrowing loss */
if( cWait==0 ){
PlainScan:
| < | 32121 32122 32123 32124 32125 32126 32127 32128 32129 32130 32131 32132 32133 32134 |
*/
static QuickScanState quickscan(char *zLine, QuickScanState qss,
SCAN_TRACKER_REFTYPE pst){
char cin;
char cWait = (char)qss; /* intentional narrowing loss */
if( cWait==0 ){
PlainScan:
while( (cin = *zLine++)!=0 ){
if( IsSpace(cin) )
continue;
switch (cin){
case '-':
if( *zLine!='-' )
break;
|
| ︙ | ︙ | |||
30805 30806 30807 30808 30809 30810 30811 |
while( (cin = *zLine++)!=0 ){
if( cin==cWait ){
switch( cWait ){
case '*':
if( *zLine != '/' )
continue;
++zLine;
| < < | 32172 32173 32174 32175 32176 32177 32178 32179 32180 32181 32182 32183 32184 32185 32186 32187 32188 32189 32190 32191 32192 32193 32194 32195 32196 |
while( (cin = *zLine++)!=0 ){
if( cin==cWait ){
switch( cWait ){
case '*':
if( *zLine != '/' )
continue;
++zLine;
CONTINUE_PROMPT_AWAITC(pst, 0);
qss = QSS_SETV(qss, 0);
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
/* Swallow doubled end-delimiter.*/
++zLine;
continue;
}
deliberate_fall_through;
case ']':
CONTINUE_PROMPT_AWAITC(pst, 0);
qss = QSS_SETV(qss, 0);
goto PlainScan;
default: assert(0);
}
}
}
|
| ︙ | ︙ | |||
30964 30965 30966 30967 30968 30969 30970 | char *zErrMsg = 0; open_db(p, 0); if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; BEGIN_TIMER; rc = shell_exec(p, zSql, &zErrMsg); | | | 32329 32330 32331 32332 32333 32334 32335 32336 32337 32338 32339 32340 32341 32342 32343 |
char *zErrMsg = 0;
open_db(p, 0);
if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
BEGIN_TIMER;
rc = shell_exec(p, zSql, &zErrMsg);
END_TIMER(p->out);
if( rc || zErrMsg ){
char zPrefix[100];
const char *zErrorTail;
const char *zErrorType;
if( zErrMsg==0 ){
zErrorType = "Error";
zErrorTail = sqlite3_errmsg(p->db);
|
| ︙ | ︙ | |||
30988 30989 30990 30991 30992 30993 30994 |
}
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"%s near line %d:", zErrorType, startline);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
}
| | | | > > > | 32353 32354 32355 32356 32357 32358 32359 32360 32361 32362 32363 32364 32365 32366 32367 32368 32369 32370 32371 32372 32373 32374 32375 32376 32377 32378 32379 32380 32381 32382 32383 32384 32385 32386 32387 |
}
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"%s near line %d:", zErrorType, startline);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
}
sqlite3_fprintf(stderr,"%s %s\n", zPrefix, zErrorTail);
sqlite3_free(zErrMsg);
zErrMsg = 0;
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
char zLineBuf[2000];
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
sqlite3_fprintf(p->out, "%s\n", zLineBuf);
}
if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}
static void echo_group_input(ShellState *p, const char *zDo){
if( ShellHasFlag(p, SHFLG_Echo) ){
sqlite3_fprintf(p->out, "%s\n", zDo);
fflush(p->out);
}
}
#ifdef SQLITE_SHELL_FIDDLE
/*
** Alternate one_input_line() impl for wasm mode. This is not in the primary
** impl because we need the global shellState and cannot access it from that
** function without moving lots of code around (creating a larger/messier diff).
|
| ︙ | ︙ | |||
31063 31064 31065 31066 31067 31068 31069 |
int rc; /* Error code */
int errCnt = 0; /* Number of errors seen */
i64 startline = 0; /* Line number for start of current input */
QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */
if( p->inputNesting==MAX_INPUT_NESTING ){
/* This will be more informative in a later version. */
| | | | 32431 32432 32433 32434 32435 32436 32437 32438 32439 32440 32441 32442 32443 32444 32445 32446 32447 32448 32449 32450 32451 32452 32453 32454 32455 32456 32457 |
int rc; /* Error code */
int errCnt = 0; /* Number of errors seen */
i64 startline = 0; /* Line number for start of current input */
QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */
if( p->inputNesting==MAX_INPUT_NESTING ){
/* This will be more informative in a later version. */
sqlite3_fprintf(stderr,"Input nesting limit (%d) reached at line %d."
" Check recursion.\n", MAX_INPUT_NESTING, p->lineno);
return 1;
}
++p->inputNesting;
p->lineno = 0;
CONTINUE_PROMPT_RESET;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
fflush(p->out);
zLine = one_input_line(p->in, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
if( p->in==0 && stdin_is_interactive ) sqlite3_fputs("\n", p->out);
break;
}
if( seenInterrupt ){
if( p->in!=0 ) break;
seenInterrupt = 0;
}
p->lineno++;
|
| ︙ | ︙ | |||
31295 31296 31297 31298 31299 31300 31301 |
" cannot read ~/.sqliterc\n");
return;
}
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
shell_check_oom(zBuf);
sqliterc = zBuf;
}
| | | | | 32663 32664 32665 32666 32667 32668 32669 32670 32671 32672 32673 32674 32675 32676 32677 32678 32679 32680 32681 32682 32683 32684 32685 |
" cannot read ~/.sqliterc\n");
return;
}
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
shell_check_oom(zBuf);
sqliterc = zBuf;
}
p->in = sqlite3_fopen(sqliterc,"rb");
if( p->in ){
if( stdin_is_interactive ){
sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
}
if( process_input(p) && bail_on_error ) exit(1);
fclose(p->in);
}else if( sqliterc_override!=0 ){
sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
if( bail_on_error ) exit(1);
}
p->in = inSaved;
p->lineno = savedLineno;
sqlite3_free(zBuf);
}
|
| ︙ | ︙ | |||
31372 31373 31374 31375 31376 31377 31378 | #endif " -stats print memory stats before each finalize\n" " -table set output mode to 'table'\n" " -tabs set output mode to 'tabs'\n" " -unsafe-testing allow unsafe commands and modes for testing\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" | < < | | | 32740 32741 32742 32743 32744 32745 32746 32747 32748 32749 32750 32751 32752 32753 32754 32755 32756 32757 32758 32759 32760 32761 32762 32763 32764 |
#endif
" -stats print memory stats before each finalize\n"
" -table set output mode to 'table'\n"
" -tabs set output mode to 'tabs'\n"
" -unsafe-testing allow unsafe commands and modes for testing\n"
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
" -vfstrace enable tracing of all VFS calls\n"
#ifdef SQLITE_HAVE_ZLIB
" -zip open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL]]\n"
"FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist. Defaults to :memory:.\n", Argv0);
if( showDetail ){
sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions);
}else{
eputz("Use the -help option for additional information\n");
}
exit(0);
}
/*
|
| ︙ | ︙ | |||
31409 31410 31411 31412 31413 31414 31415 31416 31417 31418 31419 31420 31421 31422 |
/*
** Initialize the state information in data
*/
static void main_init(ShellState *data) {
memset(data, 0, sizeof(*data));
data->normalMode = data->cMode = data->mode = MODE_List;
data->autoExplain = 1;
data->pAuxDb = &data->aAuxDb[0];
memcpy(data->colSeparator,SEP_Column, 2);
memcpy(data->rowSeparator,SEP_Row, 2);
data->showHeader = 0;
data->shellFlgs = SHFLG_Lookaside;
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
#if !defined(SQLITE_SHELL_FIDDLE)
| > > > | 32775 32776 32777 32778 32779 32780 32781 32782 32783 32784 32785 32786 32787 32788 32789 32790 32791 |
/*
** Initialize the state information in data
*/
static void main_init(ShellState *data) {
memset(data, 0, sizeof(*data));
data->normalMode = data->cMode = data->mode = MODE_List;
data->autoExplain = 1;
#ifdef _WIN32
data->crlfMode = 1;
#endif
data->pAuxDb = &data->aAuxDb[0];
memcpy(data->colSeparator,SEP_Column, 2);
memcpy(data->rowSeparator,SEP_Row, 2);
data->showHeader = 0;
data->shellFlgs = SHFLG_Lookaside;
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
#if !defined(SQLITE_SHELL_FIDDLE)
|
| ︙ | ︙ | |||
31444 31445 31446 31447 31448 31449 31450 |
sputz(stdout, zText);
#if !SQLITE_OS_WINRT
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
}
#else
static void printBold(const char *zText){
| | > | > > > > > > > > > | 32813 32814 32815 32816 32817 32818 32819 32820 32821 32822 32823 32824 32825 32826 32827 32828 32829 32830 32831 32832 32833 32834 32835 32836 32837 32838 32839 32840 32841 32842 32843 32844 32845 32846 32847 32848 32849 32850 32851 32852 32853 32854 32855 |
sputz(stdout, zText);
#if !SQLITE_OS_WINRT
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
}
#else
static void printBold(const char *zText){
sqlite3_fprintf(stdout, "\033[1m%s\033[0m", zText);
}
#endif
/*
** Get the argument to an --option. Throw an error and die if no argument
** is available.
*/
static char *cmdline_option_value(int argc, char **argv, int i){
if( i==argc ){
sqlite3_fprintf(stderr,
"%s: Error: missing argument to %s\n", argv[0], argv[argc-1]);
exit(1);
}
return argv[i];
}
static void sayAbnormalExit(void){
if( seenInterrupt ) eputz("Program interrupted.\n");
}
/* Routine to output from vfstrace
*/
static int vfstraceOut(const char *z, void *pArg){
ShellState *p = (ShellState*)pArg;
sqlite3_fputs(z, p->out);
fflush(p->out);
return 1;
}
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) \
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
# define SQLITE_SHELL_IS_UTF8 (0)
# else
# define SQLITE_SHELL_IS_UTF8 (1)
|
| ︙ | ︙ | |||
31491 31492 31493 31494 31495 31496 31497 | sqlite3_int64 mem_main_enter = 0; #endif char *zErrMsg = 0; #ifdef SQLITE_SHELL_FIDDLE # define data shellState #else ShellState data; | < > < | | < > > | > > | > > | 32870 32871 32872 32873 32874 32875 32876 32877 32878 32879 32880 32881 32882 32883 32884 32885 32886 32887 32888 32889 32890 32891 32892 32893 32894 32895 32896 32897 32898 32899 32900 32901 32902 32903 32904 32905 32906 32907 32908 32909 32910 32911 32912 32913 32914 32915 32916 32917 32918 32919 32920 32921 32922 32923 32924 |
sqlite3_int64 mem_main_enter = 0;
#endif
char *zErrMsg = 0;
#ifdef SQLITE_SHELL_FIDDLE
# define data shellState
#else
ShellState data;
#endif
const char *zInitFile = 0;
int i;
int rc = 0;
int warnInmemoryDb = 0;
int readStdin = 1;
int nCmd = 0;
int nOptsEnd = argc;
int bEnableVfstrace = 0;
char **azCmd = 0;
const char *zVfs = 0; /* Value of -vfs command-line option */
#if !SQLITE_SHELL_IS_UTF8
char **argvToFree = 0;
int argcToFree = 0;
#endif
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
#ifdef SQLITE_SHELL_FIDDLE
stdin_is_interactive = 0;
stdout_is_console = 1;
data.wasm.zDefaultDbName = "/fiddle.sqlite3";
#else
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
#endif
atexit(sayAbnormalExit);
#ifdef SQLITE_DEBUG
mem_main_enter = sqlite3_memory_used();
#endif
#if !defined(_WIN32_WCE)
if( getenv("SQLITE_DEBUG_BREAK") ){
if( isatty(0) && isatty(2) ){
char zLine[100];
sqlite3_fprintf(stderr,
"attach debugger to process %d and press ENTER to continue...",
GETPID());
if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0
&& cli_strcmp(zLine,"stop")==0
){
exit(1);
}
}else{
#if defined(_WIN32) || defined(WIN32)
#if SQLITE_OS_WINRT
__debugbreak();
#else
DebugBreak();
#endif
|
| ︙ | ︙ | |||
31552 31553 31554 31555 31556 31557 31558 |
if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){
eputz("No ^C handler.\n");
}
#endif
#if USE_SYSTEM_SQLITE+0!=1
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
| > | | 32935 32936 32937 32938 32939 32940 32941 32942 32943 32944 32945 32946 32947 32948 32949 32950 |
if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){
eputz("No ^C handler.\n");
}
#endif
#if USE_SYSTEM_SQLITE+0!=1
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
sqlite3_fprintf(stderr,
"SQLite header and source version mismatch\n%s\n%s\n",
sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
}
#endif
main_init(&data);
/* On Windows, we must translate command-line arguments into UTF-8.
|
| ︙ | ︙ | |||
31694 31695 31696 31697 31698 31699 31700 |
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
verify_uninitialized();
switch( n ){
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
}
| < | < < < < < < < < | 33078 33079 33080 33081 33082 33083 33084 33085 33086 33087 33088 33089 33090 33091 33092 33093 |
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
verify_uninitialized();
switch( n ){
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
}
}else if( cli_strcmp(z,"-vfstrace")==0 ){
bEnableVfstrace = 1;
#ifdef SQLITE_ENABLE_MULTIPLEX
}else if( cli_strcmp(z,"-multiplex")==0 ){
extern int sqlite3_multiplex_initialize(const char*,int);
sqlite3_multiplex_initialize(0, 1);
#endif
}else if( cli_strcmp(z,"-mmap")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
|
| ︙ | ︙ | |||
31760 31761 31762 31763 31764 31765 31766 |
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
ShellSetFlag(&data,SHFLG_TestingMode);
}else if( cli_strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}
}
#ifndef SQLITE_SHELL_FIDDLE
| | | 33135 33136 33137 33138 33139 33140 33141 33142 33143 33144 33145 33146 33147 33148 33149 |
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
ShellSetFlag(&data,SHFLG_TestingMode);
}else if( cli_strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}
}
#ifndef SQLITE_SHELL_FIDDLE
if( !bEnableVfstrace ) verify_uninitialized();
#endif
#ifdef SQLITE_SHELL_INIT_PROC
{
/* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
** of a C-function that will perform initialization actions on SQLite that
|
| ︙ | ︙ | |||
31784 31785 31786 31787 31788 31789 31790 |
#endif
if( zVfs ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
| | > | > > > | 33159 33160 33161 33162 33163 33164 33165 33166 33167 33168 33169 33170 33171 33172 33173 33174 33175 33176 33177 33178 33179 33180 33181 33182 33183 33184 33185 33186 33187 33188 33189 33190 33191 |
#endif
if( zVfs ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs);
exit(1);
}
}
if( data.pAuxDb->zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
data.pAuxDb->zDbFilename = ":memory:";
warnInmemoryDb = argc==1;
#else
sqlite3_fprintf(stderr,
"%s: Error: no database filename specified\n", Argv0);
return 1;
#endif
}
data.out = stdout;
if( bEnableVfstrace ){
vfstrace_register("trace",0,vfstraceOut, &data, 1);
}
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_appendvfs_init(0,0,0);
#endif
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
** files from being created if a user mistypes the database name argument
|
| ︙ | ︙ | |||
31911 31912 31913 31914 31915 31916 31917 |
** prior to sending the SQL into SQLite. Useful for injecting
** crazy bytes in the middle of SQL statements for testing and debugging.
*/
ShellSetFlag(&data, SHFLG_Backslash);
}else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
}else if( cli_strcmp(z,"-version")==0 ){
| | | 33290 33291 33292 33293 33294 33295 33296 33297 33298 33299 33300 33301 33302 33303 33304 |
** prior to sending the SQL into SQLite. Useful for injecting
** crazy bytes in the middle of SQL statements for testing and debugging.
*/
ShellSetFlag(&data, SHFLG_Backslash);
}else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
}else if( cli_strcmp(z,"-version")==0 ){
sqlite3_fprintf(stdout, "%s %s (%d-bit)\n",
sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
return 0;
}else if( cli_strcmp(z,"-interactive")==0 ){
/* Need to check for interactive override here to so that it can
** affect console setup (for Windows only) and testing thereof.
*/
stdin_is_interactive = 1;
|
| ︙ | ︙ | |||
31949 31950 31951 31952 31953 31954 31955 |
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( cli_strcmp(z,"-sorterref")==0 ){
i++;
#endif
}else if( cli_strcmp(z,"-vfs")==0 ){
i++;
| < < | 33328 33329 33330 33331 33332 33333 33334 33335 33336 33337 33338 33339 33340 33341 33342 33343 |
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( cli_strcmp(z,"-sorterref")==0 ){
i++;
#endif
}else if( cli_strcmp(z,"-vfs")==0 ){
i++;
}else if( cli_strcmp(z,"-vfstrace")==0 ){
i++;
#ifdef SQLITE_ENABLE_MULTIPLEX
}else if( cli_strcmp(z,"-multiplex")==0 ){
i++;
#endif
}else if( cli_strcmp(z,"-help")==0 ){
usage(1);
}else if( cli_strcmp(z,"-cmd")==0 ){
|
| ︙ | ︙ | |||
31976 31977 31978 31979 31980 31981 31982 |
}else{
open_db(&data, 0);
rc = shell_exec(&data, z, &zErrMsg);
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
if( bail_on_error ) return rc!=0 ? rc : 1;
}else if( rc!=0 ){
| | | | | 33353 33354 33355 33356 33357 33358 33359 33360 33361 33362 33363 33364 33365 33366 33367 33368 33369 33370 33371 33372 33373 33374 33375 33376 33377 33378 33379 33380 33381 33382 33383 33384 33385 33386 33387 33388 33389 33390 33391 33392 33393 |
}else{
open_db(&data, 0);
rc = shell_exec(&data, z, &zErrMsg);
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
if( bail_on_error ) return rc!=0 ? rc : 1;
}else if( rc!=0 ){
sqlite3_fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z);
if( bail_on_error ) return rc;
}
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( cli_strncmp(z, "-A", 2)==0 ){
if( nCmd>0 ){
sqlite3_fprintf(stderr,"Error: cannot mix regular SQL or dot-commands"
" with \"%s\"\n", z);
return 1;
}
open_db(&data, OPEN_DB_ZIPFILE);
if( z[2] ){
argv[i] = &z[2];
arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
}else{
arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
break;
#endif
}else if( cli_strcmp(z,"-safe")==0 ){
data.bSafeMode = data.bSafeModePersist = 1;
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
/* Acted upon in first pass. */
}else{
sqlite3_fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
eputz("Use -help for a list of options.\n");
return 1;
}
data.cMode = data.mode;
}
if( !readStdin ){
|
| ︙ | ︙ | |||
32029 32030 32031 32032 32033 32034 32035 |
open_db(&data, 0);
echo_group_input(&data, azCmd[i]);
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
}else{
| > | < | < < < | | | | > > | 33406 33407 33408 33409 33410 33411 33412 33413 33414 33415 33416 33417 33418 33419 33420 33421 33422 33423 33424 33425 33426 33427 33428 33429 33430 33431 33432 33433 33434 33435 33436 33437 33438 33439 33440 33441 33442 33443 33444 33445 33446 33447 33448 33449 33450 33451 33452 33453 33454 33455 33456 33457 33458 33459 33460 |
open_db(&data, 0);
echo_group_input(&data, azCmd[i]);
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
}else{
sqlite3_fprintf(stderr,
"Error: unable to process SQL: %s\n", azCmd[i]);
}
sqlite3_free(zErrMsg);
if( rc==0 ) rc = 1;
goto shell_main_exit;
}
}
}
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
int nHistory;
sqlite3_fprintf(stdout,
"SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for usage hints.\n",
sqlite3_libversion(), sqlite3_sourceid());
if( warnInmemoryDb ){
sputz(stdout, "Connected to a ");
printBold("transient in-memory database");
sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
" persistent database.\n");
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
zHistory = strdup(zHistory);
}else if( (zHome = find_home_dir(0))!=0 ){
nHistory = strlen30(zHome) + 20;
if( (zHistory = malloc(nHistory))!=0 ){
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
if( zHistory ){ shell_read_history(zHistory); }
#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE==1
linenoiseSetCompletionCallback(linenoise_completion);
#elif HAVE_LINENOISE==2
linenoiseSetCompletionCallback(linenoise_completion, NULL);
#endif
data.in = 0;
rc = process_input(&data);
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
|
| ︙ | ︙ | |||
32120 32121 32122 32123 32124 32125 32126 32127 32128 |
free(argvToFree);
#endif
free(data.colWidth);
free(data.zNonce);
/* Clear the global data structure so that valgrind will detect memory
** leaks */
memset(&data, 0, sizeof(data));
#ifdef SQLITE_DEBUG
if( sqlite3_memory_used()>mem_main_enter ){
| > > > | | 33496 33497 33498 33499 33500 33501 33502 33503 33504 33505 33506 33507 33508 33509 33510 33511 33512 33513 33514 33515 |
free(argvToFree);
#endif
free(data.colWidth);
free(data.zNonce);
/* Clear the global data structure so that valgrind will detect memory
** leaks */
memset(&data, 0, sizeof(data));
if( bEnableVfstrace ){
vfstrace_unregister("trace");
}
#ifdef SQLITE_DEBUG
if( sqlite3_memory_used()>mem_main_enter ){
sqlite3_fprintf(stderr,"Memory leaked: %u bytes\n",
(unsigned int)(sqlite3_memory_used()-mem_main_enter));
}
#endif
#else /* SQLITE_SHELL_FIDDLE... */
shell_main_exit:
#endif
return rc;
|
| ︙ | ︙ | |||
32162 32163 32164 32165 32166 32167 32168 |
SQLITE_FCNTL_VFS_POINTER, &pVfs);
}
return pVfs;
}
/* Only for emcc experimentation purposes. */
sqlite3 * fiddle_db_arg(sqlite3 *arg){
| | | 33541 33542 33543 33544 33545 33546 33547 33548 33549 33550 33551 33552 33553 33554 33555 |
SQLITE_FCNTL_VFS_POINTER, &pVfs);
}
return pVfs;
}
/* Only for emcc experimentation purposes. */
sqlite3 * fiddle_db_arg(sqlite3 *arg){
sqlite3_fprintf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg);
return arg;
}
/*
** Intended to be called via a SharedWorker() while a separate
** SharedWorker() (which manages the wasm module) is performing work
** which should be interrupted. Unfortunately, SharedWorker is not
|
| ︙ | ︙ | |||
32199 32200 32201 32202 32203 32204 32205 |
if( globalDb ){
int rc;
while( sqlite3_txn_state(globalDb,0)>0 ){
/*
** Resolve problem reported in
** https://sqlite.org/forum/forumpost/0b41a25d65
*/
| | | 33578 33579 33580 33581 33582 33583 33584 33585 33586 33587 33588 33589 33590 33591 33592 |
if( globalDb ){
int rc;
while( sqlite3_txn_state(globalDb,0)>0 ){
/*
** Resolve problem reported in
** https://sqlite.org/forum/forumpost/0b41a25d65
*/
sqlite3_fputs("Rolling back in-progress transaction.\n", stdout);
sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0);
}
rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0);
sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
}
}
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.48.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have ** the "sqlite3.h" header file at hand, you will find a copy embedded within ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in ** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: ** ** */ #ifndef SQLITE_AMALGAMATION #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif /************** Begin file sqliteInt.h ***************************************/ /* |
| ︙ | ︙ | |||
458 459 460 461 462 463 464 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.48.0" #define SQLITE_VERSION_NUMBER 3048000 #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from | > > > > > > > > | 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. ** ** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read ** from the database file in amounts that are not a multiple of the ** page size and that do not begin at a page boundary. Without this ** property, SQLite is careful to only do full-page reads and write ** on aligned pages, with the one exception that it will do a sub-page ** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 #define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from |
| ︙ | ︙ | |||
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means | > | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** <li> [SQLITE_IOCAP_SUBPAGE_READ] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means |
| ︙ | ︙ | |||
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** | > > > > > | 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_NULL_IO]] ** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor ** or file handle for the [sqlite3_file] object such that it will no longer ** read or write to the database file. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** |
| ︙ | ︙ | |||
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| ︙ | ︙ | |||
2934 2935 2936 2937 2938 2939 2940 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value | | > > > > | 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value ** and that if the number of rows modified by the most recent INSERT, UPDATE, ** or DELETE is greater than the maximum value supported by type "int", then ** the return value of sqlite3_changes() is undefined. ^Executing any other ** type of SQL statement does not modify the value returned by these functions. ** For the purposes of this interface, a CREATE TABLE AS SELECT statement ** does not count as an INSERT, UPDATE or DELETE statement and hence the rows ** added to the new table by the CREATE TABLE AS SELECT statement are not ** counted. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], ** [foreign key actions] or [REPLACE] constraint resolution are not counted. ** ** Changes to a view that are intercepted by ** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value |
| ︙ | ︙ | |||
4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
| > > > > > > > > > > > | 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
**
** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
** errors from being sent to the error log defined by
** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
** compiles to see if some SQL syntax is well-formed, without generating
** messages on the global error log when it is not. If the test compile
** fails, the sqlite3_prepare_v3() call returns the same error indications
** with or without this flag; it just omits the call to [sqlite3_log()] that
** logs the error.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
#define SQLITE_PREPARE_DONT_LOG 0x10
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
|
| ︙ | ︙ | |||
4534 4535 4536 4537 4538 4539 4540 | ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), ** and sqlite3_prepare_v3() ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the | | | > > > > | 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 | ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), ** and sqlite3_prepare_v3() ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the ** first zero terminator. ^If nByte is positive, then it is the maximum ** number of bytes read from zSql. When nByte is positive, zSql is read ** up to the first zero terminator or until the nByte bytes have been read, ** whichever comes first. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string <i>including</i> ** the nul-terminator. ** Note that nByte measure the length of the input in bytes, not ** characters, even for the UTF-16 interfaces. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** ** ^*ppStmt is left pointing to a compiled [prepared statement] that can be |
| ︙ | ︙ | |||
5911 5912 5913 5914 5915 5916 5917 | ** ** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd> ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. ** This flag instructs SQLite to omit some corner-case optimizations that ** might disrupt the operation of the [sqlite3_value_subtype()] function, ** causing it to return zero rather than the correct subtype(). | | | 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 | ** ** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd> ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. ** This flag instructs SQLite to omit some corner-case optimizations that ** might disrupt the operation of the [sqlite3_value_subtype()] function, ** causing it to return zero rather than the correct subtype(). ** All SQL functions that invoke [sqlite3_value_subtype()] should have this ** property. If the SQLITE_SUBTYPE property is omitted, then the return ** value from [sqlite3_value_subtype()] might sometimes be zero even though ** a non-zero subtype was specified by the function argument expression. ** ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> ** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_result_subtype()] to cause a sub-type to be associated with its |
| ︙ | ︙ | |||
8676 8677 8678 8679 8680 8681 8682 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 #define SQLITE_TESTCTRL_PRNG_SEED 28 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 | | | 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 #define SQLITE_TESTCTRL_PRNG_SEED 28 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking ** ** These routines provide access to the set of SQL language keywords ** recognized by SQLite. Applications can uses these routines to determine |
| ︙ | ︙ | |||
9652 9653 9654 9655 9656 9657 9658 9659 9660 9661 9662 9663 9664 9665 | ** ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); | > > > > > > > > > > | 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700 9701 9702 9703 9704 9705 9706 9707 9708 9709 9710 9711 9712 | ** ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. ** ** <b>Alternatives To Using The Backup API</b> ** ** Other techniques for safely creating a consistent backup of an SQLite ** database include: ** ** <ul> ** <li> The [VACUUM INTO] command. ** <li> The [sqlite3_rsync] utility program. ** </ul> */ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); |
| ︙ | ︙ | |||
10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 | ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a ** new [sqlite3_snapshot] object that records the current state of ** schema S in database connection D. ^On success, the ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined ** in this case. ** ** <ul> | > > > > > > > > | 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 | ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a ** new [sqlite3_snapshot] object that records the current state of ** schema S in database connection D. ^On success, the ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** ** If a read-transaction is opened by this function, then it is guaranteed ** that the returned snapshot object may not be invalidated by a database ** writer or checkpointer until after the read-transaction is closed. This ** is not guaranteed if a read-transaction is already open when this ** function is called. In that case, any subsequent write or checkpoint ** operation on the database may invalidate the returned snapshot handle, ** even while the read-transaction remains open. ** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined ** in this case. ** ** <ul> |
| ︙ | ︙ | |||
11170 11171 11172 11173 11174 11175 11176 | # define SQLITE_THREADSAFE 0 # endif #endif #if 0 } /* End of the 'extern "C"' block */ #endif | | | 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 | # define SQLITE_THREADSAFE 0 # endif #endif #if 0 } /* End of the 'extern "C"' block */ #endif /* #endif for SQLITE3_H will be added by mksqlite3.tcl */ /******** Begin file sqlite3rtree.h *********/ /* ** 2010 August 30 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
| ︙ | ︙ | |||
13421 13422 13423 13424 13425 13426 13427 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in | < < | > > > > > > > > > > > > > > > > > | 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API may be slow in some cases if the token identified by parameters ** iIdx and iToken matched a prefix token in the query. In most cases, the ** first call to this API for each prefix token in the query is forced ** to scan the portion of the full-text index that matches the prefix ** token to collect the extra data required by this API. If the prefix ** token matches a large number of token instances in the document set, ** this may be a performance problem. ** ** If the user knows in advance that a query may use this API for a ** prefix token, FTS5 may be configured to collect all required data as part ** of the initial querying of the full-text index, avoiding the second scan ** entirely. This also causes prefix queries that do not use this API to ** run more slowly and use more memory. FTS5 may be configured in this way ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ** option, or on a per-query basis using the ** [fts5_insttoken | fts5_insttoken()] user function. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. |
| ︙ | ︙ | |||
13519 13520 13521 13522 13523 13524 13525 | /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting | < | 13589 13590 13591 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 | /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** |
| ︙ | ︙ | |||
13863 13864 13865 13866 13867 13868 13869 13870 13871 13872 13873 13874 13875 13876 | #if 0 } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory. */ | > | 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 | #if 0 } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ #endif /* SQLITE3_H */ /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory. */ |
| ︙ | ︙ | |||
13908 13909 13910 13911 13912 13913 13914 13915 13916 13917 13918 13919 13920 13921 | ** ** The hard limit is the ability of a 32-bit signed integer ** to count the size: 2^31-1 or 2147483647. */ #ifndef SQLITE_MAX_LENGTH # define SQLITE_MAX_LENGTH 1000000000 #endif /* ** This is the maximum number of ** ** * Columns in a table ** * Columns in an index ** * Columns in a view | > | 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 | ** ** The hard limit is the ability of a 32-bit signed integer ** to count the size: 2^31-1 or 2147483647. */ #ifndef SQLITE_MAX_LENGTH # define SQLITE_MAX_LENGTH 1000000000 #endif #define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ /* ** This is the maximum number of ** ** * Columns in a table ** * Columns in an index ** * Columns in a view |
| ︙ | ︙ | |||
14807 14808 14809 14810 14811 14812 14813 14814 14815 14816 14817 14818 14819 14820 | /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <stddef.h> /* ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. ** This allows better measurements of where memcpy() is used when running ** cachegrind. But this macro version of memcpy() is very slow so it ** should not be used in production. This is a performance measurement ** hack only. | > | 14878 14879 14880 14881 14882 14883 14884 14885 14886 14887 14888 14889 14890 14891 14892 | /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <stddef.h> #include <ctype.h> /* ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. ** This allows better measurements of where memcpy() is used when running ** cachegrind. But this macro version of memcpy() is very slow so it ** should not be used in production. This is a performance measurement ** hack only. |
| ︙ | ︙ | |||
14829 14830 14831 14832 14833 14834 14835 | ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 # define fabs(X) ((X)<0?-(X):(X)) # define sqlite3IsOverflow(X) 0 | < | 14901 14902 14903 14904 14905 14906 14907 14908 14909 14910 14911 14912 14913 14914 | ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 # define fabs(X) ((X)<0?-(X):(X)) # define sqlite3IsOverflow(X) 0 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT # undef SQLITE_HAVE_ISNAN |
| ︙ | ︙ | |||
15004 15005 15006 15007 15008 15009 15010 | #ifndef INT8_TYPE # ifdef HAVE_INT8_T # define INT8_TYPE int8_t # else # define INT8_TYPE signed char # endif #endif | < < < | 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084 15085 15086 15087 15088 | #ifndef INT8_TYPE # ifdef HAVE_INT8_T # define INT8_TYPE int8_t # else # define INT8_TYPE signed char # endif #endif typedef sqlite_int64 i64; /* 8-byte signed integer */ typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ typedef INT16_TYPE i16; /* 2-byte signed integer */ typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ typedef INT8_TYPE i8; /* 1-byte signed integer */ |
| ︙ | ︙ | |||
16389 16390 16391 16392 16393 16394 16395 16396 16397 16398 16399 16400 16401 16402 | Pgno iTable, /* Index of root page */ int wrFlag, /* 1 for writing. 0 for read-only */ struct KeyInfo*, /* First argument to compare function */ BtCursor *pCursor /* Space to write cursor structure */ ); SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); | > > > | 16457 16458 16459 16460 16461 16462 16463 16464 16465 16466 16467 16468 16469 16470 16471 16472 16473 | Pgno iTable, /* Index of root page */ int wrFlag, /* 1 for writing. 0 for read-only */ struct KeyInfo*, /* First argument to compare function */ BtCursor *pCursor /* Space to write cursor structure */ ); SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*); #endif SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); |
| ︙ | ︙ | |||
17002 17003 17004 17005 17006 17007 17008 | /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ /* ** Additional non-public SQLITE_PREPARE_* flags */ #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ | | | 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 | /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ /* ** Additional non-public SQLITE_PREPARE_* flags */ #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ #define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); SQLITE_PRIVATE Parse *sqlite3VdbeParser(Vdbe*); |
| ︙ | ︙ | |||
17717 17718 17719 17720 17721 17722 17723 |
*/
#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < | 17788 17789 17790 17791 17792 17793 17794 17795 17796 17797 17798 17799 17800 17801 17802 17803 17804 17805 17806 |
*/
#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
/*
** typedef for the authorization callback function.
*/
typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
const char*);
#ifndef SQLITE_OMIT_DEPRECATED
/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing
** in the style of sqlite3_trace()
*/
#define SQLITE_TRACE_LEGACY 0x40 /* Use the legacy xTrace */
#define SQLITE_TRACE_XPROFILE 0x80 /* Use the legacy xProfile */
|
| ︙ | ︙ | |||
17918 17919 17920 17921 17922 17923 17924 | */ sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ void *pUnlockArg; /* Argument to xUnlockNotify */ void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ #endif | < < < | 17953 17954 17955 17956 17957 17958 17959 17960 17961 17962 17963 17964 17965 17966 | */ sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ void *pUnlockArg; /* Argument to xUnlockNotify */ void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ #endif }; /* ** A macro to discover the encoding of a database. */ #define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc) #define ENC(db) ((db)->enc) |
| ︙ | ︙ | |||
19219 19220 19221 19222 19223 19224 19225 | #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ | | | 19251 19252 19253 19254 19255 19256 19257 19258 19259 19260 19261 19262 19263 19264 19265 | #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ #define EP_SubtArg 0x80000000 /* Is argument to SQLITE_SUBTYPE function */ /* The EP_Propagate mask is a set of properties that automatically propagate ** upwards into parent nodes. */ #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) /* Macros can be used to test, set, or clear bits in the |
| ︙ | ︙ | |||
19775 19776 19777 19778 19779 19780 19781 | ** in register pDest->iSDParm then abandon the rest ** of the query. This destination implies "LIMIT 1". ** ** SRT_Set The result must be a single column. Store each ** row of result as the key in table pDest->iSDParm. ** Apply the affinity pDest->affSdst before storing ** results. if pDest->iSDParm2 is positive, then it is | | | 19807 19808 19809 19810 19811 19812 19813 19814 19815 19816 19817 19818 19819 19820 19821 | ** in register pDest->iSDParm then abandon the rest ** of the query. This destination implies "LIMIT 1". ** ** SRT_Set The result must be a single column. Store each ** row of result as the key in table pDest->iSDParm. ** Apply the affinity pDest->affSdst before storing ** results. if pDest->iSDParm2 is positive, then it is ** a register holding a Bloom filter for the IN operator ** that should be populated in addition to the ** pDest->iSDParm table. This SRT is used to ** implement "IN (SELECT ...)". ** ** SRT_EphemTab Create an temporary table pDest->iSDParm and store ** the result there. The cursor is left open after ** returning. This is like SRT_Table except that |
| ︙ | ︙ | |||
20374 20375 20376 20377 20378 20379 20380 | int bMemstat; /* True to enable memory status */ u8 bCoreMutex; /* True to enable core mutexing */ u8 bFullMutex; /* True to enable full mutexing */ u8 bOpenUri; /* True to interpret filenames as URIs */ u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ | < | 20406 20407 20408 20409 20410 20411 20412 20413 20414 20415 20416 20417 20418 20419 | int bMemstat; /* True to enable memory status */ u8 bCoreMutex; /* True to enable core mutexing */ u8 bFullMutex; /* True to enable full mutexing */ u8 bOpenUri; /* True to interpret filenames as URIs */ u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ #ifdef SQLITE_DEBUG u8 bJsonSelfcheck; /* Double-check JSON parsing */ #endif int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ |
| ︙ | ︙ | |||
20749 20750 20751 20752 20753 20754 20755 | ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. */ #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) # define SQLITE_ENABLE_FTS3 1 #endif | < < < < < < < < < | 20780 20781 20782 20783 20784 20785 20786 20787 20788 20789 20790 20791 20792 20793 | ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. */ #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) # define SQLITE_ENABLE_FTS3 1 #endif /* ** The following macros mimic the standard library functions toupper(), ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The ** sqlite versions only work for ASCII characters, regardless of locale. */ #ifdef SQLITE_ASCII # define sqlite3Toupper(x) ((x)&~(sqlite3CtypeMap[(unsigned char)(x)]&0x20)) |
| ︙ | ︙ | |||
21379 21380 21381 21382 21383 21384 21385 | SQLITE_PRIVATE i64 sqlite3RealToI64(double); SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 | | | 21401 21402 21403 21404 21405 21406 21407 21408 21409 21410 21411 21412 21413 21414 21415 | SQLITE_PRIVATE i64 sqlite3RealToI64(double); SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nByte, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); SQLITE_PRIVATE LogEst sqlite3LogEst(u64); SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst); SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double); |
| ︙ | ︙ | |||
22837 22838 22839 22840 22841 22842 22843 | #endif #ifdef SQLITE_UNLINK_AFTER_CLOSE "UNLINK_AFTER_CLOSE", #endif #ifdef SQLITE_UNTESTABLE "UNTESTABLE", #endif | < < < | 22859 22860 22861 22862 22863 22864 22865 22866 22867 22868 22869 22870 22871 22872 | #endif #ifdef SQLITE_UNLINK_AFTER_CLOSE "UNLINK_AFTER_CLOSE", #endif #ifdef SQLITE_UNTESTABLE "UNTESTABLE", #endif #ifdef SQLITE_USE_ALLOCA "USE_ALLOCA", #endif #ifdef SQLITE_USE_FCNTL_TRACE "USE_FCNTL_TRACE", #endif #ifdef SQLITE_USE_URI |
| ︙ | ︙ | |||
23115 23116 23117 23118 23119 23120 23121 | SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ | < | 23134 23135 23136 23137 23138 23139 23140 23141 23142 23143 23144 23145 23146 23147 | SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ #ifdef SQLITE_DEBUG 0, /* bJsonSelfcheck */ #endif 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ |
| ︙ | ︙ | |||
23835 23836 23837 23838 23839 23840 23841 23842 23843 23844 23845 23846 23847 23848 23849 23850 23851 | KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ int iBlobWrite; /* Value returned by preupdate_blobwrite() */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ }; /* ** An instance of this object is used to pass an vector of values into ** OP_VFilter, the xFilter method of a virtual table. The vector is the ** set of values on the right-hand side of an IN constraint. ** | > > | 23853 23854 23855 23856 23857 23858 23859 23860 23861 23862 23863 23864 23865 23866 23867 23868 23869 23870 23871 | KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ int iBlobWrite; /* Value returned by preupdate_blobwrite() */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem oldipk; /* Memory cell holding "old" IPK value */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ sqlite3_value **apDflt; /* Array of default values, if required */ }; /* ** An instance of this object is used to pass an vector of values into ** OP_VFilter, the xFilter method of a virtual table. The vector is the ** set of values on the right-hand side of an IN constraint. ** |
| ︙ | ︙ | |||
29283 29284 29285 29286 29287 29288 29289 29290 29291 29292 29293 29294 29295 29296 29297 29298 |
}
}
#ifndef NDEBUG
/*
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
** intended for use inside assert() statements.
*/
SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){
assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
}
SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
}
| > > > > > > > > > > > > > | | 29303 29304 29305 29306 29307 29308 29309 29310 29311 29312 29313 29314 29315 29316 29317 29318 29319 29320 29321 29322 29323 29324 29325 29326 29327 29328 29329 29330 29331 29332 29333 29334 29335 29336 29337 29338 29339 |
}
}
#ifndef NDEBUG
/*
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
** intended for use inside assert() statements.
**
** Because these routines raise false-positive alerts in TSAN, disable
** them (make them always return 1) when compiling with TSAN.
*/
SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){
# if defined(__has_feature)
# if __has_feature(thread_sanitizer)
p = 0;
# endif
# endif
assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
}
SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
# if defined(__has_feature)
# if __has_feature(thread_sanitizer)
p = 0;
# endif
# endif
assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
}
#endif /* NDEBUG */
#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.c ***********************************************/
/************** Begin file mutex_noop.c **************************************/
/*
** 2008 October 07
|
| ︙ | ︙ | |||
32270 32271 32272 32273 32274 32275 32276 32277 32278 32279 32280 32281 32282 32283 |
SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){
while( pExpr
&& (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
){
pExpr = pExpr->pLeft;
}
if( pExpr==0 ) return;
db->errByteOffset = pExpr->w.iOfst;
}
/*
** Enlarge the memory allocation on a StrAccum object so that it is
** able to accept at least N more bytes of text.
**
| > | 32303 32304 32305 32306 32307 32308 32309 32310 32311 32312 32313 32314 32315 32316 32317 |
SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){
while( pExpr
&& (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
){
pExpr = pExpr->pLeft;
}
if( pExpr==0 ) return;
if( ExprHasProperty(pExpr, EP_FromDDL) ) return;
db->errByteOffset = pExpr->w.iOfst;
}
/*
** Enlarge the memory allocation on a StrAccum object so that it is
** able to accept at least N more bytes of text.
**
|
| ︙ | ︙ | |||
32999 33000 33001 33002 33003 33004 33005 |
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
| | | 33033 33034 33035 33036 33037 33038 33039 33040 33041 33042 33043 33044 33045 33046 33047 |
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
sqlite3_str_appendf(&x, " isOn");
}
if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte");
if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom");
|
| ︙ | ︙ | |||
34083 34084 34085 34086 34087 34088 34089 34090 34091 34092 34093 34094 34095 34096 |
** parameters. These variants are intended to be used from a symbolic
** debugger, such as "gdb", during interactive debugging sessions.
**
** This routines are given external linkage so that they will always be
** accessible to the debugging, and to avoid warnings about unused
** functions. But these routines only exist in debugging builds, so they
** do not contaminate the interface.
*/
SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
| > > > > | 34117 34118 34119 34120 34121 34122 34123 34124 34125 34126 34127 34128 34129 34130 34131 34132 34133 34134 |
** parameters. These variants are intended to be used from a symbolic
** debugger, such as "gdb", during interactive debugging sessions.
**
** This routines are given external linkage so that they will always be
** accessible to the debugging, and to avoid warnings about unused
** functions. But these routines only exist in debugging builds, so they
** do not contaminate the interface.
**
** See Also:
**
** sqlite3ShowWhereTerm() in where.c
*/
SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
|
| ︙ | ︙ | |||
34685 34686 34687 34688 34689 34690 34691 |
** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
#define READ_UTF8(zIn, zTerm, c) \
c = *(zIn++); \
if( c>=0xc0 ){ \
c = sqlite3Utf8Trans1[c-0xc0]; \
| | | 34723 34724 34725 34726 34727 34728 34729 34730 34731 34732 34733 34734 34735 34736 34737 |
** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
#define READ_UTF8(zIn, zTerm, c) \
c = *(zIn++); \
if( c>=0xc0 ){ \
c = sqlite3Utf8Trans1[c-0xc0]; \
while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \
c = (c<<6) + (0x3f & *(zIn++)); \
} \
if( c<0x80 \
|| (c&0xFFFFF800)==0xD800 \
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
SQLITE_PRIVATE u32 sqlite3Utf8Read(
|
| ︙ | ︙ | |||
35063 35064 35065 35066 35067 35068 35069 | assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); assert( m.z || db->mallocFailed ); return m.z; } /* | | | > | > | | | 35101 35102 35103 35104 35105 35106 35107 35108 35109 35110 35111 35112 35113 35114 35115 35116 35117 35118 35119 35120 35121 35122 35123 35124 35125 35126 35127 35128 35129 35130 |
assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
assert( m.z || db->mallocFailed );
return m.z;
}
/*
** zIn is a UTF-16 encoded unicode string at least nByte bytes long.
** Return the number of bytes in the first nChar unicode characters
** in pZ. nChar must be non-negative. Surrogate pairs count as a single
** character.
*/
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nByte, int nChar){
int c;
unsigned char const *z = zIn;
unsigned char const *zEnd = &z[nByte-1];
int n = 0;
if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++;
while( n<nChar && ALWAYS(z<=zEnd) ){
c = z[0];
z += 2;
if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2;
n++;
}
return (int)(z-(unsigned char const *)zIn)
- (SQLITE_UTF16NATIVE==SQLITE_UTF16LE);
}
#if defined(SQLITE_TEST)
|
| ︙ | ︙ | |||
35657 35658 35659 35660 35661 35662 35663 35664 35665 35666 35667 35668 35669 35670 |
u64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
int nDigit = 0; /* Number of digits processed */
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
if( length==0 ) return 0;
if( enc==SQLITE_UTF8 ){
incr = 1;
| > > | 35697 35698 35699 35700 35701 35702 35703 35704 35705 35706 35707 35708 35709 35710 35711 35712 |
u64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
int nDigit = 0; /* Number of digits processed */
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
u64 s2; /* round-tripped significand */
double rr[2];
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
if( length==0 ) return 0;
if( enc==SQLITE_UTF8 ){
incr = 1;
|
| ︙ | ︙ | |||
35759 35760 35761 35762 35763 35764 35765 |
goto atof_return;
}
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
/* Try to adjust the exponent to make it smaller */
| | < | < < < < < < < < < < < | < | | > | < < < < < | < > | < < < > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | 35801 35802 35803 35804 35805 35806 35807 35808 35809 35810 35811 35812 35813 35814 35815 35816 35817 35818 35819 35820 35821 35822 35823 35824 35825 35826 35827 35828 35829 35830 35831 35832 35833 35834 35835 35836 35837 35838 35839 35840 35841 35842 35843 35844 35845 35846 35847 35848 35849 35850 35851 35852 35853 35854 35855 35856 35857 35858 35859 35860 35861 35862 35863 35864 35865 35866 35867 35868 35869 |
goto atof_return;
}
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
/* Try to adjust the exponent to make it smaller */
while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
s *= 10;
e--;
}
while( e<0 && (s%10)==0 ){
s /= 10;
e++;
}
rr[0] = (double)s;
assert( sizeof(s2)==sizeof(rr[0]) );
#ifdef SQLITE_DEBUG
rr[1] = 18446744073709549568.0;
memcpy(&s2, &rr[1], sizeof(s2));
assert( s2==0x43efffffffffffffLL );
#endif
/* Largest double that can be safely converted to u64
** vvvvvvvvvvvvvvvvvvvvvv */
if( rr[0]<=18446744073709549568.0 ){
s2 = (u64)rr[0];
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
}else{
rr[1] = 0.0;
}
assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */
if( e>0 ){
while( e>=100 ){
e -= 100;
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
}
while( e>=10 ){
e -= 10;
dekkerMul2(rr, 1.0e+10, 0.0);
}
while( e>=1 ){
e -= 1;
dekkerMul2(rr, 1.0e+01, 0.0);
}
}else{
while( e<=-100 ){
e += 100;
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
}
while( e<=-10 ){
e += 10;
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
}
while( e<=-1 ){
e += 1;
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
}
}
*pResult = rr[0]+rr[1];
if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300;
if( sign<0 ) *pResult = -*pResult;
assert( !sqlite3IsNaN(*pResult) );
atof_return:
/* return true if number and no extra non-whitespace characters after */
if( z==zEnd && nDigit>0 && eValid && eType>0 ){
return eType;
|
| ︙ | ︙ | |||
36150 36151 36152 36153 36154 36155 36156 36157 36158 |
** into the middle of p->zBuf[]. There are p->n significant digits.
** The p->z[] array is *not* zero-terminated.
*/
SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
int i;
u64 v;
int e, exp = 0;
p->isSpecial = 0;
p->z = p->zBuf;
| > > < | 36176 36177 36178 36179 36180 36181 36182 36183 36184 36185 36186 36187 36188 36189 36190 36191 36192 36193 |
** into the middle of p->zBuf[]. There are p->n significant digits.
** The p->z[] array is *not* zero-terminated.
*/
SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
int i;
u64 v;
int e, exp = 0;
double rr[2];
p->isSpecial = 0;
p->z = p->zBuf;
assert( mxRound>0 );
/* Convert negative numbers to positive. Deal with Infinity, 0.0, and
** NaN. */
if( r<0.0 ){
p->sign = '-';
r = -r;
|
| ︙ | ︙ | |||
36180 36181 36182 36183 36184 36185 36186 |
p->n = 0;
p->iDP = 0;
return;
}
/* Multiply r by powers of ten until it lands somewhere in between
** 1.0e+19 and 1.0e+17.
| | < < < < < < < < < < < < < < | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | 36207 36208 36209 36210 36211 36212 36213 36214 36215 36216 36217 36218 36219 36220 36221 36222 36223 36224 36225 36226 36227 36228 36229 36230 36231 36232 36233 36234 36235 36236 36237 36238 36239 36240 36241 36242 36243 36244 36245 36246 36247 36248 36249 36250 36251 36252 36253 36254 36255 36256 36257 36258 36259 |
p->n = 0;
p->iDP = 0;
return;
}
/* Multiply r by powers of ten until it lands somewhere in between
** 1.0e+19 and 1.0e+17.
**
** Use Dekker-style double-double computation to increase the
** precision.
**
** The error terms on constants like 1.0e+100 computed using the
** decimal extension, for example as follows:
**
** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100)));
*/
rr[0] = r;
rr[1] = 0.0;
if( rr[0]>9.223372036854774784e+18 ){
while( rr[0]>9.223372036854774784e+118 ){
exp += 100;
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
}
while( rr[0]>9.223372036854774784e+28 ){
exp += 10;
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
}
while( rr[0]>9.223372036854774784e+18 ){
exp += 1;
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
}
}else{
while( rr[0]<9.223372036854774784e-83 ){
exp -= 100;
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
}
while( rr[0]<9.223372036854774784e+07 ){
exp -= 10;
dekkerMul2(rr, 1.0e+10, 0.0);
}
while( rr[0]<9.22337203685477478e+17 ){
exp -= 1;
dekkerMul2(rr, 1.0e+01, 0.0);
}
}
v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1];
/* Extract significant digits. */
i = sizeof(p->zBuf)-1;
assert( v>0 );
while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; }
assert( i>=0 && i<sizeof(p->zBuf)-1 );
p->n = sizeof(p->zBuf) - 1 - i;
|
| ︙ | ︙ | |||
37006 37007 37008 37009 37010 37011 37012 |
const char *z = (const char*)&pIn[i+2];
if( strncmp(z,zName,nName)==0 && z[nName]==0 ) return pIn[i];
i += pIn[i+1];
}while( i<mx );
return 0;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 37016 37017 37018 37019 37020 37021 37022 37023 37024 37025 37026 37027 37028 37029 |
const char *z = (const char*)&pIn[i+2];
if( strncmp(z,zName,nName)==0 && z[nName]==0 ) return pIn[i];
i += pIn[i+1];
}while( i<mx );
return 0;
}
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
** 2001 September 22
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
|
| ︙ | ︙ | |||
38785 38786 38787 38788 38789 38790 38791 | # define F_GETLK 5 # define F_SETLK 6 # define F_SETLKW 7 # endif # endif #else /* !SQLITE_WASI */ # ifndef HAVE_FCHMOD | | | 38697 38698 38699 38700 38701 38702 38703 38704 38705 38706 38707 38708 38709 38710 38711 | # define F_GETLK 5 # define F_SETLK 6 # define F_SETLKW 7 # endif # endif #else /* !SQLITE_WASI */ # ifndef HAVE_FCHMOD # define HAVE_FCHMOD 1 # endif #endif /* SQLITE_WASI */ #ifdef SQLITE_WASI # define osGetpid(X) (pid_t)1 #else /* Always cast the getpid() return type for compatibility with |
| ︙ | ︙ | |||
41036 41037 41038 41039 41040 41041 41042 |
/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, set *pResOut
** to a non-zero value otherwise *pResOut is set to zero. The return value
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
*/
static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
| | < > > > < < | < | | < > > | > > | | | | | | < < < < < < < < < < < < < < < < < < | < < | < | < < | | 40948 40949 40950 40951 40952 40953 40954 40955 40956 40957 40958 40959 40960 40961 40962 40963 40964 40965 40966 40967 40968 40969 40970 40971 40972 40973 40974 40975 40976 40977 40978 40979 40980 40981 40982 40983 40984 40985 40986 40987 40988 |
/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, set *pResOut
** to a non-zero value otherwise *pResOut is set to zero. The return value
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
*/
static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
#ifdef SQLITE_DEBUG
unixFile *pFile = (unixFile*)id;
#else
UNUSED_PARAMETER(id);
#endif
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
assert( pFile->eFileLock<=SHARED_LOCK );
/* The flock VFS only ever takes exclusive locks (see function flockLock).
** Therefore, if this connection is holding any lock at all, no other
** connection may be holding a RESERVED lock. So set *pResOut to 0
** in this case.
**
** Or, this connection may be holding no lock. In that case, set *pResOut to
** 0 as well. The caller will then attempt to take an EXCLUSIVE lock on the
** db in order to roll the hot journal back. If there is another connection
** holding a lock, that attempt will fail and an SQLITE_BUSY returned to
** the user. With other VFS, we try to avoid this, in order to allow a reader
** to proceed while a writer is preparing its transaction. But that won't
** work with the flock VFS - as it always takes EXCLUSIVE locks - so it is
** not a problem in this case. */
*pResOut = 0;
return SQLITE_OK;
}
/*
** Lock the file with the lock specified by parameter eFileLock - one
** of the following:
**
** (1) SHARED_LOCK
|
| ︙ | ︙ | |||
42580 42581 42582 42583 42584 42585 42586 42587 42588 42589 42590 42591 42592 42593 |
}
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
| > > > > > | 42471 42472 42473 42474 42475 42476 42477 42478 42479 42480 42481 42482 42483 42484 42485 42486 42487 42488 42489 |
}
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
case SQLITE_FCNTL_NULL_IO: {
osClose(pFile->h);
pFile->h = -1;
return SQLITE_OK;
}
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
|
| ︙ | ︙ | |||
42721 42722 42723 42724 42725 42726 42727 42728 42729 42730 42731 42732 42733 42734 |
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
/* Set the POWERSAFE_OVERWRITE flag if requested. */
if( pFd->ctrlFlags & UNIXFILE_PSOW ){
pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}
pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
}
}
#else
#include <sys/dcmd_blk.h>
#include <sys/statvfs.h>
| > | 42617 42618 42619 42620 42621 42622 42623 42624 42625 42626 42627 42628 42629 42630 42631 |
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
/* Set the POWERSAFE_OVERWRITE flag if requested. */
if( pFd->ctrlFlags & UNIXFILE_PSOW ){
pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}
pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ;
pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
}
}
#else
#include <sys/dcmd_blk.h>
#include <sys/statvfs.h>
|
| ︙ | ︙ | |||
42771 42772 42773 42774 42775 42776 42777 |
SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
** so it is ordered */
0;
}else if( !strcmp(fsInfo.f_basetype, "qnx4") ){
pFile->sectorSize = fsInfo.f_bsize;
pFile->deviceCharacteristics =
/* full bitset of atomics from max sector size and smaller */
| | | | 42668 42669 42670 42671 42672 42673 42674 42675 42676 42677 42678 42679 42680 42681 42682 42683 42684 42685 42686 42687 42688 42689 42690 |
SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
** so it is ordered */
0;
}else if( !strcmp(fsInfo.f_basetype, "qnx4") ){
pFile->sectorSize = fsInfo.f_bsize;
pFile->deviceCharacteristics =
/* full bitset of atomics from max sector size and smaller */
(((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) |
SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
** so it is ordered */
0;
}else if( strstr(fsInfo.f_basetype, "dos") ){
pFile->sectorSize = fsInfo.f_bsize;
pFile->deviceCharacteristics =
/* full bitset of atomics from max sector size and smaller */
(((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) |
SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
** so it is ordered */
0;
}else{
pFile->deviceCharacteristics =
SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */
SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
|
| ︙ | ︙ | |||
50460 50461 50462 50463 50464 50465 50466 50467 50468 50469 50470 50471 50472 50473 |
pFile->h = *phFile;
*phFile = hOldFile;
OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
hOldFile, pFile->h));
return SQLITE_OK;
}
#endif
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = 0;
int rc = winGetTempname(pFile->pVfs, &zTFile);
if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
| > > > > > | 50357 50358 50359 50360 50361 50362 50363 50364 50365 50366 50367 50368 50369 50370 50371 50372 50373 50374 50375 |
pFile->h = *phFile;
*phFile = hOldFile;
OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
hOldFile, pFile->h));
return SQLITE_OK;
}
#endif
case SQLITE_FCNTL_NULL_IO: {
(void)osCloseHandle(pFile->h);
pFile->h = NULL;
return SQLITE_OK;
}
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = 0;
int rc = winGetTempname(pFile->pVfs, &zTFile);
if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
| ︙ | ︙ | |||
50521 50522 50523 50524 50525 50526 50527 |
}
/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
winFile *p = (winFile*)id;
| | | 50423 50424 50425 50426 50427 50428 50429 50430 50431 50432 50433 50434 50435 50436 50437 |
}
/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
winFile *p = (winFile*)id;
return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ |
((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
/*
** Windows will only let you create file view mappings
** on allocation size granularity boundaries.
** During sqlite3_os_init() we do a GetSystemInfo()
|
| ︙ | ︙ | |||
51909 51910 51911 51912 51913 51914 51915 | /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char *zTmpname = 0; /* For temporary filename, if necessary. */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE | | | 51811 51812 51813 51814 51815 51816 51817 51818 51819 51820 51821 51822 51823 51824 51825 | /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char *zTmpname = 0; /* For temporary filename, if necessary. */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE int eType = flags&0x0FFF00; /* Type of file to open */ #endif int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); int isReadonly = (flags & SQLITE_OPEN_READONLY); int isReadWrite = (flags & SQLITE_OPEN_READWRITE); |
| ︙ | ︙ | |||
58129 58130 58131 58132 58133 58134 58135 | #define isOpen(pFd) ((pFd)->pMethods!=0) #ifdef SQLITE_DIRECT_OVERFLOW_READ /* ** Return true if page pgno can be read directly from the database file ** by the b-tree layer. This is the case if: ** | | > | | > > | | | > > > > > | 58031 58032 58033 58034 58035 58036 58037 58038 58039 58040 58041 58042 58043 58044 58045 58046 58047 58048 58049 58050 58051 58052 58053 58054 58055 58056 58057 58058 58059 58060 58061 58062 58063 58064 58065 58066 |
#define isOpen(pFd) ((pFd)->pMethods!=0)
#ifdef SQLITE_DIRECT_OVERFLOW_READ
/*
** Return true if page pgno can be read directly from the database file
** by the b-tree layer. This is the case if:
**
** (1) the database file is open
** (2) the VFS for the database is able to do unaligned sub-page reads
** (3) there are no dirty pages in the cache, and
** (4) the desired page is not currently in the wal file.
*/
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
assert( pPager!=0 );
assert( pPager->fd!=0 );
if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */
if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
(void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
return iRead==0; /* Condition (4) */
}
#endif
assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 );
if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd)
& SQLITE_IOCAP_SUBPAGE_READ)==0 ){
return 0; /* Case (2) */
}
return 1;
}
#endif
#ifndef SQLITE_OMIT_WAL
# define pagerUseWal(x) ((x)->pWal!=0)
#else
|
| ︙ | ︙ | |||
65169 65170 65171 65172 65173 65174 65175 | ** 12: Checkpoint sequence number ** 16: Salt-1, random integer incremented with each checkpoint ** 20: Salt-2, a different random integer changing with each ckpt ** 24: Checksum-1 (first part of checksum for first 24 bytes of header). ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). ** ** Immediately following the wal-header are zero or more frames. Each | | | 65079 65080 65081 65082 65083 65084 65085 65086 65087 65088 65089 65090 65091 65092 65093 | ** 12: Checkpoint sequence number ** 16: Salt-1, random integer incremented with each checkpoint ** 20: Salt-2, a different random integer changing with each ckpt ** 24: Checksum-1 (first part of checksum for first 24 bytes of header). ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). ** ** Immediately following the wal-header are zero or more frames. Each ** frame consists of a 24-byte frame-header followed by <page-size> bytes ** of page data. The frame-header is six big-endian 32-bit unsigned ** integer values, as follows: ** ** 0: Page number. ** 4: For commit records, the size of the database image in pages ** after the commit. For all other records, zero. ** 8: Salt-1 (copied from the header) |
| ︙ | ︙ | |||
65666 65667 65668 65669 65670 65671 65672 65673 65674 65675 65676 65677 65678 65679 |
#endif
#ifdef SQLITE_DEBUG
int nSehTry; /* Number of nested SEH_TRY{} blocks */
u8 lockError; /* True if a locking error has occurred */
#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
sqlite3 *db;
#endif
};
/*
| > | 65576 65577 65578 65579 65580 65581 65582 65583 65584 65585 65586 65587 65588 65589 65590 |
#endif
#ifdef SQLITE_DEBUG
int nSehTry; /* Number of nested SEH_TRY{} blocks */
u8 lockError; /* True if a locking error has occurred */
#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
int bGetSnapshot; /* Transaction opened for sqlite3_get_snapshot() */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
sqlite3 *db;
#endif
};
/*
|
| ︙ | ︙ | |||
67558 67559 67560 67561 67562 67563 67564 |
pWal->pWiValue = 0;
}
return SQLITE_IOERR_IN_PAGE;
}
/*
** Assert that the Wal.lockMask mask, which indicates the locks held
| | | 67469 67470 67471 67472 67473 67474 67475 67476 67477 67478 67479 67480 67481 67482 67483 |
pWal->pWiValue = 0;
}
return SQLITE_IOERR_IN_PAGE;
}
/*
** Assert that the Wal.lockMask mask, which indicates the locks held
** by the connection, is consistent with the Wal.readLock, Wal.writeLock
** and Wal.ckptLock variables. To be used as:
**
** assert( walAssertLockmask(pWal) );
*/
static int walAssertLockmask(Wal *pWal){
if( pWal->exclusiveMode==0 ){
static const int S = 1;
|
| ︙ | ︙ | |||
68222 68223 68224 68225 68226 68227 68228 | assert( pWal->nWiData>0 ); assert( pWal->apWiData[0]!=0 ); pInfo = walCkptInfo(pWal); SEH_INJECT_FAULT; if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame #ifdef SQLITE_ENABLE_SNAPSHOT | | | 68133 68134 68135 68136 68137 68138 68139 68140 68141 68142 68143 68144 68145 68146 68147 |
assert( pWal->nWiData>0 );
assert( pWal->apWiData[0]!=0 );
pInfo = walCkptInfo(pWal);
SEH_INJECT_FAULT;
if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
#ifdef SQLITE_ENABLE_SNAPSHOT
&& ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
#endif
){
/* The WAL has been completely backfilled (or it is empty).
** and can be safely ignored.
*/
rc = walLockShared(pWal, WAL_READ_LOCK(0));
walShmBarrier(pWal);
|
| ︙ | ︙ | |||
69622 69623 69624 69625 69626 69627 69628 |
/* Try to open on pSnapshot when the next read-transaction starts
*/
SQLITE_PRIVATE void sqlite3WalSnapshotOpen(
Wal *pWal,
sqlite3_snapshot *pSnapshot
){
| > > > > > > > > > > > | > > | 69533 69534 69535 69536 69537 69538 69539 69540 69541 69542 69543 69544 69545 69546 69547 69548 69549 69550 69551 69552 69553 69554 69555 69556 69557 69558 69559 69560 |
/* Try to open on pSnapshot when the next read-transaction starts
*/
SQLITE_PRIVATE void sqlite3WalSnapshotOpen(
Wal *pWal,
sqlite3_snapshot *pSnapshot
){
if( pSnapshot && ((WalIndexHdr*)pSnapshot)->iVersion==0 ){
/* iVersion==0 means that this is a call to sqlite3_snapshot_get(). In
** this case set the bGetSnapshot flag so that if the call to
** sqlite3_snapshot_get() is about to read transaction on this wal
** file, it does not take read-lock 0 if the wal file has been completely
** checkpointed. Taking read-lock 0 would work, but then it would be
** possible for a subsequent writer to destroy the snapshot even while
** this connection is holding its read-transaction open. This is contrary
** to user expectations, so we avoid it by not taking read-lock 0. */
pWal->bGetSnapshot = 1;
}else{
pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
pWal->bGetSnapshot = 0;
}
}
/*
** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
** p1 is older than p2 and zero if p1 and p2 are the same snapshot.
*/
SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
|
| ︙ | ︙ | |||
75502 75503 75504 75505 75506 75507 75508 75509 75510 75511 75512 75513 75514 75515 |
** sufficient storage to hold a cursor. The BtCursor object is opaque
** to users so they cannot do the sizeof() themselves - they must call
** this routine.
*/
SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){
return ROUND8(sizeof(BtCursor));
}
/*
** Initialize memory that will be converted into a BtCursor object.
**
** The simple approach here would be to memset() the entire object
** to zero. But it turns out that the apPage[] and aiIdx[] arrays
** do not need to be zeroed and they are large, so we can save a lot
| > > > > > > > > > > > > > > > > > > > | 75426 75427 75428 75429 75430 75431 75432 75433 75434 75435 75436 75437 75438 75439 75440 75441 75442 75443 75444 75445 75446 75447 75448 75449 75450 75451 75452 75453 75454 75455 75456 75457 75458 |
** sufficient storage to hold a cursor. The BtCursor object is opaque
** to users so they cannot do the sizeof() themselves - they must call
** this routine.
*/
SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){
return ROUND8(sizeof(BtCursor));
}
#ifdef SQLITE_DEBUG
/*
** Return true if and only if the Btree object will be automatically
** closed with the BtCursor closes. This is used within assert() statements
** only.
*/
SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(
Btree *pBtree, /* the btree object */
BtCursor *pCur /* Corresponding cursor */
){
BtShared *pBt = pBtree->pBt;
if( (pBt->openFlags & BTREE_SINGLE)==0 ) return 0;
if( pBt->pCursor!=pCur ) return 0;
if( pCur->pNext!=0 ) return 0;
if( pCur->pBtree!=pBtree ) return 0;
return 1;
}
#endif
/*
** Initialize memory that will be converted into a BtCursor object.
**
** The simple approach here would be to memset() the entire object
** to zero. But it turns out that the apPage[] and aiIdx[] arrays
** do not need to be zeroed and they are large, so we can save a lot
|
| ︙ | ︙ | |||
84536 84537 84538 84539 84540 84541 84542 |
if( pList ){
apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal);
if( apVal==0 ){
rc = SQLITE_NOMEM_BKPT;
goto value_from_function_out;
}
for(i=0; i<nVal; i++){
| | > | 84479 84480 84481 84482 84483 84484 84485 84486 84487 84488 84489 84490 84491 84492 84493 84494 |
if( pList ){
apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal);
if( apVal==0 ){
rc = SQLITE_NOMEM_BKPT;
goto value_from_function_out;
}
for(i=0; i<nVal; i++){
rc = sqlite3Stat4ValueFromExpr(pCtx->pParse, pList->a[i].pExpr, aff,
&apVal[i]);
if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out;
}
}
pVal = valueNew(db, pCtx);
if( pVal==0 ){
rc = SQLITE_NOMEM_BKPT;
|
| ︙ | ︙ | |||
89569 89570 89571 89572 89573 89574 89575 | } /* The following two functions are used only within testcase() to prove ** test coverage. These functions do no exist for production builds. ** We must use separate SQLITE_NOINLINE functions here, since otherwise ** optimizer code movement causes gcov to become very confused. */ | | < < < < < < < | 89513 89514 89515 89516 89517 89518 89519 89520 89521 89522 89523 89524 89525 89526 89527 89528 89529 89530 89531 89532 89533 89534 89535 89536 89537 89538 89539 89540 89541 |
}
/* The following two functions are used only within testcase() to prove
** test coverage. These functions do no exist for production builds.
** We must use separate SQLITE_NOINLINE functions here, since otherwise
** optimizer code movement causes gcov to become very confused.
*/
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; }
static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; }
#endif
/*
** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
** number. Return negative, zero, or positive if the first (i64) is less than,
** equal to, or greater than the second (double).
*/
SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){
if( sqlite3IsNaN(r) ){
/* SQLite considers NaN to be a NULL. And all integer values are greater
** than NULL */
return 1;
}else{
i64 y;
if( r<-9223372036854775808.0 ) return +1;
if( r>=9223372036854775808.0 ) return -1;
y = (i64)r;
if( i<y ) return -1;
if( i>y ) return +1;
|
| ︙ | ︙ | |||
90593 90594 90595 90596 90597 90598 90599 90600 90601 90602 90603 90604 90605 90606 90607 90608 90609 90610 90611 90612 90613 |
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
sqlite3DbNNFreeNN(db, preupdate.aNew);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
** 2004 May 26
| > > > > > > > > | 90530 90531 90532 90533 90534 90535 90536 90537 90538 90539 90540 90541 90542 90543 90544 90545 90546 90547 90548 90549 90550 90551 90552 90553 90554 90555 90556 90557 90558 |
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
sqlite3VdbeMemRelease(&preupdate.oldipk);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
sqlite3DbNNFreeNN(db, preupdate.aNew);
}
if( preupdate.apDflt ){
int i;
for(i=0; i<pTab->nCol; i++){
sqlite3ValueFree(preupdate.apDflt[i]);
}
sqlite3DbFree(db, preupdate.apDflt);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
** 2004 May 26
|
| ︙ | ︙ | |||
92228 92229 92230 92231 92232 92233 92234 92235 92236 92237 92238 92239 92240 92241 |
** out of range, then SQLITE_RANGE is returned. Otherwise SQLITE_OK.
**
** A successful evaluation of this routine acquires the mutex on p.
** the mutex is released if any kind of error occurs.
**
** The error code stored in database p->db is overwritten with the return
** value in any case.
*/
static int vdbeUnbind(Vdbe *p, unsigned int i){
Mem *pVar;
if( vdbeSafetyNotNull(p) ){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(p->db->mutex);
| > > > > > > > > > > > | 92173 92174 92175 92176 92177 92178 92179 92180 92181 92182 92183 92184 92185 92186 92187 92188 92189 92190 92191 92192 92193 92194 92195 92196 92197 |
** out of range, then SQLITE_RANGE is returned. Otherwise SQLITE_OK.
**
** A successful evaluation of this routine acquires the mutex on p.
** the mutex is released if any kind of error occurs.
**
** The error code stored in database p->db is overwritten with the return
** value in any case.
**
** (tag-20240917-01) If vdbeUnbind(p,(u32)(i-1)) returns SQLITE_OK,
** that means all of the the following will be true:
**
** p!=0
** p->pVar!=0
** i>0
** i<=p->nVar
**
** An assert() is normally added after vdbeUnbind() to help static analyzers
** realize this.
*/
static int vdbeUnbind(Vdbe *p, unsigned int i){
Mem *pVar;
if( vdbeSafetyNotNull(p) ){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(p->db->mutex);
|
| ︙ | ︙ | |||
92285 92286 92287 92288 92289 92290 92291 92292 92293 92294 92295 92296 92297 92298 |
){
Vdbe *p = (Vdbe *)pStmt;
Mem *pVar;
int rc;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
if( zData!=0 ){
pVar = &p->aVar[i-1];
rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
if( rc==SQLITE_OK && encoding!=0 ){
rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
}
if( rc ){
| > | 92241 92242 92243 92244 92245 92246 92247 92248 92249 92250 92251 92252 92253 92254 92255 |
){
Vdbe *p = (Vdbe *)pStmt;
Mem *pVar;
int rc;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
if( zData!=0 ){
pVar = &p->aVar[i-1];
rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
if( rc==SQLITE_OK && encoding!=0 ){
rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
}
if( rc ){
|
| ︙ | ︙ | |||
92334 92335 92336 92337 92338 92339 92340 92341 92342 92343 92344 92345 92346 92347 92348 92349 92350 92351 92352 92353 92354 92355 92356 92357 92358 92359 92360 92361 92362 92363 92364 92365 92366 92367 92368 92369 92370 92371 92372 92373 92374 92375 92376 92377 92378 92379 92380 92381 92382 92383 92384 92385 |
return bindText(pStmt, i, zData, nData, xDel, 0);
}
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
}
SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
return sqlite3_bind_int64(p, i, (i64)iValue);
}
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
}
SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
int rc;
Vdbe *p = (Vdbe*)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
}
SQLITE_API int sqlite3_bind_pointer(
sqlite3_stmt *pStmt,
int i,
void *pPtr,
const char *zPTtype,
void (*xDestructor)(void*)
){
int rc;
Vdbe *p = (Vdbe*)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor);
sqlite3_mutex_leave(p->db->mutex);
}else if( xDestructor ){
xDestructor(pPtr);
}
return rc;
}
| > > > > | 92291 92292 92293 92294 92295 92296 92297 92298 92299 92300 92301 92302 92303 92304 92305 92306 92307 92308 92309 92310 92311 92312 92313 92314 92315 92316 92317 92318 92319 92320 92321 92322 92323 92324 92325 92326 92327 92328 92329 92330 92331 92332 92333 92334 92335 92336 92337 92338 92339 92340 92341 92342 92343 92344 92345 92346 |
return bindText(pStmt, i, zData, nData, xDel, 0);
}
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
}
SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
return sqlite3_bind_int64(p, i, (i64)iValue);
}
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
}
SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
int rc;
Vdbe *p = (Vdbe*)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
}
SQLITE_API int sqlite3_bind_pointer(
sqlite3_stmt *pStmt,
int i,
void *pPtr,
const char *zPTtype,
void (*xDestructor)(void*)
){
int rc;
Vdbe *p = (Vdbe*)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor);
sqlite3_mutex_leave(p->db->mutex);
}else if( xDestructor ){
xDestructor(pPtr);
}
return rc;
}
|
| ︙ | ︙ | |||
92453 92454 92455 92456 92457 92458 92459 92460 92461 92462 92463 92464 92465 92466 |
return rc;
}
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_INCRBLOB
sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
#else
rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
#endif
sqlite3_mutex_leave(p->db->mutex);
}
| > | 92414 92415 92416 92417 92418 92419 92420 92421 92422 92423 92424 92425 92426 92427 92428 |
return rc;
}
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
#ifndef SQLITE_OMIT_INCRBLOB
sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
#else
rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
#endif
sqlite3_mutex_leave(p->db->mutex);
}
|
| ︙ | ︙ | |||
92787 92788 92789 92790 92791 92792 92793 |
iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
goto preupdate_old_out;
}
| < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > | | | | | > | 92749 92750 92751 92752 92753 92754 92755 92756 92757 92758 92759 92760 92761 92762 92763 92764 92765 92766 92767 92768 92769 92770 92771 92772 92773 92774 92775 92776 92777 92778 92779 92780 92781 92782 92783 92784 92785 92786 92787 92788 92789 92790 92791 92792 92793 92794 92795 92796 92797 92798 92799 92800 92801 92802 92803 92804 92805 92806 92807 92808 92809 92810 92811 92812 92813 92814 92815 92816 92817 92818 92819 92820 |
iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
goto preupdate_old_out;
}
if( iIdx==p->pTab->iPKey ){
*ppValue = pMem = &p->oldipk;
sqlite3VdbeMemSetInt64(pMem, p->iKey1);
}else{
/* If the old.* record has not yet been loaded into memory, do so now. */
if( p->pUnpacked==0 ){
u32 nRec;
u8 *aRec;
assert( p->pCsr->eCurType==CURTYPE_BTREE );
nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
aRec = sqlite3DbMallocRaw(db, nRec);
if( !aRec ) goto preupdate_old_out;
rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
if( rc==SQLITE_OK ){
p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
}
if( rc!=SQLITE_OK ){
sqlite3DbFree(db, aRec);
goto preupdate_old_out;
}
p->aRecord = aRec;
}
pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
if( iIdx>=p->pUnpacked->nField ){
/* This occurs when the table has been extended using ALTER TABLE
** ADD COLUMN. The value to return is the default value of the column. */
Column *pCol = &p->pTab->aCol[iIdx];
if( pCol->iDflt>0 ){
if( p->apDflt==0 ){
int nByte = sizeof(sqlite3_value*)*p->pTab->nCol;
p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte);
if( p->apDflt==0 ) goto preupdate_old_out;
}
if( p->apDflt[iIdx]==0 ){
sqlite3_value *pVal = 0;
Expr *pDflt;
assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) );
pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal);
if( rc==SQLITE_OK && pVal==0 ){
rc = SQLITE_CORRUPT_BKPT;
}
p->apDflt[iIdx] = pVal;
}
*ppValue = p->apDflt[iIdx];
}else{
*ppValue = (sqlite3_value *)columnNullValue();
}
}else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
if( pMem->flags & (MEM_Int|MEM_IntReal) ){
testcase( pMem->flags & MEM_Int );
testcase( pMem->flags & MEM_IntReal );
sqlite3VdbeMemRealify(pMem);
}
}
}
preupdate_old_out:
sqlite3Error(db, rc);
return sqlite3ApiExit(db, rc);
}
|
| ︙ | ︙ | |||
93365 93366 93367 93368 93369 93370 93371 93372 93373 93374 93375 93376 93377 93378 | ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. */ /* #include "sqliteInt.h" */ /* #include "vdbeInt.h" */ /* ** Invoke this macro on memory cells just prior to changing the ** value of the cell. This macro verifies that shallow copies are ** not misused. A shallow copy of a string or blob just copies a ** pointer to the string or blob, not the content. If the original ** is changed while the copy is still in use, the string or blob might ** be changed out from under the copy. This macro verifies that nothing | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 93354 93355 93356 93357 93358 93359 93360 93361 93362 93363 93364 93365 93366 93367 93368 93369 93370 93371 93372 93373 93374 93375 93376 93377 93378 93379 93380 93381 93382 93383 93384 93385 93386 93387 93388 93389 93390 93391 93392 93393 93394 93395 93396 93397 93398 93399 93400 93401 93402 93403 93404 93405 93406 93407 93408 93409 93410 93411 93412 93413 93414 93415 93416 93417 93418 93419 93420 93421 93422 93423 93424 93425 93426 93427 93428 93429 93430 93431 93432 93433 93434 93435 93436 93437 93438 93439 93440 93441 93442 93443 93444 93445 93446 93447 93448 93449 93450 93451 93452 93453 93454 93455 93456 93457 93458 93459 93460 93461 93462 93463 93464 93465 |
** of the code in this file is, therefore, important. See other comments
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
*/
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */
/*
** High-resolution hardware timer used for debugging and testing only.
*/
#if defined(VDBE_PROFILE) \
|| defined(SQLITE_PERFORMANCE_TRACE) \
|| defined(SQLITE_ENABLE_STMT_SCANSTATUS)
/************** Include hwtime.h in the middle of vdbe.c *********************/
/************** Begin file hwtime.h ******************************************/
/*
** 2008 May 27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains inline asm code for retrieving "high-performance"
** counters for x86 and x86_64 class CPUs.
*/
#ifndef SQLITE_HWTIME_H
#define SQLITE_HWTIME_H
/*
** The following routine only works on Pentium-class (or newer) processors.
** It uses the RDTSC opcode to read the cycle count value out of the
** processor and returns that value. This can be used for high-res
** profiling.
*/
#if !defined(__STRICT_ANSI__) && \
(defined(__GNUC__) || defined(_MSC_VER)) && \
(defined(i386) || defined(__i386__) || defined(_M_IX86))
#if defined(__GNUC__)
__inline__ sqlite_uint64 sqlite3Hwtime(void){
unsigned int lo, hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return (sqlite_uint64)hi << 32 | lo;
}
#elif defined(_MSC_VER)
__declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
__asm {
rdtsc
ret ; return value at EDX:EAX
}
}
#endif
#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
__inline__ sqlite_uint64 sqlite3Hwtime(void){
unsigned int lo, hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return (sqlite_uint64)hi << 32 | lo;
}
#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))
__inline__ sqlite_uint64 sqlite3Hwtime(void){
unsigned long long retval;
unsigned long junk;
__asm__ __volatile__ ("\n\
1: mftbu %1\n\
mftb %L0\n\
mftbu %0\n\
cmpw %0,%1\n\
bne 1b"
: "=r" (retval), "=r" (junk));
return retval;
}
#else
/*
** asm() is needed for hardware timing support. Without asm(),
** disable the sqlite3Hwtime() routine.
**
** sqlite3Hwtime() is only used for some obscure debugging
** and analysis configurations, not in any deliverable, so this
** should not be a great loss.
*/
SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
#endif
#endif /* !defined(SQLITE_HWTIME_H) */
/************** End of hwtime.h **********************************************/
/************** Continuing where we left off in vdbe.c ***********************/
#endif
/*
** Invoke this macro on memory cells just prior to changing the
** value of the cell. This macro verifies that shallow copies are
** not misused. A shallow copy of a string or blob just copies a
** pointer to the string or blob, not the content. If the original
** is changed while the copy is still in use, the string or blob might
** be changed out from under the copy. This macro verifies that nothing
|
| ︙ | ︙ | |||
97873 97874 97875 97876 97877 97878 97879 97880 97881 97882 97883 97884 97885 97886 97887 97888 |
pCx->pgnoRoot = SCHEMA_ROOT;
rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR,
0, pCx->uc.pCursor);
pCx->isTable = 1;
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
if( rc ){
sqlite3BtreeClose(pCx->ub.pBtx);
}
}
}
if( rc ) goto abort_due_to_error;
pCx->nullRow = 1;
break;
}
| > > > > > | 97960 97961 97962 97963 97964 97965 97966 97967 97968 97969 97970 97971 97972 97973 97974 97975 97976 97977 97978 97979 97980 |
pCx->pgnoRoot = SCHEMA_ROOT;
rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR,
0, pCx->uc.pCursor);
pCx->isTable = 1;
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
assert( p->apCsr[pOp->p1]==pCx );
if( rc ){
assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
sqlite3BtreeClose(pCx->ub.pBtx);
p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */
}else{
assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
}
}
}
if( rc ) goto abort_due_to_error;
pCx->nullRow = 1;
break;
}
|
| ︙ | ︙ | |||
102392 102393 102394 102395 102396 102397 102398 | ** ** For loop elements, P3 is the estimated code of each invocation of this ** element. ** ** As with all opcodes, the meanings of the parameters for OP_Explain ** are subject to change from one release to the next. Applications ** should not attempt to interpret or use any of the information | | | 102484 102485 102486 102487 102488 102489 102490 102491 102492 102493 102494 102495 102496 102497 102498 |
**
** For loop elements, P3 is the estimated code of each invocation of this
** element.
**
** As with all opcodes, the meanings of the parameters for OP_Explain
** are subject to change from one release to the next. Applications
** should not attempt to interpret or use any of the information
** contained in the OP_Explain opcode. The information provided by this
** opcode is intended for testing and debugging use only.
*/
default: { /* This is really OP_Noop, OP_Explain */
assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain );
break;
}
|
| ︙ | ︙ | |||
107488 107489 107490 107491 107492 107493 107494 |
/* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match
** if there is a single VIEW candidate or if there is a single
** non-VIEW candidate plus multiple VIEW candidates. In other
** words non-VIEW candidate terms take precedence over VIEWs.
*/
if( cntTab==0
|| (cntTab==1
| | | 107580 107581 107582 107583 107584 107585 107586 107587 107588 107589 107590 107591 107592 107593 107594 |
/* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match
** if there is a single VIEW candidate or if there is a single
** non-VIEW candidate plus multiple VIEW candidates. In other
** words non-VIEW candidate terms take precedence over VIEWs.
*/
if( cntTab==0
|| (cntTab==1
&& pMatch!=0
&& ALWAYS(pMatch->pSTab!=0)
&& (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0
&& (pTab->tabFlags & TF_Ephemeral)==0)
){
cntTab = 1;
pMatch = pItem;
}else{
|
| ︙ | ︙ | |||
108121 108122 108123 108124 108125 108126 108127 |
}
return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr);
}
/* Resolve function names
*/
case TK_FUNCTION: {
| | | > > | 108213 108214 108215 108216 108217 108218 108219 108220 108221 108222 108223 108224 108225 108226 108227 108228 108229 108230 108231 108232 108233 108234 108235 108236 108237 108238 108239 108240 108241 108242 |
}
return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr);
}
/* Resolve function names
*/
case TK_FUNCTION: {
ExprList *pList; /* The argument list */
int n; /* Number of arguments */
int no_such_func = 0; /* True if no such function exists */
int wrong_num_args = 0; /* True if wrong number of arguments */
int is_agg = 0; /* True if is an aggregate function */
const char *zId; /* The function name. */
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin));
#ifndef SQLITE_OMIT_WINDOWFUNC
Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
#endif
assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER );
pList = pExpr->x.pList;
n = pList ? pList->nExpr : 0;
zId = pExpr->u.zToken;
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0);
if( pDef==0 ){
no_such_func = 1;
}else{
|
| ︙ | ︙ | |||
108183 108184 108185 108186 108187 108188 108189 108190 108191 108192 108193 108194 108195 108196 |
pNC->nNcErr++;
}
pExpr->op = TK_NULL;
return WRC_Prune;
}
}
#endif
if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
/* For the purposes of the EP_ConstFunc flag, date and time
** functions and other functions that change slowly are considered
** constant because they are constant for the duration of one query.
** This allows them to be factored out of inner loops. */
ExprSetProperty(pExpr,EP_ConstFunc);
}
| > > > > > > > > > > > > > > > > > > | 108277 108278 108279 108280 108281 108282 108283 108284 108285 108286 108287 108288 108289 108290 108291 108292 108293 108294 108295 108296 108297 108298 108299 108300 108301 108302 108303 108304 108305 108306 108307 108308 |
pNC->nNcErr++;
}
pExpr->op = TK_NULL;
return WRC_Prune;
}
}
#endif
/* If the function may call sqlite3_value_subtype(), then set the
** EP_SubtArg flag on all of its argument expressions. This prevents
** where.c from replacing the expression with a value read from an
** index on the same expression, which will not have the correct
** subtype. Also set the flag if the function expression itself is
** an EP_SubtArg expression. In this case subtypes are required as
** the function may return a value with a subtype back to its
** caller using sqlite3_result_value(). */
if( (pDef->funcFlags & SQLITE_SUBTYPE)
|| ExprHasProperty(pExpr, EP_SubtArg)
){
int ii;
for(ii=0; ii<n; ii++){
ExprSetProperty(pList->a[ii].pExpr, EP_SubtArg);
}
}
if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
/* For the purposes of the EP_ConstFunc flag, date and time
** functions and other functions that change slowly are considered
** constant because they are constant for the duration of one query.
** This allows them to be factored out of inner loops. */
ExprSetProperty(pExpr,EP_ConstFunc);
}
|
| ︙ | ︙ | |||
111949 111950 111951 111952 111953 111954 111955 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) ** ** (4) If pSrc is the right operand of a LEFT JOIN, then... ** (4a) pExpr must come from an ON clause.. ** (4b) and specifically the ON clause associated with the LEFT JOIN. ** | | | 112061 112062 112063 112064 112065 112066 112067 112068 112069 112070 112071 112072 112073 112074 112075 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) ** ** (4) If pSrc is the right operand of a LEFT JOIN, then... ** (4a) pExpr must come from an ON clause.. ** (4b) and specifically the ON clause associated with the LEFT JOIN. ** ** (5) If pSrc is the right operand of a LEFT JOIN or the left ** operand of a RIGHT JOIN, then pExpr must be from the WHERE ** clause, not an ON clause. ** ** (6) Either: ** ** (6a) pExpr does not originate in an ON or USING clause, or ** |
| ︙ | ︙ | |||
113856 113857 113858 113859 113860 113861 113862 113863 113864 113865 113866 113867 113868 113869 |
(aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
break;
}
#endif /* !defined(SQLITE_UNTESTABLE) */
}
return target;
}
/*
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
** If it is, then resolve the expression by reading from the index and
** return the register into which the value has been read. If pExpr is
** not an indexed expression, then return negative.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 113968 113969 113970 113971 113972 113973 113974 113975 113976 113977 113978 113979 113980 113981 113982 113983 113984 113985 113986 113987 113988 113989 113990 113991 113992 113993 113994 113995 113996 113997 113998 113999 114000 114001 114002 114003 114004 114005 114006 114007 114008 114009 114010 114011 114012 114013 114014 114015 114016 114017 114018 114019 114020 114021 114022 114023 114024 114025 114026 114027 114028 114029 114030 114031 114032 114033 114034 |
(aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
break;
}
#endif /* !defined(SQLITE_UNTESTABLE) */
}
return target;
}
/*
** Expression Node callback for sqlite3ExprCanReturnSubtype().
**
** Only a function call is able to return a subtype. So if the node
** is not a function call, return WRC_Prune immediately.
**
** A function call is able to return a subtype if it has the
** SQLITE_RESULT_SUBTYPE property.
**
** Assume that every function is able to pass-through a subtype from
** one of its argument (using sqlite3_result_value()). Most functions
** are not this way, but we don't have a mechanism to distinguish those
** that are from those that are not, so assume they all work this way.
** That means that if one of its arguments is another function and that
** other function is able to return a subtype, then this function is
** able to return a subtype.
*/
static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){
int n;
FuncDef *pDef;
sqlite3 *db;
if( pExpr->op!=TK_FUNCTION ){
return WRC_Prune;
}
assert( ExprUseXList(pExpr) );
db = pWalker->pParse->db;
n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0;
pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
pWalker->eCode = 1;
return WRC_Prune;
}
return WRC_Continue;
}
/*
** Return TRUE if expression pExpr is able to return a subtype.
**
** A TRUE return does not guarantee that a subtype will be returned.
** It only indicates that a subtype return is possible. False positives
** are acceptable as they only disable an optimization. False negatives,
** on the other hand, can lead to incorrect answers.
*/
static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){
Walker w;
memset(&w, 0, sizeof(w));
w.pParse = pParse;
w.xExprCallback = exprNodeCanReturnSubtype;
sqlite3WalkExpr(&w, pExpr);
return w.eCode;
}
/*
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
** If it is, then resolve the expression by reading from the index and
** return the register into which the value has been read. If pExpr is
** not an indexed expression, then return negative.
*/
|
| ︙ | ︙ | |||
113889 113890 113891 113892 113893 113894 113895 113896 113897 113898 113899 113900 113901 113902 |
|| (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT)
|| (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC)
){
/* Affinity mismatch on a generated column */
continue;
}
v = pParse->pVdbe;
assert( v!=0 );
if( p->bMaybeNullRow ){
/* If the index is on a NULL row due to an outer join, then we
** cannot extract the value from the index. The value must be
** computed using the original expression. */
int addr = sqlite3VdbeCurrentAddr(v);
| > > > > > > > > > > > | 114054 114055 114056 114057 114058 114059 114060 114061 114062 114063 114064 114065 114066 114067 114068 114069 114070 114071 114072 114073 114074 114075 114076 114077 114078 |
|| (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT)
|| (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC)
){
/* Affinity mismatch on a generated column */
continue;
}
/* Functions that might set a subtype should not be replaced by the
** value taken from an expression index if they are themselves an
** argument to another scalar function or aggregate.
** https://sqlite.org/forum/forumpost/68d284c86b082c3e */
if( ExprHasProperty(pExpr, EP_SubtArg)
&& sqlite3ExprCanReturnSubtype(pParse, pExpr)
){
continue;
}
v = pParse->pVdbe;
assert( v!=0 );
if( p->bMaybeNullRow ){
/* If the index is on a NULL row due to an outer join, then we
** cannot extract the value from the index. The value must be
** computed using the original expression. */
int addr = sqlite3VdbeCurrentAddr(v);
|
| ︙ | ︙ | |||
115419 115420 115421 115422 115423 115424 115425 115426 | ** or NULL value - then the VDBE currently being prepared is configured ** to re-prepare each time a new value is bound to variable pVar. ** ** Additionally, if pExpr is a simple SQL value and the value is the ** same as that currently bound to variable pVar, non-zero is returned. ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ | > > > | | > > > > | < | 115595 115596 115597 115598 115599 115600 115601 115602 115603 115604 115605 115606 115607 115608 115609 115610 115611 115612 115613 115614 115615 115616 115617 115618 115619 115620 115621 115622 115623 115624 115625 115626 115627 115628 115629 115630 115631 115632 115633 115634 115635 115636 115637 115638 115639 |
** or NULL value - then the VDBE currently being prepared is configured
** to re-prepare each time a new value is bound to variable pVar.
**
** Additionally, if pExpr is a simple SQL value and the value is the
** same as that currently bound to variable pVar, non-zero is returned.
** Otherwise, if the values are not the same or if pExpr is not a simple
** SQL value, zero is returned.
**
** If the SQLITE_EnableQPSG flag is set on the database connection, then
** this routine always returns false.
*/
static SQLITE_NOINLINE int exprCompareVariable(
const Parse *pParse,
const Expr *pVar,
const Expr *pExpr
){
int res = 2;
int iVar;
sqlite3_value *pL, *pR = 0;
if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){
return 0;
}
if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2;
sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
if( pR ){
iVar = pVar->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB);
if( pL ){
if( sqlite3_value_type(pL)==SQLITE_TEXT ){
sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */
}
res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0;
}
sqlite3ValueFree(pR);
sqlite3ValueFree(pL);
}
return res;
}
/*
** Do a deep comparison of two expression trees. Return 0 if the two
** expressions are completely identical. Return 1 if they differ only
** by a COLLATE operator at the top level. Return 2 if there are differences
|
| ︙ | ︙ | |||
115469 115470 115471 115472 115473 115474 115475 | ** returns 2, then you do not really know for certain if the two ** expressions are the same. But if you get a 0 or 1 return, then you ** can be sure the expressions are the same. In the places where ** this routine is used, it does not hurt to get an extra 2 - that ** just might result in some slightly slower code. But returning ** an incorrect 0 or 1 could lead to a malfunction. ** | | | | < < | | | | 115651 115652 115653 115654 115655 115656 115657 115658 115659 115660 115661 115662 115663 115664 115665 115666 115667 115668 115669 115670 115671 115672 115673 115674 115675 115676 115677 115678 115679 115680 115681 |
** returns 2, then you do not really know for certain if the two
** expressions are the same. But if you get a 0 or 1 return, then you
** can be sure the expressions are the same. In the places where
** this routine is used, it does not hurt to get an extra 2 - that
** just might result in some slightly slower code. But returning
** an incorrect 0 or 1 could lead to a malfunction.
**
** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE
** terms in pA with bindings in pParse->pReprepare can be matched against
** literals in pB. The pParse->pVdbe->expmask bitmask is updated for
** each variable referenced.
*/
SQLITE_PRIVATE int sqlite3ExprCompare(
const Parse *pParse,
const Expr *pA,
const Expr *pB,
int iTab
){
u32 combinedFlags;
if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
}
if( pParse && pA->op==TK_VARIABLE ){
return exprCompareVariable(pParse, pA, pB);
}
combinedFlags = pA->flags | pB->flags;
if( combinedFlags & EP_IntValue ){
if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
return 0;
}
return 2;
|
| ︙ | ︙ | |||
115681 115682 115683 115684 115685 115686 115687 115688 115689 115690 115691 115692 115693 |
case TK_BITNOT:
case TK_NOT: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
}
return 0;
}
/*
** Return true if we can prove the pE2 will always be true if pE1 is
** true. Return false if we cannot complete the proof or if pE2 might
** be false. Examples:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > | 115861 115862 115863 115864 115865 115866 115867 115868 115869 115870 115871 115872 115873 115874 115875 115876 115877 115878 115879 115880 115881 115882 115883 115884 115885 115886 115887 115888 115889 115890 115891 115892 115893 115894 115895 115896 115897 115898 115899 115900 115901 115902 115903 115904 115905 115906 115907 115908 115909 115910 115911 115912 115913 115914 115915 115916 115917 115918 115919 115920 115921 115922 115923 115924 115925 115926 115927 115928 115929 115930 115931 115932 115933 115934 115935 115936 115937 115938 115939 |
case TK_BITNOT:
case TK_NOT: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
}
return 0;
}
/*
** Return true if the boolean value of the expression is always either
** FALSE or NULL.
*/
static int sqlite3ExprIsNotTrue(Expr *pExpr){
int v;
if( pExpr->op==TK_NULL ) return 1;
if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1;
v = 1;
if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1;
return 0;
}
/*
** Return true if the expression is one of the following:
**
** CASE WHEN x THEN y END
** CASE WHEN x THEN y ELSE NULL END
** CASE WHEN x THEN y ELSE false END
** iif(x,y)
** iif(x,y,NULL)
** iif(x,y,false)
*/
static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){
ExprList *pList;
if( pExpr->op==TK_FUNCTION ){
const char *z = pExpr->u.zToken;
FuncDef *pDef;
if( (z[0]!='i' && z[0]!='I') ) return 0;
if( pExpr->x.pList==0 ) return 0;
pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0);
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
if( pDef==0 ) return 0;
#else
if( NEVER(pDef==0) ) return 0;
#endif
if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0;
if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0;
}else if( pExpr->op==TK_CASE ){
if( pExpr->pLeft!=0 ) return 0;
}else{
return 0;
}
pList = pExpr->x.pList;
assert( pList!=0 );
if( pList->nExpr==2 ) return 1;
if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1;
return 0;
}
/*
** Return true if we can prove the pE2 will always be true if pE1 is
** true. Return false if we cannot complete the proof or if pE2 might
** be false. Examples:
**
** pE1: x==5 pE2: x==5 Result: true
** pE1: x>0 pE2: x==5 Result: false
** pE1: x=21 pE2: x=21 OR y=43 Result: true
** pE1: x!=123 pE2: x IS NOT NULL Result: true
** pE1: x!=?1 pE2: x IS NOT NULL Result: true
** pE1: x IS NULL pE2: x IS NOT NULL Result: false
** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
** pE1: iif(x,y) pE2: x Result: true
** PE1: iif(x,y,0) pE2: x Result: true
**
** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
** Expr.iTable<0 then assume a table number given by iTab.
**
** If pParse is not NULL, then the values of bound variables in pE1 are
** compared against literal values in pE2 and pParse->pVdbe->expmask is
** modified to record which bound variables are referenced. If pParse
|
| ︙ | ︙ | |||
115727 115728 115729 115730 115731 115732 115733 115734 115735 115736 115737 115738 115739 115740 |
return 1;
}
if( pE2->op==TK_NOTNULL
&& exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
){
return 1;
}
return 0;
}
/* This is a helper function to impliesNotNullRow(). In this routine,
** set pWalker->eCode to one only if *both* of the input expressions
** separately have the implies-not-null-row property.
*/
| > > > | 115959 115960 115961 115962 115963 115964 115965 115966 115967 115968 115969 115970 115971 115972 115973 115974 115975 |
return 1;
}
if( pE2->op==TK_NOTNULL
&& exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
){
return 1;
}
if( sqlite3ExprIsIIF(pParse->db, pE1) ){
return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab);
}
return 0;
}
/* This is a helper function to impliesNotNullRow(). In this routine,
** set pWalker->eCode to one only if *both* of the input expressions
** separately have the implies-not-null-row property.
*/
|
| ︙ | ︙ | |||
120687 120688 120689 120690 120691 120692 120693 |
while( sqlite3_step(pStmt)==SQLITE_ROW ){
int nIdxCol = 1; /* Number of columns in stat4 records */
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
int nSample; /* Number of samples */
| | | | 120922 120923 120924 120925 120926 120927 120928 120929 120930 120931 120932 120933 120934 120935 120936 120937 |
while( sqlite3_step(pStmt)==SQLITE_ROW ){
int nIdxCol = 1; /* Number of columns in stat4 records */
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
int nSample; /* Number of samples */
i64 nByte; /* Bytes of space required */
i64 i; /* Bytes of space required */
tRowcnt *pSpace; /* Available allocated memory space */
u8 *pPtr; /* Available memory as a u8 for easier manipulation */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
| ︙ | ︙ | |||
121138 121139 121140 121141 121142 121143 121144 |
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
if( !REOPEN_AS_MEMDB(db) ){
rc = sqlite3Init(db, &zErrDyn);
}
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
| < < < < < < < < < | 121373 121374 121375 121376 121377 121378 121379 121380 121381 121382 121383 121384 121385 121386 |
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
if( !REOPEN_AS_MEMDB(db) ){
rc = sqlite3Init(db, &zErrDyn);
}
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
if( rc ){
if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){
int iDb = db->nDb - 1;
assert( iDb>=2 );
if( db->aDb[iDb].pBt ){
sqlite3BtreeClose(db->aDb[iDb].pBt);
db->aDb[iDb].pBt = 0;
|
| ︙ | ︙ | |||
121644 121645 121646 121647 121648 121649 121650 |
int iDb /* Index of containing database. */
){
sqlite3 *db = pParse->db; /* Database handle */
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
int rc; /* Auth callback return code */
if( db->init.busy ) return SQLITE_OK;
| | < < < < | 121870 121871 121872 121873 121874 121875 121876 121877 121878 121879 121880 121881 121882 121883 121884 |
int iDb /* Index of containing database. */
){
sqlite3 *db = pParse->db; /* Database handle */
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
int rc; /* Auth callback return code */
if( db->init.busy ) return SQLITE_OK;
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext);
if( rc==SQLITE_DENY ){
char *z = sqlite3_mprintf("%s.%s", zTab, zCol);
if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z);
sqlite3ErrorMsg(pParse, "access to %z is prohibited", z);
pParse->rc = SQLITE_AUTH;
}else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){
sqliteAuthBadReturnCode(pParse);
|
| ︙ | ︙ | |||
121755 121756 121757 121758 121759 121760 121761 | ** The following testcase() macros show that any of the 3rd through 6th ** parameters can be either NULL or a string. */ testcase( zArg1==0 ); testcase( zArg2==0 ); testcase( zArg3==0 ); testcase( pParse->zAuthContext==0 ); | | < < < < | 121977 121978 121979 121980 121981 121982 121983 121984 121985 121986 121987 121988 121989 121990 121991 |
** The following testcase() macros show that any of the 3rd through 6th
** parameters can be either NULL or a string. */
testcase( zArg1==0 );
testcase( zArg2==0 );
testcase( zArg3==0 );
testcase( pParse->zAuthContext==0 );
rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext);
if( rc==SQLITE_DENY ){
sqlite3ErrorMsg(pParse, "not authorized");
pParse->rc = SQLITE_AUTH;
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
rc = SQLITE_DENY;
sqliteAuthBadReturnCode(pParse);
}
|
| ︙ | ︙ | |||
121992 121993 121994 121995 121996 121997 121998 |
sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrRewind);
}
}
sqlite3VdbeAddOp0(v, OP_Halt);
| < < < < < < < < < < < | 122210 122211 122212 122213 122214 122215 122216 122217 122218 122219 122220 122221 122222 122223 |
sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrRewind);
}
}
sqlite3VdbeAddOp0(v, OP_Halt);
/* The cookie mask contains one bit for each database file open.
** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
** set for each database that is used. Generate code to start a
** transaction on each used database and to verify the schema cookie
** on each used database.
*/
assert( pParse->nErr>0 || sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
|
| ︙ | ︙ | |||
122131 122132 122133 122134 122135 122136 122137 | sqlite3RunParser(pParse, zSql); db->mDbFlags = savedDbFlags; sqlite3DbFree(db, zSql); memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); pParse->nested--; } | < < < < < < < < < < < < < < < < < | 122338 122339 122340 122341 122342 122343 122344 122345 122346 122347 122348 122349 122350 122351 122352 122353 122354 122355 122356 122357 122358 122359 122360 122361 122362 122363 122364 122365 122366 122367 122368 122369 |
sqlite3RunParser(pParse, zSql);
db->mDbFlags = savedDbFlags;
sqlite3DbFree(db, zSql);
memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
pParse->nested--;
}
/*
** Locate the in-memory structure that describes a particular database
** table given the name of that table and (optionally) the name of the
** database containing the table. Return NULL if not found.
**
** If zDatabase is 0, all databases are searched for the table and the
** first matching table is returned. (No checking for duplicate table
** names is done.) The search order is TEMP first, then MAIN, then any
** auxiliary databases added using the ATTACH command.
**
** See also sqlite3LocateTable().
*/
SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
Table *p = 0;
int i;
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
if( zDatabase ){
for(i=0; i<db->nDb; i++){
if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break;
}
if( i>=db->nDb ){
/* No match against the official names. But always match "main"
** to schema 0 as a legacy fallback. */
|
| ︙ | ︙ | |||
125824 125825 125826 125827 125828 125829 125830 |
}
pDb = &db->aDb[iDb];
assert( pTab!=0 );
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
&& pTblName!=0
| < < < | 126014 126015 126016 126017 126018 126019 126020 126021 126022 126023 126024 126025 126026 126027 |
}
pDb = &db->aDb[iDb];
assert( pTab!=0 );
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
&& pTblName!=0
){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
}
#ifndef SQLITE_OMIT_VIEW
if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "views may not be indexed");
|
| ︙ | ︙ | |||
128222 128223 128224 128225 128226 128227 128228 128229 128230 128231 128232 128233 128234 128235 |
** been specified
**
** 4) The table is a shadow table, the database connection is in
** defensive mode, and the current sqlite3_prepare()
** is for a top-level SQL statement.
*/
static int vtabIsReadOnly(Parse *pParse, Table *pTab){
if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){
return 1;
}
/* Within triggers:
** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY
** virtual tables
| > | 128409 128410 128411 128412 128413 128414 128415 128416 128417 128418 128419 128420 128421 128422 128423 |
** been specified
**
** 4) The table is a shadow table, the database connection is in
** defensive mode, and the current sqlite3_prepare()
** is for a top-level SQL statement.
*/
static int vtabIsReadOnly(Parse *pParse, Table *pTab){
assert( IsVirtual(pTab) );
if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){
return 1;
}
/* Within triggers:
** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY
** virtual tables
|
| ︙ | ︙ | |||
131714 131715 131716 131717 131718 131719 131720 | } #ifdef SQLITE_DEBUG /* ** Implementation of fpdecode(x,y,z) function. ** ** x is a real number that is to be decoded. y is the precision. | | > > > > > > | 131902 131903 131904 131905 131906 131907 131908 131909 131910 131911 131912 131913 131914 131915 131916 131917 131918 131919 131920 131921 131922 |
}
#ifdef SQLITE_DEBUG
/*
** Implementation of fpdecode(x,y,z) function.
**
** x is a real number that is to be decoded. y is the precision.
** z is the maximum real precision. Return a string that shows the
** results of the sqlite3FpDecode() function.
**
** Used for testing and debugging only, specifically testing and debugging
** of the sqlite3FpDecode() function. This SQL function does not appear
** in production builds. This function is not an API and is subject to
** modification or removal in future versions of SQLite.
*/
static void fpdecodeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
FpDecode s;
|
| ︙ | ︙ | |||
131738 131739 131740 131741 131742 131743 131744 131745 131746 131747 131748 131749 131750 131751 |
sqlite3FpDecode(&s, x, y, z);
if( s.isSpecial==2 ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN");
}else{
sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP);
}
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
}
#endif /* SQLITE_DEBUG */
/*
** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table. This occurs at start-time (as
** a consequence of calling sqlite3_initialize()).
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 131932 131933 131934 131935 131936 131937 131938 131939 131940 131941 131942 131943 131944 131945 131946 131947 131948 131949 131950 131951 131952 131953 131954 131955 131956 131957 131958 131959 131960 131961 131962 131963 131964 131965 131966 131967 131968 131969 131970 131971 131972 131973 131974 131975 131976 131977 131978 131979 131980 131981 131982 131983 131984 131985 131986 131987 131988 131989 131990 131991 131992 131993 131994 131995 131996 131997 131998 131999 132000 132001 132002 132003 132004 132005 132006 132007 132008 132009 132010 132011 132012 132013 132014 132015 132016 132017 132018 132019 132020 132021 |
sqlite3FpDecode(&s, x, y, z);
if( s.isSpecial==2 ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN");
}else{
sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP);
}
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
}
#endif /* SQLITE_DEBUG */
#ifdef SQLITE_DEBUG
/*
** Implementation of parseuri(uri,flags) function.
**
** Required Arguments:
** "uri" The URI to parse.
** "flags" Bitmask of flags, as if to sqlite3_open_v2().
**
** Additional arguments beyond the first two make calls to
** sqlite3_uri_key() for integers and sqlite3_uri_parameter for
** anything else.
**
** The result is a string showing the results of calling sqlite3ParseUri().
**
** Used for testing and debugging only, specifically testing and debugging
** of the sqlite3ParseUri() function. This SQL function does not appear
** in production builds. This function is not an API and is subject to
** modification or removal in future versions of SQLite.
*/
static void parseuriFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
sqlite3_str *pResult;
const char *zVfs;
const char *zUri;
unsigned int flgs;
int rc;
sqlite3_vfs *pVfs = 0;
char *zFile = 0;
char *zErr = 0;
if( argc<2 ) return;
pVfs = sqlite3_vfs_find(0);
assert( pVfs );
zVfs = pVfs->zName;
zUri = (const char*)sqlite3_value_text(argv[0]);
if( zUri==0 ) return;
flgs = (unsigned int)sqlite3_value_int(argv[1]);
rc = sqlite3ParseUri(zVfs, zUri, &flgs, &pVfs, &zFile, &zErr);
pResult = sqlite3_str_new(0);
if( pResult ){
int i;
sqlite3_str_appendf(pResult, "rc=%d", rc);
sqlite3_str_appendf(pResult, ", flags=0x%x", flgs);
sqlite3_str_appendf(pResult, ", vfs=%Q", pVfs ? pVfs->zName: 0);
sqlite3_str_appendf(pResult, ", err=%Q", zErr);
sqlite3_str_appendf(pResult, ", file=%Q", zFile);
if( zFile ){
const char *z = zFile;
z += sqlite3Strlen30(z)+1;
while( z[0] ){
sqlite3_str_appendf(pResult, ", %Q", z);
z += sqlite3Strlen30(z)+1;
}
for(i=2; i<argc; i++){
const char *zArg;
if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){
int k = sqlite3_value_int(argv[i]);
sqlite3_str_appendf(pResult, ", '%d:%q'",k,sqlite3_uri_key(zFile, k));
}else if( (zArg = (const char*)sqlite3_value_text(argv[i]))!=0 ){
sqlite3_str_appendf(pResult, ", '%q:%q'",
zArg, sqlite3_uri_parameter(zFile,zArg));
}else{
sqlite3_str_appendf(pResult, ", NULL");
}
}
}
sqlite3_result_text(ctx, sqlite3_str_finish(pResult), -1, sqlite3_free);
}
sqlite3_free_filename(zFile);
sqlite3_free(zErr);
}
#endif /* SQLITE_DEBUG */
/*
** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table. This occurs at start-time (as
** a consequence of calling sqlite3_initialize()).
|
| ︙ | ︙ | |||
131775 131776 131777 131778 131779 131780 131781 |
#ifdef SQLITE_SOUNDEX
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
#endif
| < < < | 132045 132046 132047 132048 132049 132050 132051 132052 132053 132054 132055 132056 132057 132058 |
#ifdef SQLITE_SOUNDEX
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
#endif
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
| ︙ | ︙ | |||
131803 131804 131805 131806 131807 131808 131809 |
WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION(max, -1, 1, 1, minmaxFunc ),
FUNCTION(max, 0, 1, 1, 0 ),
WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
| | > > | 132070 132071 132072 132073 132074 132075 132076 132077 132078 132079 132080 132081 132082 132083 132084 132085 132086 132087 132088 132089 132090 132091 132092 132093 132094 132095 132096 |
WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION(max, -1, 1, 1, minmaxFunc ),
FUNCTION(max, 0, 1, 1, 0 ),
WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
FUNCTION2(subtype, 1, 0, 0, subtypeFunc,
SQLITE_FUNC_TYPEOF|SQLITE_SUBTYPE),
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN),
FUNCTION(instr, 2, 0, 0, instrFunc ),
FUNCTION(printf, -1, 0, 0, printfFunc ),
FUNCTION(format, -1, 0, 0, printfFunc ),
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
FUNCTION(char, -1, 0, 0, charFunc ),
FUNCTION(abs, 1, 0, 0, absFunc ),
#ifdef SQLITE_DEBUG
FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ),
FUNCTION(parseuri, -1, 0, 0, parseuriFunc ),
#endif
#ifndef SQLITE_OMIT_FLOATING_POINT
FUNCTION(round, 1, 0, 0, roundFunc ),
FUNCTION(round, 2, 0, 0, roundFunc ),
#endif
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
| ︙ | ︙ | |||
131908 131909 131910 131911 131912 131913 131914 |
MFUNCTION(acosh, 1, acosh, math1Func ),
MFUNCTION(asinh, 1, asinh, math1Func ),
MFUNCTION(atanh, 1, atanh, math1Func ),
#endif
MFUNCTION(sqrt, 1, sqrt, math1Func ),
MFUNCTION(radians, 1, degToRad, math1Func ),
MFUNCTION(degrees, 1, radToDeg, math1Func ),
| | > > > | 132177 132178 132179 132180 132181 132182 132183 132184 132185 132186 132187 132188 132189 132190 132191 132192 132193 132194 132195 132196 132197 132198 |
MFUNCTION(acosh, 1, acosh, math1Func ),
MFUNCTION(asinh, 1, asinh, math1Func ),
MFUNCTION(atanh, 1, atanh, math1Func ),
#endif
MFUNCTION(sqrt, 1, sqrt, math1Func ),
MFUNCTION(radians, 1, degToRad, math1Func ),
MFUNCTION(degrees, 1, radToDeg, math1Func ),
MFUNCTION(pi, 0, 0, piFunc ),
#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
FUNCTION(sign, 1, 0, 0, signFunc ),
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ),
INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ),
INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ),
};
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
sqlite3WindowFunctions();
sqlite3RegisterDateTimeFunctions();
sqlite3RegisterJsonFunctions();
|
| ︙ | ︙ | |||
140425 140426 140427 140428 140429 140430 140431 |
}else{
u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */
if( db->autoCommit==0 ){
/* Foreign key support may not be enabled or disabled while not
** in auto-commit mode. */
mask &= ~(SQLITE_ForeignKeys);
}
| < < < < < < | 140697 140698 140699 140700 140701 140702 140703 140704 140705 140706 140707 140708 140709 140710 |
}else{
u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */
if( db->autoCommit==0 ){
/* Foreign key support may not be enabled or disabled while not
** in auto-commit mode. */
mask &= ~(SQLITE_ForeignKeys);
}
if( sqlite3GetBoolean(zRight, 0) ){
if( (mask & SQLITE_WriteSchema)==0
|| (db->flags & SQLITE_Defensive)==0
){
db->flags |= mask;
}
|
| ︙ | ︙ | |||
140566 140567 140568 140569 140570 140571 140572 |
Table *pTab;
if( k==0 ){ initNCol = 0; break; }
pTab = sqliteHashData(k);
if( pTab->nCol==0 ){
char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
if( zSql ){
sqlite3_stmt *pDummy = 0;
| > | | 140832 140833 140834 140835 140836 140837 140838 140839 140840 140841 140842 140843 140844 140845 140846 140847 |
Table *pTab;
if( k==0 ){ initNCol = 0; break; }
pTab = sqliteHashData(k);
if( pTab->nCol==0 ){
char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
if( zSql ){
sqlite3_stmt *pDummy = 0;
(void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG,
&pDummy, 0);
(void)sqlite3_finalize(pDummy);
sqlite3DbFree(db, zSql);
}
if( db->mallocFailed ){
sqlite3ErrorMsg(db->pParse, "out of memory");
db->pParse->rc = SQLITE_NOMEM_BKPT;
}
|
| ︙ | ︙ | |||
141042 141043 141044 141045 141046 141047 141048 141049 141050 141051 141052 141053 141054 141055 |
aRoot[++cnt] = pIdx->tnum;
}
}
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
sqlite3TouchRegister(pParse, 8+cnt);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
| > | 141309 141310 141311 141312 141313 141314 141315 141316 141317 141318 141319 141320 141321 141322 141323 |
aRoot[++cnt] = pIdx->tnum;
}
}
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
sqlite3TouchRegister(pParse, 8+cnt);
sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
| ︙ | ︙ | |||
142666 142667 142668 142669 142670 142671 142672 |
#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
#else
encoding = SQLITE_UTF8;
#endif
| < < < < < < | < | 142934 142935 142936 142937 142938 142939 142940 142941 142942 142943 142944 142945 142946 142947 142948 |
#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
#else
encoding = SQLITE_UTF8;
#endif
sqlite3SetTextEncoding(db, encoding);
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){
sqlite3SetString(pzErrMsg, db, "attached databases must use the same"
" text encoding as main database");
rc = SQLITE_ERROR;
goto initone_error_out;
|
| ︙ | ︙ | |||
143367 143368 143369 143370 143371 143372 143373 143374 143375 143376 143377 143378 143379 143380 143381 143382 143383 143384 143385 143386 143387 143388 143389 143390 143391 143392 |
#ifdef SQLITE_ENABLE_API_ARMOR
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
#endif
*ppStmt = 0;
if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
return SQLITE_MISUSE_BKPT;
}
if( nBytes>=0 ){
int sz;
const char *z = (const char*)zSql;
for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){}
nBytes = sz;
}
sqlite3_mutex_enter(db->mutex);
zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE);
if( zSql8 ){
rc = sqlite3LockAndPrepare(db, zSql8, -1, prepFlags, 0, ppStmt, &zTail8);
}
if( zTail8 && pzTail ){
/* If sqlite3_prepare returns a tail pointer, we calculate the
** equivalent pointer into the UTF-16 string by counting the unicode
** characters between zSql8 and zTail8, and then returning a pointer
** the same number of characters into the UTF-16 string.
*/
int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8));
| > > > > > > > > > > > > | | 143628 143629 143630 143631 143632 143633 143634 143635 143636 143637 143638 143639 143640 143641 143642 143643 143644 143645 143646 143647 143648 143649 143650 143651 143652 143653 143654 143655 143656 143657 143658 143659 143660 143661 143662 143663 143664 143665 143666 143667 143668 143669 143670 143671 143672 143673 |
#ifdef SQLITE_ENABLE_API_ARMOR
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
#endif
*ppStmt = 0;
if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
return SQLITE_MISUSE_BKPT;
}
/* Make sure nBytes is non-negative and correct. It should be the
** number of bytes until the end of the input buffer or until the first
** U+0000 character. If the input nBytes is odd, convert it into
** an even number. If the input nBytes is negative, then the input
** must be terminated by at least one U+0000 character */
if( nBytes>=0 ){
int sz;
const char *z = (const char*)zSql;
for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){}
nBytes = sz;
}else{
int sz;
const char *z = (const char*)zSql;
for(sz=0; z[sz]!=0 || z[sz+1]!=0; sz += 2){}
nBytes = sz;
}
sqlite3_mutex_enter(db->mutex);
zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE);
if( zSql8 ){
rc = sqlite3LockAndPrepare(db, zSql8, -1, prepFlags, 0, ppStmt, &zTail8);
}
if( zTail8 && pzTail ){
/* If sqlite3_prepare returns a tail pointer, we calculate the
** equivalent pointer into the UTF-16 string by counting the unicode
** characters between zSql8 and zTail8, and then returning a pointer
** the same number of characters into the UTF-16 string.
*/
int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8));
*pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, nBytes, chars_parsed);
}
sqlite3DbFree(db, zSql8);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
|
| ︙ | ︙ | |||
147359 147360 147361 147362 147363 147364 147365 147366 147367 147368 147369 147370 147371 |
if( db->mallocFailed ){
sqlite3ExprDelete(db, pNew);
return pExpr;
}
if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
pExpr->flags & (EP_OuterON|EP_InnerON));
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
| > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < | 147632 147633 147634 147635 147636 147637 147638 147639 147640 147641 147642 147643 147644 147645 147646 147647 147648 147649 147650 147651 147652 147653 147654 147655 147656 147657 147658 147659 147660 147661 147662 147663 147664 147665 147666 147667 147668 147669 147670 147671 |
if( db->mallocFailed ){
sqlite3ExprDelete(db, pNew);
return pExpr;
}
if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
if( pNew->op==TK_TRUEFALSE ){
pNew->u.iValue = sqlite3ExprTruthValue(pNew);
pNew->op = TK_INTEGER;
ExprSetProperty(pNew, EP_IntValue);
}
/* Ensure that the expression now has an implicit collation sequence,
** just as it did when it was a column of a view or sub-query. */
{
CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew);
CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
pSubst->pCList->a[iColumn].pExpr
);
if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){
pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew,
(pColl ? pColl->zName : "BINARY")
);
}
}
ExprClearProperty(pNew, EP_Collate);
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
pExpr->flags & (EP_OuterON|EP_InnerON));
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
}
}
}else{
if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
pExpr->iTable = pSubst->iNewTable;
}
pExpr->pLeft = substExpr(pSubst, pExpr->pLeft);
|
| ︙ | ︙ | |||
148121 148122 148123 148124 148125 148126 148127 148128 148129 148130 148131 148132 148133 148134 148135 148136 |
if( pSrc==0 ) break;
pParent->pSrc = pSrc;
}
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
assert( pItem->fg.isTabFunc==0 );
assert( pItem->fg.isSubquery
|| pItem->fg.fixedSchema
|| pItem->u4.zDatabase==0 );
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
| > < | 148394 148395 148396 148397 148398 148399 148400 148401 148402 148403 148404 148405 148406 148407 148408 148409 148410 148411 148412 148413 148414 148415 148416 148417 |
if( pSrc==0 ) break;
pParent->pSrc = pSrc;
}
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
iNewParent = pSubSrc->a[0].iCursor;
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
assert( pItem->fg.isTabFunc==0 );
assert( pItem->fg.isSubquery
|| pItem->fg.fixedSchema
|| pItem->u4.zDatabase==0 );
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
|
| ︙ | ︙ | |||
148170 148171 148172 148173 148174 148175 148176 148177 148178 148179 148180 148181 148182 148183 |
assert( pParent->pOrderBy==0 );
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
}
pWhere = pSub->pWhere;
pSub->pWhere = 0;
if( isOuterJoin>0 ){
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
}
if( pWhere ){
if( pParent->pWhere ){
pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
}else{
pParent->pWhere = pWhere;
| > | 148443 148444 148445 148446 148447 148448 148449 148450 148451 148452 148453 148454 148455 148456 148457 |
assert( pParent->pOrderBy==0 );
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
}
pWhere = pSub->pWhere;
pSub->pWhere = 0;
if( isOuterJoin>0 ){
assert( pSubSrc->nSrc==1 );
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
}
if( pWhere ){
if( pParent->pWhere ){
pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
}else{
pParent->pWhere = pWhere;
|
| ︙ | ︙ | |||
151269 151270 151271 151272 151273 151274 151275 |
TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
}else{
| | | 151543 151544 151545 151546 151547 151548 151549 151550 151551 151552 151553 151554 151555 151556 151557 |
TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
}else{
TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n"));
}
/* Convert unused result columns of the subquery into simple NULL
** expressions, to avoid unneeded searching and computation.
** tag-select-0440
*/
if( OptimizationEnabled(db, SQLITE_NullUnusedCols)
|
| ︙ | ︙ | |||
156978 156979 156980 156981 156982 156983 156984 156985 156986 156987 156988 156989 156990 156991 |
assert( !db->mallocFailed );
assert( IsOrdinaryTable(sParse.pNewTable) );
assert( sParse.zErrMsg==0 );
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
sqlite3ExprListDelete(db, pNew->u.tab.pDfltList);
pTab->nNVCol = pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
assert( pTab->pIndex==0 );
assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
| > | 157252 157253 157254 157255 157256 157257 157258 157259 157260 157261 157262 157263 157264 157265 157266 |
assert( !db->mallocFailed );
assert( IsOrdinaryTable(sParse.pNewTable) );
assert( sParse.zErrMsg==0 );
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
assert( IsOrdinaryTable(pNew) );
sqlite3ExprListDelete(db, pNew->u.tab.pDfltList);
pTab->nNVCol = pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
assert( pTab->pIndex==0 );
assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
|
| ︙ | ︙ | |||
158042 158043 158044 158045 158046 158047 158048 158049 158050 158051 158052 158053 158054 158055 158056 158057 158058 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ); SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( const Parse *pParse, /* Parse context */ const WhereInfo *pWInfo, /* WHERE clause */ const WhereLevel *pLevel /* Bloom filter on this level */ ); #else # define sqlite3WhereExplainOneScan(u,v,w,x) 0 # define sqlite3WhereExplainBloomFilter(u,v,w) 0 #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlite3WhereAddScanStatus( Vdbe *v, /* Vdbe to add scanstatus entry to */ SrcList *pSrclist, /* FROM clause pLvl reads data from */ WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ | > > > > > > > > | 158317 158318 158319 158320 158321 158322 158323 158324 158325 158326 158327 158328 158329 158330 158331 158332 158333 158334 158335 158336 158337 158338 158339 158340 158341 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ); SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( const Parse *pParse, /* Parse context */ const WhereInfo *pWInfo, /* WHERE clause */ const WhereLevel *pLevel /* Bloom filter on this level */ ); SQLITE_PRIVATE void sqlite3WhereAddExplainText( Parse *pParse, /* Parse context */ int addr, SrcList *pTabList, /* Table list this loop refers to */ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ); #else # define sqlite3WhereExplainOneScan(u,v,w,x) 0 # define sqlite3WhereExplainBloomFilter(u,v,w) 0 # define sqlite3WhereAddExplainText(u,v,w,x,y) #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlite3WhereAddScanStatus( Vdbe *v, /* Vdbe to add scanstatus entry to */ SrcList *pSrclist, /* FROM clause pLvl reads data from */ WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ |
| ︙ | ︙ | |||
158246 158247 158248 158249 158250 158251 158252 |
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3_str_append(pStr, ")", 1);
}
/*
| | < < < < | < > | > < > > < > > > > < | 158529 158530 158531 158532 158533 158534 158535 158536 158537 158538 158539 158540 158541 158542 158543 158544 158545 158546 158547 158548 158549 158550 158551 158552 158553 158554 158555 158556 158557 158558 158559 158560 158561 158562 158563 158564 158565 158566 158567 158568 158569 158570 158571 158572 158573 158574 |
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3_str_append(pStr, ")", 1);
}
/*
** This function sets the P4 value of an existing OP_Explain opcode to
** text describing the loop in pLevel. If the OP_Explain opcode already has
** a P4 value, it is freed before it is overwritten.
*/
SQLITE_PRIVATE void sqlite3WhereAddExplainText(
Parse *pParse, /* Parse context */
int addr, /* Address of OP_Explain opcode */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
#if !defined(SQLITE_DEBUG)
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
u32 flags; /* Flags that describe this loop */
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
char *zMsg; /* Text to add to EQP output */
#endif
StrAccum str; /* EQP output string */
char zBuf[100]; /* Initial space for EQP output string */
if( db->mallocFailed ) return;
pLoop = pLevel->pWLoop;
flags = pLoop->wsFlags;
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
str.printfFlags = SQLITE_PRINTF_INTERNAL;
|
| ︙ | ︙ | |||
158301 158302 158303 158304 158305 158306 158307 |
if( isSearch ){
zFmt = "PRIMARY KEY";
}
}else if( flags & WHERE_PARTIALIDX ){
zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
}else if( flags & WHERE_AUTO_INDEX ){
zFmt = "AUTOMATIC COVERING INDEX";
| | | 158584 158585 158586 158587 158588 158589 158590 158591 158592 158593 158594 158595 158596 158597 158598 |
if( isSearch ){
zFmt = "PRIMARY KEY";
}
}else if( flags & WHERE_PARTIALIDX ){
zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
}else if( flags & WHERE_AUTO_INDEX ){
zFmt = "AUTOMATIC COVERING INDEX";
}else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
zFmt = "COVERING INDEX %s";
}else{
zFmt = "INDEX %s";
}
if( zFmt ){
sqlite3_str_append(&str, " USING ", 7);
sqlite3_str_appendf(&str, zFmt, pIdx->zName);
|
| ︙ | ︙ | |||
158353 158354 158355 158356 158357 158358 158359 158360 158361 |
if( pLoop->nOut>=10 ){
sqlite3_str_appendf(&str, " (~%llu rows)",
sqlite3LogEstToInt(pLoop->nOut));
}else{
sqlite3_str_append(&str, " (~1 row)", 9);
}
#endif
zMsg = sqlite3StrAccumFinish(&str);
sqlite3ExplainBreakpoint("",zMsg);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | 158636 158637 158638 158639 158640 158641 158642 158643 158644 158645 158646 158647 158648 158649 158650 158651 158652 158653 158654 158655 158656 158657 158658 158659 158660 158661 158662 158663 158664 158665 158666 158667 158668 158669 158670 158671 158672 158673 158674 158675 158676 158677 158678 158679 158680 158681 158682 158683 158684 158685 158686 158687 158688 158689 158690 158691 158692 158693 |
if( pLoop->nOut>=10 ){
sqlite3_str_appendf(&str, " (~%llu rows)",
sqlite3LogEstToInt(pLoop->nOut));
}else{
sqlite3_str_append(&str, " (~1 row)", 9);
}
#endif
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
zMsg = sqlite3StrAccumFinish(&str);
sqlite3ExplainBreakpoint("",zMsg);
#endif
assert( pOp->opcode==OP_Explain );
assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 );
sqlite3DbFree(db, pOp->p4.z);
pOp->p4type = P4_DYNAMIC;
pOp->p4.z = sqlite3StrAccumFinish(&str);
}
}
/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
** was defined at compile-time. If it is not a no-op, a single OP_Explain
** opcode is added to the output to describe the table scan strategy in pLevel.
**
** If an OP_Explain opcode is added to the VM, its address is returned.
** Otherwise, if no OP_Explain is coded, zero is returned.
*/
SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
Parse *pParse, /* Parse context */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
int ret = 0;
#if !defined(SQLITE_DEBUG)
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
){
Vdbe *v = pParse->pVdbe;
int addr = sqlite3VdbeCurrentAddr(v);
ret = sqlite3VdbeAddOp3(
v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun
);
sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags);
}
}
return ret;
}
/*
** Add a single OP_Explain opcode that describes a Bloom filter.
**
|
| ︙ | ︙ | |||
158456 158457 158458 158459 158460 158461 158462 158463 158464 |
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
}
if( wsFlags & WHERE_INDEXED ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
}
}else{
int addr;
assert( pSrclist->a[pLvl->iFrom].fg.isSubquery );
addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub;
| > | | 158778 158779 158780 158781 158782 158783 158784 158785 158786 158787 158788 158789 158790 158791 158792 158793 158794 158795 |
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
}
if( wsFlags & WHERE_INDEXED ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
}
}else{
int addr;
VdbeOp *pOp;
assert( pSrclist->a[pLvl->iFrom].fg.isSubquery );
addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub;
pOp = sqlite3VdbeGetOp(v, addr-1);
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine );
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr );
sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1);
}
}
}
#endif
|
| ︙ | ︙ | |||
158711 158712 158713 158714 158715 158716 158717 158718 158719 158720 158721 158722 158723 158724 |
}
sqlite3ExprListDelete(db, pOrigRhs);
if( pOrigLhs ){
sqlite3ExprListDelete(db, pOrigLhs);
pNew->pLeft->x.pList = pLhs;
}
pSelect->pEList = pRhs;
if( pLhs && pLhs->nExpr==1 ){
/* Take care here not to generate a TK_VECTOR containing only a
** single value. Since the parser never creates such a vector, some
** of the subroutines do not handle this case. */
Expr *p = pLhs->a[0].pExpr;
pLhs->a[0].pExpr = 0;
sqlite3ExprDelete(db, pNew->pLeft);
| > | 159034 159035 159036 159037 159038 159039 159040 159041 159042 159043 159044 159045 159046 159047 159048 |
}
sqlite3ExprListDelete(db, pOrigRhs);
if( pOrigLhs ){
sqlite3ExprListDelete(db, pOrigLhs);
pNew->pLeft->x.pList = pLhs;
}
pSelect->pEList = pRhs;
pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */
if( pLhs && pLhs->nExpr==1 ){
/* Take care here not to generate a TK_VECTOR containing only a
** single value. Since the parser never creates such a vector, some
** of the subroutines do not handle this case. */
Expr *p = pLhs->a[0].pExpr;
pLhs->a[0].pExpr = 0;
sqlite3ExprDelete(db, pNew->pLeft);
|
| ︙ | ︙ | |||
161258 161259 161260 161261 161262 161263 161264 |
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
assert( !ExprHasProperty(pRight, EP_IntValue) );
z = (u8*)pRight->u.zToken;
}
if( z ){
| < | | | > | | | | > > | | > > > | | 161582 161583 161584 161585 161586 161587 161588 161589 161590 161591 161592 161593 161594 161595 161596 161597 161598 161599 161600 161601 161602 161603 161604 161605 161606 161607 161608 161609 161610 161611 161612 161613 161614 161615 161616 161617 161618 161619 161620 161621 161622 161623 161624 161625 161626 |
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
assert( !ExprHasProperty(pRight, EP_IntValue) );
z = (u8*)pRight->u.zToken;
}
if( z ){
/* Count the number of prefix bytes prior to the first wildcard.
** or U+fffd character. If the underlying database has a UTF16LE
** encoding, then only consider ASCII characters. Note that the
** encoding of z[] is UTF8 - we are dealing with only UTF8 here in
** this code, but the database engine itself might be processing
** content using a different encoding. */
cnt = 0;
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
cnt++;
if( c==wc[3] && z[cnt]>0 && z[cnt]<0x80 ){
cnt++;
}else if( c>=0x80 ){
const u8 *z2 = z+cnt-1;
if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){
cnt--;
break;
}else{
cnt = (int)(z2-z);
}
}
}
/* The optimization is possible only if (1) the pattern does not begin
** with a wildcard and if (2) the non-wildcard prefix does not end with
** an (illegal 0xff) character, or (3) the pattern does not consist of
** a single escape character. The second condition is necessary so
** that we can increment the prefix key to find an upper bound for the
** range search. The third is because the caller assumes that the pattern
** consists of at least one character after all escapes have been
** removed. */
if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && ALWAYS(255!=(u8)z[cnt-1]) ){
Expr *pPrefix;
/* A "complete" match if the pattern ends with "*" or "%" */
*pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE;
/* Get the pattern prefix. Remove all escapes from the prefix. */
pPrefix = sqlite3Expr(db, TK_STRING, (char*)z);
|
| ︙ | ︙ | |||
163778 163779 163780 163781 163782 163783 163784 |
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
){
return 0;
}
if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
| | | 164107 164108 164109 164110 164111 164112 164113 164114 164115 164116 164117 164118 164119 164120 164121 |
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
){
return 0;
}
if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
&& NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON))
){
return 0;
}
return 1;
}
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
| ︙ | ︙ | |||
164575 164576 164577 164578 164579 164580 164581 |
** not result in a valid plan.
**
** Whether or not an error is returned, it is the responsibility of the
** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates
** that this is required.
*/
static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
| < > > > | 164904 164905 164906 164907 164908 164909 164910 164911 164912 164913 164914 164915 164916 164917 164918 164919 164920 164921 164922 |
** not result in a valid plan.
**
** Whether or not an error is returned, it is the responsibility of the
** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates
** that this is required.
*/
static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
int rc;
sqlite3_vtab *pVtab;
assert( IsVirtual(pTab) );
pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab;
whereTraceIndexInfoInputs(p, pTab);
pParse->db->nSchemaLock++;
rc = pVtab->pModule->xBestIndex(pVtab, p);
pParse->db->nSchemaLock--;
whereTraceIndexInfoOutputs(p, pTab);
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
|
| ︙ | ︙ | |||
165269 165270 165271 165272 165273 165274 165275 | } assert( pBuilder->nRecValid==nRecValid ); return rc; } #endif /* SQLITE_ENABLE_STAT4 */ | | | 165600 165601 165602 165603 165604 165605 165606 165607 165608 165609 165610 165611 165612 165613 165614 |
}
assert( pBuilder->nRecValid==nRecValid );
return rc;
}
#endif /* SQLITE_ENABLE_STAT4 */
#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG)
/*
** Print the content of a WhereTerm object
*/
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
if( pTerm==0 ){
sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm);
}else{
|
| ︙ | ︙ | |||
165312 165313 165314 165315 165316 165317 165318 165319 165320 165321 165322 165323 165324 165325 |
}
if( pTerm->iParent>=0 ){
sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
}
sqlite3DebugPrintf("\n");
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
#endif
#ifdef WHERETRACE_ENABLED
/*
** Show the complete content of a WhereClause
*/
| > > > | 165643 165644 165645 165646 165647 165648 165649 165650 165651 165652 165653 165654 165655 165656 165657 165658 165659 |
}
if( pTerm->iParent>=0 ){
sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
}
sqlite3DebugPrintf("\n");
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){
sqlite3WhereTermPrint(pTerm, 0);
}
#endif
#ifdef WHERETRACE_ENABLED
/*
** Show the complete content of a WhereClause
*/
|
| ︙ | ︙ | |||
165520 165521 165522 165523 165524 165525 165526 | /* ** Return TRUE if X is a proper subset of Y but is of equal or less cost. ** In other words, return true if all constraints of X are also part of Y ** and Y has additional constraints that might speed the search that X lacks ** but the cost of running X is not more than the cost of running Y. ** | | | 165854 165855 165856 165857 165858 165859 165860 165861 165862 165863 165864 165865 165866 165867 165868 | /* ** Return TRUE if X is a proper subset of Y but is of equal or less cost. ** In other words, return true if all constraints of X are also part of Y ** and Y has additional constraints that might speed the search that X lacks ** but the cost of running X is not more than the cost of running Y. ** ** In other words, return true if the cost relationship between X and Y ** is inverted and needs to be adjusted. ** ** Case 1: ** ** (1a) X and Y use the same index. ** (1b) X has fewer == terms than Y ** (1c) Neither X nor Y use skip-scan |
| ︙ | ︙ | |||
166498 166499 166500 166501 166502 166503 166504 |
if( jointype & JT_LTORJ ) return 0;
pParse = pWC->pWInfo->pParse;
while( pWhere->op==TK_AND ){
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
| < | 166832 166833 166834 166835 166836 166837 166838 166839 166840 166841 166842 166843 166844 166845 |
if( jointype & JT_LTORJ ) return 0;
pParse = pWC->pWInfo->pParse;
while( pWhere->op==TK_AND ){
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
Expr *pExpr;
pExpr = pTerm->pExpr;
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
&& ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
&& (pTerm->wtFlags & TERM_VNULL)==0
|
| ︙ | ︙ | |||
169138 169139 169140 169141 169142 169143 169144 169145 169146 169147 169148 169149 169150 169151 169152 169153 169154 169155 169156 169157 169158 169159 169160 169161 169162 169163 169164 |
tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy);
}
hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0;
for(i=pWInfo->nLevel-1; i>=1; i--){
WhereTerm *pTerm, *pEnd;
SrcItem *pItem;
WhereLoop *pLoop;
pLoop = pWInfo->a[i].pWLoop;
pItem = &pWInfo->pTabList->a[pLoop->iTab];
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue;
if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0
&& (pLoop->wsFlags & WHERE_ONEROW)==0
){
continue;
}
if( (tabUsed & pLoop->maskSelf)!=0 ) continue;
pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm;
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
){
break;
}
}
if( hasRightJoin
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
| > | | > > > | 169471 169472 169473 169474 169475 169476 169477 169478 169479 169480 169481 169482 169483 169484 169485 169486 169487 169488 169489 169490 169491 169492 169493 169494 169495 169496 169497 169498 169499 169500 169501 169502 169503 169504 169505 169506 169507 169508 169509 169510 169511 169512 169513 169514 169515 |
tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy);
}
hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0;
for(i=pWInfo->nLevel-1; i>=1; i--){
WhereTerm *pTerm, *pEnd;
SrcItem *pItem;
WhereLoop *pLoop;
Bitmask m1;
pLoop = pWInfo->a[i].pWLoop;
pItem = &pWInfo->pTabList->a[pLoop->iTab];
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue;
if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0
&& (pLoop->wsFlags & WHERE_ONEROW)==0
){
continue;
}
if( (tabUsed & pLoop->maskSelf)!=0 ) continue;
pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm;
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
){
break;
}
}
if( hasRightJoin
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
&& NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor)
){
break; /* restriction (5) */
}
}
if( pTerm<pEnd ) continue;
WHERETRACE(0xffffffff,("-> omit unused FROM-clause term %c\n",pLoop->cId));
m1 = MASKBIT(i)-1;
testcase( ((pWInfo->revMask>>1) & ~m1)!=0 );
pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1);
notReady &= ~pLoop->maskSelf;
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
pTerm->wtFlags |= TERM_CODED;
}
}
if( i!=pWInfo->nLevel-1 ){
|
| ︙ | ︙ | |||
169235 169236 169237 169238 169239 169240 169241 |
}
}
nSearch += pLoop->nOut;
if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta;
}
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 169572 169573 169574 169575 169576 169577 169578 169579 169580 169581 169582 169583 169584 169585 |
}
}
nSearch += pLoop->nOut;
if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta;
}
}
/*
** The index pIdx is used by a query and contains one or more expressions.
** In other words pIdx is an index on an expression. iIdxCur is the cursor
** number for the index and iDataCur is the cursor number for the corresponding
** table.
**
** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for
|
| ︙ | ︙ | |||
169320 169321 169322 169323 169324 169325 169326 |
pExpr = pIdx->aColExpr->a[i].pExpr;
}else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
}else{
continue;
}
if( sqlite3ExprIsConstant(0,pExpr) ) continue;
| < < < < < < | 169605 169606 169607 169608 169609 169610 169611 169612 169613 169614 169615 169616 169617 169618 |
pExpr = pIdx->aColExpr->a[i].pExpr;
}else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
}else{
continue;
}
if( sqlite3ExprIsConstant(0,pExpr) ) continue;
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
p->pIENext = pParse->pIdxEpr;
#ifdef WHERETRACE_ENABLED
if( sqlite3WhereTrace & 0x200 ){
sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i);
if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(pExpr);
|
| ︙ | ︙ | |||
170432 170433 170434 170435 170436 170437 170438 |
x = sqlite3StorageColumnToTable(pTab,x);
}
x = sqlite3TableColumnToIndex(pIdx, x);
if( x>=0 ){
pOp->p2 = x;
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
| | < < < < > > > > > > > > > > > > > > > > > > | 170711 170712 170713 170714 170715 170716 170717 170718 170719 170720 170721 170722 170723 170724 170725 170726 170727 170728 170729 170730 170731 170732 170733 170734 170735 170736 170737 170738 170739 170740 170741 170742 170743 170744 170745 170746 |
x = sqlite3StorageColumnToTable(pTab,x);
}
x = sqlite3TableColumnToIndex(pIdx, x);
if( x>=0 ){
pOp->p2 = x;
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
}else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
if( pLoop->wsFlags & WHERE_IDX_ONLY ){
/* An error. pLoop is supposed to be a covering index loop,
** and yet the VM code refers to a column of the table that
** is not part of the index. */
sqlite3ErrorMsg(pParse, "internal query planner error");
pParse->rc = SQLITE_INTERNAL;
}else{
/* The WHERE_EXPRIDX flag is set by the planner when it is likely
** that pLoop is a covering index loop, but it is not possible
** to be 100% sure. In this case, any OP_Explain opcode
** corresponding to this loop describes the index as a "COVERING
** INDEX". But, pOp proves that pLoop is not actually a covering
** index loop. So clear the WHERE_EXPRIDX flag and rewrite the
** text that accompanies the OP_Explain opcode, if any. */
pLoop->wsFlags &= ~WHERE_EXPRIDX;
sqlite3WhereAddExplainText(pParse,
pLevel->addrBody-1,
pTabList,
pLevel,
pWInfo->wctrlFlags
);
}
}
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
OpcodeRewriteTrace(db, k, pOp);
}else if( pOp->opcode==OP_IfNullRow ){
|
| ︙ | ︙ | |||
172147 172148 172149 172150 172151 172152 172153 172154 172155 172156 172157 172158 172159 172160 172161 172162 172163 172164 172165 172166 172167 172168 172169 172170 172171 172172 172173 172174 172175 172176 172177 172178 172179 172180 172181 172182 172183 172184 172185 172186 172187 172188 172189 172190 172191 172192 172193 172194 |
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pWFunc;
int regArg;
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
/* All OVER clauses in the same window function aggregate step must
** be the same. */
assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)!=1 );
for(i=0; i<nArg; i++){
if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
}else{
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
}
}
regArg = reg;
if( pMWin->regStartRowid==0
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
&& (pWin->eStart!=TK_UNBOUNDED)
){
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
VdbeCoverage(v);
if( bInverse==0 ){
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
}else{
sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
VdbeCoverageNeverTaken(v);
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
assert( pFunc->zName==nth_valueName
|| pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
}else if( pFunc->xSFunc!=noopStepFunc ){
| > > > > > > > > > > > > > > < < < < < < < < < < < < < | 172440 172441 172442 172443 172444 172445 172446 172447 172448 172449 172450 172451 172452 172453 172454 172455 172456 172457 172458 172459 172460 172461 172462 172463 172464 172465 172466 172467 172468 172469 172470 172471 172472 172473 172474 172475 172476 172477 172478 172479 172480 172481 172482 172483 172484 172485 172486 172487 172488 172489 172490 172491 172492 172493 172494 172495 172496 172497 172498 172499 172500 172501 172502 172503 172504 172505 172506 172507 172508 |
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pWFunc;
int regArg;
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
int addrIf = 0;
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
/* All OVER clauses in the same window function aggregate step must
** be the same. */
assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)!=1 );
for(i=0; i<nArg; i++){
if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
}else{
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
}
}
regArg = reg;
if( pWin->pFilter ){
int regTmp;
assert( ExprUseXList(pWin->pOwner) );
assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
regTmp = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regTmp);
}
if( pMWin->regStartRowid==0
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
&& (pWin->eStart!=TK_UNBOUNDED)
){
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
VdbeCoverage(v);
if( bInverse==0 ){
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
}else{
sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
VdbeCoverageNeverTaken(v);
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
assert( pWin->pFilter==0 );
assert( pFunc->zName==nth_valueName
|| pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
}else if( pFunc->xSFunc!=noopStepFunc ){
if( pWin->bExprArgs ){
int iOp = sqlite3VdbeCurrentAddr(v);
int iEnd;
assert( ExprUseXList(pWin->pOwner) );
nArg = pWin->pOwner->x.pList->nExpr;
regArg = sqlite3GetTempRange(pParse, nArg);
|
| ︙ | ︙ | |||
172231 172232 172233 172234 172235 172236 172237 |
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
if( pWin->bExprArgs ){
sqlite3ReleaseTempRange(pParse, regArg, nArg);
}
| < > > | 172525 172526 172527 172528 172529 172530 172531 172532 172533 172534 172535 172536 172537 172538 172539 172540 172541 |
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
if( pWin->bExprArgs ){
sqlite3ReleaseTempRange(pParse, regArg, nArg);
}
}
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
/*
** Values that may be passed as the second argument to windowCodeOp().
*/
#define WINDOW_RETURN_ROW 1
|
| ︙ | ︙ | |||
173658 173659 173660 173661 173662 173663 173664 173665 173666 173667 173668 173669 173670 173671 |
** UPDATE ON (a,b,c)
**
** Then the "b" IdList records the list "a,b,c".
*/
struct TrigEvent { int a; IdList * b; };
struct FrameBound { int eType; Expr *pExpr; };
/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
*/
static void disableLookaside(Parse *pParse){
sqlite3 *db = pParse->db;
| > > > > > > > | 173953 173954 173955 173956 173957 173958 173959 173960 173961 173962 173963 173964 173965 173966 173967 173968 173969 173970 173971 173972 173973 |
** UPDATE ON (a,b,c)
**
** Then the "b" IdList records the list "a,b,c".
*/
struct TrigEvent { int a; IdList * b; };
struct FrameBound { int eType; Expr *pExpr; };
/*
** Generate a syntax error
*/
static void parserSyntaxError(Parse *pParse, Token *p){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
}
/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
*/
static void disableLookaside(Parse *pParse){
sqlite3 *db = pParse->db;
|
| ︙ | ︙ | |||
177551 177552 177553 177554 177555 177556 177557 |
{
sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
| > > > | > | 177853 177854 177855 177856 177857 177858 177859 177860 177861 177862 177863 177864 177865 177866 177867 177868 177869 177870 177871 |
{
sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0
|| sqlite3ReadSchema(pParse)==SQLITE_OK
){
sqlite3Select(pParse, yymsp[0].minor.yy555, &dest);
}
sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
}
break;
case 85: /* select ::= WITH wqlist selectnowith */
{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
break;
case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
|
| ︙ | ︙ | |||
178022 178023 178024 178025 178026 178027 178028 |
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
** in the virtual machine. #N is the N-th register. */
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
| | | 178328 178329 178330 178331 178332 178333 178334 178335 178336 178337 178338 178339 178340 178341 178342 |
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
** in the virtual machine. #N is the N-th register. */
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
parserSyntaxError(pParse, &t);
yymsp[0].minor.yy454 = 0;
}else{
yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
}
}
}
|
| ︙ | ︙ | |||
178870 178871 178872 178873 178874 178875 178876 |
sqlite3ParserARG_FETCH
sqlite3ParserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
if( TOKEN.z[0] ){
| | | 179176 179177 179178 179179 179180 179181 179182 179183 179184 179185 179186 179187 179188 179189 179190 |
sqlite3ParserARG_FETCH
sqlite3ParserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
if( TOKEN.z[0] ){
parserSyntaxError(pParse, &TOKEN);
}else{
sqlite3ErrorMsg(pParse, "incomplete input");
}
/************ End %syntax_error code ******************************************/
sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
sqlite3ParserCTX_STORE
}
|
| ︙ | ︙ | |||
180361 180362 180363 180364 180365 180366 180367 |
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
if( pParse->zErrMsg==0 ){
pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
| > | > | 180667 180668 180669 180670 180671 180672 180673 180674 180675 180676 180677 180678 180679 180680 180681 180682 180683 |
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
if( pParse->zErrMsg==0 ){
pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){
sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
}
nErr++;
}
pParse->zTail = zSql;
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3_free(pParse->apVtabLock);
#endif
|
| ︙ | ︙ | |||
181069 181070 181071 181072 181073 181074 181075 | ** If the following global variable points to a string which is the ** name of a directory, then that directory will be used to store ** all database files specified with a relative pathname. ** ** See also the "PRAGMA data_store_directory" SQL command. */ SQLITE_API char *sqlite3_data_directory = 0; | < < < < < < < < < < < < < < < < < < < < < < < < < < | 181377 181378 181379 181380 181381 181382 181383 181384 181385 181386 181387 181388 181389 181390 | ** If the following global variable points to a string which is the ** name of a directory, then that directory will be used to store ** all database files specified with a relative pathname. ** ** See also the "PRAGMA data_store_directory" SQL command. */ SQLITE_API char *sqlite3_data_directory = 0; /* ** Initialize SQLite. ** ** This routine must be called to initialize the memory allocation, ** VFS, and mutex subsystems prior to doing any serious work with ** SQLite. But as long as you do not compile with SQLITE_OMIT_AUTOINIT |
| ︙ | ︙ | |||
181290 181291 181292 181293 181294 181295 181296 |
*/
#ifdef SQLITE_EXTRA_INIT
if( bRunExtraInit ){
int SQLITE_EXTRA_INIT(const char*);
rc = SQLITE_EXTRA_INIT(0);
}
#endif
| < < < < < < < | 181572 181573 181574 181575 181576 181577 181578 181579 181580 181581 181582 181583 181584 181585 |
*/
#ifdef SQLITE_EXTRA_INIT
if( bRunExtraInit ){
int SQLITE_EXTRA_INIT(const char*);
rc = SQLITE_EXTRA_INIT(0);
}
#endif
return rc;
}
/*
** Undo the effects of sqlite3_initialize(). Must not be called while
** there are outstanding database connections or memory allocations or
** while any part of SQLite is otherwise in use in any thread. This
|
| ︙ | ︙ | |||
182367 182368 182369 182370 182371 182372 182373 | } sqlite3HashClear(&db->aModule); #endif sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); | < < < < | 182642 182643 182644 182645 182646 182647 182648 182649 182650 182651 182652 182653 182654 182655 | } sqlite3HashClear(&db->aModule); #endif sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); db->eOpenState = SQLITE_STATE_ERROR; /* The temp-database schema is allocated differently from the other schema ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). ** So it needs to be freed here. Todo: Why not roll the temp schema into ** the same sqliteMalloc() as the one that allocates the database |
| ︙ | ︙ | |||
183873 183874 183875 183876 183877 183878 183879 |
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
return -1;
}
oldLimit = db->aLimit[limitId];
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
| | | | 184144 184145 184146 184147 184148 184149 184150 184151 184152 184153 184154 184155 184156 184157 184158 184159 |
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
return -1;
}
oldLimit = db->aLimit[limitId];
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
}else if( newLimit<SQLITE_MIN_LENGTH && limitId==SQLITE_LIMIT_LENGTH ){
newLimit = SQLITE_MIN_LENGTH;
}
db->aLimit[limitId] = newLimit;
}
return oldLimit; /* IMP: R-53341-35419 */
}
/*
|
| ︙ | ︙ | |||
184393 184394 184395 184396 184397 184398 184399 184400 184401 184402 184403 184404 184405 184406 |
assert( SQLITE_OPEN_CREATE == 0x04 );
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
if( ((1<<(flags&7)) & 0x46)==0 ){
rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */
}else{
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
}
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
sqlite3_free(zErrMsg);
goto opendb_out;
| > | 184664 184665 184666 184667 184668 184669 184670 184671 184672 184673 184674 184675 184676 184677 184678 |
assert( SQLITE_OPEN_CREATE == 0x04 );
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
if( ((1<<(flags&7)) & 0x46)==0 ){
rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */
}else{
if( zFilename==0 ) zFilename = ":memory:";
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
}
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
sqlite3_free(zErrMsg);
goto opendb_out;
|
| ︙ | ︙ | |||
185235 185236 185237 185238 185239 185240 185241 185242 185243 185244 185245 185246 185247 185248 |
sqlite3ShowTriggerList(0);
#endif
#ifndef SQLITE_OMIT_WINDOWFUNC
sqlite3ShowWindow(0);
sqlite3ShowWinFunc(0);
#endif
sqlite3ShowSelect(0);
}
#endif
break;
}
/*
| > | 185507 185508 185509 185510 185511 185512 185513 185514 185515 185516 185517 185518 185519 185520 185521 |
sqlite3ShowTriggerList(0);
#endif
#ifndef SQLITE_OMIT_WINDOWFUNC
sqlite3ShowWindow(0);
sqlite3ShowWinFunc(0);
#endif
sqlite3ShowSelect(0);
sqlite3ShowWhereTerm(0);
}
#endif
break;
}
/*
|
| ︙ | ︙ | |||
185547 185548 185549 185550 185551 185552 185553 |
u64 *pU64 = va_arg(ap,u64*);
int *pI2 = va_arg(ap,int*);
*pI1 = rLogEst;
*pU64 = sqlite3LogEstToInt(rLogEst);
*pI2 = sqlite3LogEst(*pU64);
break;
}
| < < < < < < < < < < < < < < < < < < | 185820 185821 185822 185823 185824 185825 185826 185827 185828 185829 185830 185831 185832 185833 |
u64 *pU64 = va_arg(ap,u64*);
int *pI2 = va_arg(ap,int*);
*pI1 = rLogEst;
*pU64 = sqlite3LogEstToInt(rLogEst);
*pI2 = sqlite3LogEst(*pU64);
break;
}
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
/* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue)
**
** If "id" is an integer between 1 and SQLITE_NTUNE then set the value
** of the id-th tuning parameter to *piValue. If "id" is between -1
** and -SQLITE_NTUNE, then write the current value of the (-id)-th
|
| ︙ | ︙ | |||
185873 185874 185875 185876 185877 185878 185879 185880 185881 185882 185883 185884 185885 185886 185887 |
sqlite3_mutex_enter(db->mutex);
if( db->autoCommit==0 ){
int iDb = sqlite3FindDbName(db, zDb);
if( iDb==0 || iDb>1 ){
Btree *pBt = db->aDb[iDb].pBt;
if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
}
}
}
}
| > > > > | 186128 186129 186130 186131 186132 186133 186134 186135 186136 186137 186138 186139 186140 186141 186142 186143 186144 186145 186146 |
sqlite3_mutex_enter(db->mutex);
if( db->autoCommit==0 ){
int iDb = sqlite3FindDbName(db, zDb);
if( iDb==0 || iDb>1 ){
Btree *pBt = db->aDb[iDb].pBt;
if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){
Pager *pPager = sqlite3BtreePager(pBt);
i64 dummy = 0;
sqlite3PagerSnapshotOpen(pPager, (sqlite3_snapshot*)&dummy);
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
sqlite3PagerSnapshotOpen(pPager, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
}
}
}
}
|
| ︙ | ︙ | |||
189663 189664 189665 189666 189667 189668 189669 189670 189671 189672 189673 189674 189675 189676 189677 189678 189679 189680 |
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
p1 += fts3GetVarint32(p1, &iCol1);
}
if( *p2==POS_COLUMN ){
p2++;
p2 += fts3GetVarint32(p2, &iCol2);
}
while( 1 ){
if( iCol1==iCol2 ){
char *pSave = p;
sqlite3_int64 iPrev = 0;
sqlite3_int64 iPos1 = 0;
| > > > > > | 189922 189923 189924 189925 189926 189927 189928 189929 189930 189931 189932 189933 189934 189935 189936 189937 189938 189939 189940 189941 189942 189943 189944 |
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
p1 += fts3GetVarint32(p1, &iCol1);
/* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN
** entry, so this is actually end-of-doclist. */
if( iCol1==0 ) return 0;
}
if( *p2==POS_COLUMN ){
p2++;
p2 += fts3GetVarint32(p2, &iCol2);
/* As above, iCol2==0 indicates corruption. */
if( iCol2==0 ) return 0;
}
while( 1 ){
if( iCol1==iCol2 ){
char *pSave = p;
sqlite3_int64 iPrev = 0;
sqlite3_int64 iPos1 = 0;
|
| ︙ | ︙ | |||
192837 192838 192839 192840 192841 192842 192843 |
/* Allocate temporary working space. */
for(p=pExpr; p->pLeft; p=p->pLeft){
assert( p->pRight->pPhrase->doclist.nList>0 );
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
| | | 193101 193102 193103 193104 193105 193106 193107 193108 193109 193110 193111 193112 193113 193114 193115 |
/* Allocate temporary working space. */
for(p=pExpr; p->pLeft; p=p->pLeft){
assert( p->pRight->pPhrase->doclist.nList>0 );
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX);
if( !aTmp ){
*pRc = SQLITE_NOMEM;
res = 0;
}else{
char *aPoslist = p->pPhrase->doclist.pList;
int nToken = p->pPhrase->nToken;
|
| ︙ | ︙ | |||
193488 193489 193490 193491 193492 193493 193494 |
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
return SQLITE_CORRUPT_VTAB;
}
#endif
| | | 193752 193753 193754 193755 193756 193757 193758 193759 193760 193761 193762 193763 193764 193765 193766 |
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
return SQLITE_CORRUPT_VTAB;
}
#endif
#if !defined(SQLITE_CORE)
/*
** Initialize API pointer table, if required.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_fts3_init(
|
| ︙ | ︙ | |||
194390 194391 194392 194393 194394 194395 194396 |
const char *zByte;
int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0;
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
Fts3PhraseToken *pToken;
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
| < < | > > > < < < | > > > > < < < < < < | > | > > | | | 194654 194655 194656 194657 194658 194659 194660 194661 194662 194663 194664 194665 194666 194667 194668 194669 194670 194671 194672 194673 194674 194675 194676 194677 194678 194679 194680 194681 194682 194683 194684 194685 194686 194687 194688 194689 194690 194691 194692 194693 194694 194695 194696 194697 194698 194699 194700 194701 194702 194703 194704 194705 194706 194707 194708 194709 194710 194711 194712 194713 194714 194715 194716 194717 194718 194719 194720 194721 194722 194723 194724 194725 194726 194727 |
const char *zByte;
int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0;
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
Fts3PhraseToken *pToken;
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
if( !zTemp || !p ){
rc = SQLITE_NOMEM;
goto getnextstring_out;
}
assert( nToken==ii );
pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
memset(pToken, 0, sizeof(Fts3PhraseToken));
memcpy(&zTemp[nTemp], zByte, nByte);
nTemp += nByte;
pToken->n = nByte;
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
nToken = ii+1;
}
}
}
if( rc==SQLITE_DONE ){
int jj;
char *zBuf = 0;
p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
if( !p ){
rc = SQLITE_NOMEM;
goto getnextstring_out;
}
memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
p->eType = FTSQUERY_PHRASE;
p->pPhrase = (Fts3Phrase *)&p[1];
p->pPhrase->iColumn = pParse->iDefaultCol;
p->pPhrase->nToken = nToken;
zBuf = (char *)&p->pPhrase->aToken[nToken];
assert( nTemp==0 || zTemp );
if( zTemp ){
memcpy(zBuf, zTemp, nTemp);
}
for(jj=0; jj<p->pPhrase->nToken; jj++){
p->pPhrase->aToken[jj].z = zBuf;
zBuf += p->pPhrase->aToken[jj].n;
}
rc = SQLITE_OK;
}
getnextstring_out:
if( pCursor ){
pModule->xClose(pCursor);
}
sqlite3_free(zTemp);
if( rc!=SQLITE_OK ){
sqlite3_free(p);
p = 0;
}
*ppExpr = p;
return rc;
}
/*
** The output variable *ppExpr is populated with an allocated Fts3Expr
** structure, or set to 0 if the end of the input buffer is reached.
**
** Returns an SQLite error code. SQLITE_OK if everything works, SQLITE_NOMEM
|
| ︙ | ︙ | |||
208865 208866 208867 208868 208869 208870 208871 |
}
if( zPath[0]=='.' ){
int rawKey = 1;
x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
| | > > | 209128 209129 209130 209131 209132 209133 209134 209135 209136 209137 209138 209139 209140 209141 209142 209143 209144 |
}
if( zPath[0]=='.' ){
int rawKey = 1;
x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
for(i=1; zPath[i] && zPath[i]!='"'; i++){
if( zPath[i]=='\\' && zPath[i+1]!=0 ) i++;
}
nKey = i-1;
if( zPath[i] ){
i++;
}else{
return JSON_LOOKUP_PATHERROR;
}
testcase( nKey==0 );
|
| ︙ | ︙ | |||
217777 217778 217779 217780 217781 217782 217783 |
pGeomCtx->xDestructor = xDestructor;
pGeomCtx->pContext = pContext;
return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
| | | 218042 218043 218044 218045 218046 218047 218048 218049 218050 218051 218052 218053 218054 218055 218056 |
pGeomCtx->xDestructor = xDestructor;
pGeomCtx->pContext = pContext;
return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_rtree_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
|
| ︙ | ︙ | |||
218368 218369 218370 218371 218372 218373 218374 |
p->xFunc, 0, 0
);
}
return rc;
}
| | | 218633 218634 218635 218636 218637 218638 218639 218640 218641 218642 218643 218644 218645 218646 218647 |
p->xFunc, 0, 0
);
}
return rc;
}
#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_icu_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
|
| ︙ | ︙ | |||
219626 219627 219628 219629 219630 219631 219632 219633 219634 219635 219636 219637 219638 219639 |
** file should be copied to page iDbPage of the database file.
*/
struct RbuFrame {
u32 iDbPage;
u32 iWalFrame;
};
/*
** RBU handle.
**
** nPhaseOneStep:
** If the RBU database contains an rbu_count table, this value is set to
** a running estimate of the number of b-tree operations required to
** finish populating the *-oal file. This allows the sqlite3_bp_progress()
| > > > > > > > > > > > > > > > > > > > > > | 219891 219892 219893 219894 219895 219896 219897 219898 219899 219900 219901 219902 219903 219904 219905 219906 219907 219908 219909 219910 219911 219912 219913 219914 219915 219916 219917 219918 219919 219920 219921 219922 219923 219924 219925 |
** file should be copied to page iDbPage of the database file.
*/
struct RbuFrame {
u32 iDbPage;
u32 iWalFrame;
};
#ifndef UNUSED_PARAMETER
/*
** The following macros are used to suppress compiler warnings and to
** make it clear to human readers when a function parameter is deliberately
** left unused within the body of a function. This usually happens when
** a function is called via a function pointer. For example the
** implementation of an SQL aggregate step callback may not use the
** parameter indicating the number of arguments passed to the aggregate,
** if it knows that this is enforced elsewhere.
**
** When a function parameter is not used at all within the body of a function,
** it is generally named "NotUsed" or "NotUsed2" to make things even clearer.
** However, these macros may also be used to suppress warnings related to
** parameters that may or may not be used depending on compilation options.
** For example those parameters only used in assert() statements. In these
** cases the parameters are named as per the usual conventions.
*/
#define UNUSED_PARAMETER(x) (void)(x)
#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y)
#endif
/*
** RBU handle.
**
** nPhaseOneStep:
** If the RBU database contains an rbu_count table, this value is set to
** a running estimate of the number of b-tree operations required to
** finish populating the *-oal file. This allows the sqlite3_bp_progress()
|
| ︙ | ︙ | |||
219677 219678 219679 219680 219681 219682 219683 |
char *zTarget; /* Path to target db */
char *zRbu; /* Path to rbu db */
char *zState; /* Path to state db (or NULL if zRbu) */
char zStateDb[5]; /* Db name for state ("stat" or "main") */
int rc; /* Value returned by last rbu_step() call */
char *zErrmsg; /* Error message if rc!=SQLITE_OK */
int nStep; /* Rows processed for current object */
| | | 219963 219964 219965 219966 219967 219968 219969 219970 219971 219972 219973 219974 219975 219976 219977 |
char *zTarget; /* Path to target db */
char *zRbu; /* Path to rbu db */
char *zState; /* Path to state db (or NULL if zRbu) */
char zStateDb[5]; /* Db name for state ("stat" or "main") */
int rc; /* Value returned by last rbu_step() call */
char *zErrmsg; /* Error message if rc!=SQLITE_OK */
int nStep; /* Rows processed for current object */
sqlite3_int64 nProgress; /* Rows processed for all objects */
RbuObjIter objiter; /* Iterator for skipping through tbl/idx */
const char *zVfsName; /* Name of automatically created rbu vfs */
rbu_file *pTargetFd; /* File handle open on target db */
int nPagePerSector; /* Pages per sector for pTargetFd */
i64 iOalSz;
i64 nPhaseOneStep;
void *pRenameArg;
|
| ︙ | ︙ | |||
219794 219795 219796 219797 219798 219799 219800 |
int c;
unsigned char *z = (unsigned char*)*pz;
unsigned char *zStart = z;
while( (c = zValue[0x7f&*(z++)])>=0 ){
v = (v<<6) + c;
}
z--;
| | | 220080 220081 220082 220083 220084 220085 220086 220087 220088 220089 220090 220091 220092 220093 220094 |
int c;
unsigned char *z = (unsigned char*)*pz;
unsigned char *zStart = z;
while( (c = zValue[0x7f&*(z++)])>=0 ){
v = (v<<6) + c;
}
z--;
*pLen -= (int)(z - zStart);
*pz = (char*)z;
return v;
}
#if RBU_ENABLE_DELTA_CKSUM
/*
** Compute a 32-bit checksum on the N-byte buffer. Return the result.
|
| ︙ | ︙ | |||
219979 219980 219981 219982 219983 219984 219985 219986 219987 219988 219989 219990 219991 219992 | int nOrig; int nOut; int nOut2; char *aOut; assert( argc==2 ); nOrig = sqlite3_value_bytes(argv[0]); aOrig = (const char*)sqlite3_value_blob(argv[0]); nDelta = sqlite3_value_bytes(argv[1]); aDelta = (const char*)sqlite3_value_blob(argv[1]); /* Figure out the size of the output */ | > | 220265 220266 220267 220268 220269 220270 220271 220272 220273 220274 220275 220276 220277 220278 220279 | int nOrig; int nOut; int nOut2; char *aOut; assert( argc==2 ); UNUSED_PARAMETER(argc); nOrig = sqlite3_value_bytes(argv[0]); aOrig = (const char*)sqlite3_value_blob(argv[0]); nDelta = sqlite3_value_bytes(argv[1]); aDelta = (const char*)sqlite3_value_blob(argv[1]); /* Figure out the size of the output */ |
| ︙ | ︙ | |||
221558 221559 221560 221561 221562 221563 221564 |
pIter->aIdxCol[0].zSpan = &zSql[i+1];
}
nParen++;
}
else if( c==')' ){
nParen--;
if( nParen==0 ){
| | | | 221845 221846 221847 221848 221849 221850 221851 221852 221853 221854 221855 221856 221857 221858 221859 221860 221861 221862 221863 221864 221865 |
pIter->aIdxCol[0].zSpan = &zSql[i+1];
}
nParen++;
}
else if( c==')' ){
nParen--;
if( nParen==0 ){
int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan);
pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
i++;
break;
}
}else if( c==',' && nParen==1 ){
int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan);
pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1];
}else if( c=='"' || c=='\'' || c=='`' ){
for(i++; 1; i++){
if( zSql[i]==c ){
if( zSql[i+1]!=c ) break;
i++;
|
| ︙ | ︙ | |||
222254 222255 222256 222257 222258 222259 222260 222261 222262 222263 222264 222265 222266 222267 |
#endif
{
int i, sz;
sz = (int)strlen(z)&0xffffff;
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4);
}
#endif
}
/*
** Return the current wal-index header checksum for the target database
** as a 64-bit integer.
**
| > > | 222541 222542 222543 222544 222545 222546 222547 222548 222549 222550 222551 222552 222553 222554 222555 222556 |
#endif
{
int i, sz;
sz = (int)strlen(z)&0xffffff;
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4);
}
#else
UNUSED_PARAMETER2(zBase,z);
#endif
}
/*
** Return the current wal-index header checksum for the target database
** as a 64-bit integer.
**
|
| ︙ | ︙ | |||
222838 222839 222840 222841 222842 222843 222844 |
rc = prepareFreeAndCollectError(p->dbRbu, &pInsert, &p->zErrmsg,
sqlite3_mprintf(
"INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES "
"(%d, %d), "
"(%d, %Q), "
"(%d, %Q), "
"(%d, %d), "
| | | 223127 223128 223129 223130 223131 223132 223133 223134 223135 223136 223137 223138 223139 223140 223141 |
rc = prepareFreeAndCollectError(p->dbRbu, &pInsert, &p->zErrmsg,
sqlite3_mprintf(
"INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES "
"(%d, %d), "
"(%d, %Q), "
"(%d, %Q), "
"(%d, %d), "
"(%d, %lld), "
"(%d, %lld), "
"(%d, %lld), "
"(%d, %lld), "
"(%d, %lld), "
"(%d, %Q) ",
p->zStateDb,
RBU_STATE_STAGE, eStage,
|
| ︙ | ︙ | |||
223196 223197 223198 223199 223200 223201 223202 223203 223204 223205 223206 223207 223208 223209 |
sqlite3rbu *p = (sqlite3rbu*)sqlite3_user_data(pCtx);
sqlite3_stmt *pStmt = 0;
char *zErrmsg = 0;
int rc;
sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
assert( nVal==1 );
rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
sqlite3_mprintf("SELECT count(*) FROM sqlite_schema "
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
);
if( rc!=SQLITE_OK ){
sqlite3_result_error(pCtx, zErrmsg, -1);
| > | 223485 223486 223487 223488 223489 223490 223491 223492 223493 223494 223495 223496 223497 223498 223499 |
sqlite3rbu *p = (sqlite3rbu*)sqlite3_user_data(pCtx);
sqlite3_stmt *pStmt = 0;
char *zErrmsg = 0;
int rc;
sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
assert( nVal==1 );
UNUSED_PARAMETER(nVal);
rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
sqlite3_mprintf("SELECT count(*) FROM sqlite_schema "
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
);
if( rc!=SQLITE_OK ){
sqlite3_result_error(pCtx, zErrmsg, -1);
|
| ︙ | ︙ | |||
223471 223472 223473 223474 223475 223476 223477 |
*/
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
const char *zTarget,
const char *zState
){
if( zTarget==0 ){ return rbuMisuseError(); }
if( zState ){
| | | 223761 223762 223763 223764 223765 223766 223767 223768 223769 223770 223771 223772 223773 223774 223775 |
*/
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
const char *zTarget,
const char *zState
){
if( zTarget==0 ){ return rbuMisuseError(); }
if( zState ){
size_t n = strlen(zState);
if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){
return rbuMisuseError();
}
}
/* TODO: Check that both arguments are non-NULL */
return openRbuHandle(0, zTarget, zState);
}
|
| ︙ | ︙ | |||
223688 223689 223690 223691 223692 223693 223694 223695 223696 223697 223698 223699 223700 223701 |
}
/*
** Default xRename callback for RBU.
*/
static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
int rc = SQLITE_OK;
#if defined(_WIN32_WCE)
{
LPWSTR zWideOld;
LPWSTR zWideNew;
zWideOld = rbuWinUtf8ToUnicode(zOld);
if( zWideOld ){
| > | 223978 223979 223980 223981 223982 223983 223984 223985 223986 223987 223988 223989 223990 223991 223992 |
}
/*
** Default xRename callback for RBU.
*/
static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
int rc = SQLITE_OK;
UNUSED_PARAMETER(pArg);
#if defined(_WIN32_WCE)
{
LPWSTR zWideOld;
LPWSTR zWideNew;
zWideOld = rbuWinUtf8ToUnicode(zOld);
if( zWideOld ){
|
| ︙ | ︙ | |||
224592 224593 224594 224595 224596 224597 224598 224599 224600 224601 224602 224603 224604 224605 |
return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
}
/*
** No-op.
*/
static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return 0;
}
/*
** Deregister and destroy an RBU vfs created by an earlier call to
** sqlite3rbu_create_vfs().
*/
| > > > | 224883 224884 224885 224886 224887 224888 224889 224890 224891 224892 224893 224894 224895 224896 224897 224898 224899 |
return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
}
/*
** No-op.
*/
static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(a);
UNUSED_PARAMETER(b);
return 0;
}
/*
** Deregister and destroy an RBU vfs created by an earlier call to
** sqlite3rbu_create_vfs().
*/
|
| ︙ | ︙ | |||
225648 225649 225650 225651 225652 225653 225654 | ** This is an eponymous virtual table so it does not need to be created before ** use. The optional argument to the sqlite_dbpage() table name is the ** schema for the database file that is to be read. The default schema is ** "main". ** ** The data field of sqlite_dbpage table can be updated. The new ** value must be a BLOB which is the correct page size, otherwise the | > > > > | > > | 225942 225943 225944 225945 225946 225947 225948 225949 225950 225951 225952 225953 225954 225955 225956 225957 225958 225959 225960 225961 225962 |
** This is an eponymous virtual table so it does not need to be created before
** use. The optional argument to the sqlite_dbpage() table name is the
** schema for the database file that is to be read. The default schema is
** "main".
**
** The data field of sqlite_dbpage table can be updated. The new
** value must be a BLOB which is the correct page size, otherwise the
** update fails. INSERT operations also work, and operate as if they
** where REPLACE. The size of the database can be extended by INSERT-ing
** new pages on the end.
**
** Rows may not be deleted. However, doing an INSERT to page number N
** with NULL page data causes the N-th page and all subsequent pages to be
** deleted and the database to be truncated.
*/
/* #include "sqliteInt.h" ** Requires access to internal data structures ** */
#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
typedef struct DbpageTable DbpageTable;
|
| ︙ | ︙ | |||
225671 225672 225673 225674 225675 225676 225677 225678 225679 225680 225681 225682 225683 |
int iDb; /* Index of database to analyze */
int szPage; /* Size of each page in bytes */
};
struct DbpageTable {
sqlite3_vtab base; /* Base class. Must be first */
sqlite3 *db; /* The database */
};
/* Columns */
#define DBPAGE_COLUMN_PGNO 0
#define DBPAGE_COLUMN_DATA 1
#define DBPAGE_COLUMN_SCHEMA 2
| > > < | 225971 225972 225973 225974 225975 225976 225977 225978 225979 225980 225981 225982 225983 225984 225985 225986 225987 225988 225989 225990 225991 225992 |
int iDb; /* Index of database to analyze */
int szPage; /* Size of each page in bytes */
};
struct DbpageTable {
sqlite3_vtab base; /* Base class. Must be first */
sqlite3 *db; /* The database */
int iDbTrunc; /* Database to truncate */
Pgno pgnoTrunc; /* Size to truncate to */
};
/* Columns */
#define DBPAGE_COLUMN_PGNO 0
#define DBPAGE_COLUMN_DATA 1
#define DBPAGE_COLUMN_SCHEMA 2
/*
** Connect to or create a dbpagevfs virtual table.
*/
static int dbpageConnect(
sqlite3 *db,
|
| ︙ | ︙ | |||
225941 225942 225943 225944 225945 225946 225947 |
sqlite_int64 *pRowid
){
DbpageTable *pTab = (DbpageTable *)pVtab;
Pgno pgno;
DbPage *pDbPage = 0;
int rc = SQLITE_OK;
char *zErr = 0;
| < > > > > > | < | < | | | > > > > > | | | | | | > | > > > > > > > | | > < < | < | > > > > | 226242 226243 226244 226245 226246 226247 226248 226249 226250 226251 226252 226253 226254 226255 226256 226257 226258 226259 226260 226261 226262 226263 226264 226265 226266 226267 226268 226269 226270 226271 226272 226273 226274 226275 226276 226277 226278 226279 226280 226281 226282 226283 226284 226285 226286 226287 226288 226289 226290 226291 226292 226293 226294 226295 226296 226297 226298 226299 226300 226301 226302 226303 226304 226305 226306 226307 226308 226309 226310 226311 226312 226313 226314 226315 226316 226317 226318 226319 226320 226321 226322 |
sqlite_int64 *pRowid
){
DbpageTable *pTab = (DbpageTable *)pVtab;
Pgno pgno;
DbPage *pDbPage = 0;
int rc = SQLITE_OK;
char *zErr = 0;
int iDb;
Btree *pBt;
Pager *pPager;
int szPage;
int isInsert;
(void)pRowid;
if( pTab->db->flags & SQLITE_Defensive ){
zErr = "read-only";
goto update_fail;
}
if( argc==1 ){
zErr = "cannot delete";
goto update_fail;
}
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
pgno = (Pgno)sqlite3_value_int(argv[2]);
isInsert = 1;
}else{
pgno = sqlite3_value_int(argv[0]);
if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
zErr = "cannot insert";
goto update_fail;
}
isInsert = 0;
}
if( sqlite3_value_type(argv[4])==SQLITE_NULL ){
iDb = 0;
}else{
const char *zSchema = (const char*)sqlite3_value_text(argv[4]);
iDb = sqlite3FindDbName(pTab->db, zSchema);
if( iDb<0 ){
zErr = "no such schema";
goto update_fail;
}
}
pBt = pTab->db->aDb[iDb].pBt;
if( pgno<1 || NEVER(pBt==0) ){
zErr = "bad page number";
goto update_fail;
}
szPage = sqlite3BtreeGetPageSize(pBt);
if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|| sqlite3_value_bytes(argv[3])!=szPage
){
if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){
/* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and
** all subsequent pages to be deleted. */
pTab->iDbTrunc = iDb;
pgno--;
pTab->pgnoTrunc = pgno;
}else{
zErr = "bad page value";
goto update_fail;
}
}
pPager = sqlite3BtreePager(pBt);
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
if( rc==SQLITE_OK ){
const void *pData = sqlite3_value_blob(argv[3]);
if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
unsigned char *aPage = sqlite3PagerGetData(pDbPage);
memcpy(aPage, pData, szPage);
pTab->pgnoTrunc = 0;
}
}else{
pTab->pgnoTrunc = 0;
}
sqlite3PagerUnref(pDbPage);
return rc;
update_fail:
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
|
| ︙ | ︙ | |||
226013 226014 226015 226016 226017 226018 226019 226020 226021 226022 226023 226024 226025 226026 226027 226028 226029 |
DbpageTable *pTab = (DbpageTable *)pVtab;
sqlite3 *db = pTab->db;
int i;
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
}
return SQLITE_OK;
}
/*
** Invoke this routine to register the "dbpage" virtual table module
*/
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
static sqlite3_module dbpage_module = {
0, /* iVersion */
| > > > > > > > > > > > > > > > > > > > > > > | 226331 226332 226333 226334 226335 226336 226337 226338 226339 226340 226341 226342 226343 226344 226345 226346 226347 226348 226349 226350 226351 226352 226353 226354 226355 226356 226357 226358 226359 226360 226361 226362 226363 226364 226365 226366 226367 226368 226369 |
DbpageTable *pTab = (DbpageTable *)pVtab;
sqlite3 *db = pTab->db;
int i;
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
}
pTab->pgnoTrunc = 0;
return SQLITE_OK;
}
/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT
*/
static int dbpageSync(sqlite3_vtab *pVtab){
DbpageTable *pTab = (DbpageTable *)pVtab;
if( pTab->pgnoTrunc>0 ){
Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
}
pTab->pgnoTrunc = 0;
return SQLITE_OK;
}
/* Cancel any pending truncate.
*/
static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){
DbpageTable *pTab = (DbpageTable *)pVtab;
pTab->pgnoTrunc = 0;
(void)notUsed1;
return SQLITE_OK;
}
/*
** Invoke this routine to register the "dbpage" virtual table module
*/
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
static sqlite3_module dbpage_module = {
0, /* iVersion */
|
| ︙ | ︙ | |||
226037 226038 226039 226040 226041 226042 226043 |
dbpageFilter, /* xFilter - configure scan constraints */
dbpageNext, /* xNext - advance a cursor */
dbpageEof, /* xEof - check for end of scan */
dbpageColumn, /* xColumn - read data */
dbpageRowid, /* xRowid - read data */
dbpageUpdate, /* xUpdate */
dbpageBegin, /* xBegin */
| | | | 226377 226378 226379 226380 226381 226382 226383 226384 226385 226386 226387 226388 226389 226390 226391 226392 226393 226394 226395 226396 226397 226398 |
dbpageFilter, /* xFilter - configure scan constraints */
dbpageNext, /* xNext - advance a cursor */
dbpageEof, /* xEof - check for end of scan */
dbpageColumn, /* xColumn - read data */
dbpageRowid, /* xRowid - read data */
dbpageUpdate, /* xUpdate */
dbpageBegin, /* xBegin */
dbpageSync, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
dbpageRollbackTo, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
|
| ︙ | ︙ | |||
226132 226133 226134 226135 226136 226137 226138 226139 226140 226141 226142 226143 226144 226145 |
};
/*
** An object of this type is used internally as an abstraction for
** input data. Input data may be supplied either as a single large buffer
** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
** sqlite3changeset_start_strm()).
*/
struct SessionInput {
int bNoDiscard; /* If true, do not discard in InputBuffer() */
int iCurrent; /* Offset in aData[] of current change */
int iNext; /* Offset in aData[] of next change */
u8 *aData; /* Pointer to buffer containing changeset */
int nData; /* Number of bytes in aData */
| > > > > | 226472 226473 226474 226475 226476 226477 226478 226479 226480 226481 226482 226483 226484 226485 226486 226487 226488 226489 |
};
/*
** An object of this type is used internally as an abstraction for
** input data. Input data may be supplied either as a single large buffer
** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
** sqlite3changeset_start_strm()).
**
** bNoDiscard:
** If true, then the only time data is discarded is as a result of explicit
** sessionDiscardData() calls. Not within every sessionInputBuffer() call.
*/
struct SessionInput {
int bNoDiscard; /* If true, do not discard in InputBuffer() */
int iCurrent; /* Offset in aData[] of current change */
int iNext; /* Offset in aData[] of next change */
u8 *aData; /* Pointer to buffer containing changeset */
int nData; /* Number of bytes in aData */
|
| ︙ | ︙ | |||
227815 227816 227817 227818 227819 227820 227821 |
pTab->nEntry++;
/* Figure out how large an allocation is required */
nByte = sizeof(SessionChange);
for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
| > > | < > | | | > | 228159 228160 228161 228162 228163 228164 228165 228166 228167 228168 228169 228170 228171 228172 228173 228174 228175 228176 228177 228178 228179 228180 228181 228182 228183 228184 228185 |
pTab->nEntry++;
/* Figure out how large an allocation is required */
nByte = sizeof(SessionChange);
for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
/* This may fail if the column has a non-NULL default and was added
** using ALTER TABLE ADD COLUMN after this record was created. */
rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p);
}else if( pTab->abPK[i] ){
TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
assert( trc==SQLITE_OK );
}
if( rc==SQLITE_OK ){
/* This may fail if SQLite value p contains a utf-16 string that must
** be converted to utf-8 and an OOM error occurs while doing so. */
rc = sessionSerializeValue(0, p, &nByte);
}
if( rc!=SQLITE_OK ) goto error_out;
}
if( pTab->bRowid ){
nByte += 9; /* Size of rowid field - an integer */
}
/* Allocate the change object */
|
| ︙ | ︙ | |||
231182 231183 231184 231185 231186 231187 231188 231189 231190 231191 231192 231193 231194 231195 231196 |
){
int schemaMismatch = 0;
int rc = SQLITE_OK; /* Return code */
const char *zTab = 0; /* Name of current table */
int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
SessionApplyCtx sApply; /* changeset_apply() context object */
int bPatchset;
assert( xConflict!=0 );
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
sApply.bRebase = (ppRebase && pnRebase);
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
| > > > > > > > < | 231529 231530 231531 231532 231533 231534 231535 231536 231537 231538 231539 231540 231541 231542 231543 231544 231545 231546 231547 231548 231549 231550 231551 231552 231553 231554 231555 231556 231557 |
){
int schemaMismatch = 0;
int rc = SQLITE_OK; /* Return code */
const char *zTab = 0; /* Name of current table */
int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
SessionApplyCtx sApply; /* changeset_apply() context object */
int bPatchset;
u64 savedFlag = db->flags & SQLITE_FkNoAction;
assert( xConflict!=0 );
sqlite3_mutex_enter(sqlite3_db_mutex(db));
if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
db->flags |= ((u64)SQLITE_FkNoAction);
db->aDb[0].pSchema->schema_cookie -= 32;
}
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
sApply.bRebase = (ppRebase && pnRebase);
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
|
| ︙ | ︙ | |||
231352 231353 231354 231355 231356 231357 231358 231359 231360 231361 231362 231363 231364 231365 | sessionUpdateFree(&sApply); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pDelete); sqlite3_finalize(sApply.pSelect); sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_free((char*)sApply.constraints.aBuf); sqlite3_free((char*)sApply.rebase.aBuf); sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } /* ** Apply the changeset passed via pChangeset/nChangeset to the main ** database attached to handle "db". | > > > > > > | 231705 231706 231707 231708 231709 231710 231711 231712 231713 231714 231715 231716 231717 231718 231719 231720 231721 231722 231723 231724 |
sessionUpdateFree(&sApply);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
sqlite3_free((char*)sApply.rebase.aBuf);
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
assert( db->flags & SQLITE_FkNoAction );
db->flags &= ~((u64)SQLITE_FkNoAction);
db->aDb[0].pSchema->schema_cookie -= 32;
}
sqlite3_mutex_leave(sqlite3_db_mutex(db));
return rc;
}
/*
** Apply the changeset passed via pChangeset/nChangeset to the main
** database attached to handle "db".
|
| ︙ | ︙ | |||
231380 231381 231382 231383 231384 231385 231386 |
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
int flags
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
| < < < < < < < < < < < | 231739 231740 231741 231742 231743 231744 231745 231746 231747 231748 231749 231750 231751 231752 231753 231754 231755 231756 231757 231758 231759 |
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
int flags
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
);
}
return rc;
}
/*
** Apply the changeset passed via pChangeset/nChangeset to the main database
** attached to handle "db". Invoke the supplied conflict handler callback
** to resolve any conflicts encountered while applying the change.
|
| ︙ | ︙ | |||
231718 231719 231720 231721 231722 231723 231724 231725 231726 231727 231728 231729 231730 231731 231732 231733 231734 231735 231736 231737 231738 231739 231740 231741 231742 231743 231744 231745 231746 231747 |
pOut->nBuf = 0;
if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){
/* Append the missing default column values to the record. */
sessionAppendBlob(pOut, aRec, nRec, &rc);
if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){
rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt);
}
for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){
int eType = sqlite3_column_type(pTab->pDfltStmt, ii);
sessionAppendByte(pOut, eType, &rc);
switch( eType ){
case SQLITE_FLOAT:
case SQLITE_INTEGER: {
i64 iVal;
if( eType==SQLITE_INTEGER ){
iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
}else{
double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
memcpy(&iVal, &rVal, sizeof(i64));
}
if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
}
break;
}
case SQLITE_BLOB:
case SQLITE_TEXT: {
int n = sqlite3_column_bytes(pTab->pDfltStmt, ii);
| > > > > | 232066 232067 232068 232069 232070 232071 232072 232073 232074 232075 232076 232077 232078 232079 232080 232081 232082 232083 232084 232085 232086 232087 232088 232089 232090 232091 232092 232093 232094 232095 232096 232097 232098 232099 |
pOut->nBuf = 0;
if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){
/* Append the missing default column values to the record. */
sessionAppendBlob(pOut, aRec, nRec, &rc);
if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){
rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt);
if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){
rc = sqlite3_errcode(pGrp->db);
}
}
for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){
int eType = sqlite3_column_type(pTab->pDfltStmt, ii);
sessionAppendByte(pOut, eType, &rc);
switch( eType ){
case SQLITE_FLOAT:
case SQLITE_INTEGER: {
i64 iVal;
if( eType==SQLITE_INTEGER ){
iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
}else{
double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
memcpy(&iVal, &rVal, sizeof(i64));
}
if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
pOut->nBuf += 8;
}
break;
}
case SQLITE_BLOB:
case SQLITE_TEXT: {
int n = sqlite3_column_bytes(pTab->pDfltStmt, ii);
|
| ︙ | ︙ | |||
231872 231873 231874 231875 231876 231877 231878 231879 231880 231881 231882 231883 231884 231885 |
int bIndirect = 0;
SessionChange *pChange = 0;
SessionChange *pExist = 0;
SessionChange **pp = 0;
SessionTable *pTab = 0;
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
| > > | 232224 232225 232226 232227 232228 232229 232230 232231 232232 232233 232234 232235 232236 232237 232238 232239 |
int bIndirect = 0;
SessionChange *pChange = 0;
SessionChange *pExist = 0;
SessionChange **pp = 0;
SessionTable *pTab = 0;
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
assert( nRec>0 );
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
|
| ︙ | ︙ | |||
231950 231951 231952 231953 231954 231955 231956 231957 231958 231959 231960 231961 231962 231963 |
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
int bRebase /* True if hash table is for rebasing */
){
u8 *aRec;
int nRec;
int rc = SQLITE_OK;
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
if( rc!=SQLITE_OK ) break;
}
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
| > | 232304 232305 232306 232307 232308 232309 232310 232311 232312 232313 232314 232315 232316 232317 232318 |
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
int bRebase /* True if hash table is for rebasing */
){
u8 *aRec;
int nRec;
int rc = SQLITE_OK;
pIter->in.bNoDiscard = 1;
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
if( rc!=SQLITE_OK ) break;
}
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
|
| ︙ | ︙ | |||
232581 232582 232583 232584 232585 232586 232587 | } #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ /************** Begin file fts5.c ********************************************/ | | > > > > > > > > > > > > > > > > > > > > > > > > > > | 232936 232937 232938 232939 232940 232941 232942 232943 232944 232945 232946 232947 232948 232949 232950 232951 232952 232953 232954 232955 232956 232957 232958 232959 232960 232961 232962 232963 232964 232965 232966 232967 232968 232969 232970 232971 232972 232973 232974 232975 232976 232977 232978 232979 232980 232981 232982 232983 232984 232985 | } #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ /************** Begin file fts5.c ********************************************/ /* ** This, the "fts5.c" source file, is a composite file that is itself ** assembled from the following files: ** ** fts5.h ** fts5Int.h ** fts5parse.h <--- Generated from fts5parse.y by Lemon ** fts5parse.c <--- Generated from fts5parse.y by Lemon ** fts5_aux.c ** fts5_buffer.c ** fts5_config.c ** fts5_expr.c ** fts5_hash.c ** fts5_index.c ** fts5_main.c ** fts5_storage.c ** fts5_tokenize.c ** fts5_unicode2.c ** fts5_varint.c ** fts5_vocab.c */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif #if defined(NDEBUG) && defined(SQLITE_DEBUG) # undef NDEBUG #endif #ifdef HAVE_STDINT_H /* #include <stdint.h> */ #endif #ifdef HAVE_INTTYPES_H /* #include <inttypes.h> */ #endif /* ** 2014 May 31 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. |
| ︙ | ︙ | |||
232891 232892 232893 232894 232895 232896 232897 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in | < < | > > > > > > > > > > > > > > > > > | 233272 233273 233274 233275 233276 233277 233278 233279 233280 233281 233282 233283 233284 233285 233286 233287 233288 233289 233290 233291 233292 233293 233294 233295 233296 233297 233298 233299 233300 233301 233302 233303 233304 233305 233306 233307 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API may be slow in some cases if the token identified by parameters ** iIdx and iToken matched a prefix token in the query. In most cases, the ** first call to this API for each prefix token in the query is forced ** to scan the portion of the full-text index that matches the prefix ** token to collect the extra data required by this API. If the prefix ** token matches a large number of token instances in the document set, ** this may be a performance problem. ** ** If the user knows in advance that a query may use this API for a ** prefix token, FTS5 may be configured to collect all required data as part ** of the initial querying of the full-text index, avoiding the second scan ** entirely. This also causes prefix queries that do not use this API to ** run more slowly and use more memory. FTS5 may be configured in this way ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ** option, or on a per-query basis using the ** [fts5_insttoken | fts5_insttoken()] user function. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. |
| ︙ | ︙ | |||
232989 232990 232991 232992 232993 232994 232995 | /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting | < | 233385 233386 233387 233388 233389 233390 233391 233392 233393 233394 233395 233396 233397 233398 | /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** |
| ︙ | ︙ | |||
233558 233559 233560 233561 233562 233563 233564 233565 233566 233567 233568 233569 233570 233571 | int nCol; /* Number of columns */ char **azCol; /* Column names */ u8 *abUnindexed; /* True for unindexed columns */ int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ int bLocale; /* "locale=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; | > | 233953 233954 233955 233956 233957 233958 233959 233960 233961 233962 233963 233964 233965 233966 233967 | int nCol; /* Number of columns */ char **azCol; /* Column names */ u8 *abUnindexed; /* True for unindexed columns */ int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ int bLocale; /* "locale=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; |
| ︙ | ︙ | |||
233580 233581 233582 233583 233584 233585 233586 | int nAutomerge; /* 'automerge' setting */ int nCrisisMerge; /* Maximum allowed segments per level */ int nUsermerge; /* 'usermerge' setting */ int nHashSize; /* Bytes of memory for in-memory hash */ char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ int bSecureDelete; /* 'secure-delete' */ | | > | | | > | 233976 233977 233978 233979 233980 233981 233982 233983 233984 233985 233986 233987 233988 233989 233990 233991 233992 233993 233994 233995 233996 233997 233998 233999 234000 234001 234002 234003 234004 234005 234006 234007 234008 234009 234010 | int nAutomerge; /* 'automerge' setting */ int nCrisisMerge; /* Maximum allowed segments per level */ int nUsermerge; /* 'usermerge' setting */ int nHashSize; /* Bytes of memory for in-memory hash */ char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ int bSecureDelete; /* 'secure-delete' */ int nDeleteMerge; /* 'deletemerge' */ int bPrefixInsttoken; /* 'prefix-insttoken' */ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ char **pzErrmsg; #ifdef SQLITE_DEBUG int bPrefixIndex; /* True to use prefix-indexes */ #endif }; /* Current expected value of %_config table 'version' field. And ** the expected version if the 'secure-delete' option has ever been ** set on the table. */ #define FTS5_CURRENT_VERSION 4 #define FTS5_CURRENT_VERSION_SECUREDELETE 5 #define FTS5_CONTENT_NORMAL 0 #define FTS5_CONTENT_NONE 1 #define FTS5_CONTENT_EXTERNAL 2 #define FTS5_CONTENT_UNINDEXED 3 #define FTS5_DETAIL_FULL 0 #define FTS5_DETAIL_NONE 1 #define FTS5_DETAIL_COLUMNS 2 #define FTS5_PATTERN_NONE 0 #define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */ |
| ︙ | ︙ | |||
233836 233837 233838 233839 233840 233841 233842 | static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken(): */ | | > > > > > > > | 234234 234235 234236 234237 234238 234239 234240 234241 234242 234243 234244 234245 234246 234247 234248 234249 234250 234251 234252 234253 234254 234255 | static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken(): */ static int sqlite3Fts5IterToken( Fts5IndexIter *pIndexIter, const char *pToken, int nToken, i64 iRowid, int iCol, int iOff, const char **ppOut, int *pnOut ); /* ** Insert or remove data to or from the index. Each time a document is ** added to or removed from the index, this function is called one or more ** times. ** ** For an insert, it must be called once for each token in the new document. |
| ︙ | ︙ | |||
233970 233971 233972 233973 233974 233975 233976 | static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); static int sqlite3Fts5FlushToDisk(Fts5Table*); | | | | | | | < < < | 234375 234376 234377 234378 234379 234380 234381 234382 234383 234384 234385 234386 234387 234388 234389 234390 234391 234392 234393 234394 234395 |
static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig);
static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64);
static int sqlite3Fts5FlushToDisk(Fts5Table*);
static void sqlite3Fts5ClearLocale(Fts5Config *pConfig);
static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc);
static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal);
static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal,
const char **ppText, int *pnText, const char **ppLoc, int *pnLoc
);
/*
** End of interface to code in fts5.c.
**************************************************************************/
/**************************************************************************
** Interface to code in fts5_hash.c.
|
| ︙ | ︙ | |||
234061 234062 234063 234064 234065 234066 234067 | static int sqlite3Fts5StorageClose(Fts5Storage *p); static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); static int sqlite3Fts5DropAll(Fts5Config*); static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); | | | 234463 234464 234465 234466 234467 234468 234469 234470 234471 234472 234473 234474 234475 234476 234477 | static int sqlite3Fts5StorageClose(Fts5Storage *p); static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); static int sqlite3Fts5DropAll(Fts5Config*); static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); |
| ︙ | ︙ | |||
237268 237269 237270 237271 237272 237273 237274 237275 237276 237277 237278 237279 237280 237281 |
Fts5Config *pConfig, /* Configuration object to update */
const char *zCmd, /* Special command to parse */
const char *zArg, /* Argument to parse */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK;
int nCmd = (int)strlen(zCmd);
if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
const char *p;
int bFirst = 1;
if( pConfig->aPrefix==0 ){
pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
if( rc ) return rc;
| > | 237670 237671 237672 237673 237674 237675 237676 237677 237678 237679 237680 237681 237682 237683 237684 |
Fts5Config *pConfig, /* Configuration object to update */
const char *zCmd, /* Special command to parse */
const char *zArg, /* Argument to parse */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK;
int nCmd = (int)strlen(zCmd);
if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
const char *p;
int bFirst = 1;
if( pConfig->aPrefix==0 ){
pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
if( rc ) return rc;
|
| ︙ | ︙ | |||
237386 237387 237388 237389 237390 237391 237392 237393 237394 237395 237396 237397 237398 237399 |
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
rc = SQLITE_ERROR;
}else{
pConfig->bContentlessDelete = (zArg[0]=='1');
}
return rc;
}
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
if( pConfig->zContentRowid ){
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
rc = SQLITE_ERROR;
}else{
pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
| > > > > > > > > > > | 237789 237790 237791 237792 237793 237794 237795 237796 237797 237798 237799 237800 237801 237802 237803 237804 237805 237806 237807 237808 237809 237810 237811 237812 |
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
rc = SQLITE_ERROR;
}else{
pConfig->bContentlessDelete = (zArg[0]=='1');
}
return rc;
}
if( sqlite3_strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
rc = SQLITE_ERROR;
}else{
pConfig->bContentlessUnindexed = (zArg[0]=='1');
}
return rc;
}
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
if( pConfig->zContentRowid ){
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
rc = SQLITE_ERROR;
}else{
pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
|
| ︙ | ︙ | |||
237504 237505 237506 237507 237508 237509 237510 | return zRet; } static int fts5ConfigParseColumn( Fts5Config *p, char *zCol, char *zArg, | | > > > > > > > > | > > > > > > > > > | 237917 237918 237919 237920 237921 237922 237923 237924 237925 237926 237927 237928 237929 237930 237931 237932 237933 237934 237935 237936 237937 237938 237939 237940 237941 237942 237943 237944 237945 237946 237947 237948 237949 237950 237951 237952 237953 237954 237955 237956 237957 237958 237959 237960 237961 237962 237963 237964 237965 237966 237967 237968 237969 237970 237971 237972 237973 237974 237975 237976 237977 237978 237979 237980 237981 237982 237983 |
return zRet;
}
static int fts5ConfigParseColumn(
Fts5Config *p,
char *zCol,
char *zArg,
char **pzErr,
int *pbUnindexed
){
int rc = SQLITE_OK;
if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
|| 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
){
*pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
rc = SQLITE_ERROR;
}else if( zArg ){
if( 0==sqlite3_stricmp(zArg, "unindexed") ){
p->abUnindexed[p->nCol] = 1;
*pbUnindexed = 1;
}else{
*pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg);
rc = SQLITE_ERROR;
}
}
p->azCol[p->nCol++] = zCol;
return rc;
}
/*
** Populate the Fts5Config.zContentExprlist string.
*/
static int fts5ConfigMakeExprlist(Fts5Config *p){
int i;
int rc = SQLITE_OK;
Fts5Buffer buf = {0, 0, 0};
sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid);
if( p->eContent!=FTS5_CONTENT_NONE ){
assert( p->eContent==FTS5_CONTENT_EXTERNAL
|| p->eContent==FTS5_CONTENT_NORMAL
|| p->eContent==FTS5_CONTENT_UNINDEXED
);
for(i=0; i<p->nCol; i++){
if( p->eContent==FTS5_CONTENT_EXTERNAL ){
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]);
}else if( p->eContent==FTS5_CONTENT_NORMAL || p->abUnindexed[i] ){
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i);
}else{
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL");
}
}
}
if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){
for(i=0; i<p->nCol; i++){
if( p->abUnindexed[i]==0 ){
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i);
}else{
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL");
}
}
}
assert( p->zContentExprlist==0 );
p->zContentExprlist = (char*)buf.p;
return rc;
|
| ︙ | ︙ | |||
237573 237574 237575 237576 237577 237578 237579 237580 237581 237582 237583 237584 237585 237586 |
Fts5Config **ppOut, /* OUT: Results of parse */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK; /* Return code */
Fts5Config *pRet; /* New object to return */
int i;
sqlite3_int64 nByte;
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
if( pRet==0 ) return SQLITE_NOMEM;
memset(pRet, 0, sizeof(Fts5Config));
pRet->pGlobal = pGlobal;
pRet->db = db;
pRet->iCookie = -1;
| > | 238003 238004 238005 238006 238007 238008 238009 238010 238011 238012 238013 238014 238015 238016 238017 |
Fts5Config **ppOut, /* OUT: Results of parse */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK; /* Return code */
Fts5Config *pRet; /* New object to return */
int i;
sqlite3_int64 nByte;
int bUnindexed = 0; /* True if there are one or more UNINDEXED */
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
if( pRet==0 ) return SQLITE_NOMEM;
memset(pRet, 0, sizeof(Fts5Config));
pRet->pGlobal = pGlobal;
pRet->db = db;
pRet->iCookie = -1;
|
| ︙ | ︙ | |||
237632 237633 237634 237635 237636 237637 237638 |
if( bOption ){
rc = fts5ConfigParseSpecial(pRet,
ALWAYS(zOne)?zOne:"",
zTwo?zTwo:"",
pzErr
);
}else{
| | | 238063 238064 238065 238066 238067 238068 238069 238070 238071 238072 238073 238074 238075 238076 238077 |
if( bOption ){
rc = fts5ConfigParseSpecial(pRet,
ALWAYS(zOne)?zOne:"",
zTwo?zTwo:"",
pzErr
);
}else{
rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr, &bUnindexed);
zOne = 0;
}
}
}
sqlite3_free(zOne);
sqlite3_free(zTwo);
|
| ︙ | ︙ | |||
237663 237664 237665 237666 237667 237668 237669 237670 237671 237672 237673 237674 237675 237676 237677 237678 237679 237680 237681 237682 237683 237684 |
*/
if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
*pzErr = sqlite3_mprintf(
"contentless_delete=1 is incompatible with columnsize=0"
);
rc = SQLITE_ERROR;
}
/* If no zContent option was specified, fill in the default values. */
if( rc==SQLITE_OK && pRet->zContent==0 ){
const char *zTail = 0;
assert( pRet->eContent==FTS5_CONTENT_NORMAL
|| pRet->eContent==FTS5_CONTENT_NONE
);
if( pRet->eContent==FTS5_CONTENT_NORMAL ){
zTail = "content";
}else if( pRet->bColumnsize ){
zTail = "docsize";
}
if( zTail ){
pRet->zContent = sqlite3Fts5Mprintf(
| > > > > > > > > > > > > > > > > | 238094 238095 238096 238097 238098 238099 238100 238101 238102 238103 238104 238105 238106 238107 238108 238109 238110 238111 238112 238113 238114 238115 238116 238117 238118 238119 238120 238121 238122 238123 238124 238125 238126 238127 238128 238129 238130 238131 |
*/
if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
*pzErr = sqlite3_mprintf(
"contentless_delete=1 is incompatible with columnsize=0"
);
rc = SQLITE_ERROR;
}
/* We only allow contentless_unindexed=1 if the table is actually a
** contentless one.
*/
if( rc==SQLITE_OK
&& pRet->bContentlessUnindexed
&& pRet->eContent!=FTS5_CONTENT_NONE
){
*pzErr = sqlite3_mprintf(
"contentless_unindexed=1 requires a contentless table"
);
rc = SQLITE_ERROR;
}
/* If no zContent option was specified, fill in the default values. */
if( rc==SQLITE_OK && pRet->zContent==0 ){
const char *zTail = 0;
assert( pRet->eContent==FTS5_CONTENT_NORMAL
|| pRet->eContent==FTS5_CONTENT_NONE
);
if( pRet->eContent==FTS5_CONTENT_NORMAL ){
zTail = "content";
}else if( bUnindexed && pRet->bContentlessUnindexed ){
pRet->eContent = FTS5_CONTENT_UNINDEXED;
zTail = "content";
}else if( pRet->bColumnsize ){
zTail = "docsize";
}
if( zTail ){
pRet->zContent = sqlite3Fts5Mprintf(
|
| ︙ | ︙ | |||
238008 238009 238010 238011 238012 238013 238014 238015 238016 238017 238018 238019 238020 238021 |
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bSecureDelete = (bVal ? 1 : 0);
}
}else{
*pbBadkey = 1;
}
return rc;
}
/*
| > > > > > > > > > > > > > | 238455 238456 238457 238458 238459 238460 238461 238462 238463 238464 238465 238466 238467 238468 238469 238470 238471 238472 238473 238474 238475 238476 238477 238478 238479 238480 238481 |
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bSecureDelete = (bVal ? 1 : 0);
}
}
else if( 0==sqlite3_stricmp(zKey, "insttoken") ){
int bVal = -1;
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bPrefixInsttoken = (bVal ? 1 : 0);
}
}else{
*pbBadkey = 1;
}
return rc;
}
/*
|
| ︙ | ︙ | |||
241143 241144 241145 241146 241147 241148 241149 |
for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
&& memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
| | | 241603 241604 241605 241606 241607 241608 241609 241610 241611 241612 241613 241614 241615 241616 241617 |
for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
&& memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){
int iCol = p->iOff>>32;
int iTokOff = p->iOff & 0x7FFFFFFF;
rc = sqlite3Fts5IndexIterWriteTokendata(
pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
);
}
if( rc ) return rc;
|
| ︙ | ︙ | |||
241336 241337 241338 241339 241340 241341 241342 |
return SQLITE_RANGE;
}
pPhrase = pExpr->apExprPhrase[iPhrase];
if( iToken<0 || iToken>=pPhrase->nTerm ){
return SQLITE_RANGE;
}
pTerm = &pPhrase->aTerm[iToken];
| < | | > | | | | | < | 241796 241797 241798 241799 241800 241801 241802 241803 241804 241805 241806 241807 241808 241809 241810 241811 241812 241813 241814 241815 241816 241817 |
return SQLITE_RANGE;
}
pPhrase = pExpr->apExprPhrase[iPhrase];
if( iToken<0 || iToken>=pPhrase->nTerm ){
return SQLITE_RANGE;
}
pTerm = &pPhrase->aTerm[iToken];
if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){
rc = sqlite3Fts5IterToken(
pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm,
iRowid, iCol, iOff+iToken, ppOut, pnOut
);
}else{
*ppOut = pTerm->pTerm;
*pnOut = pTerm->nFullTerm;
}
return rc;
}
/*
** Clear the token mappings for all Fts5IndexIter objects mannaged by
** the expression passed as the only argument.
|
| ︙ | ︙ | |||
246845 246846 246847 246848 246849 246850 246851 246852 246853 246854 246855 246856 246857 246858 |
if( nEntry>0 ){
int nPercent = (nTomb * 100) / nEntry;
if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){
iRet = ii;
nBest = nPercent;
}
}
}
}
return iRet;
}
/*
** Do up to nPg pages of automerge work on the index.
| > > > > > | 247304 247305 247306 247307 247308 247309 247310 247311 247312 247313 247314 247315 247316 247317 247318 247319 247320 247321 247322 |
if( nEntry>0 ){
int nPercent = (nTomb * 100) / nEntry;
if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){
iRet = ii;
nBest = nPercent;
}
}
/* If pLvl is already the input level to an ongoing merge, look no
** further for a merge candidate. The caller should be allowed to
** continue merging from pLvl first. */
if( pLvl->nMerge ) break;
}
}
return iRet;
}
/*
** Do up to nPg pages of automerge work on the index.
|
| ︙ | ︙ | |||
248154 248155 248156 248157 248158 248159 248160 248161 248162 248163 248164 248165 248166 248167 248168 248169 248170 |
fts5BufferFree(p1);
fts5BufferFree(&tmp);
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
*p1 = out;
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
int iIdx, /* Index to scan for data */
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > > | | < | | | | | | | | | < < < < < < < < < < | < < < < < < < < < < | | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | < | | | | > > > > | | > > > | | 248618 248619 248620 248621 248622 248623 248624 248625 248626 248627 248628 248629 248630 248631 248632 248633 248634 248635 248636 248637 248638 248639 248640 248641 248642 248643 248644 248645 248646 248647 248648 248649 248650 248651 248652 248653 248654 248655 248656 248657 248658 248659 248660 248661 248662 248663 248664 248665 248666 248667 248668 248669 248670 248671 248672 248673 248674 248675 248676 248677 248678 248679 248680 248681 248682 248683 248684 248685 248686 248687 248688 248689 248690 248691 248692 248693 248694 248695 248696 248697 248698 248699 248700 248701 248702 248703 248704 248705 248706 248707 248708 248709 248710 248711 248712 248713 248714 248715 248716 248717 248718 248719 248720 248721 248722 248723 248724 248725 248726 248727 248728 248729 248730 248731 248732 248733 248734 248735 248736 248737 248738 248739 248740 248741 248742 248743 248744 248745 248746 248747 248748 248749 248750 248751 248752 248753 248754 248755 248756 248757 248758 248759 248760 248761 248762 248763 248764 248765 248766 248767 248768 248769 248770 248771 248772 248773 248774 248775 248776 248777 248778 248779 248780 248781 248782 248783 248784 248785 248786 248787 248788 248789 248790 248791 248792 248793 248794 248795 248796 248797 248798 248799 248800 248801 248802 248803 248804 248805 248806 248807 248808 248809 248810 248811 248812 248813 248814 248815 248816 248817 248818 248819 248820 248821 248822 248823 248824 248825 248826 248827 248828 248829 248830 248831 248832 248833 248834 248835 248836 248837 248838 248839 248840 248841 248842 248843 248844 248845 248846 248847 248848 248849 248850 248851 248852 248853 248854 248855 248856 248857 248858 248859 248860 248861 248862 248863 248864 248865 248866 248867 248868 248869 248870 248871 248872 248873 248874 248875 248876 248877 248878 248879 248880 248881 248882 248883 248884 248885 248886 248887 248888 248889 248890 248891 248892 248893 248894 248895 248896 248897 248898 248899 248900 248901 248902 248903 248904 248905 248906 248907 248908 248909 248910 248911 248912 248913 248914 248915 248916 248917 248918 248919 248920 248921 248922 248923 248924 248925 248926 248927 248928 248929 248930 248931 248932 248933 248934 248935 248936 248937 248938 248939 248940 248941 248942 248943 248944 248945 248946 248947 248948 248949 248950 248951 248952 248953 248954 248955 248956 248957 248958 248959 248960 248961 248962 248963 248964 248965 248966 248967 248968 248969 248970 248971 248972 248973 248974 248975 248976 248977 248978 248979 248980 248981 248982 248983 248984 248985 248986 248987 248988 248989 248990 248991 248992 248993 248994 248995 248996 248997 248998 248999 249000 249001 249002 249003 249004 249005 249006 249007 249008 249009 249010 249011 249012 249013 249014 249015 249016 249017 249018 249019 249020 249021 249022 249023 249024 249025 249026 249027 249028 249029 249030 249031 249032 249033 249034 249035 249036 249037 249038 249039 249040 249041 249042 249043 249044 249045 249046 249047 249048 249049 249050 249051 249052 249053 249054 249055 249056 249057 249058 249059 249060 249061 249062 249063 249064 249065 249066 249067 249068 249069 249070 249071 249072 249073 249074 249075 249076 249077 249078 249079 249080 249081 249082 249083 249084 249085 249086 249087 249088 249089 249090 249091 249092 249093 249094 249095 249096 249097 |
fts5BufferFree(p1);
fts5BufferFree(&tmp);
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
*p1 = out;
}
/*
** Iterate through a range of entries in the FTS index, invoking the xVisit
** callback for each of them.
**
** Parameter pToken points to an nToken buffer containing an FTS index term
** (i.e. a document term with the preceding 1 byte index identifier -
** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits
** all entries for terms that have pToken/nToken as a prefix. If bPrefix
** is false, then only entries with pToken/nToken as the entire key are
** visited.
**
** If the current table is a tokendata=1 table, then if bPrefix is true then
** each index term is treated separately. However, if bPrefix is false, then
** all index terms corresponding to pToken/nToken are collapsed into a single
** term before the callback is invoked.
**
** The callback invoked for each entry visited is specified by paramter xVisit.
** Each time it is invoked, it is passed a pointer to the Fts5Index object,
** a copy of the 7th paramter to this function (pCtx) and a pointer to the
** iterator that indicates the current entry. If the current entry is the
** first with a new term (i.e. different from that of the previous entry,
** including the very first term), then the final two parameters are passed
** a pointer to the term and its size in bytes, respectively. If the current
** entry is not the first associated with its term, these two parameters
** are passed 0.
**
** If parameter pColset is not NULL, then it is used to filter entries before
** the callback is invoked.
*/
static int fts5VisitEntries(
Fts5Index *p, /* Fts5 index object */
Fts5Colset *pColset, /* Columns filter to apply, or NULL */
u8 *pToken, /* Buffer containing token */
int nToken, /* Size of buffer pToken in bytes */
int bPrefix, /* True for a prefix scan */
void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int),
void *pCtx /* Passed as second argument to xVisit() */
){
const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0)
| FTS5INDEX_QUERY_SKIPEMPTY
| FTS5INDEX_QUERY_NOOUTPUT;
Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
int bNewTerm = 1;
Fts5Structure *pStruct = fts5StructureRead(p);
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
){
Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
int nNew = 0;
const u8 *pNew = 0;
p1->xSetOutputs(p1, pSeg);
if( p->rc ) break;
if( bNewTerm ){
nNew = pSeg->term.n;
pNew = pSeg->term.p;
if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break;
}
xVisit(p, pCtx, p1, pNew, nNew);
}
fts5MultiIterFree(p1);
fts5StructureRelease(pStruct);
return p->rc;
}
/*
** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
** array of these for each row it visits (so all iRowid fields are the same).
** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an
** array of these for the entire query (in which case iRowid fields may take
** a variety of values).
**
** Each instance in the array indicates the iterator (and therefore term)
** associated with position iPos of rowid iRowid. This is used by the
** xInstToken() API.
**
** iRowid:
** Rowid for the current entry.
**
** iPos:
** Position of current entry within row. In the usual ((iCol<<32)+iOff)
** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()).
**
** iIter:
** If the Fts5TokenDataIter iterator that the entry is part of is
** actually an iterator (i.e. with nIter>0, not just a container for
** Fts5TokenDataMap structures), then this variable is an index into
** the apIter[] array. The corresponding term is that which the iterator
** at apIter[iIter] currently points to.
**
** Or, if the Fts5TokenDataIter iterator is just a container object
** (nIter==0), then iIter is an index into the term.p[] buffer where
** the term is stored.
**
** nByte:
** In the case where iIter is an index into term.p[], this variable
** is the size of the term in bytes. If iIter is an index into apIter[],
** this variable is unused.
*/
struct Fts5TokenDataMap {
i64 iRowid; /* Row this token is located in */
i64 iPos; /* Position of token */
int iIter; /* Iterator token was read from */
int nByte; /* Length of token in bytes (or 0) */
};
/*
** An object used to supplement Fts5Iter for tokendata=1 iterators.
**
** This object serves two purposes. The first is as a container for an array
** of Fts5TokenDataMap structures, which are used to find the token required
** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and
** aMap[] variables.
*/
struct Fts5TokenDataIter {
int nMapAlloc; /* Allocated size of aMap[] in entries */
int nMap; /* Number of valid entries in aMap[] */
Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
/* The following are used for prefix-queries only. */
Fts5Buffer terms;
/* The following are used for other full-token tokendata queries only. */
int nIter;
int nIterAlloc;
Fts5PoslistReader *aPoslistReader;
int *aPoslistToIter;
Fts5Iter *apIter[1];
};
/*
** The two input arrays - a1[] and a2[] - are in sorted order. This function
** merges the two arrays together and writes the result to output array
** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
**
** Duplicate entries are copied into the output. So the size of the output
** array is always (n1+n2) entries.
*/
static void fts5TokendataMerge(
Fts5TokenDataMap *a1, int n1, /* Input array 1 */
Fts5TokenDataMap *a2, int n2, /* Input array 2 */
Fts5TokenDataMap *aOut /* Output array */
){
int i1 = 0;
int i2 = 0;
assert( n1>=0 && n2>=0 );
while( i1<n1 || i2<n2 ){
Fts5TokenDataMap *pOut = &aOut[i1+i2];
if( i2>=n2 || (i1<n1 && (
a1[i1].iRowid<a2[i2].iRowid
|| (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos)
))){
memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap));
i1++;
}else{
memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap));
i2++;
}
}
}
/*
** Append a mapping to the token-map belonging to object pT.
*/
static void fts5TokendataIterAppendMap(
Fts5Index *p,
Fts5TokenDataIter *pT,
int iIter,
int nByte,
i64 iRowid,
i64 iPos
){
if( p->rc==SQLITE_OK ){
if( pT->nMap==pT->nMapAlloc ){
int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
int nAlloc = nNew * sizeof(Fts5TokenDataMap);
Fts5TokenDataMap *aNew;
aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
if( aNew==0 ){
p->rc = SQLITE_NOMEM;
return;
}
pT->aMap = aNew;
pT->nMapAlloc = nNew;
}
pT->aMap[pT->nMap].iRowid = iRowid;
pT->aMap[pT->nMap].iPos = iPos;
pT->aMap[pT->nMap].iIter = iIter;
pT->aMap[pT->nMap].nByte = nByte;
pT->nMap++;
}
}
/*
** Sort the contents of the pT->aMap[] array.
**
** The sorting algorithm requries a malloc(). If this fails, an error code
** is left in Fts5Index.rc before returning.
*/
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
Fts5TokenDataMap *aTmp = 0;
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( aTmp ){
Fts5TokenDataMap *a1 = pT->aMap;
Fts5TokenDataMap *a2 = aTmp;
i64 nHalf;
for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){
int i1;
for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){
int n1 = MIN(nHalf, pT->nMap-i1);
int n2 = MIN(nHalf, pT->nMap-i1-n1);
fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]);
}
SWAPVAL(Fts5TokenDataMap*, a1, a2);
}
if( a1!=pT->aMap ){
memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap));
}
sqlite3_free(aTmp);
#ifdef SQLITE_DEBUG
{
int ii;
for(ii=1; ii<pT->nMap; ii++){
Fts5TokenDataMap *p1 = &pT->aMap[ii-1];
Fts5TokenDataMap *p2 = &pT->aMap[ii];
assert( p1->iRowid<p2->iRowid
|| (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos)
);
}
}
#endif
}
}
/*
** Delete an Fts5TokenDataIter structure and its contents.
*/
static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
if( pSet ){
int ii;
for(ii=0; ii<pSet->nIter; ii++){
fts5MultiIterFree(pSet->apIter[ii]);
}
fts5BufferFree(&pSet->terms);
sqlite3_free(pSet->aPoslistReader);
sqlite3_free(pSet->aMap);
sqlite3_free(pSet);
}
}
/*
** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata()
** to pass data to prefixIterSetupTokendataCb().
*/
typedef struct TokendataSetupCtx TokendataSetupCtx;
struct TokendataSetupCtx {
Fts5TokenDataIter *pT; /* Object being populated with mappings */
int iTermOff; /* Offset of current term in terms.p[] */
int nTermByte; /* Size of current term in bytes */
};
/*
** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This
** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each
** position in the current position-list. It doesn't matter that some of
** these may be out of order - they will be sorted later.
*/
static void prefixIterSetupTokendataCb(
Fts5Index *p,
void *pCtx,
Fts5Iter *p1,
const u8 *pNew,
int nNew
){
TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx;
int iPosOff = 0;
i64 iPos = 0;
if( pNew ){
pSetup->nTermByte = nNew-1;
pSetup->iTermOff = pSetup->pT->terms.n;
fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1);
}
while( 0==sqlite3Fts5PoslistNext64(
p1->base.pData, p1->base.nData, &iPosOff, &iPos
) ){
fts5TokendataIterAppendMap(p,
pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos
);
}
}
/*
** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries().
*/
typedef struct PrefixSetupCtx PrefixSetupCtx;
struct PrefixSetupCtx {
void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
i64 iLastRowid;
int nMerge;
Fts5Buffer *aBuf;
int nBuf;
Fts5Buffer doclist;
TokendataSetupCtx *pTokendata;
};
/*
** fts5VisitEntries() callback used by fts5SetupPrefixIter()
*/
static void prefixIterSetupCb(
Fts5Index *p,
void *pCtx,
Fts5Iter *p1,
const u8 *pNew,
int nNew
){
PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx;
const int nMerge = pSetup->nMerge;
if( p1->base.nData>0 ){
if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){
int i;
for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){
int i1 = i*nMerge;
int iStore;
assert( i1+nMerge<=pSetup->nBuf );
for(iStore=i1; iStore<i1+nMerge; iStore++){
if( pSetup->aBuf[iStore].n==0 ){
fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]);
fts5BufferZero(&pSetup->doclist);
break;
}
}
if( iStore==i1+nMerge ){
pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]);
for(iStore=i1; iStore<i1+nMerge; iStore++){
fts5BufferZero(&pSetup->aBuf[iStore]);
}
}
}
pSetup->iLastRowid = 0;
}
pSetup->xAppend(
p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist
);
pSetup->iLastRowid = p1->base.iRowid;
}
if( pSetup->pTokendata ){
prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew);
}
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
int iIdx, /* Index to scan for data */
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
PrefixSetupCtx s;
TokendataSetupCtx s2;
memset(&s, 0, sizeof(s));
memset(&s2, 0, sizeof(s2));
s.nMerge = 1;
s.iLastRowid = 0;
s.nBuf = 32;
if( iIdx==0
&& p->pConfig->eDetail==FTS5_DETAIL_FULL
&& p->pConfig->bPrefixInsttoken
){
s.pTokendata = &s2;
s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
}
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
s.xMerge = fts5MergeRowidLists;
s.xAppend = fts5AppendRowid;
}else{
s.nMerge = FTS5_MERGE_NLIST-1;
s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
s.xMerge = fts5MergePrefixLists;
s.xAppend = fts5AppendPoslist;
}
s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf);
pStruct = fts5StructureRead(p);
assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) );
if( p->rc==SQLITE_OK ){
void *pCtx = (void*)&s;
int i;
Fts5Data *pData;
/* If iIdx is non-zero, then it is the number of a prefix-index for
** prefixes 1 character longer than the prefix being queried for. That
** index contains all the doclists required, except for the one
** corresponding to the prefix itself. That one is extracted from the
** main term index here. */
if( iIdx!=0 ){
pToken[0] = FTS5_MAIN_PREFIX;
fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx);
}
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx);
assert( (s.nBuf%s.nMerge)==0 );
for(i=0; i<s.nBuf; i+=s.nMerge){
int iFree;
if( p->rc==SQLITE_OK ){
s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]);
}
for(iFree=i; iFree<i+s.nMerge; iFree++){
fts5BufferFree(&s.aBuf[iFree]);
}
}
pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING);
if( pData ){
pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = s.doclist.n;
if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n);
fts5MultiIterNew2(p, pData, bDesc, ppIter);
}
if( p->rc==SQLITE_OK && s.pTokendata ){
fts5TokendataIterSortMap(p, s2.pT);
(*ppIter)->pTokenDataIter = s2.pT;
s2.pT = 0;
}
}
fts5TokendataIterDelete(s2.pT);
fts5BufferFree(&s.doclist);
fts5StructureRelease(pStruct);
sqlite3_free(s.aBuf);
}
/*
** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
** to the document with rowid iRowid.
*/
|
| ︙ | ︙ | |||
248544 248545 248546 248547 248548 248549 248550 |
** Ensure the segment-iterator passed as the only argument points to EOF.
*/
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
fts5DataRelease(pSeg->pLeaf);
pSeg->pLeaf = 0;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 249337 249338 249339 249340 249341 249342 249343 249344 249345 249346 249347 249348 249349 249350 |
** Ensure the segment-iterator passed as the only argument points to EOF.
*/
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
fts5DataRelease(pSeg->pLeaf);
pSeg->pLeaf = 0;
}
/*
** This function appends iterator pAppend to Fts5TokenDataIter pIn and
** returns the result.
*/
static Fts5TokenDataIter *fts5AppendTokendataIter(
Fts5Index *p, /* Index object (for error code) */
Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */
|
| ︙ | ︙ | |||
248612 248613 248614 248615 248616 248617 248618 |
pRet->apIter[pRet->nIter++] = pAppend;
}
assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
return pRet;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 249373 249374 249375 249376 249377 249378 249379 249380 249381 249382 249383 249384 249385 249386 |
pRet->apIter[pRet->nIter++] = pAppend;
}
assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
return pRet;
}
/*
** The iterator passed as the only argument must be a tokendata=1 iterator
** (pIter->pTokenDataIter!=0). This function sets the iterator output
** variables (pIter->base.*) according to the contents of the current
** row.
*/
static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
|
| ︙ | ︙ | |||
248700 248701 248702 248703 248704 248705 248706 |
pIter->base.bEof = 1;
}else{
int eDetail = pIter->pIndex->pConfig->eDetail;
pIter->base.bEof = 0;
pIter->base.iRowid = iRowid;
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
| | | 249413 249414 249415 249416 249417 249418 249419 249420 249421 249422 249423 249424 249425 249426 249427 |
pIter->base.bEof = 1;
}else{
int eDetail = pIter->pIndex->pConfig->eDetail;
pIter->base.bEof = 0;
pIter->base.iRowid = iRowid;
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1);
}else
if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
int nReader = 0;
int nByte = 0;
i64 iPrev = 0;
/* Allocate array of iterators if they are not already allocated. */
|
| ︙ | ︙ | |||
248953 248954 248955 248956 248957 248958 248959 248960 248961 248962 248963 248964 248965 248966 248967 248968 248969 248970 248971 248972 248973 |
}
}
if( p->rc==SQLITE_OK ){
pRet = fts5MultiIterAlloc(p, 0);
}
if( pRet ){
pRet->pTokenDataIter = pSet;
if( pSet ){
fts5IterSetOutputsTokendata(pRet);
}else{
pRet->base.bEof = 1;
}
}else{
fts5TokendataIterDelete(pSet);
}
fts5StructureRelease(pStruct);
fts5BufferFree(&bSeek);
return pRet;
}
| > < | 249666 249667 249668 249669 249670 249671 249672 249673 249674 249675 249676 249677 249678 249679 249680 249681 249682 249683 249684 249685 249686 249687 249688 249689 249690 249691 249692 249693 249694 |
}
}
if( p->rc==SQLITE_OK ){
pRet = fts5MultiIterAlloc(p, 0);
}
if( pRet ){
pRet->nSeg = 0;
pRet->pTokenDataIter = pSet;
if( pSet ){
fts5IterSetOutputsTokendata(pRet);
}else{
pRet->base.bEof = 1;
}
}else{
fts5TokendataIterDelete(pSet);
}
fts5StructureRelease(pStruct);
fts5BufferFree(&bSeek);
return pRet;
}
/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
static int sqlite3Fts5IndexQuery(
Fts5Index *p, /* FTS index to query */
|
| ︙ | ︙ | |||
248993 248994 248995 248996 248997 248998 248999 249000 249001 249002 249003 249004 249005 249006 |
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
int bTokendata = pConfig->bTokendata;
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
bTokendata = 0;
}
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
| > > > > > | 249706 249707 249708 249709 249710 249711 249712 249713 249714 249715 249716 249717 249718 249719 249720 249721 249722 249723 249724 |
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
int bTokendata = pConfig->bTokendata;
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
/* The NOTOKENDATA flag is set when each token in a tokendata=1 table
** should be treated individually, instead of merging all those with
** a common prefix into a single entry. This is used, for example, by
** queries performed as part of an integrity-check, or by the fts5vocab
** module. */
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
bTokendata = 0;
}
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
|
| ︙ | ︙ | |||
249023 249024 249025 249026 249027 249028 249029 |
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
if( bTokendata && iIdx==0 ){
| | | | 249741 249742 249743 249744 249745 249746 249747 249748 249749 249750 249751 249752 249753 249754 249755 249756 249757 249758 249759 249760 249761 249762 249763 249764 249765 249766 249767 249768 |
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
if( bTokendata && iIdx==0 ){
buf.p[0] = FTS5_MAIN_PREFIX;
pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
}else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
if( pStruct ){
fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
pColset, buf.p, nToken+1, -1, 0, &pRet
);
fts5StructureRelease(pStruct);
}
}else{
/* Scan multiple terms in the main index for a prefix query. */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
if( pRet==0 ){
assert( p->rc!=SQLITE_OK );
}else{
assert( pRet->pColset==0 );
fts5IterSetOutputCb(&p->rc, pRet);
|
| ︙ | ︙ | |||
249072 249073 249074 249075 249076 249077 249078 |
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
| > | | 249790 249791 249792 249793 249794 249795 249796 249797 249798 249799 249800 249801 249802 249803 249804 249805 |
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
if( pIter->nSeg==0 ){
assert( pIter->pTokenDataIter );
fts5TokendataIterNext(pIter, 0, 0);
}else{
fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
}
return fts5IndexReturn(pIter->pIndex);
}
|
| ︙ | ︙ | |||
249109 249110 249111 249112 249113 249114 249115 |
/*
** Move to the next matching rowid that occurs at or after iMatch. The
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
| > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | > > > > > > > > > > | 249828 249829 249830 249831 249832 249833 249834 249835 249836 249837 249838 249839 249840 249841 249842 249843 249844 249845 249846 249847 249848 249849 249850 249851 249852 249853 249854 249855 249856 249857 249858 249859 249860 249861 249862 249863 249864 249865 249866 249867 249868 249869 249870 249871 249872 249873 249874 249875 249876 249877 249878 249879 249880 249881 249882 249883 249884 249885 249886 249887 249888 249889 249890 249891 249892 249893 249894 249895 249896 249897 249898 249899 249900 249901 249902 249903 249904 249905 249906 249907 249908 249909 249910 249911 249912 249913 249914 249915 249916 249917 249918 249919 249920 249921 249922 249923 249924 249925 249926 249927 249928 249929 249930 249931 249932 249933 249934 249935 249936 249937 |
/*
** Move to the next matching rowid that occurs at or after iMatch. The
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
if( pIter->nSeg==0 ){
assert( pIter->pTokenDataIter );
fts5TokendataIterNext(pIter, 1, iMatch);
}else{
fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
}
return fts5IndexReturn(pIter->pIndex);
}
/*
** Return the current term.
*/
static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
int n;
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
assert_nc( z || n<=1 );
*pn = n-1;
return (z ? &z[1] : 0);
}
/*
** pIter is a prefix query. This function populates pIter->pTokenDataIter
** with an Fts5TokenDataIter object containing mappings for all rows
** matched by the query.
*/
static int fts5SetupPrefixIterTokendata(
Fts5Iter *pIter,
const char *pToken, /* Token prefix to search for */
int nToken /* Size of pToken in bytes */
){
Fts5Index *p = pIter->pIndex;
Fts5Buffer token = {0, 0, 0};
TokendataSetupCtx ctx;
memset(&ctx, 0, sizeof(ctx));
fts5BufferGrow(&p->rc, &token, nToken+1);
ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));
if( p->rc==SQLITE_OK ){
/* Fill in the token prefix to search for */
token.p[0] = FTS5_MAIN_PREFIX;
memcpy(&token.p[1], pToken, nToken);
token.n = nToken+1;
fts5VisitEntries(
p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx
);
fts5TokendataIterSortMap(p, ctx.pT);
}
if( p->rc==SQLITE_OK ){
pIter->pTokenDataIter = ctx.pT;
}else{
fts5TokendataIterDelete(ctx.pT);
}
fts5BufferFree(&token);
return fts5IndexReturn(p);
}
/*
** This is used by xInstToken() to access the token at offset iOff, column
** iCol of row iRowid. The token is returned via output variables *ppOut
** and *pnOut. The iterator passed as the first argument must be a tokendata=1
** iterator (pIter->pTokenDataIter!=0).
**
** pToken/nToken:
*/
static int sqlite3Fts5IterToken(
Fts5IndexIter *pIndexIter,
const char *pToken, int nToken,
i64 iRowid,
int iCol,
int iOff,
const char **ppOut, int *pnOut
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
i64 iPos = (((i64)iCol)<<32) + iOff;
Fts5TokenDataMap *aMap = 0;
int i1 = 0;
int i2 = 0;
int iTest = 0;
assert( pT || (pToken && pIter->nSeg>0) );
if( pT==0 ){
int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken);
if( rc!=SQLITE_OK ) return rc;
pT = pIter->pTokenDataIter;
}
i2 = pT->nMap;
aMap = pT->aMap;
while( i2>i1 ){
iTest = (i1 + i2) / 2;
if( aMap[iTest].iRowid<iRowid ){
i1 = iTest+1;
}else if( aMap[iTest].iRowid>iRowid ){
|
| ︙ | ︙ | |||
249172 249173 249174 249175 249176 249177 249178 |
}else{
break;
}
}
}
if( i2>i1 ){
| > | | | > > > > > | > > | | > > > > > | > > > > > > | | | | | | > | 249947 249948 249949 249950 249951 249952 249953 249954 249955 249956 249957 249958 249959 249960 249961 249962 249963 249964 249965 249966 249967 249968 249969 249970 249971 249972 249973 249974 249975 249976 249977 249978 249979 249980 249981 249982 249983 249984 249985 249986 249987 249988 249989 249990 249991 249992 249993 249994 249995 249996 249997 249998 249999 250000 250001 250002 250003 250004 250005 250006 250007 250008 250009 250010 250011 250012 250013 250014 250015 250016 250017 250018 250019 250020 250021 250022 250023 250024 250025 |
}else{
break;
}
}
}
if( i2>i1 ){
if( pIter->nSeg==0 ){
Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
*ppOut = (const char*)pMap->aSeg[0].term.p+1;
*pnOut = pMap->aSeg[0].term.n-1;
}else{
Fts5TokenDataMap *p = &aMap[iTest];
*ppOut = (const char*)&pT->terms.p[p->iIter];
*pnOut = aMap[iTest].nByte;
}
}
return SQLITE_OK;
}
/*
** Clear any existing entries from the token-map associated with the
** iterator passed as the only argument.
*/
static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
if( pIter && pIter->pTokenDataIter
&& (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL)
){
pIter->pTokenDataIter->nMap = 0;
}
}
/*
** Set a token-mapping for the iterator passed as the first argument. This
** is used in detail=column or detail=none mode when a token is requested
** using the xInstToken() API. In this case the caller tokenizers the
** current row and configures the token-mapping via multiple calls to this
** function.
*/
static int sqlite3Fts5IndexIterWriteTokendata(
Fts5IndexIter *pIndexIter,
const char *pToken, int nToken,
i64 iRowid, int iCol, int iOff
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
Fts5Index *p = pIter->pIndex;
i64 iPos = (((i64)iCol)<<32) + iOff;
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
assert( pIter->pTokenDataIter || pIter->nSeg>0 );
if( pIter->nSeg>0 ){
/* This is a prefix term iterator. */
if( pT==0 ){
pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
pIter->pTokenDataIter = pT;
}
if( pT ){
fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
}
}else{
int ii;
for(ii=0; ii<pT->nIter; ii++){
Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
}
if( ii<pT->nIter ){
fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos);
}
}
return fts5IndexReturn(p);
}
/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
|
| ︙ | ︙ | |||
250769 250770 250771 250772 250773 250774 250775 | *ppVtab = (sqlite3_vtab*)pNew; return rc; } /* ** We must have a single struct=? constraint that will be passed through | | | 251564 251565 251566 251567 251568 251569 251570 251571 251572 251573 251574 251575 251576 251577 251578 |
*ppVtab = (sqlite3_vtab*)pNew;
return rc;
}
/*
** We must have a single struct=? constraint that will be passed through
** into the xFilter method. If there is no valid struct=? constraint,
** then return an SQLITE_CONSTRAINT error.
*/
static int fts5structBestIndexMethod(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
|
| ︙ | ︙ | |||
251111 251112 251113 251114 251115 251116 251117 251118 251119 251120 251121 251122 251123 251124 251125 251126 |
fts5_api api; /* User visible part of object (see fts5.h) */
sqlite3 *db; /* Associated database connection */
i64 iNextId; /* Used to allocate unique cursor ids */
Fts5Auxiliary *pAux; /* First in list of all aux. functions */
Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */
Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */
Fts5Cursor *pCsr; /* First in list of all open cursors */
};
/*
** Each auxiliary function registered with the FTS5 module is represented
** by an object of the following type. All such objects are stored as part
** of the Fts5Global.pAux list.
*/
struct Fts5Auxiliary {
Fts5Global *pGlobal; /* Global context for this function */
| > > > > > > > > > > | 251906 251907 251908 251909 251910 251911 251912 251913 251914 251915 251916 251917 251918 251919 251920 251921 251922 251923 251924 251925 251926 251927 251928 251929 251930 251931 |
fts5_api api; /* User visible part of object (see fts5.h) */
sqlite3 *db; /* Associated database connection */
i64 iNextId; /* Used to allocate unique cursor ids */
Fts5Auxiliary *pAux; /* First in list of all aux. functions */
Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */
Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */
Fts5Cursor *pCsr; /* First in list of all open cursors */
u32 aLocaleHdr[4];
};
/*
** Size of header on fts5_locale() values. And macro to access a buffer
** containing a copy of the header from an Fts5Config pointer.
*/
#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
#define FTS5_INSTTOKEN_SUBTYPE 73
/*
** Each auxiliary function registered with the FTS5 module is represented
** by an object of the following type. All such objects are stored as part
** of the Fts5Global.pAux list.
*/
struct Fts5Auxiliary {
Fts5Global *pGlobal; /* Global context for this function */
|
| ︙ | ︙ | |||
251275 251276 251277 251278 251279 251280 251281 | #define FTS5CSR_FREE_ZRANK 0x10 #define FTS5CSR_REQUIRE_RESEEK 0x20 #define FTS5CSR_REQUIRE_POSLIST 0x40 #define BitFlagAllTest(x,y) (((x) & (y))==(y)) #define BitFlagTest(x,y) (((x) & (y))!=0) | < < < < < < | 252080 252081 252082 252083 252084 252085 252086 252087 252088 252089 252090 252091 252092 252093 | #define FTS5CSR_FREE_ZRANK 0x10 #define FTS5CSR_REQUIRE_RESEEK 0x20 #define FTS5CSR_REQUIRE_POSLIST 0x40 #define BitFlagAllTest(x,y) (((x) & (y))==(y)) #define BitFlagTest(x,y) (((x) & (y))!=0) /* ** Macros to Set(), Clear() and Test() cursor flags. */ #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag)) #define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag)) |
| ︙ | ︙ | |||
251357 251358 251359 251360 251361 251362 251363 | } } #else # define fts5CheckTransactionState(x,y,z) #endif /* | | > > | > > | > > | 252156 252157 252158 252159 252160 252161 252162 252163 252164 252165 252166 252167 252168 252169 252170 252171 252172 252173 252174 252175 252176 252177 252178 252179 |
}
}
#else
# define fts5CheckTransactionState(x,y,z)
#endif
/*
** Return true if pTab is a contentless table. If parameter bIncludeUnindexed
** is true, this includes contentless tables that store UNINDEXED columns
** only.
*/
static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){
int eContent = pTab->p.pConfig->eContent;
return (
eContent==FTS5_CONTENT_NONE
|| (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED)
);
}
/*
** Delete a virtual table handle allocated by fts5InitVtab().
*/
static void fts5FreeVtab(Fts5FullTable *pTab){
if( pTab ){
|
| ︙ | ︙ | |||
251651 251652 251653 251654 251655 251656 251657 251658 251659 251660 251661 251662 251663 251664 |
if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Return SQLITE_CONSTRAINT. */
return SQLITE_CONSTRAINT;
}else{
if( iCol==nCol+1 ){
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
| > | 252456 252457 252458 252459 252460 252461 252462 252463 252464 252465 252466 252467 252468 252469 252470 |
if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Return SQLITE_CONSTRAINT. */
idxStr[iIdxStr] = 0;
return SQLITE_CONSTRAINT;
}else{
if( iCol==nCol+1 ){
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
|
| ︙ | ︙ | |||
252284 252285 252286 252287 252288 252289 252290 | /* ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain ** valid until after the final call to sqlite3Fts5Tokenize() that will use ** the locale. */ | | | < | < < < | < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < | < < < < < < < < | | | | < < | | < | < < < < | > | < < < | | | < < | < < | > > | > > | < < > > > > > > > | | | > | > > > > | | > > | > > > | < < < > > | < | | | | | < < < > > | > > | < < | | | < < | > | | < > | 253090 253091 253092 253093 253094 253095 253096 253097 253098 253099 253100 253101 253102 253103 253104 253105 253106 253107 253108 253109 253110 253111 253112 253113 253114 253115 253116 253117 253118 253119 253120 253121 253122 253123 253124 253125 253126 253127 253128 253129 253130 253131 253132 253133 253134 253135 253136 253137 253138 253139 253140 253141 253142 253143 253144 253145 253146 253147 253148 253149 253150 253151 253152 253153 253154 253155 253156 253157 253158 253159 253160 253161 253162 253163 253164 253165 253166 253167 253168 253169 253170 253171 253172 253173 253174 253175 253176 253177 253178 253179 253180 253181 253182 253183 253184 253185 253186 253187 253188 253189 253190 253191 253192 253193 253194 253195 253196 253197 253198 253199 253200 253201 253202 253203 253204 253205 253206 253207 253208 253209 253210 253211 253212 253213 253214 253215 253216 253217 253218 253219 |
/*
** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale
** specified by pLocale/nLocale. The buffer indicated by pLocale must remain
** valid until after the final call to sqlite3Fts5Tokenize() that will use
** the locale.
*/
static void sqlite3Fts5SetLocale(
Fts5Config *pConfig,
const char *zLocale,
int nLocale
){
Fts5TokenizerConfig *pT = &pConfig->t;
pT->pLocale = zLocale;
pT->nLocale = nLocale;
}
/*
** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale().
*/
static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){
sqlite3Fts5SetLocale(pConfig, 0, 0);
}
/*
** Return true if the value passed as the only argument is an
** fts5_locale() value.
*/
static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){
int ret = 0;
if( sqlite3_value_type(pVal)==SQLITE_BLOB ){
/* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case.
** If the blob was created using zeroblob(), then sqlite3_value_blob()
** may call malloc(). If this malloc() fails, then the values returned
** by both value_blob() and value_bytes() will be 0. If value_bytes() were
** called first, then the NULL pointer returned by value_blob() might
** be dereferenced. */
const u8 *pBlob = sqlite3_value_blob(pVal);
int nBlob = sqlite3_value_bytes(pVal);
if( nBlob>FTS5_LOCALE_HDR_SIZE
&& 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE)
){
ret = 1;
}
}
return ret;
}
/*
** Value pVal is guaranteed to be an fts5_locale() value, according to
** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale
** from the value and returns them separately.
**
** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set
** to point to buffers containing the text and locale, as utf-8,
** respectively. In this case output parameters (*pnText) and (*pnLoc) are
** set to the sizes in bytes of these two buffers.
**
** Or, if an error occurs, then an SQLite error code is returned. The final
** value of the four output parameters is undefined in this case.
*/
static int sqlite3Fts5DecodeLocaleValue(
sqlite3_value *pVal,
const char **ppText,
int *pnText,
const char **ppLoc,
int *pnLoc
){
const char *p = sqlite3_value_blob(pVal);
int n = sqlite3_value_bytes(pVal);
int nLoc = 0;
assert( sqlite3_value_type(pVal)==SQLITE_BLOB );
assert( n>FTS5_LOCALE_HDR_SIZE );
for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){
if( nLoc==(n-1) ){
return SQLITE_MISMATCH;
}
}
*ppLoc = &p[FTS5_LOCALE_HDR_SIZE];
*pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE;
*ppText = &p[nLoc+1];
*pnText = n - nLoc - 1;
return SQLITE_OK;
}
/*
** Argument pVal is the text of a full-text search expression. It may or
** may not have been wrapped by fts5_locale(). This function extracts
** the text of the expression, and sets output variable (*pzText) to
** point to a nul-terminated buffer containing the expression.
**
** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called
** to set the tokenizer to use the specified locale.
**
** If output variable (*pbFreeAndReset) is set to true, then the caller
** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer
** locale, and (b) call sqlite3_free() to free (*pzText).
*/
static int fts5ExtractExprText(
Fts5Config *pConfig, /* Fts5 configuration */
sqlite3_value *pVal, /* Value to extract expression text from */
char **pzText, /* OUT: nul-terminated buffer of text */
int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */
){
int rc = SQLITE_OK;
if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
const char *pText = 0;
int nText = 0;
const char *pLoc = 0;
int nLoc = 0;
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
*pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText);
if( rc==SQLITE_OK ){
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
}
*pbFreeAndReset = 1;
}else{
*pzText = (char*)sqlite3_value_text(pVal);
*pbFreeAndReset = 0;
}
return rc;
}
/*
|
| ︙ | ︙ | |||
252491 252492 252493 252494 252495 252496 252497 252498 252499 252500 252501 252502 252503 252504 |
int bOrderByRank; /* True if ORDER BY rank */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
int i;
int iIdxStr = 0;
Fts5Expr *pExpr = 0;
assert( pConfig->bLock==0 );
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
| > | 253242 253243 253244 253245 253246 253247 253248 253249 253250 253251 253252 253253 253254 253255 253256 |
int bOrderByRank; /* True if ORDER BY rank */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
int bPrefixInsttoken = pConfig->bPrefixInsttoken;
int i;
int iIdxStr = 0;
Fts5Expr *pExpr = 0;
assert( pConfig->bLock==0 );
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
|
| ︙ | ︙ | |||
252526 252527 252528 252529 252530 252531 252532 252533 252534 252535 252536 252537 252538 252539 |
char *zText = 0;
int bFreeAndReset = 0;
int bInternal = 0;
rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
if( rc!=SQLITE_OK ) goto filter_out;
if( zText==0 ) zText = "";
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
| > > > | 253278 253279 253280 253281 253282 253283 253284 253285 253286 253287 253288 253289 253290 253291 253292 253293 253294 |
char *zText = 0;
int bFreeAndReset = 0;
int bInternal = 0;
rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
if( rc!=SQLITE_OK ) goto filter_out;
if( zText==0 ) zText = "";
if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){
pConfig->bPrefixInsttoken = 1;
}
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
|
| ︙ | ︙ | |||
252666 252667 252668 252669 252670 252671 252672 252673 252674 252675 252676 252677 252678 252679 |
rc = fts5NextMethod(pCursor);
}
}
filter_out:
sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
return rc;
}
/*
** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
| > | 253421 253422 253423 253424 253425 253426 253427 253428 253429 253430 253431 253432 253433 253434 253435 |
rc = fts5NextMethod(pCursor);
}
}
filter_out:
sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
pConfig->bPrefixInsttoken = bPrefixInsttoken;
return rc;
}
/*
** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
|
| ︙ | ︙ | |||
252806 252807 252808 252809 252810 252811 252812 |
);
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
| | | 253562 253563 253564 253565 253566 253567 253568 253569 253570 253571 253572 253573 253574 253575 253576 |
);
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( fts5IsContentless(pTab, 1) ){
fts5SetVtabError(pTab,
"'rebuild' may not be used with a contentless fts5 table"
);
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
|
| ︙ | ︙ | |||
252875 252876 252877 252878 252879 252880 252881 |
int *pRc,
Fts5FullTable *pTab,
sqlite3_value **apVal,
i64 *piRowid
){
int rc = *pRc;
if( rc==SQLITE_OK ){
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 253631 253632 253633 253634 253635 253636 253637 253638 253639 253640 253641 253642 253643 253644 253645 253646 253647 253648 253649 253650 253651 253652 253653 253654 253655 253656 253657 253658 253659 253660 253661 253662 253663 253664 253665 253666 253667 253668 253669 253670 253671 253672 253673 253674 253675 253676 253677 253678 253679 253680 253681 253682 253683 253684 253685 253686 253687 253688 253689 253690 253691 253692 253693 253694 253695 253696 253697 253698 253699 253700 253701 253702 253703 253704 253705 253706 253707 253708 253709 253710 253711 253712 |
int *pRc,
Fts5FullTable *pTab,
sqlite3_value **apVal,
i64 *piRowid
){
int rc = *pRc;
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid);
}
*pRc = rc;
}
/*
**
** This function is called when the user attempts an UPDATE on a contentless
** table. Parameter bRowidModified is true if the UPDATE statement modifies
** the rowid value. Parameter apVal[] contains the new values for each user
** defined column of the fts5 table. pConfig is the configuration object of the
** table being updated (guaranteed to be contentless). The contentless_delete=1
** and contentless_unindexed=1 options may or may not be set.
**
** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite
** error code if it cannot. In this case an error message is also loaded into
** pConfig. Output parameter (*pbContent) is set to true if the caller should
** update the %_content table only - not the FTS index or any other shadow
** table. This occurs when an UPDATE modifies only UNINDEXED columns of the
** table.
**
** An UPDATE may proceed if:
**
** * The only columns modified are UNINDEXED columns, or
**
** * The contentless_delete=1 option was specified and all of the indexed
** columns (not a subset) have been modified.
*/
static int fts5ContentlessUpdate(
Fts5Config *pConfig,
sqlite3_value **apVal,
int bRowidModified,
int *pbContent
){
int ii;
int bSeenIndex = 0; /* Have seen modified indexed column */
int bSeenIndexNC = 0; /* Have seen unmodified indexed column */
int rc = SQLITE_OK;
for(ii=0; ii<pConfig->nCol; ii++){
if( pConfig->abUnindexed[ii]==0 ){
if( sqlite3_value_nochange(apVal[ii]) ){
bSeenIndexNC++;
}else{
bSeenIndex++;
}
}
}
if( bSeenIndex==0 && bRowidModified==0 ){
*pbContent = 1;
}else{
if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){
rc = SQLITE_ERROR;
sqlite3Fts5ConfigErrmsg(pConfig,
(pConfig->bContentlessDelete ?
"%s a subset of columns on fts5 contentless-delete table: %s" :
"%s contentless fts5 table: %s")
, "cannot UPDATE", pConfig->zName
);
}
}
return rc;
}
/*
** This function is the implementation of the xUpdate callback used by
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
** inserted, updated or deleted.
**
** A delete specifies a single argument - the rowid of the row to remove.
|
| ︙ | ︙ | |||
252969 252970 252971 252972 252973 252974 252975 |
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
}
assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
assert( nArg!=1 || eType0==SQLITE_INTEGER );
| > > | < | < < | < | | < | | | < < < | | | > | | | | | | < < | < < | < > > > > > > > > > > > | | | | | | > > > > > > > > > | | | 253786 253787 253788 253789 253790 253791 253792 253793 253794 253795 253796 253797 253798 253799 253800 253801 253802 253803 253804 253805 253806 253807 253808 253809 253810 253811 253812 253813 253814 253815 253816 253817 253818 253819 253820 253821 253822 253823 253824 253825 253826 253827 253828 253829 253830 253831 253832 253833 253834 253835 253836 253837 253838 253839 253840 253841 253842 253843 253844 253845 253846 253847 253848 253849 253850 253851 253852 253853 253854 253855 253856 253857 253858 253859 253860 253861 253862 253863 253864 253865 253866 253867 253868 253869 253870 253871 253872 253873 253874 253875 253876 253877 253878 253879 253880 253881 253882 253883 253884 253885 253886 253887 253888 253889 253890 253891 253892 253893 253894 253895 |
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
}
assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* DELETE */
if( nArg==1 ){
/* It is only possible to DELETE from a contentless table if the
** contentless_delete=1 flag is set. */
if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){
fts5SetVtabError(pTab,
"cannot DELETE from contentless fts5 table: %s", pConfig->zName
);
rc = SQLITE_ERROR;
}else{
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0);
bUpdateOrDelete = 1;
}
}
/* INSERT or UPDATE */
else{
int eType1 = sqlite3_value_numeric_type(apVal[1]);
/* It is an error to write an fts5_locale() value to a table without
** the locale=1 option. */
if( pConfig->bLocale==0 ){
int ii;
for(ii=0; ii<pConfig->nCol; ii++){
sqlite3_value *pVal = apVal[ii+2];
if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
fts5SetVtabError(pTab, "fts5_locale() requires locale=1");
rc = SQLITE_MISMATCH;
goto update_out;
}
}
}
if( eType0!=SQLITE_INTEGER ){
/* An INSERT statement. If the conflict-mode is REPLACE, first remove
** the current entry (if any). */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0);
bUpdateOrDelete = 1;
}
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
/* UPDATE */
else{
Fts5Storage *pStorage = pTab->pStorage;
i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
int bContent = 0; /* Content only update */
/* If this is a contentless table (including contentless_unindexed=1
** tables), check if the UPDATE may proceed. */
if( fts5IsContentless(pTab, 1) ){
rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent);
if( rc!=SQLITE_OK ) goto update_out;
}
if( eType1!=SQLITE_INTEGER ){
rc = SQLITE_MISMATCH;
}else if( iOld!=iNew ){
assert( bContent==0 );
if( eConflict==SQLITE_REPLACE ){
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0);
}
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}else{
rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid);
}
}
}else if( bContent ){
/* This occurs when an UPDATE on a contentless table affects *only*
** UNINDEXED columns. This is a no-op for contentless_unindexed=0
** tables, or a write to the %_content table only for =1 tables. */
assert( fts5IsContentless(pTab, 1) );
rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid);
}
}else{
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
bUpdateOrDelete = 1;
sqlite3Fts5StorageReleaseDeleteRow(pStorage);
}
}
}
if( rc==SQLITE_OK
&& bUpdateOrDelete
|
| ︙ | ︙ | |||
253167 253168 253169 253170 253171 253172 253173 |
void *pUserData,
int (*xToken)(void*, int, const char*, int, int, int)
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
int rc = SQLITE_OK;
| | | | 253994 253995 253996 253997 253998 253999 254000 254001 254002 254003 254004 254005 254006 254007 254008 254009 254010 254011 254012 |
void *pUserData,
int (*xToken)(void*, int, const char*, int, int, int)
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
int rc = SQLITE_OK;
sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc);
rc = sqlite3Fts5Tokenize(pTab->pConfig,
FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken
);
sqlite3Fts5SetLocale(pTab->pConfig, 0, 0);
return rc;
}
/*
** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0
** passed as the locale.
|
| ︙ | ︙ | |||
253198 253199 253200 253201 253202 253203 253204 253205 253206 253207 253208 253209 253210 253211 253212 253213 253214 253215 253216 253217 253218 |
return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
}
static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
}
static int fts5ApiColumnText(
Fts5Context *pCtx,
int iCol,
const char **pz,
int *pn
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
if( iCol<0 || iCol>=pTab->pConfig->nCol ){
rc = SQLITE_RANGE;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < | | 254025 254026 254027 254028 254029 254030 254031 254032 254033 254034 254035 254036 254037 254038 254039 254040 254041 254042 254043 254044 254045 254046 254047 254048 254049 254050 254051 254052 254053 254054 254055 254056 254057 254058 254059 254060 254061 254062 254063 254064 254065 254066 254067 254068 254069 254070 254071 254072 254073 254074 254075 254076 254077 254078 254079 254080 254081 254082 254083 254084 254085 254086 254087 254088 254089 254090 254091 254092 254093 254094 254095 254096 254097 254098 254099 254100 254101 254102 254103 |
return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
}
static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
}
/*
** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This
** function extracts the text value of column iCol of the current row.
** Additionally, if there is an associated locale, it invokes
** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller
** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point
** after this function returns.
**
** If successful, (*ppText) is set to point to a buffer containing the text
** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that
** buffer in bytes. It is not guaranteed to be nul-terminated. If an error
** occurs, an SQLite error code is returned. The final values of the two
** output parameters are undefined in this case.
*/
static int fts5TextFromStmt(
Fts5Config *pConfig,
sqlite3_stmt *pStmt,
int iCol,
const char **ppText,
int *pnText
){
sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1);
const char *pLoc = 0;
int nLoc = 0;
int rc = SQLITE_OK;
if( pConfig->bLocale
&& pConfig->eContent==FTS5_CONTENT_EXTERNAL
&& sqlite3Fts5IsLocaleValue(pConfig, pVal)
){
rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc);
}else{
*ppText = (const char*)sqlite3_value_text(pVal);
*pnText = sqlite3_value_bytes(pVal);
if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){
pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol);
nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol);
}
}
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
return rc;
}
static int fts5ApiColumnText(
Fts5Context *pCtx,
int iCol,
const char **pz,
int *pn
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
if( iCol<0 || iCol>=pTab->pConfig->nCol ){
rc = SQLITE_RANGE;
}else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){
*pz = 0;
*pn = 0;
}else{
rc = fts5SeekCursor(pCsr, 0);
if( rc==SQLITE_OK ){
rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn);
sqlite3Fts5ClearLocale(pTab->pConfig);
}
}
return rc;
}
/*
** This is called by various API functions - xInst, xPhraseFirst,
|
| ︙ | ︙ | |||
253247 253248 253249 253250 253251 253252 253253 |
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
rc = SQLITE_RANGE;
}else if( pConfig->eDetail!=FTS5_DETAIL_FULL
| | < < | | | 254115 254116 254117 254118 254119 254120 254121 254122 254123 254124 254125 254126 254127 254128 254129 254130 254131 254132 254133 254134 254135 254136 254137 254138 254139 254140 254141 254142 254143 254144 254145 254146 254147 254148 254149 254150 254151 254152 254153 |
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
rc = SQLITE_RANGE;
}else if( pConfig->eDetail!=FTS5_DETAIL_FULL
&& fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1)
){
*pa = 0;
*pn = 0;
return SQLITE_OK;
}else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5PoslistPopulator *aPopulator;
int i;
aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
if( aPopulator==0 ) rc = SQLITE_NOMEM;
if( rc==SQLITE_OK ){
rc = fts5SeekCursor(pCsr, 0);
}
for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
const char *z = 0;
int n = 0;
rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ExprPopulatePoslists(
pConfig, pCsr->pExpr, aPopulator, i, z, n
);
}
sqlite3Fts5ClearLocale(pConfig);
}
sqlite3_free(aPopulator);
if( pCsr->pSorter ){
sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
}
}
|
| ︙ | ︙ | |||
253445 253446 253447 253448 253449 253450 253451 |
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
if( pConfig->bColumnsize ){
i64 iRowid = fts5CursorRowid(pCsr);
rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
| | < < < | < > | 254311 254312 254313 254314 254315 254316 254317 254318 254319 254320 254321 254322 254323 254324 254325 254326 254327 254328 254329 254330 254331 254332 254333 254334 254335 254336 254337 254338 254339 254340 254341 254342 254343 254344 254345 254346 |
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
if( pConfig->bColumnsize ){
i64 iRowid = fts5CursorRowid(pCsr);
rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
}else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){
int i;
for(i=0; i<pConfig->nCol; i++){
if( pConfig->abUnindexed[i]==0 ){
pCsr->aColumnSize[i] = -1;
}
}
}else{
int i;
rc = fts5SeekCursor(pCsr, 0);
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
if( pConfig->abUnindexed[i]==0 ){
const char *z = 0;
int n = 0;
pCsr->aColumnSize[i] = 0;
rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX,
z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb
);
}
sqlite3Fts5ClearLocale(pConfig);
}
}
}
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
}
if( iCol<0 ){
int i;
|
| ︙ | ︙ | |||
253736 253737 253738 253739 253740 253741 253742 |
*pnLocale = 0;
assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
if( iCol<0 || iCol>=pConfig->nCol ){
rc = SQLITE_RANGE;
}else if(
pConfig->abUnindexed[iCol]==0
| | < < < < < < < < < | | | < < < < < < < | | < < < < < < | | < < > | 254599 254600 254601 254602 254603 254604 254605 254606 254607 254608 254609 254610 254611 254612 254613 254614 254615 254616 254617 254618 254619 254620 254621 254622 254623 254624 254625 |
*pnLocale = 0;
assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
if( iCol<0 || iCol>=pConfig->nCol ){
rc = SQLITE_RANGE;
}else if(
pConfig->abUnindexed[iCol]==0
&& 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1)
&& pConfig->bLocale
){
rc = fts5SeekCursor(pCsr, 0);
if( rc==SQLITE_OK ){
const char *zDummy = 0;
int nDummy = 0;
rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy);
if( rc==SQLITE_OK ){
*pzLocale = pConfig->t.pLocale;
*pnLocale = pConfig->t.nLocale;
}
sqlite3Fts5ClearLocale(pConfig);
}
}
return rc;
}
static const Fts5ExtensionApi sFts5Api = {
|
| ︙ | ︙ | |||
253992 253993 253994 253995 253996 253997 253998 |
break;
}
sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free);
return rc;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 254832 254833 254834 254835 254836 254837 254838 254839 254840 254841 254842 254843 254844 254845 |
break;
}
sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free);
return rc;
}
/*
** This is the xColumn method, called by SQLite to request a value from
** the row that the supplied cursor currently points to.
*/
static int fts5ColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
| ︙ | ︙ | |||
254085 254086 254087 254088 254089 254090 254091 |
|| pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
){
if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){
fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg);
}
}
}else{
| < | < < < < < < < < | > > > > > > > > | > > > > > > | 254874 254875 254876 254877 254878 254879 254880 254881 254882 254883 254884 254885 254886 254887 254888 254889 254890 254891 254892 254893 254894 254895 254896 254897 254898 254899 254900 254901 254902 254903 254904 254905 254906 254907 254908 |
|| pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
){
if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){
fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg);
}
}
}else{
if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
rc = fts5SeekCursor(pCsr, 1);
if( rc==SQLITE_OK ){
sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1);
if( pConfig->bLocale
&& pConfig->eContent==FTS5_CONTENT_EXTERNAL
&& sqlite3Fts5IsLocaleValue(pConfig, pVal)
){
const char *z = 0;
int n = 0;
rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n);
if( rc==SQLITE_OK ){
sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT);
}
sqlite3Fts5ClearLocale(pConfig);
}else{
sqlite3_result_value(pCtx, pVal);
}
}
pConfig->pzErrmsg = 0;
}
}
return rc;
}
|
| ︙ | ︙ | |||
254623 254624 254625 254626 254627 254628 254629 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
| | | 255417 255418 255419 255420 255421 255422 255423 255424 255425 255426 255427 255428 255429 255430 255431 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT);
}
/*
** Implementation of fts5_locale(LOCALE, TEXT) function.
**
** If parameter LOCALE is NULL, or a zero-length string, then a copy of
** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as
|
| ︙ | ︙ | |||
254662 254663 254664 254665 254666 254667 254668 254669 254670 254671 |
zText = (const char*)sqlite3_value_text(apArg[1]);
nText = sqlite3_value_bytes(apArg[1]);
if( zLocale==0 || zLocale[0]=='\0' ){
sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
}else{
u8 *pBlob = 0;
u8 *pCsr = 0;
int nBlob = 0;
| > < < | | | < > > > > > > > > > > > > > > | 255456 255457 255458 255459 255460 255461 255462 255463 255464 255465 255466 255467 255468 255469 255470 255471 255472 255473 255474 255475 255476 255477 255478 255479 255480 255481 255482 255483 255484 255485 255486 255487 255488 255489 255490 255491 255492 255493 255494 255495 255496 255497 255498 255499 255500 255501 255502 255503 255504 255505 255506 255507 |
zText = (const char*)sqlite3_value_text(apArg[1]);
nText = sqlite3_value_bytes(apArg[1]);
if( zLocale==0 || zLocale[0]=='\0' ){
sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
}else{
Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx);
u8 *pBlob = 0;
u8 *pCsr = 0;
int nBlob = 0;
nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText;
pBlob = (u8*)sqlite3_malloc(nBlob);
if( pBlob==0 ){
sqlite3_result_error_nomem(pCtx);
return;
}
pCsr = pBlob;
memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE);
pCsr += FTS5_LOCALE_HDR_SIZE;
memcpy(pCsr, zLocale, nLocale);
pCsr += nLocale;
(*pCsr++) = 0x00;
if( zText ) memcpy(pCsr, zText, nText);
assert( &pCsr[nText]==&pBlob[nBlob] );
sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
}
}
/*
** Implementation of fts5_insttoken() function.
*/
static void fts5InsttokenFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apArg /* Function arguments */
){
assert( nArg==1 );
(void)nArg;
sqlite3_result_value(pCtx, apArg[0]);
sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE);
}
/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
static const char *azName[] = {
|
| ︙ | ︙ | |||
254787 254788 254789 254790 254791 254792 254793 254794 254795 254796 254797 254798 254799 254800 |
pGlobal->db = db;
pGlobal->api.iVersion = 3;
pGlobal->api.xCreateFunction = fts5CreateAux;
pGlobal->api.xCreateTokenizer = fts5CreateTokenizer;
pGlobal->api.xFindTokenizer = fts5FindTokenizer;
pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2;
pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2;
rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db);
if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api);
if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api);
if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
if( rc==SQLITE_OK ){
| > > > > > > > > > > | 255593 255594 255595 255596 255597 255598 255599 255600 255601 255602 255603 255604 255605 255606 255607 255608 255609 255610 255611 255612 255613 255614 255615 255616 |
pGlobal->db = db;
pGlobal->api.iVersion = 3;
pGlobal->api.xCreateFunction = fts5CreateAux;
pGlobal->api.xCreateTokenizer = fts5CreateTokenizer;
pGlobal->api.xFindTokenizer = fts5FindTokenizer;
pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2;
pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2;
/* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector.
** The constants below were generated randomly. */
sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr);
pGlobal->aLocaleHdr[0] ^= 0xF924976D;
pGlobal->aLocaleHdr[1] ^= 0x16596E13;
pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA;
pGlobal->aLocaleHdr[3] ^= 0x9B03A67F;
assert( sizeof(pGlobal->aLocaleHdr)==16 );
rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db);
if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api);
if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api);
if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
254808 254809 254810 254811 254812 254813 254814 |
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
p, fts5SourceIdFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_locale", 2,
| | > > > > > > > | 255624 255625 255626 255627 255628 255629 255630 255631 255632 255633 255634 255635 255636 255637 255638 255639 255640 255641 255642 255643 255644 255645 255646 255647 |
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
p, fts5SourceIdFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_locale", 2,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE,
p, fts5LocaleFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_insttoken", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
p, fts5InsttokenFunc, 0, 0
);
}
}
/* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
** fts5_test_mi.c is compiled and linked into the executable. And call
** its entry point to enable the matchinfo() demo. */
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
|
| ︙ | ︙ | |||
255013 255014 255015 255016 255017 255018 255019 |
zSql = sqlite3_mprintf(azStmt[eStmt],
pC->zContentExprlist, pC->zContent, pC->zContentRowid
);
break;
case FTS5_STMT_INSERT_CONTENT:
case FTS5_STMT_REPLACE_CONTENT: {
| < | > > > | > > > > > > > > > | > > > | | | | > > | | | < | 255836 255837 255838 255839 255840 255841 255842 255843 255844 255845 255846 255847 255848 255849 255850 255851 255852 255853 255854 255855 255856 255857 255858 255859 255860 255861 255862 255863 255864 255865 255866 255867 255868 255869 255870 255871 255872 255873 255874 255875 255876 255877 255878 |
zSql = sqlite3_mprintf(azStmt[eStmt],
pC->zContentExprlist, pC->zContent, pC->zContentRowid
);
break;
case FTS5_STMT_INSERT_CONTENT:
case FTS5_STMT_REPLACE_CONTENT: {
char *zBind = 0;
int i;
assert( pC->eContent==FTS5_CONTENT_NORMAL
|| pC->eContent==FTS5_CONTENT_UNINDEXED
);
/* Add bindings for the "c*" columns - those that store the actual
** table content. If eContent==NORMAL, then there is one binding
** for each column. Or, if eContent==UNINDEXED, then there are only
** bindings for the UNINDEXED columns. */
for(i=0; rc==SQLITE_OK && i<(pC->nCol+1); i++){
if( !i || pC->eContent==FTS5_CONTENT_NORMAL || pC->abUnindexed[i-1] ){
zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1);
}
}
/* Add bindings for any "l*" columns. Only non-UNINDEXED columns
** require these. */
if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL ){
for(i=0; rc==SQLITE_OK && i<pC->nCol; i++){
if( pC->abUnindexed[i]==0 ){
zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2);
}
}
}
zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind);
sqlite3_free(zBind);
break;
}
case FTS5_STMT_REPLACE_DOCSIZE:
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName,
(pC->bContentlessDelete ? ",?" : "")
);
|
| ︙ | ︙ | |||
255212 255213 255214 255215 255216 255217 255218 |
memset(p, 0, (size_t)nByte);
p->aTotalSize = (i64*)&p[1];
p->pConfig = pConfig;
p->pIndex = pIndex;
if( bCreate ){
| | > > | > > > | | > > > > > > > > > | 256050 256051 256052 256053 256054 256055 256056 256057 256058 256059 256060 256061 256062 256063 256064 256065 256066 256067 256068 256069 256070 256071 256072 256073 256074 256075 256076 256077 256078 256079 256080 256081 256082 256083 256084 256085 256086 256087 256088 256089 256090 |
memset(p, 0, (size_t)nByte);
p->aTotalSize = (i64*)&p[1];
p->pConfig = pConfig;
p->pIndex = pIndex;
if( bCreate ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL
|| pConfig->eContent==FTS5_CONTENT_UNINDEXED
){
int nDefn = 32 + pConfig->nCol*10;
char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20);
if( zDefn==0 ){
rc = SQLITE_NOMEM;
}else{
int i;
int iOff;
sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
iOff = (int)strlen(zDefn);
for(i=0; i<pConfig->nCol; i++){
if( pConfig->eContent==FTS5_CONTENT_NORMAL
|| pConfig->abUnindexed[i]
){
sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
iOff += (int)strlen(&zDefn[iOff]);
}
}
if( pConfig->bLocale ){
for(i=0; i<pConfig->nCol; i++){
if( pConfig->abUnindexed[i]==0 ){
sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i);
iOff += (int)strlen(&zDefn[iOff]);
}
}
}
rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
}
sqlite3_free(zDefn);
}
if( rc==SQLITE_OK && pConfig->bColumnsize ){
|
| ︙ | ︙ | |||
255377 255378 255379 255380 255381 255382 255383 |
ctx.pStorage = p;
ctx.iCol = -1;
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
sqlite3_value *pVal = 0;
const char *pText = 0;
int nText = 0;
| > | > | > > > | > > > > | > | | 256229 256230 256231 256232 256233 256234 256235 256236 256237 256238 256239 256240 256241 256242 256243 256244 256245 256246 256247 256248 256249 256250 256251 256252 256253 256254 256255 256256 256257 256258 256259 256260 256261 256262 256263 256264 256265 256266 256267 256268 256269 256270 256271 256272 256273 256274 256275 |
ctx.pStorage = p;
ctx.iCol = -1;
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
sqlite3_value *pVal = 0;
const char *pText = 0;
int nText = 0;
const char *pLoc = 0;
int nLoc = 0;
assert( pSeek==0 || apVal==0 );
assert( pSeek!=0 || apVal!=0 );
if( pSeek ){
pVal = sqlite3_column_value(pSeek, iCol);
}else{
pVal = apVal[iCol-1];
}
if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
}else{
pText = (const char*)sqlite3_value_text(pVal);
nText = sqlite3_value_bytes(pVal);
if( pConfig->bLocale && pSeek ){
pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol);
nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol);
}
}
if( rc==SQLITE_OK ){
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
ctx.szCol = 0;
rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
pText, nText, (void*)&ctx, fts5StorageInsertCallback
);
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){
rc = FTS5_CORRUPT;
}
sqlite3Fts5ClearLocale(pConfig);
}
}
}
if( rc==SQLITE_OK && p->nTotalRow<1 ){
rc = FTS5_CORRUPT;
}else{
p->nTotalRow--;
|
| ︙ | ︙ | |||
255444 255445 255446 255447 255448 255449 255450 |
*/
static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
i64 iOrigin = 0;
sqlite3_stmt *pLookup = 0;
int rc = SQLITE_OK;
assert( p->pConfig->bContentlessDelete );
| | > > | 256306 256307 256308 256309 256310 256311 256312 256313 256314 256315 256316 256317 256318 256319 256320 256321 256322 |
*/
static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
i64 iOrigin = 0;
sqlite3_stmt *pLookup = 0;
int rc = SQLITE_OK;
assert( p->pConfig->bContentlessDelete );
assert( p->pConfig->eContent==FTS5_CONTENT_NONE
|| p->pConfig->eContent==FTS5_CONTENT_UNINDEXED
);
/* Look up the origin of the document in the %_docsize table. Store
** this in stack variable iOrigin. */
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pLookup, 1, iDel);
if( SQLITE_ROW==sqlite3_step(pLookup) ){
|
| ︙ | ︙ | |||
255568 255569 255570 255571 255572 255573 255574 255575 255576 255577 255578 255579 255580 255581 255582 255583 255584 255585 255586 255587 255588 255589 255590 |
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
}
if( rc==SQLITE_OK ){
if( p->pConfig->bContentlessDelete ){
rc = fts5StorageContentlessDelete(p, iDel);
}else{
rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow);
}
}
/* Delete the %_docsize record */
if( rc==SQLITE_OK && pConfig->bColumnsize ){
rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDel, 1, iDel);
sqlite3_step(pDel);
rc = sqlite3_reset(pDel);
}
}
/* Delete the %_content record */
| > > > > > > | > > | 256432 256433 256434 256435 256436 256437 256438 256439 256440 256441 256442 256443 256444 256445 256446 256447 256448 256449 256450 256451 256452 256453 256454 256455 256456 256457 256458 256459 256460 256461 256462 256463 256464 256465 256466 256467 256468 256469 256470 |
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
}
if( rc==SQLITE_OK ){
if( p->pConfig->bContentlessDelete ){
rc = fts5StorageContentlessDelete(p, iDel);
if( rc==SQLITE_OK
&& bSaveRow
&& p->pConfig->eContent==FTS5_CONTENT_UNINDEXED
){
rc = sqlite3Fts5StorageFindDeleteRow(p, iDel);
}
}else{
rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow);
}
}
/* Delete the %_docsize record */
if( rc==SQLITE_OK && pConfig->bColumnsize ){
rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDel, 1, iDel);
sqlite3_step(pDel);
rc = sqlite3_reset(pDel);
}
}
/* Delete the %_content record */
if( pConfig->eContent==FTS5_CONTENT_NORMAL
|| pConfig->eContent==FTS5_CONTENT_UNINDEXED
){
if( rc==SQLITE_OK ){
rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0);
}
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDel, 1, iDel);
sqlite3_step(pDel);
rc = sqlite3_reset(pDel);
|
| ︙ | ︙ | |||
255616 255617 255618 255619 255620 255621 255622 |
"DELETE FROM %Q.'%q_data';"
"DELETE FROM %Q.'%q_idx';",
pConfig->zDb, pConfig->zName,
pConfig->zDb, pConfig->zName
);
if( rc==SQLITE_OK && pConfig->bColumnsize ){
rc = fts5ExecPrintf(pConfig->db, 0,
| | > > > > > | | 256488 256489 256490 256491 256492 256493 256494 256495 256496 256497 256498 256499 256500 256501 256502 256503 256504 256505 256506 256507 256508 |
"DELETE FROM %Q.'%q_data';"
"DELETE FROM %Q.'%q_idx';",
pConfig->zDb, pConfig->zName,
pConfig->zDb, pConfig->zName
);
if( rc==SQLITE_OK && pConfig->bColumnsize ){
rc = fts5ExecPrintf(pConfig->db, 0,
"DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName
);
}
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_UNINDEXED ){
rc = fts5ExecPrintf(pConfig->db, 0,
"DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName
);
}
/* Reinitialize the %_data table. This call creates the initial structure
** and averages records. */
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexReinit(p->pIndex);
|
| ︙ | ︙ | |||
255658 255659 255660 255661 255662 255663 255664 |
i64 iRowid = sqlite3_column_int64(pScan, 0);
sqlite3Fts5BufferZero(&buf);
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
ctx.szCol = 0;
if( pConfig->abUnindexed[ctx.iCol]==0 ){
| < > > > > > > > > > > > > > > | > | > | | 256535 256536 256537 256538 256539 256540 256541 256542 256543 256544 256545 256546 256547 256548 256549 256550 256551 256552 256553 256554 256555 256556 256557 256558 256559 256560 256561 256562 256563 256564 256565 256566 256567 256568 256569 256570 256571 256572 256573 256574 256575 256576 256577 |
i64 iRowid = sqlite3_column_int64(pScan, 0);
sqlite3Fts5BufferZero(&buf);
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
ctx.szCol = 0;
if( pConfig->abUnindexed[ctx.iCol]==0 ){
int nText = 0; /* Size of pText in bytes */
const char *pText = 0; /* Pointer to buffer containing text value */
int nLoc = 0; /* Size of pLoc in bytes */
const char *pLoc = 0; /* Pointer to buffer containing text value */
sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1);
if( pConfig->eContent==FTS5_CONTENT_EXTERNAL
&& sqlite3Fts5IsLocaleValue(pConfig, pVal)
){
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
}else{
pText = (const char*)sqlite3_value_text(pVal);
nText = sqlite3_value_bytes(pVal);
if( pConfig->bLocale ){
int iCol = ctx.iCol + 1 + pConfig->nCol;
pLoc = (const char*)sqlite3_column_text(pScan, iCol);
nLoc = sqlite3_column_bytes(pScan, iCol);
}
}
if( rc==SQLITE_OK ){
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
rc = sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT,
pText, nText,
(void*)&ctx,
fts5StorageInsertCallback
);
sqlite3Fts5ClearLocale(pConfig);
}
}
sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
}
p->nTotalRow++;
|
| ︙ | ︙ | |||
255738 255739 255740 255741 255742 255743 255744 255745 255746 255747 255748 255749 255750 255751 |
}
/*
** Insert a new row into the FTS content table.
*/
static int sqlite3Fts5StorageContentInsert(
Fts5Storage *p,
sqlite3_value **apVal,
i64 *piRowid
){
Fts5Config *pConfig = p->pConfig;
int rc = SQLITE_OK;
/* Insert the new row into the %_content table. */
| > | > > > > > | > > > > > > > | > > | > | | | | < | > > | > | < < > > > | > > | < < > | < | | > > | | | > | 256630 256631 256632 256633 256634 256635 256636 256637 256638 256639 256640 256641 256642 256643 256644 256645 256646 256647 256648 256649 256650 256651 256652 256653 256654 256655 256656 256657 256658 256659 256660 256661 256662 256663 256664 256665 256666 256667 256668 256669 256670 256671 256672 256673 256674 256675 256676 256677 256678 256679 256680 256681 256682 256683 256684 256685 256686 256687 256688 256689 256690 256691 256692 256693 256694 256695 256696 256697 256698 256699 256700 256701 256702 256703 256704 256705 256706 256707 256708 |
}
/*
** Insert a new row into the FTS content table.
*/
static int sqlite3Fts5StorageContentInsert(
Fts5Storage *p,
int bReplace, /* True to use REPLACE instead of INSERT */
sqlite3_value **apVal,
i64 *piRowid
){
Fts5Config *pConfig = p->pConfig;
int rc = SQLITE_OK;
/* Insert the new row into the %_content table. */
if( pConfig->eContent!=FTS5_CONTENT_NORMAL
&& pConfig->eContent!=FTS5_CONTENT_UNINDEXED
){
if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
*piRowid = sqlite3_value_int64(apVal[1]);
}else{
rc = fts5StorageNewRowid(p, piRowid);
}
}else{
sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */
int i; /* Counter variable */
assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT );
assert( bReplace==0 || bReplace==1 );
rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0);
if( pInsert ) sqlite3_clear_bindings(pInsert);
/* Bind the rowid value */
sqlite3_bind_value(pInsert, 1, apVal[1]);
/* Loop through values for user-defined columns. i=2 is the leftmost
** user-defined column. As is column 1 of pSavedRow. */
for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
int bUnindexed = pConfig->abUnindexed[i-2];
if( pConfig->eContent==FTS5_CONTENT_NORMAL || bUnindexed ){
sqlite3_value *pVal = apVal[i];
if( sqlite3_value_nochange(pVal) && p->pSavedRow ){
/* This is an UPDATE statement, and user-defined column (i-2) was not
** modified. Retrieve the value from Fts5Storage.pSavedRow. */
pVal = sqlite3_column_value(p->pSavedRow, i-1);
if( pConfig->bLocale && bUnindexed==0 ){
sqlite3_bind_value(pInsert, pConfig->nCol + i,
sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1)
);
}
}else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
const char *pText = 0;
const char *pLoc = 0;
int nText = 0;
int nLoc = 0;
assert( pConfig->bLocale );
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
if( rc==SQLITE_OK ){
sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT);
if( bUnindexed==0 ){
int iLoc = pConfig->nCol + i;
sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT);
}
}
continue;
}
rc = sqlite3_bind_value(pInsert, i, pVal);
}
}
if( rc==SQLITE_OK ){
sqlite3_step(pInsert);
rc = sqlite3_reset(pInsert);
}
*piRowid = sqlite3_last_insert_rowid(pConfig->db);
}
|
| ︙ | ︙ | |||
255815 255816 255817 255818 255819 255820 255821 |
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
}
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
ctx.szCol = 0;
if( pConfig->abUnindexed[ctx.iCol]==0 ){
| < > > > < > > > > > > > > | > > > > > | | | | 256729 256730 256731 256732 256733 256734 256735 256736 256737 256738 256739 256740 256741 256742 256743 256744 256745 256746 256747 256748 256749 256750 256751 256752 256753 256754 256755 256756 256757 256758 256759 256760 256761 256762 256763 256764 256765 256766 256767 256768 256769 256770 256771 256772 256773 |
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
}
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
ctx.szCol = 0;
if( pConfig->abUnindexed[ctx.iCol]==0 ){
int nText = 0; /* Size of pText in bytes */
const char *pText = 0; /* Pointer to buffer containing text value */
int nLoc = 0; /* Size of pText in bytes */
const char *pLoc = 0; /* Pointer to buffer containing text value */
sqlite3_value *pVal = apVal[ctx.iCol+2];
if( p->pSavedRow && sqlite3_value_nochange(pVal) ){
pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1);
if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){
int iCol = ctx.iCol + 1 + pConfig->nCol;
pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol);
nLoc = sqlite3_column_bytes(p->pSavedRow, iCol);
}
}else{
pVal = apVal[ctx.iCol+2];
}
if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
}else{
pText = (const char*)sqlite3_value_text(pVal);
nText = sqlite3_value_bytes(pVal);
}
if( rc==SQLITE_OK ){
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
rc = sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx,
fts5StorageInsertCallback
);
sqlite3Fts5ClearLocale(pConfig);
}
}
sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
}
p->nTotalRow++;
|
| ︙ | ︙ | |||
255996 255997 255998 255999 256000 256001 256002 |
if( pConfig->bColumnsize ){
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
}
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
}
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
| | > > > > > > > > > > > > > > > > > > > > > > | | > | | | < < < < < < < > | | > > > | > > > | | | | | | > | 256924 256925 256926 256927 256928 256929 256930 256931 256932 256933 256934 256935 256936 256937 256938 256939 256940 256941 256942 256943 256944 256945 256946 256947 256948 256949 256950 256951 256952 256953 256954 256955 256956 256957 256958 256959 256960 256961 256962 256963 256964 256965 256966 256967 256968 256969 256970 256971 256972 256973 256974 256975 256976 256977 256978 256979 256980 256981 256982 256983 256984 256985 256986 256987 256988 256989 256990 256991 256992 |
if( pConfig->bColumnsize ){
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
}
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
}
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
if( pConfig->abUnindexed[i]==0 ){
const char *pText = 0;
int nText = 0;
const char *pLoc = 0;
int nLoc = 0;
sqlite3_value *pVal = sqlite3_column_value(pScan, i+1);
if( pConfig->eContent==FTS5_CONTENT_EXTERNAL
&& sqlite3Fts5IsLocaleValue(pConfig, pVal)
){
rc = sqlite3Fts5DecodeLocaleValue(
pVal, &pText, &nText, &pLoc, &nLoc
);
}else{
if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){
int iCol = i + 1 + pConfig->nCol;
pLoc = (const char*)sqlite3_column_text(pScan, iCol);
nLoc = sqlite3_column_bytes(pScan, iCol);
}
pText = (const char*)sqlite3_value_text(pVal);
nText = sqlite3_value_bytes(pVal);
}
ctx.iCol = i;
ctx.szCol = 0;
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
}
if( rc==SQLITE_OK ){
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
rc = sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT,
pText, nText,
(void*)&ctx,
fts5StorageIntegrityCallback
);
sqlite3Fts5ClearLocale(pConfig);
}
/* If this is not a columnsize=0 database, check that the number
** of tokens in the value matches the aColSize[] value read from
** the %_docsize table. */
if( rc==SQLITE_OK
&& pConfig->bColumnsize
&& ctx.szCol!=aColSize[i]
){
rc = FTS5_CORRUPT;
}
aTotalSize[i] += ctx.szCol;
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
sqlite3Fts5TermsetFree(ctx.pTermset);
ctx.pTermset = 0;
}
}
}
sqlite3Fts5TermsetFree(ctx.pTermset);
ctx.pTermset = 0;
if( rc!=SQLITE_OK ) break;
}
|
| ︙ | ︙ | |||
256452 256453 256454 256455 256456 256457 256458 |
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
};
#define READ_UTF8(zIn, zTerm, c) \
c = *(zIn++); \
if( c>=0xc0 ){ \
c = sqlite3Utf8Trans1[c-0xc0]; \
| | | 257404 257405 257406 257407 257408 257409 257410 257411 257412 257413 257414 257415 257416 257417 257418 |
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
};
#define READ_UTF8(zIn, zTerm, c) \
c = *(zIn++); \
if( c>=0xc0 ){ \
c = sqlite3Utf8Trans1[c-0xc0]; \
while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \
c = (c<<6) + (0x3f & *(zIn++)); \
} \
if( c<0x80 \
|| (c&0xFFFFF800)==0xD800 \
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
|
| ︙ | ︙ | |||
257607 257608 257609 257610 257611 257612 257613 |
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
| | | > < > > > > < | 258559 258560 258561 258562 258563 258564 258565 258566 258567 258568 258569 258570 258571 258572 258573 258574 258575 258576 258577 258578 258579 258580 258581 258582 258583 258584 258585 258586 258587 258588 258589 258590 258591 258592 258593 258594 258595 258596 258597 258598 258599 258600 258601 258602 258603 258604 258605 258606 258607 258608 258609 |
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = (zIn ? &zIn[nText] : 0);
u32 iCode = 0;
int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
/* Populate aBuf[] with the characters for the first trigram. */
for(ii=0; ii<3; ii++){
do {
aStart[ii] = zIn - (const unsigned char*)pText;
if( zIn>=zEof ) return SQLITE_OK;
READ_UTF8(zIn, zEof, iCode);
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
WRITE_UTF8(zOut, iCode);
}
/* At the start of each iteration of this loop:
**
** aBuf: Contains 3 characters. The 3 characters of the next trigram.
** zOut: Points to the byte following the last character in aBuf.
** aStart[3]: Contains the byte offset in the input text corresponding
** to the start of each of the three characters in the buffer.
*/
assert( zIn<=zEof );
while( 1 ){
int iNext; /* Start of character following current tri */
const char *z1;
/* Read characters from the input up until the first non-diacritic */
do {
iNext = zIn - (const unsigned char*)pText;
if( zIn>=zEof ){
iCode = 0;
break;
}
READ_UTF8(zIn, zEof, iCode);
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
/* Pass the current trigram back to fts5 */
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
if( iCode==0 || rc!=SQLITE_OK ) break;
|
| ︙ | ︙ | |||
259677 259678 259679 259680 259681 259682 259683 | }; void *p = (void*)pGlobal; return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); } | | | 260632 260633 260634 260635 260636 260637 260638 260639 260640 260641 260642 260643 260644 260645 260646 | }; void *p = (void*)pGlobal; return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); } /* Here ends the fts5.c composite file. */ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ /************** End of fts5.c ************************************************/ /************** Begin file stmt.c ********************************************/ /* ** 2017-05-31 ** |
| ︙ | ︙ | |||
260033 260034 260035 260036 260037 260038 260039 260040 |
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/
| > | 260988 260989 260990 260991 260992 260993 260994 260995 260996 |
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
#endif /* SQLITE_AMALGAMATION */
/************************** End of sqlite3.c ******************************/
|
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.48.0" #define SQLITE_VERSION_NUMBER 3048000 #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from | > > > > > > > > | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. ** ** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read ** from the database file in amounts that are not a multiple of the ** page size and that do not begin at a page boundary. Without this ** property, SQLite is careful to only do full-page reads and write ** on aligned pages, with the one exception that it will do a sub-page ** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 #define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from |
| ︙ | ︙ | |||
810 811 812 813 814 815 816 817 818 819 820 821 822 823 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means | > | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** <li> [SQLITE_IOCAP_SUBPAGE_READ] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means |
| ︙ | ︙ | |||
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** | > > > > > | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_NULL_IO]] ** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor ** or file handle for the [sqlite3_file] object such that it will no longer ** read or write to the database file. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** |
| ︙ | ︙ | |||
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| ︙ | ︙ | |||
2618 2619 2620 2621 2622 2623 2624 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value | | > > > > | 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value ** and that if the number of rows modified by the most recent INSERT, UPDATE, ** or DELETE is greater than the maximum value supported by type "int", then ** the return value of sqlite3_changes() is undefined. ^Executing any other ** type of SQL statement does not modify the value returned by these functions. ** For the purposes of this interface, a CREATE TABLE AS SELECT statement ** does not count as an INSERT, UPDATE or DELETE statement and hence the rows ** added to the new table by the CREATE TABLE AS SELECT statement are not ** counted. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], ** [foreign key actions] or [REPLACE] constraint resolution are not counted. ** ** Changes to a view that are intercepted by ** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value |
| ︙ | ︙ | |||
4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
| > > > > > > > > > > > | 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
**
** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
** errors from being sent to the error log defined by
** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
** compiles to see if some SQL syntax is well-formed, without generating
** messages on the global error log when it is not. If the test compile
** fails, the sqlite3_prepare_v3() call returns the same error indications
** with or without this flag; it just omits the call to [sqlite3_log()] that
** logs the error.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
#define SQLITE_PREPARE_DONT_LOG 0x10
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
|
| ︙ | ︙ | |||
4218 4219 4220 4221 4222 4223 4224 | ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), ** and sqlite3_prepare_v3() ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the | | | > > > > | 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 | ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), ** and sqlite3_prepare_v3() ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the ** first zero terminator. ^If nByte is positive, then it is the maximum ** number of bytes read from zSql. When nByte is positive, zSql is read ** up to the first zero terminator or until the nByte bytes have been read, ** whichever comes first. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string <i>including</i> ** the nul-terminator. ** Note that nByte measure the length of the input in bytes, not ** characters, even for the UTF-16 interfaces. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** ** ^*ppStmt is left pointing to a compiled [prepared statement] that can be |
| ︙ | ︙ | |||
5595 5596 5597 5598 5599 5600 5601 | ** ** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd> ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. ** This flag instructs SQLite to omit some corner-case optimizations that ** might disrupt the operation of the [sqlite3_value_subtype()] function, ** causing it to return zero rather than the correct subtype(). | | | 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 | ** ** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd> ** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. ** This flag instructs SQLite to omit some corner-case optimizations that ** might disrupt the operation of the [sqlite3_value_subtype()] function, ** causing it to return zero rather than the correct subtype(). ** All SQL functions that invoke [sqlite3_value_subtype()] should have this ** property. If the SQLITE_SUBTYPE property is omitted, then the return ** value from [sqlite3_value_subtype()] might sometimes be zero even though ** a non-zero subtype was specified by the function argument expression. ** ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> ** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_result_subtype()] to cause a sub-type to be associated with its |
| ︙ | ︙ | |||
8360 8361 8362 8363 8364 8365 8366 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 #define SQLITE_TESTCTRL_PRNG_SEED 28 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 | | | 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 #define SQLITE_TESTCTRL_PRNG_SEED 28 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking ** ** These routines provide access to the set of SQL language keywords ** recognized by SQLite. Applications can uses these routines to determine |
| ︙ | ︙ | |||
9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 | ** ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); | > > > > > > > > > > | 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 | ** ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. ** ** <b>Alternatives To Using The Backup API</b> ** ** Other techniques for safely creating a consistent backup of an SQLite ** database include: ** ** <ul> ** <li> The [VACUUM INTO] command. ** <li> The [sqlite3_rsync] utility program. ** </ul> */ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); |
| ︙ | ︙ | |||
10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 | ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a ** new [sqlite3_snapshot] object that records the current state of ** schema S in database connection D. ^On success, the ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined ** in this case. ** ** <ul> | > > > > > > > > | 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 | ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a ** new [sqlite3_snapshot] object that records the current state of ** schema S in database connection D. ^On success, the ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** ** If a read-transaction is opened by this function, then it is guaranteed ** that the returned snapshot object may not be invalidated by a database ** writer or checkpointer until after the read-transaction is closed. This ** is not guaranteed if a read-transaction is already open when this ** function is called. In that case, any subsequent write or checkpoint ** operation on the database may invalidate the returned snapshot handle, ** even while the read-transaction remains open. ** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined ** in this case. ** ** <ul> |
| ︙ | ︙ | |||
10854 10855 10856 10857 10858 10859 10860 | # define SQLITE_THREADSAFE 0 # endif #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif | | | 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 | # define SQLITE_THREADSAFE 0 # endif #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif /* #endif for SQLITE3_H will be added by mksqlite3.tcl */ /******** Begin file sqlite3rtree.h *********/ /* ** 2010 August 30 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
| ︙ | ︙ | |||
13105 13106 13107 13108 13109 13110 13111 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in | < < | > > > > > > > > > > > > > > > > > | 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API may be slow in some cases if the token identified by parameters ** iIdx and iToken matched a prefix token in the query. In most cases, the ** first call to this API for each prefix token in the query is forced ** to scan the portion of the full-text index that matches the prefix ** token to collect the extra data required by this API. If the prefix ** token matches a large number of token instances in the document set, ** this may be a performance problem. ** ** If the user knows in advance that a query may use this API for a ** prefix token, FTS5 may be configured to collect all required data as part ** of the initial querying of the full-text index, avoiding the second scan ** entirely. This also causes prefix queries that do not use this API to ** run more slowly and use more memory. FTS5 may be configured in this way ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ** option, or on a per-query basis using the ** [fts5_insttoken | fts5_insttoken()] user function. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. |
| ︙ | ︙ | |||
13203 13204 13205 13206 13207 13208 13209 | /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting | < | 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 | /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** |
| ︙ | ︙ | |||
13547 13548 13549 13550 13551 13552 13553 | #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ | > | 13613 13614 13615 13616 13617 13618 13619 13620 | #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ #endif /* SQLITE3_H */ |
Changes to fossil.1.
|
| | | | | | | | | | > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | .TH FOSSIL "1" "Oct 2024" "https://fossil-scm.org" "User Commands" .SH NAME fossil \- Distributed Version Control System .SH SYNOPSIS .B fossil \fIhelp\fR .br .B fossil \fIhelp COMMAND\fR .br .B fossil \fICOMMAND [OPTIONS]\fR .SH DESCRIPTION Fossil is a distributed version control system (DVCS) with built-in forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server. .SH Common COMMANDs: add cherrypick grep push sync .br addremove clean help rebuild tag .br all clone info remote timeline .br amend commit init repack tree .br annotate dbstat ls revert ui .br bisect delete merge rm undo .br blame describe merge-base settings unversioned .br branch diff mv sql update .br cat extras open ssl-config version .br changes finfo patch stash xdiff .br chat gdiff pull status .SH FEATURES Features as described on the fossil home page. .HP 1. |
| ︙ | ︙ |
Changes to skins/darkmode/css.txt.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 |
color: rgba(24,24,24,0.8);
border-radius: 0.1em;
}
.fileage tr:hover,
div.filetreeline:hover {
background-color: #333;
}
.button,
button {
color: #aaa;
| > > > | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
color: rgba(24,24,24,0.8);
border-radius: 0.1em;
}
.fileage tr:hover,
div.filetreeline:hover {
background-color: #333;
}
div.file-change-line button {
background-color: #484848
}
.button,
button {
color: #aaa;
background-color: #484848;
border-radius: 5px;
border: 0
}
.button:hover,
button:hover {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
|
| ︙ | ︙ |
Changes to skins/default/css.txt.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
display: none; /* don't use body-area h1 except… */
}
.artifact h1.page-title,
.dir h1.page-title,
.doc h1.page-title,
.wiki h1.page-title {
display: block; /* …for potentially long doc titles… */
}
.artifact .title > .page-title,
.dir .title > .page-title,
.doc .title > .page-title,
.wiki .title > .page-title {
display: none; /* …where we suppress the title area h1 instead */
}
| > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
display: none; /* don't use body-area h1 except… */
}
.artifact h1.page-title,
.dir h1.page-title,
.doc h1.page-title,
.wiki h1.page-title {
display: block; /* …for potentially long doc titles… */
color: #444;
}
.artifact .title > .page-title,
.dir .title > .page-title,
.doc .title > .page-title,
.wiki .title > .page-title {
display: none; /* …where we suppress the title area h1 instead */
}
|
| ︙ | ︙ | |||
723 724 725 726 727 728 729 |
}
.forum > .content .markdown > pre {
margin-left: 20pt; /* special case for MD in forum; need less indent */
}
/* Fossil UI uses these, but in sufficiently constrained ways that we
* don't have to be nearly as careful to avoid an overreach. */
| | | | | | | | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 |
}
.forum > .content .markdown > pre {
margin-left: 20pt; /* special case for MD in forum; need less indent */
}
/* Fossil UI uses these, but in sufficiently constrained ways that we
* don't have to be nearly as careful to avoid an overreach. */
.doc > .content h1, .artifact .content h1, .dir .content h1, .fileedit .content h1, .wiki .content h1 { margin-left: 10pt; }
.doc > .content h2, .artifact .content h2, .dir .content h2, .fileedit .content h2, .wiki .content h2 { margin-left: 20pt; }
.doc > .content h3, .artifact .content h3, .dir .content h3, .fileedit .content h3, .wiki .content h3 { margin-left: 30pt; }
.doc > .content h4, .artifact .content h4, .dir .content h4, .fileedit .content h4, .wiki .content h4 { margin-left: 40pt; }
.doc > .content h5, .artifact .content h5, .dir .content h5, .fileedit .content h5, .wiki .content h5 { margin-left: 50pt; }
.doc > .content hr, .artifact .content hr, .dir .content hr, .fileedit .content hr, .wiki .content hr { margin-left: 10pt; }
/* Don't need to be nearly as careful with tags Fossil UI doesn't use. */
.doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; }
.doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; }
.doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; }
/* Fossil UI doesn't use Pikchr at all (yet?) so we can be quite loose
|
| ︙ | ︙ |
Changes to src/add.c.
| ︙ | ︙ | |||
351 352 353 354 355 356 357 | ** ** The --ignore and --clean options are comma-separated lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | ** ** The --ignore and --clean options are comma-separated lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** ** When attempting to explicitly add files on the commandline, and if those ** match "ignore-glob", a confirmation is asked first. This can be prevented ** using the -f|--force option. ** ** The --case-sensitive option determines whether or not filenames should ** be treated case sensitive or not. If the option is not given, the default ** depends on the global setting, or the operating system default, if not set. ** |
| ︙ | ︙ |
Changes to src/allrepo.c.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
static void collect_argv(Blob *pExtra, int iStart){
int i;
for(i=iStart; i<g.argc; i++){
blob_appendf(pExtra, " %s", g.argv[i]);
}
}
| < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
static void collect_argv(Blob *pExtra, int iStart){
int i;
for(i=iStart; i<g.argc; i++){
blob_appendf(pExtra, " %s", g.argv[i]);
}
}
/*
** COMMAND: all
**
** Usage: %fossil all SUBCOMMAND ...
**
** The ~/.fossil file records the location of all repositories for a
** user. This command performs certain operations on all repositories
|
| ︙ | ︙ | |||
427 428 429 430 431 432 433 |
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server settings sync ui unset whatis");
}
verify_all_options();
| > | > > > > > > > > > > > > > > > | | < | > > > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server settings sync ui unset whatis");
}
verify_all_options();
db_multi_exec(
"CREATE TEMP TABLE repolist(\n"
" name TEXT, -- Filename\n"
" tag TEXT, -- Key for the GLOBAL_CONFIG table entry\n"
" inode TEXT -- Unique identifier for this file\n"
");\n"
/* The seenFile() table holds inode names for entries that have
** already been processed. */
"CREATE TEMP TABLE seenFile(x TEXT COLLATE nocase);\n"
/* The toDel() table holds the "tag" for entries that need to be
** deleted because they are redundant or no longer exist */
"CREATE TEMP TABLE toDel(x TEXT);\n"
);
sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func, 0, 0);
if( useCheckouts ){
db_multi_exec(
"INSERT INTO repolist "
"SELECT substr(name, 7), name, inode(substr(name,7))"
" FROM global_config"
" WHERE substr(name, 1, 6)=='ckout:'"
" ORDER BY 1"
);
}else{
db_multi_exec(
"INSERT INTO repolist "
"SELECT substr(name, 6), name, inode(substr(name,6))"
" FROM global_config"
" WHERE substr(name, 1, 5)=='repo:'"
" ORDER BY 1"
);
}
db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1");
while( db_step(&q)==SQLITE_ROW ){
int rc;
const char *zFilename = db_column_text(&q, 0);
const char *zInode = db_column_text(&q,2);
#if !USE_SEE
if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue;
#endif
if( file_access(zFilename, F_OK)
|| !file_is_canonical(zFilename)
|| (useCheckouts && file_isdir(zFilename, ExtFILE)!=1)
|| db_exists("SELECT 1 FROM temp.seenFile where x=%Q", zInode)
){
db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1));
nToDel++;
continue;
}
db_multi_exec("INSERT INTO seenFile(x) VALUES(%Q)", zInode);
if( zCmd[0]=='l' ){
fossil_print("%s\n", zFilename);
continue;
}else if( showFile ){
fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository",
zFilename);
}
|
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
632 633 634 635 636 637 638 | } /* ** Output HTML to show a list of attachments. */ void attachment_list( const char *zTarget, /* Object that things are attached to */ | | > > > > > > > | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 |
}
/*
** Output HTML to show a list of attachments.
*/
void attachment_list(
const char *zTarget, /* Object that things are attached to */
const char *zHeader, /* Header to display with attachments */
int fHorizontalRule /* Insert <hr> separator above header */
){
int cnt = 0;
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,toLocal()), filename, user,"
" (SELECT uuid FROM blob WHERE rid=attachid), src"
" FROM attachment"
" WHERE isLatest AND src!='' AND target=%Q"
" ORDER BY mtime DESC",
zTarget
);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
const char *zUuid = db_column_text(&q, 3);
const char *zSrc = db_column_text(&q, 4);
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
if( cnt==0 ){
@ <section class='attachlist'>
if( fHorizontalRule ){
@ <hr>
}
@ %s(zHeader)
@ <ul>
}
cnt++;
@ <li>
@ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a>
@ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</a>]
@ added by %h(zDispUser) on
hyperlink_to_date(zDate, ".");
@ [%z(href("%R/ainfo/%!S",zUuid))details</a>]
@ </li>
}
if( cnt ){
@ </ul>
@ </section>
}
db_finalize(&q);
}
/*
** COMMAND: attachment*
|
| ︙ | ︙ |
Changes to src/bisect.c.
| ︙ | ︙ | |||
541 542 543 544 545 546 547 | ** be done, for example, because VERSION does not compile correctly ** or is otherwise unsuitable to participate in this bisect. ** ** > fossil bisect vlist|ls|status ?-a|--all? ** ** List the versions in between the inner-most "bad" and "good". ** | | | > > | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
** be done, for example, because VERSION does not compile correctly
** or is otherwise unsuitable to participate in this bisect.
**
** > fossil bisect vlist|ls|status ?-a|--all?
**
** List the versions in between the inner-most "bad" and "good".
**
** > fossil bisect ui ?HOST@USER:PATH?
**
** Like "fossil ui" except start on a timeline that shows only the
** check-ins that are part of the current bisect. If the optional
** fourth term is added, then information is shown for the bisect that
** occurred in the PATH directory by USER on remote machine HOST.
**
** > fossil bisect undo
**
** Undo the most recent "good", "bad", or "skip" command.
*/
void bisect_cmd(void){
int n;
|
| ︙ | ︙ | |||
717 718 719 720 721 722 723 724 725 726 727 |
}else{
usage("options ?NAME? ?VALUE?");
}
}else if( strncmp(zCmd, "reset", n)==0 ){
bisect_reset();
}else if( strcmp(zCmd, "ui")==0 ){
char *newArgv[8];
newArgv[0] = g.argv[0];
newArgv[1] = "ui";
newArgv[2] = "--page";
newArgv[3] = "timeline?bisect";
| > > | | > | > > > | 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 |
}else{
usage("options ?NAME? ?VALUE?");
}
}else if( strncmp(zCmd, "reset", n)==0 ){
bisect_reset();
}else if( strcmp(zCmd, "ui")==0 ){
char *newArgv[8];
verify_all_options();
newArgv[0] = g.argv[0];
newArgv[1] = "ui";
newArgv[2] = "--page";
newArgv[3] = "timeline?bisect";
if( g.argc==4 ){
newArgv[4] = g.argv[3];
g.argc = 5;
}else{
g.argc = 4;
}
newArgv[g.argc] = 0;
g.argv = newArgv;
cmd_webserver();
}else if( strncmp(zCmd, "vlist", n)==0
|| strncmp(zCmd, "ls", n)==0
|| strncmp(zCmd, "status", n)==0
){
int fAll = find_option("all", "a", 0)!=0;
bisect_list(!fAll);
}else if( !foundCmd ){
usage:
usage("bad|good|log|chart|next|options|reset|run|skip|status|ui|undo");
}
}
|
Changes to src/blob.c.
| ︙ | ︙ | |||
663 664 665 666 667 668 669 |
void blob_dehttpize(Blob *pBlob){
blob_materialize(pBlob);
pBlob->nUsed = dehttpize(pBlob->aData);
}
/*
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
| | > | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
void blob_dehttpize(Blob *pBlob){
blob_materialize(pBlob);
pBlob->nUsed = dehttpize(pBlob->aData);
}
/*
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
** Return the actual number of bytes extracted. The cursor position
** is advanced by the number of bytes extracted.
**
** After this call completes, pTo will be an ephemeral blob.
*/
int blob_extract(Blob *pFrom, int N, Blob *pTo){
blob_is_init(pFrom);
assert_blob_is_reset(pTo);
if( pFrom->iCursor + N > pFrom->nUsed ){
|
| ︙ | ︙ | |||
685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
pTo->nAlloc = N;
pTo->aData = &pFrom->aData[pFrom->iCursor];
pTo->iCursor = 0;
pTo->xRealloc = blobReallocStatic;
pFrom->iCursor += N;
return N;
}
/*
** Rewind the cursor on a blob back to the beginning.
*/
void blob_rewind(Blob *p){
p->iCursor = 0;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 |
pTo->nAlloc = N;
pTo->aData = &pFrom->aData[pFrom->iCursor];
pTo->iCursor = 0;
pTo->xRealloc = blobReallocStatic;
pFrom->iCursor += N;
return N;
}
/*
** Extract N **lines** of text from blob pFrom beginning at the current
** cursor position and use that text to initialize blob pTo. Unlike the
** blob_extract() routine, the cursor position is unchanged.
**
** pTo is assumed to be uninitialized.
**
** After this call completes, pTo will be an ephemeral blob.
*/
int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){
int i;
int mx;
int iStart;
int n;
const char *z;
blob_zero(pTo);
z = pFrom->aData;
i = pFrom->iCursor;
mx = pFrom->nUsed;
while( N>0 ){
while( i<mx && z[i]!='\n' ){ i++; }
if( i>=mx ) break;
i++;
N--;
}
iStart = pFrom->iCursor;
n = blob_extract(pFrom, i-pFrom->iCursor, pTo);
pFrom->iCursor = iStart;
return n;
}
/*
** Return the number of lines of text in the blob. If the last
** line is incomplete (if it does not have a \n at the end) then
** it still counts.
*/
int blob_linecount(Blob *p){
int n = 0;
int i;
for(i=0; i<p->nUsed; i++){
if( p->aData[i]=='\n' ) n++;
}
if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++;
return n;
}
/*
** Rewind the cursor on a blob back to the beginning.
*/
void blob_rewind(Blob *p){
p->iCursor = 0;
}
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
638 639 640 641 642 643 644 | ** -c|--closed List closed branches ** -m|--merged List branches merged into the current branch ** -M|--unmerged List branches not merged into the current branch ** -p List only private branches ** -r Reverse the sort order ** -t Show recently changed branches first ** --self List only branches where you participate | | | | > | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | ** -c|--closed List closed branches ** -m|--merged List branches merged into the current branch ** -M|--unmerged List branches not merged into the current branch ** -p List only private branches ** -r Reverse the sort order ** -t Show recently changed branches first ** --self List only branches where you participate ** --username USER List only branches where USER participates ** --users N List up to N users participating ** ** The current branch is marked with an asterisk. Private branches are ** marked with a hash sign. ** ** If GLOB is given, show only branches matching the pattern. ** ** The "lsh" variant of this subcommand shows recently changed branches, ** and accepts an optional LIMIT argument (defaults to 5) to cap output, ** but no GLOB argument. All other options are supported, with -t being ** an implied no-op. ** ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? ** ** Create a new branch BRANCH-NAME off of check-in BASIS. ** ** Options: ** --private Branch is private (i.e., remains local) ** --bgcolor COLOR Use COLOR instead of automatic background ** --nosign Do not sign the manifest for the check-in ** that creates this branch ** --nosync Do not auto-sync prior to creating the branch ** --date-override DATE DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be ** replaced by a space, and it may also name a timezone offset |
| ︙ | ︙ |
Changes to src/browse.c.
| ︙ | ︙ | |||
1256 1257 1258 1259 1260 1261 1262 |
fossil_free(zAge);
}
@ </table></div>
db_finalize(&q1);
db_finalize(&q2);
style_finish_page();
}
| < < < < < < < < < < < < < < < < < | 1256 1257 1258 1259 1260 1261 1262 |
fossil_free(zAge);
}
@ </table></div>
db_finalize(&q1);
db_finalize(&q2);
style_finish_page();
}
|
Changes to src/builtin.c.
| ︙ | ︙ | |||
716 717 718 719 720 721 722 |
** entries: all known deps of this one. Each
** REQUIRES an EXPLICIT trailing \0, including
** the final one! */
} fjs[] = {
/* This list ordering isn't strictly important. */
{"confirmer", 0, 0},
{"copybutton", 0, "dom\0"},
| | > > | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
** entries: all known deps of this one. Each
** REQUIRES an EXPLICIT trailing \0, including
** the final one! */
} fjs[] = {
/* This list ordering isn't strictly important. */
{"confirmer", 0, 0},
{"copybutton", 0, "dom\0"},
{"diff", 0, "dom\0fetch\0storage\0"
/* maintenance note: "diff" needs "storage" for storing the the
** sbs-sync-scroll toggle. */},
{"dom", 0, 0},
{"fetch", 0, 0},
{"numbered-lines", 0, "popupwidget\0copybutton\0"},
{"pikchr", 0, "dom\0"},
{"popupwidget", 0, "dom\0"},
{"storage", 0, 0},
{"tabs", 0, "dom\0"}
|
| ︙ | ︙ |
Changes to src/chat.c.
| ︙ | ︙ | |||
1135 1136 1137 1138 1139 1140 1141 |
zUser, zUuid, zUser,
zBranch, zUuid, zBranch
);
fossil_free(zBranch);
fossil_free(zUuid);
}else if( zType[0]=='w' ){
/* Wiki page changes */
| | < > > > > > > > | 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 |
zUser, zUuid, zUser,
zBranch, zUuid, zBranch
);
fossil_free(zBranch);
fossil_free(zUuid);
}else if( zType[0]=='w' ){
/* Wiki page changes */
char *zUuid = rid_to_uuid(rid);
wiki_hyperlink_override(zUuid);
if( zMsg[0]=='-' ){
zRes = mprintf("Delete wiki page <a href='%R/whistory?name=%t'>%h</a>",
zMsg+1, zMsg+1);
}else if( zMsg[0]=='+' ){
zRes = mprintf("Added wiki page <a href='%R/whistory?name=%t'>%h</a>",
zMsg+1, zMsg+1);
}else if( zMsg[0]==':' ){
zRes = mprintf("<a href='%R/wdiff?id=%!S'>Changes</a> to wiki page "
"<a href='%R/whistory?name=%t'>%h</a>",
zUuid, zMsg+1, zMsg+1);
}else{
zRes = mprintf("%W", zMsg);
}
wiki_hyperlink_override(0);
fossil_free(zUuid);
}else if( zType[0]=='f' ){
/* Forum changes */
char *zUuid = rid_to_uuid(rid);
zRes = mprintf( "%W (artifact: <a href='%R/info/%S'>%S</a>, "
"user: <a href='%R/timeline?u=%t&c=%S'>%h</a>)",
zMsg, zUuid, zUuid, zUser, zUuid, zUser);
fossil_free(zUuid);
}else{
/* Anything else */
zRes = mprintf("%W", zMsg);
}
if( zRes ){
sqlite3_result_text(context, zRes, -1, fossil_free);
}
|
| ︙ | ︙ | |||
1194 1195 1196 1197 1198 1199 1200 | ** Copy chat content from the server down into the local clone, ** as a backup or archive. Setup privilege is required on the server. ** ** --all Download all chat content. Normally only ** previously undownloaded content is retrieved. ** --debug Additional debugging output ** --out DATABASE Store CHAT table in separate database file | | | 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | ** Copy chat content from the server down into the local clone, ** as a backup or archive. Setup privilege is required on the server. ** ** --all Download all chat content. Normally only ** previously undownloaded content is retrieved. ** --debug Additional debugging output ** --out DATABASE Store CHAT table in separate database file ** DATABASE rather than adding to local clone ** --unsafe Allow the use of unencrypted http:// ** ** > fossil chat send [ARGUMENTS] ** ** This command sends a new message to the chatroom. The message ** to be sent is determined by arguments as follows: ** |
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
428 429 430 431 432 433 434 435 436 437 438 439 440 441 | ** If both --merge and --no-merge are used, --no-merge has priority. The ** same is true of --classify and --no-classify. ** ** The "fossil changes --extra" command is equivalent to "fossil extras". ** ** General options: ** --abs-paths Display absolute pathnames ** --rel-paths Display pathnames relative to the current working ** directory ** --hash Verify file status using hashing rather than ** relying on file mtimes ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include unmanaged files beginning with a dot ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns | > | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | ** If both --merge and --no-merge are used, --no-merge has priority. The ** same is true of --classify and --no-classify. ** ** The "fossil changes --extra" command is equivalent to "fossil extras". ** ** General options: ** --abs-paths Display absolute pathnames ** -b|--brief Show a single keyword for the status ** --rel-paths Display pathnames relative to the current working ** directory ** --hash Verify file status using hashing rather than ** relying on file mtimes ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include unmanaged files beginning with a dot ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns |
| ︙ | ︙ | |||
491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
int verboseFlag = command==CHANGES && find_option("verbose", "v", 0);
const char *zIgnoreFlag = find_option("ignore", 0, 1);
unsigned scanFlags = 0;
unsigned flags = 0;
int vid, i;
fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown");
/* Load affirmative flag options. */
for( i=0; i<count(flagDefs); ++i ){
if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
&& find_option(flagDefs[i].option, 0, 0) ){
flags |= flagDefs[i].mask;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
int verboseFlag = command==CHANGES && find_option("verbose", "v", 0);
const char *zIgnoreFlag = find_option("ignore", 0, 1);
unsigned scanFlags = 0;
unsigned flags = 0;
int vid, i;
fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown");
if( find_option("brief","b",0) ){
/* The --brief or -b option is special. It cannot be used with any
** other options. It outputs a single keyword which indicates the
** fossil status, for use by shell scripts. The output might be
** one of:
**
** clean The current working directory is within an
** unmodified fossil check-out.
**
** dirty The pwd is within a fossil check-out that has
** uncommitted changes
**
** none The pwd is not within a fossil check-out.
*/
int chnged;
if( g.argc>2 ){
fossil_fatal("No other arguments or options may occur with --brief");
}
if( db_open_local(0)==0 ){
fossil_print("none\n");
return;
}
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 0);
chnged = db_int(0,
"SELECT 1 FROM vfile"
" WHERE vid=%d"
" AND (chnged>0 OR deleted OR rid==0)",
vid
);
if( chnged ){
fossil_print("dirty\n");
}else{
fossil_print("clean\n");
}
return;
}
/* Load affirmative flag options. */
for( i=0; i<count(flagDefs); ++i ){
if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
&& find_option(flagDefs[i].option, 0, 0) ){
flags |= flagDefs[i].mask;
}
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 |
flags &= ~noFlagDefs[i].mask;
}
}
/* Confirm current working directory is within check-out. */
db_must_be_within_tree();
| | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
flags &= ~noFlagDefs[i].mask;
}
}
/* Confirm current working directory is within check-out. */
db_must_be_within_tree();
/* Get check-out version. */
vid = db_lget_int("checkout", 0);
/* Relative path flag determination is done by a shared function. */
if( determine_cwd_relative_option() ){
flags |= C_RELPATH;
}
|
| ︙ | ︙ | |||
926 927 928 929 930 931 932 | } /* ** COMMAND: tree ** ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? ** | | | | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 | } /* ** COMMAND: tree ** ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? ** ** List all files in the current check-out much like the "tree" ** command does. If PATHS is included, only the named files ** (or their children if directories) are shown. ** ** Options: ** -r VERSION The specific check-in to list ** -R|--repository REPO Extract info from repository REPO ** ** See also: [[ls]] |
| ︙ | ︙ | |||
2277 2278 2279 2280 2281 2282 2283 | ** background color for a single check-in. Subsequent check-ins revert ** to the default color. ** ** A check-in is not permitted to fork unless the --allow-fork option ** appears. An empty check-in (i.e. with nothing changed) is not ** allowed unless the --allow-empty option appears. A check-in may not ** be older than its ancestor unless the --allow-older option appears. | | | 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 | ** background color for a single check-in. Subsequent check-ins revert ** to the default color. ** ** A check-in is not permitted to fork unless the --allow-fork option ** appears. An empty check-in (i.e. with nothing changed) is not ** allowed unless the --allow-empty option appears. A check-in may not ** be older than its ancestor unless the --allow-older option appears. ** If any files in the check-in appear to contain unresolved merge ** conflicts, the check-in will not be allowed unless the ** --allow-conflict option is present. In addition, the entire ** check-in process may be aborted if a file contains content that ** appears to be binary, Unicode text, or text with CR/LF line endings ** unless the interactive user chooses to proceed. If there is no ** interactive user or these warnings should be skipped for some other ** reason, the --no-warnings option may be used. A check-in is not |
| ︙ | ︙ | |||
2317 2318 2319 2320 2321 2322 2323 | ** --delta Use a delta manifest in the commit process ** --hash Verify file status using hashing rather ** than relying on file mtimes ** --ignore-clock-skew If a clock skew is detected, ignore it and ** behave as if the user had entered 'yes' to ** the question of whether to proceed despite ** the skew. | | | 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 | ** --delta Use a delta manifest in the commit process ** --hash Verify file status using hashing rather ** than relying on file mtimes ** --ignore-clock-skew If a clock skew is detected, ignore it and ** behave as if the user had entered 'yes' to ** the question of whether to proceed despite ** the skew. ** --ignore-oversize Do not warn the user about oversized files ** --integrate Close all merged-in branches ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment ** -M|--message-file FILE Read the commit comment from given file ** --mimetype MIMETYPE Mimetype of check-in comment ** -n|--dry-run If given, display instead of run actions ** -v|--verbose Show a diff in the commit message prompt ** --no-prompt This option disables prompting the user for |
| ︙ | ︙ | |||
2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 |
int nConflict = 0; /* Number of unresolved merge conflicts */
int abortCommit = 0; /* Abort the commit due to text format conversions */
Blob ans; /* Answer to continuation prompts */
char cReply; /* First character of ans */
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
int mxSize;
memset(&sCiInfo, 0, sizeof(sCiInfo));
url_proxy_options();
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
noSign = find_option("nosign",0,0)!=0;
if( find_option("nosync",0,0) ) g.fNoSync = 1;
| > > | 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 |
int nConflict = 0; /* Number of unresolved merge conflicts */
int abortCommit = 0; /* Abort the commit due to text format conversions */
Blob ans; /* Answer to continuation prompts */
char cReply; /* First character of ans */
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
int mxSize;
char *zCurBranch = 0; /* The current branch name of checkout */
char *zNewBranch = 0; /* The branch name after update */
memset(&sCiInfo, 0, sizeof(sCiInfo));
url_proxy_options();
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
noSign = find_option("nosign",0,0)!=0;
if( find_option("nosync",0,0) ) g.fNoSync = 1;
|
| ︙ | ︙ | |||
2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 |
if( privateFlag==0 && sCiInfo.zBranch==0 ) {
sCiInfo.zBranch=db_get("main-branch", 0);
}
}else{
privateParent = content_is_private(vid);
}
/* Track the "private" status */
g.markPrivate = privateFlag || privateParent;
if( privateFlag && !privateParent ){
/* Apply default branch name ("private") and color ("orange") if not
** specified otherwise on the command-line, and if the parent is not
** already private. */
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 |
if( privateFlag==0 && sCiInfo.zBranch==0 ) {
sCiInfo.zBranch=db_get("main-branch", 0);
}
}else{
privateParent = content_is_private(vid);
}
user_select();
/*
** Check that the user exists.
*/
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
fossil_fatal("no such user: %s", g.zLogin);
}
/*
** Detect if the branch name has changed from the parent check-in
** and prompt if necessary
**/
zCurBranch = db_text(0,
" SELECT value FROM tagxref AS tx"
" WHERE rid=(SELECT pid"
" FROM tagxref LEFT JOIN event ON srcid=objid"
" LEFT JOIN plink ON rid=cid"
" WHERE rid=%d AND tagxref.tagid=%d"
" AND srcid!=origid"
" AND tagtype=2 AND coalesce(euser,user)!=%Q)"
" AND tx.tagid=%d",
vid, TAG_BRANCH, g.zLogin, TAG_BRANCH
);
if( zCurBranch!=0 && zCurBranch[0]!=0
&& forceFlag==0
&& noPrompt==0
){
zNewBranch = branch_of_rid(vid);
fossil_warning(
"WARNING: The parent check-in [%.10s] has been moved from branch\n"
" '%s' over to branch '%s'.",
rid_to_uuid(vid), zCurBranch, zNewBranch
);
prompt_user("Commit anyway? (y/N) ", &ans);
cReply = blob_str(&ans)[0];
blob_reset(&ans);
if( cReply!='y' && cReply!='Y' ){
fossil_fatal("Abandoning commit because branch has changed");
}
fossil_free(zNewBranch);
fossil_free(zCurBranch);
zCurBranch = branch_of_rid(vid);
}
if( zCurBranch==0 ) zCurBranch = branch_of_rid(vid);
/* Track the "private" status */
g.markPrivate = privateFlag || privateParent;
if( privateFlag && !privateParent ){
/* Apply default branch name ("private") and color ("orange") if not
** specified otherwise on the command-line, and if the parent is not
** already private. */
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
|
| ︙ | ︙ | |||
2519 2520 2521 2522 2523 2524 2525 | ** ** The forbid-delta-manifests setting prevents new delta manifests. ** ** If the remote repository sent an avoid-delta-manifests pragma on ** the autosync above, then also try to avoid deltas, unless the ** --delta option is specified. The remote repo will send the ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" | | | 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 |
**
** The forbid-delta-manifests setting prevents new delta manifests.
**
** If the remote repository sent an avoid-delta-manifests pragma on
** the autosync above, then also try to avoid deltas, unless the
** --delta option is specified. The remote repo will send the
** avoid-delta-manifests pragma if it has its "forbid-delta-manifests"
** setting enabled.
*/
if( !db_get_boolean("seen-delta-manifest",0)
|| db_get_boolean("forbid-delta-manifests",0)
|| g.bAvoidDeltaManifests
){
if( !forceDelta ) forceBaseline = 1;
}
|
| ︙ | ︙ | |||
2598 2599 2600 2601 2602 2603 2604 |
const char *zTo = db_column_text(&q, 1);
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
}
db_finalize(&q);
}
| < < < < < < < < | 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 |
const char *zTo = db_column_text(&q, 1);
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
}
db_finalize(&q);
}
hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0);
db_begin_transaction();
db_record_repository_filename(0);
if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
fossil_fatal("nothing has changed; use --allow-empty to override");
}
|
| ︙ | ︙ | |||
2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 |
&& (sCiInfo.zBranch==0
|| db_exists("SELECT 1 FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0"
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
){
fossil_fatal("cannot commit against a closed leaf");
}
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Get the check-in comment. This might involve prompting the
** user for the check-in comment, in which case we should resync
| > > > > > > > > > > > > > > > > > > > > > > | 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 |
&& (sCiInfo.zBranch==0
|| db_exists("SELECT 1 FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0"
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
){
fossil_fatal("cannot commit against a closed leaf");
}
/* Require confirmation to continue with the check-in if the branch
** has changed and the committer did not provide the same branch
*/
zNewBranch = branch_of_rid(vid);
if( fossil_strcmp(zCurBranch, zNewBranch)!=0
&& fossil_strcmp(sCiInfo.zBranch, zNewBranch)!=0
&& forceFlag==0
&& noPrompt==0
){
fossil_warning("parent check-in [%.10s] branch changed from '%s' to '%s'",
rid_to_uuid(vid), zCurBranch, zNewBranch);
prompt_user("continue (y/N)? ", &ans);
cReply = blob_str(&ans)[0];
blob_reset(&ans);
if( cReply!='y' && cReply!='Y' ){
fossil_fatal("Abandoning commit because branch has changed");
}
fossil_free(zCurBranch);
zCurBranch = branch_of_rid(vid);
}
fossil_free(zNewBranch);
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Get the check-in comment. This might involve prompting the
** user for the check-in comment, in which case we should resync
|
| ︙ | ︙ |
Changes to src/comformat.c.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */ #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */ #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ #endif /* ** This is the previous value used by most external callers when they ** needed to specify a default maximum line length to be used with the ** comment_print() function. */ #ifndef COMMENT_LEGACY_LINE_LENGTH # define COMMENT_LEGACY_LINE_LENGTH (78) | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
#define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
#define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
#define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */
#define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */
#define COMMENT_PRINT_UNSET (-1) /* Not initialized. */
#endif
/********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/
/* Lookup table to estimate the number of columns consumed by a Unicode
** character.
*/
static const struct {
unsigned char w; /* Width of the character in columns */
int iFirst; /* First character in a span having this width */
} aUWidth[] = {
/* {1, 0x00000}, */
{0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488},
{1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0},
{0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7},
{1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616},
{0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6},
{1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee},
{0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730},
{1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4},
{0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941},
{1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955},
{0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc},
{1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce},
{0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c},
{1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49},
{0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81},
{1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6},
{0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2},
{1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d},
{0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d},
{1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83},
{0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e},
{1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e},
{0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf},
{1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce},
{0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d},
{1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5},
{0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34},
{1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2},
{0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8},
{1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36},
{0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71},
{1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88},
{0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6},
{1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033},
{0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058},
{1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f},
{1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735},
{0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4},
{1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7},
{0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b},
{1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923},
{0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939},
{1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04},
{0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c},
{1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74},
{0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b},
{1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064},
{0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329},
{1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f},
{2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806},
{1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827},
{2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e},
{1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20},
{1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00},
{1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc},
{0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c},
{1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40},
{0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185},
{1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245},
{2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001},
{1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0}
};
/*
** Return an estimate of the width, in columns, for the single Unicode
** character c. For normal characters, the answer is always 1. But the
** estimate might be 0 or 2 for zero-width and double-width characters.
**
** Different display devices display unicode using different widths. So
** it is impossible to know that true display width with 100% accuracy.
** Inaccuracies in the width estimates might cause columns to be misaligned.
** Unfortunately, there is nothing we can do about that.
*/
static int cli_wcwidth(int c){
int iFirst, iLast;
/* Fast path for common characters */
if( c<=0x300 ) return 1;
/* The general case */
iFirst = 0;
iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1;
while( iFirst<iLast-1 ){
int iMid = (iFirst+iLast)/2;
int cMid = aUWidth[iMid].iFirst;
if( cMid < c ){
iFirst = iMid;
}else if( cMid > c ){
iLast = iMid - 1;
}else{
return aUWidth[iMid].w;
}
}
if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w;
return aUWidth[iLast].w;
}
/******* End of code copied from SQLite *************************************/
/*
** This is the previous value used by most external callers when they
** needed to specify a default maximum line length to be used with the
** comment_print() function.
*/
#ifndef COMMENT_LEGACY_LINE_LENGTH
# define COMMENT_LEGACY_LINE_LENGTH (78)
|
| ︙ | ︙ | |||
106 107 108 109 110 111 112 | ** initial index and returns the index of the next spacing character -OR- ** zero if such a character cannot be found. For the purposes of this ** algorithm, the NUL character is treated the same as a spacing character. */ static int comment_next_space( const char *zLine, /* [in] The comment line being printed. */ int index, /* [in] The current character index being handled. */ | > | > | < | < > > | | < < < | < < < > > | | | | | < < > > > | > > > | > > > > > > > > > > > > > > > > > | > > | | | < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
** initial index and returns the index of the next spacing character -OR-
** zero if such a character cannot be found. For the purposes of this
** algorithm, the NUL character is treated the same as a spacing character.
*/
static int comment_next_space(
const char *zLine, /* [in] The comment line being printed. */
int index, /* [in] The current character index being handled. */
int maxChars, /* [in] Optimization hint to abort before space found. */
int *sumWidth /* [out] Summated width of all characters to next space. */
){
int cchUTF8, utf32, wcwidth = 0;
int nextIndex = index;
for(;;){
char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32);
nextIndex += cchUTF8;
wcwidth += cli_wcwidth(utf32);
if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) ||
wcwidth>maxChars ){
*sumWidth = wcwidth;
return nextIndex;
}
}
return 0; /* NOT REACHED */
}
/*
** Return information about the next (single- or multi-byte) character in the
** specified UTF-8 string: The number of UTF-8 code units (in this case: bytes)
** and the decoded UTF-32 code point. Incomplete, ill-formed and overlong
** sequences are consumed together as one invalid code point. The invalid lead
** bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2-
** and 4-byte sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF
** are treated as invalid 1-byte sequences (as lone trail bytes), all resulting
** in one invalid code point. Invalid UTF-8 sequences encoding a non-scalar code
** point (UTF-16 surrogates U+D800 to U+DFFF) are allowed.
*/
void char_info_utf8(
const char *z,
int *pCchUTF8,
int *pUtf32
){
int i = 0; /* Counted bytes. */
int cchUTF8 = 1; /* Code units consumed. */
int maxUTF8 = 1; /* Expected sequence length. */
char c = z[i++];
if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */
*pCchUTF8 = 1;
*pUtf32 = (int)z[0];
return;
}
else if( (c&0xe0)==0xc0 ) maxUTF8 = 2; /* UTF-8 lead byte 110vvvvv */
else if( (c&0xf0)==0xe0 ) maxUTF8 = 3; /* UTF-8 lead byte 1110vvvv */
else if( (c&0xf8)==0xf0 ) maxUTF8 = 4; /* UTF-8 lead byte 11110vvv */
while( cchUTF8<maxUTF8 &&
(z[i]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */
cchUTF8++;
i++;
}
*pCchUTF8 = cchUTF8;
if( cchUTF8!=maxUTF8 || /* Incomplete UTF-8 sequence. */
( cchUTF8==1 && (c&0x80)==0x80 )){ /* Lone UTF-8 trail byte. */
*pUtf32 = 0xfffd; /* U+FFFD Replacement Character */
#ifdef FOSSIL_DEBUG
assert( *pUtf32!=0xfffd ); /* Invalid UTF-8 sequence. */
#endif
return;
}
switch( cchUTF8 ){
case 4:
*pUtf32 =
( (z[0] & 0x0f)<<18 ) |
( (z[1] & 0x3f)<<12 ) |
( (z[2] & 0x3f)<< 6 ) |
( (z[4] & 0x3f)<< 0 ) ;
break;
case 3:
*pUtf32 =
( (z[0] & 0x0f)<<12 ) |
( (z[1] & 0x3f)<< 6 ) |
( (z[2] & 0x3f)<< 0 ) ;
break;
case 2:
*pUtf32 =
( (z[0] & 0x1f)<< 6 ) |
( (z[1] & 0x3f)<< 0 ) ;
break;
default:
*pUtf32 = 0xfffd; /* U+FFFD Replacement Character */
break;
}
#ifdef FOSSIL_DEBUG
assert(
*pUtf32>=0 && *pUtf32<=0x10ffff && /* Valid range U+0000 to U+10FFFF. */
*pUtf32<0xd800 && *pUtf32>0xdfff /* Non-scalar (UTF-16 surrogates). */
);
#endif
}
/*
** This function is called when printing a logical comment line to calculate
** the necessary indenting. The caller needs to emit the indenting spaces.
*/
static void comment_calc_indent(
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
int wordBreak, /* [in] Non-zero to try breaking on word boundaries. */
int origBreak, /* [in] Non-zero to break before original comment. */
int *pLineCnt, /* [in/out] Pointer to the total line count. */
const char **pzLine /* [out] Pointer to the end of the logical line. */
){
int index = 0, charCnt = 0, lineCnt = 0, maxChars, i;
char zBuf[400]; int iBuf=0; /* Output buffer and counter. */
| < | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
int wordBreak, /* [in] Non-zero to try breaking on word boundaries. */
int origBreak, /* [in] Non-zero to break before original comment. */
int *pLineCnt, /* [in/out] Pointer to the total line count. */
const char **pzLine /* [out] Pointer to the end of the logical line. */
){
int index = 0, charCnt = 0, lineCnt = 0, maxChars, i;
char zBuf[400]; int iBuf=0; /* Output buffer and counter. */
if( !zLine ) return;
if( lineChars<=0 ) return;
#if 0
assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */
assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */
#endif
if( indent>(int)sizeof(zBuf)-6 ){
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
}
if( origIndent>(int)sizeof(zBuf)-6 ){
/* Limit line indent to fit output buffer. */
origIndent = sizeof(zBuf)-6;
}
maxChars = lineChars;
for(;;){
int useChars = 1;
char c = zLine[index];
/* Flush the output buffer if there's no space left for at least one more
** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces,
** a new line, and a terminating NULL. */
if( iBuf>(int)sizeof(zBuf)-origIndent-6 ){
zBuf[iBuf]=0;
| > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
}
if( origIndent>(int)sizeof(zBuf)-6 ){
/* Limit line indent to fit output buffer. */
origIndent = sizeof(zBuf)-6;
}
maxChars = lineChars;
for(;;){
int cchUTF8, utf32;
int useChars = 1;
char c = zLine[index];
/* Flush the output buffer if there's no space left for at least one more
** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces,
** a new line, and a terminating NULL. */
if( iBuf>(int)sizeof(zBuf)-origIndent-6 ){
zBuf[iBuf]=0;
|
| ︙ | ︙ | |||
258 259 260 261 262 263 264 |
index++;
}
if( c=='\n' ){
lineCnt++;
charCnt = 0;
useChars = 0;
}else if( c=='\t' ){
| | | | | | | < | < < < < | > > > | > > > > | | > > | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
index++;
}
if( c=='\n' ){
lineCnt++;
charCnt = 0;
useChars = 0;
}else if( c=='\t' ){
int sumWidth;
int nextIndex = comment_next_space(zLine, index, maxChars, &sumWidth);
if( nextIndex<=0 || sumWidth>maxChars ){
break;
}
charCnt++;
useChars = COMMENT_TAB_WIDTH;
if( maxChars<useChars ){
zBuf[iBuf++] = ' ';
break;
}
}else if( wordBreak && fossil_isspace(c) ){
int sumWidth;
int nextIndex = comment_next_space(zLine, index, maxChars, &sumWidth);
if( nextIndex<=0 || sumWidth>=maxChars ){
break;
}
charCnt++;
}else{
charCnt++;
}
assert( c!='\n' || charCnt==0 );
zBuf[iBuf++] = c;
char_info_utf8(&zLine[index-1],&cchUTF8,&utf32);
if( cchUTF8>1 ){
int wcwidth;
wcwidth = cli_wcwidth(utf32);
if( wcwidth>maxChars && lineChars>=wcwidth ){ /* rollback */
index--;
iBuf--;
zBuf[iBuf] = 0;
break;
}
for( ; cchUTF8>1; cchUTF8-- ){
zBuf[iBuf++] = zLine[index++];
}
useChars += wcwidth - 1;
}
maxChars -= useChars;
if( maxChars<=0 ) break;
if( c=='\n' ) break;
}
if( charCnt>0 ){
zBuf[iBuf++] = '\n';
|
| ︙ | ︙ | |||
336 337 338 339 340 341 342 |
){
int maxChars = width - indent;
int si, sk, i, k, kc;
int doIndent = 0;
char *zBuf;
char zBuffer[400];
int lineCnt = 0;
| < | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
){
int maxChars = width - indent;
int si, sk, i, k, kc;
int doIndent = 0;
char *zBuf;
char zBuffer[400];
int lineCnt = 0;
if( width<0 ){
comment_set_maxchars(indent, &maxChars);
}
if( zText==0 ) zText = "(NULL)";
if( maxChars<=0 ){
maxChars = strlen(zText);
|
| ︙ | ︙ | |||
362 363 364 365 366 367 368 369 370 |
fossil_print("\n");
lineCnt = 1;
}
if( zBuf!=zBuffer) fossil_free(zBuf);
return lineCnt;
}
for(sk=si=i=k=kc=0; zText[i] && kc<maxChars; i++){
char c = zText[i];
kc++; /* Count complete UTF-8 sequences. */
| > < | < < < < | > | < < > > > > | > | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
fossil_print("\n");
lineCnt = 1;
}
if( zBuf!=zBuffer) fossil_free(zBuf);
return lineCnt;
}
for(sk=si=i=k=kc=0; zText[i] && kc<maxChars; i++){
int cchUTF8, utf32;
char c = zText[i];
kc++; /* Count complete UTF-8 sequences. */
char_info_utf8(&zText[i],&cchUTF8,&utf32);
if( cchUTF8>1 ){
int wcwidth;
wcwidth = cli_wcwidth(utf32);
if( kc+wcwidth-1>maxChars && maxChars>=wcwidth ){ /* rollback */
kc--;
break;
}
for( i--; cchUTF8>0; cchUTF8-- ){
zBuf[k++] = zText[++i];
}
kc += wcwidth - 1;
}
else if( fossil_isspace(c) ){
si = i;
sk = k;
if( k==0 || zBuf[k-1]!=' ' ){
zBuf[k++] = ' ';
}
|
| ︙ | ︙ | |||
495 496 497 498 499 500 501 |
return lineCnt;
}
zLine = zText;
for(;;){
comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
&lineCnt, &zLine);
| | > > | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
return lineCnt;
}
zLine = zText;
for(;;){
comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
&lineCnt, &zLine);
if( zLine==0 ) break;
while( fossil_isspace(zLine[0]) ) zLine++;
if( zLine[0]==0 ) break;
}
return lineCnt;
}
/*
** Return the "COMMENT_PRINT_*" flags specified by the following sources,
** evaluated in the following cascading order:
|
| ︙ | ︙ | |||
595 596 597 598 599 600 601 602 603 604 605 606 607 608 |
}
zIndent = find_option("indent",0,1);
if( zIndent ){
indent = atoi(zIndent);
}else{
indent = -1; /* automatic */
}
if( g.argc!=4 && g.argc!=5 ){
usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?");
}
zPrefix = g.argv[2];
zText = g.argv[3];
if( g.argc==5 ){
zOrigText = g.argv[4];
| > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 |
}
zIndent = find_option("indent",0,1);
if( zIndent ){
indent = atoi(zIndent);
}else{
indent = -1; /* automatic */
}
verify_all_options();
if( g.argc!=4 && g.argc!=5 ){
usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?");
}
zPrefix = g.argv[2];
zText = g.argv[3];
if( g.argc==5 ){
zOrigText = g.argv[4];
|
| ︙ | ︙ |
Changes to src/configure.c.
| ︙ | ︙ | |||
116 117 118 119 120 121 122 |
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
{ "adunit-omit-if-user", CONFIGSET_SKIN },
{ "default-csp", CONFIGSET_SKIN },
{ "sitemap-extra", CONFIGSET_SKIN },
{ "safe-html", CONFIGSET_SKIN },
| < < < < < < < < < | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
{ "adunit-omit-if-user", CONFIGSET_SKIN },
{ "default-csp", CONFIGSET_SKIN },
{ "sitemap-extra", CONFIGSET_SKIN },
{ "safe-html", CONFIGSET_SKIN },
#ifdef FOSSIL_ENABLE_TH1_HOOKS
{ "th1-hooks", CONFIGSET_TH1 },
#endif
{ "th1-uri-regexp", CONFIGSET_TH1 },
{ "project-name", CONFIGSET_PROJ },
{ "short-project-name", CONFIGSET_PROJ },
{ "project-description", CONFIGSET_PROJ },
{ "index-page", CONFIGSET_PROJ },
{ "manifest", CONFIGSET_PROJ },
{ "binary-glob", CONFIGSET_PROJ },
{ "clean-glob", CONFIGSET_PROJ },
|
| ︙ | ︙ | |||
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
** Return the mask for the named configuration parameter if it can be
** safely exported. Return 0 if the parameter is not safe to export.
**
** "Safe" in the previous paragraph means the permission is granted to
** export the property. In other words, the requesting side has presented
** login credentials and has sufficient capabilities to access the requested
** information.
*/
int configure_is_exportable(const char *zName){
int i;
int n = strlen(zName);
if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){
zName++;
n -= 2;
}
for(i=0; i<count(aConfig); i++){
if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
int m = aConfig[i].groupMask;
if( !g.perm.Admin ){
m &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER);
}
| > > > > > > > > > > > > > > > > > > | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
** Return the mask for the named configuration parameter if it can be
** safely exported. Return 0 if the parameter is not safe to export.
**
** "Safe" in the previous paragraph means the permission is granted to
** export the property. In other words, the requesting side has presented
** login credentials and has sufficient capabilities to access the requested
** information.
**
** Settings which are specifically flagged as sensitive will (as of
** 2024-10-15) cause this function to return 0, regardless of user
** permissions. As an example, if the th1-setup setting were not
** sensitive then a malicious repo admin could set that to include
** arbitrary TCL code and affect users who configure fossil with the
** --with-tcl flag.
*/
int configure_is_exportable(const char *zName){
int i;
int n = strlen(zName);
Setting *pSet;
if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){
char * zCpy;
zName++;
n -= 2;
zCpy = fossil_strndup(zName, (ssize_t)n);
pSet = db_find_setting(zCpy, 0);
fossil_free(zCpy);
}else{
pSet = db_find_setting(zName, 0);
}
if( pSet && pSet->sensitive ){
/* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */
return 0;
}
for(i=0; i<count(aConfig); i++){
if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
int m = aConfig[i].groupMask;
if( !g.perm.Admin ){
m &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER);
}
|
| ︙ | ︙ | |||
412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
azToken[nToken++] = z = blob_terminate(&value);
if( !safeSql(z) ) return;
if( nToken>=count(azToken)-1 ) break;
}
if( nToken<2 ) return;
if( aType[ii].zName[0]=='/' ){
thisMask = configure_is_exportable(azToken[1]);
}else{
thisMask = configure_is_exportable(aType[ii].zName);
}
if( (thisMask & groupMask)==0 ) return;
if( (thisMask & checkMask)!=0 ){
if( (thisMask & CONFIGSET_SCRIBER)!=0 ){
alert_schema(1);
| > > > > > | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
azToken[nToken++] = z = blob_terminate(&value);
if( !safeSql(z) ) return;
if( nToken>=count(azToken)-1 ) break;
}
if( nToken<2 ) return;
if( aType[ii].zName[0]=='/' ){
thisMask = configure_is_exportable(azToken[1]);
if( 0==thisMask ){
fossil_warning("Skipping non-exportable setting: %s = %s",
azToken[1], nToken>3 ? azToken[3] : "?");
/* Will be skipped below */
}
}else{
thisMask = configure_is_exportable(aType[ii].zName);
}
if( (thisMask & groupMask)==0 ) return;
if( (thisMask & checkMask)!=0 ){
if( (thisMask & CONFIGSET_SCRIBER)!=0 ){
alert_schema(1);
|
| ︙ | ︙ | |||
679 680 681 682 683 684 685 686 687 688 689 690 691 692 |
}
db_finalize(&q);
}
db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
" WHERE name=:name AND mtime>=%lld", iStart);
for(ii=0; ii<count(aConfig); ii++){
if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
db_bind_text(&q, ":name", aConfig[ii].zName);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&rec,"%s %s value %s",
db_column_text(&q, 0),
db_column_text(&q, 1),
db_column_text(&q, 2)
);
| > > > > > | 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
}
db_finalize(&q);
}
db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
" WHERE name=:name AND mtime>=%lld", iStart);
for(ii=0; ii<count(aConfig); ii++){
if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
const Setting * pSet = db_find_setting(aConfig[ii].zName, 0);
if( pSet && pSet->sensitive ){
/* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */
continue;
}
db_bind_text(&q, ":name", aConfig[ii].zName);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&rec,"%s %s value %s",
db_column_text(&q, 0),
db_column_text(&q, 1),
db_column_text(&q, 2)
);
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 |
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
chat_msg_from_event, 0, 0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
| > > | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 |
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
chat_msg_from_event, 0, 0);
sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func,0,0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
|
| ︙ | ︙ | |||
4078 4079 4080 4081 4082 4083 4084 | ** for the repository is created with its root at the current working ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is ** specified then that version is checked out. Otherwise the most recent ** check-in on the main branch (usually "trunk") is used. ** ** REPOSITORY can be the filename for a repository that already exists on the ** local machine or it can be a URI for a remote repository. If REPOSITORY | | | 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 | ** for the repository is created with its root at the current working ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is ** specified then that version is checked out. Otherwise the most recent ** check-in on the main branch (usually "trunk") is used. ** ** REPOSITORY can be the filename for a repository that already exists on the ** local machine or it can be a URI for a remote repository. If REPOSITORY ** is a URI in one of the formats recognized by the [[clone]] command, the ** remote repo is first cloned, then the clone is opened. The clone will be ** stored in the current directory, or in DIR if the "--repodir DIR" option ** is used. The name of the clone will be taken from the last term of the URI. ** For "http:" and "https:" URIs, you can append an extra term to the end of ** the URI to get any repository name you like. For example: ** ** fossil open https://fossil-scm.org/home/new-name |
| ︙ | ︙ | |||
4679 4680 4681 4682 4683 4684 4685 | ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ | < < < < < < < | 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 | ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ /* ** SETTING: gdiff-command width=40 default=gdiff sensitive ** The value is an external command to run when performing a graphical ** diff. If undefined, text diff will be used. */ /* ** SETTING: gmerge-command width=40 sensitive |
| ︙ | ︙ | |||
4839 4840 4841 4842 4843 4844 4845 | /* ** SETTING: proxy width=32 default=system ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" ** environment variable is consulted. If "off", a direct HTTP connection is ** used. */ /* | | | | > > > > | 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 | /* ** SETTING: proxy width=32 default=system ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" ** environment variable is consulted. If "off", a direct HTTP connection is ** used. */ /* ** SETTING: redirect-to-https default=0 width=2 ** Specifies whether or not to redirect unencrypted "http://" requests to ** encrypted "https://" URIs. A value of 0 (the default) means do not ** redirect, 1 means to redirect only the /login page, and 2 ** means to always redirect. ** ** For security, a value of 2 is recommended. The default value is 0 ** because not all sites are TLS-capable. But you should definitely enable ** TLS and change this setting to 2 for all public-facing repositories. */ /* ** SETTING: relative-paths boolean default=on ** When showing changes and extras, report paths relative ** to the current working directory. */ /* |
| ︙ | ︙ | |||
4965 4966 4967 4968 4969 4970 4971 | /* ** SETTING: th1-hooks boolean default=off ** If enabled, special TH1 commands will be called before and ** after any Fossil command or web page. */ #endif /* | | | 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 | /* ** SETTING: th1-hooks boolean default=off ** If enabled, special TH1 commands will be called before and ** after any Fossil command or web page. */ #endif /* ** SETTING: th1-setup width=40 block-text sensitive ** This is the setup script to be evaluated after creating ** and initializing the TH1 interpreter. By default, this ** is empty and no extra setup is performed. */ /* ** SETTING: th1-uri-regexp width=40 block-text ** Specify which URI's are allowed in HTTP requests from |
| ︙ | ︙ | |||
5356 5357 5358 5359 5360 5361 5362 |
fossil_print("Local database: %s\n", g.zLocalDbName);
fossil_print("Config database: %s\n", g.zConfigDbName);
}
/*
** Compute a "fingerprint" on the repository. A fingerprint is used
** to verify that that the repository has not been replaced by a clone
| | | 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 |
fossil_print("Local database: %s\n", g.zLocalDbName);
fossil_print("Config database: %s\n", g.zConfigDbName);
}
/*
** Compute a "fingerprint" on the repository. A fingerprint is used
** to verify that that the repository has not been replaced by a clone
** of the same repository. More precisely, a fingerprint is used to
** verify that the mapping between SHA3 hashes and RID values is unchanged.
**
** The check-out database ("localdb") stores RID values. When associating
** a check-out database against a repository database, it is useful to verify
** the fingerprint so that we know tha the RID values in the check-out
** database still correspond to the correct entries in the BLOB table of
** the repository.
|
| ︙ | ︙ | |||
5421 5422 5423 5424 5425 5426 5427 | /* ** COMMAND: test-fingerprint ** ** Usage: %fossil test-fingerprint ?RCVID? ** ** Display the repository fingerprint using the supplied RCVID or | | | 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 |
/*
** COMMAND: test-fingerprint
**
** Usage: %fossil test-fingerprint ?RCVID?
**
** Display the repository fingerprint using the supplied RCVID or
** using the latest RCVID if none is given on the command line.
** Show both the legacy and the newer version of the fingerprint,
** and the currently stored fingerprint if there is one.
*/
void test_fingerprint(void){
int rcvid = 0;
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
if( g.argc==3 ){
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
760 761 762 763 764 765 766 767 768 769 770 771 772 773 |
}
body.tkt div.content ol.tkt-changes > li:target > p > span {
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
span.modpending {
color: #b03800;
font-style: italic;
}
pre.th1result {
white-space: pre-wrap;
| > > > > > > > > > | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 |
}
body.tkt div.content ol.tkt-changes > li:target > p > span {
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
body.cpage-info .file-change-line,
body.cpage-vdiff .file-change-line {
margin-top: 16px;
margin-bottom: 16px;
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
display: flex;
flex-direction: row;
justify-content: space-between;
}
span.modpending {
color: #b03800;
font-style: italic;
}
pre.th1result {
white-space: pre-wrap;
|
| ︙ | ︙ | |||
917 918 919 920 921 922 923 924 925 926 927 928 929 930 |
vertical-align: top;
border-collapse: collapse;
padding: 1px;
}
div.forum_body p {
margin-top: 0;
}
td.form_label {
vertical-align: top;
text-align: right;
}
.debug {
background-color: #ffc;
border: 2px solid #ff0;
| > > > > > > > | 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 |
vertical-align: top;
border-collapse: collapse;
padding: 1px;
}
div.forum_body p {
margin-top: 0;
}
div.forum-editor-widget{
display: flex;
flex-direction: column;
}
div.forum-editor-widget > textarea {
max-width: initial;
}
td.form_label {
vertical-align: top;
text-align: right;
}
.debug {
background-color: #ffc;
border: 2px solid #ff0;
|
| ︙ | ︙ | |||
980 981 982 983 984 985 986 987 988 989 990 991 992 993 |
div.forumpost-single-controls {
/* UI controls along the bottom of a single post
** in the thread view. */
}
.forum div > form {
margin: 0.5em 0;
display: inline-block;
}
.forum-post-collapser {
/* Common style for the bottom-of-post and right-of-post
expand/collapse widgets. */
font-size: 0.8em;
padding: 0;
border: 1px solid rgba(0, 0, 0, 0.2);
| > > > > > > > | 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 |
div.forumpost-single-controls {
/* UI controls along the bottom of a single post
** in the thread view. */
}
.forum div > form {
margin: 0.5em 0;
display: inline-block;
}
body.cpage-forumedit div > form,
body.cpage-forume2 div > form{
width: 100%;
}
.forum div > form > * {
margin-bottom: 0.35em;
}
.forum-post-collapser {
/* Common style for the bottom-of-post and right-of-post
expand/collapse widgets. */
font-size: 0.8em;
padding: 0;
border: 1px solid rgba(0, 0, 0, 0.2);
|
| ︙ | ︙ | |||
1679 1680 1681 1682 1683 1684 1685 |
/************************************************************
pikchr...
DOM structure:
<DIV.pikchr-wrapper>
<DIV.pikchr-svg>
<SVG.pikchr>...</SVG>
</DIV.pikchr-svg>
| | > > > > > > | > > > > > > > > > > > > > > > > > | | | 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 |
/************************************************************
pikchr...
DOM structure:
<DIV.pikchr-wrapper>
<DIV.pikchr-svg>
<SVG.pikchr>...</SVG>
</DIV.pikchr-svg>
<DIV.pikchr-src>
<PRE>pikchr source code</PRE>
<SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN>
<!-- ^^^ is unhidden and activated by JS code -->
</DIV.pikchr-src>
</DIV.pikchr-wrapper>
************************************************************/
div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/}
div.pikchr-svg {/*wrapper for SVG.pikchr element*/}
svg.pikchr {/*pikchr SVG*/
width: 100%/*necessary for SOME SVGs for Chrome!*/;
}
div.pikchr-src {
/*Wrapper for source code view of a pikchr (see fossil.pikchr.js)*/
display: flex;
flex-direction: column;
}
div.pikchr-src > pre {
/*Source code for a pikchr*/
box-sizing: border-box;
text-align: left;
}
div.pikchr-src > span {
/*Wrapper for a link to open a pikchr in /pikchrshow*/
margin-top: 0.5em;
margin-bottom: 0.5em;
font-size: 85%;
}
div.pikchr-src > span::before {
content: "[";
}
div.pikchr-src > span::after {
content: "]";
}
/* The .source-inline class tells the .source class that the
source view, when enabled, should be "inline" (same position
as the graphic), else the sources are shifted to the left as
if they were "plain text". */
div.pikchr-wrapper.center:not(.source),
div.pikchr-wrapper.center.source.source-inline{
text-align: center;
/* Reminder for The Future: this impl also works:
display: grid; place-items: center;
and does not require setting display:inline-block on the relevant
child items, but caniuse.com/css-grid suggests that some
still-seemingly-legitimate browsers don't support grid mode. */
}
div.pikchr-wrapper.center > div.pikchr-svg {
width: 100%/*necessary for Chrome!*/;
}
div.pikchr-wrapper.center:not(.source) > div.pikchr-src,
div.pikchr-wrapper.center:not(.source) > div.pikchr-svg,
/* ^^^ Centered non-source-view elements */
div.pikchr-wrapper.center.source.source-inline div.pikchr-src,
div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg
/* ^^^ Centered inline-source-view elements */{
display:inline-block/*allows parent text-align to do the alignment*/;
/* ^^^^ Browser incompatibility: inline-block causes the centered
pikchr to shrink to the point of illegiblity in Chrome. The
closest match on Chrome seems to be using 'unset', which centers
by virtue of stretching it to the width of the window. Similarly,
|
| ︙ | ︙ | |||
1741 1742 1743 1744 1745 1746 1747 |
div.pikchr-wrapper.float-right.source.source-inline{
float: right;
padding: 4em;
}
/* For pikchr-wrapper.source mode, toggle pre.pikchr-src and
svg.pikchr visibility... */
| | | | 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 |
div.pikchr-wrapper.float-right.source.source-inline{
float: right;
padding: 4em;
}
/* For pikchr-wrapper.source mode, toggle pre.pikchr-src and
svg.pikchr visibility... */
div.pikchr-wrapper.source > div.pikchr-src {
/* Source code ^^^^^^^ is visible, else it is hidden */
}
div.pikchr-wrapper:not(.source) > div.pikchr-src {
/* Hide sources when image is being shown. */
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
div.pikchr-wrapper.source > div.pikchr-svg {
|
| ︙ | ︙ | |||
1793 1794 1795 1796 1797 1798 1799 |
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
| < < < < | 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 |
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #ffc;
}
body.branch .brlist > table > tbody td:first-child > input {
cursor: pointer;
}
|
| ︙ | ︙ |
Changes to src/delta.c.
| ︙ | ︙ | |||
223 224 225 226 227 228 229 |
** buffer is not a multiple of 4 bytes length, compute the sum that would
** have occurred if the buffer was padded with zeros to the next multiple
** of four bytes.
*/
static unsigned int checksum(const char *zIn, size_t N){
static const int byteOrderTest = 1;
const unsigned char *z = (const unsigned char *)zIn;
| < > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
** buffer is not a multiple of 4 bytes length, compute the sum that would
** have occurred if the buffer was padded with zeros to the next multiple
** of four bytes.
*/
static unsigned int checksum(const char *zIn, size_t N){
static const int byteOrderTest = 1;
const unsigned char *z = (const unsigned char *)zIn;
unsigned sum = 0;
if( N>0 ){
const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3];
assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */
if( 0==*(char*)&byteOrderTest ){
/* This is a big-endian machine */
while( z<zEnd ){
sum += *(unsigned*)z;
z += 4;
}
}else{
/* A little-endian machine */
#if GCC_VERSION>=4003000
while( z<zEnd ){
sum += __builtin_bswap32(*(unsigned*)z);
z += 4;
}
#elif defined(_MSC_VER) && _MSC_VER>=1300
while( z<zEnd ){
sum += _byteswap_ulong(*(unsigned*)z);
z += 4;
}
#else
unsigned sum0 = 0;
unsigned sum1 = 0;
unsigned sum2 = 0;
while(N >= 16){
sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
z += 16;
N -= 16;
}
while(N >= 4){
sum0 += z[0];
sum1 += z[1];
sum2 += z[2];
sum += z[3];
z += 4;
N -= 4;
}
sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
#endif
}
switch(N&3){
case 3: sum += (z[2] << 8);
case 2: sum += (z[1] << 16);
case 1: sum += (z[0] << 24);
default: ;
}
}
return sum;
}
/*
** Create a new delta.
**
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #define DIFF_JSON 0x00010000 /* JSON output */ #define DIFF_DEBUG 0x00020000 /* Debugging diff output */ #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ #define DIFF_TCL 0x00080000 /* For the --tk option */ #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ /* ** Per file information that may influence output. */ #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ #define DIFF_FILE_DELETED 0x80000000 /* Deleted or rename source */ #define DIFF_FILE_MASK 0xc0000000 /* Used for clearing file flags */ | > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #define DIFF_JSON 0x00010000 /* JSON output */ #define DIFF_DEBUG 0x00020000 /* Debugging diff output */ #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ #define DIFF_TCL 0x00080000 /* For the --tk option */ #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ #define DIFF_BY_TOKEN 0x01000000 /* Split on tokens, not lines */ /* ** Per file information that may influence output. */ #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ #define DIFF_FILE_DELETED 0x80000000 /* Deleted or rename source */ #define DIFF_FILE_MASK 0xc0000000 /* Used for clearing file flags */ |
| ︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
}while( zNL[0]!='\0' && zNL[1]!='\0' );
assert( i==nLine );
/* Return results */
*pnLine = nLine;
return a;
}
/*
** Return zero if two DLine elements are identical.
*/
static int compare_dline(const DLine *pA, const DLine *pB){
if( pA->h!=pB->h ) return 1;
return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
}while( zNL[0]!='\0' && zNL[1]!='\0' );
assert( i==nLine );
/* Return results */
*pnLine = nLine;
return a;
}
/*
** Character classes for the purpose of tokenization.
**
** 1 - alphanumeric
** 2 - whitespace
** 3 - punctuation
*/
static char aTCharClass[256] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
/*
** Count the number of tokens in the given string.
*/
static int count_tokens(const unsigned char *p, int n){
int nToken = 0;
int iPrev = 0;
int i;
for(i=0; i<n; i++){
char x = aTCharClass[p[i]];
if( x!=iPrev ){
iPrev = x;
nToken++;
}
}
return nToken;
}
/*
** Return an array of DLine objects containing a pointer to the
** start of each token and a hash of that token. The lower
** bits of the hash store the length of each token.
**
** This is like break_into_lines() except that it works with tokens
** instead of lines. A token is:
**
** * A contiguous sequence of alphanumeric characters.
** * A contiguous sequence of whitespace
** * A contiguous sequence of punctuation characters.
**
** Return 0 if the file is binary or contains a line that is
** too long.
*/
static DLine *break_into_tokens(
const char *z,
int n,
int *pnToken,
u64 diffFlags
){
int nToken, i, k;
u64 h, h2;
DLine *a;
unsigned char *p = (unsigned char*)z;
nToken = count_tokens(p, n);
a = fossil_malloc( sizeof(a[0])*(nToken+1) );
memset(a, 0, sizeof(a[0])*(nToken+1));
if( n==0 ){
*pnToken = 0;
return a;
}
i = 0;
while( n>0 ){
char x = aTCharClass[*p];
h = 0xcbf29ce484222325LL;
for(k=1; k<n && aTCharClass[p[k]]==x; k++){
h ^= p[k];
h *= 0x100000001b3LL;
}
a[i].z = (char*)p;
a[i].n = k;
a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | k;
h2 = h % nToken;
a[i].iNext = a[h2].iHash;
a[h2].iHash = i+1;
p += k; n -= k;
i++;
};
assert( i==nToken );
/* Return results */
*pnToken = nToken;
return a;
}
/*
** Return zero if two DLine elements are identical.
*/
static int compare_dline(const DLine *pA, const DLine *pB){
if( pA->h!=pB->h ) return 1;
return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK);
|
| ︙ | ︙ | |||
2460 2461 2462 2463 2464 2465 2466 | sqlite3_int64 bestScore; /* Best score so far */ sqlite3_int64 score; /* Score for current candidate LCS */ int span; /* combined width of the input sequences */ int cutoff = 4; /* Max hash chain entries to follow */ int nextCutoff = -1; /* Value of cutoff for next iteration */ span = (iE1 - iS1) + (iE2 - iS2); | | | 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 |
sqlite3_int64 bestScore; /* Best score so far */
sqlite3_int64 score; /* Score for current candidate LCS */
int span; /* combined width of the input sequences */
int cutoff = 4; /* Max hash chain entries to follow */
int nextCutoff = -1; /* Value of cutoff for next iteration */
span = (iE1 - iS1) + (iE2 - iS2);
bestScore = -9223300000*(sqlite3_int64)1000000000;
score = 0;
iSXb = iSXp = iS1;
iEXb = iEXp = iS1;
iSYb = iSYp = iS2;
iEYb = iEYp = iS2;
mid = (iE1 + iS1)/2;
do{
|
| ︙ | ︙ | |||
2995 2996 2997 2998 2999 3000 3001 |
/* Prepare the input files */
memset(&c, 0, sizeof(c));
if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
c.xDiffer = compare_dline_ignore_allws;
}else{
c.xDiffer = compare_dline;
}
| > > > > > > | | | | > | 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 |
/* Prepare the input files */
memset(&c, 0, sizeof(c));
if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
c.xDiffer = compare_dline_ignore_allws;
}else{
c.xDiffer = compare_dline;
}
if( pCfg->diffFlags & DIFF_BY_TOKEN ){
c.aFrom = break_into_tokens(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, pCfg->diffFlags);
c.aTo = break_into_tokens(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, pCfg->diffFlags);
}else{
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, pCfg->diffFlags);
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, pCfg->diffFlags);
}
if( c.aFrom==0 || c.aTo==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
if( pOut ){
diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags);
}
return 0;
|
| ︙ | ︙ | |||
3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 |
if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, pCfg->diffFlags);
return 0;
}
}
if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
diff_optimize(&c);
}
if( pOut ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
int nDel = 0, nIns = 0, i;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
| > > > > > > > > > > > > > > > > | | 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 |
if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, pCfg->diffFlags);
return 0;
}
}
if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
diff_optimize(&c);
}
if( (pCfg->diffFlags & DIFF_BY_TOKEN)!=0 ){
/* Convert token counts into byte counts. */
int i;
int iA = 0;
int iB = 0;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
int k, sum;
for(k=0, sum=0; k<c.aEdit[i]; k++) sum += c.aFrom[iA++].n;
iB += c.aEdit[i];
c.aEdit[i] = sum;
for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n;
c.aEdit[i+1] = sum;
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
c.aEdit[i+2] = sum;
}
}
if( pOut ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
int nDel = 0, nIns = 0, i;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
const int *R = c.aEdit;
unsigned int r;
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
R[r], R[r+1], R[r+2]);
}
}else if( pCfg->diffFlags & DIFF_JSON ){
|
| ︙ | ︙ | |||
3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 |
/*
** Initialize the DiffConfig object using command-line options.
**
** Process diff-related command-line options and return an appropriate
** "diffFlags" integer.
**
** --brief Show filenames only DIFF_BRIEF
** -c|--context N N lines of context. nContext
** --html Format for HTML DIFF_HTML
** --invert Invert the diff DIFF_INVERT
** -n|--linenum Show line numbers DIFF_LINENO
** --noopt Disable optimization DIFF_NOOPT
** --numstat Show change counts DIFF_NUMSTAT
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
** --unified Unified diff. ~DIFF_SIDEBYSIDE
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
** -W|--width N N character lines. wColumn
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
u64 diffFlags = 0;
const char *z;
| > > > > > > > > > | 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 |
/*
** Initialize the DiffConfig object using command-line options.
**
** Process diff-related command-line options and return an appropriate
** "diffFlags" integer.
**
** -b|--browser Show the diff output in a web-browser
** --brief Show filenames only DIFF_BRIEF
** --by Shorthand for "--browser -y"
** -c|--context N N lines of context. nContext
** --dark Use dark mode for Tcl/Tk and HTML output
** --html Format for HTML DIFF_HTML
** -i|--internal Use built-in diff, not an external tool
** --invert Invert the diff DIFF_INVERT
** --json Output formatted as JSON
** -n|--linenum Show line numbers DIFF_LINENO
** -N|--new-file Alias for --verbose
** --noopt Disable optimization DIFF_NOOPT
** --numstat Show change counts DIFF_NUMSTAT
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
** --tcl Tcl-formatted output used internally by --tk
** --unified Unified diff. ~DIFF_SIDEBYSIDE
** -v|--verbose Show complete text of added or deleted files
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
** --webpage Format output as a stand-alone HTML webpage
** -W|--width N N character lines. wColumn
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
u64 diffFlags = 0;
const char *z;
|
| ︙ | ︙ | |||
3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 |
diffFlags |= DIFF_TCL;
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
}
if( (z = find_option("context","c",1))!=0 ){
char *zEnd;
f = (int)strtol(z, &zEnd, 10);
if( zEnd[0]==0 && errno!=ERANGE ){
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
| > > > | 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 |
diffFlags |= DIFF_TCL;
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
if( find_option("bytoken",0,0)!=0 ){
diffFlags = DIFF_RAW|DIFF_BY_TOKEN;
}
}
if( (z = find_option("context","c",1))!=0 ){
char *zEnd;
f = (int)strtol(z, &zEnd, 10);
if( zEnd[0]==0 && errno!=ERANGE ){
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
|
| ︙ | ︙ | |||
3769 3770 3771 3772 3773 3774 3775 | u64 annFlags = 0; /* Flags to control annotation properties */ int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */ int szHash; /* Display size of a version hash */ Blob treename; /* Name of file to be annotated */ char *zFilename; /* Name of file to be annotated */ bBlame = g.argv[1][0]!='a'; | | | 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 |
u64 annFlags = 0; /* Flags to control annotation properties */
int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */
int szHash; /* Display size of a version hash */
Blob treename; /* Name of file to be annotated */
char *zFilename; /* Name of file to be annotated */
bBlame = g.argv[1][0]!='a';
zRevision = find_option("revision","r",1);
zLimit = find_option("limit","n",1);
zOrig = find_option("origin","o",1);
showLog = find_option("log","l",0)!=0;
if( find_option("ignore-trailing-space","Z",0)!=0 ){
annFlags = DIFF_IGNORE_EOLWS;
}
if( find_option("ignore-all-space","w",0)!=0 ){
|
| ︙ | ︙ |
Changes to src/diff.tcl.
1 2 3 4 5 |
# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
# to this file, then runs this file using "tclsh" in order to display the
# graphical diff in a separate window. A typical "set fossilcmd" line
# looks like this:
#
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
# to this file, then runs this file using "tclsh" in order to display the
# graphical diff in a separate window. A typical "set fossilcmd" line
# looks like this:
#
# set fossilcmd {| "./fossil" diff --tcl -i -v}
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
set prog {
package require Tk
array set CFG_light {
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
| | | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
@ width: fit-content;
@ text-align: right;
@ padding: 0 1em 0 0;
@ }
@ td.difflne {
@ padding-bottom: 0.4em;
@ }
@ td.diffsep {
@ width: fit-content;
@ padding: 0 0.3em 0 1em;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffsep pre {
@ line-height: inherit;
@ font-size: inherit;
|
| ︙ | ︙ | |||
377 378 379 380 381 382 383 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
| | | | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
@ width: fit-content;
@ text-align: right;
@ padding: 0 1em 0 0;
@ }
@ td.difflne {
@ padding-bottom: 0.4em;
@ }
@ td.diffsep {
@ width: fit-content;
@ padding: 0 0.3em 0 1em;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffsep pre {
@ line-height: inherit;
@ font-size: inherit;
|
| ︙ | ︙ | |||
1264 1265 1266 1267 1268 1269 1270 | ** --invert Invert the diff ** --json Output formatted as JSON ** -n|--linenum Show line numbers ** -N|--new-file Alias for --verbose ** --numstat Show only the number of added and deleted lines ** -y|--side-by-side Side-by-side diff ** --strip-trailing-cr Strip trailing CR | | | 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 | ** --invert Invert the diff ** --json Output formatted as JSON ** -n|--linenum Show line numbers ** -N|--new-file Alias for --verbose ** --numstat Show only the number of added and deleted lines ** -y|--side-by-side Side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tcl Tcl-formatted output used internally by --tk ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION Select VERSION as target for the diff ** --undo Diff against the "undo" buffer ** --unified Unified diff ** -v|--verbose Output complete text of added or deleted files ** -h|--versions Show compared versions in the diff header |
| ︙ | ︙ | |||
1310 1311 1312 1313 1314 1315 1316 |
}
zTo = zBranch;
zFrom = mprintf("root:%s", zBranch);
}
if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
fossil_fatal("cannot use --checkin together with --from or --to");
}
| | > < < > | 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 |
}
zTo = zBranch;
zFrom = mprintf("root:%s", zBranch);
}
if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
fossil_fatal("cannot use --checkin together with --from or --to");
}
diff_options(&DCfg, isGDiff, 0);
determine_exec_relative_option(1);
if( 0==zCheckin ){
if( zTo==0 || againstUndo ){
db_must_be_within_tree();
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(0, 0);
}
}else{
db_find_and_open_repository(0, 0);
}
verify_all_options();
g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
if( g.argc>=3 ){
int i;
Blob fname;
pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
for(i=2; i<g.argc; i++){
file_tree_name(g.argv[i], &fname, 0, 1);
|
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 |
fossil_print("%s\n", blob_str(&txt));
blob_reset(&txt);
}
/*
** Return a pointer to the setting information array.
**
| | > > > > > > > > > > > > > > > > > > > > > > > > > | 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 |
fossil_print("%s\n", blob_str(&txt));
blob_reset(&txt);
}
/*
** Return a pointer to the setting information array.
**
** This routine provides access to the aSetting[] array which is created
** by the mkindex utility program and included with <page_index.h>.
*/
const Setting *setting_info(int *pnCount){
if( pnCount ) *pnCount = (int)(sizeof(aSetting)/sizeof(aSetting[0])) - 1;
return aSetting;
}
/*
** Return a pointer to a specific Setting entry for the setting named
** in the argument. Or return NULL if no such setting exists.
**
** The pointer returned points into the middle of the global aSetting[]
** array that is generated by mkindex. Use setting_info() to fetch the
** whole array. Use this routine to fetch a specific entry.
*/
const Setting *setting_find(const char *zName){
int iFirst = 0;
int iLast = ArraySize(aSetting)-1;
while( iFirst<=iLast ){
int iCur = (iFirst+iLast)/2;
int c = strcmp(aSetting[iCur].name, zName);
if( c<0 ){
iFirst = iCur+1;
}else if( c>0 ){
iLast = iCur-1;
}else{
return &aSetting[iCur];
}
}
return 0;
}
/*****************************************************************************
** A virtual table for accessing the information in aCommand[], and
** especially the help-text
*/
/* helptextVtab_vtab is a subclass of sqlite3_vtab which is
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
{ "xlm", 3, "application/vnd.ms-excel" },
{ "xls", 3, "application/vnd.ms-excel" },
{ "xlsx", 4, "application/vnd.openxmlformats-"
"officedocument.spreadsheetml.sheet"},
{ "xlw", 3, "application/vnd.ms-excel" },
{ "xml", 3, "text/xml" },
{ "xpm", 3, "image/x-xpixmap" },
{ "xwd", 3, "image/x-xwindowdump" },
{ "xyz", 3, "chemical/x-pdb" },
{ "zip", 3, "application/zip" },
};
/*
** Verify that all entries in the aMime[] table are in sorted order.
| > > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
{ "xlm", 3, "application/vnd.ms-excel" },
{ "xls", 3, "application/vnd.ms-excel" },
{ "xlsx", 4, "application/vnd.openxmlformats-"
"officedocument.spreadsheetml.sheet"},
{ "xlw", 3, "application/vnd.ms-excel" },
{ "xml", 3, "text/xml" },
{ "xpm", 3, "image/x-xpixmap" },
{ "xsl", 3, "text/xml" },
{ "xslt", 4, "text/xml" },
{ "xwd", 3, "image/x-xwindowdump" },
{ "xyz", 3, "chemical/x-pdb" },
{ "zip", 3, "application/zip" },
};
/*
** Verify that all entries in the aMime[] table are in sorted order.
|
| ︙ | ︙ |
Changes to src/event.c.
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
@ %h(blob_str(&fullbody))
@ </pre>
}
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
" FROM tag"
" WHERE tagname GLOB 'event-%q*'",
zId);
| | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
@ %h(blob_str(&fullbody))
@ </pre>
}
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
" FROM tag"
" WHERE tagname GLOB 'event-%q*'",
zId);
attachment_list(zFullId, "<h2>Attachments:</h2>", 1);
document_emit_js();
style_finish_page();
manifest_destroy(pTNote);
}
/*
** Add or update a new tech note to the repository. rid is id of
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
1300 1301 1302 1303 1304 1305 1306 |
}else{
return 0;
}
}
/*
** Compute a canonical pathname for a file or directory.
| > | | | | > > > > > > > > > > > | > < | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 |
}else{
return 0;
}
}
/*
** Compute a canonical pathname for a file or directory.
**
** * Make the name absolute if it is relative.
** * Remove redundant / characters
** * Remove all /./ path elements.
** * Convert /A/../ to just /
** * On windows, add the drive letter prefix.
**
** If the slash parameter is non-zero, the trailing slash, if any,
** is retained.
**
** See also: file_canonical_name_dup()
*/
void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){
char zPwd[2000];
blob_zero(pOut);
if( file_is_absolute_path(zOrigName) ){
#if defined(_WIN32)
if( fossil_isdirsep(zOrigName[0]) ){
/* Add the drive letter to the full pathname */
file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
blob_appendf(pOut, "%.2s%/", zPwd, zOrigName);
}else
#endif
{
blob_appendf(pOut, "%/", zOrigName);
}
}else{
file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
if( zPwd[0]=='/' && strlen(zPwd)==1 ){
/* when on '/', don't add an extra '/' */
if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){
/* '.' when on '/' mean '/' */
blob_appendf(pOut, "%/", zPwd);
}else{
|
| ︙ | ︙ | |||
1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 |
**
** For case-sensitive filesystems, such as on Linux, this routine is
** just fossil_strdup(). But for case-insenstiive but "case preserving"
** filesystems, such as on MacOS or Windows, we want the filename to be
** in the preserved casing. That's what this routine does.
*/
char *file_case_preferred_name(const char *zDir, const char *zPath){
DIR *d;
int i;
char *zResult = 0;
void *zNative = 0;
if( filenames_are_case_sensitive() ){
return fossil_strdup(zPath);
| > | 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 |
**
** For case-sensitive filesystems, such as on Linux, this routine is
** just fossil_strdup(). But for case-insenstiive but "case preserving"
** filesystems, such as on MacOS or Windows, we want the filename to be
** in the preserved casing. That's what this routine does.
*/
char *file_case_preferred_name(const char *zDir, const char *zPath){
#ifndef _WIN32 /* Call win32_file_case_preferred_name() on Windows. */
DIR *d;
int i;
char *zResult = 0;
void *zNative = 0;
if( filenames_are_case_sensitive() ){
return fossil_strdup(zPath);
|
| ︙ | ︙ | |||
1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 |
fossil_path_free(zUtf8);
}
closedir(d);
}
fossil_path_free(zNative);
if( zResult==0 ) zResult = fossil_strdup(zPath);
return zResult;
}
/*
** COMMAND: test-case-filename
**
** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
**
| > > > | 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 |
fossil_path_free(zUtf8);
}
closedir(d);
}
fossil_path_free(zNative);
if( zResult==0 ) zResult = fossil_strdup(zPath);
return zResult;
#else /* !_WIN32 */
return win32_file_case_preferred_name(zDir,zPath);
#endif /* !_WIN32 */
}
/*
** COMMAND: test-case-filename
**
** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
**
|
| ︙ | ︙ | |||
1635 1636 1637 1638 1639 1640 1641 |
}
db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0);
fossil_print("filenames_are_case_sensitive() = %d\n",
filenames_are_case_sensitive());
if( zAllow ){
g.allowSymlinks = !is_false(zAllow);
}
| | > > > > > > | 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 |
}
db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0);
fossil_print("filenames_are_case_sensitive() = %d\n",
filenames_are_case_sensitive());
if( zAllow ){
g.allowSymlinks = !is_false(zAllow);
}
if( zRoot==0 ) zRoot = g.zLocalRoot==0 ? "" : g.zLocalRoot;
fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
fossil_print("local-root = [%s]\n", zRoot);
if( g.db==0 ) sqlite3_open(":memory:", &g.db);
sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func, 0, 0);
for(i=2; i<g.argc; i++){
char *z;
emitFileStat(g.argv[i], slashFlag, resetFlag);
z = file_canonical_name_dup(g.argv[i]);
fossil_print(" file_canonical_name = %s\n", z);
fossil_print(" file_nondir_path = ");
if( fossil_strnicmp(zRoot,z,(int)strlen(zRoot))!=0 ){
fossil_print("(--root is not a prefix of this file)\n");
}else{
int n = file_nondir_objects_on_path(zRoot, z);
fossil_print("%.*s\n", n, z);
}
fossil_free(z);
z = db_text(0, "SELECT inode(%Q)", g.argv[i]);
fossil_print(" file_inode_sql_func = \"%s\"\n", z);
fossil_free(z);
}
}
/*
** COMMAND: test-canonical-name
**
** Usage: %fossil test-canonical-name FILENAME...
|
| ︙ | ︙ | |||
1693 1694 1695 1696 1697 1698 1699 |
** Return TRUE if the given filename is canonical.
**
** Canonical names are full pathnames using "/" not "\" and which
** contain no "/./" or "/../" terms.
*/
int file_is_canonical(const char *z){
int i;
| | | > > | 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 |
** Return TRUE if the given filename is canonical.
**
** Canonical names are full pathnames using "/" not "\" and which
** contain no "/./" or "/../" terms.
*/
int file_is_canonical(const char *z){
int i;
if(
#if defined(_WIN32) || defined(__CYGWIN__)
!fossil_isupper(z[0]) || z[1]!=':' || !fossil_isdirsep(z[2])
#else
z[0]!='/'
#endif
) return 0;
for(i=0; z[i]; i++){
if( z[i]=='\\' ) return 0;
if( z[i]=='/' ){
if( z[i+1]=='.' ){
|
| ︙ | ︙ | |||
2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 |
/*
** Returns 1 if the given directory contains a file named .fslckout, 2
** if it contains a file named _FOSSIL_, else returns 0.
*/
int dir_has_ckout_db(const char *zDir){
int rc = 0;
char * zCkoutDb = mprintf("%//.fslckout", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 1;
}else{
fossil_free(zCkoutDb);
zCkoutDb = mprintf("%//_FOSSIL_", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 2;
}
}
fossil_free(zCkoutDb);
return rc;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 |
/*
** Returns 1 if the given directory contains a file named .fslckout, 2
** if it contains a file named _FOSSIL_, else returns 0.
*/
int dir_has_ckout_db(const char *zDir){
int rc = 0;
i64 sz;
char * zCkoutDb = mprintf("%//.fslckout", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 1;
}else{
fossil_free(zCkoutDb);
zCkoutDb = mprintf("%//_FOSSIL_", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 2;
}
}
if( rc && ((sz = file_size(zCkoutDb, ExtFILE))<1024 || (sz%512)!=0) ){
rc = 0;
}
fossil_free(zCkoutDb);
return rc;
}
/*
** This is the implementation of inode(FILENAME) SQL function.
**
** dev_inode(FILENAME) returns a string. If FILENAME exists and is
** a regular file, then the return string is of the form:
**
** DEV/INODE
**
** Where DEV and INODE are the device number and inode number for
** the file. On Windows, the volume serial number (DEV) and file
** identifier (INODE) are used to compute the value, see comments
** on the win32_file_id() function.
**
** If FILENAME does not exist, then the return is an empty string.
**
** The value of inode() can be used to eliminate files from a list
** that have duplicates because they have differing names due to links.
**
** Code that wants to use this SQL function needs to first register
** it using a call such as the following:
**
** sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
** file_inode_sql_func, 0, 0);
*/
void file_inode_sql_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFilename;
assert( argc==1 );
zFilename = (const char*)sqlite3_value_text(argv[0]);
if( zFilename==0 || zFilename[0]==0 || file_access(zFilename,F_OK) ){
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
return;
}
#if defined(_WIN32)
{
char *zFileId = win32_file_id(zFilename);
if( zFileId ){
sqlite3_result_text(context, zFileId, -1, fossil_free);
}else{
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
}
}
#else
{
struct stat buf;
int rc;
memset(&buf, 0, sizeof(buf));
rc = stat(zFilename, &buf);
if( rc ){
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
}else{
sqlite3_result_text(context,
mprintf("%lld/%lld", (i64)buf.st_dev, (i64)buf.st_ino), -1,
fossil_free);
}
}
#endif
}
|
Changes to src/finfo.c.
| ︙ | ︙ | |||
183 184 185 186 187 188 189 |
/* this is the default, no-op */
}
zLimit = find_option("limit","n",1);
zWidth = find_option("width","W",1);
iLimit = zLimit ? atoi(zLimit) : -1;
zOffset = find_option("offset",0,1);
iOffset = zOffset ? atoi(zOffset) : 0;
| | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
/* this is the default, no-op */
}
zLimit = find_option("limit","n",1);
zWidth = find_option("width","W",1);
iLimit = zLimit ? atoi(zLimit) : -1;
zOffset = find_option("offset",0,1);
iOffset = zOffset ? atoi(zOffset) : 0;
iBrief = find_option("brief","b",0) != 0;
if( iLimit==0 ){
iLimit = -1;
}
if( zWidth ){
iWidth = atoi(zWidth);
if( (iWidth!=0) && (iWidth<=22) ){
fossil_fatal("-W|--width value must be >22 or 0");
|
| ︙ | ︙ | |||
226 227 228 229 230 231 232 |
" AND event.objid=mlink.mid"
" AND event.objid=ci.rid"
" ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
TAG_BRANCH, zFilename, filename_collation(),
iLimit, iOffset
);
blob_zero(&line);
| | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
" AND event.objid=mlink.mid"
" AND event.objid=ci.rid"
" ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
TAG_BRANCH, zFilename, filename_collation(),
iLimit, iOffset
);
blob_zero(&line);
if( iBrief == 0 ){
fossil_print("History for %s\n", blob_str(&fname));
}
while( db_step(&q)==SQLITE_ROW ){
const char *zFileUuid = db_column_text(&q, 0);
const char *zCiUuid = db_column_text(&q,1);
const char *zDate = db_column_text(&q, 2);
const char *zCom = db_column_text(&q, 3);
const char *zUser = db_column_text(&q, 4);
const char *zBr = db_column_text(&q, 5);
char *zOut;
if( zBr==0 ) zBr = "trunk";
if( iBrief == 0 ){
fossil_print("%s ", zDate);
zOut = mprintf(
"[%S] %s (user: %s, artifact: [%S], branch: %s)",
zCiUuid, zCom, zUser, zFileUuid, zBr);
comment_print(zOut, zCom, 11, iWidth, get_comment_format());
fossil_free(zOut);
}else{
|
| ︙ | ︙ | |||
618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
int pfnid = db_column_int(&q, 11);
int szFile = db_column_int(&q, 12);
int fnid = db_column_int(&q, 13);
const char *zFName = db_column_text(&q,14);
int gidx;
char zTime[10];
int nParent = 0;
GraphRowId aParent[GR_MAX_RAIL];
db_bind_int(&qparent, ":fid", frid);
db_bind_int(&qparent, ":mid", fmid);
db_bind_int(&qparent, ":fnid", fnid);
while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent] = db_column_int64(&qparent, 0);
| > | 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 |
int pfnid = db_column_int(&q, 11);
int szFile = db_column_int(&q, 12);
int fnid = db_column_int(&q, 13);
const char *zFName = db_column_text(&q,14);
int gidx;
char zTime[10];
int nParent = 0;
int bIsModified = 0;
GraphRowId aParent[GR_MAX_RAIL];
db_bind_int(&qparent, ":fid", frid);
db_bind_int(&qparent, ":mid", fmid);
db_bind_int(&qparent, ":fnid", fnid);
while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent] = db_column_int64(&qparent, 0);
|
| ︙ | ︙ | |||
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 |
@ %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))%s(zTime)</a></td>
@ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
@ </td>
if( zBgClr && zBgClr[0] ){
@ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
}else{
@ <td class="timeline%s(zStyle)Cell">
}
if( tmFlags & TIMELINE_COMPACT ){
@ <span class='timelineCompactComment' data-id='%d(frid)'>
}else{
@ <span class='timeline%s(zStyle)Comment'>
if( pfnid ){
char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d",
pfnid);
| > > > > > > | > | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 |
@ %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))%s(zTime)</a></td>
@ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
@ </td>
if( zBgClr && zBgClr[0] ){
@ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
}else{
@ <td class="timeline%s(zStyle)Cell">
}
if( zPUuid && zUuid && fossil_strcmp(zPUuid, zUuid)!=0 ){
bIsModified = 1;
}
if( tmFlags & TIMELINE_COMPACT ){
@ <span class='timelineCompactComment' data-id='%d(frid)'>
}else{
@ <span class='timeline%s(zStyle)Comment'>
if( pfnid ){
char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d",
pfnid);
if( bIsModified ){
@ <b>Renamed and modified</b> %h(zPrevName) → %h(zFName).
}else{
@ <b>Renamed</b> %h(zPrevName) → %h(zFName).
}
fossil_free(zPrevName);
}
if( zUuid && ridTo==0 && nParent==0 ){
@ <b>Added:</b>
}
if( zUuid==0 ){
char *zNewName;
|
| ︙ | ︙ | |||
754 755 756 757 758 759 760 |
const char *z = zFName;
@ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
@ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
@ [annotate]</a>
@ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
@ [blame]</a>
@ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins using]</a>
| | | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 |
const char *z = zFName;
@ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
@ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
@ [annotate]</a>
@ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
@ [blame]</a>
@ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins using]</a>
if( fpid>0 && bIsModified!=0 ){
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
}
if( fileedit_is_editable(zFName) ){
@ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\
@ [edit]</a>
}
@ </span></span>
|
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
1395 1396 1397 1398 1399 1400 1401 |
){
if( zTitle ){
@ Title: <input type="input" name="title" value="%h(zTitle)" size="50"
@ maxlength="125"><br>
}
@ %z(href("%R/markup_help"))Markup style</a>:
mimetype_option_menu(zMimetype, "mimetype");
| > | | | 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 |
){
if( zTitle ){
@ Title: <input type="input" name="title" value="%h(zTitle)" size="50"
@ maxlength="125"><br>
}
@ %z(href("%R/markup_help"))Markup style</a>:
mimetype_option_menu(zMimetype, "mimetype");
@ <div class="forum-editor-widget">
@ <textarea aria-label="Content:" name="content" class="wikiedit" \
@ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea></div>
}
/*
** WEBPAGE: forumpost_close hidden
** WEBPAGE: forumpost_reopen hidden
**
** fpid=X Hash of the post to be edited. REQUIRED
|
| ︙ | ︙ | |||
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 |
forum_render_debug_options();
login_insert_csrf_secret();
@ </form>
forum_emit_js();
style_finish_page();
}
/*
** WEBPAGE: setup_forum
**
** Forum configuration and metrics.
*/
void forum_setup(void){
/* boolean config settings specific to the forum. */
| > > > > > > > > > > > > > > > | | | < < < < < < < < > > | < > | > < < < < < < < | > | > > > > > | > | < > > > > > > > | > | > > | | > > > | > > > | > | 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 |
forum_render_debug_options();
login_insert_csrf_secret();
@ </form>
forum_emit_js();
style_finish_page();
}
/*
** SETTING: forum-close-policy boolean default=off
** If true, forum moderators may close/re-open forum posts, and reply
** to closed posts. If false, only administrators may do so. Note that
** this only affects the forum web UI, not post-closing tags which
** arrive via the command-line or from synchronization with a remote.
*/
/*
** SETTING: forum-title width=20 default=Forum
** This is the name or "title" of the Forum for this repository. The
** default is just "Forum". But in some setups, admins might want to
** change it to "Developer Forum" or "User Forum" or whatever other name
** seems more appropriate for the particular usage.
*/
/*
** WEBPAGE: setup_forum
**
** Forum configuration and metrics.
*/
void forum_setup(void){
/* boolean config settings specific to the forum. */
static const char *azForumSettings[] = {
"forum-close-policy",
"forum-title",
};
login_check_credentials();
if( !g.perm.Setup ){
login_needed(g.anon.Setup);
return;
}
style_set_current_feature("forum");
style_header("Forum Setup");
@ <h2>Metrics</h2>
{
int nPosts = db_int(0, "SELECT COUNT(*) FROM event WHERE type='f'");
@ <p><a href='%R/forum'>Forum posts</a>:
@ <a href='%R/timeline?y=f'>%d(nPosts)</a></p>
}
@ <h2>Supervisors</h2>
{
Stmt q = empty_Stmt;
db_prepare(&q, "SELECT uid, login, cap FROM user "
"WHERE cap GLOB '*[as6]*' ORDER BY login");
@ <table class='bordered'>
@ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@ <tbody>
while( SQLITE_ROW==db_step(&q) ){
const int iUid = db_column_int(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zCap = db_column_text(&q, 2);
@ <tr>
@ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
@ <td>(%h(zCap))</td>
@ </tr>
}
db_finalize(&q);
@</tbody></table>
}
@ <h2>Moderators</h2>
if( db_int(0, "SELECT count(*) FROM user "
" WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
@ <p>No non-supervisor moderators
}else{
Stmt q = empty_Stmt;
int nRows = 0;
db_prepare(&q, "SELECT uid, login, cap FROM user "
"WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'"
" ORDER BY login");
@ <table class='bordered'>
@ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@ <tbody>
while( SQLITE_ROW==db_step(&q) ){
const int iUid = db_column_int(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zCap = db_column_text(&q, 2);
++nRows;
@ <tr>
@ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
@ <td>(%h(zCap))</td>
@ </tr>
}
db_finalize(&q);
@ </tbody></table>
}
@ <h2>Settings</h2>
if( P("submit") && cgi_csrf_safe(2) ){
int i = 0;
db_begin_transaction();
for(i=0; i<ArraySize(azForumSettings); i++){
char zQP[4];
const char *z;
const Setting *pSetting = setting_find(azForumSettings[i]);
if( pSetting==0 ) continue;
zQP[0] = 'a'+i;
zQP[1] = zQP[0];
zQP[2] = 0;
z = P(zQP);
if( z==0 || z[0]==0 ) continue;
db_set(pSetting->name/*works-like:"x"*/, z, 0);
}
db_end_transaction(0);
@ <p><em>Settings saved.</em></p>
}
{
int i = 0;
@ <form action="%R/setup_forum" method="post">
login_insert_csrf_secret();
@ <table class='forum-settings-list'><tbody>
for(i=0; i<ArraySize(azForumSettings); i++){
char zQP[4];
const Setting *pSetting = setting_find(azForumSettings[i]);
if( pSetting==0 ) continue;
zQP[0] = 'a'+i;
zQP[1] = zQP[0];
zQP[2] = 0;
if( pSetting->width==0 ){
/* Boolean setting */
@ <tr><td align="right">
@ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
@ </td><td>
onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0);
@ </td></tr>
}else{
/* Text value setting */
@ <tr><td align="right">
@ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
@ </td><td>
entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/,
pSetting->def, 0);
@ </td></tr>
}
}
@ </tbody></table>
@ <input type='submit' name='submit' value='Apply changes'>
@ </form>
}
style_finish_page();
|
| ︙ | ︙ | |||
1907 1908 1909 1910 1911 1912 1913 |
srchFlags = search_restrict(SRCH_FORUM);
if( !g.perm.RdForum ){
login_needed(g.anon.RdForum);
return;
}
cgi_check_for_malice();
style_set_current_feature("forum");
| > | | 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 |
srchFlags = search_restrict(SRCH_FORUM);
if( !g.perm.RdForum ){
login_needed(g.anon.RdForum);
return;
}
cgi_check_for_malice();
style_set_current_feature("forum");
style_header("%s%s", db_get("forum-title","Forum"),
isSearch ? " Search Results" : "");
style_submenu_element("Timeline", "%R/timeline?ss=v&y=f&vfx");
if( g.perm.WrForum ){
style_submenu_element("New Thread","%R/forumnew");
}else{
/* Can't combine this with previous case using the ternary operator
* because that causes an error yelling about "non-constant format"
* with some compilers. I can't see it, since both expressions have
|
| ︙ | ︙ |
Changes to src/fossil.diff.js.
1 2 3 4 5 6 7 8 9 10 11 12 |
/**
diff-related JS APIs for fossil.
*/
"use strict";
window.fossil.onPageLoad(function(){
/**
Adds toggle checkboxes to each file entry in the diff views for
/info and similar pages.
*/
const D = window.fossil.dom;
const addToggle = function(diffElem){
const sib = diffElem.previousElementSibling,
| > | > > > > > | > > > | > > > > | > > > > > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/**
diff-related JS APIs for fossil.
*/
"use strict";
window.fossil.onPageLoad(function(){
/**
Adds toggle checkboxes to each file entry in the diff views for
/info and similar pages.
*/
const D = window.fossil.dom;
const allToggles = [/*collection of all diff-toggle checkboxes */];
const addToggle = function(diffElem){
const sib = diffElem.previousElementSibling,
btnOne = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
if(!sib) return;
const lblToggle = D.append(D.label(null, " Toggle "), btnOne);
const wrapper = D.append(D.span(), lblToggle);
const btnAll = D.button("all");
btnAll.$cb = btnOne;
allToggles.push(btnOne);
D.append(sib, D.append(wrapper, lblToggle, D.text(" "), btnAll));
btnOne.addEventListener('change', function(){
diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
}, false);
btnAll.addEventListener('click', function(){
/* Toggle all entries to match this line's new state. Note that
we use click() instead of cb.checked=... so that the
on-change event handler fires. */
const checked = !this.$cb.checked;
allToggles.forEach( (cb)=>{
if(cb.checked!==checked) cb.click();
});
}, false);
};
if( !document.querySelector('body.fdiff') ){
/* Don't show the diff toggle button for /fdiff because it only
has a single file to show (and also a different DOM layout). */
document.querySelectorAll('table.diff').forEach(addToggle);
}
});
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const Diff = F.diff = {
e:{/*certain cached DOM elements*/},
config: {
|
| ︙ | ︙ | |||
636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
let cbSync /* scroll-sync checkbox */;
let eToggleParent /* element to put the sync-scroll checkbox in */;
const potentialParents = [ /* possible parents for the checkbox */
/* Put the most likely pages at the end, as array.pop() is more
efficient than array.shift() (see loop below). */
/* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
/* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
/* /vdiff */ 'body.vdiff form div.submenu',
/* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
];
while( potentialParents.length ){
if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
break;
}
| > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
let cbSync /* scroll-sync checkbox */;
let eToggleParent /* element to put the sync-scroll checkbox in */;
const potentialParents = [ /* possible parents for the checkbox */
/* Put the most likely pages at the end, as array.pop() is more
efficient than array.shift() (see loop below). */
/* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
/* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
/* /fdiff */ 'body.fdiff form div.submenu',
/* /vdiff */ 'body.vdiff form div.submenu',
/* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
];
while( potentialParents.length ){
if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
break;
}
|
| ︙ | ︙ |
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
*/
dom.label = function(forElem, text){
const rc = document.createElement('label');
if(forElem){
if(forElem instanceof HTMLElement){
forElem = this.attr(forElem, 'id');
}
| > | > | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
*/
dom.label = function(forElem, text){
const rc = document.createElement('label');
if(forElem){
if(forElem instanceof HTMLElement){
forElem = this.attr(forElem, 'id');
}
if(forElem){
dom.attr(rc, 'for', forElem);
}
}
if(text) this.append(rc, text);
return rc;
};
/**
Returns an IMG element with an optional src
attribute value.
|
| ︙ | ︙ |
Changes to src/fossil.page.chat.js.
| ︙ | ︙ | |||
2130 2131 2132 2133 2134 2135 2136 |
}
Chat.e.inputElementWrapper.classList[
s.value ? 'add' : 'remove'
]('compact');
Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus();
});
Chat.settings.addListener('edit-ctrl-send',function(s){
| | | 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 |
}
Chat.e.inputElementWrapper.classList[
s.value ? 'add' : 'remove'
]('compact');
Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus();
});
Chat.settings.addListener('edit-ctrl-send',function(s){
const label = (s.value ? "Ctrl-" : "")+"Enter submits message.";
Chat.e.inputFields.forEach((e)=>{
const v = e.dataset.placeholder0 + " " +label;
if(e.isContentEditable) e.dataset.placeholder = v;
else D.attr(e,'placeholder',v);
});
Chat.e.btnSubmit.title = label;
});
|
| ︙ | ︙ |
Changes to src/fossil.page.pikchrshowasm.js.
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
};
const renderCurrentText = function(){
const text = getCurrentText();
if(text) PS.render(text);
};
const setCurrentText = function(txt){
taInput.value = txt;
| | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
};
const renderCurrentText = function(){
const text = getCurrentText();
if(text) PS.render(text);
};
const setCurrentText = function(txt){
taInput.value = txt;
renderCurrentText();
};
PS.e.btnRender.addEventListener('click',function(ev){
ev.preventDefault();
renderCurrentText();
},false);
/** To be called immediately before work is sent to the
|
| ︙ | ︙ | |||
425 426 427 428 429 430 431 432 433 |
const btnToggle = E(fs,'legend > .fieldset-toggle'),
content = EAll(fs,':scope > div');
btnToggle.addEventListener('click', function(){
fs.classList.toggle('collapsed');
content.forEach((d)=>d.classList.toggle('hidden'));
}, false);
});
PS.e.btnRender.click();
| > > > > > > > > > > > > | | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
const btnToggle = E(fs,'legend > .fieldset-toggle'),
content = EAll(fs,':scope > div');
btnToggle.addEventListener('click', function(){
fs.classList.toggle('collapsed');
content.forEach((d)=>d.classList.toggle('hidden'));
}, false);
});
if(window.sessionStorage){
/* If sessionStorage['pikchr-xfer'] exists and the "fromSession"
URL argument was passed to this page, load the pikchr source
from the session. This is used by the "open in pikchrshow"
link in the forum. */
const src = window.sessionStorage.getItem('pikchr-xfer');
if( src && (new URL(self.location.href).searchParams).has('fromSession') ){
taInput.value = src;
window.sessionStorage.removeItem('pikchr-xfer');
}
}
PS.e.btnRender.click();
/** Debounce handler for auto-rendering while typing. */
const debounceAutoRender = F.debounce(function f(){
if(!PS._isDirty) return;
const text = getCurrentText();
if(f._ === text){
PS._isDirty = false;
return;
|
| ︙ | ︙ | |||
616 617 618 619 620 621 622 |
"Charlie" above aligned
line from 4th box.sw+(0.2,0) up until even with 4th box.n \
"Darlene" above aligned
# fill in content for the Alice lane
right
A1: circle rad 0.1in at end of first line + (0.2,-0.2) \
| | | 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
"Charlie" above aligned
line from 4th box.sw+(0.2,0) up until even with 4th box.n \
"Darlene" above aligned
# fill in content for the Alice lane
right
A1: circle rad 0.1in at end of first line + (0.2,-0.2) \
fill white thickness 1.5px "1"
arrow right 50%
circle same "2"
arrow right until even with first box.e - (0.65,0.0)
ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px
A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0
arrow from A1 to last circle chop "fork!" below aligned
|
| ︙ | ︙ |
Changes to src/fossil.pikchr.js.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
already dealt with.
This code expects the following structure around the SVGs, and
will not process any which don't match this:
<DIV.pikchr-wrapper>
<DIV.pikchr-svg><SVG.pikchr></SVG></DIV>
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > | < > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
already dealt with.
This code expects the following structure around the SVGs, and
will not process any which don't match this:
<DIV.pikchr-wrapper>
<DIV.pikchr-svg><SVG.pikchr></SVG></DIV>
<DIV.pikchr-src>
<PRE>pikchr source code</PRE>
<SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN>
</DIV>
</DIV>
*/
P.addSrcView = function f(svg){
if(!f.hasOwnProperty('parentClick')){
f.parentClick = function(ev){
if(ev.altKey || ev.metaKey || ev.ctrlKey
/* Every combination of special key (alt, shift, ctrl,
meta) is handled differently everywhere. Shift is used
by the browser, Ctrl doesn't work on an iMac, and Alt is
intercepted by most Linux window managers to control
window movement! So... we just listen for *any* of them
(except Shift) and the user will need to find one which
works on on their environment. */
|| this.classList.contains('toggle')){
this.classList.toggle('source');
ev.stopPropagation();
ev.preventDefault();
}
};
/**
Event handler for the "open in pikchrshow" links: store the
source code for the link's pikchr in
window.sessionStorage['pikchr-xfer'] then open
/pikchrshow?fromSession to trigger loading of that pikchr.
*/
f.clickPikchrShow = function(ev){
const pId = this.dataset['pikchrid'] /* ID of the associated pikchr source code element */;
if(!pId) return;
const ePikchr = this.parentNode.parentNode.querySelector('#'+pId);
if(!ePikchr) return;
ev.stopPropagation() /* keep pikchr source view from toggling */;
window.sessionStorage.setItem('pikchr-xfer', ePikchr.innerText);
/*
After returning from this function the link element will
open [/pikchrshow?fromSession], and pikchrshow will extract
the pikchr source code from sessionStorage['pikchr-xfer'].
Quirks of this ^^^ design:
We use only a single slot in sessionStorage. We could
alternately use a key like pikchr-$pId and pass that key on
to /pikchrshow via fromSession=pikchr-$pId, but that would
eventually lead to stale session entries if loading of
pikchrshow were interrupted at an untimely point. The
down-side of _not_ doing that is that some user (or
automation) options multiple "open in pikchrshow" links
rapidly enough, the will open the same pikchr (the one which
was stored in the session's slot most recently). The
current approach should be fine for normal human interaction
speeds, but if it proves to be a problem we can instead use
the above-described approach of storing each pikchr in its
own session slot and simply accept that there may be stale
entries at some point.
*/
};
};
if(!svg) svg = 'svg.pikchr';
if('string' === typeof svg){
document.querySelectorAll(svg).forEach((e)=>f.call(this, e));
return this;
}else if(svg.forEach){
svg.forEach((e)=>f.call(this, e));
return this;
}
if(svg.dataset.pikchrProcessed){
return this;
}
svg.dataset.pikchrProcessed = 1;
const parent = svg.parentNode.parentNode /* outermost DIV.pikchr-wrapper */;
const srcView = parent ? svg.parentNode.nextElementSibling /* DIV.pikchr-src */ : undefined;
if(srcView && srcView.classList.contains('pikchr-src')){
/* Without this element, there's nothing for us to do here. */
parent.addEventListener('click', f.parentClick, false);
const eSpan = window.sessionStorage
? srcView.querySelector('span') /* "open in..." link wrapper */
: undefined;
if(eSpan){
const openLink = eSpan.querySelector('a');
if(openLink){
openLink.addEventListener('click', f.clickPikchrShow, false);
eSpan.classList.remove('hidden');
}
}
}
return this;
};
})(window.fossil);
|
Changes to src/glob.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')" ** ** Commas and whitespace are considered to be element delimters. Each ** element of the GLOB list may optionally be enclosed in either '...' or ** "...". This allows commas and/or whitespace to be used in the elements ** themselves. ** | | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
**
** Commas and whitespace are considered to be element delimters. Each
** element of the GLOB list may optionally be enclosed in either '...' or
** "...". This allows commas and/or whitespace to be used in the elements
** themselves.
**
** The returned string is owned by the caller, who must fossil_free()
** it.
*/
char *glob_expr(const char *zVal, const char *zGlobList){
Blob expr;
const char *zSep = "(";
int nTerm = 0;
int i;
int cTerm;
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
** is written into that second file instead of being written on standard
** output. Use the "--out OUTPUT" option to specify an output file for
** a GET request where there is no PAYLOAD.
**
** Options:
** --compress Use ZLIB compression on the payload
** --mimetype TYPE Mimetype of the payload
** --out FILE Store the reply in FILE
** -v Verbose output
** --xfer PAYLOAD in a Fossil xfer protocol message
*/
void test_httpmsg_command(void){
const char *zMimetype;
const char *zInFile;
const char *zOutFile;
Blob in, out;
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
zMimetype = find_option("mimetype",0,1);
zOutFile = find_option("out","o",1);
if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE;
if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS;
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
| > > > > > > | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
** is written into that second file instead of being written on standard
** output. Use the "--out OUTPUT" option to specify an output file for
** a GET request where there is no PAYLOAD.
**
** Options:
** --compress Use ZLIB compression on the payload
** --mimetype TYPE Mimetype of the payload
** --no-cert-verify Disable TLS cert verification
** --out FILE Store the reply in FILE
** -v Verbose output
** --xfer PAYLOAD in a Fossil xfer protocol message
*/
void test_httpmsg_command(void){
const char *zMimetype;
const char *zInFile;
const char *zOutFile;
Blob in, out;
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
zMimetype = find_option("mimetype",0,1);
zOutFile = find_option("out","o",1);
if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE;
if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS;
if( find_option("no-cert-verify",0,0)!=0 ){
#ifdef FOSSIL_ENABLE_SSL
ssl_disable_cert_verification();
#endif
}
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
401 402 403 404 405 406 407 |
char buf[256];
len = BIO_read(bio, buf, sizeof(buf));
blob_append(&reply, buf, len);
bbuf = blob_buffer(&reply);
len = blob_size(&reply);
while(end < len) {
| | | < < < | > | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
char buf[256];
len = BIO_read(bio, buf, sizeof(buf));
blob_append(&reply, buf, len);
bbuf = blob_buffer(&reply);
len = blob_size(&reply);
while(end < len) {
if( bbuf[end]=='\n' ) {
if( (end+1<len && bbuf[end+1]=='\n')
|| (end+2<len && bbuf[end+1]=='\r' && bbuf[end+2]=='\n')
){
done = 1;
break;
}
}
end++;
}
}while(!done);
|
| ︙ | ︙ |
Changes to src/http_transport.c.
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
#endif
/*
** Initialize a Blob to the name of the configured SSH command.
*/
void transport_ssh_command(Blob *p){
char *zSsh; /* The base SSH command */
| > > | > | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
#endif
/*
** Initialize a Blob to the name of the configured SSH command.
*/
void transport_ssh_command(Blob *p){
char *zSsh; /* The base SSH command */
zSsh = g.zSshCmd;
if( zSsh==0 || zSsh[0]==0 ){
zSsh = db_get("ssh-command", zDefaultSshCmd);
}
blob_init(p, zSsh, -1);
}
/*
** SSH initialization of the transport layer
*/
int transport_ssh_open(UrlData *pUrlData){
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
370 371 372 373 374 375 376 |
const char *zName, /* Name of the file that has changed */
const char *zOld, /* blob.uuid before change. NULL for added files */
const char *zNew, /* blob.uuid after change. NULL for deletes */
const char *zOldName, /* Prior name. NULL if no name change. */
DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
int mperm /* executable or symlink permission for zNew */
){
| | > > > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
const char *zName, /* Name of the file that has changed */
const char *zOld, /* blob.uuid before change. NULL for added files */
const char *zNew, /* blob.uuid after change. NULL for deletes */
const char *zOldName, /* Prior name. NULL if no name change. */
DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
int mperm /* executable or symlink permission for zNew */
){
@ <div class='file-change-line'><span>
/* Maintenance reminder: the extra level of SPAN is for
** arranging new elements via JS. */
if( !g.perm.Hyperlink ){
if( zNew==0 ){
@ Deleted %h(zName).
}else if( zOld==0 ){
@ Added %h(zName).
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
@ Name change from %h(zOldName) to %h(zName).
}else if( fossil_strcmp(zNew, zOld)==0 ){
if( mperm==PERM_EXE ){
@ %h(zName) became executable.
}else if( mperm==PERM_LNK ){
@ %h(zName) became a symlink.
}else{
@ %h(zName) became a regular file.
}
}else{
@ Changes to %h(zName).
}
@ </span></div>
if( pCfg ){
append_diff(zOld, zNew, pCfg);
}
}else{
if( zOld && zNew ){
if( fossil_strcmp(zOld, zNew)!=0 ){
if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
|
| ︙ | ︙ | |||
434 435 436 437 438 439 440 |
}else if( zOld ){
@ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
| > | > | | < | > | > > | < > | 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
}else if( zOld ){
@ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
if( pCfg ){
@ </span></div>
append_diff(zOld, zNew, pCfg);
}else{
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
@ </span></div>
}
}else{
@ </span></div>
}
}
}
/*
** Generate javascript to enhance HTML diffs.
*/
void append_diff_javascript(int diffType){
if( diffType==0 ) return;
|
| ︙ | ︙ | |||
598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_sql_text(&sql));
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
0, 0, 0, rid, 0, 0);
db_finalize(&q);
style_finish_page();
}
/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL: /ci/ARTIFACTID
** OR: /ci?name=ARTIFACTID
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_sql_text(&sql));
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
0, 0, 0, rid, 0, 0);
db_finalize(&q);
style_finish_page();
}
/*
** WEBPAGE: ckout
**
** Show information about the current checkout. This page only functions
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from with on open check-out.
*/
void ckout_page(void){
int vid;
char *zHostname;
char *zCwd;
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
DiffConfig DCfg,*pCfg; /* Diff details */
const char *zHome; /* Home directory */
Stmt q;
if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
cgi_redirect("%R/home");
return;
}
diffType = preferred_diff_type();
pCfg = construct_diff_flags(diffType, &DCfg);
vid = db_lget_int("checkout", 0);
db_unprotect(PROTECT_ALL);
vfile_check_signature(vid, CKSIG_ENOTFILE);
db_protect_pop();
style_set_current_feature("vinfo");
zHostname = fossil_hostname();
zCwd = file_getcwd(0,0);
zHome = fossil_getenv("HOME");
if( zHome ){
int nHome = (int)strlen(zHome);
if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
zCwd = mprintf("~%s", zCwd+nHome);
}
}
if( zHostname ){
style_header("Checkout Status: %h on %h", zCwd, zHostname);
}else{
style_header("Checkout Status: %h", zCwd);
}
render_checkin_context(vid, 0, 0, 0);
if( pCfg==0 ){
style_finish_page();
return;
}
db_prepare(&q,
/* 0 1 2 3 4 5 6 */
"SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
" FROM vfile LEFT JOIN blob USING(rid)"
" WHERE vid=%d"
" AND (deleted OR chnged OR rid==0)"
" ORDER BY pathname /*scan*/",
vid
);
if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
while( db_step(&q)==SQLITE_ROW ){
const char *zTreename = db_column_text(&q,0);
int isDeleted = db_column_int(&q, 1);
int isChnged = db_column_int(&q,2);
int isNew = db_column_int(&q,3);
int srcid = db_column_int(&q, 4);
int isLink = db_column_int(&q, 5);
const char *zUuid = db_column_text(&q, 6);
int showDiff = 1;
pCfg->diffFlags &= (~DIFF_FILE_MASK);
if( isDeleted ){
@ <p>DELETED %h(zTreename)</p>
pCfg->diffFlags |= DIFF_FILE_DELETED;
showDiff = 0;
}else if( file_access(zTreename, F_OK) ){
@ <p>MISSING %h(zTreename)</p>
showDiff = 0;
}else if( isNew ){
@ <p>ADDED %h(zTreename)</p>
pCfg->diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
showDiff = 0;
}else if( isChnged==3 ){
@ <p>ADDED_BY_MERGE %h(zTreename)</p>
pCfg->diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
showDiff = 0;
}else if( isChnged==5 ){
@ <p>ADDED_BY_INTEGRATE %h(zTreename)</p>
pCfg->diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
showDiff = 0;
}else{
@ <p>CHANGED %h(zTreename)</p>
}
if( showDiff ){
Blob old, new;
if( !isLink != !file_islink(zTreename) ){
@ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
continue;
}
if( srcid>0 ){
content_get(srcid, &old);
pCfg->zLeftHash = zUuid;
}else{
blob_zero(&old);
pCfg->zLeftHash = 0;
}
blob_read_from_file(&new, zTreename, ExtFILE);
text_diff(&old, &new, cgi_output_blob(), pCfg);
blob_reset(&old);
blob_reset(&new);
}
}
db_finalize(&q);
append_diff_javascript(diffType);
style_finish_page();
}
/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL: /ci/ARTIFACTID
** OR: /ci?name=ARTIFACTID
**
|
| ︙ | ︙ | |||
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 | Manifest *pFrom, *pTo; ManifestFile *pFileFrom, *pFileTo; const char *zBranch; const char *zFrom; const char *zTo; const char *zRe; const char *zGlob; char *zMergeOrigin = 0; ReCompiled *pRe = 0; DiffConfig DCfg, *pCfg = 0; int graphFlags = 0; | > | > > | 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 |
Manifest *pFrom, *pTo;
ManifestFile *pFileFrom, *pFileTo;
const char *zBranch;
const char *zFrom;
const char *zTo;
const char *zRe;
const char *zGlob;
Glob * pGlob = 0;
char *zMergeOrigin = 0;
ReCompiled *pRe = 0;
DiffConfig DCfg, *pCfg = 0;
int graphFlags = 0;
Blob qp; /* non-glob= query parameters for generated links */
Blob qpGlob; /* glob= query parameter for generated links */
int bInvert = PB("inv");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
login_anonymous_available();
fossil_nice_default();
blob_init(&qp, 0, 0);
blob_init(&qpGlob, 0, 0);
diffType = preferred_diff_type();
zRe = P("regex");
if( zRe ) re_compile(&pRe, zRe, 0);
zBranch = P("branch");
if( zBranch && zBranch[0]==0 ) zBranch = 0;
if( zBranch ){
blob_appendf(&qp, "branch=%T", zBranch);
|
| ︙ | ︙ | |||
1244 1245 1246 1247 1248 1249 1250 |
zTo = zFrom;
zFrom = zTemp;
}
if( zGlob ){
if( !*zGlob ){
zGlob = NULL;
}else{
| | > | | > | | > | 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 |
zTo = zFrom;
zFrom = zTemp;
}
if( zGlob ){
if( !*zGlob ){
zGlob = NULL;
}else{
blob_appendf(&qpGlob, "&glob=%T", zGlob);
pGlob = glob_create(zGlob);
}
}
if( PB("nc") ){
graphFlags |= TIMELINE_NOCOLOR;
blob_appendf(&qp, "&nc");
}
pCfg = construct_diff_flags(diffType, &DCfg);
if( DCfg.diffFlags & DIFF_IGNORE_ALLWS ){
blob_appendf(&qp, "&w");
}
cgi_check_for_malice();
style_set_current_feature("vdiff");
if( zBranch==0 ){
style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
}
if( diffType!=0 ){
style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob);
}
if( diffType!=2 ){
style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
&qpGlob);
}
if( diffType!=1 ) {
style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b%b", &qp, &qpGlob);
}
if( zBranch==0 ){
style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b%b", diffType,
&qp, &qpGlob);
}
if( zGlob ){
style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp);
}else{
style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo,
(DCfg.diffFlags & DIFF_IGNORE_ALLWS)?"&w":"");
}
|
| ︙ | ︙ | |||
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 |
}
if( zGlob ){
@ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
}
@<hr><p>
}
blob_reset(&qp);
manifest_file_rewind(pFrom);
pFileFrom = manifest_file_next(pFrom, 0);
manifest_file_rewind(pTo);
pFileTo = manifest_file_next(pTo, 0);
DCfg.pRe = pRe;
while( pFileFrom || pFileTo ){
int cmp;
if( pFileFrom==0 ){
cmp = +1;
}else if( pFileTo==0 ){
cmp = -1;
}else{
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
}
if( cmp<0 ){
| > | | | | > | 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 |
}
if( zGlob ){
@ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
}
@<hr><p>
}
blob_reset(&qp);
blob_reset(&qpGlob);
manifest_file_rewind(pFrom);
pFileFrom = manifest_file_next(pFrom, 0);
manifest_file_rewind(pTo);
pFileTo = manifest_file_next(pTo, 0);
DCfg.pRe = pRe;
while( pFileFrom || pFileTo ){
int cmp;
if( pFileFrom==0 ){
cmp = +1;
}else if( pFileTo==0 ){
cmp = -1;
}else{
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
}
if( cmp<0 ){
if( !pGlob || glob_match(pGlob, pFileFrom->zName) ){
append_file_change_line(zFrom, pFileFrom->zName,
pFileFrom->zUuid, 0, 0, pCfg, 0);
}
pFileFrom = manifest_file_next(pFrom, 0);
}else if( cmp>0 ){
if( !pGlob || glob_match(pGlob, pFileTo->zName) ){
append_file_change_line(zTo, pFileTo->zName,
0, pFileTo->zUuid, 0, pCfg,
manifest_file_mperm(pFileTo));
}
pFileTo = manifest_file_next(pTo, 0);
}else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
pFileFrom = manifest_file_next(pFrom, 0);
pFileTo = manifest_file_next(pTo, 0);
}else{
if(!pGlob || (glob_match(pGlob, pFileFrom->zName)
|| glob_match(pGlob, pFileTo->zName)) ){
append_file_change_line(zFrom, pFileFrom->zName,
pFileFrom->zUuid,
pFileTo->zUuid, 0, pCfg,
manifest_file_mperm(pFileTo));
}
pFileFrom = manifest_file_next(pFrom, 0);
pFileTo = manifest_file_next(pTo, 0);
}
}
glob_free(pGlob);
manifest_destroy(pFrom);
manifest_destroy(pTo);
append_diff_javascript(diffType);
style_finish_page();
}
#if INTERFACE
|
| ︙ | ︙ |
Changes to src/interwiki.c.
| ︙ | ︙ | |||
273 274 275 276 277 278 279 280 281 |
int n = 0;
Stmt q;
db_prepare(&q,
"SELECT substr(name,11), value->>'base'"
" FROM config WHERE name glob 'interwiki:*' AND json_valid(value)"
" ORDER BY name;"
);
while( db_step(&q)==SQLITE_ROW ){
if( n==0 ){
| > | | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
int n = 0;
Stmt q;
db_prepare(&q,
"SELECT substr(name,11), value->>'base'"
" FROM config WHERE name glob 'interwiki:*' AND json_valid(value)"
" ORDER BY name;"
);
blob_append(out, "<blockquote>", -1);
while( db_step(&q)==SQLITE_ROW ){
if( n==0 ){
blob_appendf(out, "<table>\n");
}
blob_appendf(out,"<tr><td>%h</td><td> → </td>",
db_column_text(&q,0));
blob_appendf(out,"<td>%h</td></tr>\n",
db_column_text(&q,1));
n++;
}
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
749 750 751 752 753 754 755 |
@ <input type="hidden" name="g" value="%h(zGoto)">
}
if( anonFlag ){
@ <input type="hidden" name="anon" value="1">
}
if( g.zLogin ){
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
| | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
@ <input type="hidden" name="g" value="%h(zGoto)">
}
if( anonFlag ){
@ <input type="hidden" name="anon" value="1">
}
if( g.zLogin ){
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
@ <input type="submit" name="out" value="Logout" autofocus></p>
@ </form>
}else{
unsigned int uSeed = captcha_seed();
if( g.zLogin==0 && (anonFlag || zGoto==0) ){
zAnonPw = db_text(0, "SELECT pw FROM user"
" WHERE login='anonymous'"
" AND cap!=''");
|
| ︙ | ︙ | |||
775 776 777 778 779 780 781 |
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
}
@ </span></td></tr>
}
@ <tr>
@ <td class="form_label" id="userlabel1">User ID:</td>
@ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
| | | 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 |
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
}
@ </span></td></tr>
}
@ <tr>
@ <td class="form_label" id="userlabel1">User ID:</td>
@ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
@ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td>
@ </tr>
@ <tr>
@ <td class="form_label" id="pswdlabel">Password:</td>
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
@ name="p" value="" size="30">\
if( zAnonPw && !noAnon ){
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
|
| ︙ | ︙ | |||
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
** are applied if this setting is undefined or is
** an empty string.
*/
void login_restrict_robot_access(void){
const char *zReferer;
const char *zGlob;
int isMatch = 1;
if( g.zLogin!=0 ) return;
zGlob = db_get("robot-restrict",0);
if( zGlob==0 || zGlob[0]==0 ) return;
if( g.isHuman ){
zReferer = P("HTTP_REFERER");
if( zReferer && zReferer[0]!=0 ) return;
}
| > | > > > > > > > > > > > | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 |
** are applied if this setting is undefined or is
** an empty string.
*/
void login_restrict_robot_access(void){
const char *zReferer;
const char *zGlob;
int isMatch = 1;
int nQP; /* Number of query parameters other than name= */
if( g.zLogin!=0 ) return;
zGlob = db_get("robot-restrict",0);
if( zGlob==0 || zGlob[0]==0 ) return;
if( g.isHuman ){
zReferer = P("HTTP_REFERER");
if( zReferer && zReferer[0]!=0 ) return;
}
nQP = cgi_qp_count();
if( nQP<1 ) return;
isMatch = glob_multi_match(zGlob, g.zPath);
if( !isMatch ) return;
/* Check for exceptions to the restriction on the number of query
** parameters. */
zGlob = db_get("robot-restrict-qp",0);
if( zGlob && zGlob[0] ){
char *zPath = mprintf("%s/%d", g.zPath, nQP);
isMatch = glob_multi_match(zGlob, zPath);
fossil_free(zPath);
if( isMatch ) return;
}
/* If we reach this point, it means we have a situation where we
** want to restrict the activity of a robot.
*/
g.isHuman = 0;
(void)exclude_spiders(0);
cgi_reply();
|
| ︙ | ︙ | |||
2241 2242 2243 2244 2245 2246 2247 |
@ <input type="hidden" name="g" value="%h(P("g"))">
}
@ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
@ <table class="login_out">
@ <tr>
@ <td class="form_label" align="right" id="uid">User ID:</td>
@ <td><input aria-labelledby="uid" type="text" name="u" \
| | | 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 |
@ <input type="hidden" name="g" value="%h(P("g"))">
}
@ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
@ <table class="login_out">
@ <tr>
@ <td class="form_label" align="right" id="uid">User ID:</td>
@ <td><input aria-labelledby="uid" type="text" name="u" \
@ value="%h(zUserID)" size="30" autofocus></td>
@
if( iErrLine==1 ){
@ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr>
}
@ <tr>
@ <td class="form_label" align="right" id="dpyname">Display Name:</td>
@ <td><input aria-labelledby="dpyname" type="text" name="dn" \
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
2038 2039 2040 2041 2042 2043 2044 |
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
| | > | > | > > > > | > | 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 |
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
/* Second special case: If the PATH_INFO is blank, issue a redirect:
** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set.
** (2) to the home page identified by the "index-page" setting
** in the repository CONFIG table
** (3) to "/index" if there no "index-page" setting in CONFIG
*/
#ifdef FOSSIL_ENABLE_JSON
if(g.json.isJsonMode){
json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
fossil_exit(0);
}
#endif
if( g.useLocalauth && g.localOpen ){
cgi_redirectf("%R/ckout");
}else{
fossil_redirect_home() /*does not return*/;
}
}else{
zPath = mprintf("%s", zPathInfo);
}
/* Make g.zPath point to the first element of the path. Make
** g.zExtra point to everything past that point.
*/
|
| ︙ | ︙ | |||
3354 3355 3356 3357 3358 3359 3360 |
** chdir to that check-out so that the current version
** gets highlighted in the timeline by default. */
const char * zDir = g.argv[2];
if(dir_has_ckout_db(zDir)){
if(0!=file_chdir(zDir, 0)){
fossil_fatal("Cannot chdir to %s", zDir);
}
| | | 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 |
** chdir to that check-out so that the current version
** gets highlighted in the timeline by default. */
const char * zDir = g.argv[2];
if(dir_has_ckout_db(zDir)){
if(0!=file_chdir(zDir, 0)){
fossil_fatal("Cannot chdir to %s", zDir);
}
findServerArg = g.argc;
fCreate = 0;
g.argv[2] = 0;
--g.argc;
}
}
if( isUiCmd && 3==g.argc
&& (zRemote = (char*)file_skip_userhost(g.argv[2]))!=0
|
| ︙ | ︙ | |||
3382 3383 3384 3385 3386 3387 3388 |
g.useLocalauth = 1;
allowRepoList = 1;
}
if( !zRemote ){
find_server_repository(findServerArg, fCreate);
}
if( zInitPage==0 ){
| < < < | < | 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 |
g.useLocalauth = 1;
allowRepoList = 1;
}
if( !zRemote ){
find_server_repository(findServerArg, fCreate);
}
if( zInitPage==0 ){
zInitPage = "";
}
if( zPort ){
if( strchr(zPort,':') ){
int i;
for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
if( i>0 ){
if( zPort[0]=='[' && zPort[i-1]==']' ){
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
246 247 248 249 250 251 252 253 254 255 256 257 258 259 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ | > | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/merge.tcl \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ |
| ︙ | ︙ | |||
702 703 704 705 706 707 708 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = | | | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 LINENOISE_DEF. = $(LINENOISE_DEF.0) LINENOISE_OBJ.0 = LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o LINENOISE_OBJ. = $(LINENOISE_OBJ.0) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
2332 2333 2334 2335 2336 2337 2338 |
*/
int manifest_crosslink(int rid, Blob *pContent, int flags){
int i, rc = TH_OK;
Manifest *p;
int parentid = 0;
int permitHooks = (flags & MC_PERMIT_HOOKS);
const char *zScript = 0;
| | | 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 |
*/
int manifest_crosslink(int rid, Blob *pContent, int flags){
int i, rc = TH_OK;
Manifest *p;
int parentid = 0;
int permitHooks = (flags & MC_PERMIT_HOOKS);
const char *zScript = 0;
char *zUuid = 0;
if( g.fSqlTrace ){
fossil_trace("-- manifest_crosslink(%d)\n", rid);
}
manifest_create_event_triggers();
if( (p = manifest_cache_find(rid))!=0 ){
blob_reset(pContent);
|
| ︙ | ︙ | |||
2368 2369 2370 2371 2372 2373 2374 |
}
return 0;
}
db_begin_transaction();
if( p->type==CFTYPE_MANIFEST ){
if( permitHooks ){
zScript = xfer_commit_code();
| | | 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 |
}
return 0;
}
db_begin_transaction();
if( p->type==CFTYPE_MANIFEST ){
if( permitHooks ){
zScript = xfer_commit_code();
zUuid = rid_to_uuid(rid);
}
if( p->nCherrypick && db_table_exists("repository","cherrypick") ){
int i;
for(i=0; i<p->nCherrypick; i++){
db_multi_exec(
"REPLACE INTO cherrypick(parentid,childid,isExclude)"
" SELECT rid, %d, %d FROM blob WHERE uuid=%Q",
|
| ︙ | ︙ | |||
2720 2721 2722 2723 2724 2725 2726 |
" Edit [%!S|%S]:",
zTagUuid, zTagUuid);
branchMove = 0;
if( permitHooks && db_exists("SELECT 1 FROM event, blob"
" WHERE event.type='ci' AND event.objid=blob.rid"
" AND blob.uuid=%Q", zTagUuid) ){
zScript = xfer_commit_code();
| > | | 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 |
" Edit [%!S|%S]:",
zTagUuid, zTagUuid);
branchMove = 0;
if( permitHooks && db_exists("SELECT 1 FROM event, blob"
" WHERE event.type='ci' AND event.objid=blob.rid"
" AND blob.uuid=%Q", zTagUuid) ){
zScript = xfer_commit_code();
fossil_free(zUuid);
zUuid = fossil_strdup(zTagUuid);
}
}
zName = p->aTag[i].zName;
zValue = p->aTag[i].zValue;
if( strcmp(zName, "*branch")==0 ){
blob_appendf(&comment,
" Move to branch [/timeline?r=%h&nd&dp=%!S&unhide | %h].",
|
| ︙ | ︙ | |||
2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 |
);
blob_reset(&comment);
}
if( p->type==CFTYPE_FORUM ){
int froot, fprev, firt;
char *zFType;
char *zTitle;
schema_forum();
search_doc_touch('f', rid, 0);
froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid;
fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0;
firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0;
db_multi_exec(
"REPLACE INTO forumpost(fpid,froot,fprev,firt,fmtime)"
| > > | 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 |
);
blob_reset(&comment);
}
if( p->type==CFTYPE_FORUM ){
int froot, fprev, firt;
char *zFType;
char *zTitle;
assert( 0==zUuid );
schema_forum();
search_doc_touch('f', rid, 0);
froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid;
fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0;
firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0;
db_multi_exec(
"REPLACE INTO forumpost(fpid,froot,fprev,firt,fmtime)"
|
| ︙ | ︙ | |||
2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 |
db_end_transaction(0);
if( permitHooks ){
rc = xfer_run_common_script();
if( rc==TH_OK ){
rc = xfer_run_script(zScript, zUuid, 0);
}
}
if( p->type==CFTYPE_MANIFEST ){
manifest_cache_insert(p);
}else{
manifest_destroy(p);
}
assert( blob_is_reset(pContent) );
return ( rc!=TH_ERROR );
| > | 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 |
db_end_transaction(0);
if( permitHooks ){
rc = xfer_run_common_script();
if( rc==TH_OK ){
rc = xfer_run_script(zScript, zUuid, 0);
}
}
fossil_free(zUuid);
if( p->type==CFTYPE_MANIFEST ){
manifest_cache_insert(p);
}else{
manifest_destroy(p);
}
assert( blob_is_reset(pContent) );
return ( rc!=TH_ERROR );
|
| ︙ | ︙ |
Changes to src/merge.c.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
** This file contains code used to merge two or more branches into
** a single tree.
*/
#include "config.h"
#include "merge.h"
#include <assert.h>
/*
** Print information about a particular check-in.
*/
void print_checkin_description(int rid, int indent, const char *zLabel){
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,toLocal()),"
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
** This file contains code used to merge two or more branches into
** a single tree.
*/
#include "config.h"
#include "merge.h"
#include <assert.h>
/*
** Bring up a Tcl/Tk GUI to show details of the most recent merge.
*/
static void merge_info_tk(int bDark, int bAll, int nContext){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
}
/* The undocumented --script FILENAME option causes the Tk script to
** be written into the FILENAME instead of being run. This is used
** for testing and debugging. */
zTempFile = find_option("script",0,1);
verify_all_options();
blob_zero(&script);
blob_appendf(&script, "set ncontext %d\n", nContext);
blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
g.nameOfExe);
blob_appendf(&script, "set filelist [list");
if( g.argc==2 ){
/* No files named on the command-line. Use every file mentioned
** in the MERGESTAT table to generate the file list. */
Stmt q;
int cnt = 0;
db_prepare(&q,
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
"('MERGE',1),('ADDED',2),('UPDATE',2))"
"SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)"
" %s ORDER BY pri, 1",
bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&script," %s ", db_column_text(&q,1));
blob_append_tcl_literal(&script, db_column_text(&q,0),
db_column_bytes(&q,0));
cnt++;
}
db_finalize(&q);
if( cnt==0 ){
fossil_print(
"No interesting changes in this merge. Use --all to see everything\n"
);
return;
}
}else{
/* Use only files named on the command-line in the file list.
** But verify each file named is actually found in the MERGESTAT
** table first. */
for(i=2; i<g.argc; i++){
char *zFile; /* Input filename */
char *zTreename; /* Name of the file in the tree */
Blob fname; /* Filename relative to root */
char *zOp; /* Operation on this file */
zFile = mprintf("%/", g.argv[i]);
file_tree_name(zFile, &fname, 0, 1);
fossil_free(zFile);
zTreename = blob_str(&fname);
zOp = db_text(0, "SELECT op FROM mergestat WHERE fn=%Q or fnr=%Q",
zTreename, zTreename);
blob_appendf(&script, " %s ", zOp);
fossil_free(zOp);
blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename));
blob_reset(&fname);
}
}
blob_appendf(&script, "]\n");
blob_appendf(&script, "set darkmode %d\n", bDark!=0);
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
blob_size(&script), 1, 1, 0)==TCL_OK ){
blob_reset(&script);
return;
}
/*
* If evaluation of the Tcl script fails, the reason may be that Tk
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** Generate a TCL list on standard output that can be fed into the
** merge.tcl script to show the details of the most recent merge
** command associated with file "zFName". zFName must be the filename
** relative to the root of the check-in - in other words a "tree name".
**
** When this routine is called, we know that the mergestat table
** exists, but we do not know if zFName is mentioned in that table.
*/
static void merge_info_tcl(const char *zFName, int nContext){
const char *zTreename;/* Name of the file in the tree */
Stmt q; /* To query the MERGESTAT table */
MergeBuilder mb; /* The merge builder object */
Blob pivot,v1,v2,out; /* Blobs for holding content */
const char *zFN; /* A filename */
int rid; /* RID value */
int sz; /* File size value */
zTreename = zFName;
db_prepare(&q,
/* 0 1 2 3 4 5 6 7 */
"SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr"
" FROM mergestat"
" WHERE fnp=%Q OR fnr=%Q",
zTreename, zTreename
);
if( db_step(&q)!=SQLITE_ROW ){
db_finalize(&q);
fossil_print("ERROR {don't know anything about file: %s}\n", zTreename);
return;
}
mergebuilder_init_tcl(&mb);
mb.nContext = nContext;
/* Set up the pivot */
zFN = db_column_text(&q, 0);
if( zFN==0 ){
/* No pivot because the file was added */
mb.zPivot = "(no baseline)";
blob_zero(&pivot);
}else{
mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
rid = db_column_int(&q, 1);
content_get(rid, &pivot);
}
mb.pPivot = &pivot;
/* Set up the merge-in as V2 */
zFN = db_column_text(&q, 5);
if( zFN==0 ){
/* File deleted in the merged-in branch */
mb.zV2 = "(deleted file)";
blob_zero(&v2);
}else{
mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
rid = db_column_int(&q, 6);
content_get(rid, &v2);
}
mb.pV2 = &v2;
/* Set up the local content as V1 */
zFN = db_column_text(&q, 2);
if( zFN==0 ){
/* File added by merge */
mb.zV1 = "(no original)";
blob_zero(&v1);
}else{
mb.zV1 = mprintf("%s (local)", file_tail(zFN));
rid = db_column_int(&q, 3);
sz = db_column_int(&q, 4);
if( rid==0 && sz>0 ){
/* The origin file had been edited so we'll have to pull its
** original content out of the undo buffer */
Stmt q2;
db_prepare(&q2,
"SELECT content FROM undo"
" WHERE pathname=%Q AND octet_length(content)=%d",
zFN, sz
);
blob_zero(&v1);
if( db_step(&q2)==SQLITE_ROW ){
db_column_blob(&q2, 0, &v1);
}else{
mb.zV1 = "(local content missing)";
}
db_finalize(&q2);
}else{
/* The origin file was unchanged when the merge first occurred */
content_get(rid, &v1);
}
}
mb.pV1 = &v1;
/* Set up the output */
zFN = db_column_text(&q, 7);
if( zFN==0 ){
mb.zOut = "(Merge Result)";
}else{
mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
}
blob_zero(&out);
mb.pOut = &out;
merge_three_blobs(&mb);
blob_write_to_file(&out, "-");
mb.xDestroy(&mb);
blob_reset(&pivot);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&out);
db_finalize(&q);
}
/*
** COMMAND: merge-info
**
** Usage: %fossil merge-info [OPTIONS]
**
** Display information about the most recent merge operation.
**
** Options:
** -a|--all Show all file changes that happened because of
** the merge. Normally only MERGE, CONFLICT, and ERROR
** lines are shown
** -c|--context N Show N lines of context around each change,
** with negative N meaning show all content. Only
** meaningful in combination with --tcl or --tk.
** --dark Use dark mode for the Tcl/Tk-based GUI
** --tcl FILE Generate (to stdout) a TCL list containing
** information needed to display the changes to
** FILE caused by the most recent merge. FILE must
** be a pathname relative to the root of the check-out.
** --tk Bring up a Tcl/Tk GUI that shows the changes
** associated with the most recent merge.
**
*/
void merge_info_cmd(void){
const char *zCnt;
const char *zTcl;
int bTk;
int bDark;
int bAll;
int nContext;
Stmt q;
const char *zWhere;
int cnt = 0;
db_must_be_within_tree();
zTcl = find_option("tcl", 0, 1);
bTk = find_option("tk", 0, 0)!=0;
zCnt = find_option("context", "c", 1);
bDark = find_option("dark", 0, 0)!=0;
bAll = find_option("all", "a", 0)!=0;
if( bTk==0 ){
verify_all_options();
if( g.argc>2 ){
usage("[OPTIONS]");
}
}
if( zCnt ){
nContext = atoi(zCnt);
if( nContext<0 ) nContext = 0xfffffff;
}else{
nContext = 6;
}
if( !db_table_exists("localdb","mergestat") ){
if( zTcl ){
fossil_print("ERROR {no merge data available}\n");
}else{
fossil_print("No merge data is available\n");
}
return;
}
if( bTk ){
merge_info_tk(bDark, bAll, nContext);
return;
}
if( zTcl ){
merge_info_tcl(zTcl, nContext);
return;
}
if( bAll ){
zWhere = "";
}else{
zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
}
db_prepare(&q,
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
"('MERGE',1),('ADDED',2),('UPDATE',2))"
/* 0 1 2 */
"SELECT op, coalesce(fnr,fn), msg"
" FROM mergestat JOIN priority USING(op)"
" %s"
" ORDER BY pri, coalesce(fnr,fn)",
zWhere /*safe-for-%s*/
);
while( db_step(&q)==SQLITE_ROW ){
const char *zOp = db_column_text(&q, 0);
const char *zName = db_column_text(&q, 1);
const char *zErr = db_column_text(&q, 2);
if( zErr && fossil_strcmp(zOp,"CONFLICT")!=0 ){
fossil_print("%-9s %s (%s)\n", zOp, zName, zErr);
}else{
fossil_print("%-9s %s\n", zOp, zName);
}
cnt++;
}
db_finalize(&q);
if( !bAll && cnt==0 ){
fossil_print(
"No interesting changes in this merge. Use --all to see everything.\n"
);
}
}
/*
** Erase all information about prior merges. Do this, for example, after
** a commit.
*/
void merge_info_forget(void){
db_multi_exec(
"DROP TABLE IF EXISTS localdb.mergestat;"
"DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';"
);
}
/*
** Initialize the MERGESTAT table.
**
** Notes about mergestat:
**
** * ridv is a positive integer and sz is NULL if the V file contained
** no local edits prior to the merge. If the V file was modified prior
** to the merge then ridv is NULL and sz is the size of the file prior
** to merge.
**
** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
** added by merge.
*/
void merge_info_init(void){
merge_info_forget();
db_multi_exec(
"CREATE TABLE localdb.mergestat(\n"
" op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
" fnp TEXT, -- Name of the pivot file (P)\n"
" ridp INT, -- RID for the pivot file\n"
" fn TEXT, -- Name of origin file (V)\n"
" ridv INT, -- RID for origin file, or NULL if previously edited\n"
" sz INT, -- Size of origin file in bytes, NULL if unedited\n"
" fnm TEXT, -- Name of the file being merged in (M)\n"
" ridm INT, -- RID for the merge-in file\n"
" fnr TEXT, -- Name of the final output file, after all renaming\n"
" nc INT DEFAULT 0, -- Number of conflicts\n"
" msg TEXT -- Error message\n"
");"
);
}
/*
** Print information about a particular check-in.
*/
void print_checkin_description(int rid, int indent, const char *zLabel){
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,toLocal()),"
|
| ︙ | ︙ | |||
293 294 295 296 297 298 299 300 301 302 303 304 305 306 | ** "merge --cherrypick". ** ** Files which are renamed in the merged-in branch will be renamed in ** the current check-out. ** ** If the VERSION argument is omitted, then Fossil attempts to find ** a recent fork on the current branch to merge. ** ** If there are multiple VERSION arguments, then each VERSION is merged ** (or cherrypicked) in the order that they appear on the command-line. ** ** Options: ** --backout Do a reverse cherrypick merge against VERSION. ** In other words, back out the changes that were | > > > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 | ** "merge --cherrypick". ** ** Files which are renamed in the merged-in branch will be renamed in ** the current check-out. ** ** If the VERSION argument is omitted, then Fossil attempts to find ** a recent fork on the current branch to merge. ** ** Note that this command does not commit the merge, as that is a ** separate step. ** ** If there are multiple VERSION arguments, then each VERSION is merged ** (or cherrypicked) in the order that they appear on the command-line. ** ** Options: ** --backout Do a reverse cherrypick merge against VERSION. ** In other words, back out the changes that were |
| ︙ | ︙ | |||
318 319 320 321 322 323 324 | ** changes back to the nearest common ancestor. ** -f|--force Force the merge even if it would be a no-op ** --force-missing Force the merge even if there is missing content ** --integrate Merged branch will be closed when committing ** -K|--keep-merge-files On merge conflict, retain the temporary files ** used for merging, named *-baseline, *-original, ** and *-merge. | | | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 |
** changes back to the nearest common ancestor.
** -f|--force Force the merge even if it would be a no-op
** --force-missing Force the merge even if there is missing content
** --integrate Merged branch will be closed when committing
** -K|--keep-merge-files On merge conflict, retain the temporary files
** used for merging, named *-baseline, *-original,
** and *-merge.
** -n|--dry-run Do not actually change files on disk
** --nosync Do not auto-sync prior to merging
** -v|--verbose Show additional details of the merge
*/
void merge_cmd(void){
int vid; /* Current version "V" */
int mid; /* Version we are merging from "M" */
int pid = 0; /* The pivot version - most recent common ancestor P */
|
| ︙ | ︙ | |||
795 796 797 798 799 800 801 |
debug_fv_dump( debugFlag>=2 );
}
/************************************************************************
** All of the information needed to do the merge is now contained in the
** FV table. Starting here, we begin to actually carry out the merge.
**
| > > > > > | > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 |
debug_fv_dump( debugFlag>=2 );
}
/************************************************************************
** All of the information needed to do the merge is now contained in the
** FV table. Starting here, we begin to actually carry out the merge.
**
** Begin by constructing the localdb.mergestat table.
*/
merge_info_init();
/*
** Find files that have changed from P->M but not P->V.
** Copy the M content over into V.
*/
db_prepare(&q,
/* 0 1 2 3 4 5 6 7 */
"SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv"
" WHERE idp>0 AND idv>0 AND idm>0"
" AND ridm!=ridp AND ridv=ridp AND NOT chnged"
);
while( db_step(&q)==SQLITE_ROW ){
int idv = db_column_int(&q, 0);
int ridm = db_column_int(&q, 1);
const char *zName = db_column_text(&q, 2);
int islinkm = db_column_int(&q, 3);
/* Copy content from idm over into idv. Overwrite idv. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag ){
undo_save(zName);
db_multi_exec(
"UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
" mhash=CASE WHEN rid<>%d"
" THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
" WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
);
vfile_to_disk(0, idv, 0, 0);
}
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)"
"VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)",
/* fnp */ db_column_text(&q, 4),
/* ridp */ db_column_int(&q,5),
/* fn */ zName,
/* ridv */ db_column_int(&q,6),
/* fnm */ db_column_text(&q, 7),
/* ridm */ ridm,
/* fnr */ zName
);
}
db_finalize(&q);
/*
** Do a three-way merge on files that have changes on both P->M and P->V.
**
** Proceed even if the file doesn't exist on P, just like the common ancestor
** of M and V is an empty file. In this case, merge conflict marks will be
** added to the file and user will be forced to take a decision.
*/
db_prepare(&q,
/* 0 1 2 3 4 5 6 7 8 */
"SELECT ridm, idv, ridp, ridv, %z, fn, isexe, islinkv, islinkm,"
/* 9 10 11 */
" fnp, fnm, chnged"
" FROM fv"
" WHERE idv>0 AND idm>0"
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
glob_expr("fv.fn", zBinGlob)
);
while( db_step(&q)==SQLITE_ROW ){
int ridm = db_column_int(&q, 0);
int idv = db_column_int(&q, 1);
int ridp = db_column_int(&q, 2);
int ridv = db_column_int(&q, 3);
int isBinary = db_column_int(&q, 4);
const char *zName = db_column_text(&q, 5);
int isExe = db_column_int(&q, 6);
int islinkv = db_column_int(&q, 7);
int islinkm = db_column_int(&q, 8);
int chnged = db_column_int(&q, 11);
int rc;
char *zFullPath;
const char *zType = "MERGE";
Blob m, p, r;
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
if( verboseFlag ){
fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n",
zName, ridp, ridm, ridv);
}else{
fossil_print("MERGE %s\n", zName);
}
if( islinkv || islinkm ){
fossil_print("***** Cannot merge symlink %s\n", zName);
nConflict++;
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)"
"VALUES('ERROR',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')",
/* fnp */ db_column_text(&q, 9),
/* ridp */ ridp,
/* fn */ zName,
/* ridv */ ridv,
/* fnm */ db_column_text(&q, 10),
/* ridm */ ridm,
/* fnr */ zName
);
}else{
i64 sz;
const char *zErrMsg = 0;
int nc = 0;
if( !dryRunFlag ) undo_save(zName);
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
sz = file_size(zFullPath, ExtFILE);
content_get(ridp, &p);
content_get(ridm, &m);
if( isBinary ){
rc = -1;
blob_zero(&r);
}else{
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
rc = merge_3way(&p, zFullPath, &m, &r, mergeFlags);
}
if( rc>=0 ){
if( !dryRunFlag ){
blob_write_to_file(&r, zFullPath);
file_setexe(zFullPath, isExe);
}
db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
if( rc>0 ){
fossil_print("***** %d merge conflict%s in %s\n",
rc, rc>1 ? "s" : "", zName);
nConflict++;
nc = rc;
zErrMsg = "merge conflicts";
zType = "CONFLICT";
}
}else{
fossil_print("***** Cannot merge binary file %s\n", zName);
nConflict++;
nc = 1;
zErrMsg = "cannot merge binary file";
zType = "ERROR";
}
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
"VALUES(%Q,%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%lld,NULL),%Q,%d,"
"%Q,%d,%Q)",
/* op */ zType,
/* fnp */ db_column_text(&q, 9),
/* ridp */ ridp,
/* fn */ zName,
/* ridv */ chnged==0, ridv,
/* sz */ chnged!=0, sz,
/* fnm */ db_column_text(&q, 10),
/* ridm */ ridm,
/* fnr */ zName,
/* nc */ nc,
/* msg */ zErrMsg
);
fossil_free(zFullPath);
blob_reset(&p);
blob_reset(&m);
blob_reset(&r);
}
vmerge_insert(idv, ridm);
}
db_finalize(&q);
/*
** Drop files that are in P and V but not in M
*/
db_prepare(&q,
"SELECT idv, fn, chnged, ridv FROM fv"
" WHERE idp>0 AND idv>0 AND idm=0"
);
while( db_step(&q)==SQLITE_ROW ){
int idv = db_column_int(&q, 0);
const char *zName = db_column_text(&q, 1);
int chnged = db_column_int(&q, 2);
int ridv = db_column_int(&q, 3);
int sz = -1;
const char *zErrMsg = 0;
int nc = 0;
/* Delete the file idv */
fossil_print("DELETE %s\n", zName);
if( chnged ){
char *zFullPath;
fossil_warning("WARNING: local edits lost for %s", zName);
nConflict++;
ridv = 0;
nc = 1;
zErrMsg = "local edits lost";
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
sz = file_size(zFullPath, ExtFILE);
fossil_free(zFullPath);
}
if( !dryRunFlag ) undo_save(zName);
db_multi_exec(
"UPDATE vfile SET deleted=1 WHERE id=%d", idv
);
if( !dryRunFlag ){
char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
file_delete(zFullPath);
free(zFullPath);
}
db_multi_exec(
"INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)"
"VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),"
"NULL,NULL,%d,%Q)",
/* fn */ zName,
/* ridv */ chnged==0, ridv,
/* sz */ chnged!=0, sz,
/* nc */ nc,
/* msg */ zErrMsg
);
}
db_finalize(&q);
/* For certain sets of renames (e.g. A -> B and B -> A), a file that is
** being renamed must first be moved to a temporary location to avoid
** being overwritten by another rename operation. A row is added to the
** TMPRN table for each of these temporary renames.
|
| ︙ | ︙ | |||
951 952 953 954 955 956 957 958 959 960 961 962 963 964 |
int idv = db_column_int(&q, 0);
const char *zOldName = db_column_text(&q, 1);
const char *zNewName = db_column_text(&q, 2);
int isExe = db_column_int(&q, 3);
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
if( !dryRunFlag ) undo_save(zOldName);
if( !dryRunFlag ) undo_save(zNewName);
db_multi_exec(
"UPDATE vfile SET pathname=NULL, origname=pathname"
" WHERE vid=%d AND pathname=%Q;"
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
" WHERE id=%d;",
vid, zNewName, zNewName, idv
);
| > > > > | 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 |
int idv = db_column_int(&q, 0);
const char *zOldName = db_column_text(&q, 1);
const char *zNewName = db_column_text(&q, 2);
int isExe = db_column_int(&q, 3);
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
if( !dryRunFlag ) undo_save(zOldName);
if( !dryRunFlag ) undo_save(zNewName);
db_multi_exec(
"UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
zOldName
);
db_multi_exec(
"UPDATE vfile SET pathname=NULL, origname=pathname"
" WHERE vid=%d AND pathname=%Q;"
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
" WHERE id=%d;",
vid, zNewName, zNewName, idv
);
|
| ︙ | ︙ | |||
1004 1005 1006 1007 1008 1009 1010 |
" WHERE pathname IS NULL"
);
/*
** Insert into V any files that are not in V or P but are in M.
*/
db_prepare(&q,
| | | 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 |
" WHERE pathname IS NULL"
);
/*
** Insert into V any files that are not in V or P but are in M.
*/
db_prepare(&q,
"SELECT idm, fnm, ridm FROM fv"
" WHERE idp=0 AND idv=0 AND idm>0"
);
while( db_step(&q)==SQLITE_ROW ){
int idm = db_column_int(&q, 0);
const char *zName;
char *zFullName;
db_multi_exec(
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 |
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
}else{
fossil_print("ADDED %s\n", zName);
}
fossil_free(zFullName);
if( !dryRunFlag ){
undo_save(zName);
vfile_to_disk(0, idm, 0, 0);
}
}
db_finalize(&q);
| > > > > > > > | 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 |
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
}else{
fossil_print("ADDED %s\n", zName);
}
fossil_free(zFullName);
db_multi_exec(
"INSERT INTO mergestat(op,fnm,ridm,fnr)"
"VALUES('ADDED',%Q,%d,%Q)",
/* fnm */ zName,
/* ridm */ db_column_int(&q,2),
/* fnr */ zName
);
if( !dryRunFlag ){
undo_save(zName);
vfile_to_disk(0, idm, 0, 0);
}
}
db_finalize(&q);
|
| ︙ | ︙ |
Added src/merge.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
# Show details of a 3-way merge operation. The left-most column is the
# common ancestor. The next two columns are edits of that common ancestor.
# The right-most column is the result of the merge.
#
# There is always a "fossilcmd" variable which tells the script how to
# invoke Fossil to get the information it needs. This script will
# automatically append "-c N" to tell Fossil how much context it wants.
#
# If the "filelist" global variable is defined, then it is a list of
# alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and
# filenames. In that case, the initial display shows the changes for
# the first pair on the list and there is a optionmenu that allows the
# user to select other fiels on the list.
#
# There should also be a global variable named "ncontext" which is the
# number of lines of context to display. The value of this variable
# controls the "-c N" argument that is appended to fossilcmd.
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
package require Tk
array set CFG_light {
TITLE {Fossil Merge}
LN_COL_BG #dddddd
LN_COL_FG #444444
TXT_COL_BG #ffffff
TXT_COL_FG #000000
MKR_COL_BG #444444
MKR_COL_FG #dddddd
CHNG_BG #d0d070
ADD_BG #c0ffc0
RM_BG #ffc0c0
HR_FG #444444
HR_PAD_TOP 4
HR_PAD_BTM 8
FN_BG #444444
FN_FG #ffffff
FN_PAD 5
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
array set CFG_dark {
TITLE {Fossil Merge}
LN_COL_BG #dddddd
LN_COL_FG #444444
TXT_COL_BG #3f3f3f
TXT_COL_FG #dcdccc
MKR_COL_BG #444444
MKR_COL_FG #dddddd
CHNG_BG #6a6a00
ADD_BG #57934c
RM_BG #ef6767
HR_FG #444444
HR_PAD_TOP 4
HR_PAD_BTM 8
FN_BG #5e5e5e
FN_FG #ffffff
FN_PAD 5
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
array set CFG_arr {
0 CFG_light
1 CFG_dark
}
array set CFG [array get $CFG_arr($darkmode)]
if {![namespace exists ttk]} {
interp alias {} ::ttk::scrollbar {} ::scrollbar
interp alias {} ::ttk::menubutton {} ::menubutton
}
proc dehtml {x} {
set x [regsub -all {<[^>]*>} $x {}]
return [string map {& & < < > > ' ' " \"} $x]
}
proc cols {} {
return [list .lnA .txtA .lnB .txtB .lnC .txtC .lnD .txtD]
}
proc colType {c} {
regexp {[a-z]+} $c type
return $type
}
proc readMerge {args} {
global fossilcmd ncontext current_file
if {$ncontext=="All"} {
set cmd "$fossilcmd -c -1"
} else {
set cmd "$fossilcmd -c $ncontext"
}
if {[info exists current_file]} {
regsub {^[A-Z]+ } $current_file {} fn
append cmd " -tcl [list $fn]"
}
if {[catch {
set in [open $cmd r]
fconfigure $in -encoding utf-8
set mergetxt [read $in]
close $in
} msg]} {
tk_messageBox -message "Unable to run command: \"$cmd\""
set mergetxt {}
}
foreach c [cols] {
$c config -state normal
$c delete 1.0 end
}
set lnA 1
set lnB 1
set lnC 1
set lnD 1
foreach {A B C D} $mergetxt {
set key1 [string index $A 0]
if {$key1=="S"} {
scan [string range $A 1 end] "%d %d %d %d" nA nB nC nD
foreach x {A B C D} {
set N [set n$x]
incr ln$x $N
if {$N>0} {
.ln$x insert end ...\n hrln
.txt$x insert end [string repeat . 30]\n hrtxt
} else {
.ln$x insert end \n hrln
.txt$x insert end \n hrtxt
}
}
continue
}
set key2 [string index $B 0]
set key3 [string index $C 0]
set key4 [string index $D 0]
if {$key1=="."} {
.lnA insert end \n -
.txtA insert end \n -
} elseif {$key1=="N"} {
.nameA config -text [string range $A 1 end]
} else {
.lnA insert end $lnA\n -
incr lnA
if {$key1=="X"} {
.txtA insert end [string range $A 1 end]\n rm
} else {
.txtA insert end [string range $A 1 end]\n -
}
}
if {$key2=="."} {
.lnB insert end \n -
.txtB insert end \n -
} elseif {$key2=="N"} {
.nameB config -text [string range $B 1 end]
} else {
.lnB insert end $lnB\n -
incr lnB
if {$key4=="2"} {set tag chng} {set tag -}
if {$key2=="1"} {
.txtB insert end [string range $A 1 end]\n $tag
} elseif {$key2=="X"} {
.txtB insert end [string range $B 1 end]\n rm
} else {
.txtB insert end [string range $B 1 end]\n $tag
}
}
if {$key3=="."} {
.lnC insert end \n -
.txtC insert end \n -
} elseif {$key3=="N"} {
.nameC config -text [string range $C 1 end]
} else {
.lnC insert end $lnC\n -
incr lnC
if {$key4=="3"} {set tag add} {set tag -}
if {$key3=="1"} {
.txtC insert end [string range $A 1 end]\n $tag
} elseif {$key3=="2"} {
.txtC insert end [string range $B 1 end]\n chng
} elseif {$key3=="X"} {
.txtC insert end [string range $C 1 end]\n rm
} else {
.txtC insert end [string range $C 1 end]\n $tag
}
}
if {$key4=="."} {
.lnD insert end \n -
.txtD insert end \n -
} elseif {$key4=="N"} {
.nameD config -text [string range $D 1 end]
} else {
.lnD insert end $lnD\n -
incr lnD
if {$key4=="1"} {
.txtD insert end [string range $A 1 end]\n -
} elseif {$key4=="2"} {
.txtD insert end [string range $B 1 end]\n chng
} elseif {$key4=="3"} {
.txtD insert end [string range $C 1 end]\n add
} elseif {$key4=="X"} {
.txtD insert end [string range $D 1 end]\n rm
} else {
.txtD insert end [string range $D 1 end]\n -
}
}
}
foreach c [cols] {
set type [colType $c]
if {$type ne "txt"} {
$c config -width 6; # $widths($type)
}
$c config -state disabled
}
set mx $lnA
if {$lnB>$mx} {set mx $lnB}
if {$lnC>$mx} {set mx $lnC}
if {$lnD>$mx} {set mx $lnD}
global lnWidth
set lnWidth [string length [format +%d $mx]]
.lnA config -width $lnWidth
.lnB config -width $lnWidth
.lnC config -width $lnWidth
.lnD config -width $lnWidth
grid columnconfig . {0 2 4 6} -minsize $lnWidth
}
proc viewDiff {idx} {
.txtA yview $idx
.txtA xview moveto 0
}
proc cycleDiffs {{reverse 0}} {
if {$reverse} {
set range [.txtA tag prevrange fn @0,0 1.0]
if {$range eq ""} {
viewDiff {fn.last -1c}
} else {
viewDiff [lindex $range 0]
}
} else {
set range [.txtA tag nextrange fn {@0,0 +1c} end]
if {$range eq "" || [lindex [.txtA yview] 1] == 1} {
viewDiff fn.first
} else {
viewDiff [lindex $range 0]
}
}
}
proc xvis {col} {
set view [$col xview]
return [expr {[lindex $view 1]-[lindex $view 0]}]
}
proc scroll-x {args} {
set c .txt[expr {[xvis .txtA] < [xvis .txtB] ? "A" : "B"}]
eval $c xview $args
}
interp alias {} scroll-y {} .txtA yview
proc noop {args} {}
proc enableSync {axis} {
update idletasks
interp alias {} sync-$axis {}
rename _sync-$axis sync-$axis
}
proc disableSync {axis} {
rename sync-$axis _sync-$axis
interp alias {} sync-$axis {} noop
}
proc sync-y {first last} {
disableSync y
foreach c [cols] {
$c yview moveto $first
}
if {$first > 0 || $last < 1} {
grid .sby
.sby set $first $last
} else {
grid remove .sby
}
enableSync y
}
wm withdraw .
wm title . $CFG(TITLE)
wm iconname . $CFG(TITLE)
# Keystroke bindings for on the top-level window for navigation and
# control also fire when those same keystrokes are pressed in the
# Search entry box. Disable them, to prevent the diff screen from
# disappearing abruptly and unexpectedly when searching for "q".
#
bind . <Control-q> exit
bind . <Control-p> {catch searchPrev; break}
bind . <Control-n> {catch searchNext; break}
bind . <Escape><Escape> exit
bind . <Destroy> {after 0 exit}
bind . <Tab> {cycleDiffs; break}
bind . <<PrevWindow>> {cycleDiffs 1; break}
bind . <Control-f> {searchOnOff; break}
bind . <Control-g> {catch searchNext; break}
bind . <Return> {
event generate .bb.files <1>
event generate .bb.files <ButtonRelease-1>
break
}
foreach {key axis args} {
Up y {scroll -5 units}
k y {scroll -5 units}
Down y {scroll 5 units}
j y {scroll 5 units}
Left x {scroll -5 units}
h x {scroll -5 units}
Right x {scroll 5 units}
l x {scroll 5 units}
Prior y {scroll -1 page}
b y {scroll -1 page}
Next y {scroll 1 page}
space y {scroll 1 page}
Home y {moveto 0}
g y {moveto 0}
End y {moveto 1}
} {
bind . <$key> "scroll-$axis $args; break"
bind . <Shift-$key> continue
}
frame .bb
set useOptionMenu 1
if {[info exists filelist]} {
set current_file "[lindex $filelist 0] [lindex $filelist 1]"
if {[llength $filelist]>2} {
trace add variable current_file write readMerge
if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} {
set fnlist {}
foreach {op fn} $filelist {lappend fnlist "$op $fn"}
tk_optionMenu .bb.files current_file {*}$fnlist
} else {
set useOptionMenu 0
::ttk::menubutton .bb.files -text $current_file
if {[tk windowingsystem] eq "win32"} {
::ttk::style theme use winnative
.bb.files configure -padding {20 1 10 2}
}
toplevel .wfiles
wm withdraw .wfiles
update idletasks
wm transient .wfiles .
wm overrideredirect .wfiles 1
set ht [expr {[llength $filelist]/2}]
if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)}
listbox .wfiles.lb -width 0 -height $ht -activestyle none \
-yscroll {.wfiles.sb set}
set mx 1
foreach {op fn} $filelist {
set n [string length $fn]
if {$n>$mx} {set mx $n}
.wfiles.lb insert end "$op $fn"
}
.bb.files config -width $mx
::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview}
grid .wfiles.lb .wfiles.sb -sticky ns
bind .bb.files <1> {
set x [winfo rootx %W]
set y [expr {[winfo rooty %W]+[winfo height %W]}]
wm geometry .wfiles +$x+$y
wm deiconify .wfiles
focus .wfiles.lb
}
bind .wfiles <FocusOut> {wm withdraw .wfiles}
bind .wfiles <Escape> {focus .}
foreach evt {1 Return} {
bind .wfiles.lb <$evt> {
set ii [%W curselection]
set ::current_file [%W get $ii]
.bb.files config -text $::current_file
focus .
break
}
}
bind .wfiles.lb <Motion> {
%W selection clear 0 end
%W selection set @%x,%y
}
}
}
}
label .bb.ctxtag -text "Context:"
set context_choices {3 6 12 25 50 100 All}
if {$ncontext<0} {set ncontext All}
trace add variable ncontext write readMerge
if {$tcl_platform(os)=="Darwin" || $useOptionMenu} {
tk_optionMenu .bb.ctx ncontext {*}$context_choices
} else {
::ttk::menubutton .bb.ctx -text $ncontext
if {[tk windowingsystem] eq "win32"} {
::ttk::style theme use winnative
.bb.ctx configure -padding {20 1 10 2}
}
toplevel .wctx
wm withdraw .wctx
update idletasks
wm transient .wctx .
wm overrideredirect .wctx 1
listbox .wctx.lb -width 0 -height 7 -activestyle none
.wctx.lb insert end {*}$context_choices
pack .wctx.lb
bind .bb.ctx <1> {
set x [winfo rootx %W]
set y [expr {[winfo rooty %W]+[winfo height %W]}]
wm geometry .wctx +$x+$y
wm deiconify .wctx
focus .wctx.lb
}
bind .wctx <FocusOut> {wm withdraw .wctx}
bind .wctx <Escape> {focus .}
foreach evt {1 Return} {
bind .wctx.lb <$evt> {
set ::ncontext [lindex $::context_choices [%W curselection]]
.bb.ctx config -text $::ncontext
focus .
break
}
}
bind .wctx.lb <Motion> {
%W selection clear 0 end
%W selection set @%x,%y
}
}
foreach {side syncCol} {A .txtA B .txtB C .txtC D .txtD} {
set ln .ln$side
text $ln -width 6
$ln tag config - -justify right
set txt .txt$side
text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
-xscroll ".sbx$side set"
catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5
foreach tag {add rm chng} {
$txt tag config $tag -background $CFG([string toupper $tag]_BG)
$txt tag lower $tag
}
$txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
-justify center
$txt tag config err -foreground $CFG(ERR_FG)
}
text .mkr
set mxwidth [lindex [wm maxsize .] 0]
while {$CFG(WIDTH)>=40} {
set wanted [expr {([winfo reqwidth .lnA]+[winfo reqwidth .txtA])*4+30}]
if {$wanted<=$mxwidth} break
incr CFG(WIDTH) -10
.txtA config -width $CFG(WIDTH)
.txtB config -width $CFG(WIDTH)
.txtC config -width $CFG(WIDTH)
.txtD config -width $CFG(WIDTH)
}
foreach c [cols] {
set keyPrefix [string toupper [colType $c]]_COL_
if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
$c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
-padx $CFG(PADX) -yscroll sync-y
$c tag config hrln -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
-foreground $CFG(HR_FG) -justify right
$c tag config hrtxt -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
-foreground $CFG(HR_FG) -justify center
$c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD)
bindtags $c ". $c Text all"
bind $c <1> {focus %W}
}
label .nameA
label .nameB
label .nameC
label .nameD -text {Merge Result}
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
::ttk::scrollbar .sbxC -command {.txtC xview} -orient horizontal
::ttk::scrollbar .sbxD -command {.txtD xview} -orient horizontal
frame .spacer
update idletasks
proc searchOnOff {} {
if {[info exists ::search]} {
unset ::search
.txtA tag remove search 1.0 end
.txtB tag remove search 1.0 end
.txtC tag remove search 1.0 end
.txtD tag remove search 1.0 end
pack forget .bb.sframe
focus .
} else {
set ::search .txtA
if {![winfo exists .bb.sframe]} {
frame .bb.sframe
::ttk::entry .bb.sframe.e -width 10
pack .bb.sframe.e -side left -fill y -expand 1
bind .bb.sframe.e <Return> {searchNext; break}
::ttk::button .bb.sframe.nx -text \u2193 -width 1 -command searchNext
::ttk::button .bb.sframe.pv -text \u2191 -width 1 -command searchPrev
tk_optionMenu .bb.sframe.typ ::search_type \
Exact {No Case} {RegExp} {Whole Word}
.bb.sframe.typ config -width 10
set ::search_type Exact
pack .bb.sframe.nx .bb.sframe.pv .bb.sframe.typ -side left
}
pack .bb.sframe -side left
after idle {focus .bb.sframe.e}
}
}
proc searchNext {} {searchStep -forwards +1 1.0 end}
proc searchPrev {} {searchStep -backwards -1 end 1.0}
proc searchStep {direction incr start stop} {
set pattern [.bb.sframe.e get]
if {$pattern==""} return
set count 0
set w $::search
switch $w {
.txtA {set other .txtB}
.txtB {set other .txtC}
.txtC {set other .txtD}
default {set other .txtA}
}
if {[lsearch [$w mark names] search]<0} {
$w mark set search $start
}
switch $::search_type {
Exact {set st -exact}
{No Case} {set st -nocase}
{RegExp} {set st -regexp}
{Whole Word} {set st -regexp; set pattern \\y$pattern\\y}
}
set idx [$w search -count count $direction $st -- \
$pattern "search $incr chars" $stop]
if {"$idx"==""} {
set idx [$other search -count count $direction $st -- $pattern $start $stop]
if {"$idx"!=""} {
set this $w
set w $other
set other $this
} else {
set idx [$w search -count count $direction $st -- $pattern $start $stop]
}
}
$w tag remove search 1.0 end
$w mark unset search
$other tag remove search 1.0 end
$other mark unset search
if {"$idx"!=""} {
$w mark set search $idx
$w yview -pickplace $idx
$w tag add search search "$idx +$count chars"
$w tag config search -background {#fcc000}
}
set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.search -text {Search} -command searchOnOff
pack .bb.quit -side left
if {[winfo exists .bb.files]} {
pack .bb.files -side left
}
pack .bb.ctxtag .bb.ctx -side left
pack .bb.search -side left
grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
grid rowconfigure . 2 -weight 100
readMerge
grid .bb -row 0 -columnspan 8
grid .nameA -row 1 -column 1 -sticky ew
grid .nameB -row 1 -column 3 -sticky ew
grid .nameC -row 1 -column 5 -sticky ew
grid .nameD -row 1 -column 7 -sticky ew
eval grid [cols] -row 2 -sticky nsew
grid .sby -row 2 -column 8 -sticky ns
grid .sbxA -row 3 -column 1 -sticky ew
grid .sbxB -row 3 -column 3 -sticky ew
grid .sbxC -row 3 -column 5 -sticky ew
grid .sbxD -row 3 -column 7 -sticky ew
grid columnconfigure . {0 2 4 6} \
-weight 1 -uniform a -minsize [winfo reqwidth .lnA]
grid columnconfigure . {1 3 5 7} -weight 100 -uniform b
.spacer config -height [winfo height .sbxA]
wm deiconify .
|
Changes to src/merge3.c.
| ︙ | ︙ | |||
73 74 75 76 77 78 79 80 | if( aC1[0]!=aC2[0] ) return 0; if( aC1[1]!=aC2[1] ) return 0; if( aC1[2]!=aC2[2] ) return 0; if( sameLines(pV1, pV2, aC1[2]) ) return 1; return 0; } /* | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
if( aC1[0]!=aC2[0] ) return 0;
if( aC1[1]!=aC2[1] ) return 0;
if( aC1[2]!=aC2[2] ) return 0;
if( sameLines(pV1, pV2, aC1[2]) ) return 1;
return 0;
}
/*
** Text of boundary markers for merge conflicts.
*/
static const char *const mergeMarker[] = {
/*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
"<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<",
"####### SUGGESTED CONFLICT RESOLUTION follows ###################",
"||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||",
"======= MERGED IN content follows ===============================",
">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
};
/*
** Return true if the input blob contains any CR/LF pairs on the first
|
| ︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
*/
void append_merge_mark(Blob *pOut, int iMark, int ln, int useCrLf){
ensure_line_end(pOut, useCrLf);
blob_append(pOut, mergeMarker[iMark], -1);
if( ln>0 ) blob_appendf(pOut, " (line %d)", ln);
ensure_line_end(pOut, useCrLf);
}
/*
** Do a three-way merge. Initialize pOut to contain the result.
**
** The merge is an edit against pV2. Both pV1 and pV2 have a
** common origin at pPivot. Apply the changes of pPivot ==> pV1
** to pV2.
**
** The return is 0 upon complete success. If any input file is binary,
** -1 is returned and pOut is unmodified. If there are merge
** conflicts, the merge proceeds as best as it can and the number
** of conflicts is returns
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < > | | | | | > > | > > > > | < | < < < | | < | > | < < < < < | < < < | < < < | < < < | | < < < | > | < | < < | < < | < < | < | < < < < < | < | > > > > | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
*/
void append_merge_mark(Blob *pOut, int iMark, int ln, int useCrLf){
ensure_line_end(pOut, useCrLf);
blob_append(pOut, mergeMarker[iMark], -1);
if( ln>0 ) blob_appendf(pOut, " (line %d)", ln);
ensure_line_end(pOut, useCrLf);
}
#if INTERFACE
/*
** This is an abstract class for constructing a merge.
** Subclasses of this object format the merge output in different ways.
**
** To subclass, create an instance of the MergeBuilder object and fill
** in appropriate method implementations.
*/
struct MergeBuilder {
void (*xStart)(MergeBuilder*);
void (*xSame)(MergeBuilder*, unsigned int);
void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int);
void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int);
void (*xChngBoth)(MergeBuilder*, unsigned int, unsigned int);
void (*xConflict)(MergeBuilder*, unsigned int, unsigned int, unsigned int);
void (*xEnd)(MergeBuilder*);
void (*xDestroy)(MergeBuilder*);
const char *zPivot; /* Label or name for the pivot */
const char *zV1; /* Label or name for the V1 file */
const char *zV2; /* Label or name for the V2 file */
const char *zOut; /* Label or name for the output */
Blob *pPivot; /* The common ancestor */
Blob *pV1; /* First variant (local copy) */
Blob *pV2; /* Second variant (merged in) */
Blob *pOut; /* Write merge results here */
int useCrLf; /* Use CRLF line endings */
int nContext; /* Size of unchanged line boundaries */
unsigned int mxPivot; /* Number of lines in the pivot */
unsigned int mxV1; /* Number of lines in V1 */
unsigned int mxV2; /* Number of lines in V2 */
unsigned int lnPivot; /* Lines read from pivot */
unsigned int lnV1; /* Lines read from v1 */
unsigned int lnV2; /* Lines read from v2 */
unsigned int lnOut; /* Lines written to out */
unsigned int nConflict; /* Number of conflicts seen */
u64 diffFlags; /* Flags for difference engine */
};
#endif /* INTERFACE */
/************************* Generic MergeBuilder ******************************/
/* These are generic methods for MergeBuilder. They just output debugging
** information. But some of them are useful as base methods for other useful
** implementations of MergeBuilder.
*/
/* xStart() and xEnd() are called to generate header and fotter information
** in the output. This is a no-op in the generic implementation.
*/
static void dbgStartEnd(MergeBuilder *p){ (void)p; }
/* The next N lines of PIVOT are unchanged in both V1 and V2
*/
static void dbgSame(MergeBuilder *p, unsigned int N){
blob_appendf(p->pOut,
"COPY %u from BASELINE(%u..%u) or V1(%u..%u) or V2(%u..%u)\n",
N, p->lnPivot+1, p->lnPivot+N, p->lnV1+1, p->lnV1+N,
p->lnV2+1, p->lnV2+N);
p->lnPivot += N;
p->lnV1 += N;
p->lnV2 += N;
}
/* The next nPivot lines of the PIVOT are changed into nV1 lines by V1
*/
static void dbgChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
blob_appendf(p->pOut, "COPY %u from V1(%u..%u)\n",
nV1, p->lnV1+1, p->lnV1+nV1);
p->lnPivot += nPivot;
p->lnV2 += nPivot;
p->lnV1 += nV1;
}
/* The next nPivot lines of the PIVOT are changed into nV2 lines by V2
*/
static void dbgChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
blob_appendf(p->pOut, "COPY %u lines FROM V2(%u..%u)\n",
nV2, p->lnV2+1, p->lnV2+nV2);
p->lnPivot += nPivot;
p->lnV1 += nPivot;
p->lnV2 += nV2;
}
/* The next nPivot lines of the PIVOT are changed into nV lines from V1 and
** V2, which should be the same. In other words, the same change is found
** in both V1 and V2.
*/
static void dbgChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
blob_appendf(p->pOut, "COPY %u lines from V1(%u..%u) or V2(%u..%u)\n",
nV, p->lnV1+1, p->lnV1+nV, p->lnV2+1, p->lnV2+nV);
p->lnPivot += nPivot;
p->lnV1 += nV;
p->lnV2 += nV;
}
/* V1 and V2 have different and overlapping changes. The next nPivot lines
** of the PIVOT are converted into nV1 lines of V1 and nV2 lines of V2.
*/
static void dbgConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
blob_appendf(p->pOut,
"CONFLICT %u,%u,%u BASELINE(%u..%u) versus V1(%u..%u) versus V2(%u..%u)\n",
nPivot, nV1, nV2,
p->lnPivot+1, p->lnPivot+nPivot,
p->lnV1+1, p->lnV1+nV1,
p->lnV2+1, p->lnV2+nV2);
p->lnV1 += nV1;
p->lnPivot += nPivot;
p->lnV2 += nV2;
}
/* Generic destructor for the MergeBuilder object
*/
static void dbgDestroy(MergeBuilder *p){
memset(p, 0, sizeof(*p));
}
/* Generic initializer for a MergeBuilder object
*/
static void mergebuilder_init(MergeBuilder *p){
memset(p, 0, sizeof(*p));
p->xStart = dbgStartEnd;
p->xSame = dbgSame;
p->xChngV1 = dbgChngV1;
p->xChngV2 = dbgChngV2;
p->xChngBoth = dbgChngBoth;
p->xConflict = dbgConflict;
p->xEnd = dbgStartEnd;
p->xDestroy = dbgDestroy;
}
/************************* MergeBuilderToken ********************************/
/* This version of MergeBuilder actually performs a merge on file that
** are broken up into tokens instead of lines, and puts the result in pOut.
*/
static void tokenSame(MergeBuilder *p, unsigned int N){
blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N);
p->pPivot->iCursor += N;
p->pV1->iCursor += N;
p->pV2->iCursor += N;
}
static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){
blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nV1;
p->pV2->iCursor += nPivot;
}
static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){
blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nPivot;
p->pV2->iCursor += nV2;
}
static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){
blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nV;
p->pV2->iCursor += nV;
}
static void tokenConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
/* For a token-merge conflict, use the text from the merge-in */
blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nV1;
p->pV2->iCursor += nV2;
}
static void mergebuilder_init_token(MergeBuilder *p){
mergebuilder_init(p);
p->xSame = tokenSame;
p->xChngV1 = tokenChngV1;
p->xChngV2 = tokenChngV2;
p->xChngBoth = tokenChngBoth;
p->xConflict = tokenConflict;
p->diffFlags = DIFF_BY_TOKEN;
}
/*
** Attempt to do a low-level merge on a conflict. The conflict is
** described by the first four parameters, which are the same as the
** arguments to the xConflict method of the MergeBuilder object.
** This routine attempts to resolve the conflict by looking at
** elements of the conflict region that are finer grain than complete
** lines of text.
**
** The result is written into Blob pOut. pOut is initialized by this
** routine.
*/
int merge_try_to_resolve_conflict(
MergeBuilder *pMB, /* MergeBuilder that encounter conflict */
unsigned int nPivot, /* Lines of conflict in the pivot */
unsigned int nV1, /* Lines of conflict in V1 */
unsigned int nV2, /* Lines of conflict in V2 */
Blob *pOut /* Write resolution text here */
){
int nConflict;
MergeBuilder mb;
Blob pv, v1, v2;
mergebuilder_init_token(&mb);
blob_extract_lines(pMB->pPivot, nPivot, &pv);
blob_extract_lines(pMB->pV1, nV1, &v1);
blob_extract_lines(pMB->pV2, nV2, &v2);
blob_zero(pOut);
blob_materialize(&pv);
blob_materialize(&v1);
blob_materialize(&v2);
mb.pPivot = &pv;
mb.pV1 = &v1;
mb.pV2 = &v2;
mb.pOut = pOut;
nConflict = merge_three_blobs(&mb);
blob_reset(&pv);
blob_reset(&v1);
blob_reset(&v2);
/* mb has not allocated any resources, so we do not need to invoke
** the xDestroy method. */
blob_add_final_newline(pOut);
return nConflict;
}
/************************* MergeBuilderText **********************************/
/* This version of MergeBuilder actually performs a merge on file and puts
** the result in pOut
*/
static void txtStart(MergeBuilder *p){
/* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
** keep it in the output. This should be secure enough not to cause
** unintended changes to the merged file and consistent with what
** users are using in their source files.
*/
if( starts_with_utf8_bom(p->pV1, 0) && starts_with_utf8_bom(p->pV2, 0) ){
blob_append(p->pOut, (char*)get_utf8_bom(0), -1);
}
if( contains_crlf(p->pV1) && contains_crlf(p->pV2) ){
p->useCrLf = 1;
}
}
static void txtSame(MergeBuilder *p, unsigned int N){
blob_copy_lines(p->pOut, p->pPivot, N); p->lnPivot += N;
blob_copy_lines(0, p->pV1, N); p->lnV1 += N;
blob_copy_lines(0, p->pV2, N); p->lnV2 += N;
}
static void txtChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
blob_copy_lines(0, p->pV2, nPivot); p->lnV2 += nPivot;
blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
}
static void txtChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot;
blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
}
static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV;
blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV;
}
static void txtConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
int nRes; /* Lines in the computed conflict resolution */
Blob res; /* Text of the conflict resolution */
merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
nRes = blob_linecount(&res);
append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf);
blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
if( nRes>0 ){
append_merge_mark(p->pOut, 1, 0, p->useCrLf);
blob_copy_lines(p->pOut, &res, nRes);
}
blob_reset(&res);
append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf);
blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot;
append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf);
blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
append_merge_mark(p->pOut, 4, -1, p->useCrLf);
}
static void mergebuilder_init_text(MergeBuilder *p){
mergebuilder_init(p);
p->xStart = txtStart;
p->xSame = txtSame;
p->xChngV1 = txtChngV1;
p->xChngV2 = txtChngV2;
p->xChngBoth = txtChngBoth;
p->xConflict = txtConflict;
}
/************************* MergeBuilderTcl **********************************/
/* Generate merge output formatted for reading by a TCL script.
**
** The output consists of lines of text, each with 4 tokens. The tokens
** represent the content for one line from baseline, v1, v2, and output
** respectively. The first character of each token provides auxiliary
** information:
**
** . This line is omitted.
** N Name of the file.
** T Literal text follows that should have a \n terminator.
** R Literal text follows that needs a \r\n terminator.
** X Merge conflict.
** Z Literal text without a line terminator.
** S Skipped lines. Followed by number of lines to skip.
** 1 Text is a copy of token 1
** 2 Use data from data-token 2
** 3 Use data from data-token 3
*/
/* Write text that goes into the interior of a double-quoted string in TCL */
static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){
int j;
for(j=0; j<nIn; j++){
char c = zIn[j];
if( c=='\\' ){
blob_append(pOut, "\\\\", 2);
}else if( c=='"' ){
blob_append(pOut, "\\\"", 2);
}else if( c<' ' || c>0x7e ){
char z[5];
z[0] = '\\';
z[1] = "01234567"[(c>>6)&0x3];
z[2] = "01234567"[(c>>3)&0x7];
z[3] = "01234567"[c&0x7];
z[4] = 0;
blob_append(pOut, z, 4);
}else{
blob_append_char(pOut, c);
}
}
}
/* Copy one line of text from pIn and append to pOut, encoded as TCL */
static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){
int i, k;
for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
if( i==pIn->nUsed ){
k = i;
}else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
k = i-1;
i++;
}else{
k = i;
i++;
}
blob_append_char(pOut, '"');
blob_append_char(pOut, cType);
tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
pIn->iCursor = i;
blob_append_char(pOut, '"');
}
static void tclStart(MergeBuilder *p){
Blob *pOut = p->pOut;
blob_append(pOut, "\"N", 2);
tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot));
blob_append(pOut, "\" \"N", 4);
tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1));
blob_append(pOut, "\" \"N", 4);
tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2));
blob_append(pOut, "\" \"N", 4);
if( p->zOut ){
tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut));
}else{
blob_append(pOut, "(Merge Result)", -1);
}
blob_append(pOut, "\"\n", 2);
}
static void tclSame(MergeBuilder *p, unsigned int N){
int i = 0;
int nSkip;
if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){
while( i<N && i<p->nContext ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 1 1\n", 7);
i++;
}
nSkip = N - p->nContext*2;
}else{
nSkip = N - p->nContext;
}
if( nSkip>0 ){
blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n",
nSkip, nSkip, nSkip, nSkip);
blob_copy_lines(0, p->pPivot, nSkip);
i += nSkip;
}
p->lnPivot += N;
p->lnV1 += N;
p->lnV2 += N;
if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){
while( i<N ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 1 1\n", 7);
i++;
}
}
blob_copy_lines(0, p->pV1, N);
blob_copy_lines(0, p->pV2, N);
}
static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
int i;
for(i=0; i<nPivot && i<nV1; i++){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append_char(p->pOut, ' ');
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " 1 2\n", 5);
}
while( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " . 1 .\n", 7);
i++;
}
while( i<nV1 ){
blob_append(p->pOut, ". ", 2);
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " . 2\n", 5);
i++;
}
p->lnPivot += nPivot;
p->lnV1 += nV1;
p->lnV2 += nPivot;
blob_copy_lines(0, p->pV2, nPivot);
}
static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
int i;
for(i=0; i<nPivot && i<nV2; i++){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 ", 3);
tclLineOfText(p->pOut, p->pV2, 'T');
blob_append(p->pOut, " 3\n", 3);
}
while( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 . .\n", 7);
i++;
}
while( i<nV2 ){
blob_append(p->pOut, ". . ", 4);
tclLineOfText(p->pOut, p->pV2, 'T');
blob_append(p->pOut, " 3\n", 3);
i++;
}
p->lnPivot += nPivot;
p->lnV1 += nPivot;
p->lnV2 += nV2;
blob_copy_lines(0, p->pV1, nPivot);
}
static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
int i;
for(i=0; i<nPivot && i<nV; i++){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append_char(p->pOut, ' ');
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " 2 2\n", 5);
}
while( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " . . .\n", 7);
i++;
}
while( i<nV ){
blob_append(p->pOut, ". ", 2);
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " 2 2\n", 5);
i++;
}
p->lnPivot += nPivot;
p->lnV1 += nV;
p->lnV2 += nV;
blob_copy_lines(0, p->pV2, nV);
}
static void tclConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
int mx = nPivot;
int i;
int nRes;
Blob res;
merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
nRes = blob_linecount(&res);
if( nV1>mx ) mx = nV1;
if( nV2>mx ) mx = nV2;
if( nRes>mx ) mx = nRes;
if( nRes>0 ){
blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2);
}
for(i=0; i<mx; i++){
if( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'X');
}else{
blob_append_char(p->pOut, '.');
}
blob_append_char(p->pOut, ' ');
if( i<nV1 ){
tclLineOfText(p->pOut, p->pV1, 'X');
}else{
blob_append_char(p->pOut, '.');
}
blob_append_char(p->pOut, ' ');
if( i<nV2 ){
tclLineOfText(p->pOut, p->pV2, 'X');
}else{
blob_append_char(p->pOut, '.');
}
if( i<nRes ){
blob_append_char(p->pOut, ' ');
tclLineOfText(p->pOut, &res, 'X');
blob_append_char(p->pOut, '\n');
}else{
blob_append(p->pOut, " .\n", 3);
}
if( i==mx-1 ){
blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3);
}
}
blob_reset(&res);
p->lnPivot += nPivot;
p->lnV1 += nV1;
p->lnV2 += nV2;
}
void mergebuilder_init_tcl(MergeBuilder *p){
mergebuilder_init(p);
p->xStart = tclStart;
p->xSame = tclSame;
p->xChngV1 = tclChngV1;
p->xChngV2 = tclChngV2;
p->xChngBoth = tclChngBoth;
p->xConflict = tclConflict;
}
/*****************************************************************************/
/*
** The aC[] array contains triples of integers. Within each triple, the
** elements are:
**
** (0) The number of lines to copy
** (1) The number of lines to delete
** (2) The number of liens to insert
**
** Suppose we want to advance over sz lines of the original file. This routine
** returns true if that advance would land us on a copy operation. It
** returns false if the advance would end on a delete.
*/
static int ends_with_copy(int *aC, int sz){
while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
if( aC[0]>=sz ) return 1;
sz -= aC[0];
if( aC[1]>sz ) return 0;
sz -= aC[1];
aC += 3;
}
return 1;
}
/*
** aC[] is an "edit triple" for changes from A to B. Advance through
** this triple to determine the number of lines to bypass on B in order
** to match an advance of sz lines on A.
*/
static int skip_conflict(
int *aC, /* Array of integer triples describing the edit */
int i, /* Index in aC[] of current location */
int sz, /* Lines of A that have been skipped */
unsigned int *pLn /* OUT: Lines of B to skip to keep aligment with A */
){
*pLn = 0;
while( sz>0 ){
if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break;
if( aC[i]>=sz ){
aC[i] -= sz;
*pLn += sz;
break;
}
*pLn += aC[i];
*pLn += aC[i+2];
sz -= aC[i] + aC[i+1];
i += 3;
}
return i;
}
/*
** Do a three-way merge. Initialize pOut to contain the result.
**
** The merge is an edit against pV2. Both pV1 and pV2 have a
** common origin at pPivot. Apply the changes of pPivot ==> pV1
** to pV2.
**
** The return is 0 upon complete success. If any input file is binary,
** -1 is returned and pOut is unmodified. If there are merge
** conflicts, the merge proceeds as best as it can and the number
** of conflicts is returns
*/
int merge_three_blobs(MergeBuilder *p){
int *aC1; /* Changes from pPivot to pV1 */
int *aC2; /* Changes from pPivot to pV2 */
int i1, i2; /* Index into aC1[] and aC2[] */
int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
int limit1, limit2; /* Sizes of aC1[] and aC2[] */
int nConflict = 0; /* Number of merge conflicts seen so far */
DiffConfig DCfg;
/* Compute the edits that occur from pPivot => pV1 (into aC1)
** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
** an array of integer triples. Within each triple, the first integer
** is the number of lines of text to copy directly from the pivot,
** the second integer is the number of lines of text to omit from the
** pivot, and the third integer is the number of lines of text that are
** inserted. The edit array ends with a triple of 0,0,0.
*/
diff_config_init(&DCfg, 0);
DCfg.diffFlags = p->diffFlags;
aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg);
aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg);
if( aC1==0 || aC2==0 ){
free(aC1);
free(aC2);
return -1;
}
blob_rewind(p->pV1); /* Rewind inputs: Needed to reconstruct output */
blob_rewind(p->pV2);
blob_rewind(p->pPivot);
/* Determine the length of the aC1[] and aC2[] change vectors */
p->mxPivot = 0;
p->mxV1 = 0;
for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){
p->mxPivot += aC1[i1] + aC1[i1+1];
p->mxV1 += aC1[i1] + aC1[i1+2];
}
limit1 = i1;
p->mxV2 = 0;
for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){
p->mxV2 += aC2[i2] + aC2[i2+2];
}
limit2 = i2;
/* Output header text and do any other required initialization */
p->xStart(p);
/* Loop over the two edit vectors and use them to compute merged text
** which is written into pOut. i1 and i2 are multiples of 3 which are
** indices into aC1[] and aC2[] to the edit triple currently being
** processed
*/
i1 = i2 = 0;
while( i1<limit1 && i2<limit2 ){
if( aC1[i1]>0 && aC2[i2]>0 ){
/* Output text that is unchanged in both V1 and V2 */
nCpy = min(aC1[i1], aC2[i2]);
p->xSame(p, nCpy);
aC1[i1] -= nCpy;
aC2[i2] -= nCpy;
}else
if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){
/* Output edits to V2 that occurs within unchanged regions of V1 */
nDel = aC2[i2+1];
nIns = aC2[i2+2];
p->xChngV2(p, nDel, nIns);
aC1[i1] -= nDel;
i2 += 3;
}else
if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){
/* Output edits to V1 that occur within unchanged regions of V2 */
nDel = aC1[i1+1];
nIns = aC1[i1+2];
p->xChngV1(p, nDel, nIns);
aC2[i2] -= nDel;
i1 += 3;
}else
if( sameEdit(&aC1[i1], &aC2[i2], p->pV1, p->pV2) ){
/* Output edits that are identical in both V1 and V2. */
assert( aC1[i1]==0 );
nDel = aC1[i1+1];
nIns = aC1[i1+2];
p->xChngBoth(p, nDel, nIns);
i1 += 3;
i2 += 3;
}else
{
/* We have found a region where different edits to V1 and V2 overlap.
** This is a merge conflict. Find the size of the conflict, then
** output both possible edits separated by distinctive marks.
*/
unsigned int sz = 1; /* Size of the conflict in the pivot, in lines */
unsigned int nV1, nV2; /* Size of conflict in V1 and V2, in lines */
nConflict++;
while( !ends_with_copy(&aC1[i1], sz) || !ends_with_copy(&aC2[i2], sz) ){
sz++;
}
i1 = skip_conflict(aC1, i1, sz, &nV1);
i2 = skip_conflict(aC2, i2, sz, &nV2);
p->xConflict(p, sz, nV1, nV2);
}
/* If we are finished with an edit triple, advance to the next
** triple.
*/
if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
}
/* When one of the two edit vectors reaches its end, there might still
** be an insert in the other edit vector. Output this remaining
** insert.
*/
if( i1<limit1 && aC1[i1+2]>0 ){
p->xChngV1(p, 0, aC1[i1+2]);
}else if( i2<limit2 && aC2[i2+2]>0 ){
p->xChngV2(p, 0, aC2[i2+2]);
}
/* Output footer text */
p->xEnd(p);
free(aC1);
free(aC2);
return nConflict;
}
/*
** Return true if the input string contains a merge marker on a line by
** itself.
*/
int contains_merge_marker(Blob *p){
int i, j;
int len = (int)strlen(mergeMarker[0]);
const char *z = blob_buffer(p);
int n = blob_size(p) - len + 1;
assert( len==(int)strlen(mergeMarker[1]) );
assert( len==(int)strlen(mergeMarker[2]) );
assert( len==(int)strlen(mergeMarker[3]) );
assert( len==(int)strlen(mergeMarker[4]) );
assert( count(mergeMarker)==5 );
for(i=0; i<n; ){
for(j=0; j<4; j++){
if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
return 1;
}
}
while( i<n && z[i]!='\n' ){ i++; }
|
| ︙ | ︙ | |||
406 407 408 409 410 411 412 413 414 415 416 | Blob file; int rc; blob_read_from_file(&file, zFullpath, ExtFILE); rc = contains_merge_marker(&file); blob_reset(&file); return rc; } /* ** COMMAND: 3-way-merge* ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | | | > > | > > | > | > | > | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
Blob file;
int rc;
blob_read_from_file(&file, zFullpath, ExtFILE);
rc = contains_merge_marker(&file);
blob_reset(&file);
return rc;
}
/*
** Show merge output in a Tcl/Tk window, in response to the --tk option
** to the "merge" or "3-way-merge" command.
**
** If fossil has direct access to a Tcl interpreter (either loaded
** dynamically through stubs or linked in statically), we can use it
** directly. Otherwise:
** (1) Write the Tcl/Tk script used for rendering into a temp file.
** (2) Invoke "tclsh" on the temp file using fossil_system().
** (3) Delete the temp file.
*/
void merge_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
const char *zCnt;
int bDarkMode = find_option("dark",0,0)!=0;
int nContext;
zCnt = find_option("context", "c", 1);
if( zCnt==0 ){
nContext = 6;
}else{
nContext = atoi(zCnt);
if( nContext<0 ) nContext = 0xfffffff;
}
blob_zero(&script);
blob_appendf(&script, "set ncontext %d\n", nContext);
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl",
g.nameOfExe, zSubCmd);
find_option("tcl",0,0);
find_option("debug",0,0);
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
}
/* The undocumented --script FILENAME option causes the Tk script to
** be written into the FILENAME instead of being run. This is used
** for testing and debugging. */
zTempFile = find_option("script",0,1);
verify_all_options();
if( (g.argc - firstArg)!=3 ){
fossil_fatal("Requires 3 filename arguments");
}
for(i=firstArg; i<g.argc; i++){
const char *z = g.argv[i];
if( sqlite3_strglob("*}*",z) ){
blob_appendf(&script, " {%/}", z);
}else{
int j;
blob_append(&script, " ", 1);
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
}
}
blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
blob_size(&script), 1, 1, 0)==TCL_OK ){
blob_reset(&script);
return;
}
/*
* If evaluation of the Tcl script fails, the reason may be that Tk
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** COMMAND: 3-way-merge*
**
** Usage: %fossil 3-way-merge BASELINE V1 V2 [MERGED]
**
** Inputs are files BASELINE, V1, and V2. The file MERGED is generated
** as output. If no MERGED file is specified, output is sent to
** stdout.
**
** BASELINE is a common ancestor of two files V1 and V2 that have diverging
** edits. The generated output file MERGED is the combination of all
** changes in both V1 and V2.
**
** This command has no effect on the Fossil repository. It is a utility
** command made available for the convenience of users. This command can
** be used, for example, to help import changes from an upstream project.
**
** Suppose an upstream project has a file named "Xup.c" which is imported
** with modifications to the local project as "Xlocal.c". Suppose further
** that the "Xbase.c" is an exact copy of the last imported "Xup.c".
** Then to import the latest "Xup.c" while preserving all the local changes:
**
** fossil 3-way-merge Xbase.c Xlocal.c Xup.c Xlocal.c
** cp Xup.c Xbase.c
** # Verify that everything still works
** fossil commit
**
*/
void merge_3way_cmd(void){
MergeBuilder s;
int nConflict;
Blob pivot, v1, v2, out;
int noWarn = 0;
const char *zCnt;
if( find_option("tk", 0, 0)!=0 ){
merge_tk("3-way-merge", 2);
return;
}
mergebuilder_init_text(&s);
if( find_option("debug", 0, 0) ){
mergebuilder_init(&s);
}
if( find_option("tcl", 0, 0) ){
mergebuilder_init_tcl(&s);
noWarn = 1;
}
zCnt = find_option("context", "c", 1);
if( zCnt ){
s.nContext = atoi(zCnt);
if( s.nContext<0 ) s.nContext = 0xfffffff;
}else{
s.nContext = 6;
}
blob_zero(&pivot); s.pPivot = &pivot;
blob_zero(&v1); s.pV1 = &v1;
blob_zero(&v2); s.pV2 = &v2;
blob_zero(&out); s.pOut = &out;
/* We should be done with options.. */
verify_all_options();
if( g.argc!=6 && g.argc!=5 ){
usage("[OPTIONS] PIVOT V1 V2 [MERGED]");
}
s.zPivot = file_tail(g.argv[2]);
s.zV1 = file_tail(g.argv[3]);
s.zV2 = file_tail(g.argv[4]);
if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[2]);
}
if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[4]);
}
nConflict = merge_three_blobs(&s);
if( g.argc==6 ){
s.zOut = file_tail(g.argv[5]);
blob_write_to_file(s.pOut, g.argv[5]);
}else{
s.zOut = "(Merge Result)";
blob_write_to_file(s.pOut, "-");
}
s.xDestroy(&s);
blob_reset(&pivot);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&out);
if( nConflict>0 && !noWarn ){
fossil_warning("WARNING: %d merge conflicts", nConflict);
}
}
/*
** aSubst is an array of string pairs. The first element of each pair is
** a string that begins with %. The second element is a replacement for that
** string.
**
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 | return blob_str(&x); } #if INTERFACE /* ** Flags to the 3-way merger */ | | | | | | | | > > > > > > > | | | | | | 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 |
return blob_str(&x);
}
#if INTERFACE
/*
** Flags to the 3-way merger
*/
#define MERGE_DRYRUN 0x0001
/*
** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain
** its temporary files on error. By default they are removed after the
** merge, regardless of success or failure.
*/
#define MERGE_KEEP_FILES 0x0002
#endif
/*
** This routine is a wrapper around merge_three_blobs() with the following
** enhancements:
**
** (1) If the merge-command is defined, then use the external merging
** program specified instead of the built-in blob-merge to do the
** merging. Panic if the external merger fails.
** ** Not currently implemented **
**
** (2) If gmerge-command is defined and there are merge conflicts in
** merge_three_blobs() then invoke the external graphical merger
** to resolve the conflicts.
**
** (3) If a merge conflict occurs and gmerge-command is not defined,
** then write the pivot, original, and merge-in files to the
** filesystem.
*/
int merge_3way(
Blob *pPivot, /* Common ancestor (older) */
const char *zV1, /* Name of file for version merging into (mine) */
Blob *pV2, /* Version merging from (yours) */
Blob *pOut, /* Output written here */
unsigned mergeFlags /* Flags that control operation */
){
Blob v1; /* Content of zV1 */
int rc; /* Return code of subroutines and this routine */
const char *zGMerge; /* Name of the gmerge command */
MergeBuilder s; /* The merge state */
mergebuilder_init_text(&s);
s.pPivot = pPivot;
s.pV1 = &v1;
s.pV2 = pV2;
blob_zero(pOut);
s.pOut = pOut;
blob_read_from_file(s.pV1, zV1, ExtFILE);
rc = merge_three_blobs(&s);
zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0);
if( (mergeFlags & MERGE_DRYRUN)==0
&& ((zGMerge!=0 && zGMerge[0]!=0)
|| (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){
char *zPivot; /* Name of the pivot file */
char *zOrig; /* Name of the original content file */
char *zOther; /* Name of the merge file */
zPivot = file_newname(zV1, "baseline", 1);
blob_write_to_file(s.pPivot, zPivot);
zOrig = file_newname(zV1, "original", 1);
blob_write_to_file(s.pV1, zOrig);
zOther = file_newname(zV1, "merge", 1);
blob_write_to_file(s.pV2, zOther);
if( rc>0 ){
if( zGMerge && zGMerge[0] ){
char *zOut; /* Temporary output file */
char *zCmd; /* Command to invoke */
const char *azSubst[8]; /* Strings to be substituted */
zOut = file_newname(zV1, "output", 1);
azSubst[0] = "%baseline"; azSubst[1] = zPivot;
|
| ︙ | ︙ | |||
588 589 590 591 592 593 594 595 596 |
file_delete(zOther);
}
fossil_free(zPivot);
fossil_free(zOrig);
fossil_free(zOther);
}
blob_reset(&v1);
return rc;
}
| > | 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 |
file_delete(zOther);
}
fossil_free(zPivot);
fossil_free(zOrig);
fossil_free(zOther);
}
blob_reset(&v1);
s.xDestroy(&s);
return rc;
}
|
Changes to src/patch.c.
| ︙ | ︙ | |||
462 463 464 465 466 467 468 |
blob_reset(&cmd);
}
/* Deletions */
db_prepare(&q, "SELECT pathname FROM patch.chng"
" WHERE origname IS NULL AND delta IS NULL");
while( db_step(&q)==SQLITE_ROW ){
| > | | > > > | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
blob_reset(&cmd);
}
/* Deletions */
db_prepare(&q, "SELECT pathname FROM patch.chng"
" WHERE origname IS NULL AND delta IS NULL");
while( db_step(&q)==SQLITE_ROW ){
if( blob_size(&cmd)==0 ){
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " rm --hard");
}
blob_appendf(&cmd, " %$", db_column_text(&q,0));
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0));
}
}
db_finalize(&q);
if( blob_size(&cmd)>0 ){
blob_appendf(&cmd, "\n");
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
int rc = fossil_unsafe_system(blob_str(&cmd));
if( rc ){
fossil_fatal("unable to do merges:\n%s",
blob_str(&cmd));
|
| ︙ | ︙ |
Changes to src/path.c.
| ︙ | ︙ | |||
540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
*pnChng = i/2;
while( pAll ){
pChng = pAll;
pAll = pAll->pNext;
fossil_free(pChng);
}
}
}
/*
** COMMAND: test-name-changes
**
** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2
**
| > | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
*pnChng = i/2;
while( pAll ){
pChng = pAll;
pAll = pAll->pNext;
fossil_free(pChng);
}
}
path_reset();
}
/*
** COMMAND: test-name-changes
**
** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2
**
|
| ︙ | ︙ |
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
205 206 207 208 209 210 211 |
zClassSource/*safe-for-%s*/, w);
}
blob_append(pOut, zOut, -1);
if(PIKCHR_PROCESS_DIV & pikFlags){
blob_append(pOut, "</div>\n", 7);
}
if(PIKCHR_PROCESS_SRC & pikFlags){
| > > | > > > > > > > > | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
zClassSource/*safe-for-%s*/, w);
}
blob_append(pOut, zOut, -1);
if(PIKCHR_PROCESS_DIV & pikFlags){
blob_append(pOut, "</div>\n", 7);
}
if(PIKCHR_PROCESS_SRC & pikFlags){
static int counter = 0;
++counter;
blob_appendf(pOut, "<div class='pikchr-src'>"
"<pre id='pikchr-src-%d'>%h</pre>"
"<span class='hidden'>"
"<a href='%R/pikchrshow?fromSession' "
"class='pikchr-src-pikchrshow' target='_new-%d' "
"data-pikchrid='pikchr-src-%d' "
"title='Open this pikchr in /pikchrshow'"
">→ /pikchrshow</a></span>"
"</div>\n",
counter, blob_str(&bIn), counter, counter);
}
if(PIKCHR_PROCESS_DIV & pikFlags){
blob_append(pOut, "</div>\n", 7);
}
}else{
isErr = 2;
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
388 389 390 391 392 393 394 395 |
if (ttyOutput && !g.fQuiet) {
percent_complete(0);
}
manifest_disable_event_triggers();
rebuild_update_schema();
blob_init(&sql, 0, 0);
db_unprotect(PROTECT_ALL);
db_prepare(&q,
| > > > | | | > > > | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
if (ttyOutput && !g.fQuiet) {
percent_complete(0);
}
manifest_disable_event_triggers();
rebuild_update_schema();
blob_init(&sql, 0, 0);
db_unprotect(PROTECT_ALL);
#ifndef SQLITE_PREPARE_DONT_LOG
g.dbIgnoreErrors++;
#endif
db_prepare(&q,
"SELECT name FROM pragma_table_list /*scan*/"
" WHERE schema='repository' AND type IN ('table','virtual')"
" AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
"'config','shun','private','reportfmt',"
"'concealed','accesslog','modreq',"
"'purgeevent','purgeitem','unversioned',"
"'subscriber','pending_alert','chat')"
" AND name NOT GLOB 'sqlite_*'"
" AND name NOT GLOB 'fx_*';"
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
}
db_finalize(&q);
#ifndef SQLITE_PREPARE_DONT_LOG
g.dbIgnoreErrors--;
#endif
db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
blob_reset(&sql);
db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/);
ticket_create_table(0);
shun_artifacts();
db_multi_exec(
|
| ︙ | ︙ |
Changes to src/rss.c.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | ** tkt=HASH filters for only those events for the specified ticket. tag=TAG ** filters for a tag, and wiki=NAME for a wiki page. Only one may be used. ** ** In addition, name=FILENAME filters for a specific file. This may be ** combined with one of the other filters (useful for looking at a specific ** branch). */ | < | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
** tkt=HASH filters for only those events for the specified ticket. tag=TAG
** filters for a tag, and wiki=NAME for a wiki page. Only one may be used.
**
** In addition, name=FILENAME filters for a specific file. This may be
** combined with one of the other filters (useful for looking at a specific
** branch).
*/
void page_timeline_rss(void){
Stmt q;
int nLine=0;
char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
Blob bSQL;
const char *zType = PD("y","all"); /* Type of events. All if NULL */
const char *zTicketUuid = PD("tkt",NULL);
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
}
blob_zero(&bSQL);
blob_append_sql( &bSQL, "%s", zSQL1/*safe-for-%s*/ );
if( zType[0]!='a' ){
if( zType[0]=='c' && !g.perm.Read ) zType = "x";
| | | | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
}
blob_zero(&bSQL);
blob_append_sql( &bSQL, "%s", zSQL1/*safe-for-%s*/ );
if( zType[0]!='a' ){
if( zType[0]=='c' && !g.perm.Read ) zType = "x";
else if( (zType[0]=='w' || zType[0]=='e') && !g.perm.RdWiki ) zType = "x";
else if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x";
else if( zType[0]=='f' && !g.perm.RdForum ) zType = "x";
blob_append_sql(&bSQL, " AND event.type=%Q", zType);
}else{
blob_append_sql(&bSQL, " AND event.type in (");
if( g.perm.Read ){
blob_append_sql(&bSQL, "'ci',");
}
if( g.perm.RdTkt ){
blob_append_sql(&bSQL, "'t',");
}
if( g.perm.RdWiki ){
blob_append_sql(&bSQL, "'w','e',");
}
if( g.perm.RdForum ){
blob_append_sql(&bSQL, "'f',");
}
blob_append_sql(&bSQL, "'x')");
}
|
| ︙ | ︙ |
Changes to src/schema.c.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | const char zConfigSchema[] = @ -- This file contains the schema for the database that is kept in the @ -- ~/.fossil file and that stores information about the users setup. @ -- @ CREATE TABLE global_config( @ name TEXT PRIMARY KEY, @ value TEXT | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | const char zConfigSchema[] = @ -- This file contains the schema for the database that is kept in the @ -- ~/.fossil file and that stores information about the users setup. @ -- @ CREATE TABLE global_config( @ name TEXT PRIMARY KEY, @ value TEXT @ ) WITHOUT ROWID; @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLG'. @ PRAGMA application_id=252006675; ; #if INTERFACE |
| ︙ | ︙ | |||
136 137 138 139 140 141 142 | @ -- in the form of name-value pairs. @ -- @ CREATE TABLE config( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ mtime DATE, -- last modified. seconds since 1970 @ CHECK( typeof(name)='text' AND length(name)>=1 ) | | | | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | @ -- in the form of name-value pairs. @ -- @ CREATE TABLE config( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ mtime DATE, -- last modified. seconds since 1970 @ CHECK( typeof(name)='text' AND length(name)>=1 ) @ ) WITHOUT ROWID; @ @ -- Artifacts that should not be processed are identified in the @ -- "shun" table. Artifacts that are control-file forgeries or @ -- spam or artifacts whose contents violate administrative policy @ -- can be shunned in order to prevent them from contaminating @ -- the repository. @ -- @ -- Shunned artifacts do not exist in the blob table. Hence they @ -- have not artifact ID (rid) and we thus must store their full @ -- UUID. @ -- @ CREATE TABLE shun( @ uuid TEXT PRIMARY KEY,-- UUID of artifact to be shunned. Canonical form @ mtime DATE, -- When added. seconds since 1970 @ scom TEXT -- Optional text explaining why the shun occurred @ ) WITHOUT ROWID; @ @ -- Artifacts that should not be pushed are stored in the "private" @ -- table. Private artifacts are omitted from the "unclustered" and @ -- "unsent" tables. @ -- @ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact @ -- for which we do not know the content) might also be marked as private. |
| ︙ | ︙ | |||
191 192 193 194 195 196 197 | @ -- This table contains sensitive information and should not be shared @ -- with unauthorized users. @ -- @ CREATE TABLE concealed( @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content @ mtime DATE, -- Time created. Seconds since 1970 @ content TEXT -- Content intended to be concealed | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | @ -- This table contains sensitive information and should not be shared @ -- with unauthorized users. @ -- @ CREATE TABLE concealed( @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content @ mtime DATE, -- Time created. Seconds since 1970 @ content TEXT -- Content intended to be concealed @ ) WITHOUT ROWID; @ @ -- The application ID helps the unix "file" command to identify the @ -- database as a fossil repository. @ PRAGMA application_id=252006673; ; /* |
| ︙ | ︙ | |||
526 527 528 529 530 531 532 | /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of ** the check-out. See also the addendum in zLocalSchemaVmerge[]. */ const char zLocalSchema[] = | | | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of ** the check-out. See also the addendum in zLocalSchemaVmerge[]. */ const char zLocalSchema[] = @ -- The VVAR table holds miscellanous information about the local checkout @ -- in the form of name-value pairs. This is similar to the VAR table @ -- table in the repository except that this table holds information that @ -- is specific to the local check-out. @ -- @ -- Important Variables: @ -- @ -- repository Full pathname of the repository database @ -- user-id Userid to use @ -- @ CREATE TABLE vvar( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ CHECK( typeof(name)='text' AND length(name)>=1 ) @ ) WITHOUT ROWID; @ @ -- Each entry in the vfile table represents a single file in the @ -- current check-out. @ -- @ -- The file.rid field is 0 for files or folders that have been @ -- added but not yet committed. @ -- |
| ︙ | ︙ |
Changes to src/search.c.
| ︙ | ︙ | |||
792 793 794 795 796 797 798 799 800 801 |
){
search_init(zPattern, "<mark>", "</mark>", " ... ",
SRCHFLG_STATIC|SRCHFLG_HTML);
if( (srchFlags & SRCH_DOC)!=0 ){
char *zDocGlob = db_get("doc-glob","");
char *zDocBr = db_get("doc-branch","trunk");
if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
db_multi_exec(
"CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
);
| > > > > > > | | | | | | | | | | | | | | | | | | > > > > | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 |
){
search_init(zPattern, "<mark>", "</mark>", " ... ",
SRCHFLG_STATIC|SRCHFLG_HTML);
if( (srchFlags & SRCH_DOC)!=0 ){
char *zDocGlob = db_get("doc-glob","");
char *zDocBr = db_get("doc-branch","trunk");
if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
Glob * pGlob = glob_create(zDocBr)
/* We're misusing a Glob as a list of comma-/space-delimited
** tokens. We're not actually doing glob matches here. */;
int i;
db_multi_exec(
"CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
);
for( i = 0; i < pGlob->nPattern; ++i ){
const char * zBranch = pGlob->azPattern[i];
db_multi_exec(
"INSERT INTO x(label,url,score,id,date,snip)"
" SELECT printf('Document: %%s',title('d',blob.rid,foci.filename)),"
" printf('/doc/%T/%%s',foci.filename),"
" search_score(),"
" 'd'||blob.rid,"
" (SELECT datetime(event.mtime) FROM event"
" WHERE objid=symbolic_name_to_rid(%Q)),"
" search_snippet()"
" FROM foci CROSS JOIN blob"
" WHERE checkinID=symbolic_name_to_rid(%Q)"
" AND blob.uuid=foci.uuid"
" AND search_match(title('d',blob.rid,foci.filename),"
" body('d',blob.rid,foci.filename))"
" AND %z",
zBranch, zBranch, zBranch, glob_expr("foci.filename", zDocGlob)
);
}
glob_free(pGlob);
}
fossil_free(zDocGlob);
fossil_free(zDocBr);
}
if( (srchFlags & SRCH_WIKI)!=0 ){
db_multi_exec(
"WITH wiki(name,rid,mtime) AS ("
" SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
" FROM tag, tagxref"
" WHERE tag.tagname GLOB 'wiki-*'"
|
| ︙ | ︙ | |||
1876 1877 1878 1879 1880 1881 1882 |
/*
** If the doc-glob and doc-br settings are valid for document search
** and if the latest check-in on doc-br is in the unindexed set of
** check-ins, then update all 'd' entries in FTSDOCS that have
** changed.
*/
static void search_update_doc_index(void){
| | | | | < < | < | < > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 |
/*
** If the doc-glob and doc-br settings are valid for document search
** and if the latest check-in on doc-br is in the unindexed set of
** check-ins, then update all 'd' entries in FTSDOCS that have
** changed.
*/
static void search_update_doc_index(void){
const char *zDocBranches = db_get("doc-branch","trunk");
int i;
Glob * pGlob = glob_create(zDocBranches)
/* We're misusing a Glob as a list of comma-/space-delimited
** tokens. We're not actually doing glob matches here. */;
if( !pGlob ) return;
db_multi_exec(
"CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);"
"CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
);
for( i = 0; i < pGlob->nPattern; ++i ){
const char *zDocBr = pGlob->azPattern[i];
int ckid = symbolic_name_to_rid(zDocBr,"ci");
double rTime;
if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d"
" AND NOT idxed", ckid) ) continue;
/* If we get this far, it means that changes to 'd' entries are
** required. */
rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid);
db_multi_exec(
"INSERT OR IGNORE INTO current_docs(rid, name)"
" SELECT blob.rid, foci.filename FROM foci, blob"
" WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
" AND %z",
ckid, glob_expr("foci.filename", db_get("doc-glob",""))
);
db_multi_exec(
"DELETE FROM ftsidx WHERE rowid IN"
" (SELECT rowid FROM ftsdocs WHERE type='d'"
" AND rid NOT IN (SELECT rid FROM current_docs))"
);
db_multi_exec(
"DELETE FROM ftsdocs WHERE type='d'"
" AND rid NOT IN (SELECT rid FROM current_docs)"
);
db_multi_exec(
"INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)"
" SELECT 'd', rid, name, 0,"
" title('d',rid,name),"
" body('d',rid,name),"
" printf('/doc/%T/%%s',urlencode(name)),"
" %.17g"
" FROM current_docs",
zDocBr, rTime
);
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed"
);
db_multi_exec(
"UPDATE ftsdocs SET"
" idxed=1,"
" bx=NULL,"
" label='Document: '||label"
" WHERE type='d' AND NOT idxed"
);
}
glob_free(pGlob);
}
/*
** Deal with all of the unindexed 'c' terms in FTSDOCS
*/
static void search_update_checkin_index(void){
db_multi_exec(
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
499 500 501 502 503 504 505 | @ parameters. Some robots will spend hours juggling around query parameters @ or even forging fake query parameters in an effort to discover new @ behavior or to find an SQL injection opportunity or similar. This can @ waste hours of CPU time and gigabytes of bandwidth on the server. A @ suggested value for this setting is: @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>". @ (Property: robot-restrict) | | > > > > > > > > > > > | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
@ parameters. Some robots will spend hours juggling around query parameters
@ or even forging fake query parameters in an effort to discover new
@ behavior or to find an SQL injection opportunity or similar. This can
@ waste hours of CPU time and gigabytes of bandwidth on the server. A
@ suggested value for this setting is:
@ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
@ (Property: robot-restrict)
@ <br>
textarea_attribute("", 2, 80,
"robot-restrict", "rbrestrict", "", 0);
@ <br> The following comma-separated GLOB pattern allows for exceptions
@ in the maximum number of query parameters before a request is considered
@ complex. If this GLOB pattern exists and is non-empty and if it
@ matches against the pagename followed by "/" and the number of query
@ parameters, then the request is allowed through. For example, the
@ suggested pattern of "timeline/[012]" allows the /timeline page to
@ pass with up to 2 query parameters besides "name".
@ (Property: robot-restrict-qp)
@ <br>
textarea_attribute("", 2, 80,
"robot-restrict-qp", "rbrestrictqp", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
2185 2186 2187 2188 2189 2190 2191 | @ <td>Search all Markdown files in the doc/ subfolder and all README.txt @ files.</tr> @ <tr><td>*<td><td>Search all checked-in files</tr> @ <tr><td><i>(blank)</i><td> @ <td>Search nothing. (Disables document search).</tr> @ </table> @ <hr> | | | | > | 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 |
@ <td>Search all Markdown files in the doc/ subfolder and all README.txt
@ files.</tr>
@ <tr><td>*<td><td>Search all checked-in files</tr>
@ <tr><td><i>(blank)</i><td>
@ <td>Search nothing. (Disables document search).</tr>
@ </table>
@ <hr>
entry_attribute("Document Branches", 20, "doc-branch", "db", "trunk", 0);
@ <p>When searching documents, use the versions of the files found at the
@ type of the "Document Branches" branch. Recommended value: "trunk".
@ Document search is disabled if blank. It may be a list of branch names
@ separated by spaces and/or commas.
@ <hr>
onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
@ <br>
onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
@ <br>
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
@ <br>
|
| ︙ | ︙ |
Changes to src/setupuser.c.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
}
}
}
if( !bUnusedOnly ){
style_submenu_element("Unused", "setup_ulist?unused");
}
@ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \
| | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
}
}
}
if( !bUnusedOnly ){
style_submenu_element("Unused", "setup_ulist?unused");
}
@ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \
@ data-column-types='ktxKTKt' data-init-sort='4'>
@ <thead><tr>
@ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\
@ <th>Alerts</tr></thead>
@ <tbody>
db_multi_exec(
"CREATE TEMP TABLE lastAccess(uname TEXT PRIMARY KEY, atime REAL)"
"WITHOUT ROWID;"
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 |
db_prepare(&s,
"SELECT uid, login, cap, info, date(user.mtime,'unixepoch'),"
" lower(login) AS sortkey, "
" CASE WHEN info LIKE '%%expires 20%%'"
" THEN substr(info,instr(lower(info),'expires')+8,10)"
" END AS exp,"
"atime,"
| | > | > | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
db_prepare(&s,
"SELECT uid, login, cap, info, date(user.mtime,'unixepoch'),"
" lower(login) AS sortkey, "
" CASE WHEN info LIKE '%%expires 20%%'"
" THEN substr(info,instr(lower(info),'expires')+8,10)"
" END AS exp,"
"atime,"
" subscriber.ssub, subscriber.subscriberId,"
" user.mtime AS sorttime"
" FROM user LEFT JOIN lastAccess ON login=uname"
" LEFT JOIN subscriber ON login=suname"
" WHERE login NOT IN ('anonymous','nobody','developer','reader') %s"
" ORDER BY sorttime DESC", zWith/*safe-for-%s*/
);
rNow = db_double(0.0, "SELECT julianday('now');");
while( db_step(&s)==SQLITE_ROW ){
int uid = db_column_int(&s, 0);
const char *zLogin = db_column_text(&s, 1);
const char *zCap = db_column_text(&s, 2);
const char *zInfo = db_column_text(&s, 3);
const char *zDate = db_column_text(&s, 4);
const char *zSortKey = db_column_text(&s,5);
const char *zExp = db_column_text(&s,6);
double rATime = db_column_double(&s,7);
char *zAge = 0;
const char *zSub;
int sid = db_column_int(&s,9);
sqlite3_int64 sorttime = db_column_int64(&s, 10);
if( rATime>0.0 ){
zAge = human_readable_age(rNow - rATime);
}
if( bUbg ){
@ <tr style='background-color: %h(user_color(zLogin));'>
}else{
@ <tr>
}
@ <td data-sortkey='%h(zSortKey)'>\
@ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
@ <td>%h(zCap)
@ <td>%h(zInfo)
@ <td data-sortkey='%09llx(sorttime)'>%h(zDate?zDate:"")
@ <td>%h(zExp?zExp:"")
@ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"")
if( db_column_type(&s,8)==SQLITE_NULL ){
@ <td>
}else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){
@ <td><a href="%R/alerts?sid=%d(sid)"><i>off</i></a>
}else{
|
| ︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 |
return;
}
style_header("User %h", db_column_text(&q,1));
@ <table class="label-value">
@ <tr><th>uid:</th><td>%d(db_column_int(&q,0))
@ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr>
@ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr>
| | | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 |
return;
}
style_header("User %h", db_column_text(&q,1));
@ <table class="label-value">
@ <tr><th>uid:</th><td>%d(db_column_int(&q,0))
@ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr>
@ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr>
@ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</td></tr>
@ <tr><th valign="top">info:</th>
@ <td valign="top"><span style='white-space:pre-line;'>\
@ %h(db_column_text(&q,5))</span></td></tr>
@ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr>
if( db_column_type(&q,7)!=SQLITE_NULL ){
@ <tr><th>subscriberId:</th><td>%d(db_column_int(&q,7))
@ (<a href="%R/alerts?sid=%d(db_column_int(&q,7))">edit</a>)</td></tr>
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
int srchFlags;
int inSublist = 0;
int i;
int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
int e = atoi(PD("e","0"));
const char *zExtra;
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
int srchFlags;
int inSublist = 0;
int i;
int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
int e = atoi(PD("e","0"));
const char *zExtra;
login_check_credentials();
if( P("popup")!=0 ){
/* The "popup" query parameter
** then disable anti-robot defenses */
isPopup = 1;
g.perm.Hyperlink = 1;
g.jsHref = 0;
}
srchFlags = search_restrict(SRCH_ALL);
if( !isPopup ){
style_header("Site Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
@ <ul id="sitemap" class="columns" style="column-width:20em">
if( (e&1)==0 ){
@ <li>%z(href("%R/home"))Home Page</a>
}
zExtra = db_get("sitemap-extra",0);
if( zExtra && (e&2)==0 ){
int rc;
char **azExtra = 0;
int *anExtra;
int nExtra = 0;
if( isPopup ) Th_FossilInit(0);
|
| ︙ | ︙ | |||
137 138 139 140 141 142 143 |
}
}
}
Th_Free(g.interp, azExtra);
}
if( (e&1)!=0 ) goto end_of_sitemap;
| < < < < < < < < < < < > > > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
}
}
}
Th_Free(g.interp, azExtra);
}
if( (e&1)!=0 ) goto end_of_sitemap;
if( inSublist ){
@ </ul>
inSublist = 0;
}
@ </li>
if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){
@ <li>%z(href("%R/ckout"))Checkout Status</a></li>
}
if( g.perm.Read ){
const char *zEditGlob = db_get("fileedit-glob","");
@ <li>%z(href("%R/tree"))File Browser</a>
@ <ul>
@ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
@ Trunk Check-in</a></li>
@ <li>%z(href("%R/tree?type=flat"))Flat-view</a></li>
|
| ︙ | ︙ | |||
190 191 192 193 194 195 196 |
if( srchFlags ){
@ <li>%z(href("%R/search"))Search</a></li>
}
if( g.perm.Chat ){
@ <li>%z(href("%R/chat"))Chat</a></li>
}
if( g.perm.RdForum ){
| > | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
if( srchFlags ){
@ <li>%z(href("%R/search"))Search</a></li>
}
if( g.perm.Chat ){
@ <li>%z(href("%R/chat"))Chat</a></li>
}
if( g.perm.RdForum ){
const char *zTitle = db_get("forum-title","Forum");
@ <li>%z(href("%R/forum"))%h(zTitle)</a>
@ <ul>
@ <li>%z(href("%R/timeline?y=f"))Recent activity</a></li>
@ </ul>
@ </li>
}
if( g.perm.RdTkt ){
@ <li>%z(href("%R/reportlist"))Tickets/Bug Reports</a>
|
| ︙ | ︙ |
Changes to src/th_tcl.c.
| ︙ | ︙ | |||
729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
*/
registerChans = 0;
Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN));
Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT));
Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR));
}
Tcl_Preserve((ClientData)tclInterp);
if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){
int nResult;
const char *zResult = getTclResult(tclInterp, &nResult);
Th_ErrorMessage(interp,
"could not make Tcl interpreter 'safe':", zResult, nResult);
rc = TH_ERROR;
}else{
| > > > > > > > > > | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 |
*/
registerChans = 0;
Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN));
Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT));
Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR));
}
Tcl_Preserve((ClientData)tclInterp);
#if ((TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>6) \
|| (TCL_MAJOR_VERSION>8))
/* TCL 8.7+ removes Tcl_MakeSafe():
** https://core.tcl-lang.org/tcl/tktview?name=655300
** https://core.tcl-lang.org/tips/doc/trunk/tip/624.md
** 8.7 has it in the headers but not in the libs.
*/
# define Tcl_MakeSafe(X) TCL_OK
#endif
if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){
int nResult;
const char *zResult = getTclResult(tclInterp, &nResult);
Th_ErrorMessage(interp,
"could not make Tcl interpreter 'safe':", zResult, nResult);
rc = TH_ERROR;
}else{
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 |
int disableY = 0; /* Disable type selector on submenu */
int advancedMenu = 0; /* Use the advanced menu design */
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
(void)P_NoBot("ss")
/* "ss" is processed via the udc but at least one spider likes to
** try to SQL inject via this argument, so let's catch that. */;
| > | 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 |
int disableY = 0; /* Disable type selector on submenu */
int advancedMenu = 0; /* Use the advanced menu design */
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
login_check_credentials();
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
(void)P_NoBot("ss")
/* "ss" is processed via the udc but at least one spider likes to
** try to SQL inject via this argument, so let's catch that. */;
|
| ︙ | ︙ | |||
1963 1964 1965 1966 1967 1968 1969 |
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_choice("dp","dp2",&zDPName);
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
| < | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 |
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_choice("dp","dp2",&zDPName);
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
if( !bisectLocal ){
|
| ︙ | ︙ | |||
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 |
const char *z;
if( (z = P("tl"))!=0 ){
zTagName = z;
zMatchStyle = "brlist";
}
if( (z = P("rl"))!=0 ){
zBrName = z;
zMatchStyle = "brlist";
}
}
/* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
if( zBrName && !related ){
cgi_delete_query_parameter("r");
| > | 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 |
const char *z;
if( (z = P("tl"))!=0 ){
zTagName = z;
zMatchStyle = "brlist";
}
if( (z = P("rl"))!=0 ){
zBrName = z;
related = 1;
zMatchStyle = "brlist";
}
}
/* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
if( zBrName && !related ){
cgi_delete_query_parameter("r");
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
779 780 781 782 783 784 785 |
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( zFullName ){
| | | 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 |
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( zFullName ){
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
}
style_finish_page();
}
/*
** TH1 command: append_field FIELD STRING
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
int nConflict = 0; /* Number of merge conflicts */
int nOverwrite = 0; /* Number of unmanaged files overwritten */
int nUpdate = 0; /* Number of changes of any kind */
int bNosync = 0; /* --nosync. Omit the auto-sync */
int width; /* Width of printed comment lines */
Stmt mtimeXfer; /* Statement to transfer mtimes */
const char *zWidth; /* Width option string value */
if( !internalUpdate ){
undo_capture_command_line();
url_proxy_options();
}
zWidth = find_option("width","W",1);
if( zWidth ){
| > > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
int nConflict = 0; /* Number of merge conflicts */
int nOverwrite = 0; /* Number of unmanaged files overwritten */
int nUpdate = 0; /* Number of changes of any kind */
int bNosync = 0; /* --nosync. Omit the auto-sync */
int width; /* Width of printed comment lines */
Stmt mtimeXfer; /* Statement to transfer mtimes */
const char *zWidth; /* Width option string value */
const char *zCurBrName; /* Current branch name */
const char *zNewBrName; /* New branch name */
const char *zBrChgMsg = ""; /* Message to display if branch changes */
if( !internalUpdate ){
undo_capture_command_line();
url_proxy_options();
}
zWidth = find_option("width","W",1);
if( zWidth ){
|
| ︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
bNosync = find_option("nosync",0,0)!=0;
/* We should be done with options.. */
verify_all_options();
db_must_be_within_tree();
vid = db_lget_int("checkout", 0);
user_select();
if( !dryRunFlag && !internalUpdate && !bNosync ){
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){
fossil_fatal("update abandoned due to sync failure");
}
}
| > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
bNosync = find_option("nosync",0,0)!=0;
/* We should be done with options.. */
verify_all_options();
db_must_be_within_tree();
vid = db_lget_int("checkout", 0);
zCurBrName = branch_of_rid(vid);
user_select();
if( !dryRunFlag && !internalUpdate && !bNosync ){
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){
fossil_fatal("update abandoned due to sync failure");
}
}
|
| ︙ | ︙ | |||
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
db_prepare(&mtimeXfer,
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
" WHERE id=:idt"
);
assert( g.zLocalRoot!=0 );
assert( strlen(g.zLocalRoot)>0 );
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0); /* The filename from root */
int idv = db_column_int(&q, 1); /* VFILE entry for current */
int ridv = db_column_int(&q, 2); /* RecordID for current */
int idt = db_column_int(&q, 3); /* VFILE entry for target */
int ridt = db_column_int(&q, 4); /* RecordID for target */
int chnged = db_column_int(&q, 5); /* Current is edited */
const char *zNewName = db_column_text(&q,6);/* New filename */
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
int islinkv = db_column_int(&q, 8); /* Is current file is a link */
int islinkt = db_column_int(&q, 9); /* Is target file is a link */
int deleted = db_column_int(&q, 10); /* Marked for deletion */
char *zFullPath; /* Full pathname of the file */
char *zFullNewPath; /* Full pathname of dest */
char nameChng; /* True if the name changed */
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
nameChng = fossil_strcmp(zName, zNewName);
nUpdate++;
if( deleted ){
db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
}
if( idv>0 && ridv==0 && idt>0 && ridt>0 ){
/* Conflict. This file has been added to the current check-out
** but also exists in the target check-out. Use the current version.
*/
fossil_print("CONFLICT %s\n", zName);
nConflict++;
}else if( idt>0 && idv==0 ){
/* File added in the target. */
if( file_isfile_or_link(zFullPath) ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup previously unanaged file before to be overwritten */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
fossil_print("ADD %s - overwrites an unmanaged file", zName);
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
}else{
fossil_print("ADD %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){
/* The file is unedited. Change it to the target version */
if( deleted ){
fossil_print("UPDATE %s - change to unmanaged file\n", zName);
}else{
fossil_print("UPDATE %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
/* The file missing from the local check-out. Restore it to the
** version that appears in the target. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt==0 && idv>0 ){
if( ridv==0 ){
/* Added in current check-out. Continue to hold the file as
** as an addition */
db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
}else if( chnged ){
/* Edited locally but deleted from the target. Do not track the
** file but keep the edited version around. */
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
zName);
nConflict++;
}else{
fossil_print("REMOVE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ){
char *zDir;
file_delete(zFullPath);
| > > > > > > > > > > > > > > > > > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
db_prepare(&mtimeXfer,
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
" WHERE id=:idt"
);
assert( g.zLocalRoot!=0 );
assert( strlen(g.zLocalRoot)>0 );
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
merge_info_init();
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0); /* The filename from root */
int idv = db_column_int(&q, 1); /* VFILE entry for current */
int ridv = db_column_int(&q, 2); /* RecordID for current */
int idt = db_column_int(&q, 3); /* VFILE entry for target */
int ridt = db_column_int(&q, 4); /* RecordID for target */
int chnged = db_column_int(&q, 5); /* Current is edited */
const char *zNewName = db_column_text(&q,6);/* New filename */
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
int islinkv = db_column_int(&q, 8); /* Is current file is a link */
int islinkt = db_column_int(&q, 9); /* Is target file is a link */
int deleted = db_column_int(&q, 10); /* Marked for deletion */
char *zFullPath; /* Full pathname of the file */
char *zFullNewPath; /* Full pathname of dest */
char nameChng; /* True if the name changed */
const char *zOp = 0; /* Type of change. */
i64 sz = 0; /* Size of the file */
int nc = 0; /* Number of conflicts */
const char *zErrMsg = 0; /* Error message */
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
sz = file_size(zFullNewPath, ExtFILE);
nameChng = fossil_strcmp(zName, zNewName);
nUpdate++;
if( deleted ){
db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
}
if( idv>0 && ridv==0 && idt>0 && ridt>0 ){
/* Conflict. This file has been added to the current check-out
** but also exists in the target check-out. Use the current version.
*/
fossil_print("CONFLICT %s\n", zName);
nConflict++;
zOp = "CONFLICT";
nc = 1;
zErrMsg = "duplicate file";
}else if( idt>0 && idv==0 ){
/* File added in the target. */
if( file_isfile_or_link(zFullPath) ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup previously unanaged file before to be overwritten */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
fossil_print("ADD %s - overwrites an unmanaged file", zName);
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
nc = 1;
zOp = "CONFLICT";
zErrMsg = "new file overwrites unmanaged file";
}else{
fossil_print("ADD %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){
/* The file is unedited. Change it to the target version */
if( deleted ){
fossil_print("UPDATE %s - change to unmanaged file\n", zName);
}else{
fossil_print("UPDATE %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
zOp = "UPDATE";
}else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
/* The file missing from the local check-out. Restore it to the
** version that appears in the target. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
zOp = "UPDATE";
}else if( idt==0 && idv>0 ){
if( ridv==0 ){
/* Added in current check-out. Continue to hold the file as
** as an addition */
db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
}else if( chnged ){
/* Edited locally but deleted from the target. Do not track the
** file but keep the edited version around. */
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
zName);
zOp = "CONFLICT";
zErrMsg = "edited locally but deleted by update";
nc = 1;
nConflict++;
}else{
fossil_print("REMOVE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ){
char *zDir;
file_delete(zFullPath);
|
| ︙ | ︙ | |||
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
/* Merge the changes in the current tree into the target version */
Blob r, t, v;
int rc;
if( nameChng ){
fossil_print("MERGE %s -> %s\n", zName, zNewName);
}else{
fossil_print("MERGE %s\n", zName);
}
if( islinkv || islinkt ){
fossil_print("***** Cannot merge symlink %s\n", zNewName);
nConflict++;
}else{
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
content_get(ridt, &t);
content_get(ridv, &v);
rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
if( rc>=0 ){
if( !dryRunFlag ){
blob_write_to_file(&r, zFullNewPath);
file_setexe(zFullNewPath, isexe);
}
if( rc>0 ){
fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
nConflict++;
}
}else{
if( !dryRunFlag ){
if( !keepMergeFlag ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup non-mergeable binary file when --keep-merge-files is
not specified */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
}
blob_write_to_file(&t, zFullNewPath);
file_setexe(zFullNewPath, isexe);
}
fossil_print("***** Cannot merge binary file %s", zNewName);
if( !dryRunFlag ){
fossil_print(", original copy backed up locally");
}
fossil_print("\n");
nConflict++;
}
}
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
blob_reset(&v);
blob_reset(&t);
blob_reset(&r);
}else{
nUpdate--;
if( chnged ){
if( verboseFlag ) fossil_print("EDITED %s\n", zName);
}else{
db_bind_int(&mtimeXfer, ":idv", idv);
db_bind_int(&mtimeXfer, ":idt", idt);
db_step(&mtimeXfer);
db_reset(&mtimeXfer);
if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
}
}
free(zFullPath);
free(zFullNewPath);
}
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
/* Merge the changes in the current tree into the target version */
Blob r, t, v;
int rc;
if( nameChng ){
fossil_print("MERGE %s -> %s\n", zName, zNewName);
}else{
fossil_print("MERGE %s\n", zName);
}
if( islinkv || islinkt ){
fossil_print("***** Cannot merge symlink %s\n", zNewName);
zOp = "CONFLICT";
nConflict++;
}else{
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
content_get(ridt, &t);
content_get(ridv, &v);
rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
if( rc>=0 ){
if( !dryRunFlag ){
blob_write_to_file(&r, zFullNewPath);
file_setexe(zFullNewPath, isexe);
}
if( rc>0 ){
nc = rc;
zOp = "CONFLICT";
zErrMsg = "merge conflicts";
fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
nConflict++;
}else{
zOp = "MERGE";
}
}else{
if( !dryRunFlag ){
if( !keepMergeFlag ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup non-mergeable binary file when --keep-merge-files is
not specified */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
}
blob_write_to_file(&t, zFullNewPath);
file_setexe(zFullNewPath, isexe);
}
fossil_print("***** Cannot merge binary file %s", zNewName);
if( !dryRunFlag ){
fossil_print(", original copy backed up locally");
}
fossil_print("\n");
nConflict++;
zOp = "ERROR";
zErrMsg = "cannot merge binary file";
nc = 1;
}
}
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
blob_reset(&v);
blob_reset(&t);
blob_reset(&r);
}else{
nUpdate--;
if( chnged ){
if( verboseFlag ) fossil_print("EDITED %s\n", zName);
}else{
db_bind_int(&mtimeXfer, ":idv", idv);
db_bind_int(&mtimeXfer, ":idt", idt);
db_step(&mtimeXfer);
db_reset(&mtimeXfer);
if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
}
}
if( zOp!=0 ){
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
"VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)",
/* op */ zOp,
/* fnp */ zName,
/* ridp */ ridv,
/* fn */ zNewName,
/* sz */ sz,
/* fnm */ zName,
/* ridm */ ridt,
/* fnr */ zNewName,
/* nc */ nc,
/* msg */ zErrMsg
);
}
free(zFullPath);
free(zFullNewPath);
}
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
zNewBrName = branch_of_rid(tid);
if( g.argc<3 && fossil_strcmp(zCurBrName, zNewBrName)!=0 ){
zBrChgMsg = mprintf(" Branch changed from %s to %s.",
zCurBrName, zNewBrName);
}
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
fossil_print("%-13s None. Already up-to-date.%s\n", "changes:", zBrChgMsg);
}else{
fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid),
db_text("", "SELECT datetime(mtime) || ' UTC' FROM event "
" WHERE objid=%d", vid));
show_common_info(tid, "updated-to:", 1, 0);
fossil_print("%-13s %d file%s modified.%s\n", "changes:",
nUpdate, nUpdate>1 ? "s" : "", zBrChgMsg);
}
/* Report on conflicts
*/
if( !dryRunFlag ){
Stmt q;
int nMerge = 0;
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
620 621 622 623 624 625 626 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
| | | | | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
char * zLabel = mprintf("<h2><a href='%R/attachlist?page=%T'>"
"Attachments</a>:</h2>",
zPageName);
attachment_list(zPageName, zLabel, 1);
fossil_free(zLabel);
document_emit_js(/*for optional pikchr support*/);
style_finish_page();
}
}
/*
|
| ︙ | ︙ | |||
1329 1330 1331 1332 1333 1334 1335 |
/* Status bar */
CX("<div id='fossil-status-bar' "
"title='Status message area. Double-click to clear them.'>"
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
| | | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 |
/* Status bar */
CX("<div id='fossil-status-bar' "
"title='Status message area. Double-click to clear them.'>"
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
CX("<div id='wikiedit-edit-status'>"
"<span class='name'></span>"
"<span class='links'></span>"
"</div>");
/* Main tab container... */
CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>");
/* The .hidden class on the following tab elements is to help lessen
|
| ︙ | ︙ |
Changes to src/winfile.c.
| ︙ | ︙ | |||
288 289 290 291 292 293 294 295 | } zUtf8 = fossil_path_to_utf8(zWide); fossil_free(zWide); for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; strncpy(zBuf, zUtf8, nBuf); fossil_path_free(zUtf8); } #endif /* _WIN32 -- This code is for win32 only */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 |
}
zUtf8 = fossil_path_to_utf8(zWide);
fossil_free(zWide);
for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/';
strncpy(zBuf, zUtf8, nBuf);
fossil_path_free(zUtf8);
}
/* Perform case-insensitive comparison of two UTF-16 file names. Try to load the
** CompareStringOrdinal() function on Windows Vista and newer, and resort to the
** RtlEqualUnicodeString() function on Windows XP.
** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW()
** performs linguistic comparison, while the former performs binary comparison.
** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is
** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases
** that are technically impossible and contradicting all known laws of physics.
*/
int win32_filenames_equal_nocase(
const wchar_t *fn1,
const wchar_t *fn2
){
static FARPROC fnCompareStringOrdinal;
static FARPROC fnRtlInitUnicodeString;
static FARPROC fnRtlEqualUnicodeString;
static int loaded_CompareStringOrdinal;
static int loaded_RtlUnicodeStringAPIs;
if( !loaded_CompareStringOrdinal ){
fnCompareStringOrdinal =
GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
loaded_CompareStringOrdinal = 1;
}
if( fnCompareStringOrdinal ){
return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0;
}
if( !loaded_RtlUnicodeStringAPIs ){
fnRtlInitUnicodeString =
GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString");
fnRtlEqualUnicodeString =
GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString");
loaded_RtlUnicodeStringAPIs = 1;
}
if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){
struct { /* UNICODE_STRING from <ntdef.h> */
unsigned short Length;
unsigned short MaximumLength;
wchar_t *Buffer;
} u1, u2;
fnRtlInitUnicodeString(&u1,fn1);
fnRtlInitUnicodeString(&u2,fn2);
return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1);
}
/* In what kind of strange parallel universe are we? */
return lstrcmpiW(fn1,fn2)==0;
}
/* Helper macros to deal with directory separators. */
#define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' )
#define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;}
/* The Win32 version of file_case_preferred_name() from file.c, which is able to
** find case-preserved file names containing non-ASCII characters. The result is
** allocated by fossil_malloc() and *should* be free'd by the caller. While this
** function usually gets canonicalized paths, it is able to handle any input and
** figure out more cases than the original:
**
** fossil test-case-filename C:/ .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE
** → Original: .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE
** → Modified: .//..\Windows\/.//.\System32\.\notepad.exe
**
** md ÄÖÜ
** fossil test-case-filename ./\ .\äöü\/[empty]\\/
** → Original: ./äöü\/[empty]\\/
** → Modified: .\ÄÖÜ\/[empty]\\/
**
** The function preserves slashes and backslashes: only single file or directory
** components without directory separators ("basenames") are converted to UTF-16
** using fossil_utf8_to_path(), so bypassing its slash ↔ backslash translations.
** Note that the original function doesn't preserve all slashes and backslashes,
** for example in the second example above.
**
** NOTE: As of Windows 10, version 1803, case sensitivity may be enabled on a
** per-directory basis, as returned by NtQueryInformationFile() with the file
** information class FILE_CASE_SENSITIVE_INFORMATION. So this function may be
** changed to act like fossil_strdup() for files located in such directories.
*/
char *win32_file_case_preferred_name(
const char *zBase,
const char *zPath
){
int cchBase;
int cchPath;
int cchBuf;
int cchRes;
char *zBuf;
char *zRes;
int ncUsed;
int i, j;
if( filenames_are_case_sensitive() ){
return fossil_strdup(zPath);
}
cchBase = strlen(zBase);
cchPath = strlen(zPath);
cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */
cchRes = cchPath + 1; /* + NULL */
zBuf = fossil_malloc(cchBuf);
zRes = fossil_malloc(cchRes);
ncUsed = 0;
memcpy(zBuf,zBase,cchBase);
if( !IS_DIRSEP(zBuf,cchBase-1) ){
zBuf[cchBase++]=L'/';
}
memcpy(zBuf+cchBase,zPath,cchPath+1);
i = j = cchBase;
while( 1 ){
WIN32_FIND_DATAW fd;
HANDLE hFind;
wchar_t *wzBuf;
char *zCompBuf = 0;
char *zComp = &zBuf[i];
int cchComp;
char chSep;
int fDone;
if( IS_DIRSEP(zBuf,i) ){
if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/
cchRes += 32; /* Overprovisioning. */
zRes = fossil_realloc(zRes,cchRes);
}
zRes[ncUsed++] = zBuf[i];
i = j = i+1;
continue;
}
NEXT_DIRSEP(zBuf,j);
fDone = zBuf[j]==0;
chSep = zBuf[j];
zBuf[j] = 0; /* Truncate working buffer. */
wzBuf = fossil_utf8_to_path(zBuf,0);
hFind = FindFirstFileW(wzBuf,&fd);
if( hFind!=INVALID_HANDLE_VALUE ){
wchar_t *wzComp = fossil_utf8_to_path(zComp,0);
FindClose(hFind);
/* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */
if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){
zCompBuf = fossil_path_to_utf8(fd.cFileName);
zComp = zCompBuf;
}
fossil_path_free(wzComp);
}
fossil_path_free(wzBuf);
cchComp = strlen(zComp);
if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */
cchRes += cchComp + 32; /* Overprovisioning. */
zRes = fossil_realloc(zRes,cchRes);
}
memcpy(zRes+ncUsed,zComp,cchComp);
ncUsed += cchComp;
if( zCompBuf ){
fossil_path_free(zCompBuf);
}
if( fDone ){
zRes[ncUsed] = 0;
break;
}
zBuf[j] = chSep; /* Undo working buffer truncation. */
i = j;
}
fossil_free(zBuf);
return zRes;
}
/* Return the unique identifier (UID) for a file, made up of the file identifier
** (equal to "inode" for Unix-style file systems) plus the volume serial number.
** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort
** to the GetFileInformationByHandle() function on Windows XP. The result string
** is allocated by mprintf(), or NULL on failure.
*/
char *win32_file_id(
const char *zFileName
){
static FARPROC fnGetFileInformationByHandleEx;
static int loaded_fnGetFileInformationByHandleEx;
wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0);
HANDLE hFile;
char *zFileId = 0;
hFile = CreateFileW(
wzFileName,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if( hFile!=INVALID_HANDLE_VALUE ){
BY_HANDLE_FILE_INFORMATION fi;
struct { /* FILE_ID_INFO from <winbase.h> */
u64 VolumeSerialNumber;
unsigned char FileId[16];
} fi2;
if( !loaded_fnGetFileInformationByHandleEx ){
fnGetFileInformationByHandleEx = GetProcAddress(
GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
loaded_fnGetFileInformationByHandleEx = 1;
}
if( fnGetFileInformationByHandleEx ){
if( fnGetFileInformationByHandleEx(
hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){
zFileId = mprintf(
"%016llx/"
"%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x",
fi2.VolumeSerialNumber,
fi2.FileId[15], fi2.FileId[14],
fi2.FileId[13], fi2.FileId[12],
fi2.FileId[11], fi2.FileId[10],
fi2.FileId[9], fi2.FileId[8],
fi2.FileId[7], fi2.FileId[6],
fi2.FileId[5], fi2.FileId[4],
fi2.FileId[3], fi2.FileId[2],
fi2.FileId[1], fi2.FileId[0]);
}
}
if( zFileId==0 ){
if( GetFileInformationByHandle(hFile,&fi) ){
ULARGE_INTEGER FileId = {
/*.LowPart = */ fi.nFileIndexLow,
/*.HighPart = */ fi.nFileIndexHigh
};
zFileId = mprintf(
"%08x/%016llx",
fi.dwVolumeSerialNumber,(u64)FileId.QuadPart);
}
}
CloseHandle(hFile);
}
fossil_path_free(wzFileName);
return zFileId;
}
#endif /* _WIN32 -- This code is for win32 only */
|
Changes to src/xfer.c.
| ︙ | ︙ | |||
334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
pHash = &pXfer->aToken[3];
if( pXfer->nToken==5
|| !blob_is_filename(&pXfer->aToken[1])
|| !blob_is_int64(&pXfer->aToken[2], &mtime)
|| (!blob_eq(pHash,"-") && !blob_is_hname(pHash))
|| !blob_is_int(&pXfer->aToken[4], &sz)
|| !blob_is_int(&pXfer->aToken[5], &flags)
){
blob_appendf(&pXfer->err, "malformed uvfile line");
return;
}
blob_init(&content, 0, 0);
blob_init(&x, 0, 0);
if( sz>0 && (flags & 0x0005)==0 ){
| > | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
pHash = &pXfer->aToken[3];
if( pXfer->nToken==5
|| !blob_is_filename(&pXfer->aToken[1])
|| !blob_is_int64(&pXfer->aToken[2], &mtime)
|| (!blob_eq(pHash,"-") && !blob_is_hname(pHash))
|| !blob_is_int(&pXfer->aToken[4], &sz)
|| !blob_is_int(&pXfer->aToken[5], &flags)
|| (mtime<0 || sz<0 || flags<0)
){
blob_appendf(&pXfer->err, "malformed uvfile line");
return;
}
blob_init(&content, 0, 0);
blob_init(&x, 0, 0);
if( sz>0 && (flags & 0x0005)==0 ){
|
| ︙ | ︙ | |||
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 |
/*
** Return the TH1 code to evaluate when a ticket change is processed.
*/
const char *xfer_ticket_code(void){
return db_get("xfer-ticket-script", 0);
}
/*
** Run the specified TH1 script, if any, and returns 1 on error.
*/
int xfer_run_script(
const char *zScript,
const char *zUuidOrList,
| > > > > > > > > > > > > > > | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 |
/*
** Return the TH1 code to evaluate when a ticket change is processed.
*/
const char *xfer_ticket_code(void){
return db_get("xfer-ticket-script", 0);
}
/*
** Reset the CGI content, roll back any pending db transaction, and
** emit an "error" xfer message. The message text gets fossil-encoded
** by this function. This is only intended for use with
** fail-fast/fatal errors, not ones which can be skipped over.
*/
static void xfer_fatal_error(const char *zMsg){
cgi_reset_content();
if( db_transaction_nesting_depth()>0 ){
db_rollback_transaction();
}
@ error %F(zMsg)
}
/*
** Run the specified TH1 script, if any, and returns 1 on error.
*/
int xfer_run_script(
const char *zScript,
const char *zUuidOrList,
|
| ︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 |
&& iVers>=2
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
max = db_int(0, "SELECT max(rid) FROM blob");
while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
if( time(NULL) >= xfer.maxTime ) break;
if( iVers>=3 ){
send_compressed_file(&xfer, seqno);
}else{
send_file(&xfer, seqno, 0, 1);
| > > > > | 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 |
&& iVers>=2
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
if( seqno<=0 ){
xfer_fatal_error("invalid clone sequence number");
return;
}
max = db_int(0, "SELECT max(rid) FROM blob");
while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
if( time(NULL) >= xfer.maxTime ) break;
if( iVers>=3 ){
send_compressed_file(&xfer, seqno);
}else{
send_file(&xfer, seqno, 0, 1);
|
| ︙ | ︙ | |||
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 |
** Client has sent a configuration value to the server.
** This is only permitted for high-privilege users.
*/
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
&& blob_is_int(&xfer.aToken[2], &size) ){
const char *zName = blob_str(&xfer.aToken[1]);
Blob content;
blob_zero(&content);
blob_extract(xfer.pIn, size, &content);
if( !g.perm.Admin ){
cgi_reset_content();
@ error not\sauthorized\sto\spush\sconfiguration
nErr++;
break;
| > > > > | 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 |
** Client has sent a configuration value to the server.
** This is only permitted for high-privilege users.
*/
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
&& blob_is_int(&xfer.aToken[2], &size) ){
const char *zName = blob_str(&xfer.aToken[1]);
Blob content;
if( size<0 ){
xfer_fatal_error("invalid config record");
return;
}
blob_zero(&content);
blob_extract(xfer.pIn, size, &content);
if( !g.perm.Admin ){
cgi_reset_content();
@ error not\sauthorized\sto\spush\sconfiguration
nErr++;
break;
|
| ︙ | ︙ | |||
1842 1843 1844 1845 1846 1847 1848 |
}
/* Send the server timestamp last, in case prior processing happened
** to use up a significant fraction of our time window.
*/
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@ # timestamp %s(zNow) errors %d(nErr)
| | | 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 |
}
/* Send the server timestamp last, in case prior processing happened
** to use up a significant fraction of our time window.
*/
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@ # timestamp %s(zNow) errors %d(nErr)
fossil_free(zNow);
db_commit_transaction();
configure_rebuild();
}
/*
** COMMAND: test-xfer
|
| ︙ | ︙ | |||
2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 |
&& blob_is_int64(&xfer.aToken[2], &mtime)
&& blob_is_int(&xfer.aToken[4], &size)
&& (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3]))
){
const char *zName = blob_str(&xfer.aToken[1]);
const char *zHash = blob_str(&xfer.aToken[3]);
int iStatus;
iStatus = unversioned_status(zName, mtime, zHash);
if( (syncFlags & SYNC_UV_REVERT)!=0 ){
if( iStatus==4 ) iStatus = 2;
if( iStatus==5 ) iStatus = 1;
}
if( syncFlags & (SYNC_UV_TRACE|SYNC_UV_DRYRUN) ){
const char *zMsg = 0;
| > > > > | 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 |
&& blob_is_int64(&xfer.aToken[2], &mtime)
&& blob_is_int(&xfer.aToken[4], &size)
&& (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3]))
){
const char *zName = blob_str(&xfer.aToken[1]);
const char *zHash = blob_str(&xfer.aToken[3]);
int iStatus;
if( mtime<0 || size<0 ){
xfer_fatal_error("invalid uvigot");
return ++nErr;
}
iStatus = unversioned_status(zName, mtime, zHash);
if( (syncFlags & SYNC_UV_REVERT)!=0 ){
if( iStatus==4 ) iStatus = 2;
if( iStatus==5 ) iStatus = 1;
}
if( syncFlags & (SYNC_UV_TRACE|SYNC_UV_DRYRUN) ){
const char *zMsg = 0;
|
| ︙ | ︙ | |||
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 |
** The received configuration setting is silently ignored if it was
** not requested by a prior "reqconfig" sent from client to server.
*/
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
&& blob_is_int(&xfer.aToken[2], &size) ){
const char *zName = blob_str(&xfer.aToken[1]);
Blob content;
blob_zero(&content);
blob_extract(xfer.pIn, size, &content);
g.perm.Admin = g.perm.RdAddr = 1;
configure_receive(zName, &content, origConfigRcvMask);
nCardRcvd++;
nArtifactRcvd++;
blob_reset(&content);
| > > > > | 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 |
** The received configuration setting is silently ignored if it was
** not requested by a prior "reqconfig" sent from client to server.
*/
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
&& blob_is_int(&xfer.aToken[2], &size) ){
const char *zName = blob_str(&xfer.aToken[1]);
Blob content;
if( size<0 ){
xfer_fatal_error("invalid config record");
return ++nErr;
}
blob_zero(&content);
blob_extract(xfer.pIn, size, &content);
g.perm.Admin = g.perm.RdAddr = 1;
configure_receive(zName, &content, origConfigRcvMask);
nCardRcvd++;
nArtifactRcvd++;
blob_reset(&content);
|
| ︙ | ︙ | |||
2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 |
** When doing a clone, the server tries to send all of its artifacts
** in sequence. This card indicates the sequence number of the next
** blob that needs to be sent. If N<=0 that indicates that all blobs
** have been sent.
*/
if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
blob_is_int(&xfer.aToken[1], &cloneSeqno);
}else
/* message MESSAGE
**
** A message is received from the server. Print it.
** Similar to "error" but does not stop processing.
**
| > > > > | 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 |
** When doing a clone, the server tries to send all of its artifacts
** in sequence. This card indicates the sequence number of the next
** blob that needs to be sent. If N<=0 that indicates that all blobs
** have been sent.
*/
if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
blob_is_int(&xfer.aToken[1], &cloneSeqno);
if( cloneSeqno<0 ){
xfer_fatal_error("invalid clone_seqno");
return ++nErr;
}
}else
/* message MESSAGE
**
** A message is received from the server. Print it.
** Similar to "error" but does not stop processing.
**
|
| ︙ | ︙ | |||
2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 |
*/
else if( blob_eq(&xfer.aToken[1], "ci-lock-fail") && xfer.nToken==4 ){
char *zUser = blob_terminate(&xfer.aToken[2]);
sqlite3_int64 mtime, iNow;
defossilize(zUser);
iNow = time(NULL);
if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){
iNow = time(NULL);
fossil_print("\nParent check-in locked by %s %s ago\n",
zUser, human_readable_age((iNow+1-mtime)/86400.0));
}else{
fossil_print("\nParent check-in locked by %s\n", zUser);
}
g.ckinLockFail = fossil_strdup(zUser);
| > > > > | 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 |
*/
else if( blob_eq(&xfer.aToken[1], "ci-lock-fail") && xfer.nToken==4 ){
char *zUser = blob_terminate(&xfer.aToken[2]);
sqlite3_int64 mtime, iNow;
defossilize(zUser);
iNow = time(NULL);
if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){
if( mtime<0 ){
xfer_fatal_error("invalid ci-lock-fail time");
return ++nErr;
}
iNow = time(NULL);
fossil_print("\nParent check-in locked by %s %s ago\n",
zUser, human_readable_age((iNow+1-mtime)/86400.0));
}else{
fossil_print("\nParent check-in locked by %s\n", zUser);
}
g.ckinLockFail = fossil_strdup(zUser);
|
| ︙ | ︙ |
Changes to test/comment.test.
| ︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 |
test comment-59 {$RESULT eq " 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(5 lines output)"}
###############################################################################
fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig
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)"}
###############################################################################
test_cleanup
| > > > > > > > > > > > > > > > > > > > > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
test comment-59 {$RESULT eq " 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(5 lines output)"}
###############################################################################
fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig
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)"}
###############################################################################
fossil test-comment-format --width 72 --file "" [file join $testdir "utf8-comment.txt"]
test comment-61 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\] an\nd symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}
###############################################################################
fossil test-comment-format --width 72 --wordbreak --file "" [file join $testdir "utf8-comment.txt"]
test comment-62 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}
###############################################################################
fossil test-comment-format --width 72 --legacy --file "" [file join $testdir "utf8-comment.txt"]
test comment-63 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}
###############################################################################
fossil test-comment-format --width 72 --legacy --wordbreak --file "" [file join $testdir "utf8-comment.txt"]
test comment-64 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}
###############################################################################
test_cleanup
|
Changes to test/glob.test.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
incr i
}
}
glob-parse 100 test test [string map [list \r\n \n] \
{SQL expression: (x GLOB 'test')
pattern[0] = [test]
| | | | | | | | | | | | | | | | | | | | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
incr i
}
}
glob-parse 100 test test [string map [list \r\n \n] \
{SQL expression: (x GLOB 'test')
pattern[0] = [test]
1 1 test}]
glob-parse 101 "one two" one [string map [list \r\n \n] \
{SQL expression: (x GLOB 'one' OR x GLOB 'two')
pattern[0] = [one]
pattern[1] = [two]
1 1 one}]
glob-parse 102 t* test [string map [list \r\n \n] \
{SQL expression: (x GLOB 't*')
pattern[0] = [t*]
1 1 test}]
glob-parse 103 "o* two" one [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*' OR x GLOB 'two')
pattern[0] = [o*]
pattern[1] = [two]
1 1 one}]
glob-parse 104 {"o* two" "three four"} "one two" [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o* two' OR x GLOB 'three four')
pattern[0] = [o* two]
pattern[1] = [three four]
1 1 one two}]
glob-parse 105 {"o* two" "three four"} "two one" [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o* two' OR x GLOB 'three four')
pattern[0] = [o* two]
pattern[1] = [three four]
0 0 two one}]
glob-parse 106 "\"o*\ntwo\" \"three\nfour\"" "one\ntwo" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*
two' OR x GLOB 'three
four')
pattern[0] = [o*
two]
pattern[1] = [three
four]
1 1 one
two}]
glob-parse 107 "\"o*\ntwo\" \"three\nfour\"" "two\none" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*
two' OR x GLOB 'three
four')
pattern[0] = [o*
two]
pattern[1] = [three
four]
0 0 two
one}]
glob-parse 108 "\"o*\rtwo\" \"three\rfour\"" "one\rtwo" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*
two' OR x GLOB 'three
four')
pattern[0] = [o*
two]
pattern[1] = [three
four]
1 1 one
two}]
glob-parse 109 "\"o*\rtwo\" \"three\rfour\"" "two\rone" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*
two' OR x GLOB 'three
four')
pattern[0] = [o*
two]
pattern[1] = [three
four]
0 0 two
one}]
glob-parse 110 "'o*\ntwo' 'three\nfour'" "one\ntwo" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*
two' OR x GLOB 'three
four')
pattern[0] = [o*
two]
pattern[1] = [three
four]
1 1 one
two}]
glob-parse 111 "'o*\ntwo' 'three\nfour'" "two\none" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*
two' OR x GLOB 'three
four')
pattern[0] = [o*
two]
pattern[1] = [three
four]
0 0 two
one}]
glob-parse 112 "\"'o*' 'two'\" \"'three' 'four'\"" "'one' 'two'" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''')
pattern[0] = ['o*' 'two']
pattern[1] = ['three' 'four']
1 1 'one' 'two'}]
glob-parse 113 "\"'o*' 'two'\" \"'three' 'four'\"" "two one" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''')
pattern[0] = ['o*' 'two']
pattern[1] = ['three' 'four']
0 0 two one}]
glob-parse 114 o*,two one [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*' OR x GLOB 'two')
pattern[0] = [o*]
pattern[1] = [two]
1 1 one}]
glob-parse 115 "o*,two three,four" "one two" [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*' OR x GLOB 'two' OR x GLOB 'three' OR x GLOB 'four')
pattern[0] = [o*]
pattern[1] = [two]
pattern[2] = [three]
pattern[3] = [four]
1 1 one two}]
glob-parse 116 'o*,two' one [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*,two')
pattern[0] = [o*,two]
0 0 one}]
glob-parse 117 'o*,two' one,two [string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*,two')
pattern[0] = [o*,two]
1 1 one,two}]
glob-parse 118 "'o*,two three,four'" "one two three,four" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*,two three,four')
pattern[0] = [o*,two three,four]
0 0 one two three,four}]
glob-parse 119 "'o*,two three,four'" "one,two three,four" \
[string map [list \r\n \n] \
{SQL expression: (x GLOB 'o*,two three,four')
pattern[0] = [o*,two three,four]
1 1 one,two three,four}]
###############################################################################
test_cleanup
|
Changes to test/settings.test.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # # Complete syntax as tested: # # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? # fossil unset PROPERTY ?OPTIONS? # # Where the only supported options are "--global" and "--exact". # ############################################################################### # # NOTE: The [extract_setting_names] procedure extracts the list of setting # names from the line-ending normalized output of the "fossil settings" # command. It assumes that a setting name must begin with a lowercase # letter. It also assumes that any output lines that start with a | > > > > > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | # # Complete syntax as tested: # # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? # fossil unset PROPERTY ?OPTIONS? # # Where the only supported options are "--global" and "--exact". # ############################################################################### # # NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list # of settings to test and needs to be manually updated when new settings # are added. # ############################################################################### # # NOTE: The [extract_setting_names] procedure extracts the list of setting # names from the line-ending normalized output of the "fossil settings" # command. It assumes that a setting name must begin with a lowercase # letter. It also assumes that any output lines that start with a |
| ︙ | ︙ |
Changes to test/tester.tcl.
| ︙ | ︙ | |||
357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
pgp-command \
preferred-diff-type \
proxy \
redirect-to-https \
relative-paths \
repo-cksum \
repolist-skin \
safe-html \
self-pw-reset \
self-register \
sitemap-extra \
ssh-command \
ssl-ca-location \
ssl-identity \
| > > | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
pgp-command \
preferred-diff-type \
proxy \
redirect-to-https \
relative-paths \
repo-cksum \
repolist-skin \
robot-restrict \
robots-txt \
safe-html \
self-pw-reset \
self-register \
sitemap-extra \
ssh-command \
ssl-ca-location \
ssl-identity \
|
| ︙ | ︙ |
Added test/utf8-comment.txt.
> | 1 | The comment formatter handles fullwidth and multi-byte [äöü] and symbols [☃] and emoji [💾] characters! |
Changes to tools/makemake.tcl.
| ︙ | ︙ | |||
207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
}
# Additional resource files that get built into the executable.
# These paths are all resolved from the src/ directory, so must
# be relative to that.
set extra_files {
diff.tcl
markdown.md
wiki.wiki
*.js
default.css
style.*.css
../skins/*/*.txt
sounds/*.wav
| > | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
}
# Additional resource files that get built into the executable.
# These paths are all resolved from the src/ directory, so must
# be relative to that.
set extra_files {
diff.tcl
merge.tcl
markdown.md
wiki.wiki
*.js
default.css
style.*.css
../skins/*/*.txt
sounds/*.wav
|
| ︙ | ︙ | |||
443 444 445 446 447 448 449 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 LINENOISE_DEF. = $(LINENOISE_DEF.0) LINENOISE_OBJ.0 = LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o LINENOISE_OBJ. = $(LINENOISE_OBJ.0) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the |
| ︙ | ︙ |
Changes to tools/man_page_command_list.tcl.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
# The only supported command-line argument is the optional output filename.
if {[llength $argv] == 1} {
set file [lindex $argv 0]
}
# Get list of common commands.
set commands [exec fossil help]
| | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# The only supported command-line argument is the optional output filename.
if {[llength $argv] == 1} {
set file [lindex $argv 0]
}
# Get list of common commands.
set commands [exec fossil help]
regsub -nocase {.*?\nfrequently used commands:.*\n} $commands {} commands
regsub -nocase {\nthis is fossil version.*} $commands {} commands
regsub -all {\s+} $commands " " commands
set commands [lsort $commands]
# Compute number of rows.
set rows [expr {([llength $commands] + $columns - 1) / $columns}]
|
| ︙ | ︙ |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
632 633 634 635 636 637 638 639 640 641 642 643 644 645 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ | > | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/merge.tcl \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
590 591 592 593 594 595 596 597 598 599 600 601 602 603 |
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
"$(SRCDIR)\graph.js" \
"$(SRCDIR)\hbmenu.js" \
"$(SRCDIR)\href.js" \
"$(SRCDIR)\login.js" \
"$(SRCDIR)\markdown.md" \
"$(SRCDIR)\menu.js" \
"$(SRCDIR)\scroll.js" \
"$(SRCDIR)\skin.js" \
"$(SRCDIR)\sorttable.js" \
"$(SRCDIR)\sounds\0.wav" \
"$(SRCDIR)\sounds\1.wav" \
"$(SRCDIR)\sounds\2.wav" \
"$(SRCDIR)\sounds\3.wav" \
| > | 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
"$(SRCDIR)\graph.js" \
"$(SRCDIR)\hbmenu.js" \
"$(SRCDIR)\href.js" \
"$(SRCDIR)\login.js" \
"$(SRCDIR)\markdown.md" \
"$(SRCDIR)\menu.js" \
"$(SRCDIR)\merge.tcl" \
"$(SRCDIR)\scroll.js" \
"$(SRCDIR)\skin.js" \
"$(SRCDIR)\sorttable.js" \
"$(SRCDIR)\sounds\0.wav" \
"$(SRCDIR)\sounds\1.wav" \
"$(SRCDIR)\sounds\2.wav" \
"$(SRCDIR)\sounds\3.wav" \
|
| ︙ | ︙ | |||
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ echo "$(SRCDIR)\graph.js" >> $@ echo "$(SRCDIR)\hbmenu.js" >> $@ echo "$(SRCDIR)\href.js" >> $@ echo "$(SRCDIR)\login.js" >> $@ echo "$(SRCDIR)\markdown.md" >> $@ echo "$(SRCDIR)\menu.js" >> $@ echo "$(SRCDIR)\scroll.js" >> $@ echo "$(SRCDIR)\skin.js" >> $@ echo "$(SRCDIR)\sorttable.js" >> $@ echo "$(SRCDIR)\sounds/0.wav" >> $@ echo "$(SRCDIR)\sounds/1.wav" >> $@ echo "$(SRCDIR)\sounds/2.wav" >> $@ echo "$(SRCDIR)\sounds/3.wav" >> $@ | > | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 | echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ echo "$(SRCDIR)\graph.js" >> $@ echo "$(SRCDIR)\hbmenu.js" >> $@ echo "$(SRCDIR)\href.js" >> $@ echo "$(SRCDIR)\login.js" >> $@ echo "$(SRCDIR)\markdown.md" >> $@ echo "$(SRCDIR)\menu.js" >> $@ echo "$(SRCDIR)\merge.tcl" >> $@ echo "$(SRCDIR)\scroll.js" >> $@ echo "$(SRCDIR)\skin.js" >> $@ echo "$(SRCDIR)\sorttable.js" >> $@ echo "$(SRCDIR)\sounds/0.wav" >> $@ echo "$(SRCDIR)\sounds/1.wav" >> $@ echo "$(SRCDIR)\sounds/2.wav" >> $@ echo "$(SRCDIR)\sounds/3.wav" >> $@ |
| ︙ | ︙ |
Changes to www/aboutcgi.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 | <title>How CGI Works In Fossil</title> <h2>Introduction</h2> CGI or "Common Gateway Interface" is a venerable yet reliable technique for generating dynamic web content. This article gives a quick background on how CGI works and describes how Fossil can act as a CGI service. This is a "how it works" guide. This document provides background information on the CGI protocol so that you can better understand what is going on behind the scenes. If you just want to set up Fossil as a CGI server, see the [./server/ | Fossil Server Setup] page. Or | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <title>How CGI Works In Fossil</title> <h2>Introduction</h2> CGI or "Common Gateway Interface" is a venerable yet reliable technique for generating dynamic web content. This article gives a quick background on how CGI works and describes how Fossil can act as a CGI service. This is a "how it works" guide. This document provides background information on the CGI protocol so that you can better understand what is going on behind the scenes. If you just want to set up Fossil as a CGI server, see the [./server/ | Fossil Server Setup] page. Or if you want to develop CGI-based extensions to Fossil, see the [./serverext.wiki|CGI Server Extensions] page. <h2>A Quick Review Of CGI</h2> An HTTP request is a block of text that is sent by a client application (usually a web browser) and arrives at the web server over a network connection. The HTTP request contains a URL that describes the information being requested. The URL in the HTTP request is typically the same URL that appears in the URL bar at the top of the web browser that is making the request. The URL might contain a "?" character followed by query parameters. The HTTP will usually also contain other information such as the name of the application that made the request, whether or not the requesting application can accept a compressed reply, POST parameters from forms, and so forth. The job of the web server is to interpret the HTTP request and formulate an appropriate reply. The web server is free to interpret the HTTP request in any way it wants, but most web servers follow a similar pattern, described below. (Note: details may vary from one web server to another.) Suppose the filename component of the URL in the HTTP request looks like this: <pre>/one/two/timeline/four</pre> Most web servers will search their content area for files that match |
| ︙ | ︙ |
Changes to www/adding_code.wiki.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
generate web page output, depending on context.
The [./makefile.wiki|Makefile] for Fossil takes care of running these
preprocessors with all the right arguments and in the right order. So it is
not necessary to understand the details of how these preprocessors work.
(Though, the sources for all three preprocessors are included in the source
tree and are well commented, if you want to dig deeper.) It is only necessary
| | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
generate web page output, depending on context.
The [./makefile.wiki|Makefile] for Fossil takes care of running these
preprocessors with all the right arguments and in the right order. So it is
not necessary to understand the details of how these preprocessors work.
(Though, the sources for all three preprocessors are included in the source
tree and are well commented, if you want to dig deeper.) It is only necessary
to know that these preprocessors exist and hence will affect the way you
write code.
<h2>3.0 Adding New Source Code Files</h2>
New source code files are added in the "src/" subdirectory of the Fossil
source tree. Suppose one wants to add a new source code file named
"xyzzy.c". The first step is to add this file to the various makefiles.
|
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | be used by other source code files. The makeheaders preprocessor uses definitions in the INTERFACE section to help it generate header files. See [../tools/makeheaders.html | makeheaders.html] for additional information. After creating a template file such as shown above, and after updating the makefiles, you should be able to recompile Fossil and have it include | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | be used by other source code files. The makeheaders preprocessor uses definitions in the INTERFACE section to help it generate header files. See [../tools/makeheaders.html | makeheaders.html] for additional information. After creating a template file such as shown above, and after updating the makefiles, you should be able to recompile Fossil and have it include your new source file, even before your source file contains any code. It is recommended that you try this. Be sure to [/help/add|fossil add] your new source file to the self-hosting Fossil repository and then [/help/commit|commit] your changes! <h2 id="newcmd">4.0 Creating A New Command</h2> |
| ︙ | ︙ | |||
210 211 212 213 214 215 216 | (by the "translate" preprocessor) into printf-like code that generates the content of the webpage. Different techniques are used to generate non-HTML content. In the unlikely event that you need to generate non-HTML content, look at existing webpage implementations (ex: "logo" or "style.css") to see how that is done. There are lots of other things that a real web-page implementation will | | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | (by the "translate" preprocessor) into printf-like code that generates the content of the webpage. Different techniques are used to generate non-HTML content. In the unlikely event that you need to generate non-HTML content, look at existing webpage implementations (ex: "logo" or "style.css") to see how that is done. There are lots of other things that a real web-page implementation will need to do, such as verifying user credentials, parsing query parameters, and interacting with the repository. But now that you have the general idea of how webpages are implemented, you can look at the many other webpage implementations already built into Fossil to see how all that works. <h2>6.0 See Also</h2> * [./makefile.wiki|The Fossil Build Process] * [./tech_overview.wiki|A Technical Overview Of Fossil] * [./contribute.wiki|Contributing To The Fossil Project] * [./serverext.wiki|Adding CGI Extensions To A Fossil Server] |
Changes to www/alerts.md.
| ︙ | ︙ | |||
309 310 311 312 313 314 315 | <a id="advanced"></a> ## Advanced Email Setups Fossil offers several methods of sending email: 1. Pipe the email message text into a command. | | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | <a id="advanced"></a> ## Advanced Email Setups Fossil offers several methods of sending email: 1. Pipe the email message text into a command. 2. Store email messages as entries in an SQLite database. 3. Store email messages as individual files in a directory. 4. Send emails to an SMTP relay. 5. Send emails directly to the recipients via SMTP. This wide range of options allows Fossil to talk to pretty much any SMTP setup. |
| ︙ | ︙ | |||
388 389 390 391 392 393 394 | The self-hosting Fossil repository at <https://fossil-scm.org/> currently uses this method rather than [the pipe method](#pipe) because it is running inside of a restrictive [chroot jail][cj] which is unable to hand off messages to the local MTA directly. When you configure a Fossil server this way, it adds outgoing email | | | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | The self-hosting Fossil repository at <https://fossil-scm.org/> currently uses this method rather than [the pipe method](#pipe) because it is running inside of a restrictive [chroot jail][cj] which is unable to hand off messages to the local MTA directly. When you configure a Fossil server this way, it adds outgoing email messages to an SQLite database file. A separate daemon process can then extract those messages for further disposition. Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl) used on `fossil-scm.org`: it is just a short Tcl script that continuously monitors this database for new messages and hands any that it finds off to a local MTA using the same [pipe to MTA protocol](#pipe) as above. |
| ︙ | ︙ |
Changes to www/antibot.wiki.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | from a human or a robot. In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>". If this setting is set to "UserAgent only" or "UserAgent and Javascript", and if the UserAgent string looks like a human and not a robot, then Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability | | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
from a human or a robot.
In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled
"<b>Enable hyperlinks based on User-Agent and/or Javascript</b>".
If this setting is set to "UserAgent only" or "UserAgent and Javascript",
and if the UserAgent string looks like a human and not a robot, then
Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability
is omitted from the user permissions. This setting gives humans easy
access to the hyperlinks while preventing robots
from walking the billions of pages on a typical Fossil site.
If the setting is "UserAgent only", then the hyperlinks are simply
enabled and that is all. But if the setting is "UserAgent and Javascript",
then the hyperlinks are not enabled directly.
Instead, the HTML code that is generated contains anchor tags ("<a>")
with "href=" attributes that point to [/honeypot] rather than the correct
link. JavaScript code is added to the end of the page that goes back and
fills in the correct "href=" attributes of
the anchor tags with the true hyperlink targets, thus enabling the hyperlinks.
This extra step of using JavaScript to enable the hyperlink targets
is a security measure against robots that forge a human-looking
|
| ︙ | ︙ |
Changes to www/backoffice.md.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | Multiple repositories can be named. This one command will handle launching the backoffice for all of them. There are additional useful command-line options. See the "[fossil backoffice](/help?cmd=backoffice)" documentation for details. The backoffice processes run manually using the "fossil backoffice" | | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | Multiple repositories can be named. This one command will handle launching the backoffice for all of them. There are additional useful command-line options. See the "[fossil backoffice](/help?cmd=backoffice)" documentation for details. The backoffice processes run manually using the "fossil backoffice" command do not normally use a lease. That means that if you run the "fossil backoffice" command with --poll and you forget to disable automatic backoffice by setting the "backoffice-disable" flag, then you might have one backoffice running due to a command and another due to a webpage access, both at the same time. This is harmless. The only downside is that it uses extra CPU time. How Backoffice Is Implemented ----------------------------- The backoffice is implemented by the |
| ︙ | ︙ | |||
208 209 210 211 212 213 214 | "backoffice-nodelay" setting prevents the "next" process from taking a lease and sleeping. If "backoffice-nodelay" is set, that causes all backoffice processes to exit either immediately or after doing whatever backoffice works needs to be done. If something is going wrong and backoffice leases are causing delays in webpage processing, then setting "backoffice-nodelay" to true can work around the problem until the bug can be fixed. The "backoffice-logfile" setting is the name of a log | | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | "backoffice-nodelay" setting prevents the "next" process from taking a lease and sleeping. If "backoffice-nodelay" is set, that causes all backoffice processes to exit either immediately or after doing whatever backoffice works needs to be done. If something is going wrong and backoffice leases are causing delays in webpage processing, then setting "backoffice-nodelay" to true can work around the problem until the bug can be fixed. The "backoffice-logfile" setting is the name of a log file onto which is appended a short message every time a backoffice process actually starts to do the backoffice work. This log file can be used to verify that backoffice really is running, if there is any doubt. The "backoffice-disable" setting prevents automatic backoffice processing, if true. Use this to completely disable backoffice processing that occurs automatically after each HTTP request. The "backoffice-disable" setting does not affect the operation of the manual "fossil backoffice" command. Most installations should leave "backoffice-nodelay" and "backoffice-disable" set to their default values of off and leave "backoffice-logfile" unset or set to an empty string. |
Changes to www/blame.wiki.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
<ol type='1'>
<li>Locate the check-in that contains the file that is to be
annotated. Call this check-in C0.
<li>Find all direct ancestors of C0. A direct ancestor is the closure
of the primary parent of C0. Merged in branches are not part of
the direct ancestors of C0.
<li>Prune the list of ancestors of C0 so that it contains only
| | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<ol type='1'>
<li>Locate the check-in that contains the file that is to be
annotated. Call this check-in C0.
<li>Find all direct ancestors of C0. A direct ancestor is the closure
of the primary parent of C0. Merged in branches are not part of
the direct ancestors of C0.
<li>Prune the list of ancestors of C0 so that it contains only
check-ins in which the file to be annotated was modified.
<li>Load the complete text of the file to be annotated from check-in C0.
Call this version of the file F0.
<li>Parse F0 into lines. Mark each line as "unchanged".
<li>For each ancestor of C0 on the pruned list (call the ancestor CX),
beginning with the most
recent ancestor and moving toward the oldest ancestor, do the
following steps:
|
| ︙ | ︙ | |||
43 44 45 46 47 48 49 | <h2>3.0 Discussion and Notes</h2> The time-consuming part of this algorithm is step 6b - computing the diff from all historical versions of the file to the version of the file under analysis. For a large file that has many historical changes, this can take several seconds. For this reason, the default | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <h2>3.0 Discussion and Notes</h2> The time-consuming part of this algorithm is step 6b - computing the diff from all historical versions of the file to the version of the file under analysis. For a large file that has many historical changes, this can take several seconds. For this reason, the default [/help?cmd=/annotate|/annotate] webpage only shows those lines that were changed by the 20 most recent modifications to the file. This allows the loop on step 6 to terminate after only 19 diffs instead of the hundreds or thousands of diffs that might be required for a frequently modified file. As currently implemented (as of 2015-12-12) the annotate algorithm does not follow files across name changes. File name change information is available in the database, and so the algorithm could be enhanced to follow files across name changes by modifications to step 3. Step 2 is interesting in that it is [/artifact/6cb824a0417?ln=196-201 | implemented] using a [https://www.sqlite.org/lang_with.html#recursivecte|recursive common table expression]. |
Changes to www/bugtheory.wiki.
| ︙ | ︙ | |||
110 111 112 113 114 115 116 | to repopulate the table using the new column names. Note that the TICKET table schema and content is part of the local state of a repository and is not shared with other repositories during a sync, push, or pull. Each repository also defines scripts used to generate web pages for creating new tickets, viewing existing tickets, and modifying an existing ticket. These scripts consist of HTML with an embedded | | | | | | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | to repopulate the table using the new column names. Note that the TICKET table schema and content is part of the local state of a repository and is not shared with other repositories during a sync, push, or pull. Each repository also defines scripts used to generate web pages for creating new tickets, viewing existing tickets, and modifying an existing ticket. These scripts consist of HTML with an embedded scripts written in a Tcl-like language called "[./th1.md|TH1]". Every new fossil repository is created with default scripts. Administrators wishing to customize their ticket entry, viewing, and editing screens should modify the default scripts to suit their needs. These screen generator scripts are part of the local state of a repository and are not shared with other repositories during a sync, push, or pull. <i>To be continued...</i> |
Changes to www/build.wiki.
| ︙ | ︙ | |||
53 54 55 56 57 58 59 | </ol> <h2>Aside: Is it really safe to use an unreleased development version of the Fossil source code?</h2> Yes! Any check-in on the [/timeline?t=trunk | trunk branch] of the Fossil | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | </ol> <h2>Aside: Is it really safe to use an unreleased development version of the Fossil source code?</h2> Yes! Any check-in on the [/timeline?t=trunk | trunk branch] of the Fossil [https://fossil-scm.org/ | self-hosting repository] will work fine. (Dodgy code is always on a branch.) In the unlikely event that you pick a version with a serious bug, it still won't clobber your files. Fossil uses several [./selfcheck.wiki | self-checks] prior to committing any repository change that prevent loss-of-work due to bugs. The Fossil [./selfhost.wiki | self-hosting repositories], especially |
| ︙ | ︙ | |||
473 474 475 476 477 478 479 | wrapper around the Emscripten C compiler (<tt>emcc</tt>). The wrapper script uses the configure-time state to attempt to set up the various environment variables which are required by <tt>emcc</tt> and will fail if it cannot do so. Once it's set up the environment, it passes on all of its arguments to <tt>emcc</tt>. The WASM-related build parts are set up such that none of them should | | | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 | wrapper around the Emscripten C compiler (<tt>emcc</tt>). The wrapper script uses the configure-time state to attempt to set up the various environment variables which are required by <tt>emcc</tt> and will fail if it cannot do so. Once it's set up the environment, it passes on all of its arguments to <tt>emcc</tt>. The WASM-related build parts are set up such that none of them should ever trigger implicitly (e.g. via dependencies resolution) in a normal build cycle. They are instead explicitly built as described below. From the top of the source tree, all WASM-related components can be built with: <pre><code>$ make wasm</code></pre> |
| ︙ | ︙ |
Changes to www/caps/index.md.
| ︙ | ︙ | |||
292 293 294 295 296 297 298 | defeated because your user has [**Setup**][s] capability on both sides of the sync. Be aware that those file checks do still matter, however: Fossil requires write access to a repo DB while cloning from it, so you can’t clone from a read-only repo DB file over a local file path. Even more surprising to you may be the fact that user caps do not affect cloning and syncing over SSH! (Not unless you go [out of your way][sshfc] | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | defeated because your user has [**Setup**][s] capability on both sides of the sync. Be aware that those file checks do still matter, however: Fossil requires write access to a repo DB while cloning from it, so you can’t clone from a read-only repo DB file over a local file path. Even more surprising to you may be the fact that user caps do not affect cloning and syncing over SSH! (Not unless you go [out of your way][sshfc] to patch around it, at any rate.) When you make a change to such a repository, the stock Fossil behavior is that the change first goes to the local repo clone where file system permissions are all that matter, but then upon sync, the situation is effectively the same as when the parent repo is on the local file system. The reason behind this is that if you can log into the remote system over SSH and that user has the necessary file system permissions on that remote repo DB file to allow clone and sync operations, then |
| ︙ | ︙ |
Changes to www/caps/ref.html.
| ︙ | ︙ | |||
250 251 252 253 254 255 256 |
<th>TktFmt</th>
<td>
Create new ticket report formats. Note that although this allows
the user to provide SQL code to be run in the server’s context,
and this capability is given to the untrusted “anonymous” user
category by default, this is a safe capability to give to users
because it is internally restricted to read-only queries on the
| | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
<th>TktFmt</th>
<td>
Create new ticket report formats. Note that although this allows
the user to provide SQL code to be run in the server’s context,
and this capability is given to the untrusted “anonymous” user
category by default, this is a safe capability to give to users
because it is internally restricted to read-only queries on the
tickets table only. (This restriction is done with an SQLite
authorization hook, not by any method so weak as SQL text
filtering.) Mnemonic: new <b>t</b>icket report.
</td>
</tr>
<tr id="u">
<th>u</th>
|
| ︙ | ︙ |
Changes to www/cgi.wiki.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | Setting up a server using Fossil is easy. A [./server/|separate document] talks about all of the many different methods for setting up a Fossil server, one of which is [./server/any/cgi.md | as a CGI script]. CGI is the technique that the three [./selfhost.wiki|self-hosting Fossil repositories] all use. Setting up a Fossil server using CGI is mostly about writing a short | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | Setting up a server using Fossil is easy. A [./server/|separate document] talks about all of the many different methods for setting up a Fossil server, one of which is [./server/any/cgi.md | as a CGI script]. CGI is the technique that the three [./selfhost.wiki|self-hosting Fossil repositories] all use. Setting up a Fossil server using CGI is mostly about writing a short script (usually just 2 lines) in the cgi-bin folder of an ordinary web-server. But there are a lot of extra options that can be added to this script, to customize the configuration. This article describes those options. <h1>CGI Script Options</h1> The CGI script used to launch a Fossil server will usually look something |
| ︙ | ︙ | |||
189 190 191 192 193 194 195 | Specifies the delivery mode for JavaScript files. See "[/help?cmd=http | http --jsmode]" for the allowed values and their meanings. <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> This parameter causes the contents of the given file to override the | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 | Specifies the delivery mode for JavaScript files. See "[/help?cmd=http | http --jsmode]" for the allowed values and their meanings. <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> This parameter causes the contents of the given file to override the site's <tt>mainmenu</tt> configuration setting, in much the same way that the <tt>skin</tt> setting overrides the skin. This can be used to apply a common main menu to a number of sites, and centrally maintain it, without having to copy its contents into each site. Note, however, that the contents of this setting are not stored in the repository and will not be cloned along with the repository. |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | > > > > > > > > > > > > > > | > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<title>Change Log</title>
<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
* Added the [/help?cmd=/ckout|/ckout web page] to provide information
about pending changes in a working check-out
* The [/help?cmd=ui|fossil ui] command defaults to using the /ckout
page as its start page.
* Added the [/help?cmd=merge-info|fossil merge-info] command and especially
the --tk option to that command, to provide analysis of the most recent
merge or update operation.
* Issue a warning if a user tries to commit on a check-in where the
branch has been changed.
* When a merge conflict occurs, a new section is added to the conflict
text that shows Fossil's suggested resolution to the conflict.
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
that have non-ASCII filenames
* Add the [/help?cmd=tree|fossil tree] command.
* On case-insensitive filesystems, store files using the filesystem's
preferred case rather than the case typed in by the user.
* Change the name "fossil cherry-pick" command to "fossil cherrypick",
which is more familiar to Git users. Retain the legacy name for
compatibility.
* Add new query parameters to the [/help?cmd=/timeline|/timeline page]:
d2=, p2=, and dp2=.
* Add options to the [/help?cmd=tag|fossil tag] command that will list tag values.
* Add the -b|--brief option to the [/help?cmd=status|fossil status] command.
* Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page].
* Add history search to the [/help?cmd=/chat|/chat page].
* Add Unix socket support to the [/help?cmd=server|server command].
* On Windows, use the root certificates managed by the operating system
(requires OpenSSL 3.2.0 or greater).
* Take into account zero-width and double-width unicode characters when
formatting the command-line timeline.
* Update the built-in SQLite to version 3.47.0. Precompiled binaries are
linked against OpenSSL 3.4.0.
* Numerous minor fixes and additions.
<h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2>
* Apache change work-around → As part of a security fix, the Apache webserver
mod_cgi module has stopped relaying the Content-Length field of the HTTP
reply header from the CGI programs back to the client in cases where the
|
| ︙ | ︙ | |||
83 84 85 86 87 88 89 |
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
* Add ability to "close" forum threads, such that unprivileged users
may no longer respond to them. Only administrators can close
threads or respond to them by default, and the
[/help?cmd=forum-close-policy|forum-close-policy setting] can be
used to add that capability to moderators.
| | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
* Add ability to "close" forum threads, such that unprivileged users
may no longer respond to them. Only administrators can close
threads or respond to them by default, and the
[/help?cmd=forum-close-policy|forum-close-policy setting] can be
used to add that capability to moderators.
* Add the [/help?cmd=all|fossil all whatis] command.
* The [/help?cmd=status|fossil status] command and relevant UI pages now
correctly report files which were both renamed <b>and</b> edited as such.
* Show default value of settings that have a default in
[/help?cmd=help|fossil help SETTING] output.
* On timeline graphs, show closed check-ins using an X in the middle of the
node circle or box.
* New options for email notification: Get email only for the first
|
| ︙ | ︙ | |||
125 126 127 128 129 130 131 |
<li> Add a PATH= argument to the [/help?cmd=ui|fossil ui remote:/] and
[/help?cmd=patch|fossil patch push/pull remote:...] commands so that
they work when the "remote" machine is a Mac and the "fossil"
executable is in the $HOME/bin directory.
</ul>
* Update built-in libraries SQLite, ZLib, Pikchr to their latest versions.
* Documentation enhancements and typo fixes.
| < | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
<li> Add a PATH= argument to the [/help?cmd=ui|fossil ui remote:/] and
[/help?cmd=patch|fossil patch push/pull remote:...] commands so that
they work when the "remote" machine is a Mac and the "fossil"
executable is in the $HOME/bin directory.
</ul>
* Update built-in libraries SQLite, ZLib, Pikchr to their latest versions.
* Documentation enhancements and typo fixes.
<h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2>
* Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a">
<li> Add the ft=TAG query parameter which in combination with d=Y
shows all descendants of Y up to TAG
<li> Enhance the s=PATTERN (search) query parameter so that forum post
|
| ︙ | ︙ | |||
153 154 155 156 157 158 159 |
* The stock OCI container no longer includes BusyBox, thus no longer
needs to start as root to chroot that power away. That in turn
frees us from needing to build and install the container as root,
since it no longer has to create a private <tt>/dev</tt> tree
inside the jail for Fossil's use.
* Add support for the trigram tokenizer for FTS5 search to enable
searching in Chinese.
| | | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
* The stock OCI container no longer includes BusyBox, thus no longer
needs to start as root to chroot that power away. That in turn
frees us from needing to build and install the container as root,
since it no longer has to create a private <tt>/dev</tt> tree
inside the jail for Fossil's use.
* Add support for the trigram tokenizer for FTS5 search to enable
searching in Chinese.
* Comment lines (starting with a '#') are now supported inside
[./settings.wiki#versionable|versioned settings].
* Default permissions for anonymous users in new repositories are
changed to "hz".
* The [/help?cmd=status|fossil status] command now detects when a
file used to be a symlink and has been replaced by a regular file.
(It previously checked for the inverse case only.)
* The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same
|
| ︙ | ︙ |
Changes to www/chat.md.
| ︙ | ︙ | |||
153 154 155 156 157 158 159 | ~~~~ Substitute the appropriate project URL, robot account name and password, message text and file attachment, of course. ### <a id="chat-robot"></a> Chat Messages For Timeline Events | | | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | ~~~~ Substitute the appropriate project URL, robot account name and password, message text and file attachment, of course. ### <a id="chat-robot"></a> Chat Messages For Timeline Events If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not an empty string, then any change to the repository that would normally result in a new timeline entry is announced in the chatroom. The announcement appears to come from a user whose name is given by the chat-timeline-user setting. This mechanism is similar to [email notification](./alerts.md) except that the notification is sent via chat instead of via email. |
| ︙ | ︙ | |||
191 192 193 194 195 196 197 |
Sends a new chat message to the server.
* [/chat-delete](/help?name=/chat-delete) →
Deletes a chat message.
Fossil chat uses the venerable "hanging GET" or
"[long polling](wikipedia:/wiki/Push_technology#Long_polling)"
| | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
Sends a new chat message to the server.
* [/chat-delete](/help?name=/chat-delete) →
Deletes a chat message.
Fossil chat uses the venerable "hanging GET" or
"[long polling](wikipedia:/wiki/Push_technology#Long_polling)"
technique to receive asynchronous notification of new messages.
This is done because long polling works well with CGI and SCGI,
which are the usual mechanisms for setting up a Fossil server.
More advanced notification techniques such as
[Server-sent events](wikipedia:/wiki/Server-sent_events) and especially
[WebSockets](wikipedia:/wiki/WebSocket) might seem more appropriate for
a chat system, but those technologies are not compatible with CGI.
|
| ︙ | ︙ |
Changes to www/checkin_names.wiki.
| ︙ | ︙ | |||
244 245 246 247 248 249 250 | The prefixes "<tt>root:</tt>", "<tt>start:</tt>", and "<tt>merge-in:</tt>" can be chained: one can say for example <pre> fossil info merge-in:xyzzy:2022-03-01 </pre> | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | The prefixes "<tt>root:</tt>", "<tt>start:</tt>", and "<tt>merge-in:</tt>" can be chained: one can say for example <pre> fossil info merge-in:xyzzy:2022-03-01 </pre> to get information about the most recent merge-in point on the branch "xyzzy" that happened on or before March 1, 2022. <h2 id="special">Special Tags</h2> The tag "tip" means the most recent check-in. The "tip" tag is practically equivalent to the timestamp "9999-12-31". |
| ︙ | ︙ |
Changes to www/concepts.wiki.
| ︙ | ︙ | |||
161 162 163 164 165 166 167 | manifest for that check-in. The manifest file is not normally a real file on disk. Instead, the manifest is computed in memory by Fossil whenever it needs it. However, the "fossil setting manifest on" command will cause the manifest file to be materialized to disk, if desired. Both Fossil itself, and SQLite cause the manifest file to be materialized to disk | | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | manifest for that check-in. The manifest file is not normally a real file on disk. Instead, the manifest is computed in memory by Fossil whenever it needs it. However, the "fossil setting manifest on" command will cause the manifest file to be materialized to disk, if desired. Both Fossil itself, and SQLite cause the manifest file to be materialized to disk so that the makefiles for these projects can read the manifest and embed version information in generated binaries. Fossil automatically generates a manifest whenever you "commit" a new check-in. So this is not something that you, the developer, need to worry with. The format of a manifest is intentionally designed to be simple to parse, so that if you want to read and interpret a manifest, either by hand or with a script, that is easy to do. But you will probably never need to do so. In addition to identifying all files in the check-in, a manifest also contains a check-in comment, the date and time when the check-in was established, who created the check-in, and links to other check-ins from which the current check-in is derived. There are also a couple of checksums used to verify the integrity of the check-in. And the whole manifest might be PGP clearsigned. <h3 id="keyconc">2.3 Key concepts</h3> <ul> <li>A <b>check-in</b> is a set of files arranged |
| ︙ | ︙ | |||
215 216 217 218 219 220 221 | install any other software in order to use Fossil. You do <u>not</u> need CVS, gzip, diff, rsync, Python, Perl, Tcl, Java, Apache, PostgreSQL, MySQL, SQLite, patch, or any similar software on your system in order to use Fossil effectively. You will want to have some kind of text editor for entering check-in comments. Fossil will use whatever text editor is identified by your VISUAL environment variable. Fossil will also use GPG to clearsign your manifests if you happen to have it installed, | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | install any other software in order to use Fossil. You do <u>not</u> need CVS, gzip, diff, rsync, Python, Perl, Tcl, Java, Apache, PostgreSQL, MySQL, SQLite, patch, or any similar software on your system in order to use Fossil effectively. You will want to have some kind of text editor for entering check-in comments. Fossil will use whatever text editor is identified by your VISUAL environment variable. Fossil will also use GPG to clearsign your manifests if you happen to have it installed, but Fossil will skip that step if GPG is missing from your system. You can optionally set up Fossil to use external "diff" programs, though Fossil has an excellent built-in "diff" algorithm that works fine for most people. If you happen to have Tcl/Tk installed on your system, Fossil will use it to generate a graphical "diff" display when you use the --tk option to the "diff" command, but this too is entirely optional. |
| ︙ | ︙ | |||
284 285 286 287 288 289 290 | <pre> fossil setting autosync on fossil setting autosync off fossil settings </pre> By default, Fossil runs with autosync mode turned on. The | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | <pre> fossil setting autosync on fossil setting autosync off fossil settings </pre> By default, Fossil runs with autosync mode turned on. The authors find that projects run more smoothly in autosync mode since autosync helps to prevent pointless forking and merging and helps keeps all collaborators working on exactly the same code rather than on their own personal forks of the code. In the author's view, manual-merge mode should be reserved for disconnected operation. <h3>4.1 Autosync Workflow</h3> |
| ︙ | ︙ |
Changes to www/contribute.wiki.
1 2 | <title>Contributing To Fossil</title> | | | 1 2 3 4 5 6 7 8 9 10 | <title>Contributing To Fossil</title> Fossil users are encouraged to contribute enhancements back to the project. This note outlines some of the procedures for making useful contributions. <h2>1.0 Contributor Agreement</h2> In order to accept non-trivial contributions, we <u>must</u> have a [./copyright-release.pdf | Contributor Agreement (PDF)] |
| ︙ | ︙ |
Changes to www/custom_ticket.wiki.
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | <h2>Modify the 'view ticket' page</h2> Look for the text "Contact:" (about halfway through). Then insert these lines after the closing tr tag and before the "enable_output" line: <verbatim> | > | | | | | | > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
<h2>Modify the 'view ticket' page</h2>
Look for the text "Contact:" (about halfway through). Then insert these lines
after the closing tr tag and before the "enable_output" line:
<verbatim>
<tr>
<td align="right">Assigned to:</td><td bgcolor="#d0d0d0">
$<assigned_to>
</td>
<td align="right">Opened by:</td><td bgcolor="#d0d0d0">
$<opened_by>
</td>
</tr>
</verbatim>
This will add a row which displays these two fields, in the event the user has
<a href="./caps/ref.html#w">ticket "edit" capability</a>.
<h2>Modify the 'edit ticket' page</h2>
|
| ︙ | ︙ | |||
107 108 109 110 111 112 113 | </td> </tr> </verbatim> That will give you a drop-down list of assignees. The first argument to the TH1 command 'combobox' is the database field which the combobox is associated to. The next argument is the list of choices you want to show in the combobox (and | | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | </td> </tr> </verbatim> That will give you a drop-down list of assignees. The first argument to the TH1 command 'combobox' is the database field which the combobox is associated to. The next argument is the list of choices you want to show in the combobox (and that you specified in the second step above.) The last argument should be 1 for a true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for details). Now, similar to the previous section, look for "Contact:" and add this: <verbatim> <tr> |
| ︙ | ︙ |
Changes to www/embeddeddoc.wiki.
| ︙ | ︙ | |||
176 177 178 179 180 181 182 | </verbatim> As with "$ROOT", this substitution only works for Markdown and HTML documents. For Wiki documents, you would need to use a relative URL. <h2 id="th1">2.3 TH1 Documents</h2> | > > > > > > > > > | > | < < | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
</verbatim>
As with "$ROOT", this substitution only works for Markdown and HTML
documents. For Wiki documents, you would need to use a relative URL.
<h2 id="th1">2.3 TH1 Documents</h2>
Enabling TH1 document support requires the following:
* Configure the build with the <code>--with-th1-docs</code> flag.
* Enable the <code>th1-docs</code> setting, which is only available
after building with <code>--with-th1-docs</code>.
* Affected files must have a <code>.th1</code> file extension.
* The code to run must be embedded in blocks of
<code><th1>...</th1></code>.
Fossil will substitute the value of [./th1.md | TH1 expressions]
within the <code><th1>...</th1></code> blocks into
the output HTML.
Since TH1 is a full scripting language, this feature essential grants
the ability to execute code on the server to anyone with check-in
privilege for the project.
This is a security risk that needs to be carefully managed.
The feature is off by default.
Administrators should understand and carefully assess the risks
before enabling the use of TH1 within embedded documentation.
|
| ︙ | ︙ |
Changes to www/env-opts.md.
| ︙ | ︙ | |||
466 467 468 469 470 471 472 | Occasionally, fossil wants to launch a web browser for the user, most obviously as part of the `fossil ui` command. In that specific case, the browser is launched pointing at the web server started by `fossil ui` listening on a private TCP port. On all platforms, if the local or global settings `web-browser` is | | | | | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | Occasionally, fossil wants to launch a web browser for the user, most obviously as part of the `fossil ui` command. In that specific case, the browser is launched pointing at the web server started by `fossil ui` listening on a private TCP port. On all platforms, if the local or global settings `web-browser` is set, that is the command used to open a URL. Otherwise, the specific actions vary by platform. On Unix-like platforms other than Apple's, it looks for the first program from the list `xdg-open`, `gnome-open`, `firefox`, and `google-chrome` that it can find on the `PATH`. On Apple platforms, it assumes that `open` is the command to open a URL in the user's configured default browser. On Windows platforms, it assumes that `start` is the command to open a URL in the user's configured default browser. [configdb]: ./tech_overview.wiki#configdb [configloc]: ./tech_overview.wiki#configloc |
Changes to www/event.wiki.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | Users must have check-in privileges (permission "i") in order to create or edit technotes. In addition, users must have create-wiki privilege (permission "f") to create new technotes and edit-wiki privilege (permission "k") in order to edit existing technotes. Technote content may be formatted as [/wiki_rules | Fossil wiki], | | | 113 114 115 116 117 118 119 120 | Users must have check-in privileges (permission "i") in order to create or edit technotes. In addition, users must have create-wiki privilege (permission "f") to create new technotes and edit-wiki privilege (permission "k") in order to edit existing technotes. Technote content may be formatted as [/wiki_rules | Fossil wiki], [/md_rules | Markdown], or plain text. |
Changes to www/fileedit-page.md.
1 2 3 4 | # The fileedit Page This document describes the limitations of, caveats for, and disclaimers for the [](/fileedit) page, which provides users with | > | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 | # The fileedit Page This document describes the limitations of, caveats for, and disclaimers for the [](/fileedit) page, which provides users with basic editing features for files via the web interface when they have [checkin privileges](./caps/index.md). # Important Caveats and Disclaimers Predictably, the ability to edit files in a repository from a web browser halfway around the world comes with several obligatory caveats and disclaimers... |
| ︙ | ︙ | |||
217 218 219 220 221 222 223 | preview. It won't work with `htmlIframe`, as that represents an iframe element which contains a complete HTML document. - `element`: the DOM element in which the preview is rendered. - `mimetype`: the mimetype of the being-previewed content, as determined by Fossil (by its file extension). The event listener callback shown above doesn't use the `mimetype`, | | | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | preview. It won't work with `htmlIframe`, as that represents an iframe element which contains a complete HTML document. - `element`: the DOM element in which the preview is rendered. - `mimetype`: the mimetype of the being-previewed content, as determined by Fossil (by its file extension). The event listener callback shown above doesn't use the `mimetype`, but makes use of the other two. It fishes all `code` blocks out of the preview which explicitly have a CSS class named `language-`something, and then asks highlightjs to highlight them. ## <a id="editor"></a> Integrating a Custom Editor Widget (These instructions also work for the `/wikiedit` page by replacing "fileedit" with "wikiedit" in any strings or symbol names!) |
| ︙ | ︙ |
Changes to www/fileformat.wiki.
| ︙ | ︙ | |||
358 359 360 361 362 363 364 | The <b>W</b> card is used to specify the text of the wiki page. The argument to the <b>W</b> card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the <b>W</b> card. The wiki text is always followed by one extra newline. The <b>C</b> card on a wiki page is optional. The argument is a comment | | | 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | The <b>W</b> card is used to specify the text of the wiki page. The argument to the <b>W</b> card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the <b>W</b> card. The wiki text is always followed by one extra newline. The <b>C</b> card on a wiki page is optional. The argument is a comment that explains why the changes were made. The ability to have a <b>C</b> card on a wiki page artifact was added on 2019-12-02 at the suggestion of user George Krivov and is not currently used or generated by the implementation. Older versions of Fossil will reject a wiki-page artifact that includes a <b>C</b> card. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. |
| ︙ | ︙ | |||
394 395 396 397 398 399 400 | The ticket-id is given in the <b>K</b> card. A ticket exists if it contains one or more changes. The first "change" to a ticket is what brings the ticket into existence. <b>J</b> cards specify changes to the "value" of "fields" in the ticket. If the <i>value</i> parameter of the <b>J</b> card is omitted, then the field is set to an empty string. | | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | The ticket-id is given in the <b>K</b> card. A ticket exists if it contains one or more changes. The first "change" to a ticket is what brings the ticket into existence. <b>J</b> cards specify changes to the "value" of "fields" in the ticket. If the <i>value</i> parameter of the <b>J</b> card is omitted, then the field is set to an empty string. Each fossil server has a ticket configuration which specifies the fields it understands. The ticket configuration is part of the local state for the repository and thus can vary from one repository to another. Hence a <b>J</b> card might specify a <i>field</i> that does not exist in the local ticket configuration. If a <b>J</b> card specifies a <i>field</i> that is not in the local configuration, then that <b>J</b> card is simply ignored. The first argument of the <b>J</b> card is the field name. The second value is the field value. If the field name begins with "+" then the value is appended to the prior value. Otherwise, the value |
| ︙ | ︙ | |||
460 461 462 463 464 465 466 | <h3 id="event">2.7 Technical Notes</h3> A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | <h3 id="event">2.7 Technical Notes</h3> A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on a technote artifact: <div class="indent"> <b>C</b> <i>comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>E</b> <i>technote-time</i> <i>technote-id</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> |
| ︙ | ︙ | |||
508 509 510 511 512 513 514 | second argument is the single character "<b>*</b>" instead of an artifact ID and the name is always prefaced by "<b>+</b>". The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the | | | | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | second argument is the single character "<b>*</b>" instead of an artifact ID and the name is always prefaced by "<b>+</b>". The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the name means that tags can only be "add" and they can only be non-propagating tags. In a technote, <b>T</b> cards are normally used to set the background display color for timelines. The optional <b>U</b> card gives the name of the user who entered the technote. A single <b>W</b> card provides wiki text for the document associated with the technote. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. |
| ︙ | ︙ |
Changes to www/forum.wiki.
| ︙ | ︙ | |||
403 404 405 406 407 408 409 | closed by the closure of its parent, will <em>not</em> offer a re-open option. Though forum users are permitted to delete their own posts, they are not permitted, without appropriate permissions, to close their own posts. This is intentional, as closing one's own post can be used to antagonize other forum users. For example, by posting something | | | 403 404 405 406 407 408 409 410 411 | closed by the closure of its parent, will <em>not</em> offer a re-open option. Though forum users are permitted to delete their own posts, they are not permitted, without appropriate permissions, to close their own posts. This is intentional, as closing one's own post can be used to antagonize other forum users. For example, by posting something trollish or highly controversial in nature and closing the post to further responses. |
Changes to www/fossil-is-not-relational.md.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | any given artifact into the data store, the timestamp of each such change, the inheritance tree of checkins, and many other pieces of metadata. - Raw file content of versioned files. These data are external to artifacts, which refer to them by their hashes. How they are stored is not the concern of the data model, but (spoiler alert!) Fossil | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | any given artifact into the data store, the timestamp of each such change, the inheritance tree of checkins, and many other pieces of metadata. - Raw file content of versioned files. These data are external to artifacts, which refer to them by their hashes. How they are stored is not the concern of the data model, but (spoiler alert!) Fossil stores them in an SQLite database, one record per distinct hash, in its `blob` table (which we will cover more very soon). Non-SCM-relevant state includes: - Fossil's list of users and their metadata (permissions, email address, etc.). Artifacts themselves reference users only by their user names. Artifacts neither care whether, nor guarantee that, user |
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
</tr>
<tr>
<td>Designed for Linux kernel development</td>
<td>Designed for SQLite development</td>
<td><a href="#scale">2.5.2 ↓</a></td>
</tr>
<tr>
| < < < < < | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
</tr>
<tr>
<td>Designed for Linux kernel development</td>
<td>Designed for SQLite development</td>
<td><a href="#scale">2.5.2 ↓</a></td>
</tr>
<tr>
<td>Focus on individual branches</td>
<td>Focus on the entire tree of changes</td>
<td><a href="#branches">2.5.3 ↓</a></td>
</tr>
<tr>
<td>One check-out per repository</td>
<td>Many check-outs per repository</td>
<td><a href="#checkouts">2.6 ↓</a></td>
</tr>
<tr>
|
| ︙ | ︙ | |||
589 590 591 592 593 594 595 596 597 598 599 600 601 602 | by every contributor to Linux: such extreme visibility does not scale well. Contrast Fossil, which was written for the cathedral-style SQLite project and its handful of active committers. Seeing all changes on all branches all at once helps keep the whole team up-to-date with what everybody else is doing, resulting in a more tightly focused and cohesive implementation. <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. | > > > | | 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | by every contributor to Linux: such extreme visibility does not scale well. Contrast Fossil, which was written for the cathedral-style SQLite project and its handful of active committers. Seeing all changes on all branches all at once helps keep the whole team up-to-date with what everybody else is doing, resulting in a more tightly focused and cohesive implementation. Parts of this section are [https://fossil-scm.org/forum/forumpost/5961e969fa|disputed] by [https://github.com/olorin37|Jakub A. G.]. <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. Fossil doesn't work that way. A Fossil repository is an SQLite database file which is normally stored outside the working checkout directory. You can [/help?cmd=open | open] a Fossil repository any number of times into any number of working directories. A common usage pattern is to have one working directory per active working branch, so that switching branches is done with a <tt>cd</tt> command rather than by checking out the branches successively in a single working directory. |
| ︙ | ︙ | |||
658 659 660 661 662 663 664 665 666 667 668 669 670 671 | Disk space is cheap. Having several working directories — each with its own local state — makes switching versions cheap and fast. Plus, <tt>cd</tt> is faster to type than <tt>git checkout</tt> or <tt>fossil update</tt>. <h3 id="history">2.7 What you should have done vs. What you actually did</h3> Git puts a lot of emphasis on maintaining a "clean" check-in history. Extraneous and experimental branches by individual developers often never make it into the main repository. Branches may be rebased before being pushed to make | > > | 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 | Disk space is cheap. Having several working directories — each with its own local state — makes switching versions cheap and fast. Plus, <tt>cd</tt> is faster to type than <tt>git checkout</tt> or <tt>fossil update</tt>. Parts of this section are [https://fossil-scm.org/forum/forumpost/5961e969fa|disputed] by [https://github.com/olorin37|Jakub A. G.]. <h3 id="history">2.7 What you should have done vs. What you actually did</h3> Git puts a lot of emphasis on maintaining a "clean" check-in history. Extraneous and experimental branches by individual developers often never make it into the main repository. Branches may be rebased before being pushed to make |
| ︙ | ︙ | |||
859 860 861 862 863 864 865 | [https://en.wikipedia.org/wiki/SHA-2 | older SHA-2 algorithm]. As of this writing in February 2020, that plan hasn't been implemented, as far as this author is aware, but there is now [https://lwn.net/ml/git/20200113124729.3684846-1-sandals@crustytoothpaste.net/ | a competing SHA-256 based plan] which requires complete repository conversion from SHA-1 to SHA-256, breaking all public hashes in the repo. One way to characterize such a massive upheaval in Git terms is a | | < | | 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 | [https://en.wikipedia.org/wiki/SHA-2 | older SHA-2 algorithm]. As of this writing in February 2020, that plan hasn't been implemented, as far as this author is aware, but there is now [https://lwn.net/ml/git/20200113124729.3684846-1-sandals@crustytoothpaste.net/ | a competing SHA-256 based plan] which requires complete repository conversion from SHA-1 to SHA-256, breaking all public hashes in the repo. One way to characterize such a massive upheaval in Git terms is a whole-project rebase, which violates the [https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing|Golden Rule of Rebasing]. Regardless of the eventual implementation details, we fully expect Git to move off SHA-1 eventually and for the changes to take years more to percolate through the community. Almost three years after Fossil solved this problem, the [https://sha-mbles.github.io/ | SHAmbles attack] was published, further |
| ︙ | ︙ |
Changes to www/fossil_prompt.wiki.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 | . fossil_prompt.sh </pre> For a permanent installation, you can graft the code into your <tt>.bashrc</tt> file in your home directory. The code is very simple (only 32 non-comment lines, as of this writing) | | | 15 16 17 18 19 20 21 22 | . fossil_prompt.sh </pre> For a permanent installation, you can graft the code into your <tt>.bashrc</tt> file in your home directory. The code is very simple (only 32 non-comment lines, as of this writing) and hence easy to customize. |
Changes to www/gitusers.md.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 | underneath your working directory. This difference shows up in several separate places when it comes to moving from Git to Fossil. #### <a id="cwork" name="scw"></a> Checkout Workflows | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | underneath your working directory. This difference shows up in several separate places when it comes to moving from Git to Fossil. #### <a id="cwork" name="scw"></a> Checkout Workflows A Fossil repository is an SQLite database storing the entire history of a project. It is not normally stored inside the working tree. A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory that contains a snapshot of your project that you are currently working on, extracted for you from the repository database file by the `fossil` program. There are ways to |
| ︙ | ︙ | |||
146 147 148 149 150 151 152 | destructive!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | destructive!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the `.fslckout` file at the root of a check-out directory. This is an SQLite database that keeps track of local state such as what version you have checked out, the contents of the [stash] for that working directory, the [undo] buffers, per-checkout [settings][set], and so forth. The stash and undo buffers are considered precious uncommitted changes, so you have to force Fossil to discard these as part of closing the check-out. |
| ︙ | ︙ | |||
771 772 773 774 775 776 777 | Fossil because they don’t change any previous commits, allowing the other clones to continue working safely with their existing commit hashes.) In this scheme, Alice then needs to say “`fossil update trunk`” in order to return her check-out’s parent commit to the previous version lest her next attempted commit land atop this mistake branch. The fact that Bob | | | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | Fossil because they don’t change any previous commits, allowing the other clones to continue working safely with their existing commit hashes.) In this scheme, Alice then needs to say “`fossil update trunk`” in order to return her check-out’s parent commit to the previous version lest her next attempted commit land atop this mistake branch. The fact that Bob marked the branch as closed will prevent that from going through, cluing Alice into what she needs to do to remedy the situation, but that merely shows why it’s a better workflow if Alice makes the amendment herself: ``` fossil amend --branch MISTAKE --hide --close \ -m "shunt Bob’s erroneous commit off" tip fossil up trunk |
| ︙ | ︙ |
Changes to www/glossary.md.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
A project made of an operating system
installation’s configuration file set is not a good use of Fossil,
because you’ll have all of your OS’s *other* files intermixed.
Worse, Fossil doesn’t track OS permissions, so even if you were to
try to use Fossil as a system deployment tool by archiving versions
of the OS configuration files and then unpacking them on a new
system, the extracted project files would have read/write access by
| | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
A project made of an operating system
installation’s configuration file set is not a good use of Fossil,
because you’ll have all of your OS’s *other* files intermixed.
Worse, Fossil doesn’t track OS permissions, so even if you were to
try to use Fossil as a system deployment tool by archiving versions
of the OS configuration files and then unpacking them on a new
system, the extracted project files would have read/write access by
the user who did the extraction, which probably isn’t what you were
wanting.
Even with these problems aside, do you really want a `.fslckout`
SQLite database at the root of your filesystem? Are you prepared for
the consequences of saying `fossil clean --verily` on such a system?
You can constrain that with [the `ignore-glob` setting][IGS], but
are you prepared to write and maintain all the rules needed to keep
|
| ︙ | ︙ | |||
346 347 348 349 350 351 352 |
(Contrast the Cygwin and WSL Fossil binaries, which use POSIX file
naming rules.)
* In the same way that one cannot extract files from a zip archive
without having a copy of that zip file, one cannot make check-outs
without access to the repository file or a clone thereof.
| | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
(Contrast the Cygwin and WSL Fossil binaries, which use POSIX file
naming rules.)
* In the same way that one cannot extract files from a zip archive
without having a copy of that zip file, one cannot make check-outs
without access to the repository file or a clone thereof.
* Because a Fossil repository is an SQLite database file, the same
rules for avoiding data corruption apply to it. In particular, it is
[nearly a hard requirement][h2cflp] that the repository clone be on
the same machine as the one where you make check-outs and the
subsequent check-ins.
That said, the relative locations of the repo and the check-out
within the local file system are arbitrary. The repository may be
|
| ︙ | ︙ |
Changes to www/grep.md.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
`-R` in Fossil has a different meaning, by convention. Until then, you
can get the same effect on systems with a POSIX shell like so:
$ fossil grep COMMAND: $(fossil ls src)
If you run that in a check-out of the [Fossil self-hosting source
repository][fshsr], that returns the first line of the built-in
| | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
`-R` in Fossil has a different meaning, by convention. Until then, you
can get the same effect on systems with a POSIX shell like so:
$ fossil grep COMMAND: $(fossil ls src)
If you run that in a check-out of the [Fossil self-hosting source
repository][fshsr], that returns the first line of the built-in
documentation for each Fossil command, across all historical versions.
Fossil `grep` has extensions relative to these other `grep` standards,
such as `--verbose` to print each checkin ID considered, regardless of
whether it matches. This one is noteworthy here because the behavior
used to be under `-v` before we reassigned it to give POSIX-like `grep
-v` behavior.
|
| ︙ | ︙ |
Changes to www/hashpolicy.wiki.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 | all artifacts in all Fossil repositories were named by only a SHA1 hash. Version 2.0 extended the [./fileformat.wiki|Fossil file format] to allow artifacts to be named by either SHA1 or SHA3-256 hashes. (SHA3-256 is the only variant of SHA3 that Fossil uses for artifact naming, so for the remainder of this article it will be called simply "SHA3". Similarly, "Hardened SHA1" will | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | all artifacts in all Fossil repositories were named by only a SHA1 hash. Version 2.0 extended the [./fileformat.wiki|Fossil file format] to allow artifacts to be named by either SHA1 or SHA3-256 hashes. (SHA3-256 is the only variant of SHA3 that Fossil uses for artifact naming, so for the remainder of this article it will be called simply "SHA3". Similarly, "Hardened SHA1" will be shortened to "SHA1" in the remaining text.) To be clear: Fossil (version 2.0 and later) allows the SHA1 and SHA3 hashes to be mixed within the same repository. Older check-ins, created years ago, continue to be named using their legacy SHA1 hashes while newer check-ins are named using modern SHA3 hashes. There is no need to "convert" a repository from SHA1 over to SHA3. |
| ︙ | ︙ |
Changes to www/hints.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 |
<title>Fossil Tips And Usage Hints</title>
A collection of useful hints and tricks in no particular order:
1. Click on two nodes of any timeline graph in succession
to see a diff between the two versions.
2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands
to get a pop-up
window containing a complete side-by-side diff. (NB: The pop-up
window is run as a separate Tcl/Tk process, so you will need to
have Tcl/Tk installed on your machine for this to work. Visit
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<title>Fossil Tips And Usage Hints</title>
A collection of useful hints and tricks in no particular order:
1. Click on two nodes of any timeline graph in succession
to see a diff between the two versions.
2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands
to get a pop-up
window containing a complete side-by-side diff. (NB: The pop-up
window is run as a separate Tcl/Tk process, so you will need to
have Tcl/Tk installed on your machine for this to work. Visit
[http://www.activestate.com/activetcl] for a quick download of
Tcl/Tk if you do not already have it on your system.)
3. The "[/help/clean | fossil clean -x]" command is a great
alternative to "make clean". You can use "[/help/clean | fossil clean -f]"
as a slightly safer alternative if the "ignore-glob" setting is
not set. WARNING: make sure you did a "fossil add" for all source-files
you plan to commit, otherwise those files will be deleted without warning.
|
| ︙ | ︙ |
Changes to www/history.md.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | is still the top committer to Fossil, but there are also [many other contributors][120]. [120]: /reports?type=ci&view=byuser ## History | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | is still the top committer to Fossil, but there are also [many other contributors][120]. [120]: /reports?type=ci&view=byuser ## History The SQLite project started out using [CVS][300], as CVS was the most commonly used version control system in that era (circa 2000). CVS was an amazing version control system for its day in that it allowed multiple developers to be editing the same file at the same time. [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System Though innovative and much loved in its time, CVS was not without problems. |
| ︙ | ︙ |
Changes to www/hooks.md.
1 2 3 4 5 6 7 8 9 | # Hooks Hooks are short scripts that Fossil runs at defined points of processing. Administrators can use hooks to help enforce policy or connect Fossil to a continuous integration (CI) system. ## Interim Documentation. * This is a work-in-progress. The interface is in flux. | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Hooks
Hooks are short scripts that Fossil runs at defined points of processing.
Administrators can use hooks to help enforce policy or connect Fossil to
a continuous integration (CI) system.
## Interim Documentation.
* This is a work-in-progress. The interface is in flux.
For the time being, the documentation is a list of
bullet points. We hope to transform this into a proper document
later, after things settle down.
* Contributions and suggestions to the hook system and/or the
documentation are welcomed.
## General Notes.
|
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
* All scripts are expected to run relatively quickly. If a long-running
process is started by a hook, it should be run in the background so
that the original script can return.
* The "%F" sequence inside the script is translated into the
name of the fossil executable.
| | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
* All scripts are expected to run relatively quickly. If a long-running
process is started by a hook, it should be run in the background so
that the original script can return.
* The "%F" sequence inside the script is translated into the
name of the fossil executable.
* The "%R" sequence in the script is translated into the name of
the repository.
* The "%A" sequence becomes the name of an auxiliary input file,
the meaning of which depends on the hook type. The auxiliary filename
might be an empty string. Take care to use appropriate quoting!
## Disabled Hooks
* Hooks with type "disabled" never run. They are a place-holder for
scripts that might be converted to some other hook-type later.
|
| ︙ | ︙ | |||
142 143 144 145 146 147 148 |
read that file if it wants access to the commit information.
## Commit-Msg Hooks
* Commit-msg hooks are not yet implemented.
* The commit-msg hooks run during "fossil commit" after the check-in
| | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
read that file if it wants access to the commit information.
## Commit-Msg Hooks
* Commit-msg hooks are not yet implemented.
* The commit-msg hooks run during "fossil commit" after the check-in
message has been entered by the user. The "%A" argument to the
commit-msg hook is the text of the commit message. The intent
of the commit-msg hook is to validate the text of the commit
message to (for example) check for typos or ensure that it
conforms to standards.
* If any commit-msg hook returns a non-zero exit code, then
the commit is abandoned. All
commit-msg hooks must exit(0) in order for the commit to
proceed.
* Commit-msg hooks are advisory only. Each developer is in total
control of the local repository and can easily bypass the hooks
to cause a non-conforming checkin to be committed.
|
Changes to www/index.wiki.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
| | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
<h3>Latest Release: 2.25 ([/timeline?c=version-2.25|2024-11-06])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_25|Change Summary]
* [/timeline?p=version-2.25&bt=version-2.24&y=ci|Check-ins in version 2.25]
* [/timeline?df=version-2.25&y=ci|Check-ins derived from the 2.25 release]
* [/timeline?t=release|Timeline of all past releases]
<hr>
<h3>Quick Start</h3>
1. [/uv/download.html|Download] or install using a package manager or
[./build.wiki|compile from sources].
|
| ︙ | ︙ |
Changes to www/inout.wiki.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 | chokes on the conversion when the in-flight repository size exceeds available memory. We do not know why it buffers the entire stream emitted by "<tt>git fast-export</tt>" before sending it along to Fossil, but we've seen the problem recur on multiple machines. While one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't seem to be affected by this problem — we instead recommend using | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | chokes on the conversion when the in-flight repository size exceeds available memory. We do not know why it buffers the entire stream emitted by "<tt>git fast-export</tt>" before sending it along to Fossil, but we've seen the problem recur on multiple machines. While one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't seem to be affected by this problem — we instead recommend using Microsoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows Subsystem for Linux] or either of the two popular "Git for Windows" distributions based on MSYS2. They handle pipes the POSIX way, avoiding any dependency on the amount of data involved. <h2>Fossil → Git</h2> To convert a Fossil repository into a Git repository, run commands like |
| ︙ | ︙ | |||
74 75 76 77 78 79 80 | those concepts. As with the "import" command, the --git option is not required since the git-fast-export file format is currently the only VCS interchange format that Fossil will generate. However, future versions of Fossil might add the ability to generate other VCS interchange formats, and so for compatibility, the use of the --git | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | those concepts. As with the "import" command, the --git option is not required since the git-fast-export file format is currently the only VCS interchange format that Fossil will generate. However, future versions of Fossil might add the ability to generate other VCS interchange formats, and so for compatibility, the use of the --git option is recommended. <h2>Mirror A Fossil Repository In Git</h2> Fossil version 2.9 and later supports a simple mechanism for doing a Git or [./mirrortogithub.md|GitHub mirror of a Fossil repository]. See that separate document for details. Fossil is self-hosting, |
| ︙ | ︙ |
Changes to www/interwiki.md.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
1. <b>Path Links</b> → the PageName begins with the "/" character
or is an empty string.
2. <b>Hash Links</b> → the PageName is a hexadecimal number with
at least four digits.
| | | | | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
1. <b>Path Links</b> → the PageName begins with the "/" character
or is an empty string.
2. <b>Hash Links</b> → the PageName is a hexadecimal number with
at least four digits.
3. <b>Wiki Links</b> → A PageName that is not a Path or Hash.
The Intermap defines a base URL for each Tag. Path links are appended
directly to the URL contained in the Intermap. The Intermap can define
additional text to put in between the base URL and the PageName for
Hash and Wiki links, respectively.
<a id="intermap"></a>
## Intermap
The intermap defines a mapping from interwiki Tags to full URLs. The
Intermap can be viewed and managed using the [fossil interwiki][iwiki]
command or the [/intermap][imap] webpage.
[iwiki]: /help?cmd=interwiki
[imap]: /intermap
The current intermap for a server is seen on the [/intermap][imap] page
(which is read-only for non-Setup users) and at the bottom of the built-in
[Fossil Wiki rules](/wiki_rules) and [Markdown rules](/md_rules)
documentation pages.
Each intermap entry stores, at a minimum, the base URL for the remote
wiki. The intermap entry might also store additional path text that
is used for Hash and Wiki links. If only the base URL is provided,
then the intermap will only allow Path style interwiki links. The
Hash and Wiki style interwiki links are only allowed if the necessary
extensions are provided in the intermap.
## Disadvantages and Limitations
* Configuration is required. The intermap must be set up correctly
before interwiki links will work. This contrasts with ordinary
links that just work without any configuration. Cloning a repository
copies the intermap, but normal syncs do not keep the intermap in
sync. Use the "[fossil config pull interwiki][fcfg]" command to
synchronize the intermap.
* The is no backlink tracking. For ordinary intrawiki links, Fossil keeps
track of both the source and target, and when displaying targets it
commonly shows links to that target. For example, if you mention a
check-in as part of a comment of another check-in, that new check-in
shows up in the "References" section of the target check-in.
([example](31af805348690958). In other words, Fossil tracks not just
"_source→target_", but it also tracks "_target→source_".
But backtracking does not work for interwiki links, since the Fossil
running on the target has no way of scanning the source text and
hence has no way of knowing that it is a target of a link from the source.
[fcfg]: /help?cmd=config
## Intermap Storage Details
|
| ︙ | ︙ |
Changes to www/json-api/api-artifact.md.
| ︙ | ︙ | |||
69 70 71 72 73 74 75 | <a id="file"></a> # File Artifacts Fetches information about file artifacts. **FIXME:** the content type guessing is currently very primitive, and | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | <a id="file"></a> # File Artifacts Fetches information about file artifacts. **FIXME:** the content type guessing is currently very primitive, and may (but I haven't seen this) mis-diagnose some non-binary files as binary. Fossil doesn't yet have a mechanism for mime-type mappings. **Status:** implemented 20111020 **Required permissions:** "o" **Request:** `/json/artifact/FILE_HASH` |
| ︙ | ︙ |
Changes to www/json-api/api-auth.md.
| ︙ | ︙ | |||
31 32 33 34 35 36 37 | the top-level POST request envelope) to prove to fossil that they are authorized to access the requested resources. For most intents and purposes, the "auth token" and the "login cookie" are the same thing (or serve the same purpose), and the auth token is in fact just the value part of the login cookie (which has a project-specific key). Note that fossil has two conventional user names which can show up in | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | the top-level POST request envelope) to prove to fossil that they are authorized to access the requested resources. For most intents and purposes, the "auth token" and the "login cookie" are the same thing (or serve the same purpose), and the auth token is in fact just the value part of the login cookie (which has a project-specific key). Note that fossil has two conventional user names which can show up in various responses but do not refer to specific people: nobody and anonymous. The nobody user is anyone who is not logged in. The anonymous user is logged in but has no persistent user data (no associated user name, email address, or similar). Normally the guest (nobody) user has more access restrictions. The distinction between the two is largely historical - it is a mechanism to keep bots from following the multitudes of links generated by the HTML interface (especially the ZIP files), while allowing interested users to do so by logging in as the |
| ︙ | ︙ | |||
156 157 158 159 160 161 162 | request envelope or as a GET parameter, and it *must* be given if the request requires restricted access to a resource. e.g. if reading tickets is disabled for the guest user then all non-guest users must send authentication info in their requests in order to be able to fetch ticket info. Cookie-aware clients should send the login-generated cookie with each | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | request envelope or as a GET parameter, and it *must* be given if the request requires restricted access to a resource. e.g. if reading tickets is disabled for the guest user then all non-guest users must send authentication info in their requests in order to be able to fetch ticket info. Cookie-aware clients should send the login-generated cookie with each request, in which case they do not need to explicitly include the `authToken` in the JSON envelope/GET arguments. If submitted, the `authToken` is used, otherwise the cookie, if set, is used. Note that fossil uses a project-dependent cookie name in order to help thwart attacks, so there is no simple mapping of cookie *name* to auth token. That said, the cookie's *value* is also the auth token's value. > Special case: when accessing fossil over a local server instance |
| ︙ | ︙ | |||
198 199 200 201 202 203 204 | non-anonymous login except that extra "anonymousSeed" property is required. The password value *may* be time-limited, and *may* eventually become invalidated due to old age. This is unspecified. ***Potential***** (low-probability) bug regarding the seed value:** from | | | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | non-anonymous login except that extra "anonymousSeed" property is required. The password value *may* be time-limited, and *may* eventually become invalidated due to old age. This is unspecified. ***Potential***** (low-probability) bug regarding the seed value:** from what I hear, some unusual JSON platforms don't support full 32-bit precision. If absolutely necessary we could chop off a bit or two from the seed value (*if* it ever becomes a problem and if DRH blesses it). Or we could just make it a double. <a id="logout"></a> # Logout |
| ︙ | ︙ |
Changes to www/json-api/api-checkout.md.
1 2 3 4 5 6 7 8 9 10 11 | # JSON API: /status ([⬑JSON API Index](index.md)) # Status of Local Checkout **Status:** implemented 20130223 **Required permissions:** n/a (local access only) **Request:** `/json/status` | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # JSON API: /status ([⬑JSON API Index](index.md)) # Status of Local Checkout **Status:** implemented 20130223 **Required permissions:** n/a (local access only) **Request:** `/json/status` This command requires a local checkout and is the analog to the "fossil status" command. **Request Options:** currently none. Payload example: ```json |
| ︙ | ︙ |
Changes to www/json-api/api-finfo.md.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
CLI mode: `--name` or positional argument.
- `checkin=string`. Only return info related to this specific checkin,
as opposed to listing all checkins. If set, neither "before" nor
"after" have any effect.\
CLI mode: `--checkin|-ci`
- `before=DATETIME` only lists checkins from on or before this time.\
CLI mode: `--before|-b`
| | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
CLI mode: `--name` or positional argument.
- `checkin=string`. Only return info related to this specific checkin,
as opposed to listing all checkins. If set, neither "before" nor
"after" have any effect.\
CLI mode: `--checkin|-ci`
- `before=DATETIME` only lists checkins from on or before this time.\
CLI mode: `--before|-b`
- `after=DATETIME` only lists checkins from on or after this time.
Using this option swaps the sort order, to provide reasonable
behaviour in conjunction with the limit option.\
Only one of "before" and "after" may be specified, and if both are
specified then which one takes precedence is unspecified.\
CLI mode: `--after|-a`
- `limit=integer` limits the output to (at most) the given number of
associated checkins.\
|
| ︙ | ︙ |
Changes to www/json-api/api-query.md.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
"mtime":1303870798,
},
…
]
}
```
| | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
"mtime":1303870798,
},
…
]
}
```
The column names are provided in a separate field because their order
is guaranteed to match the order of the query columns, whereas object
key/value pairs might get reordered (typically sorted by key) when
travelling through different JSON implementations. In this manner,
clients can e.g. be sure to render the columns in the proper
(query-specified) order.
When in "array" mode the "rows" results will be an array of arrays. For
example, the above "rows" property would instead look like:
`[ [1, "drh", "All Tickets", 1303870798, … ], … ]`
Note the column *names* are never *guaranteed* to be exactly as they
appear in the SQL *unless* they are qualified with an AS, e.g. `SELECT
foo AS foo...`. When generating reports which need fixed column names, it
is highly recommended to use an AS qualifier for every column, even if
they use the same name as the column. This is the only way to guarantee
that the result column names will be stable. (FYI: that behaviour comes
from sqlite3, not the JSON bits, and this behaviour *has* been known to
change between sqlite3 versions (so this is not just an idle threat of
*potential* future incompatibility).)
|
Changes to www/json-api/api-tag.md.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
- `name=string` The tag name to search for. Can optionally be the 3rd
path element.
- `limit=int` (defalt=0) Limits the number of results (0=no limit).
Since they are ordered from oldest to newest, the newest N results
will be returned.
- `type=string` (default=`*`) Searches only for the given type of
| | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
- `name=string` The tag name to search for. Can optionally be the 3rd
path element.
- `limit=int` (defalt=0) Limits the number of results (0=no limit).
Since they are ordered from oldest to newest, the newest N results
will be returned.
- `type=string` (default=`*`) Searches only for the given type of
artifact (using fossil's conventional type naming: ci, e, t, w.)
- `raw=bool` (=false) If enabled, the response is an array of hashes
of the requested artifact type; otherwise,
it is an array of higher-level objects. If this is
true, the "name" property is interpreted as-is. If it is false, the
name is automatically prepended with "sym-" (meaning a branch).
(FIXME: the current semantics are confusing and hard to remember.
Re-do them.)
**Response payload example, in RAW mode: (expect this format to change
at some point!)**
|
| ︙ | ︙ |
Changes to www/json-api/conventions.md.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
using the given name. e.g. if `jsonp=foo` then the result would look like:\
`foo( {...the response envelope...} )`
The API allows most of those (normally all but the payload) to come in
as either GET parameters or properties of the top-level POSTed request
JSON envelope, with GET taking priority over POST. (Reminder to self: we
could potentially also use values from cookies. Fossil currently only
| | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
using the given name. e.g. if `jsonp=foo` then the result would look like:\
`foo( {...the response envelope...} )`
The API allows most of those (normally all but the payload) to come in
as either GET parameters or properties of the top-level POSTed request
JSON envelope, with GET taking priority over POST. (Reminder to self: we
could potentially also use values from cookies. Fossil currently only
uses 1 cookie (the login token), and I'd prefer to keep it that way.)
POST requests without such an envelope will be rejected, generating a
Fossil/JSON error response (as opposed to an HTTP error response). GET
requests, by definition, never have an envelope.
POSTed client requests *must* send a Content-Type header of either
`application/json` , `application/javascript`, or `text/plain`, or the
|
| ︙ | ︙ | |||
167 168 169 170 171 172 173 | conversions behave. No higher-level constructs, e.g. JSON **arrays** or **objects**, are accepted in string form. Such parameters must be set in the POST envelope or payload, as specified by the specific API. This API does not currently use any **floating-point** parameters, but | | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | conversions behave. No higher-level constructs, e.g. JSON **arrays** or **objects**, are accepted in string form. Such parameters must be set in the POST envelope or payload, as specified by the specific API. This API does not currently use any **floating-point** parameters, but does return floating-point results in a couple of places. For **integer** parameters we use a conventional string-to-int algorithm and assume base 10 (analog to atoi(3)). The API may err on the side of usability when given technically invalid values. e.g. "123abc" will likely be interpreted as the integer 123. No APIs currently rely on integer parameters with more than 32 bits (signedness is call-dependent but few, if any, use negative values). |
| ︙ | ︙ | |||
203 204 205 206 207 208 209 | TODO: consider changing the GET-value-to-bool semantics to match the JS semantics, for consistency (within the JSON API at least, but that might cause inconsistencies vis-a-vis the HTML interface). <a id="response-envelope"></a> # Response Envelope | | | | | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | TODO: consider changing the GET-value-to-bool semantics to match the JS semantics, for consistency (within the JSON API at least, but that might cause inconsistencies vis-a-vis the HTML interface). <a id="response-envelope"></a> # Response Envelope Every response comes in the form of an HTTP response or (in CLI mode) JSON sent to stdout. The body of the response is a JSON object following a common envelope format. The envelope has the following properties: - `fossil`: Fossil server version string. This property is basically "the official response envelope marker" - if it is set, clients can "probably safely assume" that the object indeed came from one of the Fossil/JSON APIs. This API never creates responses which do not contain this property. - `requestId`: Only set if the request contained it, and then it is echoed back to the caller as-is. This can be used to determine (client-side) which request a given response is coming in for (assuming multiple asynchronous requests are pending). In practice this generally isn’t needed because response handling tends to be done by closures associated with the original request object (at least in JavaScript code). In languages without closures it might have some use. It may be any legal JSON value - it need not be confined to a string or number. - `resultCode`: Standardized result code string in the form `FOSSIL-####`. Only error responses contain a `resultCode`. - `resultText`: Possibly a descriptive string, possibly empty. Supplements the resultCode, but can also be set on success responses (but normally isn't). Clients must not rely on any specific values being set here. - `payload`: Request-specific response payload (data type/structure is request-specific). The payload is never set for error responses, only for success responses (and only those which actually have a payload - not all do). - `timestamp`: Response timestamp (GMT Unix Epoch). We use seconds precision because I did not know at the time that Fossil actually records millisecond precision. - `payloadVersion`: Not initially needed, but reserved for future use in maintaining version compatibility when the format of a given response type's payload changes. If needed, the "first version" value is assumed to be 0, for semantic [near-]compatibility with the undefined value clients see when this property is not set. - `command`: Normalized form of the command being run. It consists of |
| ︙ | ︙ | |||
400 401 402 403 404 405 406 | can filter the output to globally replace ASCII 9dec (TAB) with ASCII 32dec (space). Because JSON string values *never* contain hard tabs (they are represented by `\t`) there is no chance that such a global replacement will corrupt JSON string contents - only the formatting will be affected. Potential TODO: because extraneous indention "could potentially" be used | | | 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | can filter the output to globally replace ASCII 9dec (TAB) with ASCII 32dec (space). Because JSON string values *never* contain hard tabs (they are represented by `\t`) there is no chance that such a global replacement will corrupt JSON string contents - only the formatting will be affected. Potential TODO: because extraneous indention "could potentially" be used as a form of DoS, the option *might* be subject to later removal from HTTP mode (in CLI it's fine). In HTTP mode no trailing newline is added to the output, whereas in CLI mode one is normally appended (exception: in JSONP mode no newline is appended, to (rather pedantically and arbitraily) allow the client to add a semicolon at the end if he likes). There is currently no option to control the newline behaviour, but the underlying JSON code supports |
| ︙ | ︙ | |||
480 481 482 483 484 485 486 |
admins consider error messages to be security-relevant and like to
"dumb them down" on their way to the client, whereas developers
normally want to see very specific error codes when tracking down a
problem. We can offer a configuration option to "dumb down" error
codes to their generic category by simply doing a modulo 100
(or 1000) against the native error code number. e.g. FOSSIL-1271
could (via a simple modulo) be reduced to FOSSIL-1200 or
| | | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
admins consider error messages to be security-relevant and like to
"dumb them down" on their way to the client, whereas developers
normally want to see very specific error codes when tracking down a
problem. We can offer a configuration option to "dumb down" error
codes to their generic category by simply doing a modulo 100
(or 1000) against the native error code number. e.g. FOSSIL-1271
could (via a simple modulo) be reduced to FOSSIL-1200 or
FOSSIL-1000, depending on the paranoia level of the sysadmin. I have
tried to order the result code numbers so that a dumb-down level of
2 provides reasonably usable results without giving away too much
detail to malicious clients.\
(**TODO:** `g.json.errorDetailParanoia` is used to set the
default dumb-down level, but it is currently set at compile-time.
It needs to be moved to a config option. We have a chicken/egg scenario
with error reporting and db access there (where the config is
|
| ︙ | ︙ | |||
531 532 533 534 535 536 537 | - `FOSSIL-1104`: RE-USE - `FOSSIL-1105`: A server-side timeout was reached. (i’m not sure we can actually implement this one, though.) - `FOSSIL-1106`: Assertion failed (or would have had we continued). Note: if an `assert()` fails in CGI/server modes, the HTTP response will be code 500 (Internal Server Error). We want to avoid that and return a JSON response instead. All of that said - there seems | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | - `FOSSIL-1104`: RE-USE - `FOSSIL-1105`: A server-side timeout was reached. (i’m not sure we can actually implement this one, though.) - `FOSSIL-1106`: Assertion failed (or would have had we continued). Note: if an `assert()` fails in CGI/server modes, the HTTP response will be code 500 (Internal Server Error). We want to avoid that and return a JSON response instead. All of that said - there seems to be little reason to implement this, since assertions are "truly serious" errors. - `FOSSIL-1107`: Allocation/out of memory error. This cannot be reasonably reported because fossil aborts if an allocation fails. - `FOSSIL-1108`: Requested API is not yet implemented. - `FOSSIL-1109`: Panic! Fossil's `fossil_panic()` or `cgi_panic()` was called. In non-JSON HTML mode this produces an HTTP 500 error. Clients "should" report this as a potential bug, as it |
| ︙ | ︙ |
Changes to www/json-api/hacking.md.
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
foregoes fossil's conventional command/path dispatching mechanism. Only
the top-most "json" command/path is dispatched directly by fossil's
core. The disadvantages of this are that we lose fossil's conventional
help text mechanism (which is based on code comments in the
command/path's dispatcher impl) and the ability to write abbreviated
command names in CLI mode ("json" itself may be abbreviated, but not the
subcommands). The advantages are that we can handle CLI/HTTP modes
| | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
foregoes fossil's conventional command/path dispatching mechanism. Only
the top-most "json" command/path is dispatched directly by fossil's
core. The disadvantages of this are that we lose fossil's conventional
help text mechanism (which is based on code comments in the
command/path's dispatcher impl) and the ability to write abbreviated
command names in CLI mode ("json" itself may be abbreviated, but not the
subcommands). The advantages are that we can handle CLI/HTTP modes
almost identically (there are a couple of minor differences) by unifying
them under the same callback functions much more easily.
The top-level "json" command/path uses its own dispatching mechanism
which uses either the path (in HTTP mode) or CLI positional arguments to
dispatch commands (stopping at the first "flag option" (e.g. -foo) in
CLI mode). The command handlers are simply callback functions which
return a cson\_value pointer (the C representation of an arbitrary JSON
|
| ︙ | ︙ | |||
123 124 125 126 127 128 129 | they will exit differently. Because of this, it is perfectly fine for a command handler to exit via one of fossil's conventional mechanisms (e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` if they really want to). One exception is `fossil_exit()`, which does _not_ generate any extra output and will `exit()` the app. In the JSON API, as a rule of thumb, `fossil_exit()` is only used when we *want* a failed request to cause an HTTP 500 error, and it is reserved for | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | they will exit differently. Because of this, it is perfectly fine for a command handler to exit via one of fossil's conventional mechanisms (e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` if they really want to). One exception is `fossil_exit()`, which does _not_ generate any extra output and will `exit()` the app. In the JSON API, as a rule of thumb, `fossil_exit()` is only used when we *want* a failed request to cause an HTTP 500 error, and it is reserved for allocation errors and similar truly catastrophic failures. That said... libcson has been hacked to use `fossil_alloc()` and friends for memory management, and those routines exit on error, so alloc error handling in the JSON command handler code can afford to be a little lax (the majority of *potential* errors clients get from the cson API have allocation failure as their root cause). As a side-note: the vast majority (if not all) of the cson API calls are |
| ︙ | ︙ | |||
257 258 259 260 261 262 263 |
functions which behave ever so slightly differently. A summary:
- `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON
environment," which is a superset of the GET/POST/`POST.payload` (if
`POST.payload` is-a Object).
- `json_find_option_TYPE()`: searches the CLI args (only when in CLI
mode) and the JSON environment.
| | | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
functions which behave ever so slightly differently. A summary:
- `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON
environment," which is a superset of the GET/POST/`POST.payload` (if
`POST.payload` is-a Object).
- `json_find_option_TYPE()`: searches the CLI args (only when in CLI
mode) and the JSON environment.
- The use of fossil's `P()` and `PD()` macros is discouraged in JSON
callbacks because they can only handle String data from the CLI or
GET parameters (not POST/`POST.payload`). (Note that `P()` and `PD()`
*normally* also handle POSTed keys, but they only "see" values
posted as form-urlencoded fields, and not JSON format.)
- `find_option()` (from `src/main.c`) "should" also be avoided in
JSON API handlers because it removes flag from the g.argv
arguments list. That said, the JSON API does use `find_option()` in
several of its option-finding convenience wrappers.
For example code: the existing command callbacks demonstrate all kinds
of uses and the various styles of parameter/option inspection. Check out
any of the functions named `json_page_SOMETHING()`.
<a href="creating-json"></a>
# Creating JSON Data
<a href="creating-json-values"></a>
## Creating JSON Values
cson has a fairly rich API for creating and manipulating the various
JSON-defined value types. For a detailed overview and demonstration I
recommend reading:
[](https://fossil.wanderinghorse.net/wikis/cson/?page=HowTo)
That said, the Fossil/JSON API has several convenience wrappers to save
a few bytes of typing:
|
| ︙ | ︙ | |||
308 309 310 311 312 313 314 | The `cson_sqlite3_xxx()` family of functions convert `sqlite3_stmt` rows to Arrays or Objects, or convert single columns to a JSON-compatible form. See `json_stmt_to_array_of_obj()`, `json_stmt_to_array_of_array()` (both in `src/json.c`), and `cson_sqlite3_column_to_value()` and friends (in `extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric | | | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | The `cson_sqlite3_xxx()` family of functions convert `sqlite3_stmt` rows to Arrays or Objects, or convert single columns to a JSON-compatible form. See `json_stmt_to_array_of_obj()`, `json_stmt_to_array_of_array()` (both in `src/json.c`), and `cson_sqlite3_column_to_value()` and friends (in `extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric types, but they optimistically/naively *assume* that any fields of type TEXT or BLOB are actually UTF8 data, and treat them as such. cson's string class only handles UTF8 data and it is semantically illegal to feed them anything but UTF8. Violating this will likely result in down-stream errors (e.g. when emitting the JSON string output). **The moral of this story is:** *do not use these APIs to fetch binary data*. JSON doesn't do binary and the `cson_string` class does not protect itself against clients feeding it non-UTF8 data. Here's a basic example of using these features: ```c |
| ︙ | ︙ | |||
345 346 347 348 349 350 351 | than they appear in the query (this is documented behaviour of sqlite3). Because the JSON API needs to return stable field names, we need to use AS clauses to be guaranteed that the db driver will return the column names we want. Note that the AS clause is often used to translate column names into something more JSON-conventional or user-friendly, e.g. "SELECT cap AS capabilities...". Alternately, we can convert the individual `sqlite3_stmt` column values to JSON using | | | 345 346 347 348 349 350 351 352 353 | than they appear in the query (this is documented behaviour of sqlite3). Because the JSON API needs to return stable field names, we need to use AS clauses to be guaranteed that the db driver will return the column names we want. Note that the AS clause is often used to translate column names into something more JSON-conventional or user-friendly, e.g. "SELECT cap AS capabilities...". Alternately, we can convert the individual `sqlite3_stmt` column values to JSON using `cson_sqlite3_column_to_value()`, without referring directly to the db-reported column name. |
Changes to www/makefile.wiki.
1 2 3 4 5 6 7 8 9 10 11 | <title>The Fossil Build Process</title> <h1>1.0 Introduction</h1> The build process for Fossil is tricky in that the source code needs to be processed by three different preprocessor programs before it is compiled. Most users will download a [https://fossil-scm.org/home/uv/download.html | precompiled binary] so this is of no consequence to them, and even those who want to compile the code themselves can use one of the [./build.wiki | existing makefiles]. | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <title>The Fossil Build Process</title> <h1>1.0 Introduction</h1> The build process for Fossil is tricky in that the source code needs to be processed by three different preprocessor programs before it is compiled. Most users will download a [https://fossil-scm.org/home/uv/download.html | precompiled binary] so this is of no consequence to them, and even those who want to compile the code themselves can use one of the [./build.wiki | existing makefiles]. So most people do not need to be concerned with the build complexities of Fossil. But hard-core developers who desire a deep understanding of how Fossil is put together can benefit from reviewing this article. <h1 id="srctour">2.0 Source Code Tour</h1> The source code for Fossil is found in the |
| ︙ | ︙ | |||
54 55 56 57 58 59 60 | 8. linenoise.h The TH1 script engine is implemented using files: 9. th.c 10. th.h | | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 8. linenoise.h The TH1 script engine is implemented using files: 9. th.c 10. th.h The preprocessing steps are omitted for all of these imported files. The VERSION.h header file is generated from other information sources using a small program called: 11. [/file/tools/mkversion.c | mkversion.c] The builtin_data.h header file contains the definitions of C-language byte-array constants that contain various resources such as scripts and images. The builtin_data.h header file is generated from the original resource files using a small program called: 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] script used to implement the --tk option to [/help?cmd=diff| fossil diff], the [/file/src/markdown.md | markdown documentation], and the various |
| ︙ | ︙ | |||
130 131 132 133 134 135 136 | should understand that whenever "src.c" or "src.h" is used in the text that follows, we really mean all (79) other source files other than the exceptions described above. <h1>3.0 Automatically generated files</h1> The "VERSION.h" header file contains some C preprocessor macros that | | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | should understand that whenever "src.c" or "src.h" is used in the text that follows, we really mean all (79) other source files other than the exceptions described above. <h1>3.0 Automatically generated files</h1> The "VERSION.h" header file contains some C preprocessor macros that identify the version of Fossil that is to be built. The VERSION.h file is generated automatically from information extracted from the "manifest", "manifest.uuid", and "VERSION" source files in the root directory of the source tree. (The "manifest" and "manifest.uuid" files are automatically generated and updated by Fossil itself. See the [/help/setting | fossil set manifest] command for additional information.) |
| ︙ | ︙ | |||
292 293 294 295 296 297 298 | the HTTPS option is enabled, then it will also need to link against the appropriate SSL implementation. And, of course, Fossil needs to link against the standard C library. No other libraries or external dependences are used. <h1>7.0 Debugging</h1> | | | | | | > | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | the HTTPS option is enabled, then it will also need to link against the appropriate SSL implementation. And, of course, Fossil needs to link against the standard C library. No other libraries or external dependences are used. <h1>7.0 Debugging</h1> Debug mode is controlled via a FOSSIL_DEBUG preprocessor macro. This can be set explicitly with the make command for the target platform. However, in practice it is instead recommended to add a respective configure option for the target platform and then perform a clean build. This way the Debug flags are consistently applied across the whole build process. For example, use these Debug flags in addition to other flags passed to the configure scripts: On Linux, *NIX and similar platforms: <pre> ./configure --fossil-debug </pre> On Windows: <pre> win\buildmsvc.bat FOSSIL_DEBUG=1 </pre> The resulting fossil binary can then be loaded into a platform-specific debugger. Source files displayed in the debugger correspond to the ones generated from the translation stage of the build process, as that is what was actually compiled into the respective object files that make up the fossil binary. <h1>8.0 See Also</h1> * [./tech_overview.wiki | A Technical Overview Of Fossil] * [./adding_code.wiki | How To Add Features To Fossil] |
Changes to www/mdtest/test1.md.
1 2 | # Markdown Link-test | | | 1 2 3 4 5 6 7 8 9 10 | # Markdown Link-test This document exists solely as a test for some of the hyperlinking capabilities of Markdown as implemented by Fossil. ## Relative-Path Links * The index: [](../index.wiki) * Load management: [](../loadmgmt.md) |
| ︙ | ︙ |
Changes to www/newrepo.wiki.
| ︙ | ︙ | |||
55 56 57 58 59 60 61 | $ mkdir demo $ cd demo $ fossil open ../demo.fossil </verbatim> That creates a file called <tt>_FOSSIL_</tt> in the current directory, and this file contains all kinds of fossil-related | | > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | $ mkdir demo $ cd demo $ fossil open ../demo.fossil </verbatim> That creates a file called <tt>_FOSSIL_</tt> in the current directory, and this file contains all kinds of fossil-related information about your local repository. Under Linux, the BSDs or macOS, this will instead be called <tt>.fslckout</tt>. You can ignore it for all purposes, but be sure not to accidentally remove it or otherwise damage it - it belongs to fossil, not you. The next thing we need to do is add files to our repository. As it happens, we have a few C source files lying around, which we'll simply copy into our working directory. |
| ︙ | ︙ |
Changes to www/password.wiki.
| ︙ | ︙ | |||
61 62 63 64 65 66 67 | "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> When a user logs into Fossil using the web interface, the login name | | > > > > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. For most modern fossil server setups with [/help?cmd=redirect-to-https|redirect-to-https] enabled, this will be protected by the SSL connection over HTTPS so it cannot be easily viewed. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields of the USER table on the server. The USER.CEXPIRE field holds an expiration date for the cookie, encoded as a Julian day number. On all subsequent HTTP requests, the cookie value is matched against the USER table to enable access to the repository. Note that in order to log into a Fossil server, it is necessary to write information into the repository database. Hence, login is not |
| ︙ | ︙ | |||
121 122 123 124 125 126 127 | <pre> http://<span style="color:blue">login:password</span>@servername.org/path </pre> For older clients, the password is used for the shared secret as stated in the URL and with no encoding. For newer clients, the shared secret is derived from the password | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | <pre> http://<span style="color:blue">login:password</span>@servername.org/path </pre> For older clients, the password is used for the shared secret as stated in the URL and with no encoding. For newer clients, the shared secret is derived from the password by transforming the password using the SHA1 hash encoding described above. However, if the first character of the password is "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password is used directly as the share secret without the SHA1 encoding. <pre> http://<span style="color:blue">login:*password</span>@servername.org/path </pre> This *-before-the-password trick can be used by newer clients to sync against a legacy server that does not understand the new SHA1 password encoding. |
Changes to www/patchcmd.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # The "fossil patch" command The "[fossil patch](/help?cmd=patch)" command is designed to transfer uncommitted changes from one check-out to another, including transfering those changes to other machines. For example, if you are working on a Windows desktop and you want to test your changes on a Linux server before you commit, you can use the "fossil patch push" command to make a copy of all your changes on the remote Linux server: fossil patch push linuxserver:/path/to/checkout | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # The "fossil patch" command The "[fossil patch](/help?cmd=patch)" command is designed to transfer uncommitted changes from one check-out to another, including transfering those changes to other machines. For example, if you are working on a Windows desktop and you want to test your changes on a Linux server before you commit, you can use the "fossil patch push" command to make a copy of all your changes on the remote Linux server: fossil patch push linuxserver:/path/to/checkout In the previous line "linuxserver" is the name of the remote machine and "/path/to/checkout" is an existing checkout directory for the same project on the remote machine. The "fossil patch push" command works by first creating a patch file, then transfering that patch file to the remote machine using "ssh", then applying the patch. If you do not have ssh available, you can break these steps apart as follows: |
| ︙ | ︙ | |||
83 84 85 86 87 88 89 | ## Implementation Details The "fossil patch create" command records all of the local, uncommitted changes in an SQLite database file. If the argument to "fossil patch create" is a filename, then the patch-file database is written into that file. If the argument is "-" then the database is written on standard output. | | | | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
## Implementation Details
The "fossil patch create" command records all of the local, uncommitted
changes in an SQLite database file. If the argument to "fossil patch create"
is a filename, then the patch-file database is written into that file.
If the argument is "-" then the database is written on standard output.
The "fossil patch apply" command reads the patch-file database
and applies it to the local check-out. If a filename is given as an
argument, then the database is read from that file. If the argument is "-"
then the database is read from standard input.
Hence the command:
fossil patch push remote:projectA
Is equivalent to:
fossil patch create - | ssh -T remote 'cd projectA;fossil patch apply -'
Likewise, a command like this:
fossil patch pull remote:projB
could be entered like this:
ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply -
The "fossil patch view" command just opens the patch-file database and prints
a summary of its contents on standard output.
|
Changes to www/pikchr.md.
1 2 3 | # The Pikchr Diagram Language Pikchr (pronounced "picture") is a [PIC][1]-like markup language for creating | | | 1 2 3 4 5 6 7 8 9 10 11 | # The Pikchr Diagram Language Pikchr (pronounced "picture") is a [PIC][1]-like markup language for creating diagrams in technical documentation. Source text for Pikchr diagrams can be embedded directly in either [Markdown][2] or [Fossil Wiki][3]. Fossil translates the Pikchr source text into SVG which is displayed as part of the rendered wiki. [1]: wikipedia:/wiki/Pic_language [2]: /md_rules [3]: /wiki_rules |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
* **float-left** → The diagram is shown at the left margin and
text fills in around the diagram.
* **float-right** → The diagram is shown at the right margin and
text fills in around the diagram.
* **source** → The display starts out showing the Pikchr source text.
| | | 132 133 134 135 136 137 138 139 |
* **float-left** → The diagram is shown at the left margin and
text fills in around the diagram.
* **float-right** → The diagram is shown at the right margin and
text fills in around the diagram.
* **source** → The display starts out showing the Pikchr source text.
The reader must click (or Alt-click or Ctrl-click) to show the diagram.
|
Changes to www/qandc.wiki.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | is not the point. Fossil has several key features that Trac lacks and that I need: most notably the fact that fossil supports disconnected operation. As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | is not the point. Fossil has several key features that Trac lacks and that I need: most notably the fact that fossil supports disconnected operation. As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, SQLite, and so forth) in order to run fossil. Fossil runs just fine in a chroot jail all by itself. And the self-contained fossil executable is much less than 1MB in size. (Update 2015-01-12: Fossil has grown in the years since the previous sentence was written but is still much less than 2MB according to "size" when compiled using -Os on x64 Linux.) Fossil is the very opposite of bloat. </div> |
Changes to www/quickstart.wiki.
| ︙ | ︙ | |||
362 363 364 365 366 367 368 | The <i>VERSION</i> can be the name of a branch or tag or any abbreviation to the 40-character artifact identifier for a particular check-in, or it can be a date/time stamp. ([./checkin_names.wiki | more info]) If you omit the <i>VERSION</i>, then fossil moves you to the | | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | The <i>VERSION</i> can be the name of a branch or tag or any abbreviation to the 40-character artifact identifier for a particular check-in, or it can be a date/time stamp. ([./checkin_names.wiki | more info]) If you omit the <i>VERSION</i>, then fossil moves you to the latest version of the branch you are currently on. The default behavior is for [./concepts.wiki#workflow|autosync] to be turned on. That means that a [/help/pull|pull] automatically occurs when you run [/help/update|update] and a [/help/push|push] happens automatically after you [/help/commit|commit]. So in normal practice, the push, pull, and sync commands are rarely used. But it is important to know about them, all the same. |
| ︙ | ︙ |
Changes to www/rebaseharm.md.
| ︙ | ︙ | |||
222 223 224 225 226 227 228 | The [golden rule of rebasing][golden] is that you should never do it on public branches, so if you are using rebase as intended, that means you are keeping private branches. Or, to put it another way, you are doing siloed development. You are not sharing your intermediate work with collaborators. This is not good for product quality. [Nagappan, et. al][nagappan] studied bugs in Windows Vista and found | | | | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | The [golden rule of rebasing][golden] is that you should never do it on public branches, so if you are using rebase as intended, that means you are keeping private branches. Or, to put it another way, you are doing siloed development. You are not sharing your intermediate work with collaborators. This is not good for product quality. [Nagappan, et. al][nagappan] studied bugs in Windows Vista and found that the best predictor of bugs is the distance on the org-chart between the stake-holders. The bug rate is inversely related to the amount of communication among the engineers. Similar findings arise in other disciplines. Keeping private branches does not prove that developers are communicating insufficiently, but it is a key symptom of that problem. [Weinberg][weinberg] argues programming should be "egoless." That is to say, programmers should avoid linking their code with their sense of self, as that makes it more difficult for them to find and respond to bugs, and hence makes them less productive. Many developers are drawn to private branches out of sense of ego. "I want to get the code right before I publish it." I sympathize with this sentiment, |
| ︙ | ︙ |
Changes to www/selfcheck.wiki.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | part to the defensive measures described here, no data has been lost. The integrity checks are doing their job well.</p> <h2>Atomic Check-ins With Rollback</h2> The Fossil repository is stored in an <a href="http://www.sqlite.org/">SQLite</a> database file. | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | part to the defensive measures described here, no data has been lost. The integrity checks are doing their job well.</p> <h2>Atomic Check-ins With Rollback</h2> The Fossil repository is stored in an <a href="http://www.sqlite.org/">SQLite</a> database file. ([./tech_overview.wiki | Additional information] about the repository file format.) SQLite is very mature and stable and has been in wide-spread use for many years, so we are confident it will not cause repository corruption. SQLite databases do not corrupt even if a program or system crash or power failure occurs in the middle of the update. If some kind of crash does occur in the middle of a change, then all the changes are rolled |
| ︙ | ︙ |
Changes to www/selfhost.wiki.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | <a href="http://www.linode.com/">Linode 8192</a> located in Dallas, TX - on the same virtual machine that hosts <a href="http://www.sqlite.org/">SQLite</a> and over a dozen other smaller projects. This demonstrates that Fossil can run on a low-power host processor. Multiple fossil-based projects can easily be hosted on the same machine, even if that machine is itself one of several dozen virtual machines on | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <a href="http://www.linode.com/">Linode 8192</a> located in Dallas, TX - on the same virtual machine that hosts <a href="http://www.sqlite.org/">SQLite</a> and over a dozen other smaller projects. This demonstrates that Fossil can run on a low-power host processor. Multiple fossil-based projects can easily be hosted on the same machine, even if that machine is itself one of several dozen virtual machines on a single physical box. The CGI script that runs the canonical Fossil self-hosting repository is as follows: <pre> #!/usr/bin/fossil repository: /fossil/fossil.fossil </pre> |
| ︙ | ︙ |
Changes to www/server/debian/service.md.
| ︙ | ︙ | |||
181 182 183 184 185 186 187 | Note the change of configuration directory from the `~/.local` directory to the system level. We need to start this socket listener at the root level because of the low-numbered TCP port restriction we brought up above. This configuration says more or less the same thing as the socket part | | | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | Note the change of configuration directory from the `~/.local` directory to the system level. We need to start this socket listener at the root level because of the low-numbered TCP port restriction we brought up above. This configuration says more or less the same thing as the socket part of an `inetd` entry [exemplified elsewhere in this documentation](../any/inetd.md). Next, create the service definition file in that same directory as `fossil@.service`: ```dosini [Unit] |
| ︙ | ︙ |
Changes to www/server/openbsd/fastcgi.md.
| ︙ | ︙ | |||
170 171 172 173 174 175 176 | files larger than this limit as unversioned content, you need to raise the limit as we’ve done above with the “`connection max request body`” setting, raising the limit to 100 MiB. [dlim]: https://man.openbsd.org/httpd.conf.5#connection [uv]: ../../unvers.wiki | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | files larger than this limit as unversioned content, you need to raise the limit as we’ve done above with the “`connection max request body`” setting, raising the limit to 100 MiB. [dlim]: https://man.openbsd.org/httpd.conf.5#connection [uv]: ../../unvers.wiki **NOTE:** If not already in possession of an HTTPS certificate, comment out the `https` server block and proceed to securing a free [Let's Encrypt Certificate](#letsencrypt); otherwise skip to [Start `httpd`](#starthttpd). ## <a id="letsencrypt"></a>Let's Encrypt Certificate |
| ︙ | ︙ |
Changes to www/server/windows/service.md.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | ``` This will create a windows service named 'Fossil-DSCM' running under the local system account and accessible on port 8080 by default. `fossil winsrv` can also start, stop, and delete the service. For all available options, please execute `fossil help winsrv` on a windows install of Fossil. | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | ``` This will create a windows service named 'Fossil-DSCM' running under the local system account and accessible on port 8080 by default. `fossil winsrv` can also start, stop, and delete the service. For all available options, please execute `fossil help winsrv` on a windows install of Fossil. If you wish to serve a directory of repositories, the `fossil winsrv` command requires a slightly different set of options vs. `fossil server`: ``` fossil winsrv create --repository D:/Path/to/Repos --repolist ``` ### Choice of Directory Considerations |
| ︙ | ︙ |
Changes to www/settings.wiki.
| ︙ | ︙ | |||
42 43 44 45 46 47 48 | looking for unmanaged files with the <tt>extras</tt> command. Because these options can change over time, and the inconvenience of replicating changes, these settings are "versionable". As well as being able to be set using the <tt>settings</tt> command or the web interface, you can create versioned files in the <tt>.fossil-settings</tt> subdirectory of the check-out root, named with the setting name. | < | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | looking for unmanaged files with the <tt>extras</tt> command. Because these options can change over time, and the inconvenience of replicating changes, these settings are "versionable". As well as being able to be set using the <tt>settings</tt> command or the web interface, you can create versioned files in the <tt>.fossil-settings</tt> subdirectory of the check-out root, named with the setting name. Each file holds the value of a setting, and these files are checked in, committed, merged, and so on, as with any other file. Where a setting is a list of values, such as <tt>ignore-glob</tt>, you can use a newline as a separator as well as a comma. For example, to set the list of ignored files, create a <tt>.fossil-settings/ignore-glob</tt> file where each line contains a glob for ignored files. |
| ︙ | ︙ |
Changes to www/shunning.wiki.
| ︙ | ︙ | |||
31 32 33 34 35 36 37 | <h2>Alternatives</h2> All of these are rare cases: Fossil is [./antibot.wiki | designed to foil spammers up front], legally problematic check-ins should range from rare to nonexistent, and you have to go way out of your way to force Fossil to insert bad control artifacts. Therefore, before we get to | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<h2>Alternatives</h2>
All of these are rare cases: Fossil is [./antibot.wiki | designed to
foil spammers up front], legally problematic check-ins should range from
rare to nonexistent, and you have to go way out of your way to force
Fossil to insert bad control artifacts. Therefore, before we get to
methods of permanently deleting content from a Fossil repo, let's give
some alternatives that usually suffice, which don't damage the project's
fossil record:
* When a forum post or wiki article is "deleted," what actually
happens is that a new empty version is added to the Fossil repository.
The web interface interprets this
as "deleted," but the prior version remains available if you go
|
| ︙ | ︙ | |||
131 132 133 134 135 136 137 |
by the normal "sync" mechanism,
but it is still possible to copy shuns from one repository to another
using the "configuration" command:
<b>fossil configuration pull shun</b> <i>remote-url</i><br>
<b>fossil configuration push shun</b> <i>remote-url</i>
| | | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
by the normal "sync" mechanism,
but it is still possible to copy shuns from one repository to another
using the "configuration" command:
<b>fossil configuration pull shun</b> <i>remote-url</i><br>
<b>fossil configuration push shun</b> <i>remote-url</i>
The two commands above will pull or push shunning lists from or to
the <i>remote-url</i> indicated and merge the lists on the receiving
end. "Admin" privilege on the remote server is required in order to
push a shun list. In contrast, the shunning list will be automatically
received by default as part of a normal client "pull" operation unless
disabled by the "<tt>auto-shun</tt>" setting.
Note that the shunning list remains in the repository even after the
|
| ︙ | ︙ |
Changes to www/stats.wiki.
1 2 3 4 5 | <title>Fossil Performance</title> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <title>Fossil Performance</title> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? In an attempt to answer these questions, this report looks at several projects that use fossil for configuration management and examines how well they are working. The following table is a summary of the results. (Last updated on 2018-06-04.) Explanation and analysis follows the table. <table> <tr> |
| ︙ | ︙ | |||
98 99 100 101 102 103 104 | and are related to other artifacts. But at a low level (for example when synchronizing two instances of the same project) the only thing that matters is the unordered collection of artifacts. In fact, one of the key characteristics of Fossil is that the entire project history can be reconstructed simply by scanning the artifacts in an arbitrary order. The number of check-ins is the number of times that the "commit" command | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | and are related to other artifacts. But at a low level (for example when synchronizing two instances of the same project) the only thing that matters is the unordered collection of artifacts. In fact, one of the key characteristics of Fossil is that the entire project history can be reconstructed simply by scanning the artifacts in an arbitrary order. The number of check-ins is the number of times that the "commit" command has been run. A single check-in might change 3 or 4 files, or it might change dozens or hundreds of files. Regardless of the number of files changed, it still only counts as one check-in. The "Uncompressed Size" is the total size of all the artifacts within the repository assuming they were all uncompressed and stored separately on the disk. Fossil makes use of delta compression between related versions of the same file, and then uses zlib compression on the resulting |
| ︙ | ︙ |
Changes to www/style.wiki.
1 2 | <title>Coding Style</title> | | | 1 2 3 4 5 6 7 8 9 10 | <title>Coding Style</title> Fossil source code should follow the style guidelines below. <em> The Fossil source tree includes a few files taken from external sources (examples: [https://github.com/antirez/linenoise|linenoise] and [http://zlib.net/|zLib]) and this externally sourced code might not comply with these style guidelines. </em> |
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
into strings, which may exceed the 509 character limit defined by ANSI.
(example: bld/page_index.h)
<li> -Wno-long-long: Fossil uses the 'long long' integer type, which is not strictly ANSI C-89 (defined in C99).
The use of 'long long' resolves many problems with 64-bit arithmetics, especially on 32-bit machines.
(http_ssl.c, sha3.c, shell.c, util.c)
| | | > | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
into strings, which may exceed the 509 character limit defined by ANSI.
(example: bld/page_index.h)
<li> -Wno-long-long: Fossil uses the 'long long' integer type, which is not strictly ANSI C-89 (defined in C99).
The use of 'long long' resolves many problems with 64-bit arithmetics, especially on 32-bit machines.
(http_ssl.c, sha3.c, shell.c, util.c)
<li> alloca(): By default, sqlite3.c was compiled with the -DSQLITE_USE_ALLOCA flag to use the alloca() function.
This is no longer the case as of 20220119. alloca() is not considered ANSI C, and normally not
recommended due to portability issues, but performance and/or memory consumption
improvement may have been a stronger argument in favor of its usage.
(sqlite3.c)
</ol>
<li> All comments and identifiers are in English.
<li> The program is single-threaded. Do not use threads.
(One exception to this is the HTTP server implementation for Windows,
|
| ︙ | ︙ | |||
73 74 75 76 77 78 79 |
<b>3. Function header comments</b>:
<ol>
<li value=30> Every function has a header comment describing the purpose and use
of the function.
| | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
<b>3. Function header comments</b>:
<ol>
<li value=30> Every function has a header comment describing the purpose and use
of the function.
<li> A function header comment defines the behavior of the function in
sufficient detail to allow the function to be re-implemented from
scratch without reference to the original code.
<li> Functions that perform dynamic memory allocation (either directly
or indirectly via subfunctions) say so in their header comments.
</ol>
|
| ︙ | ︙ |
Changes to www/sync.wiki.
| ︙ | ︙ | |||
516 517 518 519 520 521 522 | The "gimme" name means "give me". The imperative "give me" is pronounced as if it were a single word "gimme" in some dialects of English (including the dialect spoken by the original author of Fossil). <h4>3.7.1 Unversioned Gimme Cards</h4> | | | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | The "gimme" name means "give me". The imperative "give me" is pronounced as if it were a single word "gimme" in some dialects of English (including the dialect spoken by the original author of Fossil). <h4>3.7.1 Unversioned Gimme Cards</h4> When synchronizing unversioned content, the client may send "uvgimme" cards to the server. A uvgimme card requests that the server send unversioned content to the client. The format of a uvgimme card is as follows: <pre> <b>uvgimme</b> <i>name</i> </pre> |
| ︙ | ︙ | |||
568 569 570 571 572 573 574 | The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <pre> <b>reqconfig</b> <i>configuration-name</i> </pre> | | > > > > | > > > > > > > > > | > | | < < > < | > > > > < > > > > > | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <pre> <b>reqconfig</b> <i>configuration-name</i> </pre> As of 2024-10-22, the configuration-name must be one of the following values: <table border=0 align="center"> <tr><td valign="top"> <ul> <li> css <li> header <li> mainmenu <li> footer <li> details <li> js <li> default-skin <li> logo-mimetype <li> logo-image <li> background-mimetype <li> background-image <li> icon-mimetype <li> icon-image <li> timeline-block-markup <li> timeline-date-format <li> timeline-default-style <ul></td><td valign="top"><ul> <li> timeline-dwelltime <li> timeline-closetime <li> timeline-hard-newlines <li> timeline-max-comment <li> timeline-plaintext <li> timeline-truncate-at-blank <li> timeline-tslink-info <li> timeline-utc <li> adunit <li> adunit-omit-if-admin <li> adunit-omit-if-user <li> default-csp <li> sitemap-extra <li> safe-html <li> th1-hooks <li> th1-uri-regexp <li> project-name <ul></td><td valign="top"><ul> <li> short-project-name <li> project-description <li> index-page <li> manifest <li> binary-glob <li> clean-glob <li> ignore-glob <li> keep-glob <li> crlf-glob <li> crnl-glob <li> encoding-glob <li> empty-dirs <li> <s title="removed 2020-08, version 2.12.1">allow-symlinks</s> <li> dotfiles <li> parent-project-code <li> parent-project-name <ul></td><td valign="top"><ul> <li> hash-policy <li> comment-format <li> mimetypes <li> forbid-delta-manifests <li> mv-rm-files <li> ticket-table <li> ticket-common <li> ticket-change <li> ticket-newpage <li> ticket-viewpage <li> ticket-editpage <li> ticket-reportlist <li> ticket-report-template <li> ticket-key-template <li> ticket-title-expr <li> ticket-closed-expr <ul></td><td valign="top"><ul> <li> user-color-map <li> xfer-common-script <li> xfer-push-script <li> xfer-commit-script <li> xfer-ticket-script <li> @reportfmt <li> @user <li> @concealed <li> @shun <li> @alias <li> @subscriber <li> @interwiki </ul></td></tr> </table> New configuration-names are likely to be added in future releases of Fossil. If the server receives a configuration-name that it does not understand, the entire reqconfig card is silently ignored. The reqconfig card might also be ignored if the user lacks sufficient privilege to |
| ︙ | ︙ | |||
859 860 861 862 863 864 865 | of clusters to find every artifact in the repository. <h2 id="strategies">5.0 Synchronization Strategies</h2> <h3 id="pull-strategy">5.1 Pull</h3> A typical pull operation proceeds as shown below. Details | | | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 | of clusters to find every artifact in the repository. <h2 id="strategies">5.0 Synchronization Strategies</h2> <h3 id="pull-strategy">5.1 Pull</h3> A typical pull operation proceeds as shown below. Details of the actual implementation may vary slightly but the gist of a pull is captured in the following steps: <ol> <li>The client sends login and pull cards. <li>The client sends a cookie card if it has previously received a cookie. <li>The client sends gimme cards for every phantom that it holds. <hr> |
| ︙ | ︙ | |||
1069 1070 1071 1072 1073 1074 1075 |
<li> The name of a temporary file into which the CMD should write the
reply sync protocol text, again without any HTTP headers
</ol>
In a complex debugging situation, you can run the command
"fossil sync --transport-command ./debugging_script" where
"debugging_script" is some script of your own that invokes
| | | 1089 1090 1091 1092 1093 1094 1095 1096 |
<li> The name of a temporary file into which the CMD should write the
reply sync protocol text, again without any HTTP headers
</ol>
In a complex debugging situation, you can run the command
"fossil sync --transport-command ./debugging_script" where
"debugging_script" is some script of your own that invokes
the anomalous behavior you are trying to debug.
|
Changes to www/tech_overview.wiki.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
* Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined
* Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems
if the ~/.fossil file does not already exist
* Otherwise, use the traditional unix name of "~/.fossil"
This algorithm is complex due to the need for historical compatibility.
Originally, the database was always just "~/.fossil". Then support
| | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
* Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined
* Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems
if the ~/.fossil file does not already exist
* Otherwise, use the traditional unix name of "~/.fossil"
This algorithm is complex due to the need for historical compatibility.
Originally, the database was always just "~/.fossil". Then support
for the FOSSIL_HOME environment variable was added. Later, support for the
[https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames]
was added. Each of these changes needed to continue to support legacy
installations.
On Windows, the configuration database is the first of the following
for which the corresponding environment variables exist:
|
| ︙ | ︙ |
Changes to www/th1.md.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | TH1 is a string-processing language. All values are strings. Any numerical operations are accomplished by converting from string to numeric, performing the computation, then converting the result back into a string. (This might seem inefficient, but it is faster than people imagine, and numeric computations do not come up very often for the kinds of work that TH1 does, so it has never been a factor.) | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | TH1 is a string-processing language. All values are strings. Any numerical operations are accomplished by converting from string to numeric, performing the computation, then converting the result back into a string. (This might seem inefficient, but it is faster than people imagine, and numeric computations do not come up very often for the kinds of work that TH1 does, so it has never been a factor.) A TH1 script consists of a sequence of commands. Each command is terminated by the first *unescaped* newline or ";" character. The text of the command (excluding the newline or semicolon terminator) is broken into space-separated tokens. The first token is the command name and subsequent tokens are the arguments. In this sense, TH1 syntax is similar to the familiar command-line shell syntax. A token is any sequence of characters other than whitespace and semicolons. |
| ︙ | ︙ | |||
286 287 288 289 290 291 292 | * capexpr CAPABILITY-EXPR The capability expression is a list. Each term of the list is a cluster of [capability letters](./caps/ref.html). The overall expression is true if any one term is true. A single term is true if all letters within that term are true. Or, if the term begins with "!", then the term is true | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | * capexpr CAPABILITY-EXPR The capability expression is a list. Each term of the list is a cluster of [capability letters](./caps/ref.html). The overall expression is true if any one term is true. A single term is true if all letters within that term are true. Or, if the term begins with "!", then the term is true if none of the terms are true. Or, if the term begins with "@" then the term is true if all of the capability letters in that term are available to the "anonymous" user. Or, if the term is "*" then it is always true. Examples: ``` |
| ︙ | ︙ | |||
381 382 383 384 385 386 387 |
compile-time by `FOSSIL_HASH_DIGITS_URL`, defaults to 16).
<a id="date"></a>TH1 date Command
-----------------------------------
* date ?-local?
| | | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
compile-time by `FOSSIL_HASH_DIGITS_URL`, defaults to 16).
<a id="date"></a>TH1 date Command
-----------------------------------
* date ?-local?
Return a string which is the current time and date. If the -local
option is used, the date appears using localtime instead of UTC.
<a id="decorate"></a>TH1 decorate Command
-------------------------------------------
* decorate STRING
|
| ︙ | ︙ | |||
568 569 570 571 572 573 574 | as a hidden element of the form. <a id="linecount"></a>TH1 linecount Command --------------------------------------------- * linecount STRING MAX MIN | | | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | as a hidden element of the form. <a id="linecount"></a>TH1 linecount Command --------------------------------------------- * linecount STRING MAX MIN Returns one more than the number of `\n` characters in STRING. But never returns less than MIN or more than MAX. <a id="markdown"></a>TH1 markdown Command ------------------------------------------- * markdown STRING |
| ︙ | ︙ | |||
840 841 842 843 844 845 846 | <a id="verifyCsrf"></a>TH1 verifyCsrf Command ----------------------------------------------- * verifyCsrf Before using the results of a form, first call this command to verify | | | 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 | <a id="verifyCsrf"></a>TH1 verifyCsrf Command ----------------------------------------------- * verifyCsrf Before using the results of a form, first call this command to verify that the Anti-CSRF token is present and is valid. If the Anti-CSRF token is missing or is incorrect, that indicates a cross-site scripting attack. If the event of an attack is detected, an error message is generated and all further processing is aborted. <a id="verifyLogin"></a>TH1 verifyLogin Command ------------------------------------------------- |
| ︙ | ︙ |
Changes to www/theory1.wiki.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | the Fossil implementation is enhanced, and the content is recomputed from the unchanging bag of artifacts. The local relational database is an implementation detail which currently happens to use SQLite. Another way to think of the relational tables in a Fossil repository is as an index for the artifacts. Without the relational tables, to generate a report like a timeline would require scanning every artifact - | | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | the Fossil implementation is enhanced, and the content is recomputed from the unchanging bag of artifacts. The local relational database is an implementation detail which currently happens to use SQLite. Another way to think of the relational tables in a Fossil repository is as an index for the artifacts. Without the relational tables, to generate a report like a timeline would require scanning every artifact - the equivalent of a full table scan. The relational tables hold pointers to the relevant artifacts in presorted order so that generating a timeline is much more efficient. So like an index in a relational database, the relational tables in a Fossil repository do not add any new information, they merely make the information in the artifacts faster and easier to look up. Fossil is not "based" on SQLite. Fossil simply exploits SQLite as a powerful tool to make the implementation easier. And Fossil doesn't use a distributed NoSQL database because Fossil is a distributed NoSQL database. That answers |
| ︙ | ︙ |
Changes to www/tickets.wiki.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | implement the change. The key corresponds to a field of the ticket that is modified. The value may either replace the earlier value for that key, or the value may be appended to the prior value. <h2>2.0 Ticket Tables</h2> The low-level artifact format for ticket content is tedious and | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | implement the change. The key corresponds to a field of the ticket that is modified. The value may either replace the earlier value for that key, or the value may be appended to the prior value. <h2>2.0 Ticket Tables</h2> The low-level artifact format for ticket content is tedious and cumbersome to access in real time. To facilitate reporting and display of tickets, the low-level artifact information is collected and summarized in a pair of SQL tables in each local repository. Display and reporting of tickets is accomplished by querying these two tables. Note that only the low-level ticket change artifacts are synced. The content of the two ticket tables can always be reconstructed from the ticket change artifacts. And, indeed, the reconstruction of the ticket |
| ︙ | ︙ | |||
189 190 191 192 193 194 195 | to be in HTML and others to be plaintext. Some site administrators wanted the ability to mix plaintext, wiki, and HTML comments and display each comment according to its chosen format. Hence, Fossil was enhanced to support the "new-style" tickets. The TICKETCHNG table was added to support new-style tickets. In the new style, comment text is stored with the "icomment" (for "Incremental Comment") | | | 189 190 191 192 193 194 195 196 197 198 199 | to be in HTML and others to be plaintext. Some site administrators wanted the ability to mix plaintext, wiki, and HTML comments and display each comment according to its chosen format. Hence, Fossil was enhanced to support the "new-style" tickets. The TICKETCHNG table was added to support new-style tickets. In the new style, comment text is stored with the "icomment" (for "Incremental Comment") key and appears separately, and with its own mimetype, in multiple rows of the TICKETCHNG table. It then falls to the TH1 script code on the View Ticket Page to query the TICKETCHNG table and extract and format the various comments in time stamp order. |
Changes to www/unvers.wiki.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | Notice that the "-u" option does not work on [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. The "-u" option is only available on "sync" and "clone". A rough equivalent of an unversioned pull would be the [/help?cmd=unversioned|fossil unversioned revert] command. The "unversioned revert" | | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | Notice that the "-u" option does not work on [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. The "-u" option is only available on "sync" and "clone". A rough equivalent of an unversioned pull would be the [/help?cmd=unversioned|fossil unversioned revert] command. The "unversioned revert" command causes the unversioned content on the local repository to be overwritten by the unversioned content found on the remote repository. Beware that because unversioned file sync is an uncommonly dangerous capability — there being no history to revert to in the case of human error — even the all-powerful Fossil "setup" user does not get unversioned file sync capability by default. See [./caps/admin-v-setup.md#dcap | this] for the full details, but the short-and-sweet takeaway is that a user needs the "y" capability on the |
| ︙ | ︙ |
Changes to www/userlinks.wiki.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
* The [./selfcheck.wiki | automatic self-check] mechanism
helps insure project integrity.
* Fossil contains a [./wikitheory.wiki | built-in wiki].
* An [./event.wiki | Event] is a special kind of wiki page associated
with a point in time rather than a name.
* [./settings.wiki | Settings] control the behaviour of Fossil.
* [./ssl.wiki | Use SSL] to encrypt communication with the server.
| | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
* The [./selfcheck.wiki | automatic self-check] mechanism
helps insure project integrity.
* Fossil contains a [./wikitheory.wiki | built-in wiki].
* An [./event.wiki | Event] is a special kind of wiki page associated
with a point in time rather than a name.
* [./settings.wiki | Settings] control the behaviour of Fossil.
* [./ssl.wiki | Use SSL] to encrypt communication with the server.
* The [https://fossil-scm.org/forum|Fossil forum] is, as of late-2024,
the project's central communication channel. The
[https://www.mail-archive.com/fossil-users@lists.fossil-scm.org
| read-only mailing list archives] house discussions spanning Fossil's
first decade.
* [./stats.wiki | Performance statistics] taken from real-world projects
hosted on Fossil.
* How to [./shunning.wiki | delete content] from a Fossil repository.
|
| ︙ | ︙ |
Changes to www/webui.wiki.
| ︙ | ︙ | |||
114 115 116 117 118 119 120 | on with separate sub-teams within your project team. The "Files" link on the menu allows you to browse through the <b>file hierarchy</b> of the project and to view complete changes histories on individual files, with hyperlinks to the check-ins that made those changes, and with diffs and annotated diffs between versions. | | | | | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | on with separate sub-teams within your project team. The "Files" link on the menu allows you to browse through the <b>file hierarchy</b> of the project and to view complete changes histories on individual files, with hyperlinks to the check-ins that made those changes, and with diffs and annotated diffs between versions. The web interface supports [./embeddeddoc.wiki | embedded documentation] files (usually in wiki format) that are checked into the project as part of the source tree. Such files can be viewed as if they were ordinary web pages. This document that you are now reading is an example of embedded documentation. <h2>Customizing The Web Interface Appearance</h2> Users with appropriate permissions can customize the look and feel of the web interface using the "Admin" link on the main menu of the web interface. Templates for the header and footer of each page can be edited, as can the CSS |
| ︙ | ︙ |
Changes to www/whyallinone.md.
| ︙ | ︙ | |||
139 140 141 142 143 144 145 |
services such as GitHub, GitLab, Bitbucket, and so forth, but that
just takes you back to point 3 above.
7. Hosting all of these elements within a single service gives a
consistent look-and-feel across all aspects of the project.
Skinning independent software packages’ web interfaces to make
| | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
services such as GitHub, GitLab, Bitbucket, and so forth, but that
just takes you back to point 3 above.
7. Hosting all of these elements within a single service gives a
consistent look-and-feel across all aspects of the project.
Skinning independent software packages’ web interfaces to make
them appear unified is more work than [skinning] everything once, as
in Fossil, and even then, you can’t make independently-developed
software look like it was produced by a single entity without
resorting to heroic levels of customization. If you use a separate
DVCS web front end, chat system, forum manager, documentation
system, ticket tracker, and so on, you are likely to be relegated
to simply matching colors and fonts; you *might* also get the
ability to add a common logo to the header of all of these
|
| ︙ | ︙ | |||
215 216 217 218 219 220 221 | optional. Its forum and chat features are disabled by default, and you can disable the ticket-tracking and wiki features with a quick configuration change to its [role-based access control system](./caps/). When you’re ready to turn these additional features on, you can do so with a few mouse clicks. Because Fossil is web-native out of the box, if you’ve delegated these | | > > | | > | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | optional. Its forum and chat features are disabled by default, and you can disable the ticket-tracking and wiki features with a quick configuration change to its [role-based access control system](./caps/). When you’re ready to turn these additional features on, you can do so with a few mouse clicks. Because Fossil is web-native out of the box, if you’ve delegated these features to outside systems to flesh out Git’s DVCS-only nature, you are free to do the same with Fossil. One of the many things the [skinning] facility allows is replacing the built-in links to the wiki, forum, ticket system, etc. with links to external systems. How easy those systems make it to link back into Fossil is up to their developers. [Discord]: https://discord.com/ [edoc]: ./embeddeddoc.wiki [Jira]: https://www.atlassian.com/software/jira [MediaWiki]: https://www.mediawiki.org/ [skinning]: ./customskin.md [Sphinx]: https://www.sphinx-doc.org/en/master/ [SSO]: https://en.wikipedia.org/wiki/Single_sign-on [srckl]: https://www.sqlite.org/src/ext/checklist/top/index |
Changes to www/wikitheory.wiki.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
* [/timeline?r=graph-test-branch] shows the text of the
[/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch]
wiki page at the top of the timeline
* [/info/19c60b7fc9e2] shows the text of the
[/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...]
wiki page in the "About" section.
| | | 82 83 84 85 86 87 88 89 90 |
* [/timeline?r=graph-test-branch] shows the text of the
[/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch]
wiki page at the top of the timeline
* [/info/19c60b7fc9e2] shows the text of the
[/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...]
wiki page in the "About" section.
These special wiki pages are very useful for recording historical
notes.
|