* sqlite - 3.28.0 - - update from CygWin Split package up with subpackages for various SQL extensions. * Added tcl to makedepends. to fix build error.
56392 lines
1.9 MiB
Plaintext
56392 lines
1.9 MiB
Plaintext
--- origsrc/sqlite-autoconf-3280000/, 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/, 2019-03-05 13:14:40.375391100 +0100
|
|
@@ -0,0 +1,896 @@
|
|
+#! /bin/sh
|
|
+# restart with tclsh \
|
|
+exec tclsh "$0" ${1+"$@"}
|
|
+package require sqlite3
|
|
+
|
|
+# Run this TCL script using an SQLite-enabled TCL interpreter to get a report
|
|
+# on how much disk space is used by a particular data to actually store data
|
|
+# versus how much space is unused.
|
|
+#
|
|
+# The dbstat virtual table is required.
|
|
+#
|
|
+
|
|
+if {[catch {
|
|
+
|
|
+# Argument $tname is the name of a table within the database opened by
|
|
+# database handle [db]. Return true if it is a WITHOUT ROWID table, or
|
|
+# false otherwise.
|
|
+#
|
|
+proc is_without_rowid {tname} {
|
|
+ set t [string map {' ''} $tname]
|
|
+ db eval "PRAGMA index_list = '$t'" o {
|
|
+ if {$o(origin) == "pk"} {
|
|
+ set n $o(name)
|
|
+ if {0==[db one { SELECT count(*) FROM sqlite_master WHERE name=$n }]} {
|
|
+ return 1
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return 0
|
|
+}
|
|
+
|
|
+# Read and run TCL commands from standard input. Used to implement
|
|
+# the --tclsh option.
|
|
+#
|
|
+proc tclsh {} {
|
|
+ set line {}
|
|
+ while {![eof stdin]} {
|
|
+ if {$line!=""} {
|
|
+ puts -nonewline "> "
|
|
+ } else {
|
|
+ puts -nonewline "% "
|
|
+ }
|
|
+ flush stdout
|
|
+ append line [gets stdin]
|
|
+ if {[info complete $line]} {
|
|
+ if {[catch {uplevel #0 $line} result]} {
|
|
+ puts stderr "Error: $result"
|
|
+ } elseif {$result!=""} {
|
|
+ puts $result
|
|
+ }
|
|
+ set line {}
|
|
+ } else {
|
|
+ append line \n
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+# Get the name of the database to analyze
|
|
+#
|
|
+proc usage {} {
|
|
+ set argv0 [file rootname [file tail [info script]]]
|
|
+ puts stderr "Usage: $argv0 ?--pageinfo? ?--stats? database-filename"
|
|
+ puts stderr {
|
|
+Analyze the SQLite3 database file specified by the "database-filename"
|
|
+argument and output a report detailing size and storage efficiency
|
|
+information for the database and its constituent tables and indexes.
|
|
+
|
|
+Options:
|
|
+
|
|
+ --pageinfo Show how each page of the database-file is used
|
|
+
|
|
+ --stats Output SQL text that creates a new database containing
|
|
+ statistics about the database that was analyzed
|
|
+
|
|
+ --tclsh Run the built-in TCL interpreter interactively (for debugging)
|
|
+
|
|
+ --version Show the version number of SQLite
|
|
+}
|
|
+ exit 1
|
|
+}
|
|
+set file_to_analyze {}
|
|
+set flags(-pageinfo) 0
|
|
+set flags(-stats) 0
|
|
+set flags(-debug) 0
|
|
+append argv {}
|
|
+foreach arg $argv {
|
|
+ if {[regexp {^-+pageinfo$} $arg]} {
|
|
+ set flags(-pageinfo) 1
|
|
+ } elseif {[regexp {^-+stats$} $arg]} {
|
|
+ set flags(-stats) 1
|
|
+ } elseif {[regexp {^-+debug$} $arg]} {
|
|
+ set flags(-debug) 1
|
|
+ } elseif {[regexp {^-+tclsh$} $arg]} {
|
|
+ tclsh
|
|
+ exit 0
|
|
+ } elseif {[regexp {^-+version$} $arg]} {
|
|
+ sqlite3 mem :memory:
|
|
+ puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
|
|
+ mem close
|
|
+ exit 0
|
|
+ } elseif {[regexp {^-} $arg]} {
|
|
+ puts stderr "Unknown option: $arg"
|
|
+ usage
|
|
+ } elseif {$file_to_analyze!=""} {
|
|
+ usage
|
|
+ } else {
|
|
+ set file_to_analyze $arg
|
|
+ }
|
|
+}
|
|
+if {$file_to_analyze==""} usage
|
|
+set root_filename $file_to_analyze
|
|
+regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
|
|
+if {![file exists $root_filename]} {
|
|
+ puts stderr "No such file: $root_filename"
|
|
+ exit 1
|
|
+}
|
|
+if {![file readable $root_filename]} {
|
|
+ puts stderr "File is not readable: $root_filename"
|
|
+ exit 1
|
|
+}
|
|
+set true_file_size [file size $root_filename]
|
|
+if {$true_file_size<512} {
|
|
+ puts stderr "Empty or malformed database: $root_filename"
|
|
+ exit 1
|
|
+}
|
|
+
|
|
+# Compute the total file size assuming test_multiplexor is being used.
|
|
+# Assume that SQLITE_ENABLE_8_3_NAMES might be enabled
|
|
+#
|
|
+set extension [file extension $root_filename]
|
|
+set pattern $root_filename
|
|
+append pattern {[0-3][0-9][0-9]}
|
|
+foreach f [glob -nocomplain $pattern] {
|
|
+ incr true_file_size [file size $f]
|
|
+ set extension {}
|
|
+}
|
|
+if {[string length $extension]>=2 && [string length $extension]<=4} {
|
|
+ set pattern [file rootname $root_filename]
|
|
+ append pattern {.[0-3][0-9][0-9]}
|
|
+ foreach f [glob -nocomplain $pattern] {
|
|
+ incr true_file_size [file size $f]
|
|
+ }
|
|
+}
|
|
+
|
|
+# Open the database
|
|
+#
|
|
+if {[catch {sqlite3 db $file_to_analyze -uri 1} msg]} {
|
|
+ puts stderr "error trying to open $file_to_analyze: $msg"
|
|
+ exit 1
|
|
+}
|
|
+if {$flags(-debug)} {
|
|
+ proc dbtrace {txt} {puts $txt; flush stdout;}
|
|
+ db trace ::dbtrace
|
|
+}
|
|
+
|
|
+# Make sure all required compile-time options are available
|
|
+#
|
|
+if {![db exists {SELECT 1 FROM pragma_compile_options
|
|
+ WHERE compile_options='ENABLE_DBSTAT_VTAB'}]} {
|
|
+ puts "The SQLite database engine linked with this application\
|
|
+ lacks required capabilities. Recompile using the\
|
|
+ -DSQLITE_ENABLE_DBSTAT_VTAB compile-time option to fix\
|
|
+ this problem."
|
|
+ exit 1
|
|
+}
|
|
+
|
|
+db eval {SELECT count(*) FROM sqlite_master}
|
|
+set pageSize [expr {wide([db one {PRAGMA page_size}])}]
|
|
+
|
|
+if {$flags(-pageinfo)} {
|
|
+ db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
|
+ db eval {SELECT name, path, pageno FROM temp.stat ORDER BY pageno} {
|
|
+ puts "$pageno $name $path"
|
|
+ }
|
|
+ exit 0
|
|
+}
|
|
+if {$flags(-stats)} {
|
|
+ db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
|
+ puts "BEGIN;"
|
|
+ puts "CREATE TABLE stats("
|
|
+ puts " name STRING, /* Name of table or index */"
|
|
+ puts " path INTEGER, /* Path to page from root */"
|
|
+ puts " pageno INTEGER, /* Page number */"
|
|
+ puts " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */"
|
|
+ puts " ncell INTEGER, /* Cells on page (0 for overflow) */"
|
|
+ puts " payload INTEGER, /* Bytes of payload on this page */"
|
|
+ puts " unused INTEGER, /* Bytes of unused space on this page */"
|
|
+ puts " mx_payload INTEGER, /* Largest payload size of all cells */"
|
|
+ puts " pgoffset INTEGER, /* Offset of page in file */"
|
|
+ puts " pgsize INTEGER /* Size of the page */"
|
|
+ puts ");"
|
|
+ db eval {SELECT quote(name) || ',' ||
|
|
+ quote(path) || ',' ||
|
|
+ quote(pageno) || ',' ||
|
|
+ quote(pagetype) || ',' ||
|
|
+ quote(ncell) || ',' ||
|
|
+ quote(payload) || ',' ||
|
|
+ quote(unused) || ',' ||
|
|
+ quote(mx_payload) || ',' ||
|
|
+ quote(pgoffset) || ',' ||
|
|
+ quote(pgsize) AS x FROM stat} {
|
|
+ puts "INSERT INTO stats VALUES($x);"
|
|
+ }
|
|
+ puts "COMMIT;"
|
|
+ exit 0
|
|
+}
|
|
+
|
|
+
|
|
+# In-memory database for collecting statistics. This script loops through
|
|
+# the tables and indices in the database being analyzed, adding a row for each
|
|
+# to an in-memory database (for which the schema is shown below). It then
|
|
+# queries the in-memory db to produce the space-analysis report.
|
|
+#
|
|
+sqlite3 mem :memory:
|
|
+if {$flags(-debug)} {
|
|
+ proc dbtrace {txt} {puts $txt; flush stdout;}
|
|
+ mem trace ::dbtrace
|
|
+}
|
|
+set tabledef {CREATE TABLE space_used(
|
|
+ name clob, -- Name of a table or index in the database file
|
|
+ tblname clob, -- Name of associated table
|
|
+ is_index boolean, -- TRUE if it is an index, false for a table
|
|
+ is_without_rowid boolean, -- TRUE if WITHOUT ROWID table
|
|
+ nentry int, -- Number of entries in the BTree
|
|
+ leaf_entries int, -- Number of leaf entries
|
|
+ depth int, -- Depth of the b-tree
|
|
+ payload int, -- Total amount of data stored in this table or index
|
|
+ ovfl_payload int, -- Total amount of data stored on overflow pages
|
|
+ ovfl_cnt int, -- Number of entries that use overflow
|
|
+ mx_payload int, -- Maximum payload size
|
|
+ int_pages int, -- Number of interior pages used
|
|
+ leaf_pages int, -- Number of leaf pages used
|
|
+ ovfl_pages int, -- Number of overflow pages used
|
|
+ int_unused int, -- Number of unused bytes on interior pages
|
|
+ leaf_unused int, -- Number of unused bytes on primary pages
|
|
+ ovfl_unused int, -- Number of unused bytes on overflow pages
|
|
+ gap_cnt int, -- Number of gaps in the page layout
|
|
+ compressed_size int -- Total bytes stored on disk
|
|
+);}
|
|
+mem eval $tabledef
|
|
+
|
|
+# Create a temporary "dbstat" virtual table.
|
|
+#
|
|
+db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
|
+db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat
|
|
+ ORDER BY name, path}
|
|
+db eval {DROP TABLE temp.stat}
|
|
+
|
|
+set isCompressed 0
|
|
+set compressOverhead 0
|
|
+set depth 0
|
|
+set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
|
|
+foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
|
+
|
|
+ set is_index [expr {$name!=$tblname}]
|
|
+ set is_without_rowid [is_without_rowid $name]
|
|
+ db eval {
|
|
+ SELECT
|
|
+ sum(ncell) AS nentry,
|
|
+ sum((pagetype=='leaf')*ncell) AS leaf_entries,
|
|
+ sum(payload) AS payload,
|
|
+ sum((pagetype=='overflow') * payload) AS ovfl_payload,
|
|
+ sum(path LIKE '%+000000') AS ovfl_cnt,
|
|
+ max(mx_payload) AS mx_payload,
|
|
+ sum(pagetype=='internal') AS int_pages,
|
|
+ sum(pagetype=='leaf') AS leaf_pages,
|
|
+ sum(pagetype=='overflow') AS ovfl_pages,
|
|
+ sum((pagetype=='internal') * unused) AS int_unused,
|
|
+ sum((pagetype=='leaf') * unused) AS leaf_unused,
|
|
+ sum((pagetype=='overflow') * unused) AS ovfl_unused,
|
|
+ sum(pgsize) AS compressed_size,
|
|
+ max((length(CASE WHEN path LIKE '%+%' THEN '' ELSE path END)+3)/4)
|
|
+ AS depth
|
|
+ FROM temp.dbstat WHERE name = $name
|
|
+ } break
|
|
+
|
|
+ set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
|
|
+ set storage [expr {$total_pages*$pageSize}]
|
|
+ if {!$isCompressed && $storage>$compressed_size} {
|
|
+ set isCompressed 1
|
|
+ set compressOverhead 14
|
|
+ }
|
|
+
|
|
+ # Column 'gap_cnt' is set to the number of non-contiguous entries in the
|
|
+ # list of pages visited if the b-tree structure is traversed in a top-down
|
|
+ # fashion (each node visited before its child-tree is passed). Any overflow
|
|
+ # chains present are traversed from start to finish before any child-tree
|
|
+ # is.
|
|
+ #
|
|
+ set gap_cnt 0
|
|
+ set prev 0
|
|
+ db eval {
|
|
+ SELECT pageno, pagetype FROM temp.dbstat
|
|
+ WHERE name=$name
|
|
+ ORDER BY pageno
|
|
+ } {
|
|
+ if {$prev>0 && $pagetype=="leaf" && $pageno!=$prev+1} {
|
|
+ incr gap_cnt
|
|
+ }
|
|
+ set prev $pageno
|
|
+ }
|
|
+ mem eval {
|
|
+ INSERT INTO space_used VALUES(
|
|
+ $name,
|
|
+ $tblname,
|
|
+ $is_index,
|
|
+ $is_without_rowid,
|
|
+ $nentry,
|
|
+ $leaf_entries,
|
|
+ $depth,
|
|
+ $payload,
|
|
+ $ovfl_payload,
|
|
+ $ovfl_cnt,
|
|
+ $mx_payload,
|
|
+ $int_pages,
|
|
+ $leaf_pages,
|
|
+ $ovfl_pages,
|
|
+ $int_unused,
|
|
+ $leaf_unused,
|
|
+ $ovfl_unused,
|
|
+ $gap_cnt,
|
|
+ $compressed_size
|
|
+ );
|
|
+ }
|
|
+}
|
|
+
|
|
+proc integerify {real} {
|
|
+ if {[string is double -strict $real]} {
|
|
+ return [expr {wide($real)}]
|
|
+ } else {
|
|
+ return 0
|
|
+ }
|
|
+}
|
|
+mem function int integerify
|
|
+
|
|
+# Quote a string for use in an SQL query. Examples:
|
|
+#
|
|
+# [quote {hello world}] == {'hello world'}
|
|
+# [quote {hello world's}] == {'hello world''s'}
|
|
+#
|
|
+proc quote {txt} {
|
|
+ return [string map {' ''} $txt]
|
|
+}
|
|
+
|
|
+# Output a title line
|
|
+#
|
|
+proc titleline {title} {
|
|
+ if {$title==""} {
|
|
+ puts [string repeat * 79]
|
|
+ } else {
|
|
+ set len [string length $title]
|
|
+ set stars [string repeat * [expr 79-$len-5]]
|
|
+ puts "*** $title $stars"
|
|
+ }
|
|
+}
|
|
+
|
|
+# Generate a single line of output in the statistics section of the
|
|
+# report.
|
|
+#
|
|
+proc statline {title value {extra {}}} {
|
|
+ set len [string length $title]
|
|
+ set dots [string repeat . [expr 50-$len]]
|
|
+ set len [string length $value]
|
|
+ set sp2 [string range { } $len end]
|
|
+ if {$extra ne ""} {
|
|
+ set extra " $extra"
|
|
+ }
|
|
+ puts "$title$dots $value$sp2$extra"
|
|
+}
|
|
+
|
|
+# Generate a formatted percentage value for $num/$denom
|
|
+#
|
|
+proc percent {num denom {of {}}} {
|
|
+ if {$denom==0.0} {return ""}
|
|
+ set v [expr {$num*100.0/$denom}]
|
|
+ set of {}
|
|
+ if {$v==100.0 || $v<0.001 || ($v>1.0 && $v<99.0)} {
|
|
+ return [format {%5.1f%% %s} $v $of]
|
|
+ } elseif {$v<0.1 || $v>99.9} {
|
|
+ return [format {%7.3f%% %s} $v $of]
|
|
+ } else {
|
|
+ return [format {%6.2f%% %s} $v $of]
|
|
+ }
|
|
+}
|
|
+
|
|
+proc divide {num denom} {
|
|
+ if {$denom==0} {return 0.0}
|
|
+ return [format %.2f [expr double($num)/double($denom)]]
|
|
+}
|
|
+
|
|
+# Generate a subreport that covers some subset of the database.
|
|
+# the $where clause determines which subset to analyze.
|
|
+#
|
|
+proc subreport {title where showFrag} {
|
|
+ global pageSize file_pgcnt compressOverhead
|
|
+
|
|
+ # Query the in-memory database for the sum of various statistics
|
|
+ # for the subset of tables/indices identified by the WHERE clause in
|
|
+ # $where. Note that even if the WHERE clause matches no rows, the
|
|
+ # following query returns exactly one row (because it is an aggregate).
|
|
+ #
|
|
+ # The results of the query are stored directly by SQLite into local
|
|
+ # variables (i.e. $nentry, $payload etc.).
|
|
+ #
|
|
+ mem eval "
|
|
+ SELECT
|
|
+ int(sum(
|
|
+ CASE WHEN (is_without_rowid OR is_index) THEN nentry
|
|
+ ELSE leaf_entries
|
|
+ END
|
|
+ )) AS nentry,
|
|
+ int(sum(payload)) AS payload,
|
|
+ int(sum(ovfl_payload)) AS ovfl_payload,
|
|
+ max(mx_payload) AS mx_payload,
|
|
+ int(sum(ovfl_cnt)) as ovfl_cnt,
|
|
+ int(sum(leaf_pages)) AS leaf_pages,
|
|
+ int(sum(int_pages)) AS int_pages,
|
|
+ int(sum(ovfl_pages)) AS ovfl_pages,
|
|
+ int(sum(leaf_unused)) AS leaf_unused,
|
|
+ int(sum(int_unused)) AS int_unused,
|
|
+ int(sum(ovfl_unused)) AS ovfl_unused,
|
|
+ int(sum(gap_cnt)) AS gap_cnt,
|
|
+ int(sum(compressed_size)) AS compressed_size,
|
|
+ int(max(depth)) AS depth,
|
|
+ count(*) AS cnt
|
|
+ FROM space_used WHERE $where" {} {}
|
|
+
|
|
+ # Output the sub-report title, nicely decorated with * characters.
|
|
+ #
|
|
+ puts ""
|
|
+ titleline $title
|
|
+ puts ""
|
|
+
|
|
+ # Calculate statistics and store the results in TCL variables, as follows:
|
|
+ #
|
|
+ # total_pages: Database pages consumed.
|
|
+ # total_pages_percent: Pages consumed as a percentage of the file.
|
|
+ # storage: Bytes consumed.
|
|
+ # payload_percent: Payload bytes used as a percentage of $storage.
|
|
+ # total_unused: Unused bytes on pages.
|
|
+ # avg_payload: Average payload per btree entry.
|
|
+ # avg_fanout: Average fanout for internal pages.
|
|
+ # avg_unused: Average unused bytes per btree entry.
|
|
+ # avg_meta: Average metadata overhead per entry.
|
|
+ # ovfl_cnt_percent: Percentage of btree entries that use overflow pages.
|
|
+ #
|
|
+ set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
|
|
+ set total_pages_percent [percent $total_pages $file_pgcnt]
|
|
+ set storage [expr {$total_pages*$pageSize}]
|
|
+ set payload_percent [percent $payload $storage {of storage consumed}]
|
|
+ set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
|
|
+ set avg_payload [divide $payload $nentry]
|
|
+ set avg_unused [divide $total_unused $nentry]
|
|
+ set total_meta [expr {$storage - $payload - $total_unused}]
|
|
+ set total_meta [expr {$total_meta + 4*($ovfl_pages - $ovfl_cnt)}]
|
|
+ set meta_percent [percent $total_meta $storage {of metadata}]
|
|
+ set avg_meta [divide $total_meta $nentry]
|
|
+ if {$int_pages>0} {
|
|
+ # TODO: Is this formula correct?
|
|
+ set nTab [mem eval "
|
|
+ SELECT count(*) FROM (
|
|
+ SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0
|
|
+ )
|
|
+ "]
|
|
+ set avg_fanout [mem eval "
|
|
+ SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used
|
|
+ WHERE $where
|
|
+ "]
|
|
+ set avg_fanout [format %.2f $avg_fanout]
|
|
+ }
|
|
+ set ovfl_cnt_percent [percent $ovfl_cnt $nentry {of all entries}]
|
|
+
|
|
+ # Print out the sub-report statistics.
|
|
+ #
|
|
+ statline {Percentage of total database} $total_pages_percent
|
|
+ statline {Number of entries} $nentry
|
|
+ statline {Bytes of storage consumed} $storage
|
|
+ if {$compressed_size!=$storage} {
|
|
+ set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}]
|
|
+ set pct [expr {$compressed_size*100.0/$storage}]
|
|
+ set pct [format {%5.1f%%} $pct]
|
|
+ statline {Bytes used after compression} $compressed_size $pct
|
|
+ }
|
|
+ statline {Bytes of payload} $payload $payload_percent
|
|
+ statline {Bytes of metadata} $total_meta $meta_percent
|
|
+ if {$cnt==1} {statline {B-tree depth} $depth}
|
|
+ statline {Average payload per entry} $avg_payload
|
|
+ statline {Average unused bytes per entry} $avg_unused
|
|
+ statline {Average metadata per entry} $avg_meta
|
|
+ if {[info exists avg_fanout]} {
|
|
+ statline {Average fanout} $avg_fanout
|
|
+ }
|
|
+ if {$showFrag && $total_pages>1} {
|
|
+ set fragmentation [percent $gap_cnt [expr {$total_pages-1}]]
|
|
+ statline {Non-sequential pages} $gap_cnt $fragmentation
|
|
+ }
|
|
+ statline {Maximum payload per entry} $mx_payload
|
|
+ statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
|
|
+ if {$int_pages>0} {
|
|
+ statline {Index pages used} $int_pages
|
|
+ }
|
|
+ statline {Primary pages used} $leaf_pages
|
|
+ statline {Overflow pages used} $ovfl_pages
|
|
+ statline {Total pages used} $total_pages
|
|
+ if {$int_unused>0} {
|
|
+ set int_unused_percent [
|
|
+ percent $int_unused [expr {$int_pages*$pageSize}] {of index space}]
|
|
+ statline "Unused bytes on index pages" $int_unused $int_unused_percent
|
|
+ }
|
|
+ statline "Unused bytes on primary pages" $leaf_unused [
|
|
+ percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}]
|
|
+ statline "Unused bytes on overflow pages" $ovfl_unused [
|
|
+ percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}]
|
|
+ statline "Unused bytes on all pages" $total_unused [
|
|
+ percent $total_unused $storage {of all space}]
|
|
+ return 1
|
|
+}
|
|
+
|
|
+# Calculate the overhead in pages caused by auto-vacuum.
|
|
+#
|
|
+# This procedure calculates and returns the number of pages used by the
|
|
+# auto-vacuum 'pointer-map'. If the database does not support auto-vacuum,
|
|
+# then 0 is returned. The two arguments are the size of the database file in
|
|
+# pages and the page size used by the database (in bytes).
|
|
+proc autovacuum_overhead {filePages pageSize} {
|
|
+
|
|
+ # Set $autovacuum to non-zero for databases that support auto-vacuum.
|
|
+ set autovacuum [db one {PRAGMA auto_vacuum}]
|
|
+
|
|
+ # If the database is not an auto-vacuum database or the file consists
|
|
+ # of one page only then there is no overhead for auto-vacuum. Return zero.
|
|
+ if {0==$autovacuum || $filePages==1} {
|
|
+ return 0
|
|
+ }
|
|
+
|
|
+ # The number of entries on each pointer map page. The layout of the
|
|
+ # database file is one pointer-map page, followed by $ptrsPerPage other
|
|
+ # pages, followed by a pointer-map page etc. The first pointer-map page
|
|
+ # is the second page of the file overall.
|
|
+ set ptrsPerPage [expr double($pageSize/5)]
|
|
+
|
|
+ # Return the number of pointer map pages in the database.
|
|
+ return [expr wide(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
|
|
+}
|
|
+
|
|
+
|
|
+# Calculate the summary statistics for the database and store the results
|
|
+# in TCL variables. They are output below. Variables are as follows:
|
|
+#
|
|
+# pageSize: Size of each page in bytes.
|
|
+# file_bytes: File size in bytes.
|
|
+# file_pgcnt: Number of pages in the file.
|
|
+# file_pgcnt2: Number of pages in the file (calculated).
|
|
+# av_pgcnt: Pages consumed by the auto-vacuum pointer-map.
|
|
+# av_percent: Percentage of the file consumed by auto-vacuum pointer-map.
|
|
+# inuse_pgcnt: Data pages in the file.
|
|
+# inuse_percent: Percentage of pages used to store data.
|
|
+# free_pgcnt: Free pages calculated as (<total pages> - <in-use pages>)
|
|
+# free_pgcnt2: Free pages in the file according to the file header.
|
|
+# free_percent: Percentage of file consumed by free pages (calculated).
|
|
+# free_percent2: Percentage of file consumed by free pages (header).
|
|
+# ntable: Number of tables in the db.
|
|
+# nindex: Number of indices in the db.
|
|
+# nautoindex: Number of indices created automatically.
|
|
+# nmanindex: Number of indices created manually.
|
|
+# user_payload: Number of bytes of payload in table btrees
|
|
+# (not including sqlite_master)
|
|
+# user_percent: $user_payload as a percentage of total file size.
|
|
+
|
|
+### The following, setting $file_bytes based on the actual size of the file
|
|
+### on disk, causes this tool to choke on zipvfs databases. So set it based
|
|
+### on the return of [PRAGMA page_count] instead.
|
|
+if 0 {
|
|
+ set file_bytes [file size $file_to_analyze]
|
|
+ set file_pgcnt [expr {$file_bytes/$pageSize}]
|
|
+}
|
|
+set file_pgcnt [db one {PRAGMA page_count}]
|
|
+set file_bytes [expr {$file_pgcnt * $pageSize}]
|
|
+
|
|
+set av_pgcnt [autovacuum_overhead $file_pgcnt $pageSize]
|
|
+set av_percent [percent $av_pgcnt $file_pgcnt]
|
|
+
|
|
+set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
|
|
+set inuse_pgcnt [expr wide([mem eval $sql])]
|
|
+set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
|
|
+
|
|
+set free_pgcnt [expr {$file_pgcnt-$inuse_pgcnt-$av_pgcnt}]
|
|
+set free_percent [percent $free_pgcnt $file_pgcnt]
|
|
+set free_pgcnt2 [db one {PRAGMA freelist_count}]
|
|
+set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
|
|
+
|
|
+set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}]
|
|
+
|
|
+set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}]
|
|
+set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}]
|
|
+set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
|
|
+set nautoindex [db eval $sql]
|
|
+set nmanindex [expr {$nindex-$nautoindex}]
|
|
+
|
|
+# set total_payload [mem eval "SELECT sum(payload) FROM space_used"]
|
|
+set user_payload [mem one {SELECT int(sum(payload)) FROM space_used
|
|
+ WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}]
|
|
+set user_percent [percent $user_payload $file_bytes]
|
|
+
|
|
+# Output the summary statistics calculated above.
|
|
+#
|
|
+puts "/** Disk-Space Utilization Report For $root_filename"
|
|
+puts ""
|
|
+statline {Page size in bytes} $pageSize
|
|
+statline {Pages in the whole file (measured)} $file_pgcnt
|
|
+statline {Pages in the whole file (calculated)} $file_pgcnt2
|
|
+statline {Pages that store data} $inuse_pgcnt $inuse_percent
|
|
+statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2
|
|
+statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
|
|
+statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
|
|
+statline {Number of tables in the database} $ntable
|
|
+statline {Number of indices} $nindex
|
|
+statline {Number of defined indices} $nmanindex
|
|
+statline {Number of implied indices} $nautoindex
|
|
+if {$isCompressed} {
|
|
+ statline {Size of uncompressed content in bytes} $file_bytes
|
|
+ set efficiency [percent $true_file_size $file_bytes]
|
|
+ statline {Size of compressed file on disk} $true_file_size $efficiency
|
|
+} else {
|
|
+ statline {Size of the file in bytes} $file_bytes
|
|
+}
|
|
+statline {Bytes of user payload stored} $user_payload $user_percent
|
|
+
|
|
+# Output table rankings
|
|
+#
|
|
+puts ""
|
|
+titleline "Page counts for all tables with their indices"
|
|
+puts ""
|
|
+mem eval {SELECT tblname, count(*) AS cnt,
|
|
+ int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
|
|
+ FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
|
|
+ statline [string toupper $tblname] $size [percent $size $file_pgcnt]
|
|
+}
|
|
+puts ""
|
|
+titleline "Page counts for all tables and indices separately"
|
|
+puts ""
|
|
+mem eval {
|
|
+ SELECT
|
|
+ upper(name) AS nm,
|
|
+ int(int_pages+leaf_pages+ovfl_pages) AS size
|
|
+ FROM space_used
|
|
+ ORDER BY size+0 DESC, name} {} {
|
|
+ statline $nm $size [percent $size $file_pgcnt]
|
|
+}
|
|
+if {$isCompressed} {
|
|
+ puts ""
|
|
+ titleline "Bytes of disk space used after compression"
|
|
+ puts ""
|
|
+ set csum 0
|
|
+ mem eval {SELECT tblname,
|
|
+ int(sum(compressed_size)) +
|
|
+ $compressOverhead*sum(int_pages+leaf_pages+ovfl_pages)
|
|
+ AS csize
|
|
+ FROM space_used GROUP BY tblname ORDER BY csize+0 DESC, tblname} {} {
|
|
+ incr csum $csize
|
|
+ statline [string toupper $tblname] $csize [percent $csize $true_file_size]
|
|
+ }
|
|
+ set overhead [expr {$true_file_size - $csum}]
|
|
+ if {$overhead>0} {
|
|
+ statline {Header and free space} $overhead [percent $overhead $true_file_size]
|
|
+ }
|
|
+}
|
|
+
|
|
+# Output subreports
|
|
+#
|
|
+if {$nindex>0} {
|
|
+ subreport {All tables and indices} 1 0
|
|
+}
|
|
+subreport {All tables} {NOT is_index} 0
|
|
+if {$nindex>0} {
|
|
+ subreport {All indices} {is_index} 0
|
|
+}
|
|
+foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
|
|
+ ORDER BY name}] {
|
|
+ set qn [quote $tbl]
|
|
+ set name [string toupper $tbl]
|
|
+ set n [mem eval {SELECT count(*) FROM space_used WHERE tblname=$tbl}]
|
|
+ if {$n>1} {
|
|
+ set idxlist [mem eval "SELECT name FROM space_used
|
|
+ WHERE tblname='$qn' AND is_index
|
|
+ ORDER BY 1"]
|
|
+ subreport "Table $name and all its indices" "tblname='$qn'" 0
|
|
+ subreport "Table $name w/o any indices" "name='$qn'" 1
|
|
+ if {[llength $idxlist]>1} {
|
|
+ subreport "Indices of table $name" "tblname='$qn' AND is_index" 0
|
|
+ }
|
|
+ foreach idx $idxlist {
|
|
+ set qidx [quote $idx]
|
|
+ subreport "Index [string toupper $idx] of table $name" "name='$qidx'" 1
|
|
+ }
|
|
+ } else {
|
|
+ subreport "Table $name" "name='$qn'" 1
|
|
+ }
|
|
+}
|
|
+
|
|
+# Output instructions on what the numbers above mean.
|
|
+#
|
|
+puts ""
|
|
+titleline Definitions
|
|
+puts {
|
|
+Page size in bytes
|
|
+
|
|
+ The number of bytes in a single page of the database file.
|
|
+ Usually 1024.
|
|
+
|
|
+Number of pages in the whole file
|
|
+}
|
|
+puts " The number of $pageSize-byte pages that go into forming the complete
|
|
+ database"
|
|
+puts {
|
|
+Pages that store data
|
|
+
|
|
+ The number of pages that store data, either as primary B*Tree pages or
|
|
+ as overflow pages. The number at the right is the data pages divided by
|
|
+ the total number of pages in the file.
|
|
+
|
|
+Pages on the freelist
|
|
+
|
|
+ The number of pages that are not currently in use but are reserved for
|
|
+ future use. The percentage at the right is the number of freelist pages
|
|
+ divided by the total number of pages in the file.
|
|
+
|
|
+Pages of auto-vacuum overhead
|
|
+
|
|
+ The number of pages that store data used by the database to facilitate
|
|
+ auto-vacuum. This is zero for databases that do not support auto-vacuum.
|
|
+
|
|
+Number of tables in the database
|
|
+
|
|
+ The number of tables in the database, including the SQLITE_MASTER table
|
|
+ used to store schema information.
|
|
+
|
|
+Number of indices
|
|
+
|
|
+ The total number of indices in the database.
|
|
+
|
|
+Number of defined indices
|
|
+
|
|
+ The number of indices created using an explicit CREATE INDEX statement.
|
|
+
|
|
+Number of implied indices
|
|
+
|
|
+ The number of indices used to implement PRIMARY KEY or UNIQUE constraints
|
|
+ on tables.
|
|
+
|
|
+Size of the file in bytes
|
|
+
|
|
+ The total amount of disk space used by the entire database files.
|
|
+
|
|
+Bytes of user payload stored
|
|
+
|
|
+ The total number of bytes of user payload stored in the database. The
|
|
+ schema information in the SQLITE_MASTER table is not counted when
|
|
+ computing this number. The percentage at the right shows the payload
|
|
+ divided by the total file size.
|
|
+
|
|
+Percentage of total database
|
|
+
|
|
+ The amount of the complete database file that is devoted to storing
|
|
+ information described by this category.
|
|
+
|
|
+Number of entries
|
|
+
|
|
+ The total number of B-Tree key/value pairs stored under this category.
|
|
+
|
|
+Bytes of storage consumed
|
|
+
|
|
+ The total amount of disk space required to store all B-Tree entries
|
|
+ under this category. The is the total number of pages used times
|
|
+ the pages size.
|
|
+
|
|
+Bytes of payload
|
|
+
|
|
+ The amount of payload stored under this category. Payload is the data
|
|
+ part of table entries and the key part of index entries. The percentage
|
|
+ at the right is the bytes of payload divided by the bytes of storage
|
|
+ consumed.
|
|
+
|
|
+Bytes of metadata
|
|
+
|
|
+ The amount of formatting and structural information stored in the
|
|
+ table or index. Metadata includes the btree page header, the cell pointer
|
|
+ array, the size field for each cell, the left child pointer or non-leaf
|
|
+ cells, the overflow pointers for overflow cells, and the rowid value for
|
|
+ rowid table cells. In other words, metadata is everything that is neither
|
|
+ unused space nor content. The record header in the payload is counted as
|
|
+ content, not metadata.
|
|
+
|
|
+Average payload per entry
|
|
+
|
|
+ The average amount of payload on each entry. This is just the bytes of
|
|
+ payload divided by the number of entries.
|
|
+
|
|
+Average unused bytes per entry
|
|
+
|
|
+ The average amount of free space remaining on all pages under this
|
|
+ category on a per-entry basis. This is the number of unused bytes on
|
|
+ all pages divided by the number of entries.
|
|
+
|
|
+Non-sequential pages
|
|
+
|
|
+ The number of pages in the table or index that are out of sequence.
|
|
+ Many filesystems are optimized for sequential file access so a small
|
|
+ number of non-sequential pages might result in faster queries,
|
|
+ especially for larger database files that do not fit in the disk cache.
|
|
+ Note that after running VACUUM, the root page of each table or index is
|
|
+ at the beginning of the database file and all other pages are in a
|
|
+ separate part of the database file, resulting in a single non-
|
|
+ sequential page.
|
|
+
|
|
+Maximum payload per entry
|
|
+
|
|
+ The largest payload size of any entry.
|
|
+
|
|
+Entries that use overflow
|
|
+
|
|
+ The number of entries that user one or more overflow pages.
|
|
+
|
|
+Total pages used
|
|
+
|
|
+ This is the number of pages used to hold all information in the current
|
|
+ category. This is the sum of index, primary, and overflow pages.
|
|
+
|
|
+Index pages used
|
|
+
|
|
+ This is the number of pages in a table B-tree that hold only key (rowid)
|
|
+ information and no data.
|
|
+
|
|
+Primary pages used
|
|
+
|
|
+ This is the number of B-tree pages that hold both key and data.
|
|
+
|
|
+Overflow pages used
|
|
+
|
|
+ The total number of overflow pages used for this category.
|
|
+
|
|
+Unused bytes on index pages
|
|
+
|
|
+ The total number of bytes of unused space on all index pages. The
|
|
+ percentage at the right is the number of unused bytes divided by the
|
|
+ total number of bytes on index pages.
|
|
+
|
|
+Unused bytes on primary pages
|
|
+
|
|
+ The total number of bytes of unused space on all primary pages. The
|
|
+ percentage at the right is the number of unused bytes divided by the
|
|
+ total number of bytes on primary pages.
|
|
+
|
|
+Unused bytes on overflow pages
|
|
+
|
|
+ The total number of bytes of unused space on all overflow pages. The
|
|
+ percentage at the right is the number of unused bytes divided by the
|
|
+ total number of bytes on overflow pages.
|
|
+
|
|
+Unused bytes on all pages
|
|
+
|
|
+ The total number of bytes of unused space on all primary and overflow
|
|
+ pages. The percentage at the right is the number of unused bytes
|
|
+ divided by the total number of bytes.
|
|
+}
|
|
+
|
|
+# Output a dump of the in-memory database. This can be used for more
|
|
+# complex offline analysis.
|
|
+#
|
|
+titleline {}
|
|
+puts "The entire text of this report can be sourced into any SQL database"
|
|
+puts "engine for further analysis. All of the text above is an SQL comment."
|
|
+puts "The data used to generate this report follows:"
|
|
+puts "*/"
|
|
+puts "BEGIN;"
|
|
+puts $tabledef
|
|
+unset -nocomplain x
|
|
+mem eval {SELECT * FROM space_used} x {
|
|
+ puts -nonewline "INSERT INTO space_used VALUES"
|
|
+ set sep (
|
|
+ foreach col $x(*) {
|
|
+ set v $x($col)
|
|
+ if {$v=="" || ![string is double $v]} {set v '[quote $v]'}
|
|
+ puts -nonewline $sep$v
|
|
+ set sep ,
|
|
+ }
|
|
+ puts ");"
|
|
+}
|
|
+puts "COMMIT;"
|
|
+
|
|
+} err]} {
|
|
+ puts "ERROR: $err"
|
|
+ puts $errorInfo
|
|
+ exit 1
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/Makefile.am 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/Makefile.am 2019-03-05 13:14:40.387107300 +0100
|
|
@@ -1,19 +1,104 @@
|
|
|
|
-AM_CFLAGS = @BUILD_CFLAGS@
|
|
-lib_LTLIBRARIES = libsqlite3.la
|
|
+AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @SESSION_FLAGS@ -DUSE_SYSTEM_SQLITE=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_DBSTAT_VTAB=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -DSQLITE_OMIT_LOOKASIDE=1 -DSQLITE_SECURE_DELETE=1 -DSQLITE_USE_ALLOCA=1
|
|
+
|
|
+lib_LTLIBRARIES = libsqlite3.la libsqlite3amatch.la libsqlite3anycollseq.la \
|
|
+ libsqlite3carray.la libsqlite3closure.la libsqlite3compress.la \
|
|
+ libsqlite3completion.la libsqlite3csv.la libsqlite3eval.la \
|
|
+ libsqlite3fileio.la libsqlite3fuzzer.la libsqlite3ieee754.la \
|
|
+ libsqlite3memvfs.la libsqlite3nextchar.la libsqlite3percentile.la \
|
|
+ libsqlite3rbu.la libsqlite3regexp.la libsqlite3remember.la \
|
|
+ libsqlite3rot13.la libsqlite3series.la libsqlite3sha1.la \
|
|
+ libsqlite3shathree.la libsqlite3showauth.la libsqlite3spellfix.la \
|
|
+ libsqlite3stmt.la libsqlite3totype.la libsqlite3unionvtab.la \
|
|
+ libsqlite3vfslog.la libsqlite3vfsstat.la libsqlite3vtshim.la \
|
|
+ libsqlite3wholenumber.la libsqlite3icu.la
|
|
libsqlite3_la_SOURCES = sqlite3.c
|
|
-libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8
|
|
+libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3amatch_la_SOURCES = amatch.c
|
|
+libsqlite3amatch_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3anycollseq_la_SOURCES = anycollseq.c
|
|
+libsqlite3anycollseq_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3carray_la_SOURCES = carray.c
|
|
+libsqlite3carray_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3closure_la_SOURCES = closure.c
|
|
+libsqlite3closure_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3compress_la_SOURCES = compress.c
|
|
+libsqlite3compress_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc -lz
|
|
+libsqlite3completion_la_SOURCES = completion.c
|
|
+libsqlite3completion_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3csv_la_SOURCES = csv.c
|
|
+libsqlite3csv_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3eval_la_SOURCES = eval.c
|
|
+libsqlite3eval_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3fileio_la_SOURCES = fileio.c
|
|
+libsqlite3fileio_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3fuzzer_la_SOURCES = fuzzer.c
|
|
+libsqlite3fuzzer_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3icu_la_SOURCES = icu.c
|
|
+libsqlite3icu_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc -licui18n -licuuc
|
|
+libsqlite3ieee754_la_SOURCES = ieee754.c
|
|
+libsqlite3ieee754_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3memvfs_la_SOURCES = memvfs.c
|
|
+libsqlite3memvfs_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3nextchar_la_SOURCES = nextchar.c
|
|
+libsqlite3nextchar_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3percentile_la_SOURCES = percentile.c
|
|
+libsqlite3percentile_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3rbu_la_SOURCES = sqlite3rbu.c
|
|
+libsqlite3rbu_la_LIBADD = $(top_builddir)/libsqlite3.la
|
|
+libsqlite3rbu_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3rbu_la_DEPENDENCIES = $(top_builddir)/libsqlite3.la
|
|
+libsqlite3regexp_la_SOURCES = regexp.c
|
|
+libsqlite3regexp_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3remember_la_SOURCES = remember.c
|
|
+libsqlite3remember_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3rot13_la_SOURCES = rot13.c
|
|
+libsqlite3rot13_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3sha1_la_SOURCES = sha1.c
|
|
+libsqlite3sha1_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3shathree_la_SOURCES = shathree.c
|
|
+libsqlite3shathree_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3series_la_SOURCES = series.c
|
|
+libsqlite3series_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3showauth_la_SOURCES = showauth.c
|
|
+libsqlite3showauth_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3spellfix_la_SOURCES = spellfix.c
|
|
+libsqlite3spellfix_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3stmt_la_SOURCES = stmt.c
|
|
+libsqlite3stmt_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3totype_la_SOURCES = totype.c
|
|
+libsqlite3totype_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3unionvtab_la_SOURCES = unionvtab.c
|
|
+libsqlite3unionvtab_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3vfslog_la_SOURCES = vfslog.c
|
|
+libsqlite3vfslog_la_LIBADD = $(top_builddir)/libsqlite3.la
|
|
+libsqlite3vfslog_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3vfslog_la_DEPENDENCIES = $(top_builddir)/libsqlite3.la
|
|
+libsqlite3vtshim_la_SOURCES = vtshim.c
|
|
+libsqlite3vtshim_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3vfsstat_la_SOURCES = vfsstat.c
|
|
+libsqlite3vfsstat_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
+libsqlite3wholenumber_la_SOURCES = wholenumber.c
|
|
+libsqlite3wholenumber_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
|
|
|
-bin_PROGRAMS = sqlite3
|
|
+bin_PROGRAMS = sqlite3 sqldiff rbu
|
|
sqlite3_SOURCES = shell.c sqlite3.h
|
|
EXTRA_sqlite3_SOURCES = sqlite3.c
|
|
-sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
|
|
-sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
|
|
-sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
|
+sqlite3_LDADD = $(top_builddir)/libsqlite3vfslog.la @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
|
|
+sqlite3_DEPENDENCIES = $(top_builddir)/libsqlite3vfslog.la @EXTRA_SHELL_OBJ@
|
|
+sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+sqldiff_SOURCES = sqldiff.c sqlite3.h
|
|
+sqldiff_LDADD = @EXTRA_SHELL_OBJ@
|
|
+sqldiff_DEPENDENCIES = @EXTRA_SHELL_OBJ@
|
|
+sqldiff_CFLAGS = $(AM_CFLAGS)
|
|
+rbu_SOURCES = rbu.c sqlite3.h
|
|
+rbu_LDADD = $(top_builddir)/libsqlite3rbu.la @EXTRA_SHELL_OBJ@
|
|
+rbu_DEPENDENCIES = $(top_builddir)/libsqlite3rbu.la @EXTRA_SHELL_OBJ@
|
|
+rbu_CFLAGS = $(AM_CFLAGS)
|
|
|
|
include_HEADERS = sqlite3.h sqlite3ext.h
|
|
|
|
-EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs Makefile.fallback
|
|
+EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs
|
|
pkgconfigdir = ${libdir}/pkgconfig
|
|
pkgconfig_DATA = sqlite3.pc
|
|
|
|
--- origsrc/sqlite-autoconf-3280000/Makefile.msc 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/Makefile.msc 2019-03-05 13:14:40.396874500 +0100
|
|
@@ -277,13 +277,6 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
|
|
!IF $(MINIMAL_AMALGAMATION)==0
|
|
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
|
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_INTROSPECTION_PRAGMAS=1
|
|
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
|
|
!ENDIF
|
|
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
|
!ENDIF
|
|
@@ -433,9 +426,9 @@ UCRTLIBPATH = $(UCRTLIBPATH:\\=\)
|
|
# will run on the platform that is doing the build.
|
|
#
|
|
!IF $(USE_FULLWARN)!=0
|
|
-BCC = $(NCC) -nologo -W4 -Fd$*.pdb $(CCOPTS) $(BCCOPTS)
|
|
+BCC = $(NCC) -nologo -W4 $(CCOPTS) $(BCCOPTS)
|
|
!ELSE
|
|
-BCC = $(NCC) -nologo -W3 -Fd$*.pdb $(CCOPTS) $(BCCOPTS)
|
|
+BCC = $(NCC) -nologo -W3 $(CCOPTS) $(BCCOPTS)
|
|
!ENDIF
|
|
|
|
# Check if assembly code listings should be generated for the source
|
|
@@ -568,7 +561,6 @@ SHELL_CORE_DEP =
|
|
!ENDIF
|
|
!ENDIF
|
|
|
|
-
|
|
# This is the core library that the shell executable should link with.
|
|
#
|
|
!IFNDEF SHELL_CORE_LIB
|
|
@@ -808,7 +800,7 @@ BCC = $(BCC) -Zi
|
|
# Command line prefixes for compiling code, compiling resources,
|
|
# linking, etc.
|
|
#
|
|
-LTCOMPILE = $(TCC) -Fo$@ -Fd$*.pdb
|
|
+LTCOMPILE = $(TCC) -Fo$@
|
|
LTRCOMPILE = $(RCC) -r
|
|
LTLIB = lib.exe
|
|
LTLINK = $(TCC) -Fe$@
|
|
@@ -816,7 +808,7 @@ LTLINK = $(TCC) -Fe$@
|
|
# If requested, link to the RPCRT4 library.
|
|
#
|
|
!IF $(USE_RPCRT4_LIB)!=0
|
|
-LTLIBS = $(LTLIBS) rpcrt4.lib
|
|
+LTLINK = $(LTLINK) rpcrt4.lib
|
|
!ENDIF
|
|
|
|
# If a platform was set, force the linker to target that.
|
|
@@ -826,11 +818,6 @@ LTLIBS = $(LTLIBS) rpcrt4.lib
|
|
!IFDEF PLATFORM
|
|
LTLINKOPTS = /NOLOGO /MACHINE:$(PLATFORM)
|
|
LTLIBOPTS = /NOLOGO /MACHINE:$(PLATFORM)
|
|
-!ELSEIF "$(VISUALSTUDIOVERSION)"=="12.0" || \
|
|
- "$(VISUALSTUDIOVERSION)"=="14.0" || \
|
|
- "$(VISUALSTUDIOVERSION)"=="15.0"
|
|
-LTLINKOPTS = /NOLOGO /MACHINE:x86
|
|
-LTLIBOPTS = /NOLOGO /MACHINE:x86
|
|
!ELSE
|
|
LTLINKOPTS = /NOLOGO
|
|
LTLIBOPTS = /NOLOGO
|
|
@@ -940,25 +927,14 @@ LIBRESOBJS =
|
|
# when the shell is not being dynamically linked.
|
|
#
|
|
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
|
|
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
|
|
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
|
|
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
|
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_DESERIALIZE=1
|
|
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
!ENDIF
|
|
|
|
|
|
# This is the default Makefile target. The objects listed here
|
|
# are what get build when you type just "make" with no arguments.
|
|
#
|
|
-core: dll shell
|
|
-
|
|
-# Targets that require the Tcl library.
|
|
-#
|
|
-tcl: $(ALL_TCL_TARGETS)
|
|
-
|
|
-# This Makefile target builds all of the standard binaries.
|
|
-#
|
|
-all: core tcl
|
|
+all: dll shell
|
|
|
|
# Dynamic link library section.
|
|
#
|
|
@@ -978,11 +954,11 @@ Replace.exe:
|
|
sqlite3.def: Replace.exe $(LIBOBJ)
|
|
echo EXPORTS > sqlite3.def
|
|
dumpbin /all $(LIBOBJ) \
|
|
- | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
|
+ | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
|
| sort >> sqlite3.def
|
|
|
|
-$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
|
|
- $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \
|
|
+$(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
|
|
+ $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\shell.c $(SHELL_CORE_SRC) \
|
|
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
|
|
|
|
|
@@ -997,7 +973,7 @@ sqlite3.lo: $(SQLITE3C)
|
|
!IF $(USE_RC)!=0
|
|
_HASHCHAR=^#
|
|
!IF ![echo !IFNDEF VERSION > rcver.vc] && \
|
|
- ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| "%SystemRoot%\System32\find.exe" "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
|
|
+ ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| find "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
|
|
![echo !ENDIF >> rcver.vc]
|
|
!INCLUDE rcver.vc
|
|
!ENDIF
|
|
--- origsrc/sqlite-autoconf-3280000/README.first 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/README.first 2019-03-05 13:14:40.406629500 +0100
|
|
@@ -0,0 +1,11 @@
|
|
+This directory contains components use to build an autoconf-ready package
|
|
+of the SQLite amalgamation: sqlite-autoconf-30XXXXXX.tar.gz
|
|
+
|
|
+To build the autoconf amalgamation, run from the top-level:
|
|
+
|
|
+ ./configure
|
|
+ make amalgamation-tarball
|
|
+
|
|
+The amalgamation-tarball target (also available in "main.mk") runs the
|
|
+script tool/mkautoconfamal.sh which does the work. Refer to that script
|
|
+for details.
|
|
--- origsrc/sqlite-autoconf-3280000/amatch.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/amatch.c 2019-05-14 16:22:31.215635100 +0200
|
|
@@ -0,0 +1,1515 @@
|
|
+/*
|
|
+** 2013-03-14
|
|
+**
|
|
+** 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 for a demonstration virtual table that finds
|
|
+** "approximate matches" - strings from a finite set that are nearly the
|
|
+** same as a single input string. The virtual table is called "amatch".
|
|
+**
|
|
+** A amatch virtual table is created like this:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE f USING approximate_match(
|
|
+** vocabulary_table=<tablename>, -- V
|
|
+** vocabulary_word=<columnname>, -- W
|
|
+** vocabulary_language=<columnname>, -- L
|
|
+** edit_distances=<edit-cost-table>
|
|
+** );
|
|
+**
|
|
+** When it is created, the new amatch table must be supplied with the
|
|
+** the name of a table V and columns V.W and V.L such that
|
|
+**
|
|
+** SELECT W FROM V WHERE L=$language
|
|
+**
|
|
+** returns the allowed vocabulary for the match. If the "vocabulary_language"
|
|
+** or L columnname is left unspecified or is an empty string, then no
|
|
+** filtering of the vocabulary by language is performed.
|
|
+**
|
|
+** For efficiency, it is essential that the vocabulary table be indexed:
|
|
+**
|
|
+** CREATE vocab_index ON V(W)
|
|
+**
|
|
+** A separate edit-cost-table provides scoring information that defines
|
|
+** what it means for one string to be "close" to another.
|
|
+**
|
|
+** The edit-cost-table must contain exactly four columns (more precisely,
|
|
+** the statement "SELECT * FROM <edit-cost-table>" must return records
|
|
+** that consist of four columns). It does not matter what the columns are
|
|
+** named.
|
|
+**
|
|
+** Each row in the edit-cost-table represents a single character
|
|
+** transformation going from user input to the vocabulary. The leftmost
|
|
+** column of the row (column 0) contains an integer identifier of the
|
|
+** language to which the transformation rule belongs (see "MULTIPLE LANGUAGES"
|
|
+** below). The second column of the row (column 1) contains the input
|
|
+** character or characters - the characters of user input. The third
|
|
+** column contains characters as they appear in the vocabulary table.
|
|
+** And the fourth column contains the integer cost of making the
|
|
+** transformation. For example:
|
|
+**
|
|
+** CREATE TABLE f_data(iLang, cFrom, cTo, Cost);
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
|
|
+**
|
|
+** The first row inserted into the edit-cost-table by the SQL script
|
|
+** above indicates that the cost of having an extra 'a' in the vocabulary
|
|
+** table that is missing in the user input 100. (All costs are integers.
|
|
+** Overall cost must not exceed 16777216.) The second INSERT statement
|
|
+** creates a rule saying that the cost of having a single letter 'b' in
|
|
+** user input which is missing in the vocabulary table is 87. The third
|
|
+** INSERT statement mean that the cost of matching an 'o' in user input
|
|
+** against an 'oe' in the vocabulary table is 38. And so forth.
|
|
+**
|
|
+** The following rules are special:
|
|
+**
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '?', '', 97);
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '', '?', 98);
|
|
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '?', '?', 99);
|
|
+**
|
|
+** The '?' to '' rule is the cost of having any single character in the input
|
|
+** that is not found in the vocabular. The '' to '?' rule is the cost of
|
|
+** having a character in the vocabulary table that is missing from input.
|
|
+** And the '?' to '?' rule is the cost of doing an arbitrary character
|
|
+** substitution. These three generic rules apply across all languages.
|
|
+** In other words, the iLang field is ignored for the generic substitution
|
|
+** rules. If more than one cost is given for a generic substitution rule,
|
|
+** then the lowest cost is used.
|
|
+**
|
|
+** Once it has been created, the amatch virtual table can be queried
|
|
+** as follows:
|
|
+**
|
|
+** SELECT word, distance FROM f
|
|
+** WHERE word MATCH 'abcdefg'
|
|
+** AND distance<200;
|
|
+**
|
|
+** This query outputs the strings contained in the T(F) field that
|
|
+** are close to "abcdefg" and in order of increasing distance. No string
|
|
+** is output more than once. If there are multiple ways to transform the
|
|
+** target string ("abcdefg") into a string in the vocabulary table then
|
|
+** the lowest cost transform is the one that is returned. In this example,
|
|
+** the search is limited to strings with a total distance of less than 200.
|
|
+**
|
|
+** For efficiency, it is important to put tight bounds on the distance.
|
|
+** The time and memory space needed to perform this query is exponential
|
|
+** in the maximum distance. A good rule of thumb is to limit the distance
|
|
+** to no more than 1.5 or 2 times the maximum cost of any rule in the
|
|
+** edit-cost-table.
|
|
+**
|
|
+** The amatch is a read-only table. Any attempt to DELETE, INSERT, or
|
|
+** UPDATE on a amatch table will throw an error.
|
|
+**
|
|
+** It is important to put some kind of a limit on the amatch output. This
|
|
+** can be either in the form of a LIMIT clause at the end of the query,
|
|
+** or better, a "distance<NNN" constraint where NNN is some number. The
|
|
+** running time and memory requirement is exponential in the value of NNN
|
|
+** so you want to make sure that NNN is not too big. A value of NNN that
|
|
+** is about twice the average transformation cost seems to give good results.
|
|
+**
|
|
+** The amatch table can be useful for tasks such as spelling correction.
|
|
+** Suppose all allowed words are in table vocabulary(w). Then one would create
|
|
+** an amatch virtual table like this:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE ex1 USING amatch(
|
|
+** vocabtable=vocabulary,
|
|
+** vocabcolumn=w,
|
|
+** edit_distances=ec1
|
|
+** );
|
|
+**
|
|
+** Then given an input word $word, look up close spellings this way:
|
|
+**
|
|
+** SELECT word, distance FROM ex1
|
|
+** WHERE word MATCH $word AND distance<200;
|
|
+**
|
|
+** MULTIPLE LANGUAGES
|
|
+**
|
|
+** Normally, the "iLang" value associated with all character transformations
|
|
+** in the edit-cost-table is zero. However, if required, the amatch
|
|
+** virtual table allows multiple languages to be defined. Each query uses
|
|
+** only a single iLang value. This allows, for example, a single
|
|
+** amatch table to support multiple languages.
|
|
+**
|
|
+** By default, only the rules with iLang=0 are used. To specify an
|
|
+** alternative language, a "language = ?" expression must be added to the
|
|
+** WHERE clause of a SELECT, where ? is the integer identifier of the desired
|
|
+** language. For example:
|
|
+**
|
|
+** SELECT word, distance FROM ex1
|
|
+** WHERE word MATCH $word
|
|
+** AND distance<=200
|
|
+** AND language=1 -- Specify use language 1 instead of 0
|
|
+**
|
|
+** If no "language = ?" constraint is specified in the WHERE clause, language
|
|
+** 0 is used.
|
|
+**
|
|
+** LIMITS
|
|
+**
|
|
+** The maximum language number is 2147483647. The maximum length of either
|
|
+** of the strings in the second or third column of the amatch data table
|
|
+** is 50 bytes. The maximum cost on a rule is 1000.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include <stdio.h>
|
|
+#include <ctype.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this implementation
|
|
+*/
|
|
+typedef struct amatch_vtab amatch_vtab;
|
|
+typedef struct amatch_cursor amatch_cursor;
|
|
+typedef struct amatch_rule amatch_rule;
|
|
+typedef struct amatch_word amatch_word;
|
|
+typedef struct amatch_avl amatch_avl;
|
|
+
|
|
+
|
|
+/*****************************************************************************
|
|
+** AVL Tree implementation
|
|
+*/
|
|
+/*
|
|
+** Objects that want to be members of the AVL tree should embedded an
|
|
+** instance of this structure.
|
|
+*/
|
|
+struct amatch_avl {
|
|
+ amatch_word *pWord; /* Points to the object being stored in the tree */
|
|
+ char *zKey; /* Key. zero-terminated string. Must be unique */
|
|
+ amatch_avl *pBefore; /* Other elements less than zKey */
|
|
+ amatch_avl *pAfter; /* Other elements greater than zKey */
|
|
+ amatch_avl *pUp; /* Parent element */
|
|
+ short int height; /* Height of this node. Leaf==1 */
|
|
+ short int imbalance; /* Height difference between pBefore and pAfter */
|
|
+};
|
|
+
|
|
+/* Recompute the amatch_avl.height and amatch_avl.imbalance fields for p.
|
|
+** Assume that the children of p have correct heights.
|
|
+*/
|
|
+static void amatchAvlRecomputeHeight(amatch_avl *p){
|
|
+ short int hBefore = p->pBefore ? p->pBefore->height : 0;
|
|
+ short int hAfter = p->pAfter ? p->pAfter->height : 0;
|
|
+ p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
|
|
+ p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** P B
|
|
+** / \ / \
|
|
+** B Z ==> X P
|
|
+** / \ / \
|
|
+** X Y Y Z
|
|
+**
|
|
+*/
|
|
+static amatch_avl *amatchAvlRotateBefore(amatch_avl *pP){
|
|
+ amatch_avl *pB = pP->pBefore;
|
|
+ amatch_avl *pY = pB->pAfter;
|
|
+ pB->pUp = pP->pUp;
|
|
+ pB->pAfter = pP;
|
|
+ pP->pUp = pB;
|
|
+ pP->pBefore = pY;
|
|
+ if( pY ) pY->pUp = pP;
|
|
+ amatchAvlRecomputeHeight(pP);
|
|
+ amatchAvlRecomputeHeight(pB);
|
|
+ return pB;
|
|
+}
|
|
+
|
|
+/*
|
|
+** P A
|
|
+** / \ / \
|
|
+** X A ==> P Z
|
|
+** / \ / \
|
|
+** Y Z X Y
|
|
+**
|
|
+*/
|
|
+static amatch_avl *amatchAvlRotateAfter(amatch_avl *pP){
|
|
+ amatch_avl *pA = pP->pAfter;
|
|
+ amatch_avl *pY = pA->pBefore;
|
|
+ pA->pUp = pP->pUp;
|
|
+ pA->pBefore = pP;
|
|
+ pP->pUp = pA;
|
|
+ pP->pAfter = pY;
|
|
+ if( pY ) pY->pUp = pP;
|
|
+ amatchAvlRecomputeHeight(pP);
|
|
+ amatchAvlRecomputeHeight(pA);
|
|
+ return pA;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the pBefore or pAfter pointer in the parent
|
|
+** of p that points to p. Or if p is the root node, return pp.
|
|
+*/
|
|
+static amatch_avl **amatchAvlFromPtr(amatch_avl *p, amatch_avl **pp){
|
|
+ amatch_avl *pUp = p->pUp;
|
|
+ if( pUp==0 ) return pp;
|
|
+ if( pUp->pAfter==p ) return &pUp->pAfter;
|
|
+ return &pUp->pBefore;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Rebalance all nodes starting with p and working up to the root.
|
|
+** Return the new root.
|
|
+*/
|
|
+static amatch_avl *amatchAvlBalance(amatch_avl *p){
|
|
+ amatch_avl *pTop = p;
|
|
+ amatch_avl **pp;
|
|
+ while( p ){
|
|
+ amatchAvlRecomputeHeight(p);
|
|
+ if( p->imbalance>=2 ){
|
|
+ amatch_avl *pB = p->pBefore;
|
|
+ if( pB->imbalance<0 ) p->pBefore = amatchAvlRotateAfter(pB);
|
|
+ pp = amatchAvlFromPtr(p,&p);
|
|
+ p = *pp = amatchAvlRotateBefore(p);
|
|
+ }else if( p->imbalance<=(-2) ){
|
|
+ amatch_avl *pA = p->pAfter;
|
|
+ if( pA->imbalance>0 ) p->pAfter = amatchAvlRotateBefore(pA);
|
|
+ pp = amatchAvlFromPtr(p,&p);
|
|
+ p = *pp = amatchAvlRotateAfter(p);
|
|
+ }
|
|
+ pTop = p;
|
|
+ p = p->pUp;
|
|
+ }
|
|
+ return pTop;
|
|
+}
|
|
+
|
|
+/* Search the tree rooted at p for an entry with zKey. Return a pointer
|
|
+** to the entry or return NULL.
|
|
+*/
|
|
+static amatch_avl *amatchAvlSearch(amatch_avl *p, const char *zKey){
|
|
+ int c;
|
|
+ while( p && (c = strcmp(zKey, p->zKey))!=0 ){
|
|
+ p = (c<0) ? p->pBefore : p->pAfter;
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/* Find the first node (the one with the smallest key).
|
|
+*/
|
|
+static amatch_avl *amatchAvlFirst(amatch_avl *p){
|
|
+ if( p ) while( p->pBefore ) p = p->pBefore;
|
|
+ return p;
|
|
+}
|
|
+
|
|
+#if 0 /* NOT USED */
|
|
+/* Return the node with the next larger key after p.
|
|
+*/
|
|
+static amatch_avl *amatchAvlNext(amatch_avl *p){
|
|
+ amatch_avl *pPrev = 0;
|
|
+ while( p && p->pAfter==pPrev ){
|
|
+ pPrev = p;
|
|
+ p = p->pUp;
|
|
+ }
|
|
+ if( p && pPrev==0 ){
|
|
+ p = amatchAvlFirst(p->pAfter);
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* NOT USED */
|
|
+/* Verify AVL tree integrity
|
|
+*/
|
|
+static int amatchAvlIntegrity(amatch_avl *pHead){
|
|
+ amatch_avl *p;
|
|
+ if( pHead==0 ) return 1;
|
|
+ if( (p = pHead->pBefore)!=0 ){
|
|
+ assert( p->pUp==pHead );
|
|
+ assert( amatchAvlIntegrity(p) );
|
|
+ assert( strcmp(p->zKey, pHead->zKey)<0 );
|
|
+ while( p->pAfter ) p = p->pAfter;
|
|
+ assert( strcmp(p->zKey, pHead->zKey)<0 );
|
|
+ }
|
|
+ if( (p = pHead->pAfter)!=0 ){
|
|
+ assert( p->pUp==pHead );
|
|
+ assert( amatchAvlIntegrity(p) );
|
|
+ assert( strcmp(p->zKey, pHead->zKey)>0 );
|
|
+ p = amatchAvlFirst(p);
|
|
+ assert( strcmp(p->zKey, pHead->zKey)>0 );
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+static int amatchAvlIntegrity2(amatch_avl *pHead){
|
|
+ amatch_avl *p, *pNext;
|
|
+ for(p=amatchAvlFirst(pHead); p; p=pNext){
|
|
+ pNext = amatchAvlNext(p);
|
|
+ if( pNext==0 ) break;
|
|
+ assert( strcmp(p->zKey, pNext->zKey)<0 );
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* Insert a new node pNew. Return NULL on success. If the key is not
|
|
+** unique, then do not perform the insert but instead leave pNew unchanged
|
|
+** and return a pointer to an existing node with the same key.
|
|
+*/
|
|
+static amatch_avl *amatchAvlInsert(amatch_avl **ppHead, amatch_avl *pNew){
|
|
+ int c;
|
|
+ amatch_avl *p = *ppHead;
|
|
+ if( p==0 ){
|
|
+ p = pNew;
|
|
+ pNew->pUp = 0;
|
|
+ }else{
|
|
+ while( p ){
|
|
+ c = strcmp(pNew->zKey, p->zKey);
|
|
+ if( c<0 ){
|
|
+ if( p->pBefore ){
|
|
+ p = p->pBefore;
|
|
+ }else{
|
|
+ p->pBefore = pNew;
|
|
+ pNew->pUp = p;
|
|
+ break;
|
|
+ }
|
|
+ }else if( c>0 ){
|
|
+ if( p->pAfter ){
|
|
+ p = p->pAfter;
|
|
+ }else{
|
|
+ p->pAfter = pNew;
|
|
+ pNew->pUp = p;
|
|
+ break;
|
|
+ }
|
|
+ }else{
|
|
+ return p;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ pNew->pBefore = 0;
|
|
+ pNew->pAfter = 0;
|
|
+ pNew->height = 1;
|
|
+ pNew->imbalance = 0;
|
|
+ *ppHead = amatchAvlBalance(p);
|
|
+ /* assert( amatchAvlIntegrity(*ppHead) ); */
|
|
+ /* assert( amatchAvlIntegrity2(*ppHead) ); */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Remove node pOld from the tree. pOld must be an element of the tree or
|
|
+** the AVL tree will become corrupt.
|
|
+*/
|
|
+static void amatchAvlRemove(amatch_avl **ppHead, amatch_avl *pOld){
|
|
+ amatch_avl **ppParent;
|
|
+ amatch_avl *pBalance = 0;
|
|
+ /* assert( amatchAvlSearch(*ppHead, pOld->zKey)==pOld ); */
|
|
+ ppParent = amatchAvlFromPtr(pOld, ppHead);
|
|
+ if( pOld->pBefore==0 && pOld->pAfter==0 ){
|
|
+ *ppParent = 0;
|
|
+ pBalance = pOld->pUp;
|
|
+ }else if( pOld->pBefore && pOld->pAfter ){
|
|
+ amatch_avl *pX, *pY;
|
|
+ pX = amatchAvlFirst(pOld->pAfter);
|
|
+ *amatchAvlFromPtr(pX, 0) = pX->pAfter;
|
|
+ if( pX->pAfter ) pX->pAfter->pUp = pX->pUp;
|
|
+ pBalance = pX->pUp;
|
|
+ pX->pAfter = pOld->pAfter;
|
|
+ if( pX->pAfter ){
|
|
+ pX->pAfter->pUp = pX;
|
|
+ }else{
|
|
+ assert( pBalance==pOld );
|
|
+ pBalance = pX;
|
|
+ }
|
|
+ pX->pBefore = pY = pOld->pBefore;
|
|
+ if( pY ) pY->pUp = pX;
|
|
+ pX->pUp = pOld->pUp;
|
|
+ *ppParent = pX;
|
|
+ }else if( pOld->pBefore==0 ){
|
|
+ *ppParent = pBalance = pOld->pAfter;
|
|
+ pBalance->pUp = pOld->pUp;
|
|
+ }else if( pOld->pAfter==0 ){
|
|
+ *ppParent = pBalance = pOld->pBefore;
|
|
+ pBalance->pUp = pOld->pUp;
|
|
+ }
|
|
+ *ppHead = amatchAvlBalance(pBalance);
|
|
+ pOld->pUp = 0;
|
|
+ pOld->pBefore = 0;
|
|
+ pOld->pAfter = 0;
|
|
+ /* assert( amatchAvlIntegrity(*ppHead) ); */
|
|
+ /* assert( amatchAvlIntegrity2(*ppHead) ); */
|
|
+}
|
|
+/*
|
|
+** End of the AVL Tree implementation
|
|
+******************************************************************************/
|
|
+
|
|
+
|
|
+/*
|
|
+** Various types.
|
|
+**
|
|
+** amatch_cost is the "cost" of an edit operation.
|
|
+**
|
|
+** amatch_len is the length of a matching string.
|
|
+**
|
|
+** amatch_langid is an ruleset identifier.
|
|
+*/
|
|
+typedef int amatch_cost;
|
|
+typedef signed char amatch_len;
|
|
+typedef int amatch_langid;
|
|
+
|
|
+/*
|
|
+** Limits
|
|
+*/
|
|
+#define AMATCH_MX_LENGTH 50 /* Maximum length of a rule string */
|
|
+#define AMATCH_MX_LANGID 2147483647 /* Maximum rule ID */
|
|
+#define AMATCH_MX_COST 1000 /* Maximum single-rule cost */
|
|
+
|
|
+/*
|
|
+** A match or partial match
|
|
+*/
|
|
+struct amatch_word {
|
|
+ amatch_word *pNext; /* Next on a list of all amatch_words */
|
|
+ amatch_avl sCost; /* Linkage of this node into the cost tree */
|
|
+ amatch_avl sWord; /* Linkage of this node into the word tree */
|
|
+ amatch_cost rCost; /* Cost of the match so far */
|
|
+ int iSeq; /* Sequence number */
|
|
+ char zCost[10]; /* Cost key (text rendering of rCost) */
|
|
+ short int nMatch; /* Input characters matched */
|
|
+ char zWord[4]; /* Text of the word. Extra space appended as needed */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Each transformation rule is stored as an instance of this object.
|
|
+** All rules are kept on a linked list sorted by rCost.
|
|
+*/
|
|
+struct amatch_rule {
|
|
+ amatch_rule *pNext; /* Next rule in order of increasing rCost */
|
|
+ char *zFrom; /* Transform from (a string from user input) */
|
|
+ amatch_cost rCost; /* Cost of this transformation */
|
|
+ amatch_langid iLang; /* The langauge to which this rule belongs */
|
|
+ amatch_len nFrom, nTo; /* Length of the zFrom and zTo strings */
|
|
+ char zTo[4]; /* Tranform to V.W value (extra space appended) */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A amatch virtual-table object
|
|
+*/
|
|
+struct amatch_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ char *zClassName; /* Name of this class. Default: "amatch" */
|
|
+ char *zDb; /* Name of database. (ex: "main") */
|
|
+ char *zSelf; /* Name of this virtual table */
|
|
+ char *zCostTab; /* Name of edit-cost-table */
|
|
+ char *zVocabTab; /* Name of vocabulary table */
|
|
+ char *zVocabWord; /* Name of vocabulary table word column */
|
|
+ char *zVocabLang; /* Name of vocabulary table language column */
|
|
+ amatch_rule *pRule; /* All active rules in this amatch */
|
|
+ amatch_cost rIns; /* Generic insertion cost '' -> ? */
|
|
+ amatch_cost rDel; /* Generic deletion cost ? -> '' */
|
|
+ amatch_cost rSub; /* Generic substitution cost ? -> ? */
|
|
+ sqlite3 *db; /* The database connection */
|
|
+ sqlite3_stmt *pVCheck; /* Query to check zVocabTab */
|
|
+ int nCursor; /* Number of active cursors */
|
|
+};
|
|
+
|
|
+/* A amatch cursor object */
|
|
+struct amatch_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_int64 iRowid; /* The rowid of the current word */
|
|
+ amatch_langid iLang; /* Use this language ID */
|
|
+ amatch_cost rLimit; /* Maximum cost of any term */
|
|
+ int nBuf; /* Space allocated for zBuf */
|
|
+ int oomErr; /* True following an OOM error */
|
|
+ int nWord; /* Number of amatch_word objects */
|
|
+ char *zBuf; /* Temp-use buffer space */
|
|
+ char *zInput; /* Input word to match against */
|
|
+ amatch_vtab *pVtab; /* The virtual table this cursor belongs to */
|
|
+ amatch_word *pAllWords; /* List of all amatch_word objects */
|
|
+ amatch_word *pCurrent; /* Most recent solution */
|
|
+ amatch_avl *pCost; /* amatch_word objects keyed by iCost */
|
|
+ amatch_avl *pWord; /* amatch_word objects keyed by zWord */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The two input rule lists are both sorted in order of increasing
|
|
+** cost. Merge them together into a single list, sorted by cost, and
|
|
+** return a pointer to the head of that list.
|
|
+*/
|
|
+static amatch_rule *amatchMergeRules(amatch_rule *pA, amatch_rule *pB){
|
|
+ amatch_rule head;
|
|
+ amatch_rule *pTail;
|
|
+
|
|
+ pTail = &head;
|
|
+ while( pA && pB ){
|
|
+ if( pA->rCost<=pB->rCost ){
|
|
+ pTail->pNext = pA;
|
|
+ pTail = pA;
|
|
+ pA = pA->pNext;
|
|
+ }else{
|
|
+ pTail->pNext = pB;
|
|
+ pTail = pB;
|
|
+ pB = pB->pNext;
|
|
+ }
|
|
+ }
|
|
+ if( pA==0 ){
|
|
+ pTail->pNext = pB;
|
|
+ }else{
|
|
+ pTail->pNext = pA;
|
|
+ }
|
|
+ return head.pNext;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Statement pStmt currently points to a row in the amatch data table. This
|
|
+** function allocates and populates a amatch_rule structure according to
|
|
+** the content of the row.
|
|
+**
|
|
+** If successful, *ppRule is set to point to the new object and SQLITE_OK
|
|
+** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
|
|
+** to an error message and an SQLite error code returned.
|
|
+*/
|
|
+static int amatchLoadOneRule(
|
|
+ amatch_vtab *p, /* Fuzzer virtual table handle */
|
|
+ sqlite3_stmt *pStmt, /* Base rule on statements current row */
|
|
+ amatch_rule **ppRule, /* OUT: New rule object */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ sqlite3_int64 iLang = sqlite3_column_int64(pStmt, 0);
|
|
+ const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
|
|
+ const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
|
|
+ amatch_cost rCost = sqlite3_column_int(pStmt, 3);
|
|
+
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ int nFrom; /* Size of string zFrom, in bytes */
|
|
+ int nTo; /* Size of string zTo, in bytes */
|
|
+ amatch_rule *pRule = 0; /* New rule object to return */
|
|
+
|
|
+ if( zFrom==0 ) zFrom = "";
|
|
+ if( zTo==0 ) zTo = "";
|
|
+ nFrom = (int)strlen(zFrom);
|
|
+ nTo = (int)strlen(zTo);
|
|
+
|
|
+ /* Silently ignore null transformations */
|
|
+ if( strcmp(zFrom, zTo)==0 ){
|
|
+ if( zFrom[0]=='?' && zFrom[1]==0 ){
|
|
+ if( p->rSub==0 || p->rSub>rCost ) p->rSub = rCost;
|
|
+ }
|
|
+ *ppRule = 0;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+
|
|
+ if( rCost<=0 || rCost>AMATCH_MX_COST ){
|
|
+ *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
|
|
+ p->zClassName, AMATCH_MX_COST
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else
|
|
+ if( nFrom>AMATCH_MX_LENGTH || nTo>AMATCH_MX_LENGTH ){
|
|
+ *pzErr = sqlite3_mprintf("%s: maximum string length is %d",
|
|
+ p->zClassName, AMATCH_MX_LENGTH
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else
|
|
+ if( iLang<0 || iLang>AMATCH_MX_LANGID ){
|
|
+ *pzErr = sqlite3_mprintf("%s: iLang must be between 0 and %d",
|
|
+ p->zClassName, AMATCH_MX_LANGID
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else
|
|
+ if( strcmp(zFrom,"")==0 && strcmp(zTo,"?")==0 ){
|
|
+ if( p->rIns==0 || p->rIns>rCost ) p->rIns = rCost;
|
|
+ }else
|
|
+ if( strcmp(zFrom,"?")==0 && strcmp(zTo,"")==0 ){
|
|
+ if( p->rDel==0 || p->rDel>rCost ) p->rDel = rCost;
|
|
+ }else
|
|
+ {
|
|
+ pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
|
|
+ if( pRule==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pRule, 0, sizeof(*pRule));
|
|
+ pRule->zFrom = &pRule->zTo[nTo+1];
|
|
+ pRule->nFrom = (amatch_len)nFrom;
|
|
+ memcpy(pRule->zFrom, zFrom, nFrom+1);
|
|
+ memcpy(pRule->zTo, zTo, nTo+1);
|
|
+ pRule->nTo = (amatch_len)nTo;
|
|
+ pRule->rCost = rCost;
|
|
+ pRule->iLang = (int)iLang;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *ppRule = pRule;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free all the content in the edit-cost-table
|
|
+*/
|
|
+static void amatchFreeRules(amatch_vtab *p){
|
|
+ while( p->pRule ){
|
|
+ amatch_rule *pRule = p->pRule;
|
|
+ p->pRule = pRule->pNext;
|
|
+ sqlite3_free(pRule);
|
|
+ }
|
|
+ p->pRule = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Load the content of the amatch data table into memory.
|
|
+*/
|
|
+static int amatchLoadRules(
|
|
+ sqlite3 *db, /* Database handle */
|
|
+ amatch_vtab *p, /* Virtual amatch table to configure */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ char *zSql; /* SELECT used to read from rules table */
|
|
+ amatch_rule *pHead = 0;
|
|
+
|
|
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zCostTab);
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int rc2; /* finalize() return code */
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
|
|
+ }else if( sqlite3_column_count(pStmt)!=4 ){
|
|
+ *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
|
|
+ p->zClassName, p->zCostTab, sqlite3_column_count(pStmt)
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ amatch_rule *pRule = 0;
|
|
+ rc = amatchLoadOneRule(p, pStmt, &pRule, pzErr);
|
|
+ if( pRule ){
|
|
+ pRule->pNext = pHead;
|
|
+ pHead = pRule;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ rc2 = sqlite3_finalize(pStmt);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+
|
|
+ /* All rules are now in a singly linked list starting at pHead. This
|
|
+ ** block sorts them by cost and then sets amatch_vtab.pRule to point to
|
|
+ ** point to the head of the sorted list.
|
|
+ */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ unsigned int i;
|
|
+ amatch_rule *pX;
|
|
+ amatch_rule *a[15];
|
|
+ for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
|
|
+ while( (pX = pHead)!=0 ){
|
|
+ pHead = pX->pNext;
|
|
+ pX->pNext = 0;
|
|
+ for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
|
|
+ pX = amatchMergeRules(a[i], pX);
|
|
+ a[i] = 0;
|
|
+ }
|
|
+ a[i] = amatchMergeRules(a[i], pX);
|
|
+ }
|
|
+ for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
|
|
+ pX = amatchMergeRules(a[i], pX);
|
|
+ }
|
|
+ p->pRule = amatchMergeRules(p->pRule, pX);
|
|
+ }else{
|
|
+ /* An error has occurred. Setting p->pRule to point to the head of the
|
|
+ ** allocated list ensures that the list will be cleaned up in this case.
|
|
+ */
|
|
+ assert( p->pRule==0 );
|
|
+ p->pRule = pHead;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function converts an SQL quoted string into an unquoted string
|
|
+** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
|
+** containing the result. The caller should eventually free this buffer
|
|
+** using sqlite3_free.
|
|
+**
|
|
+** Examples:
|
|
+**
|
|
+** "abc" becomes abc
|
|
+** 'xyz' becomes xyz
|
|
+** [pqr] becomes pqr
|
|
+** `mno` becomes mno
|
|
+*/
|
|
+static char *amatchDequote(const char *zIn){
|
|
+ size_t nIn; /* Size of input string, in bytes */
|
|
+ char *zOut; /* Output (dequoted) string */
|
|
+
|
|
+ nIn = strlen(zIn);
|
|
+ zOut = sqlite3_malloc(nIn+1);
|
|
+ if( zOut ){
|
|
+ char q = zIn[0]; /* Quote character (if any ) */
|
|
+
|
|
+ if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
|
+ memcpy(zOut, zIn, (size_t)(nIn+1));
|
|
+ }else{
|
|
+ int iOut = 0; /* Index of next byte to write to output */
|
|
+ int iIn; /* Index of next byte to read from input */
|
|
+
|
|
+ if( q=='[' ) q = ']';
|
|
+ for(iIn=1; iIn<nIn; iIn++){
|
|
+ if( zIn[iIn]==q ) iIn++;
|
|
+ zOut[iOut++] = zIn[iIn];
|
|
+ }
|
|
+ }
|
|
+ assert( (int)strlen(zOut)<=nIn );
|
|
+ }
|
|
+ return zOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Deallocate the pVCheck prepared statement.
|
|
+*/
|
|
+static void amatchVCheckClear(amatch_vtab *p){
|
|
+ if( p->pVCheck ){
|
|
+ sqlite3_finalize(p->pVCheck);
|
|
+ p->pVCheck = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Deallocate an amatch_vtab object
|
|
+*/
|
|
+static void amatchFree(amatch_vtab *p){
|
|
+ if( p ){
|
|
+ amatchFreeRules(p);
|
|
+ amatchVCheckClear(p);
|
|
+ sqlite3_free(p->zClassName);
|
|
+ sqlite3_free(p->zDb);
|
|
+ sqlite3_free(p->zCostTab);
|
|
+ sqlite3_free(p->zVocabTab);
|
|
+ sqlite3_free(p->zVocabWord);
|
|
+ sqlite3_free(p->zVocabLang);
|
|
+ sqlite3_free(p->zSelf);
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xDisconnect/xDestroy method for the amatch module.
|
|
+*/
|
|
+static int amatchDisconnect(sqlite3_vtab *pVtab){
|
|
+ amatch_vtab *p = (amatch_vtab*)pVtab;
|
|
+ assert( p->nCursor==0 );
|
|
+ amatchFree(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check to see if the argument is of the form:
|
|
+**
|
|
+** KEY = VALUE
|
|
+**
|
|
+** If it is, return a pointer to the first character of VALUE.
|
|
+** If not, return NULL. Spaces around the = are ignored.
|
|
+*/
|
|
+static const char *amatchValueOfKey(const char *zKey, const char *zStr){
|
|
+ int nKey = (int)strlen(zKey);
|
|
+ int nStr = (int)strlen(zStr);
|
|
+ int i;
|
|
+ if( nStr<nKey+1 ) return 0;
|
|
+ if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
|
|
+ for(i=nKey; isspace((unsigned char)zStr[i]); i++){}
|
|
+ if( zStr[i]!='=' ) return 0;
|
|
+ i++;
|
|
+ while( isspace((unsigned char)zStr[i]) ){ i++; }
|
|
+ return zStr+i;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xConnect/xCreate method for the amatch module. Arguments are:
|
|
+**
|
|
+** argv[0] -> module name ("approximate_match")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[3...] -> arguments
|
|
+*/
|
|
+static int amatchConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ amatch_vtab *pNew = 0; /* New virtual table */
|
|
+ const char *zModule = argv[0];
|
|
+ const char *zDb = argv[1];
|
|
+ const char *zVal;
|
|
+ int i;
|
|
+
|
|
+ (void)pAux;
|
|
+ *ppVtab = 0;
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ rc = SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->db = db;
|
|
+ pNew->zClassName = sqlite3_mprintf("%s", zModule);
|
|
+ if( pNew->zClassName==0 ) goto amatchConnectError;
|
|
+ pNew->zDb = sqlite3_mprintf("%s", zDb);
|
|
+ if( pNew->zDb==0 ) goto amatchConnectError;
|
|
+ pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
|
|
+ if( pNew->zSelf==0 ) goto amatchConnectError;
|
|
+ for(i=3; i<argc; i++){
|
|
+ zVal = amatchValueOfKey("vocabulary_table", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zVocabTab);
|
|
+ pNew->zVocabTab = amatchDequote(zVal);
|
|
+ if( pNew->zVocabTab==0 ) goto amatchConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ zVal = amatchValueOfKey("vocabulary_word", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zVocabWord);
|
|
+ pNew->zVocabWord = amatchDequote(zVal);
|
|
+ if( pNew->zVocabWord==0 ) goto amatchConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ zVal = amatchValueOfKey("vocabulary_language", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zVocabLang);
|
|
+ pNew->zVocabLang = amatchDequote(zVal);
|
|
+ if( pNew->zVocabLang==0 ) goto amatchConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ zVal = amatchValueOfKey("edit_distances", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zCostTab);
|
|
+ pNew->zCostTab = amatchDequote(zVal);
|
|
+ if( pNew->zCostTab==0 ) goto amatchConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
|
|
+ amatchFree(pNew);
|
|
+ *ppVtab = 0;
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ rc = SQLITE_OK;
|
|
+ if( pNew->zCostTab==0 ){
|
|
+ *pzErr = sqlite3_mprintf("no edit_distances table specified");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ rc = amatchLoadRules(db, pNew, pzErr);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(word,distance,language,"
|
|
+ "command HIDDEN,nword HIDDEN)"
|
|
+ );
|
|
+#define AMATCH_COL_WORD 0
|
|
+#define AMATCH_COL_DISTANCE 1
|
|
+#define AMATCH_COL_LANGUAGE 2
|
|
+#define AMATCH_COL_COMMAND 3
|
|
+#define AMATCH_COL_NWORD 4
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ amatchFree(pNew);
|
|
+ }
|
|
+ *ppVtab = &pNew->base;
|
|
+ return rc;
|
|
+
|
|
+amatchConnectError:
|
|
+ amatchFree(pNew);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a new amatch cursor.
|
|
+*/
|
|
+static int amatchOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ amatch_vtab *p = (amatch_vtab*)pVTab;
|
|
+ amatch_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->pVtab = p;
|
|
+ *ppCursor = &pCur->base;
|
|
+ p->nCursor++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
|
+** to indicate that it is at EOF.
|
|
+*/
|
|
+static void amatchClearCursor(amatch_cursor *pCur){
|
|
+ amatch_word *pWord, *pNextWord;
|
|
+ for(pWord=pCur->pAllWords; pWord; pWord=pNextWord){
|
|
+ pNextWord = pWord->pNext;
|
|
+ sqlite3_free(pWord);
|
|
+ }
|
|
+ pCur->pAllWords = 0;
|
|
+ sqlite3_free(pCur->zInput);
|
|
+ pCur->zInput = 0;
|
|
+ sqlite3_free(pCur->zBuf);
|
|
+ pCur->zBuf = 0;
|
|
+ pCur->nBuf = 0;
|
|
+ pCur->pCost = 0;
|
|
+ pCur->pWord = 0;
|
|
+ pCur->pCurrent = 0;
|
|
+ pCur->rLimit = 1000000;
|
|
+ pCur->iLang = 0;
|
|
+ pCur->nWord = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close a amatch cursor.
|
|
+*/
|
|
+static int amatchClose(sqlite3_vtab_cursor *cur){
|
|
+ amatch_cursor *pCur = (amatch_cursor *)cur;
|
|
+ amatchClearCursor(pCur);
|
|
+ pCur->pVtab->nCursor--;
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Render a 24-bit unsigned integer as a 4-byte base-64 number.
|
|
+*/
|
|
+static void amatchEncodeInt(int x, char *z){
|
|
+ static const char a[] =
|
|
+ "0123456789"
|
|
+ "ABCDEFGHIJ"
|
|
+ "KLMNOPQRST"
|
|
+ "UVWXYZ^abc"
|
|
+ "defghijklm"
|
|
+ "nopqrstuvw"
|
|
+ "xyz~";
|
|
+ z[0] = a[(x>>18)&0x3f];
|
|
+ z[1] = a[(x>>12)&0x3f];
|
|
+ z[2] = a[(x>>6)&0x3f];
|
|
+ z[3] = a[x&0x3f];
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write the zCost[] field for a amatch_word object
|
|
+*/
|
|
+static void amatchWriteCost(amatch_word *pWord){
|
|
+ amatchEncodeInt(pWord->rCost, pWord->zCost);
|
|
+ amatchEncodeInt(pWord->iSeq, pWord->zCost+4);
|
|
+ pWord->zCost[8] = 0;
|
|
+}
|
|
+
|
|
+/* Circumvent compiler warnings about the use of strcpy() by supplying
|
|
+** our own implementation.
|
|
+*/
|
|
+static void amatchStrcpy(char *dest, const char *src){
|
|
+ while( (*(dest++) = *(src++))!=0 ){}
|
|
+}
|
|
+static void amatchStrcat(char *dest, const char *src){
|
|
+ while( *dest ) dest++;
|
|
+ amatchStrcpy(dest, src);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add a new amatch_word object to the queue.
|
|
+**
|
|
+** If a prior amatch_word object with the same zWord, and nMatch
|
|
+** already exists, update its rCost (if the new rCost is less) but
|
|
+** otherwise leave it unchanged. Do not add a duplicate.
|
|
+**
|
|
+** Do nothing if the cost exceeds threshold.
|
|
+*/
|
|
+static void amatchAddWord(
|
|
+ amatch_cursor *pCur,
|
|
+ amatch_cost rCost,
|
|
+ int nMatch,
|
|
+ const char *zWordBase,
|
|
+ const char *zWordTail
|
|
+){
|
|
+ amatch_word *pWord;
|
|
+ amatch_avl *pNode;
|
|
+ amatch_avl *pOther;
|
|
+ int nBase, nTail;
|
|
+ char zBuf[4];
|
|
+
|
|
+ if( rCost>pCur->rLimit ){
|
|
+ return;
|
|
+ }
|
|
+ nBase = (int)strlen(zWordBase);
|
|
+ nTail = (int)strlen(zWordTail);
|
|
+ if( nBase+nTail+3>pCur->nBuf ){
|
|
+ pCur->nBuf = nBase+nTail+100;
|
|
+ pCur->zBuf = sqlite3_realloc(pCur->zBuf, pCur->nBuf);
|
|
+ if( pCur->zBuf==0 ){
|
|
+ pCur->nBuf = 0;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ amatchEncodeInt(nMatch, zBuf);
|
|
+ memcpy(pCur->zBuf, zBuf+2, 2);
|
|
+ memcpy(pCur->zBuf+2, zWordBase, nBase);
|
|
+ memcpy(pCur->zBuf+2+nBase, zWordTail, nTail+1);
|
|
+ pNode = amatchAvlSearch(pCur->pWord, pCur->zBuf);
|
|
+ if( pNode ){
|
|
+ pWord = pNode->pWord;
|
|
+ if( pWord->rCost>rCost ){
|
|
+#ifdef AMATCH_TRACE_1
|
|
+ printf("UPDATE [%s][%.*s^%s] %d (\"%s\" \"%s\")\n",
|
|
+ pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput,
|
|
+ pWord->rCost, pWord->zWord, pWord->zCost);
|
|
+#endif
|
|
+ amatchAvlRemove(&pCur->pCost, &pWord->sCost);
|
|
+ pWord->rCost = rCost;
|
|
+ amatchWriteCost(pWord);
|
|
+#ifdef AMATCH_TRACE_1
|
|
+ printf(" ---> %d (\"%s\" \"%s\")\n",
|
|
+ pWord->rCost, pWord->zWord, pWord->zCost);
|
|
+#endif
|
|
+ pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost);
|
|
+ assert( pOther==0 ); (void)pOther;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ pWord = sqlite3_malloc( sizeof(*pWord) + nBase + nTail - 1 );
|
|
+ if( pWord==0 ) return;
|
|
+ memset(pWord, 0, sizeof(*pWord));
|
|
+ pWord->rCost = rCost;
|
|
+ pWord->iSeq = pCur->nWord++;
|
|
+ amatchWriteCost(pWord);
|
|
+ pWord->nMatch = (short)nMatch;
|
|
+ pWord->pNext = pCur->pAllWords;
|
|
+ pCur->pAllWords = pWord;
|
|
+ pWord->sCost.zKey = pWord->zCost;
|
|
+ pWord->sCost.pWord = pWord;
|
|
+ pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost);
|
|
+ assert( pOther==0 ); (void)pOther;
|
|
+ pWord->sWord.zKey = pWord->zWord;
|
|
+ pWord->sWord.pWord = pWord;
|
|
+ amatchStrcpy(pWord->zWord, pCur->zBuf);
|
|
+ pOther = amatchAvlInsert(&pCur->pWord, &pWord->sWord);
|
|
+ assert( pOther==0 ); (void)pOther;
|
|
+#ifdef AMATCH_TRACE_1
|
|
+ printf("INSERT [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", pWord->zWord+2,
|
|
+ pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, rCost,
|
|
+ pWord->zWord, pWord->zCost);
|
|
+#endif
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a cursor to its next row of output
|
|
+*/
|
|
+static int amatchNext(sqlite3_vtab_cursor *cur){
|
|
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
|
+ amatch_word *pWord = 0;
|
|
+ amatch_avl *pNode;
|
|
+ int isMatch = 0;
|
|
+ amatch_vtab *p = pCur->pVtab;
|
|
+ int nWord;
|
|
+ int rc;
|
|
+ int i;
|
|
+ const char *zW;
|
|
+ amatch_rule *pRule;
|
|
+ char *zBuf = 0;
|
|
+ char nBuf = 0;
|
|
+ char zNext[8];
|
|
+ char zNextIn[8];
|
|
+ int nNextIn;
|
|
+
|
|
+ if( p->pVCheck==0 ){
|
|
+ char *zSql;
|
|
+ if( p->zVocabLang && p->zVocabLang[0] ){
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT \"%w\" FROM \"%w\"",
|
|
+ " WHERE \"%w\">=?1 AND \"%w\"=?2"
|
|
+ " ORDER BY 1",
|
|
+ p->zVocabWord, p->zVocabTab,
|
|
+ p->zVocabWord, p->zVocabLang
|
|
+ );
|
|
+ }else{
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT \"%w\" FROM \"%w\""
|
|
+ " WHERE \"%w\">=?1"
|
|
+ " ORDER BY 1",
|
|
+ p->zVocabWord, p->zVocabTab,
|
|
+ p->zVocabWord
|
|
+ );
|
|
+ }
|
|
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->pVCheck, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) return rc;
|
|
+ }
|
|
+ sqlite3_bind_int(p->pVCheck, 2, pCur->iLang);
|
|
+
|
|
+ do{
|
|
+ pNode = amatchAvlFirst(pCur->pCost);
|
|
+ if( pNode==0 ){
|
|
+ pWord = 0;
|
|
+ break;
|
|
+ }
|
|
+ pWord = pNode->pWord;
|
|
+ amatchAvlRemove(&pCur->pCost, &pWord->sCost);
|
|
+
|
|
+#ifdef AMATCH_TRACE_1
|
|
+ printf("PROCESS [%s][%.*s^%s] %d (\"%s\" \"%s\")\n",
|
|
+ pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch,
|
|
+ pWord->rCost, pWord->zWord, pWord->zCost);
|
|
+#endif
|
|
+ nWord = (int)strlen(pWord->zWord+2);
|
|
+ if( nWord+20>nBuf ){
|
|
+ nBuf = (char)(nWord+100);
|
|
+ zBuf = sqlite3_realloc(zBuf, nBuf);
|
|
+ if( zBuf==0 ) return SQLITE_NOMEM;
|
|
+ }
|
|
+ amatchStrcpy(zBuf, pWord->zWord+2);
|
|
+ zNext[0] = 0;
|
|
+ zNextIn[0] = pCur->zInput[pWord->nMatch];
|
|
+ if( zNextIn[0] ){
|
|
+ for(i=1; i<=4 && (pCur->zInput[pWord->nMatch+i]&0xc0)==0x80; i++){
|
|
+ zNextIn[i] = pCur->zInput[pWord->nMatch+i];
|
|
+ }
|
|
+ zNextIn[i] = 0;
|
|
+ nNextIn = i;
|
|
+ }else{
|
|
+ nNextIn = 0;
|
|
+ }
|
|
+
|
|
+ if( zNextIn[0] && zNextIn[0]!='*' ){
|
|
+ sqlite3_reset(p->pVCheck);
|
|
+ amatchStrcat(zBuf, zNextIn);
|
|
+ sqlite3_bind_text(p->pVCheck, 1, zBuf, nWord+nNextIn, SQLITE_STATIC);
|
|
+ rc = sqlite3_step(p->pVCheck);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ zW = (const char*)sqlite3_column_text(p->pVCheck, 0);
|
|
+ if( strncmp(zBuf, zW, nWord+nNextIn)==0 ){
|
|
+ amatchAddWord(pCur, pWord->rCost, pWord->nMatch+nNextIn, zBuf, "");
|
|
+ }
|
|
+ }
|
|
+ zBuf[nWord] = 0;
|
|
+ }
|
|
+
|
|
+ while( 1 ){
|
|
+ amatchStrcpy(zBuf+nWord, zNext);
|
|
+ sqlite3_reset(p->pVCheck);
|
|
+ sqlite3_bind_text(p->pVCheck, 1, zBuf, -1, SQLITE_TRANSIENT);
|
|
+ rc = sqlite3_step(p->pVCheck);
|
|
+ if( rc!=SQLITE_ROW ) break;
|
|
+ zW = (const char*)sqlite3_column_text(p->pVCheck, 0);
|
|
+ amatchStrcpy(zBuf+nWord, zNext);
|
|
+ if( strncmp(zW, zBuf, nWord)!=0 ) break;
|
|
+ if( (zNextIn[0]=='*' && zNextIn[1]==0)
|
|
+ || (zNextIn[0]==0 && zW[nWord]==0)
|
|
+ ){
|
|
+ isMatch = 1;
|
|
+ zNextIn[0] = 0;
|
|
+ nNextIn = 0;
|
|
+ break;
|
|
+ }
|
|
+ zNext[0] = zW[nWord];
|
|
+ for(i=1; i<=4 && (zW[nWord+i]&0xc0)==0x80; i++){
|
|
+ zNext[i] = zW[nWord+i];
|
|
+ }
|
|
+ zNext[i] = 0;
|
|
+ zBuf[nWord] = 0;
|
|
+ if( p->rIns>0 ){
|
|
+ amatchAddWord(pCur, pWord->rCost+p->rIns, pWord->nMatch,
|
|
+ zBuf, zNext);
|
|
+ }
|
|
+ if( p->rSub>0 ){
|
|
+ amatchAddWord(pCur, pWord->rCost+p->rSub, pWord->nMatch+nNextIn,
|
|
+ zBuf, zNext);
|
|
+ }
|
|
+ if( p->rIns<0 && p->rSub<0 ) break;
|
|
+ zNext[i-1]++; /* FIX ME */
|
|
+ }
|
|
+ sqlite3_reset(p->pVCheck);
|
|
+
|
|
+ if( p->rDel>0 ){
|
|
+ zBuf[nWord] = 0;
|
|
+ amatchAddWord(pCur, pWord->rCost+p->rDel, pWord->nMatch+nNextIn,
|
|
+ zBuf, "");
|
|
+ }
|
|
+
|
|
+ for(pRule=p->pRule; pRule; pRule=pRule->pNext){
|
|
+ if( pRule->iLang!=pCur->iLang ) continue;
|
|
+ if( strncmp(pRule->zFrom, pCur->zInput+pWord->nMatch, pRule->nFrom)==0 ){
|
|
+ amatchAddWord(pCur, pWord->rCost+pRule->rCost,
|
|
+ pWord->nMatch+pRule->nFrom, pWord->zWord+2, pRule->zTo);
|
|
+ }
|
|
+ }
|
|
+ }while( !isMatch );
|
|
+ pCur->pCurrent = pWord;
|
|
+ sqlite3_free(zBuf);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called to "rewind" a cursor back to the beginning so that
|
|
+** it starts its output over again. Always called at least once
|
|
+** prior to any amatchColumn, amatchRowid, or amatchEof call.
|
|
+*/
|
|
+static int amatchFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ amatch_cursor *pCur = (amatch_cursor *)pVtabCursor;
|
|
+ const char *zWord = "*";
|
|
+ int idx;
|
|
+
|
|
+ amatchClearCursor(pCur);
|
|
+ idx = 0;
|
|
+ if( idxNum & 1 ){
|
|
+ zWord = (const char*)sqlite3_value_text(argv[0]);
|
|
+ idx++;
|
|
+ }
|
|
+ if( idxNum & 2 ){
|
|
+ pCur->rLimit = (amatch_cost)sqlite3_value_int(argv[idx]);
|
|
+ idx++;
|
|
+ }
|
|
+ if( idxNum & 4 ){
|
|
+ pCur->iLang = (amatch_cost)sqlite3_value_int(argv[idx]);
|
|
+ idx++;
|
|
+ }
|
|
+ pCur->zInput = sqlite3_mprintf("%s", zWord);
|
|
+ if( pCur->zInput==0 ) return SQLITE_NOMEM;
|
|
+ amatchAddWord(pCur, 0, 0, "", "");
|
|
+ amatchNext(pVtabCursor);
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only the word and distance columns have values. All other columns
|
|
+** return NULL
|
|
+*/
|
|
+static int amatchColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
|
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case AMATCH_COL_WORD: {
|
|
+ sqlite3_result_text(ctx, pCur->pCurrent->zWord+2, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case AMATCH_COL_DISTANCE: {
|
|
+ sqlite3_result_int(ctx, pCur->pCurrent->rCost);
|
|
+ break;
|
|
+ }
|
|
+ case AMATCH_COL_LANGUAGE: {
|
|
+ sqlite3_result_int(ctx, pCur->iLang);
|
|
+ break;
|
|
+ }
|
|
+ case AMATCH_COL_NWORD: {
|
|
+ sqlite3_result_int(ctx, pCur->nWord);
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ sqlite3_result_null(ctx);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The rowid.
|
|
+*/
|
|
+static int amatchRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** EOF indicator
|
|
+*/
|
|
+static int amatchEof(sqlite3_vtab_cursor *cur){
|
|
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
|
+ return pCur->pCurrent==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Search for terms of these forms:
|
|
+**
|
|
+** (A) word MATCH $str
|
|
+** (B1) distance < $value
|
|
+** (B2) distance <= $value
|
|
+** (C) language == $language
|
|
+**
|
|
+** The distance< and distance<= are both treated as distance<=.
|
|
+** The query plan number is a bit vector:
|
|
+**
|
|
+** bit 1: Term of the form (A) found
|
|
+** bit 2: Term like (B1) or (B2) found
|
|
+** bit 3: Term like (C) found
|
|
+**
|
|
+** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set
|
|
+** then $value is in filter.argv[0] if bit-1 is clear and is in
|
|
+** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is
|
|
+** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
|
|
+** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
|
|
+** filter.argv[2] if both bit-1 and bit-2 are set.
|
|
+*/
|
|
+static int amatchBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int iPlan = 0;
|
|
+ int iDistTerm = -1;
|
|
+ int iLangTerm = -1;
|
|
+ int i;
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ (void)tab;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+ if( (iPlan & 1)==0
|
|
+ && pConstraint->iColumn==0
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
|
+ ){
|
|
+ iPlan |= 1;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ }
|
|
+ if( (iPlan & 2)==0
|
|
+ && pConstraint->iColumn==1
|
|
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
|
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
|
|
+ ){
|
|
+ iPlan |= 2;
|
|
+ iDistTerm = i;
|
|
+ }
|
|
+ if( (iPlan & 4)==0
|
|
+ && pConstraint->iColumn==2
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= 4;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ iLangTerm = i;
|
|
+ }
|
|
+ }
|
|
+ if( iPlan & 2 ){
|
|
+ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
|
|
+ }
|
|
+ if( iPlan & 4 ){
|
|
+ int idx = 1;
|
|
+ if( iPlan & 1 ) idx++;
|
|
+ if( iPlan & 2 ) idx++;
|
|
+ pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx;
|
|
+ }
|
|
+ pIdxInfo->idxNum = iPlan;
|
|
+ if( pIdxInfo->nOrderBy==1
|
|
+ && pIdxInfo->aOrderBy[0].iColumn==1
|
|
+ && pIdxInfo->aOrderBy[0].desc==0
|
|
+ ){
|
|
+ pIdxInfo->orderByConsumed = 1;
|
|
+ }
|
|
+ pIdxInfo->estimatedCost = (double)10000;
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The xUpdate() method.
|
|
+**
|
|
+** This implementation disallows DELETE and UPDATE. The only thing
|
|
+** allowed is INSERT into the "command" column.
|
|
+*/
|
|
+static int amatchUpdate(
|
|
+ sqlite3_vtab *pVTab,
|
|
+ int argc,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ amatch_vtab *p = (amatch_vtab*)pVTab;
|
|
+ const unsigned char *zCmd;
|
|
+ (void)pRowid;
|
|
+ if( argc==1 ){
|
|
+ pVTab->zErrMsg = sqlite3_mprintf("DELETE from %s is not allowed",
|
|
+ p->zSelf);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
|
|
+ pVTab->zErrMsg = sqlite3_mprintf("UPDATE of %s is not allowed",
|
|
+ p->zSelf);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ if( sqlite3_value_type(argv[2+AMATCH_COL_WORD])!=SQLITE_NULL
|
|
+ || sqlite3_value_type(argv[2+AMATCH_COL_DISTANCE])!=SQLITE_NULL
|
|
+ || sqlite3_value_type(argv[2+AMATCH_COL_LANGUAGE])!=SQLITE_NULL
|
|
+ ){
|
|
+ pVTab->zErrMsg = sqlite3_mprintf(
|
|
+ "INSERT INTO %s allowed for column [command] only", p->zSelf);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ zCmd = sqlite3_value_text(argv[2+AMATCH_COL_COMMAND]);
|
|
+ if( zCmd==0 ) return SQLITE_OK;
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** A virtual table module that implements the "approximate_match".
|
|
+*/
|
|
+static const sqlite3_module amatchModule = {
|
|
+ 0, /* iVersion */
|
|
+ amatchConnect, /* xCreate */
|
|
+ amatchConnect, /* xConnect */
|
|
+ amatchBestIndex, /* xBestIndex */
|
|
+ amatchDisconnect, /* xDisconnect */
|
|
+ amatchDisconnect, /* xDestroy */
|
|
+ amatchOpen, /* xOpen - open a cursor */
|
|
+ amatchClose, /* xClose - close a cursor */
|
|
+ amatchFilter, /* xFilter - configure scan constraints */
|
|
+ amatchNext, /* xNext - advance a cursor */
|
|
+ amatchEof, /* xEof - check for end of scan */
|
|
+ amatchColumn, /* xColumn - read data */
|
|
+ amatchRowid, /* xRowid - read data */
|
|
+ amatchUpdate, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+/*
|
|
+** Register the amatch virtual table
|
|
+*/
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_amatch_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Not used */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0);
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Not used */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0);
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/anycollseq.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/anycollseq.c 2019-05-14 16:22:31.217587800 +0200
|
|
@@ -0,0 +1,70 @@
|
|
+/*
|
|
+** 2017-04-16
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements a run-time loadable extension to SQLite that
|
|
+** registers a sqlite3_collation_needed() callback to register a fake
|
|
+** collating function for any unknown collating sequence. The fake
|
|
+** collating function works like BINARY.
|
|
+**
|
|
+** This extension can be used to load schemas that contain one or more
|
|
+** unknown collating sequences.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+
|
|
+static int anyCollFunc(
|
|
+ void *NotUsed,
|
|
+ int nKey1, const void *pKey1,
|
|
+ int nKey2, const void *pKey2
|
|
+){
|
|
+ int rc, n;
|
|
+ n = nKey1<nKey2 ? nKey1 : nKey2;
|
|
+ rc = memcmp(pKey1, pKey2, n);
|
|
+ if( rc==0 ) rc = nKey1 - nKey2;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void anyCollNeeded(
|
|
+ void *NotUsed,
|
|
+ sqlite3 *db,
|
|
+ int eTextRep,
|
|
+ const char *zCollName
|
|
+){
|
|
+ sqlite3_create_collation(db, zCollName, eTextRep, 0, anyCollFunc);
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_anycollseq_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_collation_needed(db, 0, anyCollNeeded);
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_collation_needed(db, 0, anyCollNeeded);
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/appendvfs.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/appendvfs.c 2019-05-14 16:22:31.219540400 +0200
|
|
@@ -0,0 +1,565 @@
|
|
+/*
|
|
+** 2017-10-20
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements a VFS shim that allows an SQLite database to be
|
|
+** appended onto the end of some other file, such as an executable.
|
|
+**
|
|
+** A special record must appear at the end of the file that identifies the
|
|
+** file as an appended database and provides an offset to page 1. For
|
|
+** best performance page 1 should be located at a disk page boundary, though
|
|
+** that is not required.
|
|
+**
|
|
+** When opening a database using this VFS, the connection might treat
|
|
+** the file as an ordinary SQLite database, or it might treat is as a
|
|
+** database appended onto some other file. Here are the rules:
|
|
+**
|
|
+** (1) When opening a new empty file, that file is treated as an ordinary
|
|
+** database.
|
|
+**
|
|
+** (2) When opening a file that begins with the standard SQLite prefix
|
|
+** string "SQLite format 3", that file is treated as an ordinary
|
|
+** database.
|
|
+**
|
|
+** (3) When opening a file that ends with the appendvfs trailer string
|
|
+** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended
|
|
+** database.
|
|
+**
|
|
+** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
|
|
+** set, then a new database is appended to the already existing file.
|
|
+**
|
|
+** (5) Otherwise, SQLITE_CANTOPEN is returned.
|
|
+**
|
|
+** To avoid unnecessary complications with the PENDING_BYTE, the size of
|
|
+** the file containing the database is limited to 1GB. This VFS will refuse
|
|
+** to read or write past the 1GB mark. This restriction might be lifted in
|
|
+** future versions. For now, if you need a large database, then keep the
|
|
+** database in a separate file.
|
|
+**
|
|
+** If the file being opened is not an appended database, then this shim is
|
|
+** a pass-through into the default underlying VFS.
|
|
+**/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+/* The append mark at the end of the database is:
|
|
+**
|
|
+** Start-Of-SQLite3-NNNNNNNN
|
|
+** 123456789 123456789 12345
|
|
+**
|
|
+** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
|
|
+** the offset to page 1.
|
|
+*/
|
|
+#define APND_MARK_PREFIX "Start-Of-SQLite3-"
|
|
+#define APND_MARK_PREFIX_SZ 17
|
|
+#define APND_MARK_SIZE 25
|
|
+
|
|
+/*
|
|
+** Maximum size of the combined prefix + database + append-mark. This
|
|
+** must be less than 0x40000000 to avoid locking issues on Windows.
|
|
+*/
|
|
+#define APND_MAX_SIZE (65536*15259)
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this utility
|
|
+*/
|
|
+typedef struct sqlite3_vfs ApndVfs;
|
|
+typedef struct ApndFile ApndFile;
|
|
+
|
|
+/* Access to a lower-level VFS that (might) implement dynamic loading,
|
|
+** access to randomness, etc.
|
|
+*/
|
|
+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
|
+#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
|
|
+
|
|
+/* An open file */
|
|
+struct ApndFile {
|
|
+ sqlite3_file base; /* IO methods */
|
|
+ sqlite3_int64 iPgOne; /* File offset to page 1 */
|
|
+ sqlite3_int64 iMark; /* Start of the append-mark */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Methods for ApndFile
|
|
+*/
|
|
+static int apndClose(sqlite3_file*);
|
|
+static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
+static int apndWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
+static int apndTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
+static int apndSync(sqlite3_file*, int flags);
|
|
+static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
+static int apndLock(sqlite3_file*, int);
|
|
+static int apndUnlock(sqlite3_file*, int);
|
|
+static int apndCheckReservedLock(sqlite3_file*, int *pResOut);
|
|
+static int apndFileControl(sqlite3_file*, int op, void *pArg);
|
|
+static int apndSectorSize(sqlite3_file*);
|
|
+static int apndDeviceCharacteristics(sqlite3_file*);
|
|
+static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
|
+static int apndShmLock(sqlite3_file*, int offset, int n, int flags);
|
|
+static void apndShmBarrier(sqlite3_file*);
|
|
+static int apndShmUnmap(sqlite3_file*, int deleteFlag);
|
|
+static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
|
+static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
|
+
|
|
+/*
|
|
+** Methods for ApndVfs
|
|
+*/
|
|
+static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
+static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
+static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
+static int apndFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
+static void *apndDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
+static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
+static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
+static void apndDlClose(sqlite3_vfs*, void*);
|
|
+static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
+static int apndSleep(sqlite3_vfs*, int microseconds);
|
|
+static int apndCurrentTime(sqlite3_vfs*, double*);
|
|
+static int apndGetLastError(sqlite3_vfs*, int, char *);
|
|
+static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
+static int apndSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
|
|
+static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z);
|
|
+static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName);
|
|
+
|
|
+static sqlite3_vfs apnd_vfs = {
|
|
+ 3, /* iVersion (set when registered) */
|
|
+ 0, /* szOsFile (set when registered) */
|
|
+ 1024, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ "apndvfs", /* zName */
|
|
+ 0, /* pAppData (set when registered) */
|
|
+ apndOpen, /* xOpen */
|
|
+ apndDelete, /* xDelete */
|
|
+ apndAccess, /* xAccess */
|
|
+ apndFullPathname, /* xFullPathname */
|
|
+ apndDlOpen, /* xDlOpen */
|
|
+ apndDlError, /* xDlError */
|
|
+ apndDlSym, /* xDlSym */
|
|
+ apndDlClose, /* xDlClose */
|
|
+ apndRandomness, /* xRandomness */
|
|
+ apndSleep, /* xSleep */
|
|
+ apndCurrentTime, /* xCurrentTime */
|
|
+ apndGetLastError, /* xGetLastError */
|
|
+ apndCurrentTimeInt64, /* xCurrentTimeInt64 */
|
|
+ apndSetSystemCall, /* xSetSystemCall */
|
|
+ apndGetSystemCall, /* xGetSystemCall */
|
|
+ apndNextSystemCall /* xNextSystemCall */
|
|
+};
|
|
+
|
|
+static const sqlite3_io_methods apnd_io_methods = {
|
|
+ 3, /* iVersion */
|
|
+ apndClose, /* xClose */
|
|
+ apndRead, /* xRead */
|
|
+ apndWrite, /* xWrite */
|
|
+ apndTruncate, /* xTruncate */
|
|
+ apndSync, /* xSync */
|
|
+ apndFileSize, /* xFileSize */
|
|
+ apndLock, /* xLock */
|
|
+ apndUnlock, /* xUnlock */
|
|
+ apndCheckReservedLock, /* xCheckReservedLock */
|
|
+ apndFileControl, /* xFileControl */
|
|
+ apndSectorSize, /* xSectorSize */
|
|
+ apndDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ apndShmMap, /* xShmMap */
|
|
+ apndShmLock, /* xShmLock */
|
|
+ apndShmBarrier, /* xShmBarrier */
|
|
+ apndShmUnmap, /* xShmUnmap */
|
|
+ apndFetch, /* xFetch */
|
|
+ apndUnfetch /* xUnfetch */
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Close an apnd-file.
|
|
+*/
|
|
+static int apndClose(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xClose(pFile);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read data from an apnd-file.
|
|
+*/
|
|
+static int apndRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add the append-mark onto the end of the file.
|
|
+*/
|
|
+static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){
|
|
+ int i;
|
|
+ unsigned char a[APND_MARK_SIZE];
|
|
+ memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
|
|
+ for(i=0; i<8; i++){
|
|
+ a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff;
|
|
+ }
|
|
+ return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an apnd-file.
|
|
+*/
|
|
+static int apndWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ int rc;
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL;
|
|
+ rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne);
|
|
+ if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){
|
|
+ sqlite3_int64 sz = 0;
|
|
+ rc = pFile->pMethods->xFileSize(pFile, &sz);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->iMark = sz - APND_MARK_SIZE;
|
|
+ if( iOfst + iAmt + p->iPgOne > p->iMark ){
|
|
+ p->iMark = p->iPgOne + iOfst + iAmt;
|
|
+ rc = apndWriteMark(p, pFile);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an apnd-file.
|
|
+*/
|
|
+static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ int rc;
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->iMark = p->iPgOne+size;
|
|
+ rc = apndWriteMark(p, pFile);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an apnd-file.
|
|
+*/
|
|
+static int apndSync(sqlite3_file *pFile, int flags){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xSync(pFile, flags);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an apnd-file.
|
|
+*/
|
|
+static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ int rc;
|
|
+ pFile = ORIGFILE(p);
|
|
+ rc = pFile->pMethods->xFileSize(pFile, pSize);
|
|
+ if( rc==SQLITE_OK && p->iPgOne ){
|
|
+ *pSize -= p->iPgOne + APND_MARK_SIZE;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an apnd-file.
|
|
+*/
|
|
+static int apndLock(sqlite3_file *pFile, int eLock){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xLock(pFile, eLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unlock an apnd-file.
|
|
+*/
|
|
+static int apndUnlock(sqlite3_file *pFile, int eLock){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xUnlock(pFile, eLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an apnd-file.
|
|
+*/
|
|
+static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an apnd-file.
|
|
+*/
|
|
+static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ int rc;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ rc = pFile->pMethods->xFileControl(pFile, op, pArg);
|
|
+ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
|
|
+ *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->iPgOne, *(char**)pArg);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the sector-size in bytes for an apnd-file.
|
|
+*/
|
|
+static int apndSectorSize(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xSectorSize(pFile);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an apnd-file.
|
|
+*/
|
|
+static int apndDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xDeviceCharacteristics(pFile);
|
|
+}
|
|
+
|
|
+/* Create a shared memory file mapping */
|
|
+static int apndShmMap(
|
|
+ sqlite3_file *pFile,
|
|
+ int iPg,
|
|
+ int pgsz,
|
|
+ int bExtend,
|
|
+ void volatile **pp
|
|
+){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
|
|
+}
|
|
+
|
|
+/* Perform locking on a shared-memory segment */
|
|
+static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xShmLock(pFile,offset,n,flags);
|
|
+}
|
|
+
|
|
+/* Memory barrier operation on shared memory */
|
|
+static void apndShmBarrier(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ pFile->pMethods->xShmBarrier(pFile);
|
|
+}
|
|
+
|
|
+/* Unmap a shared memory segment */
|
|
+static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
|
|
+}
|
|
+
|
|
+/* Fetch a page of a memory-mapped file */
|
|
+static int apndFetch(
|
|
+ sqlite3_file *pFile,
|
|
+ sqlite3_int64 iOfst,
|
|
+ int iAmt,
|
|
+ void **pp
|
|
+){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
|
|
+}
|
|
+
|
|
+/* Release a memory-mapped page */
|
|
+static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check to see if the file is an ordinary SQLite database file.
|
|
+*/
|
|
+static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
|
|
+ int rc;
|
|
+ char zHdr[16];
|
|
+ static const char aSqliteHdr[] = "SQLite format 3";
|
|
+ if( sz<512 ) return 0;
|
|
+ rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0);
|
|
+ if( rc ) return 0;
|
|
+ return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Try to read the append-mark off the end of a file. Return the
|
|
+** start of the appended database if the append-mark is present. If
|
|
+** there is no append-mark, return -1;
|
|
+*/
|
|
+static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
|
|
+ int rc, i;
|
|
+ sqlite3_int64 iMark;
|
|
+ unsigned char a[APND_MARK_SIZE];
|
|
+
|
|
+ if( sz<=APND_MARK_SIZE ) return -1;
|
|
+ rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
|
|
+ if( rc ) return -1;
|
|
+ if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
|
|
+ iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ]&0x7f))<<56;
|
|
+ for(i=1; i<8; i++){
|
|
+ iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i);
|
|
+ }
|
|
+ return iMark;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an apnd file handle.
|
|
+*/
|
|
+static int apndOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ ApndFile *p;
|
|
+ sqlite3_file *pSubFile;
|
|
+ sqlite3_vfs *pSubVfs;
|
|
+ int rc;
|
|
+ sqlite3_int64 sz;
|
|
+ pSubVfs = ORIGVFS(pVfs);
|
|
+ if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
|
|
+ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
|
|
+ }
|
|
+ p = (ApndFile*)pFile;
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ pSubFile = ORIGFILE(pFile);
|
|
+ p->base.pMethods = &apnd_io_methods;
|
|
+ rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
|
|
+ if( rc ) goto apnd_open_done;
|
|
+ rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
|
|
+ if( rc ){
|
|
+ pSubFile->pMethods->xClose(pSubFile);
|
|
+ goto apnd_open_done;
|
|
+ }
|
|
+ if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
|
|
+ memmove(pFile, pSubFile, pSubVfs->szOsFile);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ p->iMark = 0;
|
|
+ p->iPgOne = apndReadMark(sz, pFile);
|
|
+ if( p->iPgOne>0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ if( (flags & SQLITE_OPEN_CREATE)==0 ){
|
|
+ pSubFile->pMethods->xClose(pSubFile);
|
|
+ rc = SQLITE_CANTOPEN;
|
|
+ }
|
|
+ p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff;
|
|
+apnd_open_done:
|
|
+ if( rc ) pFile->pMethods = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** All other VFS methods are pass-thrus.
|
|
+*/
|
|
+static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
|
|
+}
|
|
+static int apndAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
|
|
+}
|
|
+static int apndFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
|
|
+}
|
|
+static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
|
+}
|
|
+static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
|
+}
|
|
+static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
|
+}
|
|
+static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
|
+}
|
|
+static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
|
+}
|
|
+static int apndSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
|
+}
|
|
+static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
|
+}
|
|
+static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
|
+}
|
|
+static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
+ return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
|
+}
|
|
+static int apndSetSystemCall(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_syscall_ptr pCall
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
|
|
+}
|
|
+static sqlite3_syscall_ptr apndGetSystemCall(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
|
|
+}
|
|
+static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
|
+ return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+/*
|
|
+** This routine is called when the extension is loaded.
|
|
+** Register the new VFS.
|
|
+*/
|
|
+int sqlite3_appendvfs_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3_vfs *pOrig;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg;
|
|
+ (void)db;
|
|
+ pOrig = sqlite3_vfs_find(0);
|
|
+ apnd_vfs.iVersion = pOrig->iVersion;
|
|
+ apnd_vfs.pAppData = pOrig;
|
|
+ apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile);
|
|
+ rc = sqlite3_vfs_register(&apnd_vfs, 0);
|
|
+#ifdef APPENDVFS_TEST
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_auto_extension((void(*)(void))apndvfsRegister);
|
|
+ }
|
|
+#endif
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/blobio.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/blobio.c 2019-05-14 16:22:31.220516800 +0200
|
|
@@ -0,0 +1,152 @@
|
|
+/*
|
|
+** 2019-03-30
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** An SQL function that uses the incremental BLOB I/O mechanism of SQLite
|
|
+** to read or write part of a blob. This is intended for debugging use
|
|
+** in the CLI.
|
|
+**
|
|
+** readblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,N)
|
|
+**
|
|
+** Returns N bytes of the blob starting at OFFSET.
|
|
+**
|
|
+** writeblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,NEWDATA)
|
|
+**
|
|
+** NEWDATA must be a blob. The content of NEWDATA overwrites the
|
|
+** existing BLOB data at SCHEMA.TABLE.COLUMN for row ROWID beginning
|
|
+** at OFFSET bytes into the blob.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+static void readblobFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3_blob *pBlob = 0;
|
|
+ const char *zSchema;
|
|
+ const char *zTable;
|
|
+ const char *zColumn;
|
|
+ sqlite3_int64 iRowid;
|
|
+ int iOfst;
|
|
+ unsigned char *aData;
|
|
+ int nData;
|
|
+ sqlite3 *db;
|
|
+ int rc;
|
|
+
|
|
+ zSchema = (const char*)sqlite3_value_text(argv[0]);
|
|
+ zTable = (const char*)sqlite3_value_text(argv[1]);
|
|
+ if( zTable==0 ){
|
|
+ sqlite3_result_error(context, "bad table name", -1);
|
|
+ return;
|
|
+ }
|
|
+ zColumn = (const char*)sqlite3_value_text(argv[2]);
|
|
+ if( zTable==0 ){
|
|
+ sqlite3_result_error(context, "bad column name", -1);
|
|
+ return;
|
|
+ }
|
|
+ iRowid = sqlite3_value_int64(argv[3]);
|
|
+ iOfst = sqlite3_value_int(argv[4]);
|
|
+ nData = sqlite3_value_int(argv[5]);
|
|
+ if( nData<=0 ) return;
|
|
+ aData = sqlite3_malloc64( nData+1 );
|
|
+ if( aData==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ db = sqlite3_context_db_handle(context);
|
|
+ rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 0, &pBlob);
|
|
+ if( rc ){
|
|
+ sqlite3_free(aData);
|
|
+ sqlite3_result_error(context, "cannot open BLOB pointer", -1);
|
|
+ return;
|
|
+ }
|
|
+ rc = sqlite3_blob_read(pBlob, aData, nData, iOfst);
|
|
+ sqlite3_blob_close(pBlob);
|
|
+ if( rc ){
|
|
+ sqlite3_free(aData);
|
|
+ sqlite3_result_error(context, "BLOB write failed", -1);
|
|
+ }else{
|
|
+ sqlite3_result_blob(context, aData, nData, sqlite3_free);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void writeblobFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3_blob *pBlob = 0;
|
|
+ const char *zSchema;
|
|
+ const char *zTable;
|
|
+ const char *zColumn;
|
|
+ sqlite3_int64 iRowid;
|
|
+ int iOfst;
|
|
+ unsigned char *aData;
|
|
+ int nData;
|
|
+ sqlite3 *db;
|
|
+ int rc;
|
|
+
|
|
+ zSchema = (const char*)sqlite3_value_text(argv[0]);
|
|
+ zTable = (const char*)sqlite3_value_text(argv[1]);
|
|
+ if( zTable==0 ){
|
|
+ sqlite3_result_error(context, "bad table name", -1);
|
|
+ return;
|
|
+ }
|
|
+ zColumn = (const char*)sqlite3_value_text(argv[2]);
|
|
+ if( zTable==0 ){
|
|
+ sqlite3_result_error(context, "bad column name", -1);
|
|
+ return;
|
|
+ }
|
|
+ iRowid = sqlite3_value_int64(argv[3]);
|
|
+ iOfst = sqlite3_value_int(argv[4]);
|
|
+ if( sqlite3_value_type(argv[5])!=SQLITE_BLOB ){
|
|
+ sqlite3_result_error(context, "6th argument must be a BLOB", -1);
|
|
+ return;
|
|
+ }
|
|
+ nData = sqlite3_value_bytes(argv[5]);
|
|
+ aData = (unsigned char *)sqlite3_value_blob(argv[5]);
|
|
+ db = sqlite3_context_db_handle(context);
|
|
+ rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 1, &pBlob);
|
|
+ if( rc ){
|
|
+ sqlite3_result_error(context, "cannot open BLOB pointer", -1);
|
|
+ return;
|
|
+ }
|
|
+ rc = sqlite3_blob_write(pBlob, aData, nData, iOfst);
|
|
+ sqlite3_blob_close(pBlob);
|
|
+ if( rc ){
|
|
+ sqlite3_result_error(context, "BLOB write failed", -1);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_blobio_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "readblob", 6, SQLITE_UTF8, 0,
|
|
+ readblobFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "writeblob", 6, SQLITE_UTF8, 0,
|
|
+ writeblobFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/btreeinfo.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/btreeinfo.c 2019-05-14 16:22:31.222469400 +0200
|
|
@@ -0,0 +1,429 @@
|
|
+/*
|
|
+** 2017-10-24
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file contains an implementation of the "sqlite_btreeinfo" virtual table.
|
|
+**
|
|
+** The sqlite_btreeinfo virtual table is a read-only eponymous-only virtual
|
|
+** table that shows information about all btrees in an SQLite database file.
|
|
+** The schema is like this:
|
|
+**
|
|
+** CREATE TABLE sqlite_btreeinfo(
|
|
+** type TEXT, -- "table" or "index"
|
|
+** name TEXT, -- Name of table or index for this btree.
|
|
+** tbl_name TEXT, -- Associated table
|
|
+** rootpage INT, -- The root page of the btree
|
|
+** sql TEXT, -- SQL for this btree - from sqlite_master
|
|
+** hasRowid BOOLEAN, -- True if the btree has a rowid
|
|
+** nEntry INT, -- Estimated number of enteries
|
|
+** nPage INT, -- Estimated number of pages
|
|
+** depth INT, -- Depth of the btree
|
|
+** szPage INT, -- Size of each page in bytes
|
|
+** zSchema TEXT HIDDEN -- The schema to which this btree belongs
|
|
+** );
|
|
+**
|
|
+** The first 5 fields are taken directly from the sqlite_master table.
|
|
+** Considering only the first 5 fields, the only difference between
|
|
+** this virtual table and the sqlite_master table is that this virtual
|
|
+** table omits all entries that have a 0 or NULL rowid - in other words
|
|
+** it omits triggers and views.
|
|
+**
|
|
+** The value added by this table comes in the next 5 fields.
|
|
+**
|
|
+** Note that nEntry and nPage are *estimated*. They are computed doing
|
|
+** a single search from the root to a leaf, counting the number of cells
|
|
+** at each level, and assuming that unvisited pages have a similar number
|
|
+** of cells.
|
|
+**
|
|
+** The sqlite_dbpage virtual table must be available for this virtual table
|
|
+** to operate.
|
|
+**
|
|
+** USAGE EXAMPLES:
|
|
+**
|
|
+** Show the table btrees in a schema order with the tables with the most
|
|
+** rows occuring first:
|
|
+**
|
|
+** SELECT name, nEntry
|
|
+** FROM sqlite_btreeinfo
|
|
+** WHERE type='table'
|
|
+** ORDER BY nEntry DESC, name;
|
|
+**
|
|
+** Show the names of all WITHOUT ROWID tables:
|
|
+**
|
|
+** SELECT name FROM sqlite_btreeinfo
|
|
+** WHERE type='table' AND NOT hasRowid;
|
|
+*/
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+/* Columns available in this virtual table */
|
|
+#define BINFO_COLUMN_TYPE 0
|
|
+#define BINFO_COLUMN_NAME 1
|
|
+#define BINFO_COLUMN_TBL_NAME 2
|
|
+#define BINFO_COLUMN_ROOTPAGE 3
|
|
+#define BINFO_COLUMN_SQL 4
|
|
+#define BINFO_COLUMN_HASROWID 5
|
|
+#define BINFO_COLUMN_NENTRY 6
|
|
+#define BINFO_COLUMN_NPAGE 7
|
|
+#define BINFO_COLUMN_DEPTH 8
|
|
+#define BINFO_COLUMN_SZPAGE 9
|
|
+#define BINFO_COLUMN_SCHEMA 10
|
|
+
|
|
+/* Forward declarations */
|
|
+typedef struct BinfoTable BinfoTable;
|
|
+typedef struct BinfoCursor BinfoCursor;
|
|
+
|
|
+/* A cursor for the sqlite_btreeinfo table */
|
|
+struct BinfoCursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
|
|
+ sqlite3_stmt *pStmt; /* Query against sqlite_master */
|
|
+ int rc; /* Result of previous sqlite_step() call */
|
|
+ int hasRowid; /* hasRowid value. Negative if unknown. */
|
|
+ sqlite3_int64 nEntry; /* nEntry value */
|
|
+ int nPage; /* nPage value */
|
|
+ int depth; /* depth value */
|
|
+ int szPage; /* size of a btree page. 0 if unknown */
|
|
+ char *zSchema; /* Schema being interrogated */
|
|
+};
|
|
+
|
|
+/* The sqlite_btreeinfo table */
|
|
+struct BinfoTable {
|
|
+ sqlite3_vtab base; /* Base class. Must be first */
|
|
+ sqlite3 *db; /* The databse connection */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Connect to the sqlite_btreeinfo virtual table.
|
|
+*/
|
|
+static int binfoConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ BinfoTable *pTab = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(\n"
|
|
+ " type TEXT,\n"
|
|
+ " name TEXT,\n"
|
|
+ " tbl_name TEXT,\n"
|
|
+ " rootpage INT,\n"
|
|
+ " sql TEXT,\n"
|
|
+ " hasRowid BOOLEAN,\n"
|
|
+ " nEntry INT,\n"
|
|
+ " nPage INT,\n"
|
|
+ " depth INT,\n"
|
|
+ " szPage INT,\n"
|
|
+ " zSchema TEXT HIDDEN\n"
|
|
+ ")");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pTab = (BinfoTable *)sqlite3_malloc(sizeof(BinfoTable));
|
|
+ if( pTab==0 ) rc = SQLITE_NOMEM;
|
|
+ }
|
|
+ assert( rc==SQLITE_OK || pTab==0 );
|
|
+ if( pTab ){
|
|
+ pTab->db = db;
|
|
+ }
|
|
+ *ppVtab = (sqlite3_vtab*)pTab;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Disconnect from or destroy a btreeinfo virtual table.
|
|
+*/
|
|
+static int binfoDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** idxNum:
|
|
+**
|
|
+** 0 Use "main" for the schema
|
|
+** 1 Schema identified by parameter ?1
|
|
+*/
|
|
+static int binfoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
|
+ int i;
|
|
+ pIdxInfo->estimatedCost = 10000.0; /* Cost estimate */
|
|
+ pIdxInfo->estimatedRows = 100;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
|
+ if( p->usable
|
|
+ && p->iColumn==BINFO_COLUMN_SCHEMA
|
|
+ && p->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ pIdxInfo->estimatedCost = 1000.0;
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a new btreeinfo cursor.
|
|
+*/
|
|
+static int binfoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ BinfoCursor *pCsr;
|
|
+
|
|
+ pCsr = (BinfoCursor *)sqlite3_malloc(sizeof(BinfoCursor));
|
|
+ if( pCsr==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pCsr, 0, sizeof(BinfoCursor));
|
|
+ pCsr->base.pVtab = pVTab;
|
|
+ }
|
|
+
|
|
+ *ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close a btreeinfo cursor.
|
|
+*/
|
|
+static int binfoClose(sqlite3_vtab_cursor *pCursor){
|
|
+ BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
|
+ sqlite3_finalize(pCsr->pStmt);
|
|
+ sqlite3_free(pCsr->zSchema);
|
|
+ sqlite3_free(pCsr);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Move a btreeinfo cursor to the next entry in the file.
|
|
+*/
|
|
+static int binfoNext(sqlite3_vtab_cursor *pCursor){
|
|
+ BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
|
+ pCsr->rc = sqlite3_step(pCsr->pStmt);
|
|
+ pCsr->hasRowid = -1;
|
|
+ return pCsr->rc==SQLITE_ERROR ? SQLITE_ERROR : SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* We have reached EOF if previous sqlite3_step() returned
|
|
+** anything other than SQLITE_ROW;
|
|
+*/
|
|
+static int binfoEof(sqlite3_vtab_cursor *pCursor){
|
|
+ BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
|
+ return pCsr->rc!=SQLITE_ROW;
|
|
+}
|
|
+
|
|
+/* Position a cursor back to the beginning.
|
|
+*/
|
|
+static int binfoFilter(
|
|
+ sqlite3_vtab_cursor *pCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
|
+ BinfoTable *pTab = (BinfoTable *)pCursor->pVtab;
|
|
+ char *zSql;
|
|
+ int rc;
|
|
+
|
|
+ sqlite3_free(pCsr->zSchema);
|
|
+ if( idxNum==1 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){
|
|
+ pCsr->zSchema = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
|
+ }else{
|
|
+ pCsr->zSchema = sqlite3_mprintf("main");
|
|
+ }
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT 0, 'table','sqlite_master','sqlite_master',1,NULL "
|
|
+ "UNION ALL "
|
|
+ "SELECT rowid, type, name, tbl_name, rootpage, sql"
|
|
+ " FROM \"%w\".sqlite_master WHERE rootpage>=1",
|
|
+ pCsr->zSchema);
|
|
+ sqlite3_finalize(pCsr->pStmt);
|
|
+ pCsr->pStmt = 0;
|
|
+ pCsr->hasRowid = -1;
|
|
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = binfoNext(pCursor);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Decode big-endian integers */
|
|
+static unsigned int get_uint16(unsigned char *a){
|
|
+ return (a[0]<<8)|a[1];
|
|
+}
|
|
+static unsigned int get_uint32(unsigned char *a){
|
|
+ return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|a[3];
|
|
+}
|
|
+
|
|
+/* Examine the b-tree rooted at pgno and estimate its size.
|
|
+** Return non-zero if anything goes wrong.
|
|
+*/
|
|
+static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
|
|
+ sqlite3_int64 nEntry = 1;
|
|
+ int nPage = 1;
|
|
+ unsigned char *aData;
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ int pgsz = 0;
|
|
+ int nCell;
|
|
+ int iCell;
|
|
+
|
|
+ rc = sqlite3_prepare_v2(db,
|
|
+ "SELECT data FROM sqlite_dbpage('main') WHERE pgno=?1", -1,
|
|
+ &pStmt, 0);
|
|
+ if( rc ) return rc;
|
|
+ pCsr->depth = 1;
|
|
+ while(1){
|
|
+ sqlite3_bind_int(pStmt, 1, pgno);
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ if( rc!=SQLITE_ROW ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ break;
|
|
+ }
|
|
+ pCsr->szPage = pgsz = sqlite3_column_bytes(pStmt, 0);
|
|
+ aData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
|
|
+ if( aData==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }
|
|
+ if( pgno==1 ){
|
|
+ aData += 100;
|
|
+ pgsz -= 100;
|
|
+ }
|
|
+ pCsr->hasRowid = aData[0]!=2 && aData[0]!=10;
|
|
+ nCell = get_uint16(aData+3);
|
|
+ nEntry *= (nCell+1);
|
|
+ if( aData[0]==10 || aData[0]==13 ) break;
|
|
+ nPage *= (nCell+1);
|
|
+ if( nCell<=1 ){
|
|
+ pgno = get_uint32(aData+8);
|
|
+ }else{
|
|
+ iCell = get_uint16(aData+12+2*(nCell/2));
|
|
+ if( pgno==1 ) iCell -= 100;
|
|
+ if( iCell<=12 || iCell>=pgsz-4 ){
|
|
+ rc = SQLITE_CORRUPT;
|
|
+ break;
|
|
+ }
|
|
+ pgno = get_uint32(aData+iCell);
|
|
+ }
|
|
+ pCsr->depth++;
|
|
+ sqlite3_reset(pStmt);
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ pCsr->nPage = nPage;
|
|
+ pCsr->nEntry = nEntry;
|
|
+ if( rc==SQLITE_ROW ) rc = SQLITE_OK;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Return a column for the sqlite_btreeinfo table */
|
|
+static int binfoColumn(
|
|
+ sqlite3_vtab_cursor *pCursor,
|
|
+ sqlite3_context *ctx,
|
|
+ int i
|
|
+){
|
|
+ BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
|
+ if( i>=BINFO_COLUMN_HASROWID && i<=BINFO_COLUMN_SZPAGE && pCsr->hasRowid<0 ){
|
|
+ int pgno = sqlite3_column_int(pCsr->pStmt, BINFO_COLUMN_ROOTPAGE+1);
|
|
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
|
|
+ int rc = binfoCompute(db, pgno, pCsr);
|
|
+ if( rc ){
|
|
+ pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+ switch( i ){
|
|
+ case BINFO_COLUMN_NAME:
|
|
+ case BINFO_COLUMN_TYPE:
|
|
+ case BINFO_COLUMN_TBL_NAME:
|
|
+ case BINFO_COLUMN_ROOTPAGE:
|
|
+ case BINFO_COLUMN_SQL: {
|
|
+ sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
|
|
+ break;
|
|
+ }
|
|
+ case BINFO_COLUMN_HASROWID: {
|
|
+ sqlite3_result_int(ctx, pCsr->hasRowid);
|
|
+ break;
|
|
+ }
|
|
+ case BINFO_COLUMN_NENTRY: {
|
|
+ sqlite3_result_int64(ctx, pCsr->nEntry);
|
|
+ break;
|
|
+ }
|
|
+ case BINFO_COLUMN_NPAGE: {
|
|
+ sqlite3_result_int(ctx, pCsr->nPage);
|
|
+ break;
|
|
+ }
|
|
+ case BINFO_COLUMN_DEPTH: {
|
|
+ sqlite3_result_int(ctx, pCsr->depth);
|
|
+ break;
|
|
+ }
|
|
+ case BINFO_COLUMN_SCHEMA: {
|
|
+ sqlite3_result_text(ctx, pCsr->zSchema, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Return the ROWID for the sqlite_btreeinfo table */
|
|
+static int binfoRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
|
+ BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
|
+ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Invoke this routine to register the "sqlite_btreeinfo" virtual table module
|
|
+*/
|
|
+int sqlite3BinfoRegister(sqlite3 *db){
|
|
+ static const sqlite3_module binfo_module = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ binfoConnect, /* xConnect */
|
|
+ binfoBestIndex, /* xBestIndex */
|
|
+ binfoDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ binfoOpen, /* xOpen - open a cursor */
|
|
+ binfoClose, /* xClose - close a cursor */
|
|
+ binfoFilter, /* xFilter - configure scan constraints */
|
|
+ binfoNext, /* xNext - advance a cursor */
|
|
+ binfoEof, /* xEof - check for end of scan */
|
|
+ binfoColumn, /* xColumn - read data */
|
|
+ binfoRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+ };
|
|
+ return sqlite3_create_module(db, "sqlite_btreeinfo", &binfo_module, 0);
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_btreeinfo_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ return sqlite3BinfoRegister(db);
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/carray.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/carray.c 2019-05-14 16:22:31.224422000 +0200
|
|
@@ -0,0 +1,420 @@
|
|
+/*
|
|
+** 2016-06-29
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file demonstrates how to create a table-valued-function that
|
|
+** returns the values in a C-language array.
|
|
+** Examples:
|
|
+**
|
|
+** SELECT * FROM carray($ptr,5)
|
|
+**
|
|
+** The query above returns 5 integers contained in a C-language array
|
|
+** at the address $ptr. $ptr is a pointer to the array of integers.
|
|
+** The pointer value must be assigned to $ptr using the
|
|
+** sqlite3_bind_pointer() interface with a pointer type of "carray".
|
|
+** For example:
|
|
+**
|
|
+** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
|
|
+** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
|
|
+** sqlite3_bind_value(pStmt, i, aX, "carray", 0);
|
|
+**
|
|
+** There is an optional third parameter to determine the datatype of
|
|
+** the C-language array. Allowed values of the third parameter are
|
|
+** 'int32', 'int64', 'double', 'char*'. Example:
|
|
+**
|
|
+** SELECT * FROM carray($ptr,10,'char*');
|
|
+**
|
|
+** The default value of the third parameter is 'int32'.
|
|
+**
|
|
+** HOW IT WORKS
|
|
+**
|
|
+** The carray "function" is really a virtual table with the
|
|
+** following schema:
|
|
+**
|
|
+** CREATE TABLE carray(
|
|
+** value,
|
|
+** pointer HIDDEN,
|
|
+** count HIDDEN,
|
|
+** ctype TEXT HIDDEN
|
|
+** );
|
|
+**
|
|
+** If the hidden columns "pointer" and "count" are unconstrained, then
|
|
+** the virtual table has no rows. Otherwise, the virtual table interprets
|
|
+** the integer value of "pointer" as a pointer to the array and "count"
|
|
+** as the number of elements in the array. The virtual table steps through
|
|
+** the array, element by element.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Allowed datatypes
|
|
+*/
|
|
+#define CARRAY_INT32 0
|
|
+#define CARRAY_INT64 1
|
|
+#define CARRAY_DOUBLE 2
|
|
+#define CARRAY_TEXT 3
|
|
+
|
|
+/*
|
|
+** Names of types
|
|
+*/
|
|
+static const char *const azType[] = { "int32", "int64", "double", "char*" };
|
|
+
|
|
+
|
|
+/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct carray_cursor carray_cursor;
|
|
+struct carray_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+ void *pPtr; /* Pointer to the array of values */
|
|
+ size_t iCnt; /* Number of integers in the array */
|
|
+ unsigned char eType; /* One of the CARRAY_type values */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The carrayConnect() method is invoked to create a new
|
|
+** carray_vtab that describes the carray virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for carray_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the carray_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against carray will look like.
|
|
+*/
|
|
+static int carrayConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define CARRAY_COLUMN_VALUE 0
|
|
+#define CARRAY_COLUMN_POINTER 1
|
|
+#define CARRAY_COLUMN_COUNT 2
|
|
+#define CARRAY_COLUMN_CTYPE 3
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for carray_cursor objects.
|
|
+*/
|
|
+static int carrayDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new carray_cursor object.
|
|
+*/
|
|
+static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ carray_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a carray_cursor.
|
|
+*/
|
|
+static int carrayClose(sqlite3_vtab_cursor *cur){
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a carray_cursor to its next row of output.
|
|
+*/
|
|
+static int carrayNext(sqlite3_vtab_cursor *cur){
|
|
+ carray_cursor *pCur = (carray_cursor*)cur;
|
|
+ pCur->iRowid++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the carray_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int carrayColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ carray_cursor *pCur = (carray_cursor*)cur;
|
|
+ sqlite3_int64 x = 0;
|
|
+ switch( i ){
|
|
+ case CARRAY_COLUMN_POINTER: return SQLITE_OK;
|
|
+ case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
|
|
+ case CARRAY_COLUMN_CTYPE: {
|
|
+ sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ default: {
|
|
+ switch( pCur->eType ){
|
|
+ case CARRAY_INT32: {
|
|
+ int *p = (int*)pCur->pPtr;
|
|
+ sqlite3_result_int(ctx, p[pCur->iRowid-1]);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ case CARRAY_INT64: {
|
|
+ sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr;
|
|
+ sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ case CARRAY_DOUBLE: {
|
|
+ double *p = (double*)pCur->pPtr;
|
|
+ sqlite3_result_double(ctx, p[pCur->iRowid-1]);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ case CARRAY_TEXT: {
|
|
+ const char **p = (const char**)pCur->pPtr;
|
|
+ sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_int64(ctx, x);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ carray_cursor *pCur = (carray_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int carrayEof(sqlite3_vtab_cursor *cur){
|
|
+ carray_cursor *pCur = (carray_cursor*)cur;
|
|
+ return pCur->iRowid>pCur->iCnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the carray_cursor object back
|
|
+** to the first row of output.
|
|
+*/
|
|
+static int carrayFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ carray_cursor *pCur = (carray_cursor *)pVtabCursor;
|
|
+ if( idxNum ){
|
|
+ pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
|
|
+ pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
|
|
+ if( idxNum<3 ){
|
|
+ pCur->eType = CARRAY_INT32;
|
|
+ }else{
|
|
+ unsigned char i;
|
|
+ const char *zType = (const char*)sqlite3_value_text(argv[2]);
|
|
+ for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
|
|
+ if( sqlite3_stricmp(zType, azType[i])==0 ) break;
|
|
+ }
|
|
+ if( i>=sizeof(azType)/sizeof(azType[0]) ){
|
|
+ pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
|
|
+ "unknown datatype: %Q", zType);
|
|
+ return SQLITE_ERROR;
|
|
+ }else{
|
|
+ pCur->eType = i;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ pCur->pPtr = 0;
|
|
+ pCur->iCnt = 0;
|
|
+ }
|
|
+ pCur->iRowid = 1;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the carray virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+**
|
|
+** In this implementation idxNum is used to represent the
|
|
+** query plan. idxStr is unused.
|
|
+**
|
|
+** idxNum is 2 if the pointer= and count= constraints exist,
|
|
+** 3 if the ctype= constraint also exists, and is 0 otherwise.
|
|
+** If idxNum is 0, then carray becomes an empty table.
|
|
+*/
|
|
+static int carrayBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop over constraints */
|
|
+ int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
|
|
+ int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
|
|
+ int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
|
|
+
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ switch( pConstraint->iColumn ){
|
|
+ case CARRAY_COLUMN_POINTER:
|
|
+ ptrIdx = i;
|
|
+ break;
|
|
+ case CARRAY_COLUMN_COUNT:
|
|
+ cntIdx = i;
|
|
+ break;
|
|
+ case CARRAY_COLUMN_CTYPE:
|
|
+ ctypeIdx = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( ptrIdx>=0 && cntIdx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
|
|
+ pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
|
|
+ pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
|
|
+ pIdxInfo->estimatedCost = (double)1;
|
|
+ pIdxInfo->estimatedRows = 100;
|
|
+ pIdxInfo->idxNum = 2;
|
|
+ if( ctypeIdx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
|
|
+ pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
|
|
+ pIdxInfo->idxNum = 3;
|
|
+ }
|
|
+ }else{
|
|
+ pIdxInfo->estimatedCost = (double)2147483647;
|
|
+ pIdxInfo->estimatedRows = 2147483647;
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** carray virtual table.
|
|
+*/
|
|
+static const sqlite3_module carrayModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ carrayConnect, /* xConnect */
|
|
+ carrayBestIndex, /* xBestIndex */
|
|
+ carrayDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ carrayOpen, /* xOpen - open a cursor */
|
|
+ carrayClose, /* xClose - close a cursor */
|
|
+ carrayFilter, /* xFilter - configure scan constraints */
|
|
+ carrayNext, /* xNext - advance a cursor */
|
|
+ carrayEof, /* xEof - check for end of scan */
|
|
+ carrayColumn, /* xColumn - read data */
|
|
+ carrayRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+
|
|
+/*
|
|
+** For testing purpose in the TCL test harness, we need a method for
|
|
+** setting the pointer value. The inttoptr(X) SQL function accomplishes
|
|
+** this. Tcl script will bind an integer to X and the inttoptr() SQL
|
|
+** function will use sqlite3_result_pointer() to convert that integer into
|
|
+** a pointer.
|
|
+**
|
|
+** This is for testing on TCL only.
|
|
+*/
|
|
+#ifdef SQLITE_TEST
|
|
+static void inttoptrFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ void *p;
|
|
+ sqlite3_int64 i64;
|
|
+ i64 = sqlite3_value_int64(argv[0]);
|
|
+ if( sizeof(i64)==sizeof(p) ){
|
|
+ memcpy(&p, &i64, sizeof(p));
|
|
+ }else{
|
|
+ int i32 = i64 & 0xffffffff;
|
|
+ memcpy(&p, &i32, sizeof(p));
|
|
+ }
|
|
+ sqlite3_result_pointer(context, p, "carray", 0);
|
|
+}
|
|
+#endif /* SQLITE_TEST */
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_carray_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
|
|
+#ifdef SQLITE_TEST
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "inttoptr", 1, SQLITE_UTF8, 0,
|
|
+ inttoptrFunc, 0, 0);
|
|
+ }
|
|
+#endif /* SQLITE_TEST */
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/closure.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/closure.c 2019-05-14 16:22:31.225398300 +0200
|
|
@@ -0,0 +1,980 @@
|
|
+/*
|
|
+** 2013-04-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 for a virtual table that finds the transitive
|
|
+** closure of a parent/child relationship in a real table. The virtual
|
|
+** table is called "transitive_closure".
|
|
+**
|
|
+** A transitive_closure virtual table is created like this:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE x USING transitive_closure(
|
|
+** tablename=<tablename>, -- T
|
|
+** idcolumn=<columnname>, -- X
|
|
+** parentcolumn=<columnname> -- P
|
|
+** );
|
|
+**
|
|
+** When it is created, the new transitive_closure table may be supplied
|
|
+** with default values for the name of a table T and columns T.X and T.P.
|
|
+** The T.X and T.P columns must contain integers. The ideal case is for
|
|
+** T.X to be the INTEGER PRIMARY KEY. The T.P column should reference
|
|
+** the T.X column. The row referenced by T.P is the parent of the current row.
|
|
+**
|
|
+** The tablename, idcolumn, and parentcolumn supplied by the CREATE VIRTUAL
|
|
+** TABLE statement may be overridden in individual queries by including
|
|
+** terms like tablename='newtable', idcolumn='id2', or
|
|
+** parentcolumn='parent3' in the WHERE clause of the query.
|
|
+**
|
|
+** For efficiency, it is essential that there be an index on the P column:
|
|
+**
|
|
+** CREATE Tidx1 ON T(P)
|
|
+**
|
|
+** Suppose a specific instance of the closure table is as follows:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE ct1 USING transitive_closure(
|
|
+** tablename='group',
|
|
+** idcolumn='groupId',
|
|
+** parentcolumn='parentId'
|
|
+** );
|
|
+**
|
|
+** Such an instance of the transitive_closure virtual table would be
|
|
+** appropriate for walking a tree defined using a table like this, for example:
|
|
+**
|
|
+** CREATE TABLE group(
|
|
+** groupId INTEGER PRIMARY KEY,
|
|
+** parentId INTEGER REFERENCES group
|
|
+** );
|
|
+** CREATE INDEX group_idx1 ON group(parentId);
|
|
+**
|
|
+** The group table above would presumably have other application-specific
|
|
+** fields. The key point here is that rows of the group table form a
|
|
+** tree. The purpose of the ct1 virtual table is to easily extract
|
|
+** branches of that tree.
|
|
+**
|
|
+** Once it has been created, the ct1 virtual table can be queried
|
|
+** as follows:
|
|
+**
|
|
+** SELECT * FROM element
|
|
+** WHERE element.groupId IN (SELECT id FROM ct1 WHERE root=?1);
|
|
+**
|
|
+** The above query will return all elements that are part of group ?1
|
|
+** or children of group ?1 or grand-children of ?1 and so forth for all
|
|
+** descendents of group ?1. The same query can be formulated as a join:
|
|
+**
|
|
+** SELECT element.* FROM element, ct1
|
|
+** WHERE element.groupid=ct1.id
|
|
+** AND ct1.root=?1;
|
|
+**
|
|
+** The depth of the transitive_closure (the number of generations of
|
|
+** parent/child relations to follow) can be limited by setting "depth"
|
|
+** column in the WHERE clause. So, for example, the following query
|
|
+** finds only children and grandchildren but no further descendents:
|
|
+**
|
|
+** SELECT element.* FROM element, ct1
|
|
+** WHERE element.groupid=ct1.id
|
|
+** AND ct1.root=?1
|
|
+** AND ct1.depth<=2;
|
|
+**
|
|
+** The "ct1.depth<=2" term could be a strict equality "ct1.depth=2" in
|
|
+** order to find only the grandchildren of ?1, not ?1 itself or the
|
|
+** children of ?1.
|
|
+**
|
|
+** The root=?1 term must be supplied in WHERE clause or else the query
|
|
+** of the ct1 virtual table will return an empty set. The tablename,
|
|
+** idcolumn, and parentcolumn attributes can be overridden in the WHERE
|
|
+** clause if desired. So, for example, the ct1 table could be repurposed
|
|
+** to find ancestors rather than descendents by inverting the roles of
|
|
+** the idcolumn and parentcolumn:
|
|
+**
|
|
+** SELECT element.* FROM element, ct1
|
|
+** WHERE element.groupid=ct1.id
|
|
+** AND ct1.root=?1
|
|
+** AND ct1.idcolumn='parentId'
|
|
+** AND ct1.parentcolumn='groupId';
|
|
+**
|
|
+** Multiple calls to ct1 could be combined. For example, the following
|
|
+** query finds all elements that "cousins" of groupId ?1. That is to say
|
|
+** elements where the groupId is a grandchild of the grandparent of ?1.
|
|
+** (This definition of "cousins" also includes siblings and self.)
|
|
+**
|
|
+** SELECT element.* FROM element, ct1
|
|
+** WHERE element.groupId=ct1.id
|
|
+** AND ct1.depth=2
|
|
+** AND ct1.root IN (SELECT id FROM ct1
|
|
+** WHERE root=?1
|
|
+** AND depth=2
|
|
+** AND idcolumn='parentId'
|
|
+** AND parentcolumn='groupId');
|
|
+**
|
|
+** In our example, the group.groupId column is unique and thus the
|
|
+** subquery will return exactly one row. For that reason, the IN
|
|
+** operator could be replaced by "=" to get the same result. But
|
|
+** in the general case where the idcolumn is not unique, an IN operator
|
|
+** would be required for this kind of query.
|
|
+**
|
|
+** Note that because the tablename, idcolumn, and parentcolumn can
|
|
+** all be specified in the query, it is possible for an application
|
|
+** to define a single transitive_closure virtual table for use on lots
|
|
+** of different hierarchy tables. One might say:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
|
|
+**
|
|
+** As each database connection is being opened. Then the application
|
|
+** would always have a "closure" virtual table handy to use for querying.
|
|
+**
|
|
+** SELECT element.* FROM element, closure
|
|
+** WHERE element.groupid=ct1.id
|
|
+** AND closure.root=?1
|
|
+** AND closure.tablename='group'
|
|
+** AND closure.idname='groupId'
|
|
+** AND closure.parentname='parentId';
|
|
+**
|
|
+** See the documentation at http://www.sqlite.org/loadext.html for information
|
|
+** on how to compile and use loadable extensions such as this one.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include <stdio.h>
|
|
+#include <ctype.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this implementation
|
|
+*/
|
|
+typedef struct closure_vtab closure_vtab;
|
|
+typedef struct closure_cursor closure_cursor;
|
|
+typedef struct closure_queue closure_queue;
|
|
+typedef struct closure_avl closure_avl;
|
|
+
|
|
+/*****************************************************************************
|
|
+** AVL Tree implementation
|
|
+*/
|
|
+/*
|
|
+** Objects that want to be members of the AVL tree should embedded an
|
|
+** instance of this structure.
|
|
+*/
|
|
+struct closure_avl {
|
|
+ sqlite3_int64 id; /* Id of this entry in the table */
|
|
+ int iGeneration; /* Which generation is this entry part of */
|
|
+ closure_avl *pList; /* A linked list of nodes */
|
|
+ closure_avl *pBefore; /* Other elements less than id */
|
|
+ closure_avl *pAfter; /* Other elements greater than id */
|
|
+ closure_avl *pUp; /* Parent element */
|
|
+ short int height; /* Height of this node. Leaf==1 */
|
|
+ short int imbalance; /* Height difference between pBefore and pAfter */
|
|
+};
|
|
+
|
|
+/* Recompute the closure_avl.height and closure_avl.imbalance fields for p.
|
|
+** Assume that the children of p have correct heights.
|
|
+*/
|
|
+static void closureAvlRecomputeHeight(closure_avl *p){
|
|
+ short int hBefore = p->pBefore ? p->pBefore->height : 0;
|
|
+ short int hAfter = p->pAfter ? p->pAfter->height : 0;
|
|
+ p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
|
|
+ p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** P B
|
|
+** / \ / \
|
|
+** B Z ==> X P
|
|
+** / \ / \
|
|
+** X Y Y Z
|
|
+**
|
|
+*/
|
|
+static closure_avl *closureAvlRotateBefore(closure_avl *pP){
|
|
+ closure_avl *pB = pP->pBefore;
|
|
+ closure_avl *pY = pB->pAfter;
|
|
+ pB->pUp = pP->pUp;
|
|
+ pB->pAfter = pP;
|
|
+ pP->pUp = pB;
|
|
+ pP->pBefore = pY;
|
|
+ if( pY ) pY->pUp = pP;
|
|
+ closureAvlRecomputeHeight(pP);
|
|
+ closureAvlRecomputeHeight(pB);
|
|
+ return pB;
|
|
+}
|
|
+
|
|
+/*
|
|
+** P A
|
|
+** / \ / \
|
|
+** X A ==> P Z
|
|
+** / \ / \
|
|
+** Y Z X Y
|
|
+**
|
|
+*/
|
|
+static closure_avl *closureAvlRotateAfter(closure_avl *pP){
|
|
+ closure_avl *pA = pP->pAfter;
|
|
+ closure_avl *pY = pA->pBefore;
|
|
+ pA->pUp = pP->pUp;
|
|
+ pA->pBefore = pP;
|
|
+ pP->pUp = pA;
|
|
+ pP->pAfter = pY;
|
|
+ if( pY ) pY->pUp = pP;
|
|
+ closureAvlRecomputeHeight(pP);
|
|
+ closureAvlRecomputeHeight(pA);
|
|
+ return pA;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the pBefore or pAfter pointer in the parent
|
|
+** of p that points to p. Or if p is the root node, return pp.
|
|
+*/
|
|
+static closure_avl **closureAvlFromPtr(closure_avl *p, closure_avl **pp){
|
|
+ closure_avl *pUp = p->pUp;
|
|
+ if( pUp==0 ) return pp;
|
|
+ if( pUp->pAfter==p ) return &pUp->pAfter;
|
|
+ return &pUp->pBefore;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Rebalance all nodes starting with p and working up to the root.
|
|
+** Return the new root.
|
|
+*/
|
|
+static closure_avl *closureAvlBalance(closure_avl *p){
|
|
+ closure_avl *pTop = p;
|
|
+ closure_avl **pp;
|
|
+ while( p ){
|
|
+ closureAvlRecomputeHeight(p);
|
|
+ if( p->imbalance>=2 ){
|
|
+ closure_avl *pB = p->pBefore;
|
|
+ if( pB->imbalance<0 ) p->pBefore = closureAvlRotateAfter(pB);
|
|
+ pp = closureAvlFromPtr(p,&p);
|
|
+ p = *pp = closureAvlRotateBefore(p);
|
|
+ }else if( p->imbalance<=(-2) ){
|
|
+ closure_avl *pA = p->pAfter;
|
|
+ if( pA->imbalance>0 ) p->pAfter = closureAvlRotateBefore(pA);
|
|
+ pp = closureAvlFromPtr(p,&p);
|
|
+ p = *pp = closureAvlRotateAfter(p);
|
|
+ }
|
|
+ pTop = p;
|
|
+ p = p->pUp;
|
|
+ }
|
|
+ return pTop;
|
|
+}
|
|
+
|
|
+/* Search the tree rooted at p for an entry with id. Return a pointer
|
|
+** to the entry or return NULL.
|
|
+*/
|
|
+static closure_avl *closureAvlSearch(closure_avl *p, sqlite3_int64 id){
|
|
+ while( p && id!=p->id ){
|
|
+ p = (id<p->id) ? p->pBefore : p->pAfter;
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/* Find the first node (the one with the smallest key).
|
|
+*/
|
|
+static closure_avl *closureAvlFirst(closure_avl *p){
|
|
+ if( p ) while( p->pBefore ) p = p->pBefore;
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/* Return the node with the next larger key after p.
|
|
+*/
|
|
+closure_avl *closureAvlNext(closure_avl *p){
|
|
+ closure_avl *pPrev = 0;
|
|
+ while( p && p->pAfter==pPrev ){
|
|
+ pPrev = p;
|
|
+ p = p->pUp;
|
|
+ }
|
|
+ if( p && pPrev==0 ){
|
|
+ p = closureAvlFirst(p->pAfter);
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/* Insert a new node pNew. Return NULL on success. If the key is not
|
|
+** unique, then do not perform the insert but instead leave pNew unchanged
|
|
+** and return a pointer to an existing node with the same key.
|
|
+*/
|
|
+static closure_avl *closureAvlInsert(
|
|
+ closure_avl **ppHead, /* Head of the tree */
|
|
+ closure_avl *pNew /* New node to be inserted */
|
|
+){
|
|
+ closure_avl *p = *ppHead;
|
|
+ if( p==0 ){
|
|
+ p = pNew;
|
|
+ pNew->pUp = 0;
|
|
+ }else{
|
|
+ while( p ){
|
|
+ if( pNew->id<p->id ){
|
|
+ if( p->pBefore ){
|
|
+ p = p->pBefore;
|
|
+ }else{
|
|
+ p->pBefore = pNew;
|
|
+ pNew->pUp = p;
|
|
+ break;
|
|
+ }
|
|
+ }else if( pNew->id>p->id ){
|
|
+ if( p->pAfter ){
|
|
+ p = p->pAfter;
|
|
+ }else{
|
|
+ p->pAfter = pNew;
|
|
+ pNew->pUp = p;
|
|
+ break;
|
|
+ }
|
|
+ }else{
|
|
+ return p;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ pNew->pBefore = 0;
|
|
+ pNew->pAfter = 0;
|
|
+ pNew->height = 1;
|
|
+ pNew->imbalance = 0;
|
|
+ *ppHead = closureAvlBalance(p);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Walk the tree can call xDestroy on each node
|
|
+*/
|
|
+static void closureAvlDestroy(closure_avl *p, void (*xDestroy)(closure_avl*)){
|
|
+ if( p ){
|
|
+ closureAvlDestroy(p->pBefore, xDestroy);
|
|
+ closureAvlDestroy(p->pAfter, xDestroy);
|
|
+ xDestroy(p);
|
|
+ }
|
|
+}
|
|
+/*
|
|
+** End of the AVL Tree implementation
|
|
+******************************************************************************/
|
|
+
|
|
+/*
|
|
+** A closure virtual-table object
|
|
+*/
|
|
+struct closure_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ char *zDb; /* Name of database. (ex: "main") */
|
|
+ char *zSelf; /* Name of this virtual table */
|
|
+ char *zTableName; /* Name of table holding parent/child relation */
|
|
+ char *zIdColumn; /* Name of ID column of zTableName */
|
|
+ char *zParentColumn; /* Name of PARENT column in zTableName */
|
|
+ sqlite3 *db; /* The database connection */
|
|
+ int nCursor; /* Number of pending cursors */
|
|
+};
|
|
+
|
|
+/* A closure cursor object */
|
|
+struct closure_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ closure_vtab *pVtab; /* The virtual table this cursor belongs to */
|
|
+ char *zTableName; /* Name of table holding parent/child relation */
|
|
+ char *zIdColumn; /* Name of ID column of zTableName */
|
|
+ char *zParentColumn; /* Name of PARENT column in zTableName */
|
|
+ closure_avl *pCurrent; /* Current element of output */
|
|
+ closure_avl *pClosure; /* The complete closure tree */
|
|
+};
|
|
+
|
|
+/* A queue of AVL nodes */
|
|
+struct closure_queue {
|
|
+ closure_avl *pFirst; /* Oldest node on the queue */
|
|
+ closure_avl *pLast; /* Youngest node on the queue */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Add a node to the end of the queue
|
|
+*/
|
|
+static void queuePush(closure_queue *pQueue, closure_avl *pNode){
|
|
+ pNode->pList = 0;
|
|
+ if( pQueue->pLast ){
|
|
+ pQueue->pLast->pList = pNode;
|
|
+ }else{
|
|
+ pQueue->pFirst = pNode;
|
|
+ }
|
|
+ pQueue->pLast = pNode;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Extract the oldest element (the front element) from the queue.
|
|
+*/
|
|
+static closure_avl *queuePull(closure_queue *pQueue){
|
|
+ closure_avl *p = pQueue->pFirst;
|
|
+ if( p ){
|
|
+ pQueue->pFirst = p->pList;
|
|
+ if( pQueue->pFirst==0 ) pQueue->pLast = 0;
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function converts an SQL quoted string into an unquoted string
|
|
+** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
|
+** containing the result. The caller should eventually free this buffer
|
|
+** using sqlite3_free.
|
|
+**
|
|
+** Examples:
|
|
+**
|
|
+** "abc" becomes abc
|
|
+** 'xyz' becomes xyz
|
|
+** [pqr] becomes pqr
|
|
+** `mno` becomes mno
|
|
+*/
|
|
+static char *closureDequote(const char *zIn){
|
|
+ size_t nIn; /* Size of input string, in bytes */
|
|
+ char *zOut; /* Output (dequoted) string */
|
|
+
|
|
+ nIn = strlen(zIn);
|
|
+ zOut = sqlite3_malloc(nIn+1);
|
|
+ if( zOut ){
|
|
+ char q = zIn[0]; /* Quote character (if any ) */
|
|
+
|
|
+ if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
|
+ memcpy(zOut, zIn, (size_t)(nIn+1));
|
|
+ }else{
|
|
+ size_t iOut = 0; /* Index of next byte to write to output */
|
|
+ size_t iIn; /* Index of next byte to read from input */
|
|
+
|
|
+ if( q=='[' ) q = ']';
|
|
+ for(iIn=1; iIn<nIn; iIn++){
|
|
+ if( zIn[iIn]==q ) iIn++;
|
|
+ zOut[iOut++] = zIn[iIn];
|
|
+ }
|
|
+ }
|
|
+ assert( strlen(zOut)<=nIn );
|
|
+ }
|
|
+ return zOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Deallocate an closure_vtab object
|
|
+*/
|
|
+static void closureFree(closure_vtab *p){
|
|
+ if( p ){
|
|
+ sqlite3_free(p->zDb);
|
|
+ sqlite3_free(p->zSelf);
|
|
+ sqlite3_free(p->zTableName);
|
|
+ sqlite3_free(p->zIdColumn);
|
|
+ sqlite3_free(p->zParentColumn);
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xDisconnect/xDestroy method for the closure module.
|
|
+*/
|
|
+static int closureDisconnect(sqlite3_vtab *pVtab){
|
|
+ closure_vtab *p = (closure_vtab*)pVtab;
|
|
+ assert( p->nCursor==0 );
|
|
+ closureFree(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check to see if the argument is of the form:
|
|
+**
|
|
+** KEY = VALUE
|
|
+**
|
|
+** If it is, return a pointer to the first character of VALUE.
|
|
+** If not, return NULL. Spaces around the = are ignored.
|
|
+*/
|
|
+static const char *closureValueOfKey(const char *zKey, const char *zStr){
|
|
+ int nKey = (int)strlen(zKey);
|
|
+ int nStr = (int)strlen(zStr);
|
|
+ int i;
|
|
+ if( nStr<nKey+1 ) return 0;
|
|
+ if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
|
|
+ for(i=nKey; isspace((unsigned char)zStr[i]); i++){}
|
|
+ if( zStr[i]!='=' ) return 0;
|
|
+ i++;
|
|
+ while( isspace((unsigned char)zStr[i]) ){ i++; }
|
|
+ return zStr+i;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xConnect/xCreate method for the closure module. Arguments are:
|
|
+**
|
|
+** argv[0] -> module name ("transitive_closure")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[3...] -> arguments
|
|
+*/
|
|
+static int closureConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ closure_vtab *pNew = 0; /* New virtual table */
|
|
+ const char *zDb = argv[1];
|
|
+ const char *zVal;
|
|
+ int i;
|
|
+
|
|
+ (void)pAux;
|
|
+ *ppVtab = 0;
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ rc = SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->db = db;
|
|
+ pNew->zDb = sqlite3_mprintf("%s", zDb);
|
|
+ if( pNew->zDb==0 ) goto closureConnectError;
|
|
+ pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
|
|
+ if( pNew->zSelf==0 ) goto closureConnectError;
|
|
+ for(i=3; i<argc; i++){
|
|
+ zVal = closureValueOfKey("tablename", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zTableName);
|
|
+ pNew->zTableName = closureDequote(zVal);
|
|
+ if( pNew->zTableName==0 ) goto closureConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ zVal = closureValueOfKey("idcolumn", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zIdColumn);
|
|
+ pNew->zIdColumn = closureDequote(zVal);
|
|
+ if( pNew->zIdColumn==0 ) goto closureConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ zVal = closureValueOfKey("parentcolumn", argv[i]);
|
|
+ if( zVal ){
|
|
+ sqlite3_free(pNew->zParentColumn);
|
|
+ pNew->zParentColumn = closureDequote(zVal);
|
|
+ if( pNew->zParentColumn==0 ) goto closureConnectError;
|
|
+ continue;
|
|
+ }
|
|
+ *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
|
|
+ closureFree(pNew);
|
|
+ *ppVtab = 0;
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,"
|
|
+ "idcolumn HIDDEN,parentcolumn HIDDEN)"
|
|
+ );
|
|
+#define CLOSURE_COL_ID 0
|
|
+#define CLOSURE_COL_DEPTH 1
|
|
+#define CLOSURE_COL_ROOT 2
|
|
+#define CLOSURE_COL_TABLENAME 3
|
|
+#define CLOSURE_COL_IDCOLUMN 4
|
|
+#define CLOSURE_COL_PARENTCOLUMN 5
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ closureFree(pNew);
|
|
+ }
|
|
+ *ppVtab = &pNew->base;
|
|
+ return rc;
|
|
+
|
|
+closureConnectError:
|
|
+ closureFree(pNew);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a new closure cursor.
|
|
+*/
|
|
+static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ closure_vtab *p = (closure_vtab*)pVTab;
|
|
+ closure_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->pVtab = p;
|
|
+ *ppCursor = &pCur->base;
|
|
+ p->nCursor++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
|
+** to indicate that it is at EOF.
|
|
+*/
|
|
+static void closureClearCursor(closure_cursor *pCur){
|
|
+ closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free);
|
|
+ sqlite3_free(pCur->zTableName);
|
|
+ sqlite3_free(pCur->zIdColumn);
|
|
+ sqlite3_free(pCur->zParentColumn);
|
|
+ pCur->zTableName = 0;
|
|
+ pCur->zIdColumn = 0;
|
|
+ pCur->zParentColumn = 0;
|
|
+ pCur->pCurrent = 0;
|
|
+ pCur->pClosure = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close a closure cursor.
|
|
+*/
|
|
+static int closureClose(sqlite3_vtab_cursor *cur){
|
|
+ closure_cursor *pCur = (closure_cursor *)cur;
|
|
+ closureClearCursor(pCur);
|
|
+ pCur->pVtab->nCursor--;
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance a cursor to its next row of output
|
|
+*/
|
|
+static int closureNext(sqlite3_vtab_cursor *cur){
|
|
+ closure_cursor *pCur = (closure_cursor*)cur;
|
|
+ pCur->pCurrent = closureAvlNext(pCur->pCurrent);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate and insert a node
|
|
+*/
|
|
+static int closureInsertNode(
|
|
+ closure_queue *pQueue, /* Add new node to this queue */
|
|
+ closure_cursor *pCur, /* The cursor into which to add the node */
|
|
+ sqlite3_int64 id, /* The node ID */
|
|
+ int iGeneration /* The generation number for this node */
|
|
+){
|
|
+ closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->id = id;
|
|
+ pNew->iGeneration = iGeneration;
|
|
+ closureAvlInsert(&pCur->pClosure, pNew);
|
|
+ queuePush(pQueue, pNew);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called to "rewind" a cursor back to the beginning so that
|
|
+** it starts its output over again. Always called at least once
|
|
+** prior to any closureColumn, closureRowid, or closureEof call.
|
|
+**
|
|
+** This routine actually computes the closure.
|
|
+**
|
|
+** See the comment at the beginning of closureBestIndex() for a
|
|
+** description of the meaning of idxNum. The idxStr parameter is
|
|
+** not used.
|
|
+*/
|
|
+static int closureFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ closure_cursor *pCur = (closure_cursor *)pVtabCursor;
|
|
+ closure_vtab *pVtab = pCur->pVtab;
|
|
+ sqlite3_int64 iRoot;
|
|
+ int mxGen = 999999999;
|
|
+ char *zSql;
|
|
+ sqlite3_stmt *pStmt;
|
|
+ closure_avl *pAvl;
|
|
+ int rc = SQLITE_OK;
|
|
+ const char *zTableName = pVtab->zTableName;
|
|
+ const char *zIdColumn = pVtab->zIdColumn;
|
|
+ const char *zParentColumn = pVtab->zParentColumn;
|
|
+ closure_queue sQueue;
|
|
+
|
|
+ (void)idxStr; /* Unused parameter */
|
|
+ (void)argc; /* Unused parameter */
|
|
+ closureClearCursor(pCur);
|
|
+ memset(&sQueue, 0, sizeof(sQueue));
|
|
+ if( (idxNum & 1)==0 ){
|
|
+ /* No root=$root in the WHERE clause. Return an empty set */
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ iRoot = sqlite3_value_int64(argv[0]);
|
|
+ if( (idxNum & 0x000f0)!=0 ){
|
|
+ mxGen = sqlite3_value_int(argv[(idxNum>>4)&0x0f]);
|
|
+ if( (idxNum & 0x00002)!=0 ) mxGen--;
|
|
+ }
|
|
+ if( (idxNum & 0x00f00)!=0 ){
|
|
+ zTableName = (const char*)sqlite3_value_text(argv[(idxNum>>8)&0x0f]);
|
|
+ pCur->zTableName = sqlite3_mprintf("%s", zTableName);
|
|
+ }
|
|
+ if( (idxNum & 0x0f000)!=0 ){
|
|
+ zIdColumn = (const char*)sqlite3_value_text(argv[(idxNum>>12)&0x0f]);
|
|
+ pCur->zIdColumn = sqlite3_mprintf("%s", zIdColumn);
|
|
+ }
|
|
+ if( (idxNum & 0x0f0000)!=0 ){
|
|
+ zParentColumn = (const char*)sqlite3_value_text(argv[(idxNum>>16)&0x0f]);
|
|
+ pCur->zParentColumn = sqlite3_mprintf("%s", zParentColumn);
|
|
+ }
|
|
+
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT \"%w\".\"%w\" FROM \"%w\" WHERE \"%w\".\"%w\"=?1",
|
|
+ zTableName, zIdColumn, zTableName, zTableName, zParentColumn);
|
|
+ if( zSql==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ){
|
|
+ sqlite3_free(pVtab->base.zErrMsg);
|
|
+ pVtab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pVtab->db));
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = closureInsertNode(&sQueue, pCur, iRoot, 0);
|
|
+ }
|
|
+ while( (pAvl = queuePull(&sQueue))!=0 ){
|
|
+ if( pAvl->iGeneration>=mxGen ) continue;
|
|
+ sqlite3_bind_int64(pStmt, 1, pAvl->id);
|
|
+ while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ if( sqlite3_column_type(pStmt,0)==SQLITE_INTEGER ){
|
|
+ sqlite3_int64 iNew = sqlite3_column_int64(pStmt, 0);
|
|
+ if( closureAvlSearch(pCur->pClosure, iNew)==0 ){
|
|
+ rc = closureInsertNode(&sQueue, pCur, iNew, pAvl->iGeneration+1);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_reset(pStmt);
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pCur->pCurrent = closureAvlFirst(pCur->pClosure);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only the word and distance columns have values. All other columns
|
|
+** return NULL
|
|
+*/
|
|
+static int closureColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
|
+ closure_cursor *pCur = (closure_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case CLOSURE_COL_ID: {
|
|
+ sqlite3_result_int64(ctx, pCur->pCurrent->id);
|
|
+ break;
|
|
+ }
|
|
+ case CLOSURE_COL_DEPTH: {
|
|
+ sqlite3_result_int(ctx, pCur->pCurrent->iGeneration);
|
|
+ break;
|
|
+ }
|
|
+ case CLOSURE_COL_ROOT: {
|
|
+ sqlite3_result_null(ctx);
|
|
+ break;
|
|
+ }
|
|
+ case CLOSURE_COL_TABLENAME: {
|
|
+ sqlite3_result_text(ctx,
|
|
+ pCur->zTableName ? pCur->zTableName : pCur->pVtab->zTableName,
|
|
+ -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ case CLOSURE_COL_IDCOLUMN: {
|
|
+ sqlite3_result_text(ctx,
|
|
+ pCur->zIdColumn ? pCur->zIdColumn : pCur->pVtab->zIdColumn,
|
|
+ -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ case CLOSURE_COL_PARENTCOLUMN: {
|
|
+ sqlite3_result_text(ctx,
|
|
+ pCur->zParentColumn ? pCur->zParentColumn : pCur->pVtab->zParentColumn,
|
|
+ -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The rowid. For the closure table, this is the same as the "id" column.
|
|
+*/
|
|
+static int closureRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ closure_cursor *pCur = (closure_cursor*)cur;
|
|
+ *pRowid = pCur->pCurrent->id;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** EOF indicator
|
|
+*/
|
|
+static int closureEof(sqlite3_vtab_cursor *cur){
|
|
+ closure_cursor *pCur = (closure_cursor*)cur;
|
|
+ return pCur->pCurrent==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Search for terms of these forms:
|
|
+**
|
|
+** (A) root = $root
|
|
+** (B1) depth < $depth
|
|
+** (B2) depth <= $depth
|
|
+** (B3) depth = $depth
|
|
+** (C) tablename = $tablename
|
|
+** (D) idcolumn = $idcolumn
|
|
+** (E) parentcolumn = $parentcolumn
|
|
+**
|
|
+**
|
|
+**
|
|
+** idxNum meaning
|
|
+** ---------- ------------------------------------------------------
|
|
+** 0x00000001 Term of the form (A) found
|
|
+** 0x00000002 The term of bit-2 is like (B1)
|
|
+** 0x000000f0 Index in filter.argv[] of $depth. 0 if not used.
|
|
+** 0x00000f00 Index in filter.argv[] of $tablename. 0 if not used.
|
|
+** 0x0000f000 Index in filter.argv[] of $idcolumn. 0 if not used
|
|
+** 0x000f0000 Index in filter.argv[] of $parentcolumn. 0 if not used.
|
|
+**
|
|
+** There must be a term of type (A). If there is not, then the index type
|
|
+** is 0 and the query will return an empty set.
|
|
+*/
|
|
+static int closureBestIndex(
|
|
+ sqlite3_vtab *pTab, /* The virtual table */
|
|
+ sqlite3_index_info *pIdxInfo /* Information about the query */
|
|
+){
|
|
+ int iPlan = 0;
|
|
+ int i;
|
|
+ int idx = 1;
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+ closure_vtab *pVtab = (closure_vtab*)pTab;
|
|
+ double rCost = 10000000.0;
|
|
+
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+ if( (iPlan & 1)==0
|
|
+ && pConstraint->iColumn==CLOSURE_COL_ROOT
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= 1;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ rCost /= 100.0;
|
|
+ }
|
|
+ if( (iPlan & 0x0000f0)==0
|
|
+ && pConstraint->iColumn==CLOSURE_COL_DEPTH
|
|
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
|
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE
|
|
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ)
|
|
+ ){
|
|
+ iPlan |= idx<<4;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
|
+ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
|
|
+ rCost /= 5.0;
|
|
+ }
|
|
+ if( (iPlan & 0x000f00)==0
|
|
+ && pConstraint->iColumn==CLOSURE_COL_TABLENAME
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= idx<<8;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ rCost /= 5.0;
|
|
+ }
|
|
+ if( (iPlan & 0x00f000)==0
|
|
+ && pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= idx<<12;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ }
|
|
+ if( (iPlan & 0x0f0000)==0
|
|
+ && pConstraint->iColumn==CLOSURE_COL_PARENTCOLUMN
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= idx<<16;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ }
|
|
+ }
|
|
+ if( (pVtab->zTableName==0 && (iPlan & 0x000f00)==0)
|
|
+ || (pVtab->zIdColumn==0 && (iPlan & 0x00f000)==0)
|
|
+ || (pVtab->zParentColumn==0 && (iPlan & 0x0f0000)==0)
|
|
+ ){
|
|
+ /* All of tablename, idcolumn, and parentcolumn must be specified
|
|
+ ** in either the CREATE VIRTUAL TABLE or in the WHERE clause constraints
|
|
+ ** or else the result is an empty set. */
|
|
+ iPlan = 0;
|
|
+ }
|
|
+ if( (iPlan&1)==0 ){
|
|
+ /* If there is no usable "root=?" term, then set the index-type to 0.
|
|
+ ** Also clear any argvIndex variables already set. This is necessary
|
|
+ ** to prevent the core from throwing an "xBestIndex malfunction error"
|
|
+ ** error (because the argvIndex values are not contiguously assigned
|
|
+ ** starting from 1). */
|
|
+ rCost *= 1e30;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 0;
|
|
+ }
|
|
+ iPlan = 0;
|
|
+ }
|
|
+ pIdxInfo->idxNum = iPlan;
|
|
+ if( pIdxInfo->nOrderBy==1
|
|
+ && pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
|
|
+ && pIdxInfo->aOrderBy[0].desc==0
|
|
+ ){
|
|
+ pIdxInfo->orderByConsumed = 1;
|
|
+ }
|
|
+ pIdxInfo->estimatedCost = rCost;
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** A virtual table module that implements the "transitive_closure".
|
|
+*/
|
|
+static const sqlite3_module closureModule = {
|
|
+ 0, /* iVersion */
|
|
+ closureConnect, /* xCreate */
|
|
+ closureConnect, /* xConnect */
|
|
+ closureBestIndex, /* xBestIndex */
|
|
+ closureDisconnect, /* xDisconnect */
|
|
+ closureDisconnect, /* xDestroy */
|
|
+ closureOpen, /* xOpen - open a cursor */
|
|
+ closureClose, /* xClose - close a cursor */
|
|
+ closureFilter, /* xFilter - configure scan constraints */
|
|
+ closureNext, /* xNext - advance a cursor */
|
|
+ closureEof, /* xEof - check for end of scan */
|
|
+ closureColumn, /* xColumn - read data */
|
|
+ closureRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+/*
|
|
+** Register the closure virtual table
|
|
+*/
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_closure_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg;
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg;
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/completion.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/completion.c 2019-05-14 16:22:31.227351000 +0200
|
|
@@ -0,0 +1,515 @@
|
|
+/*
|
|
+** 2017-07-10
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements an eponymous virtual table that returns suggested
|
|
+** completions for a partial SQL input.
|
|
+**
|
|
+** Suggested usage:
|
|
+**
|
|
+** SELECT DISTINCT candidate COLLATE nocase
|
|
+** FROM completion($prefix,$wholeline)
|
|
+** ORDER BY 1;
|
|
+**
|
|
+** The two query parameters are optional. $prefix is the text of the
|
|
+** current word being typed and that is to be completed. $wholeline is
|
|
+** the complete input line, used for context.
|
|
+**
|
|
+** The raw completion() table might return the same candidate multiple
|
|
+** times, for example if the same column name is used to two or more
|
|
+** tables. And the candidates are returned in an arbitrary order. Hence,
|
|
+** the DISTINCT and ORDER BY are recommended.
|
|
+**
|
|
+** This virtual table operates at the speed of human typing, and so there
|
|
+** is no attempt to make it fast. Even a slow implementation will be much
|
|
+** faster than any human can type.
|
|
+**
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <ctype.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/* completion_vtab is a subclass of sqlite3_vtab which will
|
|
+** serve as the underlying representation of a completion virtual table
|
|
+*/
|
|
+typedef struct completion_vtab completion_vtab;
|
|
+struct completion_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this completion vtab */
|
|
+};
|
|
+
|
|
+/* completion_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct completion_cursor completion_cursor;
|
|
+struct completion_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this cursor */
|
|
+ int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */
|
|
+ char *zPrefix; /* The prefix for the word we want to complete */
|
|
+ char *zLine; /* The whole that we want to complete */
|
|
+ const char *zCurrentRow; /* Current output row */
|
|
+ int szRow; /* Length of the zCurrentRow string */
|
|
+ sqlite3_stmt *pStmt; /* Current statement */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+ int ePhase; /* Current phase */
|
|
+ int j; /* inter-phase counter */
|
|
+};
|
|
+
|
|
+/* Values for ePhase:
|
|
+*/
|
|
+#define COMPLETION_FIRST_PHASE 1
|
|
+#define COMPLETION_KEYWORDS 1
|
|
+#define COMPLETION_PRAGMAS 2
|
|
+#define COMPLETION_FUNCTIONS 3
|
|
+#define COMPLETION_COLLATIONS 4
|
|
+#define COMPLETION_INDEXES 5
|
|
+#define COMPLETION_TRIGGERS 6
|
|
+#define COMPLETION_DATABASES 7
|
|
+#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */
|
|
+#define COMPLETION_COLUMNS 9
|
|
+#define COMPLETION_MODULES 10
|
|
+#define COMPLETION_EOF 11
|
|
+
|
|
+/*
|
|
+** The completionConnect() method is invoked to create a new
|
|
+** completion_vtab that describes the completion virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for completion_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the completion_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against completion will look like.
|
|
+*/
|
|
+static int completionConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ completion_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+ (void)(pAux); /* Unused parameter */
|
|
+ (void)(argc); /* Unused parameter */
|
|
+ (void)(argv); /* Unused parameter */
|
|
+ (void)(pzErr); /* Unused parameter */
|
|
+
|
|
+/* Column numbers */
|
|
+#define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */
|
|
+#define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */
|
|
+#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */
|
|
+#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x("
|
|
+ " candidate TEXT,"
|
|
+ " prefix TEXT HIDDEN,"
|
|
+ " wholeline TEXT HIDDEN,"
|
|
+ " phase INT HIDDEN" /* Used for debugging only */
|
|
+ ")");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->db = db;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for completion_cursor objects.
|
|
+*/
|
|
+static int completionDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new completion_cursor object.
|
|
+*/
|
|
+static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ completion_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->db = ((completion_vtab*)p)->db;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset the completion_cursor.
|
|
+*/
|
|
+static void completionCursorReset(completion_cursor *pCur){
|
|
+ sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0; pCur->nPrefix = 0;
|
|
+ sqlite3_free(pCur->zLine); pCur->zLine = 0; pCur->nLine = 0;
|
|
+ sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0;
|
|
+ pCur->j = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a completion_cursor.
|
|
+*/
|
|
+static int completionClose(sqlite3_vtab_cursor *cur){
|
|
+ completionCursorReset((completion_cursor*)cur);
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance a completion_cursor to its next row of output.
|
|
+**
|
|
+** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object
|
|
+** record the current state of the scan. This routine sets ->zCurrentRow
|
|
+** to the current row of output and then returns. If no more rows remain,
|
|
+** then ->ePhase is set to COMPLETION_EOF which will signal the virtual
|
|
+** table that has reached the end of its scan.
|
|
+**
|
|
+** The current implementation just lists potential identifiers and
|
|
+** keywords and filters them by zPrefix. Future enhancements should
|
|
+** take zLine into account to try to restrict the set of identifiers and
|
|
+** keywords based on what would be legal at the current point of input.
|
|
+*/
|
|
+static int completionNext(sqlite3_vtab_cursor *cur){
|
|
+ completion_cursor *pCur = (completion_cursor*)cur;
|
|
+ int eNextPhase = 0; /* Next phase to try if current phase reaches end */
|
|
+ int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
|
|
+ pCur->iRowid++;
|
|
+ while( pCur->ePhase!=COMPLETION_EOF ){
|
|
+ switch( pCur->ePhase ){
|
|
+ case COMPLETION_KEYWORDS: {
|
|
+ if( pCur->j >= sqlite3_keyword_count() ){
|
|
+ pCur->zCurrentRow = 0;
|
|
+ pCur->ePhase = COMPLETION_DATABASES;
|
|
+ }else{
|
|
+ sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow);
|
|
+ }
|
|
+ iCol = -1;
|
|
+ break;
|
|
+ }
|
|
+ case COMPLETION_DATABASES: {
|
|
+ if( pCur->pStmt==0 ){
|
|
+ sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1,
|
|
+ &pCur->pStmt, 0);
|
|
+ }
|
|
+ iCol = 1;
|
|
+ eNextPhase = COMPLETION_TABLES;
|
|
+ break;
|
|
+ }
|
|
+ case COMPLETION_TABLES: {
|
|
+ if( pCur->pStmt==0 ){
|
|
+ sqlite3_stmt *pS2;
|
|
+ char *zSql = 0;
|
|
+ const char *zSep = "";
|
|
+ sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
|
|
+ while( sqlite3_step(pS2)==SQLITE_ROW ){
|
|
+ const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "%z%s"
|
|
+ "SELECT name FROM \"%w\".sqlite_master",
|
|
+ zSql, zSep, zDb
|
|
+ );
|
|
+ if( zSql==0 ) return SQLITE_NOMEM;
|
|
+ zSep = " UNION ";
|
|
+ }
|
|
+ sqlite3_finalize(pS2);
|
|
+ sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ iCol = 0;
|
|
+ eNextPhase = COMPLETION_COLUMNS;
|
|
+ break;
|
|
+ }
|
|
+ case COMPLETION_COLUMNS: {
|
|
+ if( pCur->pStmt==0 ){
|
|
+ sqlite3_stmt *pS2;
|
|
+ char *zSql = 0;
|
|
+ const char *zSep = "";
|
|
+ sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
|
|
+ while( sqlite3_step(pS2)==SQLITE_ROW ){
|
|
+ const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "%z%s"
|
|
+ "SELECT pti.name FROM \"%w\".sqlite_master AS sm"
|
|
+ " JOIN pragma_table_info(sm.name,%Q) AS pti"
|
|
+ " WHERE sm.type='table'",
|
|
+ zSql, zSep, zDb, zDb
|
|
+ );
|
|
+ if( zSql==0 ) return SQLITE_NOMEM;
|
|
+ zSep = " UNION ";
|
|
+ }
|
|
+ sqlite3_finalize(pS2);
|
|
+ sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ iCol = 0;
|
|
+ eNextPhase = COMPLETION_EOF;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( iCol<0 ){
|
|
+ /* This case is when the phase presets zCurrentRow */
|
|
+ if( pCur->zCurrentRow==0 ) continue;
|
|
+ }else{
|
|
+ if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){
|
|
+ /* Extract the next row of content */
|
|
+ pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol);
|
|
+ pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
|
|
+ }else{
|
|
+ /* When all rows are finished, advance to the next phase */
|
|
+ sqlite3_finalize(pCur->pStmt);
|
|
+ pCur->pStmt = 0;
|
|
+ pCur->ePhase = eNextPhase;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if( pCur->nPrefix==0 ) break;
|
|
+ if( pCur->nPrefix<=pCur->szRow
|
|
+ && sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0
|
|
+ ){
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the completion_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int completionColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ completion_cursor *pCur = (completion_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case COMPLETION_COLUMN_CANDIDATE: {
|
|
+ sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ case COMPLETION_COLUMN_PREFIX: {
|
|
+ sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ case COMPLETION_COLUMN_WHOLELINE: {
|
|
+ sqlite3_result_text(ctx, pCur->zLine, -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ case COMPLETION_COLUMN_PHASE: {
|
|
+ sqlite3_result_int(ctx, pCur->ePhase);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ completion_cursor *pCur = (completion_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int completionEof(sqlite3_vtab_cursor *cur){
|
|
+ completion_cursor *pCur = (completion_cursor*)cur;
|
|
+ return pCur->ePhase >= COMPLETION_EOF;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the completion_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to completionColumn() or completionRowid() or
|
|
+** completionEof().
|
|
+*/
|
|
+static int completionFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ completion_cursor *pCur = (completion_cursor *)pVtabCursor;
|
|
+ int iArg = 0;
|
|
+ (void)(idxStr); /* Unused parameter */
|
|
+ (void)(argc); /* Unused parameter */
|
|
+ completionCursorReset(pCur);
|
|
+ if( idxNum & 1 ){
|
|
+ pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
|
|
+ if( pCur->nPrefix>0 ){
|
|
+ pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
|
|
+ if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
|
|
+ }
|
|
+ iArg = 1;
|
|
+ }
|
|
+ if( idxNum & 2 ){
|
|
+ pCur->nLine = sqlite3_value_bytes(argv[iArg]);
|
|
+ if( pCur->nLine>0 ){
|
|
+ pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
|
|
+ if( pCur->zLine==0 ) return SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+ if( pCur->zLine!=0 && pCur->zPrefix==0 ){
|
|
+ int i = pCur->nLine;
|
|
+ while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
|
|
+ i--;
|
|
+ }
|
|
+ pCur->nPrefix = pCur->nLine - i;
|
|
+ if( pCur->nPrefix>0 ){
|
|
+ pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
|
|
+ if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+ pCur->iRowid = 0;
|
|
+ pCur->ePhase = COMPLETION_FIRST_PHASE;
|
|
+ return completionNext(pVtabCursor);
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the completion virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+**
|
|
+** There are two hidden parameters that act as arguments to the table-valued
|
|
+** function: "prefix" and "wholeline". Bit 0 of idxNum is set if "prefix"
|
|
+** is available and bit 1 is set if "wholeline" is available.
|
|
+*/
|
|
+static int completionBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop over constraints */
|
|
+ int idxNum = 0; /* The query plan bitmask */
|
|
+ int prefixIdx = -1; /* Index of the start= constraint, or -1 if none */
|
|
+ int wholelineIdx = -1; /* Index of the stop= constraint, or -1 if none */
|
|
+ int nArg = 0; /* Number of arguments that completeFilter() expects */
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ (void)(tab); /* Unused parameter */
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ switch( pConstraint->iColumn ){
|
|
+ case COMPLETION_COLUMN_PREFIX:
|
|
+ prefixIdx = i;
|
|
+ idxNum |= 1;
|
|
+ break;
|
|
+ case COMPLETION_COLUMN_WHOLELINE:
|
|
+ wholelineIdx = i;
|
|
+ idxNum |= 2;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( prefixIdx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg;
|
|
+ pIdxInfo->aConstraintUsage[prefixIdx].omit = 1;
|
|
+ }
|
|
+ if( wholelineIdx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg;
|
|
+ pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1;
|
|
+ }
|
|
+ pIdxInfo->idxNum = idxNum;
|
|
+ pIdxInfo->estimatedCost = (double)5000 - 1000*nArg;
|
|
+ pIdxInfo->estimatedRows = 500 - 100*nArg;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** completion virtual table.
|
|
+*/
|
|
+static const sqlite3_module completionModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ completionConnect, /* xConnect */
|
|
+ completionBestIndex, /* xBestIndex */
|
|
+ completionDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ completionOpen, /* xOpen - open a cursor */
|
|
+ completionClose, /* xClose - close a cursor */
|
|
+ completionFilter, /* xFilter - configure scan constraints */
|
|
+ completionNext, /* xNext - advance a cursor */
|
|
+ completionEof, /* xEof - check for end of scan */
|
|
+ completionColumn, /* xColumn - read data */
|
|
+ completionRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+int sqlite3CompletionVtabInit(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "completion", &completionModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_completion_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)(pzErrMsg); /* Unused parameter */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3CompletionVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)(pzErrMsg); /* Unused parameter */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3CompletionVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/compress.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/compress.c 2019-05-14 16:22:31.228327300 +0200
|
|
@@ -0,0 +1,193 @@
|
|
+/*
|
|
+** 2014-06-13
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This SQLite extension implements SQL compression functions
|
|
+** compress() and uncompress() using ZLIB.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <zlib.h>
|
|
+
|
|
+/*
|
|
+** Implementation of the "compress(X)" SQL function. The input X is
|
|
+** compressed using zLib and the output is returned.
|
|
+**
|
|
+** The output is a BLOB that begins with an integer that forming the
|
|
+** input size in bytes (the size of X before compression). The variable-
|
|
+** length integer is implemented as 1 to 5 bytes. If the first byte is 0,
|
|
+** 4 bytes are used, the most significant bits first. Otherwise there are
|
|
+** seven bits per integer stored in the lower seven bits of each byte.
|
|
+** More significant bits occur first. The most significant bit (0x80)
|
|
+** is a flag to indicate the end of the integer.
|
|
+**
|
|
+** This function, SQLAR, and ZIP all use the same "deflate" compression
|
|
+** algorithm, but each is subtly different:
|
|
+**
|
|
+** * ZIP uses raw deflate.
|
|
+**
|
|
+** * SQLAR uses the "zlib format" which is raw deflate with a two-byte
|
|
+** algorithm-identification header and a four-byte checksum at the end.
|
|
+**
|
|
+** * This utility uses the "zlib format" like SQLAR, but adds the variable-
|
|
+** length integer uncompressed size value at the beginning.
|
|
+**
|
|
+** This function might be extended in the future to support compression
|
|
+** formats other than deflate, by providing a different algorithm-id
|
|
+** mark following the variable-length integer size parameter.
|
|
+*/
|
|
+static void compressFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *pIn;
|
|
+ unsigned char *pOut;
|
|
+ size_t nIn;
|
|
+ unsigned long int nOut;
|
|
+ int j, rc;
|
|
+
|
|
+ pIn = sqlite3_value_blob(argv[0]);
|
|
+ nIn = sqlite3_value_bytes(argv[0]);
|
|
+ nOut = 13 + nIn + (nIn+999)/1000;
|
|
+ pOut = sqlite3_malloc( nOut+5 );
|
|
+ if( (nIn<=0xffffff) || (!(nIn&0x80)&&!(nIn&0x8000)&&!(nIn&0x800000))) {
|
|
+ pOut[0] = nIn>>24 & 0xff;
|
|
+ pOut[1] = nIn>>16 & 0xff;
|
|
+ pOut[2] = nIn>>8 & 0xff;
|
|
+ pOut[3] = nIn & 0xff;
|
|
+ j = 4;
|
|
+ }else{
|
|
+ int i;
|
|
+ unsigned char x[8];
|
|
+ for(i=4; i>=0; i--){
|
|
+ x[i] = (nIn >> (7*(4-i)))&0x7f;
|
|
+ }
|
|
+ for(i=0; i<4 && x[i]==0; i++){}
|
|
+ for(j=0; i<=4; i++, j++) pOut[j] = x[i];
|
|
+ pOut[j-1] |= 0x80;
|
|
+ }
|
|
+ rc = compress(&pOut[j], &nOut, pIn, nIn);
|
|
+ if( rc==Z_OK ){
|
|
+ sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
|
|
+ }else{
|
|
+ sqlite3_free(pOut);
|
|
+ sqlite3_result_error(context, "input cannot be zlib compressed", -1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the "uncompress(X)" SQL function. The argument X
|
|
+** is a blob which was obtained from compress(Y). The output will be
|
|
+** the value Y.
|
|
+*/
|
|
+static void uncompressFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *pIn;
|
|
+ unsigned char *pOut;
|
|
+ unsigned int nIn;
|
|
+ unsigned long nOut;
|
|
+ int rc;
|
|
+
|
|
+ pIn = sqlite3_value_blob(argv[0]);
|
|
+ nIn = sqlite3_value_bytes(argv[0]);
|
|
+ nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3];
|
|
+ if( pIn[0] ){
|
|
+ unsigned long nOut2 = 0;
|
|
+ int i;
|
|
+
|
|
+ /*
|
|
+ ** If the high-byte of the blob length > 0, there are actually
|
|
+ ** two possibilities:
|
|
+ ** 1) The blob is > 16MByte, possible but unlikely as most blobs
|
|
+ ** are not that big.
|
|
+ ** 2) The content was compressed with SQLite's ext/misc/compress.c
|
|
+ **
|
|
+ ** Just try both possibilities (smallest first), if the decompression
|
|
+ ** fails (either by Z_BUF_ERROR or Z_DATA_ERROR) we will find out quick
|
|
+ ** enough which one was correct.
|
|
+ */
|
|
+ for(i=0; i<nIn && i<5; i++){
|
|
+ nOut2 = (nOut2<<7) | (pIn[i]&0x7f);
|
|
+ if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
|
+ }
|
|
+ if( (nOut < nOut2) || (i==5 && (pIn[4]^0x80)&0xf0) ){
|
|
+ pOut = sqlite3_malloc( nOut+1 );
|
|
+ rc = uncompress(pOut, &nOut, &pIn[4], nIn-4);
|
|
+ if( rc==Z_OK ){
|
|
+ sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
|
+ return;
|
|
+ }
|
|
+ sqlite3_free(pOut);
|
|
+ }
|
|
+ pOut = sqlite3_malloc( nOut2+1 );
|
|
+ rc = uncompress(pOut, &nOut2, &pIn[i], nIn-i);
|
|
+ if( rc==Z_OK ){
|
|
+ sqlite3_result_blob(context, pOut, nOut2, sqlite3_free);
|
|
+ return;
|
|
+ }
|
|
+ if( nOut < nOut2 ){
|
|
+ goto error;
|
|
+ }
|
|
+ sqlite3_free(pOut);
|
|
+ }
|
|
+ pOut = sqlite3_malloc( nOut+1 );
|
|
+ rc = uncompress(pOut, &nOut, &pIn[4], nIn-4);
|
|
+ if( rc==Z_OK ){
|
|
+ sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
|
+ }else{
|
|
+ error:
|
|
+ sqlite3_free(pOut);
|
|
+ sqlite3_result_error(context, "input is not zlib compressed", -1);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_compress_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
|
|
+ compressFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
|
|
+ uncompressFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
|
|
+ compressFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
|
|
+ uncompressFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/configure.ac 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/configure.ac 2019-05-14 15:41:53.704309900 +0200
|
|
@@ -12,7 +12,6 @@
|
|
AC_PREREQ(2.61)
|
|
AC_INIT(sqlite, 3.28.0, http://www.sqlite.org)
|
|
AC_CONFIG_SRCDIR([sqlite3.c])
|
|
-AC_CONFIG_AUX_DIR([.])
|
|
|
|
# Use automake.
|
|
AM_INIT_AUTOMAKE([foreign])
|
|
@@ -25,11 +24,11 @@ AC_PROG_LIBTOOL
|
|
AC_PROG_MKDIR_P
|
|
|
|
# Check for library functions that SQLite can optionally use.
|
|
-AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r])
|
|
+AC_CHECK_HEADERS([malloc.h])
|
|
+AC_CHECK_FUNCS([fdatasync usleep strchrnul localtime_r gmtime_r localtime_s malloc_usable_size utime flock readlink lstat pread pread64 pwrite pwrite64 rand_s])
|
|
AC_FUNC_STRERROR_R
|
|
|
|
AC_CONFIG_FILES([Makefile sqlite3.pc])
|
|
-BUILD_CFLAGS=
|
|
AC_SUBST(BUILD_CFLAGS)
|
|
|
|
#-------------------------------------------------------------------------
|
|
@@ -87,11 +86,13 @@ AC_SUBST(READLINE_LIBS)
|
|
AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
|
|
[--enable-threadsafe], [build a thread-safe library [default=yes]])],
|
|
[], [enable_threadsafe=yes])
|
|
+THREADSAFE_FLAGS=-DSQLITE_THREADSAFE=0
|
|
if test x"$enable_threadsafe" != "xno"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
|
|
+ THREADSAFE_FLAGS="-D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
|
|
AC_SEARCH_LIBS(pthread_create, pthread)
|
|
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
|
|
fi
|
|
+AC_SUBST(THREADSAFE_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -103,66 +104,36 @@ AC_ARG_ENABLE(dynamic-extensions, [AS_HE
|
|
if test x"$enable_dynamic_extensions" != "xno"; then
|
|
AC_SEARCH_LIBS(dlopen, dl)
|
|
else
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_OMIT_LOAD_EXTENSION=1"
|
|
+ DYNAMIC_EXTENSION_FLAGS=-DSQLITE_OMIT_LOAD_EXTENSION=1
|
|
fi
|
|
AC_MSG_CHECKING([for whether to support dynamic extensions])
|
|
AC_MSG_RESULT($enable_dynamic_extensions)
|
|
-#-----------------------------------------------------------------------
|
|
-
|
|
-#-----------------------------------------------------------------------
|
|
-# --enable-fts4
|
|
-#
|
|
-AC_ARG_ENABLE(fts4, [AS_HELP_STRING(
|
|
- [--enable-fts4], [include fts4 support [default=yes]])],
|
|
- [], [enable_fts4=yes])
|
|
-if test x"$enable_fts4" = "xyes"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS4"
|
|
-fi
|
|
-#-----------------------------------------------------------------------
|
|
-
|
|
-#-----------------------------------------------------------------------
|
|
-# --enable-fts3
|
|
-#
|
|
-AC_ARG_ENABLE(fts3, [AS_HELP_STRING(
|
|
- [--enable-fts3], [include fts3 support [default=no]])],
|
|
- [], [])
|
|
-if test x"$enable_fts3" = "xyes" -a x"$enable_fts4" = "xno"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS3"
|
|
-fi
|
|
+AC_SUBST(DYNAMIC_EXTENSION_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
# --enable-fts5
|
|
#
|
|
AC_ARG_ENABLE(fts5, [AS_HELP_STRING(
|
|
- [--enable-fts5], [include fts5 support [default=yes]])],
|
|
- [], [enable_fts5=yes])
|
|
+ [--enable-fts5], [include fts5 support [default=no]])],
|
|
+ [], [enable_fts5=no])
|
|
if test x"$enable_fts5" = "xyes"; then
|
|
AC_SEARCH_LIBS(log, m)
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS5"
|
|
+ FTS5_FLAGS=-DSQLITE_ENABLE_FTS5
|
|
fi
|
|
+AC_SUBST(FTS5_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
# --enable-json1
|
|
#
|
|
AC_ARG_ENABLE(json1, [AS_HELP_STRING(
|
|
- [--enable-json1], [include json1 support [default=yes]])],
|
|
- [],[enable_json1=yes])
|
|
+ [--enable-json1], [include json1 support [default=no]])],
|
|
+ [], [enable_json1=no])
|
|
if test x"$enable_json1" = "xyes"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_JSON1"
|
|
-fi
|
|
-#-----------------------------------------------------------------------
|
|
-
|
|
-#-----------------------------------------------------------------------
|
|
-# --enable-rtree
|
|
-#
|
|
-AC_ARG_ENABLE(rtree, [AS_HELP_STRING(
|
|
- [--enable-rtree], [include rtree support [default=yes]])],
|
|
- [], [enable_rtree=yes])
|
|
-if test x"$enable_rtree" = "xyes"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE"
|
|
+ JSON1_FLAGS=-DSQLITE_ENABLE_JSON1
|
|
fi
|
|
+AC_SUBST(JSON1_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -170,22 +141,11 @@ fi
|
|
#
|
|
AC_ARG_ENABLE(session, [AS_HELP_STRING(
|
|
[--enable-session], [enable the session extension [default=no]])],
|
|
- [], [])
|
|
+ [], [enable_session=no])
|
|
if test x"$enable_session" = "xyes"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
|
-fi
|
|
-#-----------------------------------------------------------------------
|
|
-
|
|
-#-----------------------------------------------------------------------
|
|
-# --enable-debug
|
|
-#
|
|
-AC_ARG_ENABLE(debug, [AS_HELP_STRING(
|
|
- [--enable-debug], [build with debugging features enabled [default=no]])],
|
|
- [], [])
|
|
-if test x"$enable_debug" = "xyes"; then
|
|
- BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
|
|
- CFLAGS="-g -O0"
|
|
+ SESSION_FLAGS="-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
|
fi
|
|
+AC_SUBST(SESSION_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -204,12 +164,6 @@ AC_SUBST(EXTRA_SHELL_OBJ)
|
|
#-----------------------------------------------------------------------
|
|
|
|
AC_CHECK_FUNCS(posix_fallocate)
|
|
-AC_CHECK_HEADERS(zlib.h,[
|
|
- AC_SEARCH_LIBS(deflate,z,[BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_HAVE_ZLIB"])
|
|
-])
|
|
-
|
|
-AC_SEARCH_LIBS(system,,,[SHELL_CFLAGS="-DSQLITE_NOHAVE_SYSTEM"])
|
|
-AC_SUBST(SHELL_CFLAGS)
|
|
|
|
#-----------------------------------------------------------------------
|
|
# UPDATE: Maybe it's better if users just set CFLAGS before invoking
|
|
--- origsrc/sqlite-autoconf-3280000/csv.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/csv.c 2019-05-14 16:22:31.230280000 +0200
|
|
@@ -0,0 +1,958 @@
|
|
+/*
|
|
+** 2016-05-28
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file contains the implementation of an SQLite virtual table for
|
|
+** reading CSV files.
|
|
+**
|
|
+** Usage:
|
|
+**
|
|
+** .load ./csv
|
|
+** CREATE VIRTUAL TABLE temp.csv USING csv(filename=FILENAME);
|
|
+** SELECT * FROM csv;
|
|
+**
|
|
+** The columns are named "c1", "c2", "c3", ... by default. Or the
|
|
+** application can define its own CREATE TABLE statement using the
|
|
+** schema= parameter, like this:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE temp.csv2 USING csv(
|
|
+** filename = "../http.log",
|
|
+** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)"
|
|
+** );
|
|
+**
|
|
+** Instead of specifying a file, the text of the CSV can be loaded using
|
|
+** the data= parameter.
|
|
+**
|
|
+** If the columns=N parameter is supplied, then the CSV file is assumed to have
|
|
+** N columns. If both the columns= and schema= parameters are omitted, then
|
|
+** the number and names of the columns is determined by the first line of
|
|
+** the CSV input.
|
|
+**
|
|
+** Some extra debugging features (used for testing virtual tables) are available
|
|
+** if this module is compiled with -DSQLITE_TEST.
|
|
+*/
|
|
+#include <sqlite3ext.h>
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <assert.h>
|
|
+#include <stdarg.h>
|
|
+#include <ctype.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** A macro to hint to the compiler that a function should not be
|
|
+** inlined.
|
|
+*/
|
|
+#if defined(__GNUC__)
|
|
+# define CSV_NOINLINE __attribute__((noinline))
|
|
+#elif defined(_MSC_VER) && _MSC_VER>=1310
|
|
+# define CSV_NOINLINE __declspec(noinline)
|
|
+#else
|
|
+# define CSV_NOINLINE
|
|
+#endif
|
|
+
|
|
+
|
|
+/* Max size of the error message in a CsvReader */
|
|
+#define CSV_MXERR 200
|
|
+
|
|
+/* Size of the CsvReader input buffer */
|
|
+#define CSV_INBUFSZ 1024
|
|
+
|
|
+/* A context object used when read a CSV file. */
|
|
+typedef struct CsvReader CsvReader;
|
|
+struct CsvReader {
|
|
+ FILE *in; /* Read the CSV text from this input stream */
|
|
+ char *z; /* Accumulated text for a field */
|
|
+ int n; /* Number of bytes in z */
|
|
+ int nAlloc; /* Space allocated for z[] */
|
|
+ int nLine; /* Current line number */
|
|
+ int bNotFirst; /* True if prior text has been seen */
|
|
+ int cTerm; /* Character that terminated the most recent field */
|
|
+ size_t iIn; /* Next unread character in the input buffer */
|
|
+ size_t nIn; /* Number of characters in the input buffer */
|
|
+ char *zIn; /* The input buffer */
|
|
+ char zErr[CSV_MXERR]; /* Error message */
|
|
+};
|
|
+
|
|
+/* Initialize a CsvReader object */
|
|
+static void csv_reader_init(CsvReader *p){
|
|
+ p->in = 0;
|
|
+ p->z = 0;
|
|
+ p->n = 0;
|
|
+ p->nAlloc = 0;
|
|
+ p->nLine = 0;
|
|
+ p->bNotFirst = 0;
|
|
+ p->nIn = 0;
|
|
+ p->zIn = 0;
|
|
+ p->zErr[0] = 0;
|
|
+}
|
|
+
|
|
+/* Close and reset a CsvReader object */
|
|
+static void csv_reader_reset(CsvReader *p){
|
|
+ if( p->in ){
|
|
+ fclose(p->in);
|
|
+ sqlite3_free(p->zIn);
|
|
+ }
|
|
+ sqlite3_free(p->z);
|
|
+ csv_reader_init(p);
|
|
+}
|
|
+
|
|
+/* Report an error on a CsvReader */
|
|
+static void csv_errmsg(CsvReader *p, const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFormat);
|
|
+ sqlite3_vsnprintf(CSV_MXERR, p->zErr, zFormat, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+/* Open the file associated with a CsvReader
|
|
+** Return the number of errors.
|
|
+*/
|
|
+static int csv_reader_open(
|
|
+ CsvReader *p, /* The reader to open */
|
|
+ const char *zFilename, /* Read from this filename */
|
|
+ const char *zData /* ... or use this data */
|
|
+){
|
|
+ if( zFilename ){
|
|
+ p->zIn = sqlite3_malloc( CSV_INBUFSZ );
|
|
+ if( p->zIn==0 ){
|
|
+ csv_errmsg(p, "out of memory");
|
|
+ return 1;
|
|
+ }
|
|
+ p->in = fopen(zFilename, "rb");
|
|
+ if( p->in==0 ){
|
|
+ sqlite3_free(p->zIn);
|
|
+ csv_reader_reset(p);
|
|
+ csv_errmsg(p, "cannot open '%s' for reading", zFilename);
|
|
+ return 1;
|
|
+ }
|
|
+ }else{
|
|
+ assert( p->in==0 );
|
|
+ p->zIn = (char*)zData;
|
|
+ p->nIn = strlen(zData);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* The input buffer has overflowed. Refill the input buffer, then
|
|
+** return the next character
|
|
+*/
|
|
+static CSV_NOINLINE int csv_getc_refill(CsvReader *p){
|
|
+ size_t got;
|
|
+
|
|
+ assert( p->iIn>=p->nIn ); /* Only called on an empty input buffer */
|
|
+ assert( p->in!=0 ); /* Only called if reading froma file */
|
|
+
|
|
+ got = fread(p->zIn, 1, CSV_INBUFSZ, p->in);
|
|
+ if( got==0 ) return EOF;
|
|
+ p->nIn = got;
|
|
+ p->iIn = 1;
|
|
+ return p->zIn[0];
|
|
+}
|
|
+
|
|
+/* Return the next character of input. Return EOF at end of input. */
|
|
+static int csv_getc(CsvReader *p){
|
|
+ if( p->iIn >= p->nIn ){
|
|
+ if( p->in!=0 ) return csv_getc_refill(p);
|
|
+ return EOF;
|
|
+ }
|
|
+ return ((unsigned char*)p->zIn)[p->iIn++];
|
|
+}
|
|
+
|
|
+/* Increase the size of p->z and append character c to the end.
|
|
+** Return 0 on success and non-zero if there is an OOM error */
|
|
+static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){
|
|
+ char *zNew;
|
|
+ int nNew = p->nAlloc*2 + 100;
|
|
+ zNew = sqlite3_realloc(p->z, nNew);
|
|
+ if( zNew ){
|
|
+ p->z = zNew;
|
|
+ p->nAlloc = nNew;
|
|
+ p->z[p->n++] = c;
|
|
+ return 0;
|
|
+ }else{
|
|
+ csv_errmsg(p, "out of memory");
|
|
+ return 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Append a single character to the CsvReader.z[] array.
|
|
+** Return 0 on success and non-zero if there is an OOM error */
|
|
+static int csv_append(CsvReader *p, char c){
|
|
+ if( p->n>=p->nAlloc-1 ) return csv_resize_and_append(p, c);
|
|
+ p->z[p->n++] = c;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Read a single field of CSV text. Compatible with rfc4180 and extended
|
|
+** with the option of having a separator other than ",".
|
|
+**
|
|
+** + Input comes from p->in.
|
|
+** + Store results in p->z of length p->n. Space to hold p->z comes
|
|
+** from sqlite3_malloc64().
|
|
+** + Keep track of the line number in p->nLine.
|
|
+** + Store the character that terminates the field in p->cTerm. Store
|
|
+** EOF on end-of-file.
|
|
+**
|
|
+** Return 0 at EOF or on OOM. On EOF, the p->cTerm character will have
|
|
+** been set to EOF.
|
|
+*/
|
|
+static const char *csv_read_one_field(CsvReader *p){
|
|
+ int c;
|
|
+ p->n = 0;
|
|
+ c = csv_getc(p);
|
|
+ if( c==EOF ){
|
|
+ p->cTerm = EOF;
|
|
+ return 0;
|
|
+ }
|
|
+ if( c=='"' ){
|
|
+ int pc, ppc;
|
|
+ int startLine = p->nLine;
|
|
+ pc = ppc = 0;
|
|
+ while( 1 ){
|
|
+ c = csv_getc(p);
|
|
+ if( c<='"' || pc=='"' ){
|
|
+ if( c=='\n' ) p->nLine++;
|
|
+ if( c=='"' ){
|
|
+ if( pc=='"' ){
|
|
+ pc = 0;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if( (c==',' && pc=='"')
|
|
+ || (c=='\n' && pc=='"')
|
|
+ || (c=='\n' && pc=='\r' && ppc=='"')
|
|
+ || (c==EOF && pc=='"')
|
|
+ ){
|
|
+ do{ p->n--; }while( p->z[p->n]!='"' );
|
|
+ p->cTerm = (char)c;
|
|
+ break;
|
|
+ }
|
|
+ if( pc=='"' && c!='\r' ){
|
|
+ csv_errmsg(p, "line %d: unescaped %c character", p->nLine, '"');
|
|
+ break;
|
|
+ }
|
|
+ if( c==EOF ){
|
|
+ csv_errmsg(p, "line %d: unterminated %c-quoted field\n",
|
|
+ startLine, '"');
|
|
+ p->cTerm = (char)c;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( csv_append(p, (char)c) ) return 0;
|
|
+ ppc = pc;
|
|
+ pc = c;
|
|
+ }
|
|
+ }else{
|
|
+ /* If this is the first field being parsed and it begins with the
|
|
+ ** UTF-8 BOM (0xEF BB BF) then skip the BOM */
|
|
+ if( (c&0xff)==0xef && p->bNotFirst==0 ){
|
|
+ csv_append(p, (char)c);
|
|
+ c = csv_getc(p);
|
|
+ if( (c&0xff)==0xbb ){
|
|
+ csv_append(p, (char)c);
|
|
+ c = csv_getc(p);
|
|
+ if( (c&0xff)==0xbf ){
|
|
+ p->bNotFirst = 1;
|
|
+ p->n = 0;
|
|
+ return csv_read_one_field(p);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ while( c>',' || (c!=EOF && c!=',' && c!='\n') ){
|
|
+ if( csv_append(p, (char)c) ) return 0;
|
|
+ c = csv_getc(p);
|
|
+ }
|
|
+ if( c=='\n' ){
|
|
+ p->nLine++;
|
|
+ if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--;
|
|
+ }
|
|
+ p->cTerm = (char)c;
|
|
+ }
|
|
+ if( p->z ) p->z[p->n] = 0;
|
|
+ p->bNotFirst = 1;
|
|
+ return p->z;
|
|
+}
|
|
+
|
|
+
|
|
+/* Forward references to the various virtual table methods implemented
|
|
+** in this file. */
|
|
+static int csvtabCreate(sqlite3*, void*, int, const char*const*,
|
|
+ sqlite3_vtab**,char**);
|
|
+static int csvtabConnect(sqlite3*, void*, int, const char*const*,
|
|
+ sqlite3_vtab**,char**);
|
|
+static int csvtabBestIndex(sqlite3_vtab*,sqlite3_index_info*);
|
|
+static int csvtabDisconnect(sqlite3_vtab*);
|
|
+static int csvtabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
|
|
+static int csvtabClose(sqlite3_vtab_cursor*);
|
|
+static int csvtabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv);
|
|
+static int csvtabNext(sqlite3_vtab_cursor*);
|
|
+static int csvtabEof(sqlite3_vtab_cursor*);
|
|
+static int csvtabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int);
|
|
+static int csvtabRowid(sqlite3_vtab_cursor*,sqlite3_int64*);
|
|
+
|
|
+/* An instance of the CSV virtual table */
|
|
+typedef struct CsvTable {
|
|
+ sqlite3_vtab base; /* Base class. Must be first */
|
|
+ char *zFilename; /* Name of the CSV file */
|
|
+ char *zData; /* Raw CSV data in lieu of zFilename */
|
|
+ long iStart; /* Offset to start of data in zFilename */
|
|
+ int nCol; /* Number of columns in the CSV file */
|
|
+ unsigned int tstFlags; /* Bit values used for testing */
|
|
+} CsvTable;
|
|
+
|
|
+/* Allowed values for tstFlags */
|
|
+#define CSVTEST_FIDX 0x0001 /* Pretend that constrained searchs cost less*/
|
|
+
|
|
+/* A cursor for the CSV virtual table */
|
|
+typedef struct CsvCursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
|
|
+ CsvReader rdr; /* The CsvReader object */
|
|
+ char **azVal; /* Value of the current row */
|
|
+ int *aLen; /* Length of each entry */
|
|
+ sqlite3_int64 iRowid; /* The current rowid. Negative for EOF */
|
|
+} CsvCursor;
|
|
+
|
|
+/* Transfer error message text from a reader into a CsvTable */
|
|
+static void csv_xfer_error(CsvTable *pTab, CsvReader *pRdr){
|
|
+ sqlite3_free(pTab->base.zErrMsg);
|
|
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", pRdr->zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor fo a CsvTable object.
|
|
+*/
|
|
+static int csvtabDisconnect(sqlite3_vtab *pVtab){
|
|
+ CsvTable *p = (CsvTable*)pVtab;
|
|
+ sqlite3_free(p->zFilename);
|
|
+ sqlite3_free(p->zData);
|
|
+ sqlite3_free(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Skip leading whitespace. Return a pointer to the first non-whitespace
|
|
+** character, or to the zero terminator if the string has only whitespace */
|
|
+static const char *csv_skip_whitespace(const char *z){
|
|
+ while( isspace((unsigned char)z[0]) ) z++;
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/* Remove trailing whitespace from the end of string z[] */
|
|
+static void csv_trim_whitespace(char *z){
|
|
+ size_t n = strlen(z);
|
|
+ while( n>0 && isspace((unsigned char)z[n]) ) n--;
|
|
+ z[n] = 0;
|
|
+}
|
|
+
|
|
+/* Dequote the string */
|
|
+static void csv_dequote(char *z){
|
|
+ int j;
|
|
+ char cQuote = z[0];
|
|
+ size_t i, n;
|
|
+
|
|
+ if( cQuote!='\'' && cQuote!='"' ) return;
|
|
+ n = strlen(z);
|
|
+ if( n<2 || z[n-1]!=z[0] ) return;
|
|
+ for(i=1, j=0; i<n-1; i++){
|
|
+ if( z[i]==cQuote && z[i+1]==cQuote ) i++;
|
|
+ z[j++] = z[i];
|
|
+ }
|
|
+ z[j] = 0;
|
|
+}
|
|
+
|
|
+/* Check to see if the string is of the form: "TAG = VALUE" with optional
|
|
+** whitespace before and around tokens. If it is, return a pointer to the
|
|
+** first character of VALUE. If it is not, return NULL.
|
|
+*/
|
|
+static const char *csv_parameter(const char *zTag, int nTag, const char *z){
|
|
+ z = csv_skip_whitespace(z);
|
|
+ if( strncmp(zTag, z, nTag)!=0 ) return 0;
|
|
+ z = csv_skip_whitespace(z+nTag);
|
|
+ if( z[0]!='=' ) return 0;
|
|
+ return csv_skip_whitespace(z+1);
|
|
+}
|
|
+
|
|
+/* Decode a parameter that requires a dequoted string.
|
|
+**
|
|
+** Return 1 if the parameter is seen, or 0 if not. 1 is returned
|
|
+** even if there is an error. If an error occurs, then an error message
|
|
+** is left in p->zErr. If there are no errors, p->zErr[0]==0.
|
|
+*/
|
|
+static int csv_string_parameter(
|
|
+ CsvReader *p, /* Leave the error message here, if there is one */
|
|
+ const char *zParam, /* Parameter we are checking for */
|
|
+ const char *zArg, /* Raw text of the virtual table argment */
|
|
+ char **pzVal /* Write the dequoted string value here */
|
|
+){
|
|
+ const char *zValue;
|
|
+ zValue = csv_parameter(zParam,(int)strlen(zParam),zArg);
|
|
+ if( zValue==0 ) return 0;
|
|
+ p->zErr[0] = 0;
|
|
+ if( *pzVal ){
|
|
+ csv_errmsg(p, "more than one '%s' parameter", zParam);
|
|
+ return 1;
|
|
+ }
|
|
+ *pzVal = sqlite3_mprintf("%s", zValue);
|
|
+ if( *pzVal==0 ){
|
|
+ csv_errmsg(p, "out of memory");
|
|
+ return 1;
|
|
+ }
|
|
+ csv_trim_whitespace(*pzVal);
|
|
+ csv_dequote(*pzVal);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+
|
|
+/* Return 0 if the argument is false and 1 if it is true. Return -1 if
|
|
+** we cannot really tell.
|
|
+*/
|
|
+static int csv_boolean(const char *z){
|
|
+ if( sqlite3_stricmp("yes",z)==0
|
|
+ || sqlite3_stricmp("on",z)==0
|
|
+ || sqlite3_stricmp("true",z)==0
|
|
+ || (z[0]=='1' && z[1]==0)
|
|
+ ){
|
|
+ return 1;
|
|
+ }
|
|
+ if( sqlite3_stricmp("no",z)==0
|
|
+ || sqlite3_stricmp("off",z)==0
|
|
+ || sqlite3_stricmp("false",z)==0
|
|
+ || (z[0]=='0' && z[1]==0)
|
|
+ ){
|
|
+ return 0;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/* Check to see if the string is of the form: "TAG = BOOLEAN" or just "TAG".
|
|
+** If it is, set *pValue to be the value of the boolean ("true" if there is
|
|
+** not "= BOOLEAN" component) and return non-zero. If the input string
|
|
+** does not begin with TAG, return zero.
|
|
+*/
|
|
+static int csv_boolean_parameter(
|
|
+ const char *zTag, /* Tag we are looking for */
|
|
+ int nTag, /* Size of the tag in bytes */
|
|
+ const char *z, /* Input parameter */
|
|
+ int *pValue /* Write boolean value here */
|
|
+){
|
|
+ int b;
|
|
+ z = csv_skip_whitespace(z);
|
|
+ if( strncmp(zTag, z, nTag)!=0 ) return 0;
|
|
+ z = csv_skip_whitespace(z + nTag);
|
|
+ if( z[0]==0 ){
|
|
+ *pValue = 1;
|
|
+ return 1;
|
|
+ }
|
|
+ if( z[0]!='=' ) return 0;
|
|
+ z = csv_skip_whitespace(z+1);
|
|
+ b = csv_boolean(z);
|
|
+ if( b>=0 ){
|
|
+ *pValue = b;
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parameters:
|
|
+** filename=FILENAME Name of file containing CSV content
|
|
+** data=TEXT Direct CSV content.
|
|
+** schema=SCHEMA Alternative CSV schema.
|
|
+** header=YES|NO First row of CSV defines the names of
|
|
+** columns if "yes". Default "no".
|
|
+** columns=N Assume the CSV file contains N columns.
|
|
+**
|
|
+** Only available if compiled with SQLITE_TEST:
|
|
+**
|
|
+** testflags=N Bitmask of test flags. Optional
|
|
+**
|
|
+** If schema= is omitted, then the columns are named "c0", "c1", "c2",
|
|
+** and so forth. If columns=N is omitted, then the file is opened and
|
|
+** the number of columns in the first row is counted to determine the
|
|
+** column count. If header=YES, then the first row is skipped.
|
|
+*/
|
|
+static int csvtabConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ CsvTable *pNew = 0; /* The CsvTable object to construct */
|
|
+ int bHeader = -1; /* header= flags. -1 means not seen yet */
|
|
+ int rc = SQLITE_OK; /* Result code from this routine */
|
|
+ int i, j; /* Loop counters */
|
|
+#ifdef SQLITE_TEST
|
|
+ int tstFlags = 0; /* Value for testflags=N parameter */
|
|
+#endif
|
|
+ int b; /* Value of a boolean parameter */
|
|
+ int nCol = -99; /* Value of the columns= parameter */
|
|
+ CsvReader sRdr; /* A CSV file reader used to store an error
|
|
+ ** message and/or to count the number of columns */
|
|
+ static const char *const azParam[] = {
|
|
+ "filename", "data", "schema",
|
|
+ };
|
|
+ char *azPValue[3]; /* Parameter values */
|
|
+# define CSV_FILENAME (azPValue[0])
|
|
+# define CSV_DATA (azPValue[1])
|
|
+# define CSV_SCHEMA (azPValue[2])
|
|
+
|
|
+
|
|
+ assert( sizeof(azPValue)==sizeof(azParam) );
|
|
+ memset(&sRdr, 0, sizeof(sRdr));
|
|
+ memset(azPValue, 0, sizeof(azPValue));
|
|
+ for(i=3; i<argc; i++){
|
|
+ const char *z = argv[i];
|
|
+ const char *zValue;
|
|
+ for(j=0; j<sizeof(azParam)/sizeof(azParam[0]); j++){
|
|
+ if( csv_string_parameter(&sRdr, azParam[j], z, &azPValue[j]) ) break;
|
|
+ }
|
|
+ if( j<sizeof(azParam)/sizeof(azParam[0]) ){
|
|
+ if( sRdr.zErr[0] ) goto csvtab_connect_error;
|
|
+ }else
|
|
+ if( csv_boolean_parameter("header",6,z,&b) ){
|
|
+ if( bHeader>=0 ){
|
|
+ csv_errmsg(&sRdr, "more than one 'header' parameter");
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+ bHeader = b;
|
|
+ }else
|
|
+#ifdef SQLITE_TEST
|
|
+ if( (zValue = csv_parameter("testflags",9,z))!=0 ){
|
|
+ tstFlags = (unsigned int)atoi(zValue);
|
|
+ }else
|
|
+#endif
|
|
+ if( (zValue = csv_parameter("columns",7,z))!=0 ){
|
|
+ if( nCol>0 ){
|
|
+ csv_errmsg(&sRdr, "more than one 'columns' parameter");
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+ nCol = atoi(zValue);
|
|
+ if( nCol<=0 ){
|
|
+ csv_errmsg(&sRdr, "column= value must be positive");
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+ }else
|
|
+ {
|
|
+ csv_errmsg(&sRdr, "bad parameter: '%s'", z);
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+ }
|
|
+ if( (CSV_FILENAME==0)==(CSV_DATA==0) ){
|
|
+ csv_errmsg(&sRdr, "must specify either filename= or data= but not both");
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+
|
|
+ if( (nCol<=0 || bHeader==1)
|
|
+ && csv_reader_open(&sRdr, CSV_FILENAME, CSV_DATA)
|
|
+ ){
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) goto csvtab_connect_oom;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ if( CSV_SCHEMA==0 ){
|
|
+ sqlite3_str *pStr = sqlite3_str_new(0);
|
|
+ char *zSep = "";
|
|
+ int iCol = 0;
|
|
+ sqlite3_str_appendf(pStr, "CREATE TABLE x(");
|
|
+ if( nCol<0 && bHeader<1 ){
|
|
+ nCol = 0;
|
|
+ do{
|
|
+ csv_read_one_field(&sRdr);
|
|
+ nCol++;
|
|
+ }while( sRdr.cTerm==',' );
|
|
+ }
|
|
+ if( nCol>0 && bHeader<1 ){
|
|
+ for(iCol=0; iCol<nCol; iCol++){
|
|
+ sqlite3_str_appendf(pStr, "%sc%d TEXT", zSep, iCol);
|
|
+ zSep = ",";
|
|
+ }
|
|
+ }else{
|
|
+ do{
|
|
+ const char *z = csv_read_one_field(&sRdr);
|
|
+ if( (nCol>0 && iCol<nCol) || (nCol<0 && bHeader) ){
|
|
+ sqlite3_str_appendf(pStr,"%s\"%w\" TEXT", zSep, z);
|
|
+ zSep = ",";
|
|
+ iCol++;
|
|
+ }
|
|
+ }while( sRdr.cTerm==',' );
|
|
+ if( nCol<0 ){
|
|
+ nCol = iCol;
|
|
+ }else{
|
|
+ while( iCol<nCol ){
|
|
+ sqlite3_str_appendf(pStr,"%sc%d TEXT", zSep, ++iCol);
|
|
+ zSep = ",";
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ pNew->nCol = nCol;
|
|
+ sqlite3_str_appendf(pStr, ")");
|
|
+ CSV_SCHEMA = sqlite3_str_finish(pStr);
|
|
+ if( CSV_SCHEMA==0 ) goto csvtab_connect_oom;
|
|
+ }else if( nCol<0 ){
|
|
+ do{
|
|
+ csv_read_one_field(&sRdr);
|
|
+ pNew->nCol++;
|
|
+ }while( sRdr.cTerm==',' );
|
|
+ }else{
|
|
+ pNew->nCol = nCol;
|
|
+ }
|
|
+ pNew->zFilename = CSV_FILENAME; CSV_FILENAME = 0;
|
|
+ pNew->zData = CSV_DATA; CSV_DATA = 0;
|
|
+#ifdef SQLITE_TEST
|
|
+ pNew->tstFlags = tstFlags;
|
|
+#endif
|
|
+ if( bHeader!=1 ){
|
|
+ pNew->iStart = 0;
|
|
+ }else if( pNew->zData ){
|
|
+ pNew->iStart = (int)sRdr.iIn;
|
|
+ }else{
|
|
+ pNew->iStart = (int)(ftell(sRdr.in) - sRdr.nIn + sRdr.iIn);
|
|
+ }
|
|
+ csv_reader_reset(&sRdr);
|
|
+ rc = sqlite3_declare_vtab(db, CSV_SCHEMA);
|
|
+ if( rc ){
|
|
+ csv_errmsg(&sRdr, "bad schema: '%s' - %s", CSV_SCHEMA, sqlite3_errmsg(db));
|
|
+ goto csvtab_connect_error;
|
|
+ }
|
|
+ for(i=0; i<sizeof(azPValue)/sizeof(azPValue[0]); i++){
|
|
+ sqlite3_free(azPValue[i]);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+
|
|
+csvtab_connect_oom:
|
|
+ rc = SQLITE_NOMEM;
|
|
+ csv_errmsg(&sRdr, "out of memory");
|
|
+
|
|
+csvtab_connect_error:
|
|
+ if( pNew ) csvtabDisconnect(&pNew->base);
|
|
+ for(i=0; i<sizeof(azPValue)/sizeof(azPValue[0]); i++){
|
|
+ sqlite3_free(azPValue[i]);
|
|
+ }
|
|
+ if( sRdr.zErr[0] ){
|
|
+ sqlite3_free(*pzErr);
|
|
+ *pzErr = sqlite3_mprintf("%s", sRdr.zErr);
|
|
+ }
|
|
+ csv_reader_reset(&sRdr);
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset the current row content held by a CsvCursor.
|
|
+*/
|
|
+static void csvtabCursorRowReset(CsvCursor *pCur){
|
|
+ CsvTable *pTab = (CsvTable*)pCur->base.pVtab;
|
|
+ int i;
|
|
+ for(i=0; i<pTab->nCol; i++){
|
|
+ sqlite3_free(pCur->azVal[i]);
|
|
+ pCur->azVal[i] = 0;
|
|
+ pCur->aLen[i] = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The xConnect and xCreate methods do the same thing, but they must be
|
|
+** different so that the virtual table is not an eponymous virtual table.
|
|
+*/
|
|
+static int csvtabCreate(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return csvtabConnect(db, pAux, argc, argv, ppVtab, pzErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a CsvCursor.
|
|
+*/
|
|
+static int csvtabClose(sqlite3_vtab_cursor *cur){
|
|
+ CsvCursor *pCur = (CsvCursor*)cur;
|
|
+ csvtabCursorRowReset(pCur);
|
|
+ csv_reader_reset(&pCur->rdr);
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new CsvTable cursor object.
|
|
+*/
|
|
+static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ CsvTable *pTab = (CsvTable*)p;
|
|
+ CsvCursor *pCur;
|
|
+ size_t nByte;
|
|
+ nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol;
|
|
+ pCur = sqlite3_malloc( nByte );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, nByte);
|
|
+ pCur->azVal = (char**)&pCur[1];
|
|
+ pCur->aLen = (int*)&pCur->azVal[pTab->nCol];
|
|
+ *ppCursor = &pCur->base;
|
|
+ if( csv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData) ){
|
|
+ csv_xfer_error(pTab, &pCur->rdr);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a CsvCursor to its next row of input.
|
|
+** Set the EOF marker if we reach the end of input.
|
|
+*/
|
|
+static int csvtabNext(sqlite3_vtab_cursor *cur){
|
|
+ CsvCursor *pCur = (CsvCursor*)cur;
|
|
+ CsvTable *pTab = (CsvTable*)cur->pVtab;
|
|
+ int i = 0;
|
|
+ const char *z;
|
|
+ do{
|
|
+ z = csv_read_one_field(&pCur->rdr);
|
|
+ if( z==0 ){
|
|
+ break;
|
|
+ }
|
|
+ if( i<pTab->nCol ){
|
|
+ if( pCur->aLen[i] < pCur->rdr.n+1 ){
|
|
+ char *zNew = sqlite3_realloc(pCur->azVal[i], pCur->rdr.n+1);
|
|
+ if( zNew==0 ){
|
|
+ csv_errmsg(&pCur->rdr, "out of memory");
|
|
+ csv_xfer_error(pTab, &pCur->rdr);
|
|
+ break;
|
|
+ }
|
|
+ pCur->azVal[i] = zNew;
|
|
+ pCur->aLen[i] = pCur->rdr.n+1;
|
|
+ }
|
|
+ memcpy(pCur->azVal[i], z, pCur->rdr.n+1);
|
|
+ i++;
|
|
+ }
|
|
+ }while( pCur->rdr.cTerm==',' );
|
|
+ if( z==0 || (pCur->rdr.cTerm==EOF && i<pTab->nCol) ){
|
|
+ pCur->iRowid = -1;
|
|
+ }else{
|
|
+ pCur->iRowid++;
|
|
+ while( i<pTab->nCol ){
|
|
+ sqlite3_free(pCur->azVal[i]);
|
|
+ pCur->azVal[i] = 0;
|
|
+ pCur->aLen[i] = 0;
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the CsvCursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int csvtabColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ CsvCursor *pCur = (CsvCursor*)cur;
|
|
+ CsvTable *pTab = (CsvTable*)cur->pVtab;
|
|
+ if( i>=0 && i<pTab->nCol && pCur->azVal[i]!=0 ){
|
|
+ sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_STATIC);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row.
|
|
+*/
|
|
+static int csvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ CsvCursor *pCur = (CsvCursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int csvtabEof(sqlite3_vtab_cursor *cur){
|
|
+ CsvCursor *pCur = (CsvCursor*)cur;
|
|
+ return pCur->iRowid<0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only a full table scan is supported. So xFilter simply rewinds to
|
|
+** the beginning.
|
|
+*/
|
|
+static int csvtabFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ CsvCursor *pCur = (CsvCursor*)pVtabCursor;
|
|
+ CsvTable *pTab = (CsvTable*)pVtabCursor->pVtab;
|
|
+ pCur->iRowid = 0;
|
|
+ if( pCur->rdr.in==0 ){
|
|
+ assert( pCur->rdr.zIn==pTab->zData );
|
|
+ assert( pTab->iStart>=0 );
|
|
+ assert( (size_t)pTab->iStart<=pCur->rdr.nIn );
|
|
+ pCur->rdr.iIn = pTab->iStart;
|
|
+ }else{
|
|
+ fseek(pCur->rdr.in, pTab->iStart, SEEK_SET);
|
|
+ pCur->rdr.iIn = 0;
|
|
+ pCur->rdr.nIn = 0;
|
|
+ }
|
|
+ return csvtabNext(pVtabCursor);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only a forward full table scan is supported. xBestIndex is mostly
|
|
+** a no-op. If CSVTEST_FIDX is set, then the presence of equality
|
|
+** constraints lowers the estimated cost, which is fiction, but is useful
|
|
+** for testing certain kinds of virtual table behavior.
|
|
+*/
|
|
+static int csvtabBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ pIdxInfo->estimatedCost = 1000000;
|
|
+#ifdef SQLITE_TEST
|
|
+ if( (((CsvTable*)tab)->tstFlags & CSVTEST_FIDX)!=0 ){
|
|
+ /* The usual (and sensible) case is to always do a full table scan.
|
|
+ ** The code in this branch only runs when testflags=1. This code
|
|
+ ** generates an artifical and unrealistic plan which is useful
|
|
+ ** for testing virtual table logic but is not helpful to real applications.
|
|
+ **
|
|
+ ** Any ==, LIKE, or GLOB constraint is marked as usable by the virtual
|
|
+ ** table (even though it is not) and the cost of running the virtual table
|
|
+ ** is reduced from 1 million to just 10. The constraints are *not* marked
|
|
+ ** as omittable, however, so the query planner should still generate a
|
|
+ ** plan that gives a correct answer, even if they plan is not optimal.
|
|
+ */
|
|
+ int i;
|
|
+ int nConst = 0;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ unsigned char op;
|
|
+ if( pIdxInfo->aConstraint[i].usable==0 ) continue;
|
|
+ op = pIdxInfo->aConstraint[i].op;
|
|
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ || op==SQLITE_INDEX_CONSTRAINT_LIKE
|
|
+ || op==SQLITE_INDEX_CONSTRAINT_GLOB
|
|
+ ){
|
|
+ pIdxInfo->estimatedCost = 10;
|
|
+ pIdxInfo->aConstraintUsage[nConst].argvIndex = nConst+1;
|
|
+ nConst++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+static const sqlite3_module CsvModule = {
|
|
+ 0, /* iVersion */
|
|
+ csvtabCreate, /* xCreate */
|
|
+ csvtabConnect, /* xConnect */
|
|
+ csvtabBestIndex, /* xBestIndex */
|
|
+ csvtabDisconnect, /* xDisconnect */
|
|
+ csvtabDisconnect, /* xDestroy */
|
|
+ csvtabOpen, /* xOpen - open a cursor */
|
|
+ csvtabClose, /* xClose - close a cursor */
|
|
+ csvtabFilter, /* xFilter - configure scan constraints */
|
|
+ csvtabNext, /* xNext - advance a cursor */
|
|
+ csvtabEof, /* xEof - check for end of scan */
|
|
+ csvtabColumn, /* xColumn - read data */
|
|
+ csvtabRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+
|
|
+#ifdef SQLITE_TEST
|
|
+/*
|
|
+** For virtual table testing, make a version of the CSV virtual table
|
|
+** available that has an xUpdate function. But the xUpdate always returns
|
|
+** SQLITE_READONLY since the CSV file is not really writable.
|
|
+*/
|
|
+static int csvtabUpdate(sqlite3_vtab *p,int n,sqlite3_value**v,sqlite3_int64*x){
|
|
+ return SQLITE_READONLY;
|
|
+}
|
|
+static const sqlite3_module CsvModuleFauxWrite = {
|
|
+ 0, /* iVersion */
|
|
+ csvtabCreate, /* xCreate */
|
|
+ csvtabConnect, /* xConnect */
|
|
+ csvtabBestIndex, /* xBestIndex */
|
|
+ csvtabDisconnect, /* xDisconnect */
|
|
+ csvtabDisconnect, /* xDestroy */
|
|
+ csvtabOpen, /* xOpen - open a cursor */
|
|
+ csvtabClose, /* xClose - close a cursor */
|
|
+ csvtabFilter, /* xFilter - configure scan constraints */
|
|
+ csvtabNext, /* xNext - advance a cursor */
|
|
+ csvtabEof, /* xEof - check for end of scan */
|
|
+ csvtabColumn, /* xColumn - read data */
|
|
+ csvtabRowid, /* xRowid - read data */
|
|
+ csvtabUpdate, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+#endif /* SQLITE_TEST */
|
|
+
|
|
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+/*
|
|
+** This routine is called when the extension is loaded. The new
|
|
+** CSV virtual table module is registered with the calling database
|
|
+** connection.
|
|
+*/
|
|
+int sqlite3_csv_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ int rc;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_module(db, "csv", &CsvModule, 0);
|
|
+#ifdef SQLITE_TEST
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_module(db, "csv_wr", &CsvModuleFauxWrite, 0);
|
|
+ }
|
|
+#endif
|
|
+ return rc;
|
|
+#else
|
|
+ return SQLITE_OK;
|
|
+#endif
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ return sqlite3_create_module(db, "csv", &CsvModule, 0);
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/dbdump.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/dbdump.c 2019-05-14 16:22:31.231256400 +0200
|
|
@@ -0,0 +1,724 @@
|
|
+/*
|
|
+** 2016-03-13
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements a C-language subroutine that converts the content
|
|
+** of an SQLite database into UTF-8 text SQL statements that can be used
|
|
+** to exactly recreate the original database. ROWID values are preserved.
|
|
+**
|
|
+** A prototype of the implemented subroutine is this:
|
|
+**
|
|
+** int sqlite3_db_dump(
|
|
+** sqlite3 *db,
|
|
+** const char *zSchema,
|
|
+** const char *zTable,
|
|
+** void (*xCallback)(void*, const char*),
|
|
+** void *pArg
|
|
+** );
|
|
+**
|
|
+** The db parameter is the database connection. zSchema is the schema within
|
|
+** that database which is to be dumped. Usually the zSchema is "main" but
|
|
+** can also be "temp" or any ATTACH-ed database. If zTable is not NULL, then
|
|
+** only the content of that one table is dumped. If zTable is NULL, then all
|
|
+** tables are dumped.
|
|
+**
|
|
+** The generate text is passed to xCallback() in multiple calls. The second
|
|
+** argument to xCallback() is a copy of the pArg parameter. The first
|
|
+** argument is some of the output text that this routine generates. The
|
|
+** signature to xCallback() is designed to make it compatible with fputs().
|
|
+**
|
|
+** The sqlite3_db_dump() subroutine returns SQLITE_OK on success or some error
|
|
+** code if it encounters a problem.
|
|
+**
|
|
+** If this file is compiled with -DDBDUMP_STANDALONE then a "main()" routine
|
|
+** is included so that this routine becomes a command-line utility. The
|
|
+** command-line utility takes two or three arguments which are the name
|
|
+** of the database file, the schema, and optionally the table, forming the
|
|
+** first three arguments of a single call to the library routine.
|
|
+*/
|
|
+#include "sqlite3.h"
|
|
+#include <stdarg.h>
|
|
+#include <string.h>
|
|
+#include <ctype.h>
|
|
+
|
|
+/*
|
|
+** The state of the dump process.
|
|
+*/
|
|
+typedef struct DState DState;
|
|
+struct DState {
|
|
+ sqlite3 *db; /* The database connection */
|
|
+ int nErr; /* Number of errors seen so far */
|
|
+ int rc; /* Error code */
|
|
+ int writableSchema; /* True if in writable_schema mode */
|
|
+ int (*xCallback)(const char*,void*); /* Send output here */
|
|
+ void *pArg; /* Argument to xCallback() */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A variable length string to which one can append text.
|
|
+*/
|
|
+typedef struct DText DText;
|
|
+struct DText {
|
|
+ char *z; /* The text */
|
|
+ int n; /* Number of bytes of content in z[] */
|
|
+ int nAlloc; /* Number of bytes allocated to z[] */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Initialize and destroy a DText object
|
|
+*/
|
|
+static void initText(DText *p){
|
|
+ memset(p, 0, sizeof(*p));
|
|
+}
|
|
+static void freeText(DText *p){
|
|
+ sqlite3_free(p->z);
|
|
+ initText(p);
|
|
+}
|
|
+
|
|
+/* zIn is either a pointer to a NULL-terminated string in memory obtained
|
|
+** from malloc(), or a NULL pointer. The string pointed to by zAppend is
|
|
+** added to zIn, and the result returned in memory obtained from malloc().
|
|
+** zIn, if it was not NULL, is freed.
|
|
+**
|
|
+** If the third argument, quote, is not '\0', then it is used as a
|
|
+** quote character for zAppend.
|
|
+*/
|
|
+static void appendText(DText *p, char const *zAppend, char quote){
|
|
+ int len;
|
|
+ int i;
|
|
+ int nAppend = (int)(strlen(zAppend) & 0x3fffffff);
|
|
+
|
|
+ len = nAppend+p->n+1;
|
|
+ if( quote ){
|
|
+ len += 2;
|
|
+ for(i=0; i<nAppend; i++){
|
|
+ if( zAppend[i]==quote ) len++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->n+len>=p->nAlloc ){
|
|
+ char *zNew;
|
|
+ p->nAlloc = p->nAlloc*2 + len + 20;
|
|
+ zNew = sqlite3_realloc(p->z, p->nAlloc);
|
|
+ if( zNew==0 ){
|
|
+ freeText(p);
|
|
+ return;
|
|
+ }
|
|
+ p->z = zNew;
|
|
+ }
|
|
+
|
|
+ if( quote ){
|
|
+ char *zCsr = p->z+p->n;
|
|
+ *zCsr++ = quote;
|
|
+ for(i=0; i<nAppend; i++){
|
|
+ *zCsr++ = zAppend[i];
|
|
+ if( zAppend[i]==quote ) *zCsr++ = quote;
|
|
+ }
|
|
+ *zCsr++ = quote;
|
|
+ p->n = (int)(zCsr - p->z);
|
|
+ *zCsr = '\0';
|
|
+ }else{
|
|
+ memcpy(p->z+p->n, zAppend, nAppend);
|
|
+ p->n += nAppend;
|
|
+ p->z[p->n] = '\0';
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Attempt to determine if identifier zName needs to be quoted, either
|
|
+** because it contains non-alphanumeric characters, or because it is an
|
|
+** SQLite keyword. Be conservative in this estimate: When in doubt assume
|
|
+** that quoting is required.
|
|
+**
|
|
+** Return '"' if quoting is required. Return 0 if no quoting is required.
|
|
+*/
|
|
+static char quoteChar(const char *zName){
|
|
+ int i;
|
|
+ if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
|
|
+ for(i=0; zName[i]; i++){
|
|
+ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
|
|
+ }
|
|
+ return sqlite3_keyword_check(zName, i) ? '"' : 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Release memory previously allocated by tableColumnList().
|
|
+*/
|
|
+static void freeColumnList(char **azCol){
|
|
+ int i;
|
|
+ for(i=1; azCol[i]; i++){
|
|
+ sqlite3_free(azCol[i]);
|
|
+ }
|
|
+ /* azCol[0] is a static string */
|
|
+ sqlite3_free(azCol);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a list of pointers to strings which are the names of all
|
|
+** columns in table zTab. The memory to hold the names is dynamically
|
|
+** allocated and must be released by the caller using a subsequent call
|
|
+** to freeColumnList().
|
|
+**
|
|
+** The azCol[0] entry is usually NULL. However, if zTab contains a rowid
|
|
+** value that needs to be preserved, then azCol[0] is filled in with the
|
|
+** name of the rowid column.
|
|
+**
|
|
+** The first regular column in the table is azCol[1]. The list is terminated
|
|
+** by an entry with azCol[i]==0.
|
|
+*/
|
|
+static char **tableColumnList(DState *p, const char *zTab){
|
|
+ char **azCol = 0;
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ char *zSql;
|
|
+ int nCol = 0;
|
|
+ int nAlloc = 0;
|
|
+ int nPK = 0; /* Number of PRIMARY KEY columns seen */
|
|
+ int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
|
|
+ int preserveRowid = 1;
|
|
+ int rc;
|
|
+
|
|
+ zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab);
|
|
+ if( zSql==0 ) return 0;
|
|
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) return 0;
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ if( nCol>=nAlloc-2 ){
|
|
+ char **azNew;
|
|
+ nAlloc = nAlloc*2 + nCol + 10;
|
|
+ azNew = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
|
|
+ if( azNew==0 ) goto col_oom;
|
|
+ azCol = azNew;
|
|
+ azCol[0] = 0;
|
|
+ }
|
|
+ azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
|
+ if( azCol[nCol]==0 ) goto col_oom;
|
|
+ if( sqlite3_column_int(pStmt, 5) ){
|
|
+ nPK++;
|
|
+ if( nPK==1
|
|
+ && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2),
|
|
+ "INTEGER")==0
|
|
+ ){
|
|
+ isIPK = 1;
|
|
+ }else{
|
|
+ isIPK = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ pStmt = 0;
|
|
+ azCol[nCol+1] = 0;
|
|
+
|
|
+ /* The decision of whether or not a rowid really needs to be preserved
|
|
+ ** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table
|
|
+ ** or a table with an INTEGER PRIMARY KEY. We are unable to preserve
|
|
+ ** rowids on tables where the rowid is inaccessible because there are other
|
|
+ ** columns in the table named "rowid", "_rowid_", and "oid".
|
|
+ */
|
|
+ if( isIPK ){
|
|
+ /* If a single PRIMARY KEY column with type INTEGER was seen, then it
|
|
+ ** might be an alise for the ROWID. But it might also be a WITHOUT ROWID
|
|
+ ** table or a INTEGER PRIMARY KEY DESC column, neither of which are
|
|
+ ** ROWID aliases. To distinguish these cases, check to see if
|
|
+ ** there is a "pk" entry in "PRAGMA index_list". There will be
|
|
+ ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID.
|
|
+ */
|
|
+ zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)"
|
|
+ " WHERE origin='pk'", zTab);
|
|
+ if( zSql==0 ) goto col_oom;
|
|
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ){
|
|
+ freeColumnList(azCol);
|
|
+ return 0;
|
|
+ }
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ sqlite3_finalize(pStmt);
|
|
+ pStmt = 0;
|
|
+ preserveRowid = rc==SQLITE_ROW;
|
|
+ }
|
|
+ if( preserveRowid ){
|
|
+ /* Only preserve the rowid if we can find a name to use for the
|
|
+ ** rowid */
|
|
+ static char *azRowid[] = { "rowid", "_rowid_", "oid" };
|
|
+ int i, j;
|
|
+ for(j=0; j<3; j++){
|
|
+ for(i=1; i<=nCol; i++){
|
|
+ if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break;
|
|
+ }
|
|
+ if( i>nCol ){
|
|
+ /* At this point, we know that azRowid[j] is not the name of any
|
|
+ ** ordinary column in the table. Verify that azRowid[j] is a valid
|
|
+ ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID
|
|
+ ** tables will fail this last check */
|
|
+ rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0);
|
|
+ if( rc==SQLITE_OK ) azCol[0] = azRowid[j];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return azCol;
|
|
+
|
|
+col_oom:
|
|
+ sqlite3_finalize(pStmt);
|
|
+ freeColumnList(azCol);
|
|
+ p->nErr++;
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Send mprintf-formatted content to the output callback.
|
|
+*/
|
|
+static void output_formatted(DState *p, const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ char *z;
|
|
+ va_start(ap, zFormat);
|
|
+ z = sqlite3_vmprintf(zFormat, ap);
|
|
+ va_end(ap);
|
|
+ p->xCallback(z, p->pArg);
|
|
+ sqlite3_free(z);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Find a string that is not found anywhere in z[]. Return a pointer
|
|
+** to that string.
|
|
+**
|
|
+** Try to use zA and zB first. If both of those are already found in z[]
|
|
+** then make up some string and store it in the buffer zBuf.
|
|
+*/
|
|
+static const char *unused_string(
|
|
+ const char *z, /* Result must not appear anywhere in z */
|
|
+ const char *zA, const char *zB, /* Try these first */
|
|
+ char *zBuf /* Space to store a generated string */
|
|
+){
|
|
+ unsigned i = 0;
|
|
+ if( strstr(z, zA)==0 ) return zA;
|
|
+ if( strstr(z, zB)==0 ) return zB;
|
|
+ do{
|
|
+ sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++);
|
|
+ }while( strstr(z,zBuf)!=0 );
|
|
+ return zBuf;
|
|
+}
|
|
+
|
|
+/*
|
|
+** 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.
|
|
+*/
|
|
+static void output_quoted_escaped_string(DState *p, const char *z){
|
|
+ int i;
|
|
+ char c;
|
|
+ for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
|
|
+ if( c==0 ){
|
|
+ output_formatted(p,"'%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 ){
|
|
+ p->xCallback("replace(", p->pArg);
|
|
+ zNL = unused_string(z, "\\n", "\\012", zBuf1);
|
|
+ }
|
|
+ if( nCR ){
|
|
+ p->xCallback("replace(", p->pArg);
|
|
+ zCR = unused_string(z, "\\r", "\\015", zBuf2);
|
|
+ }
|
|
+ p->xCallback("'", p->pArg);
|
|
+ while( *z ){
|
|
+ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){}
|
|
+ if( c=='\'' ) i++;
|
|
+ if( i ){
|
|
+ output_formatted(p, "%.*s", i, z);
|
|
+ z += i;
|
|
+ }
|
|
+ if( c=='\'' ){
|
|
+ p->xCallback("'", p->pArg);
|
|
+ continue;
|
|
+ }
|
|
+ if( c==0 ){
|
|
+ break;
|
|
+ }
|
|
+ z++;
|
|
+ if( c=='\n' ){
|
|
+ p->xCallback(zNL, p->pArg);
|
|
+ continue;
|
|
+ }
|
|
+ p->xCallback(zCR, p->pArg);
|
|
+ }
|
|
+ p->xCallback("'", p->pArg);
|
|
+ if( nCR ){
|
|
+ output_formatted(p, ",'%s',char(13))", zCR);
|
|
+ }
|
|
+ if( nNL ){
|
|
+ output_formatted(p, ",'%s',char(10))", zNL);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is an sqlite3_exec callback routine used for dumping the database.
|
|
+** Each row received by this callback consists of a table name,
|
|
+** the table type ("index" or "table") and SQL to create the table.
|
|
+** This routine should print text sufficient to recreate the table.
|
|
+*/
|
|
+static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
|
+ int rc;
|
|
+ const char *zTable;
|
|
+ const char *zType;
|
|
+ const char *zSql;
|
|
+ DState *p = (DState*)pArg;
|
|
+ sqlite3_stmt *pStmt;
|
|
+
|
|
+ (void)azCol;
|
|
+ if( nArg!=3 ) return 1;
|
|
+ zTable = azArg[0];
|
|
+ zType = azArg[1];
|
|
+ zSql = azArg[2];
|
|
+
|
|
+ if( strcmp(zTable, "sqlite_sequence")==0 ){
|
|
+ p->xCallback("DELETE FROM sqlite_sequence;\n", p->pArg);
|
|
+ }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
|
|
+ p->xCallback("ANALYZE sqlite_master;\n", p->pArg);
|
|
+ }else if( strncmp(zTable, "sqlite_", 7)==0 ){
|
|
+ return 0;
|
|
+ }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
|
|
+ if( !p->writableSchema ){
|
|
+ p->xCallback("PRAGMA writable_schema=ON;\n", p->pArg);
|
|
+ p->writableSchema = 1;
|
|
+ }
|
|
+ output_formatted(p,
|
|
+ "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
|
|
+ "VALUES('table','%q','%q',0,'%q');",
|
|
+ zTable, zTable, zSql);
|
|
+ return 0;
|
|
+ }else{
|
|
+ if( sqlite3_strglob("CREATE TABLE ['\"]*", zSql)==0 ){
|
|
+ p->xCallback("CREATE TABLE IF NOT EXISTS ", p->pArg);
|
|
+ p->xCallback(zSql+13, p->pArg);
|
|
+ }else{
|
|
+ p->xCallback(zSql, p->pArg);
|
|
+ }
|
|
+ p->xCallback(";\n", p->pArg);
|
|
+ }
|
|
+
|
|
+ if( strcmp(zType, "table")==0 ){
|
|
+ DText sSelect;
|
|
+ DText sTable;
|
|
+ char **azTCol;
|
|
+ int i;
|
|
+ int nCol;
|
|
+
|
|
+ azTCol = tableColumnList(p, zTable);
|
|
+ if( azTCol==0 ) return 0;
|
|
+
|
|
+ initText(&sTable);
|
|
+ appendText(&sTable, "INSERT INTO ", 0);
|
|
+
|
|
+ /* Always quote the table name, even if it appears to be pure ascii,
|
|
+ ** in case it is a keyword. Ex: INSERT INTO "table" ... */
|
|
+ appendText(&sTable, zTable, quoteChar(zTable));
|
|
+
|
|
+ /* If preserving the rowid, add a column list after the table name.
|
|
+ ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)"
|
|
+ ** instead of the usual "INSERT INTO tab VALUES(...)".
|
|
+ */
|
|
+ if( azTCol[0] ){
|
|
+ appendText(&sTable, "(", 0);
|
|
+ appendText(&sTable, azTCol[0], 0);
|
|
+ for(i=1; azTCol[i]; i++){
|
|
+ appendText(&sTable, ",", 0);
|
|
+ appendText(&sTable, azTCol[i], quoteChar(azTCol[i]));
|
|
+ }
|
|
+ appendText(&sTable, ")", 0);
|
|
+ }
|
|
+ appendText(&sTable, " VALUES(", 0);
|
|
+
|
|
+ /* Build an appropriate SELECT statement */
|
|
+ initText(&sSelect);
|
|
+ appendText(&sSelect, "SELECT ", 0);
|
|
+ if( azTCol[0] ){
|
|
+ appendText(&sSelect, azTCol[0], 0);
|
|
+ appendText(&sSelect, ",", 0);
|
|
+ }
|
|
+ for(i=1; azTCol[i]; i++){
|
|
+ appendText(&sSelect, azTCol[i], quoteChar(azTCol[i]));
|
|
+ if( azTCol[i+1] ){
|
|
+ appendText(&sSelect, ",", 0);
|
|
+ }
|
|
+ }
|
|
+ nCol = i;
|
|
+ if( azTCol[0]==0 ) nCol--;
|
|
+ freeColumnList(azTCol);
|
|
+ appendText(&sSelect, " FROM ", 0);
|
|
+ appendText(&sSelect, zTable, quoteChar(zTable));
|
|
+
|
|
+ rc = sqlite3_prepare_v2(p->db, sSelect.z, -1, &pStmt, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ p->nErr++;
|
|
+ if( p->rc==SQLITE_OK ) p->rc = rc;
|
|
+ }else{
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ p->xCallback(sTable.z, p->pArg);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( i ) p->xCallback(",", p->pArg);
|
|
+ switch( sqlite3_column_type(pStmt,i) ){
|
|
+ case SQLITE_INTEGER: {
|
|
+ output_formatted(p, "%lld", sqlite3_column_int64(pStmt,i));
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_FLOAT: {
|
|
+ double r = sqlite3_column_double(pStmt,i);
|
|
+ sqlite3_uint64 ur;
|
|
+ memcpy(&ur,&r,sizeof(r));
|
|
+ if( ur==0x7ff0000000000000LL ){
|
|
+ p->xCallback("1e999", p->pArg);
|
|
+ }else if( ur==0xfff0000000000000LL ){
|
|
+ p->xCallback("-1e999", p->pArg);
|
|
+ }else{
|
|
+ output_formatted(p, "%!.20g", r);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_NULL: {
|
|
+ p->xCallback("NULL", p->pArg);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ output_quoted_escaped_string(p,
|
|
+ (const char*)sqlite3_column_text(pStmt,i));
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_BLOB: {
|
|
+ int nByte = sqlite3_column_bytes(pStmt,i);
|
|
+ unsigned char *a = (unsigned char*)sqlite3_column_blob(pStmt,i);
|
|
+ int j;
|
|
+ p->xCallback("x'", p->pArg);
|
|
+ for(j=0; j<nByte; j++){
|
|
+ char zWord[3];
|
|
+ zWord[0] = "0123456789abcdef"[(a[j]>>4)&15];
|
|
+ zWord[1] = "0123456789abcdef"[a[j]&15];
|
|
+ zWord[2] = 0;
|
|
+ p->xCallback(zWord, p->pArg);
|
|
+ }
|
|
+ p->xCallback("'", p->pArg);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ p->xCallback(");\n", p->pArg);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ freeText(&sTable);
|
|
+ freeText(&sSelect);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Execute a query statement that will generate SQL output. Print
|
|
+** the result columns, comma-separated, on a line and then add a
|
|
+** semicolon terminator to the end of that line.
|
|
+**
|
|
+** If the number of columns is 1 and that column contains text "--"
|
|
+** then write the semicolon on a separate line. That way, if a
|
|
+** "--" comment occurs at the end of the statement, the comment
|
|
+** won't consume the semicolon terminator.
|
|
+*/
|
|
+static void output_sql_from_query(
|
|
+ DState *p, /* Query context */
|
|
+ const char *zSelect, /* SELECT statement to extract content */
|
|
+ ...
|
|
+){
|
|
+ sqlite3_stmt *pSelect;
|
|
+ int rc;
|
|
+ int nResult;
|
|
+ int i;
|
|
+ const char *z;
|
|
+ char *zSql;
|
|
+ va_list ap;
|
|
+ va_start(ap, zSelect);
|
|
+ zSql = sqlite3_vmprintf(zSelect, ap);
|
|
+ va_end(ap);
|
|
+ if( zSql==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ p->nErr++;
|
|
+ return;
|
|
+ }
|
|
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pSelect, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc!=SQLITE_OK || !pSelect ){
|
|
+ output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc,
|
|
+ sqlite3_errmsg(p->db));
|
|
+ p->nErr++;
|
|
+ return;
|
|
+ }
|
|
+ rc = sqlite3_step(pSelect);
|
|
+ nResult = sqlite3_column_count(pSelect);
|
|
+ while( rc==SQLITE_ROW ){
|
|
+ z = (const char*)sqlite3_column_text(pSelect, 0);
|
|
+ p->xCallback(z, p->pArg);
|
|
+ for(i=1; i<nResult; i++){
|
|
+ p->xCallback(",", p->pArg);
|
|
+ p->xCallback((const char*)sqlite3_column_text(pSelect,i), p->pArg);
|
|
+ }
|
|
+ if( z==0 ) z = "";
|
|
+ while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
|
|
+ if( z[0] ){
|
|
+ p->xCallback("\n;\n", p->pArg);
|
|
+ }else{
|
|
+ p->xCallback(";\n", p->pArg);
|
|
+ }
|
|
+ rc = sqlite3_step(pSelect);
|
|
+ }
|
|
+ rc = sqlite3_finalize(pSelect);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc,
|
|
+ sqlite3_errmsg(p->db));
|
|
+ if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Run zQuery. Use dump_callback() as the callback routine so that
|
|
+** the contents of the query are output as SQL statements.
|
|
+**
|
|
+** If we get a SQLITE_CORRUPT error, rerun the query after appending
|
|
+** "ORDER BY rowid DESC" to the end.
|
|
+*/
|
|
+static void run_schema_dump_query(
|
|
+ DState *p,
|
|
+ const char *zQuery,
|
|
+ ...
|
|
+){
|
|
+ char *zErr = 0;
|
|
+ char *z;
|
|
+ va_list ap;
|
|
+ va_start(ap, zQuery);
|
|
+ z = sqlite3_vmprintf(zQuery, ap);
|
|
+ va_end(ap);
|
|
+ sqlite3_exec(p->db, z, dump_callback, p, &zErr);
|
|
+ sqlite3_free(z);
|
|
+ if( zErr ){
|
|
+ output_formatted(p, "/****** %s ******/\n", zErr);
|
|
+ sqlite3_free(zErr);
|
|
+ p->nErr++;
|
|
+ zErr = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert an SQLite database into SQL statements that will recreate that
|
|
+** database.
|
|
+*/
|
|
+int sqlite3_db_dump(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which schema to dump. Usually "main". */
|
|
+ const char *zTable, /* Which table to dump. NULL means everything. */
|
|
+ int (*xCallback)(const char*,void*), /* Output sent to this callback */
|
|
+ void *pArg /* Second argument of the callback */
|
|
+){
|
|
+ DState x;
|
|
+ memset(&x, 0, sizeof(x));
|
|
+ x.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
|
+ if( x.rc ) return x.rc;
|
|
+ x.db = db;
|
|
+ x.xCallback = xCallback;
|
|
+ x.pArg = pArg;
|
|
+ xCallback("PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n", pArg);
|
|
+ if( zTable==0 ){
|
|
+ run_schema_dump_query(&x,
|
|
+ "SELECT name, type, sql FROM \"%w\".sqlite_master "
|
|
+ "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'",
|
|
+ zSchema
|
|
+ );
|
|
+ run_schema_dump_query(&x,
|
|
+ "SELECT name, type, sql FROM \"%w\".sqlite_master "
|
|
+ "WHERE name=='sqlite_sequence'", zSchema
|
|
+ );
|
|
+ output_sql_from_query(&x,
|
|
+ "SELECT sql FROM sqlite_master "
|
|
+ "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
|
|
+ );
|
|
+ }else{
|
|
+ run_schema_dump_query(&x,
|
|
+ "SELECT name, type, sql FROM \"%w\".sqlite_master "
|
|
+ "WHERE tbl_name=%Q COLLATE nocase AND type=='table'"
|
|
+ " AND sql NOT NULL",
|
|
+ zSchema, zTable
|
|
+ );
|
|
+ output_sql_from_query(&x,
|
|
+ "SELECT sql FROM \"%w\".sqlite_master "
|
|
+ "WHERE sql NOT NULL"
|
|
+ " AND type IN ('index','trigger','view')"
|
|
+ " AND tbl_name=%Q COLLATE nocase",
|
|
+ zSchema, zTable
|
|
+ );
|
|
+ }
|
|
+ if( x.writableSchema ){
|
|
+ xCallback("PRAGMA writable_schema=OFF;\n", pArg);
|
|
+ }
|
|
+ xCallback(x.nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n", pArg);
|
|
+ sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
|
+ return x.rc;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/* The generic subroutine is above. The code the follows implements
|
|
+** the command-line interface.
|
|
+*/
|
|
+#ifdef DBDUMP_STANDALONE
|
|
+#include <stdio.h>
|
|
+
|
|
+/*
|
|
+** Command-line interface
|
|
+*/
|
|
+int main(int argc, char **argv){
|
|
+ sqlite3 *db;
|
|
+ const char *zDb;
|
|
+ const char *zSchema;
|
|
+ const char *zTable = 0;
|
|
+ int rc;
|
|
+
|
|
+ if( argc<2 || argc>4 ){
|
|
+ fprintf(stderr, "Usage: %s DATABASE ?SCHEMA? ?TABLE?\n", argv[0]);
|
|
+ return 1;
|
|
+ }
|
|
+ zDb = argv[1];
|
|
+ zSchema = argc>=3 ? argv[2] : "main";
|
|
+ zTable = argc==4 ? argv[3] : 0;
|
|
+
|
|
+ rc = sqlite3_open(zDb, &db);
|
|
+ if( rc ){
|
|
+ fprintf(stderr, "Cannot open \"%s\": %s\n", zDb, sqlite3_errmsg(db));
|
|
+ sqlite3_close(db);
|
|
+ return 1;
|
|
+ }
|
|
+ rc = sqlite3_db_dump(db, zSchema, zTable,
|
|
+ (int(*)(const char*,void*))fputs, (void*)stdout);
|
|
+ if( rc ){
|
|
+ fprintf(stderr, "Error: sqlite3_db_dump() returns %d\n", rc);
|
|
+ }
|
|
+ sqlite3_close(db);
|
|
+ return rc!=SQLITE_OK;
|
|
+}
|
|
+#endif /* DBDUMP_STANDALONE */
|
|
--- origsrc/sqlite-autoconf-3280000/eval.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/eval.c 2019-05-14 16:22:31.233209100 +0200
|
|
@@ -0,0 +1,141 @@
|
|
+/*
|
|
+** 2014-11-10
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This SQLite extension implements SQL function eval() which runs
|
|
+** SQL statements recursively.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Structure used to accumulate the output
|
|
+*/
|
|
+struct EvalResult {
|
|
+ char *z; /* Accumulated output */
|
|
+ const char *zSep; /* Separator */
|
|
+ int szSep; /* Size of the separator string */
|
|
+ size_t nAlloc; /* Number of bytes allocated for z[] */
|
|
+ size_t nUsed; /* Number of bytes of z[] actually used */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Callback from sqlite_exec() for the eval() function.
|
|
+*/
|
|
+static int callback(void *pCtx, int argc, char **argv, char **colnames){
|
|
+ struct EvalResult *p = (struct EvalResult*)pCtx;
|
|
+ int i;
|
|
+ if( argv==0 ) return 0;
|
|
+ for(i=0; i<argc; i++){
|
|
+ const char *z = argv[i] ? argv[i] : "";
|
|
+ size_t sz = strlen(z);
|
|
+ if( sz+p->nUsed+p->szSep+1 > p->nAlloc ){
|
|
+ char *zNew;
|
|
+ p->nAlloc = p->nAlloc*2 + sz + p->szSep + 1;
|
|
+ /* Using sqlite3_realloc64() would be better, but it is a recent
|
|
+ ** addition and will cause a segfault if loaded by an older version
|
|
+ ** of SQLite. */
|
|
+ zNew = p->nAlloc<=0x7fffffff ? sqlite3_realloc(p->z, p->nAlloc) : 0;
|
|
+ if( zNew==0 ){
|
|
+ sqlite3_free(p->z);
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ return 1;
|
|
+ }
|
|
+ p->z = zNew;
|
|
+ }
|
|
+ if( p->nUsed>0 ){
|
|
+ memcpy(&p->z[p->nUsed], p->zSep, p->szSep);
|
|
+ p->nUsed += p->szSep;
|
|
+ }
|
|
+ memcpy(&p->z[p->nUsed], z, sz);
|
|
+ p->nUsed += sz;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the eval(X) and eval(X,Y) SQL functions.
|
|
+**
|
|
+** Evaluate the SQL text in X. Return the results, using string
|
|
+** Y as the separator. If Y is omitted, use a single space character.
|
|
+*/
|
|
+static void sqlEvalFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *zSql;
|
|
+ sqlite3 *db;
|
|
+ char *zErr = 0;
|
|
+ int rc;
|
|
+ struct EvalResult x;
|
|
+
|
|
+ memset(&x, 0, sizeof(x));
|
|
+ x.zSep = " ";
|
|
+ zSql = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zSql==0 ) return;
|
|
+ if( argc>1 ){
|
|
+ x.zSep = (const char*)sqlite3_value_text(argv[1]);
|
|
+ if( x.zSep==0 ) return;
|
|
+ }
|
|
+ x.szSep = (int)strlen(x.zSep);
|
|
+ db = sqlite3_context_db_handle(context);
|
|
+ rc = sqlite3_exec(db, zSql, callback, &x, &zErr);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_result_error(context, zErr, -1);
|
|
+ sqlite3_free(zErr);
|
|
+ }else if( x.zSep==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ sqlite3_free(x.z);
|
|
+ }else{
|
|
+ sqlite3_result_text(context, x.z, x.nUsed, sqlite3_free);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_eval_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
|
+ sqlEvalFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
|
+ sqlEvalFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
|
+ sqlEvalFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
|
+ sqlEvalFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/explain.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/explain.c 2019-05-14 16:22:31.235161700 +0200
|
|
@@ -0,0 +1,322 @@
|
|
+/*
|
|
+** 2018-09-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 demonstrates an eponymous virtual table that returns the
|
|
+** EXPLAIN output from an SQL statement.
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** .load ./explain
|
|
+** SELECT p2 FROM explain('SELECT * FROM sqlite_master')
|
|
+** WHERE opcode='OpenRead';
|
|
+**
|
|
+** This module was originally written to help simplify SQLite testing,
|
|
+** by providing an easier means of verifying certain patterns in the
|
|
+** generated bytecode.
|
|
+*/
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/* explain_vtab is a subclass of sqlite3_vtab which will
|
|
+** serve as the underlying representation of a explain virtual table
|
|
+*/
|
|
+typedef struct explain_vtab explain_vtab;
|
|
+struct explain_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this explain vtab */
|
|
+};
|
|
+
|
|
+/* explain_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result from an EXPLAIN operation.
|
|
+*/
|
|
+typedef struct explain_cursor explain_cursor;
|
|
+struct explain_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this cursor */
|
|
+ char *zSql; /* Value for the EXPLN_COLUMN_SQL column */
|
|
+ sqlite3_stmt *pExplain; /* Statement being explained */
|
|
+ int rc; /* Result of last sqlite3_step() on pExplain */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The explainConnect() method is invoked to create a new
|
|
+** explain_vtab that describes the explain virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for explain_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the explain_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against explain will look like.
|
|
+*/
|
|
+static int explainConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ explain_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define EXPLN_COLUMN_ADDR 0 /* Instruction address */
|
|
+#define EXPLN_COLUMN_OPCODE 1 /* Opcode */
|
|
+#define EXPLN_COLUMN_P1 2 /* Operand 1 */
|
|
+#define EXPLN_COLUMN_P2 3 /* Operand 2 */
|
|
+#define EXPLN_COLUMN_P3 4 /* Operand 3 */
|
|
+#define EXPLN_COLUMN_P4 5 /* Operand 4 */
|
|
+#define EXPLN_COLUMN_P5 6 /* Operand 5 */
|
|
+#define EXPLN_COLUMN_COMMENT 7 /* Comment */
|
|
+#define EXPLN_COLUMN_SQL 8 /* SQL that is being explained */
|
|
+
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(addr,opcode,p1,p2,p3,p4,p5,comment,sql HIDDEN)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->db = db;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for explain_cursor objects.
|
|
+*/
|
|
+static int explainDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new explain_cursor object.
|
|
+*/
|
|
+static int explainOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ explain_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->db = ((explain_vtab*)p)->db;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a explain_cursor.
|
|
+*/
|
|
+static int explainClose(sqlite3_vtab_cursor *cur){
|
|
+ explain_cursor *pCur = (explain_cursor*)cur;
|
|
+ sqlite3_finalize(pCur->pExplain);
|
|
+ sqlite3_free(pCur->zSql);
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a explain_cursor to its next row of output.
|
|
+*/
|
|
+static int explainNext(sqlite3_vtab_cursor *cur){
|
|
+ explain_cursor *pCur = (explain_cursor*)cur;
|
|
+ pCur->rc = sqlite3_step(pCur->pExplain);
|
|
+ if( pCur->rc!=SQLITE_DONE && pCur->rc!=SQLITE_ROW ) return pCur->rc;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the explain_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int explainColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ explain_cursor *pCur = (explain_cursor*)cur;
|
|
+ if( i==EXPLN_COLUMN_SQL ){
|
|
+ sqlite3_result_text(ctx, pCur->zSql, -1, SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pExplain, i));
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int explainRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ explain_cursor *pCur = (explain_cursor*)cur;
|
|
+ *pRowid = sqlite3_column_int64(pCur->pExplain, 0);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int explainEof(sqlite3_vtab_cursor *cur){
|
|
+ explain_cursor *pCur = (explain_cursor*)cur;
|
|
+ return pCur->rc!=SQLITE_ROW;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the explain_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to explainColumn() or explainRowid() or
|
|
+** explainEof().
|
|
+**
|
|
+** The argv[0] is the SQL statement that is to be explained.
|
|
+*/
|
|
+static int explainFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ explain_cursor *pCur = (explain_cursor *)pVtabCursor;
|
|
+ char *zSql = 0;
|
|
+ int rc;
|
|
+ sqlite3_finalize(pCur->pExplain);
|
|
+ pCur->pExplain = 0;
|
|
+ if( sqlite3_value_type(argv[0])!=SQLITE_TEXT ){
|
|
+ pCur->rc = SQLITE_DONE;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ sqlite3_free(pCur->zSql);
|
|
+ pCur->zSql = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
|
+ if( pCur->zSql ){
|
|
+ zSql = sqlite3_mprintf("EXPLAIN %s", pCur->zSql);
|
|
+ }
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pExplain, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ if( rc ){
|
|
+ sqlite3_finalize(pCur->pExplain);
|
|
+ pCur->pExplain = 0;
|
|
+ sqlite3_free(pCur->zSql);
|
|
+ pCur->zSql = 0;
|
|
+ }else{
|
|
+ pCur->rc = sqlite3_step(pCur->pExplain);
|
|
+ rc = (pCur->rc==SQLITE_DONE || pCur->rc==SQLITE_ROW) ? SQLITE_OK : pCur->rc;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the explain virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int explainBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop counter */
|
|
+ int idx = -1; /* Index of a usable == constraint against SQL */
|
|
+ int unusable = 0; /* True if there are unusable constraints on SQL */
|
|
+
|
|
+ pIdxInfo->estimatedRows = 500;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
|
+ if( p->iColumn!=EXPLN_COLUMN_SQL ) continue;
|
|
+ if( !p->usable ){
|
|
+ unusable = 1;
|
|
+ }else if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ idx = i;
|
|
+ }
|
|
+ }
|
|
+ if( idx>=0 ){
|
|
+ /* There exists a usable == constraint against the SQL column */
|
|
+ pIdxInfo->estimatedCost = 10.0;
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ pIdxInfo->aConstraintUsage[idx].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[idx].omit = 1;
|
|
+ }else if( unusable ){
|
|
+ /* There are unusable constraints against the SQL column. Do not allow
|
|
+ ** this plan to continue forward. */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** explain virtual table.
|
|
+*/
|
|
+static sqlite3_module explainModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ explainConnect, /* xConnect */
|
|
+ explainBestIndex, /* xBestIndex */
|
|
+ explainDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ explainOpen, /* xOpen - open a cursor */
|
|
+ explainClose, /* xClose - close a cursor */
|
|
+ explainFilter, /* xFilter - configure scan constraints */
|
|
+ explainNext, /* xNext - advance a cursor */
|
|
+ explainEof, /* xEof - check for end of scan */
|
|
+ explainColumn, /* xColumn - read data */
|
|
+ explainRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+int sqlite3ExplainVtabInit(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "explain", &explainModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_explain_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3ExplainVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/fileio.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/fileio.c 2019-05-14 16:22:31.236138000 +0200
|
|
@@ -0,0 +1,1018 @@
|
|
+/*
|
|
+** 2014-06-13
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This SQLite extension implements SQL functions readfile() and
|
|
+** writefile(), and eponymous virtual type "fsdir".
|
|
+**
|
|
+** WRITEFILE(FILE, DATA [, MODE [, MTIME]]):
|
|
+**
|
|
+** If neither of the optional arguments is present, then this UDF
|
|
+** function writes blob DATA to file FILE. If successful, the number
|
|
+** of bytes written is returned. If an error occurs, NULL is returned.
|
|
+**
|
|
+** If the first option argument - MODE - is present, then it must
|
|
+** be passed an integer value that corresponds to a POSIX mode
|
|
+** value (file type + permissions, as returned in the stat.st_mode
|
|
+** field by the stat() system call). Three types of files may
|
|
+** be written/created:
|
|
+**
|
|
+** regular files: (mode & 0170000)==0100000
|
|
+** symbolic links: (mode & 0170000)==0120000
|
|
+** directories: (mode & 0170000)==0040000
|
|
+**
|
|
+** For a directory, the DATA is ignored. For a symbolic link, it is
|
|
+** interpreted as text and used as the target of the link. For a
|
|
+** regular file, it is interpreted as a blob and written into the
|
|
+** named file. Regardless of the type of file, its permissions are
|
|
+** 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 three 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:
|
|
+**
|
|
+** Used as follows:
|
|
+**
|
|
+** SELECT * FROM fsdir($path [, $dir]);
|
|
+**
|
|
+** Parameter $path is an absolute or relative pathname. If the file that it
|
|
+** refers to does not exist, it is an error. If the path refers to a regular
|
|
+** file or symbolic link, it returns a single row. Or, if the path refers
|
|
+** to a directory, it returns one row for the directory, and one row for each
|
|
+** file within the hierarchy rooted at $path.
|
|
+**
|
|
+** Each row has the following columns:
|
|
+**
|
|
+** name: Path to file or directory (text value).
|
|
+** mode: Value of stat.st_mode for directory entry (an integer).
|
|
+** mtime: Value of stat.st_mtime for directory entry (an integer).
|
|
+** data: For a regular file, a blob containing the file data. For a
|
|
+** symlink, a text value containing the text of the link. For a
|
|
+** directory, NULL.
|
|
+**
|
|
+** If a non-NULL value is specified for the optional $dir parameter and
|
|
+** $path is a relative path, then $path is interpreted relative to $dir.
|
|
+** And the paths returned in the "name" column of the table are also
|
|
+** relative to directory $dir.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <dirent.h>
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+# include <unistd.h>
|
|
+# include <utime.h>
|
|
+# include <sys/time.h>
|
|
+#else
|
|
+# include "windows.h"
|
|
+# include <io.h>
|
|
+# include <direct.h>
|
|
+# include "test_windirent.h"
|
|
+# ifndef chmod
|
|
+# define chmod _chmod
|
|
+# endif
|
|
+# ifndef stat
|
|
+# define stat _stat
|
|
+# 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 */
|
|
+#define FSDIR_COLUMN_MODE 1 /* Access mode */
|
|
+#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
|
|
+#define FSDIR_COLUMN_DATA 3 /* File content */
|
|
+#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
|
|
+#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
|
|
+
|
|
+
|
|
+/*
|
|
+** Set the result stored by context ctx to a blob containing the
|
|
+** contents of file zName. Or, leave the result unchanged (NULL)
|
|
+** if the file does not exist or is unreadable.
|
|
+**
|
|
+** If the file exceeds the SQLite blob size limit, through an
|
|
+** SQLITE_TOOBIG error.
|
|
+**
|
|
+** Throw an SQLITE_IOERR if there are difficulties pulling the file
|
|
+** off of disk.
|
|
+*/
|
|
+static void readFileContents(sqlite3_context *ctx, const char *zName){
|
|
+ FILE *in;
|
|
+ sqlite3_int64 nIn;
|
|
+ void *pBuf;
|
|
+ sqlite3 *db;
|
|
+ int mxBlob;
|
|
+
|
|
+ in = 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);
|
|
+ db = sqlite3_context_db_handle(ctx);
|
|
+ mxBlob = sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1);
|
|
+ if( nIn>mxBlob ){
|
|
+ sqlite3_result_error_code(ctx, SQLITE_TOOBIG);
|
|
+ fclose(in);
|
|
+ return;
|
|
+ }
|
|
+ pBuf = sqlite3_malloc( nIn ? nIn : 1 );
|
|
+ if( pBuf==0 ){
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ fclose(in);
|
|
+ return;
|
|
+ }
|
|
+ if( nIn==(sqlite3_int64)fread(pBuf, 1, (size_t)nIn, in) ){
|
|
+ sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free);
|
|
+ }else{
|
|
+ sqlite3_result_error_code(ctx, SQLITE_IOERR);
|
|
+ sqlite3_free(pBuf);
|
|
+ }
|
|
+ fclose(in);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the "readfile(X)" SQL function. The entire content
|
|
+** of the file named X is read and returned as a BLOB. NULL is returned
|
|
+** if the file does not exist or is unreadable.
|
|
+*/
|
|
+static void readfileFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *zName;
|
|
+ (void)(argc); /* Unused parameter */
|
|
+ zName = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zName==0 ) return;
|
|
+ readFileContents(context, zName);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set the error message contained in context ctx to the results of
|
|
+** vprintf(zFmt, ...).
|
|
+*/
|
|
+static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
|
|
+ char *zMsg = 0;
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ zMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ sqlite3_result_error(ctx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+#if defined(_WIN32)
|
|
+/*
|
|
+** This function is designed to convert a Win32 FILETIME structure into the
|
|
+** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC).
|
|
+*/
|
|
+static sqlite3_uint64 fileTimeToUnixTime(
|
|
+ LPFILETIME pFileTime
|
|
+){
|
|
+ SYSTEMTIME epochSystemTime;
|
|
+ ULARGE_INTEGER epochIntervals;
|
|
+ FILETIME epochFileTime;
|
|
+ ULARGE_INTEGER fileIntervals;
|
|
+
|
|
+ memset(&epochSystemTime, 0, sizeof(SYSTEMTIME));
|
|
+ epochSystemTime.wYear = 1970;
|
|
+ epochSystemTime.wMonth = 1;
|
|
+ epochSystemTime.wDay = 1;
|
|
+ SystemTimeToFileTime(&epochSystemTime, &epochFileTime);
|
|
+ epochIntervals.LowPart = epochFileTime.dwLowDateTime;
|
|
+ epochIntervals.HighPart = epochFileTime.dwHighDateTime;
|
|
+
|
|
+ fileIntervals.LowPart = pFileTime->dwLowDateTime;
|
|
+ fileIntervals.HighPart = pFileTime->dwHighDateTime;
|
|
+
|
|
+ return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function attempts to normalize the time values found in the stat()
|
|
+** buffer to UTC. This is necessary on Win32, where the runtime library
|
|
+** appears to return these values as local times.
|
|
+*/
|
|
+static void statTimesToUtc(
|
|
+ const char *zPath,
|
|
+ struct stat *pStatBuf
|
|
+){
|
|
+ HANDLE hFindFile;
|
|
+ WIN32_FIND_DATAW fd;
|
|
+ LPWSTR zUnicodeName;
|
|
+ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
|
|
+ zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
|
|
+ if( zUnicodeName ){
|
|
+ memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
|
|
+ hFindFile = FindFirstFileW(zUnicodeName, &fd);
|
|
+ if( hFindFile!=NULL ){
|
|
+ pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
|
|
+ pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
|
|
+ pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
|
|
+ FindClose(hFindFile);
|
|
+ }
|
|
+ sqlite3_free(zUnicodeName);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** This function is used in place of stat(). On Windows, special handling
|
|
+** is required in order for the included time to be returned as UTC. On all
|
|
+** other systems, this function simply calls stat().
|
|
+*/
|
|
+static int fileStat(
|
|
+ const char *zPath,
|
|
+ struct stat *pStatBuf
|
|
+){
|
|
+#if defined(_WIN32)
|
|
+ int rc = stat(zPath, pStatBuf);
|
|
+ if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
|
+ return rc;
|
|
+#else
|
|
+ return stat(zPath, pStatBuf);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is used in place of lstat(). On Windows, special handling
|
|
+** is required in order for the included time to be returned as UTC. On all
|
|
+** other systems, this function simply calls lstat().
|
|
+*/
|
|
+static int fileLinkStat(
|
|
+ const char *zPath,
|
|
+ struct stat *pStatBuf
|
|
+){
|
|
+#if defined(_WIN32)
|
|
+ int rc = lstat(zPath, pStatBuf);
|
|
+ if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
|
+ return rc;
|
|
+#else
|
|
+ return lstat(zPath, pStatBuf);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** Argument zFile is the name of a file that will be created and/or written
|
|
+** by SQL function writefile(). This function ensures that the directory
|
|
+** zFile will be written to exists, creating it if required. The permissions
|
|
+** for any path components created by this function are set in accordance
|
|
+** with the current umask.
|
|
+**
|
|
+** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
|
|
+** SQLITE_OK is returned if the directory is successfully created, or
|
|
+** SQLITE_ERROR otherwise.
|
|
+*/
|
|
+static int makeDirectory(
|
|
+ const char *zFile
|
|
+){
|
|
+ char *zCopy = sqlite3_mprintf("%s", zFile);
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( zCopy==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int nCopy = (int)strlen(zCopy);
|
|
+ int i = 1;
|
|
+
|
|
+ while( rc==SQLITE_OK ){
|
|
+ struct stat sStat;
|
|
+ int rc2;
|
|
+
|
|
+ for(; zCopy[i]!='/' && i<nCopy; i++);
|
|
+ if( i==nCopy ) break;
|
|
+ zCopy[i] = '\0';
|
|
+
|
|
+ rc2 = fileStat(zCopy, &sStat);
|
|
+ if( rc2!=0 ){
|
|
+ if( mkdir(zCopy, 0777) ) rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
|
|
+ }
|
|
+ zCopy[i] = '/';
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zCopy);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function does the work for the writefile() UDF. Refer to
|
|
+** header comments at the top of this file for details.
|
|
+*/
|
|
+static int writeFile(
|
|
+ sqlite3_context *pCtx, /* Context to return bytes written in */
|
|
+ const char *zFile, /* File to write */
|
|
+ sqlite3_value *pData, /* Data to write */
|
|
+ mode_t mode, /* MODE parameter passed to writefile() */
|
|
+ sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
|
|
+){
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+ if( S_ISLNK(mode) ){
|
|
+ const char *zTo = (const char*)sqlite3_value_text(pData);
|
|
+ if( symlink(zTo, zFile)<0 ) return 1;
|
|
+ }else
|
|
+#endif
|
|
+ {
|
|
+ if( S_ISDIR(mode) ){
|
|
+ if( mkdir(zFile, mode) ){
|
|
+ /* The mkdir() call to create the directory failed. This might not
|
|
+ ** be an error though - if there is already a directory at the same
|
|
+ ** path and either the permissions already match or can be changed
|
|
+ ** to do so using chmod(), it is not an error. */
|
|
+ struct stat sStat;
|
|
+ if( errno!=EEXIST
|
|
+ || 0!=fileStat(zFile, &sStat)
|
|
+ || !S_ISDIR(sStat.st_mode)
|
|
+ || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
|
|
+ ){
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_int64 nWrite = 0;
|
|
+ const char *z;
|
|
+ int rc = 0;
|
|
+ FILE *out = 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;
|
|
+ }
|
|
+ }
|
|
+ fclose(out);
|
|
+ if( rc==0 && mode && chmod(zFile, mode & 0777) ){
|
|
+ rc = 1;
|
|
+ }
|
|
+ if( rc ) return 2;
|
|
+ sqlite3_result_int64(pCtx, nWrite);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( mtime>=0 ){
|
|
+#if defined(_WIN32)
|
|
+ /* Windows */
|
|
+ FILETIME lastAccess;
|
|
+ FILETIME lastWrite;
|
|
+ SYSTEMTIME currentTime;
|
|
+ LONGLONG intervals;
|
|
+ HANDLE hFile;
|
|
+ LPWSTR zUnicodeName;
|
|
+ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
|
|
+
|
|
+ GetSystemTime(¤tTime);
|
|
+ SystemTimeToFileTime(¤tTime, &lastAccess);
|
|
+ intervals = Int32x32To64(mtime, 10000000) + 116444736000000000;
|
|
+ lastWrite.dwLowDateTime = (DWORD)intervals;
|
|
+ lastWrite.dwHighDateTime = intervals >> 32;
|
|
+ zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
|
|
+ if( zUnicodeName==0 ){
|
|
+ return 1;
|
|
+ }
|
|
+ hFile = CreateFileW(
|
|
+ zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
|
|
+ FILE_FLAG_BACKUP_SEMANTICS, NULL
|
|
+ );
|
|
+ sqlite3_free(zUnicodeName);
|
|
+ if( hFile!=INVALID_HANDLE_VALUE ){
|
|
+ BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite);
|
|
+ CloseHandle(hFile);
|
|
+ return !bResult;
|
|
+ }else{
|
|
+ return 1;
|
|
+ }
|
|
+#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
|
|
+ /* Recent unix */
|
|
+ struct timespec times[2];
|
|
+ times[0].tv_nsec = times[1].tv_nsec = 0;
|
|
+ times[0].tv_sec = time(0);
|
|
+ times[1].tv_sec = mtime;
|
|
+ if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
|
|
+ return 1;
|
|
+ }
|
|
+#else
|
|
+ /* Legacy unix */
|
|
+ struct timeval times[2];
|
|
+ times[0].tv_usec = times[1].tv_usec = 0;
|
|
+ times[0].tv_sec = time(0);
|
|
+ times[1].tv_sec = mtime;
|
|
+ if( utimes(zFile, times) ){
|
|
+ return 1;
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the "writefile(W,X[,Y[,Z]]])" SQL function.
|
|
+** Refer to header comments at the top of this file for details.
|
|
+*/
|
|
+static void writefileFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *zFile;
|
|
+ mode_t mode = 0;
|
|
+ int res;
|
|
+ sqlite3_int64 mtime = -1;
|
|
+
|
|
+ if( argc<2 || argc>4 ){
|
|
+ sqlite3_result_error(context,
|
|
+ "wrong number of arguments to function writefile()", -1
|
|
+ );
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ zFile = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zFile==0 ) return;
|
|
+ if( argc>=3 ){
|
|
+ mode = (mode_t)sqlite3_value_int(argv[2]);
|
|
+ }
|
|
+ if( argc==4 ){
|
|
+ mtime = sqlite3_value_int64(argv[3]);
|
|
+ }
|
|
+
|
|
+ res = writeFile(context, zFile, argv[1], mode, mtime);
|
|
+ if( res==1 && errno==ENOENT ){
|
|
+ if( makeDirectory(zFile)==SQLITE_OK ){
|
|
+ res = writeFile(context, zFile, argv[1], mode, mtime);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( argc>2 && res!=0 ){
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+ if( S_ISLNK(mode) ){
|
|
+ ctxErrorMsg(context, "failed to create symlink: %s", zFile);
|
|
+ }else
|
|
+#endif
|
|
+ if( S_ISDIR(mode) ){
|
|
+ ctxErrorMsg(context, "failed to create directory: %s", zFile);
|
|
+ }else{
|
|
+ ctxErrorMsg(context, "failed to write file: %s", zFile);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: lsmode(MODE)
|
|
+**
|
|
+** Given a numberic st_mode from stat(), convert it into a human-readable
|
|
+** text string in the style of "ls -l".
|
|
+*/
|
|
+static void lsModeFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ int iMode = sqlite3_value_int(argv[0]);
|
|
+ char z[16];
|
|
+ (void)argc;
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+ if( S_ISLNK(iMode) ){
|
|
+ z[0] = 'l';
|
|
+ }else
|
|
+#endif
|
|
+ if( S_ISREG(iMode) ){
|
|
+ z[0] = '-';
|
|
+ }else if( S_ISDIR(iMode) ){
|
|
+ z[0] = 'd';
|
|
+ }else{
|
|
+ z[0] = '?';
|
|
+ }
|
|
+ for(i=0; i<3; i++){
|
|
+ int m = (iMode >> ((2-i)*3));
|
|
+ char *a = &z[1 + i*3];
|
|
+ a[0] = (m & 0x4) ? 'r' : '-';
|
|
+ a[1] = (m & 0x2) ? 'w' : '-';
|
|
+ a[2] = (m & 0x1) ? 'x' : '-';
|
|
+ }
|
|
+ z[10] = '\0';
|
|
+ sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
|
|
+}
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Cursor type for recursively iterating through a directory structure.
|
|
+*/
|
|
+typedef struct fsdir_cursor fsdir_cursor;
|
|
+typedef struct FsdirLevel FsdirLevel;
|
|
+
|
|
+struct FsdirLevel {
|
|
+ DIR *pDir; /* From opendir() */
|
|
+ char *zDir; /* Name of directory (nul-terminated) */
|
|
+};
|
|
+
|
|
+struct fsdir_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+
|
|
+ int nLvl; /* Number of entries in aLvl[] array */
|
|
+ int iLvl; /* Index of current entry */
|
|
+ FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
|
|
+
|
|
+ const char *zBase;
|
|
+ int nBase;
|
|
+
|
|
+ struct stat sStat; /* Current lstat() results */
|
|
+ char *zPath; /* Path to current entry */
|
|
+ sqlite3_int64 iRowid; /* Current rowid */
|
|
+};
|
|
+
|
|
+typedef struct fsdir_tab fsdir_tab;
|
|
+struct fsdir_tab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Construct a new fsdir virtual table object.
|
|
+*/
|
|
+static int fsdirConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ fsdir_tab *pNew = 0;
|
|
+ int rc;
|
|
+ (void)pAux;
|
|
+ (void)argc;
|
|
+ (void)argv;
|
|
+ (void)pzErr;
|
|
+ rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for fsdir vtab objects.
|
|
+*/
|
|
+static int fsdirDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new fsdir_cursor object.
|
|
+*/
|
|
+static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ fsdir_cursor *pCur;
|
|
+ (void)p;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->iLvl = -1;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset a cursor back to the state it was in when first returned
|
|
+** by fsdirOpen().
|
|
+*/
|
|
+static void fsdirResetCursor(fsdir_cursor *pCur){
|
|
+ int i;
|
|
+ for(i=0; i<=pCur->iLvl; i++){
|
|
+ FsdirLevel *pLvl = &pCur->aLvl[i];
|
|
+ if( pLvl->pDir ) closedir(pLvl->pDir);
|
|
+ sqlite3_free(pLvl->zDir);
|
|
+ }
|
|
+ sqlite3_free(pCur->zPath);
|
|
+ sqlite3_free(pCur->aLvl);
|
|
+ pCur->aLvl = 0;
|
|
+ pCur->zPath = 0;
|
|
+ pCur->zBase = 0;
|
|
+ pCur->nBase = 0;
|
|
+ pCur->nLvl = 0;
|
|
+ pCur->iLvl = -1;
|
|
+ pCur->iRowid = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for an fsdir_cursor.
|
|
+*/
|
|
+static int fsdirClose(sqlite3_vtab_cursor *cur){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+
|
|
+ fsdirResetCursor(pCur);
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set the error message for the virtual table associated with cursor
|
|
+** pCur to the results of vprintf(zFmt, ...).
|
|
+*/
|
|
+static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance an fsdir_cursor to its next row of output.
|
|
+*/
|
|
+static int fsdirNext(sqlite3_vtab_cursor *cur){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ mode_t m = pCur->sStat.st_mode;
|
|
+
|
|
+ pCur->iRowid++;
|
|
+ if( S_ISDIR(m) ){
|
|
+ /* Descend into this directory */
|
|
+ int iNew = pCur->iLvl + 1;
|
|
+ FsdirLevel *pLvl;
|
|
+ if( iNew>=pCur->nLvl ){
|
|
+ int nNew = iNew+1;
|
|
+ size_t nByte = nNew*sizeof(FsdirLevel);
|
|
+ FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc(pCur->aLvl, nByte);
|
|
+ if( aNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl));
|
|
+ pCur->aLvl = aNew;
|
|
+ pCur->nLvl = nNew;
|
|
+ }
|
|
+ pCur->iLvl = iNew;
|
|
+ pLvl = &pCur->aLvl[iNew];
|
|
+
|
|
+ pLvl->zDir = pCur->zPath;
|
|
+ pCur->zPath = 0;
|
|
+ pLvl->pDir = opendir(pLvl->zDir);
|
|
+ if( pLvl->pDir==0 ){
|
|
+ fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while( pCur->iLvl>=0 ){
|
|
+ FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl];
|
|
+ struct dirent *pEntry = readdir(pLvl->pDir);
|
|
+ if( pEntry ){
|
|
+ if( pEntry->d_name[0]=='.' ){
|
|
+ if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue;
|
|
+ if( pEntry->d_name[1]=='\0' ) continue;
|
|
+ }
|
|
+ sqlite3_free(pCur->zPath);
|
|
+ pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name);
|
|
+ if( pCur->zPath==0 ) return SQLITE_NOMEM;
|
|
+ if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
|
|
+ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ closedir(pLvl->pDir);
|
|
+ sqlite3_free(pLvl->zDir);
|
|
+ pLvl->pDir = 0;
|
|
+ pLvl->zDir = 0;
|
|
+ pCur->iLvl--;
|
|
+ }
|
|
+
|
|
+ /* EOF */
|
|
+ sqlite3_free(pCur->zPath);
|
|
+ pCur->zPath = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the series_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int fsdirColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case FSDIR_COLUMN_NAME: {
|
|
+ sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case FSDIR_COLUMN_MODE:
|
|
+ sqlite3_result_int64(ctx, pCur->sStat.st_mode);
|
|
+ break;
|
|
+
|
|
+ case FSDIR_COLUMN_MTIME:
|
|
+ sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
|
|
+ break;
|
|
+
|
|
+ case FSDIR_COLUMN_DATA: {
|
|
+ mode_t m = pCur->sStat.st_mode;
|
|
+ if( S_ISDIR(m) ){
|
|
+ sqlite3_result_null(ctx);
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+ }else if( S_ISLNK(m) ){
|
|
+ char aStatic[64];
|
|
+ char *aBuf = aStatic;
|
|
+ size_t nBuf = 64;
|
|
+ size_t n;
|
|
+
|
|
+ while( 1 ){
|
|
+ n = readlink(pCur->zPath, aBuf, nBuf);
|
|
+ if( n<nBuf ) break;
|
|
+ if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
|
+ nBuf = nBuf*2;
|
|
+ aBuf = sqlite3_malloc(nBuf);
|
|
+ if( aBuf==0 ){
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
|
|
+ if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
|
+#endif
|
|
+ }else{
|
|
+ readFileContents(ctx, pCur->zPath);
|
|
+ }
|
|
+ }
|
|
+ case FSDIR_COLUMN_PATH:
|
|
+ default: {
|
|
+ /* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
|
|
+ ** always return their values as NULL */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** first row returned is assigned rowid value 1, and each subsequent
|
|
+** row a value 1 more than that of the previous.
|
|
+*/
|
|
+static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int fsdirEof(sqlite3_vtab_cursor *cur){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ return (pCur->zPath==0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFilter callback.
|
|
+**
|
|
+** idxNum==1 PATH parameter only
|
|
+** idxNum==2 Both PATH and DIR supplied
|
|
+*/
|
|
+static int fsdirFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ const char *zDir = 0;
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ (void)idxStr;
|
|
+ fsdirResetCursor(pCur);
|
|
+
|
|
+ if( idxNum==0 ){
|
|
+ fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ assert( argc==idxNum && (argc==1 || argc==2) );
|
|
+ zDir = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zDir==0 ){
|
|
+ fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ if( argc==2 ){
|
|
+ pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
|
|
+ }
|
|
+ if( pCur->zBase ){
|
|
+ pCur->nBase = (int)strlen(pCur->zBase)+1;
|
|
+ pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir);
|
|
+ }else{
|
|
+ pCur->zPath = sqlite3_mprintf("%s", zDir);
|
|
+ }
|
|
+
|
|
+ if( pCur->zPath==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
|
|
+ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the generate_series virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+**
|
|
+** In this implementation idxNum is used to represent the
|
|
+** query plan. idxStr is unused.
|
|
+**
|
|
+** The query plan is represented by values of idxNum:
|
|
+**
|
|
+** (1) The path value is supplied by argv[0]
|
|
+** (2) Path is in argv[0] and dir is in argv[1]
|
|
+*/
|
|
+static int fsdirBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop over constraints */
|
|
+ int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
|
|
+ int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
|
|
+ int seenPath = 0; /* True if an unusable PATH= constraint is seen */
|
|
+ int seenDir = 0; /* True if an unusable DIR= constraint is seen */
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ (void)tab;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ switch( pConstraint->iColumn ){
|
|
+ case FSDIR_COLUMN_PATH: {
|
|
+ if( pConstraint->usable ){
|
|
+ idxPath = i;
|
|
+ seenPath = 0;
|
|
+ }else if( idxPath<0 ){
|
|
+ seenPath = 1;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case FSDIR_COLUMN_DIR: {
|
|
+ if( pConstraint->usable ){
|
|
+ idxDir = i;
|
|
+ seenDir = 0;
|
|
+ }else if( idxDir<0 ){
|
|
+ seenDir = 1;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( seenPath || seenDir ){
|
|
+ /* If input parameters are unusable, disallow this plan */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+
|
|
+ if( idxPath<0 ){
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ /* The pIdxInfo->estimatedCost should have been initialized to a huge
|
|
+ ** number. Leave it unchanged. */
|
|
+ pIdxInfo->estimatedRows = 0x7fffffff;
|
|
+ }else{
|
|
+ pIdxInfo->aConstraintUsage[idxPath].omit = 1;
|
|
+ pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
|
|
+ if( idxDir>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[idxDir].omit = 1;
|
|
+ pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
|
|
+ pIdxInfo->idxNum = 2;
|
|
+ pIdxInfo->estimatedCost = 10.0;
|
|
+ }else{
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ pIdxInfo->estimatedCost = 100.0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register the "fsdir" virtual table.
|
|
+*/
|
|
+static int fsdirRegister(sqlite3 *db){
|
|
+ static const sqlite3_module fsdirModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ fsdirConnect, /* xConnect */
|
|
+ fsdirBestIndex, /* xBestIndex */
|
|
+ fsdirDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ fsdirOpen, /* xOpen - open a cursor */
|
|
+ fsdirClose, /* xClose - close a cursor */
|
|
+ fsdirFilter, /* xFilter - configure scan constraints */
|
|
+ fsdirNext, /* xNext - advance a cursor */
|
|
+ fsdirEof, /* xEof - check for end of scan */
|
|
+ fsdirColumn, /* xColumn - read data */
|
|
+ fsdirRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+ };
|
|
+
|
|
+ int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
|
|
+ return rc;
|
|
+}
|
|
+#else /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+# define fsdirRegister(x) SQLITE_OK
|
|
+#endif
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_fileio_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
|
+ readfileFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
|
|
+ writefileFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0,
|
|
+ lsModeFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = fsdirRegister(db);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
|
+ readfileFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
|
+ writefileFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/fossildelta.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/fossildelta.c 2019-05-14 16:22:31.238090700 +0200
|
|
@@ -0,0 +1,1111 @@
|
|
+/*
|
|
+** 2019-02-19
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This SQLite extension implements the delta functions used by the RBU
|
|
+** extension. Three scalar functions and one table-valued function are
|
|
+** implemented here:
|
|
+**
|
|
+** delta_apply(X,D) -- apply delta D to file X and return the result
|
|
+** delta_create(X,Y) -- compute and return a delta that carries X into Y
|
|
+** delta_output_size(D) -- blob size in bytes output from applying delta D
|
|
+** delta_parse(D) -- returns rows describing delta D
|
|
+**
|
|
+** The delta format is the Fossil delta format, described in a comment
|
|
+** on the delete_create() function implementation below, and also at
|
|
+**
|
|
+** https://www.fossil-scm.org/fossil/doc/trunk/www/delta_format.wiki
|
|
+**
|
|
+** This delta format is used by the RBU extension, which is the main
|
|
+** reason that these routines are included in the extension library.
|
|
+** RBU does not use this extension directly. Rather, this extension is
|
|
+** provided as a convenience to developers who want to analyze RBU files
|
|
+** that contain deltas.
|
|
+*/
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include <stdlib.h>
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+
|
|
+/*
|
|
+** The "u32" type must be an unsigned 32-bit integer. Adjust this
|
|
+*/
|
|
+typedef unsigned int u32;
|
|
+
|
|
+/*
|
|
+** Must be a 16-bit value
|
|
+*/
|
|
+typedef short int s16;
|
|
+typedef unsigned short int u16;
|
|
+
|
|
+
|
|
+/*
|
|
+** The width of a hash window in bytes. The algorithm only works if this
|
|
+** is a power of 2.
|
|
+*/
|
|
+#define NHASH 16
|
|
+
|
|
+/*
|
|
+** The current state of the rolling hash.
|
|
+**
|
|
+** z[] holds the values that have been hashed. z[] is a circular buffer.
|
|
+** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of
|
|
+** the window.
|
|
+**
|
|
+** Hash.a is the sum of all elements of hash.z[]. Hash.b is a weighted
|
|
+** sum. Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1.
|
|
+** (Each index for z[] should be module NHASH, of course. The %NHASH operator
|
|
+** is omitted in the prior expression for brevity.)
|
|
+*/
|
|
+typedef struct hash hash;
|
|
+struct hash {
|
|
+ u16 a, b; /* Hash values */
|
|
+ u16 i; /* Start of the hash window */
|
|
+ char z[NHASH]; /* The values that have been hashed */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Initialize the rolling hash using the first NHASH characters of z[]
|
|
+*/
|
|
+static void hash_init(hash *pHash, const char *z){
|
|
+ u16 a, b, i;
|
|
+ a = b = z[0];
|
|
+ for(i=1; i<NHASH; i++){
|
|
+ a += z[i];
|
|
+ b += a;
|
|
+ }
|
|
+ memcpy(pHash->z, z, NHASH);
|
|
+ pHash->a = a & 0xffff;
|
|
+ pHash->b = b & 0xffff;
|
|
+ pHash->i = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance the rolling hash by a single character "c"
|
|
+*/
|
|
+static void hash_next(hash *pHash, int c){
|
|
+ u16 old = pHash->z[pHash->i];
|
|
+ pHash->z[pHash->i] = c;
|
|
+ pHash->i = (pHash->i+1)&(NHASH-1);
|
|
+ pHash->a = pHash->a - old + c;
|
|
+ pHash->b = pHash->b - NHASH*old + pHash->a;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a 32-bit hash value
|
|
+*/
|
|
+static u32 hash_32bit(hash *pHash){
|
|
+ return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute a hash on NHASH bytes.
|
|
+**
|
|
+** This routine is intended to be equivalent to:
|
|
+** hash h;
|
|
+** hash_init(&h, zInput);
|
|
+** return hash_32bit(&h);
|
|
+*/
|
|
+static u32 hash_once(const char *z){
|
|
+ u16 a, b, i;
|
|
+ a = b = z[0];
|
|
+ for(i=1; i<NHASH; i++){
|
|
+ a += z[i];
|
|
+ b += a;
|
|
+ }
|
|
+ return a | (((u32)b)<<16);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write an base-64 integer into the given buffer.
|
|
+*/
|
|
+static void putInt(unsigned int v, char **pz){
|
|
+ static const char zDigits[] =
|
|
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
|
|
+ /* 123456789 123456789 123456789 123456789 123456789 123456789 123 */
|
|
+ int i, j;
|
|
+ char zBuf[20];
|
|
+ if( v==0 ){
|
|
+ *(*pz)++ = '0';
|
|
+ return;
|
|
+ }
|
|
+ for(i=0; v>0; i++, v>>=6){
|
|
+ zBuf[i] = zDigits[v&0x3f];
|
|
+ }
|
|
+ for(j=i-1; j>=0; j--){
|
|
+ *(*pz)++ = zBuf[j];
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read bytes from *pz and convert them into a positive integer. When
|
|
+** finished, leave *pz pointing to the first character past the end of
|
|
+** the integer. The *pLen parameter holds the length of the string
|
|
+** in *pz and is decremented once for each character in the integer.
|
|
+*/
|
|
+static unsigned int deltaGetInt(const char **pz, int *pLen){
|
|
+ static const signed char zValue[] = {
|
|
+ -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,
|
|
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
|
|
+ -1, 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, -1, -1, -1, -1, 36,
|
|
+ -1, 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, -1, -1, -1, 63, -1,
|
|
+ };
|
|
+ unsigned int v = 0;
|
|
+ int c;
|
|
+ unsigned char *z = (unsigned char*)*pz;
|
|
+ unsigned char *zStart = z;
|
|
+ while( (c = zValue[0x7f&*(z++)])>=0 ){
|
|
+ v = (v<<6) + c;
|
|
+ }
|
|
+ z--;
|
|
+ *pLen -= z - zStart;
|
|
+ *pz = (char*)z;
|
|
+ return v;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the number digits in the base-64 representation of a positive integer
|
|
+*/
|
|
+static int digit_count(int v){
|
|
+ unsigned int i, x;
|
|
+ for(i=1, x=64; v>=x; i++, x <<= 6){}
|
|
+ return i;
|
|
+}
|
|
+
|
|
+#ifdef __GNUC__
|
|
+# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
|
|
+#else
|
|
+# define GCC_VERSION 0
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Compute a 32-bit big-endian checksum on the N-byte buffer. If the
|
|
+** 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;
|
|
+ const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3];
|
|
+ unsigned sum = 0;
|
|
+ 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.
|
|
+**
|
|
+** The delta is written into a preallocated buffer, zDelta, which
|
|
+** should be at least 60 bytes longer than the target file, zOut.
|
|
+** The delta string will be NUL-terminated, but it might also contain
|
|
+** embedded NUL characters if either the zSrc or zOut files are
|
|
+** binary. This function returns the length of the delta string
|
|
+** in bytes, excluding the final NUL terminator character.
|
|
+**
|
|
+** Output Format:
|
|
+**
|
|
+** The delta begins with a base64 number followed by a newline. This
|
|
+** number is the number of bytes in the TARGET file. Thus, given a
|
|
+** delta file z, a program can compute the size of the output file
|
|
+** simply by reading the first line and decoding the base-64 number
|
|
+** found there. The delta_output_size() routine does exactly this.
|
|
+**
|
|
+** After the initial size number, the delta consists of a series of
|
|
+** literal text segments and commands to copy from the SOURCE file.
|
|
+** A copy command looks like this:
|
|
+**
|
|
+** NNN@MMM,
|
|
+**
|
|
+** where NNN is the number of bytes to be copied and MMM is the offset
|
|
+** into the source file of the first byte (both base-64). If NNN is 0
|
|
+** it means copy the rest of the input file. Literal text is like this:
|
|
+**
|
|
+** NNN:TTTTT
|
|
+**
|
|
+** where NNN is the number of bytes of text (base-64) and TTTTT is the text.
|
|
+**
|
|
+** The last term is of the form
|
|
+**
|
|
+** NNN;
|
|
+**
|
|
+** In this case, NNN is a 32-bit bigendian checksum of the output file
|
|
+** that can be used to verify that the delta applied correctly. All
|
|
+** numbers are in base-64.
|
|
+**
|
|
+** Pure text files generate a pure text delta. Binary files generate a
|
|
+** delta that may contain some binary data.
|
|
+**
|
|
+** Algorithm:
|
|
+**
|
|
+** The encoder first builds a hash table to help it find matching
|
|
+** patterns in the source file. 16-byte chunks of the source file
|
|
+** sampled at evenly spaced intervals are used to populate the hash
|
|
+** table.
|
|
+**
|
|
+** Next we begin scanning the target file using a sliding 16-byte
|
|
+** window. The hash of the 16-byte window in the target is used to
|
|
+** search for a matching section in the source file. When a match
|
|
+** is found, a copy command is added to the delta. An effort is
|
|
+** made to extend the matching section to regions that come before
|
|
+** and after the 16-byte hash window. A copy command is only issued
|
|
+** if the result would use less space that just quoting the text
|
|
+** literally. Literal text is added to the delta for sections that
|
|
+** do not match or which can not be encoded efficiently using copy
|
|
+** commands.
|
|
+*/
|
|
+static int delta_create(
|
|
+ const char *zSrc, /* The source or pattern file */
|
|
+ unsigned int lenSrc, /* Length of the source file */
|
|
+ const char *zOut, /* The target file */
|
|
+ unsigned int lenOut, /* Length of the target file */
|
|
+ char *zDelta /* Write the delta into this buffer */
|
|
+){
|
|
+ int i, base;
|
|
+ char *zOrigDelta = zDelta;
|
|
+ hash h;
|
|
+ int nHash; /* Number of hash table entries */
|
|
+ int *landmark; /* Primary hash table */
|
|
+ int *collide; /* Collision chain */
|
|
+ int lastRead = -1; /* Last byte of zSrc read by a COPY command */
|
|
+
|
|
+ /* Add the target file size to the beginning of the delta
|
|
+ */
|
|
+ putInt(lenOut, &zDelta);
|
|
+ *(zDelta++) = '\n';
|
|
+
|
|
+ /* If the source file is very small, it means that we have no
|
|
+ ** chance of ever doing a copy command. Just output a single
|
|
+ ** literal segment for the entire target and exit.
|
|
+ */
|
|
+ if( lenSrc<=NHASH ){
|
|
+ putInt(lenOut, &zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, zOut, lenOut);
|
|
+ zDelta += lenOut;
|
|
+ putInt(checksum(zOut, lenOut), &zDelta);
|
|
+ *(zDelta++) = ';';
|
|
+ return zDelta - zOrigDelta;
|
|
+ }
|
|
+
|
|
+ /* Compute the hash table used to locate matching sections in the
|
|
+ ** source file.
|
|
+ */
|
|
+ nHash = lenSrc/NHASH;
|
|
+ collide = sqlite3_malloc64( (sqlite3_int64)nHash*2*sizeof(int) );
|
|
+ memset(collide, -1, nHash*2*sizeof(int));
|
|
+ landmark = &collide[nHash];
|
|
+ for(i=0; i<lenSrc-NHASH; i+=NHASH){
|
|
+ int hv = hash_once(&zSrc[i]) % nHash;
|
|
+ collide[i/NHASH] = landmark[hv];
|
|
+ landmark[hv] = i/NHASH;
|
|
+ }
|
|
+
|
|
+ /* Begin scanning the target file and generating copy commands and
|
|
+ ** literal sections of the delta.
|
|
+ */
|
|
+ base = 0; /* We have already generated everything before zOut[base] */
|
|
+ while( base+NHASH<lenOut ){
|
|
+ int iSrc, iBlock;
|
|
+ unsigned int bestCnt, bestOfst=0, bestLitsz=0;
|
|
+ hash_init(&h, &zOut[base]);
|
|
+ i = 0; /* Trying to match a landmark against zOut[base+i] */
|
|
+ bestCnt = 0;
|
|
+ while( 1 ){
|
|
+ int hv;
|
|
+ int limit = 250;
|
|
+
|
|
+ hv = hash_32bit(&h) % nHash;
|
|
+ iBlock = landmark[hv];
|
|
+ while( iBlock>=0 && (limit--)>0 ){
|
|
+ /*
|
|
+ ** The hash window has identified a potential match against
|
|
+ ** landmark block iBlock. But we need to investigate further.
|
|
+ **
|
|
+ ** Look for a region in zOut that matches zSrc. Anchor the search
|
|
+ ** at zSrc[iSrc] and zOut[base+i]. Do not include anything prior to
|
|
+ ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen].
|
|
+ **
|
|
+ ** Set cnt equal to the length of the match and set ofst so that
|
|
+ ** zSrc[ofst] is the first element of the match. litsz is the number
|
|
+ ** of characters between zOut[base] and the beginning of the match.
|
|
+ ** sz will be the overhead (in bytes) needed to encode the copy
|
|
+ ** command. Only generate copy command if the overhead of the
|
|
+ ** copy command is less than the amount of literal text to be copied.
|
|
+ */
|
|
+ int cnt, ofst, litsz;
|
|
+ int j, k, x, y;
|
|
+ int sz;
|
|
+ int limitX;
|
|
+
|
|
+ /* Beginning at iSrc, match forwards as far as we can. j counts
|
|
+ ** the number of characters that match */
|
|
+ iSrc = iBlock*NHASH;
|
|
+ y = base+i;
|
|
+ limitX = ( lenSrc-iSrc <= lenOut-y ) ? lenSrc : iSrc + lenOut - y;
|
|
+ for(x=iSrc; x<limitX; x++, y++){
|
|
+ if( zSrc[x]!=zOut[y] ) break;
|
|
+ }
|
|
+ j = x - iSrc - 1;
|
|
+
|
|
+ /* Beginning at iSrc-1, match backwards as far as we can. k counts
|
|
+ ** the number of characters that match */
|
|
+ for(k=1; k<iSrc && k<=i; k++){
|
|
+ if( zSrc[iSrc-k]!=zOut[base+i-k] ) break;
|
|
+ }
|
|
+ k--;
|
|
+
|
|
+ /* Compute the offset and size of the matching region */
|
|
+ ofst = iSrc-k;
|
|
+ cnt = j+k+1;
|
|
+ litsz = i-k; /* Number of bytes of literal text before the copy */
|
|
+ /* sz will hold the number of bytes needed to encode the "insert"
|
|
+ ** command and the copy command, not counting the "insert" text */
|
|
+ sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3;
|
|
+ if( cnt>=sz && cnt>bestCnt ){
|
|
+ /* Remember this match only if it is the best so far and it
|
|
+ ** does not increase the file size */
|
|
+ bestCnt = cnt;
|
|
+ bestOfst = iSrc-k;
|
|
+ bestLitsz = litsz;
|
|
+ }
|
|
+
|
|
+ /* Check the next matching block */
|
|
+ iBlock = collide[iBlock];
|
|
+ }
|
|
+
|
|
+ /* We have a copy command that does not cause the delta to be larger
|
|
+ ** than a literal insert. So add the copy command to the delta.
|
|
+ */
|
|
+ if( bestCnt>0 ){
|
|
+ if( bestLitsz>0 ){
|
|
+ /* Add an insert command before the copy */
|
|
+ putInt(bestLitsz,&zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, &zOut[base], bestLitsz);
|
|
+ zDelta += bestLitsz;
|
|
+ base += bestLitsz;
|
|
+ }
|
|
+ base += bestCnt;
|
|
+ putInt(bestCnt, &zDelta);
|
|
+ *(zDelta++) = '@';
|
|
+ putInt(bestOfst, &zDelta);
|
|
+ *(zDelta++) = ',';
|
|
+ if( bestOfst + bestCnt -1 > lastRead ){
|
|
+ lastRead = bestOfst + bestCnt - 1;
|
|
+ }
|
|
+ bestCnt = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* If we reach this point, it means no match is found so far */
|
|
+ if( base+i+NHASH>=lenOut ){
|
|
+ /* We have reached the end of the file and have not found any
|
|
+ ** matches. Do an "insert" for everything that does not match */
|
|
+ putInt(lenOut-base, &zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, &zOut[base], lenOut-base);
|
|
+ zDelta += lenOut-base;
|
|
+ base = lenOut;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Advance the hash by one character. Keep looking for a match */
|
|
+ hash_next(&h, zOut[base+i+NHASH]);
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ /* Output a final "insert" record to get all the text at the end of
|
|
+ ** the file that does not match anything in the source file.
|
|
+ */
|
|
+ if( base<lenOut ){
|
|
+ putInt(lenOut-base, &zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, &zOut[base], lenOut-base);
|
|
+ zDelta += lenOut-base;
|
|
+ }
|
|
+ /* Output the final checksum record. */
|
|
+ putInt(checksum(zOut, lenOut), &zDelta);
|
|
+ *(zDelta++) = ';';
|
|
+ sqlite3_free(collide);
|
|
+ return zDelta - zOrigDelta;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the size (in bytes) of the output from applying
|
|
+** a delta.
|
|
+**
|
|
+** This routine is provided so that an procedure that is able
|
|
+** to call delta_apply() can learn how much space is required
|
|
+** for the output and hence allocate nor more space that is really
|
|
+** needed.
|
|
+*/
|
|
+static int delta_output_size(const char *zDelta, int lenDelta){
|
|
+ int size;
|
|
+ size = deltaGetInt(&zDelta, &lenDelta);
|
|
+ if( *zDelta!='\n' ){
|
|
+ /* ERROR: size integer not terminated by "\n" */
|
|
+ return -1;
|
|
+ }
|
|
+ return size;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Apply a delta.
|
|
+**
|
|
+** The output buffer should be big enough to hold the whole output
|
|
+** file and a NUL terminator at the end. The delta_output_size()
|
|
+** routine will determine this size for you.
|
|
+**
|
|
+** The delta string should be null-terminated. But the delta string
|
|
+** may contain embedded NUL characters (if the input and output are
|
|
+** binary files) so we also have to pass in the length of the delta in
|
|
+** the lenDelta parameter.
|
|
+**
|
|
+** This function returns the size of the output file in bytes (excluding
|
|
+** the final NUL terminator character). Except, if the delta string is
|
|
+** malformed or intended for use with a source file other than zSrc,
|
|
+** then this routine returns -1.
|
|
+**
|
|
+** Refer to the delta_create() documentation above for a description
|
|
+** of the delta file format.
|
|
+*/
|
|
+static int delta_apply(
|
|
+ const char *zSrc, /* The source or pattern file */
|
|
+ int lenSrc, /* Length of the source file */
|
|
+ const char *zDelta, /* Delta to apply to the pattern */
|
|
+ int lenDelta, /* Length of the delta */
|
|
+ char *zOut /* Write the output into this preallocated buffer */
|
|
+){
|
|
+ unsigned int limit;
|
|
+ unsigned int total = 0;
|
|
+#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
|
|
+ char *zOrigOut = zOut;
|
|
+#endif
|
|
+
|
|
+ limit = deltaGetInt(&zDelta, &lenDelta);
|
|
+ if( *zDelta!='\n' ){
|
|
+ /* ERROR: size integer not terminated by "\n" */
|
|
+ return -1;
|
|
+ }
|
|
+ zDelta++; lenDelta--;
|
|
+ while( *zDelta && lenDelta>0 ){
|
|
+ unsigned int cnt, ofst;
|
|
+ cnt = deltaGetInt(&zDelta, &lenDelta);
|
|
+ switch( zDelta[0] ){
|
|
+ case '@': {
|
|
+ zDelta++; lenDelta--;
|
|
+ ofst = deltaGetInt(&zDelta, &lenDelta);
|
|
+ if( lenDelta>0 && zDelta[0]!=',' ){
|
|
+ /* ERROR: copy command not terminated by ',' */
|
|
+ return -1;
|
|
+ }
|
|
+ zDelta++; lenDelta--;
|
|
+ total += cnt;
|
|
+ if( total>limit ){
|
|
+ /* ERROR: copy exceeds output file size */
|
|
+ return -1;
|
|
+ }
|
|
+ if( ofst+cnt > lenSrc ){
|
|
+ /* ERROR: copy extends past end of input */
|
|
+ return -1;
|
|
+ }
|
|
+ memcpy(zOut, &zSrc[ofst], cnt);
|
|
+ zOut += cnt;
|
|
+ break;
|
|
+ }
|
|
+ case ':': {
|
|
+ zDelta++; lenDelta--;
|
|
+ total += cnt;
|
|
+ if( total>limit ){
|
|
+ /* ERROR: insert command gives an output larger than predicted */
|
|
+ return -1;
|
|
+ }
|
|
+ if( cnt>lenDelta ){
|
|
+ /* ERROR: insert count exceeds size of delta */
|
|
+ return -1;
|
|
+ }
|
|
+ memcpy(zOut, zDelta, cnt);
|
|
+ zOut += cnt;
|
|
+ zDelta += cnt;
|
|
+ lenDelta -= cnt;
|
|
+ break;
|
|
+ }
|
|
+ case ';': {
|
|
+ zDelta++; lenDelta--;
|
|
+ zOut[0] = 0;
|
|
+#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
|
|
+ if( cnt!=checksum(zOrigOut, total) ){
|
|
+ /* ERROR: bad checksum */
|
|
+ return -1;
|
|
+ }
|
|
+#endif
|
|
+ if( total!=limit ){
|
|
+ /* ERROR: generated size does not match predicted size */
|
|
+ return -1;
|
|
+ }
|
|
+ return total;
|
|
+ }
|
|
+ default: {
|
|
+ /* ERROR: unknown delta operator */
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ /* ERROR: unterminated delta */
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL functions: delta_create(X,Y)
|
|
+**
|
|
+** Return a delta for carrying X into Y.
|
|
+*/
|
|
+static void deltaCreateFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *aOrig; int nOrig; /* old blob */
|
|
+ const char *aNew; int nNew; /* new blob */
|
|
+ char *aOut; int nOut; /* output delta */
|
|
+
|
|
+ assert( argc==2 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
|
+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
|
|
+ nOrig = sqlite3_value_bytes(argv[0]);
|
|
+ aOrig = (const char*)sqlite3_value_blob(argv[0]);
|
|
+ nNew = sqlite3_value_bytes(argv[1]);
|
|
+ aNew = (const char*)sqlite3_value_blob(argv[1]);
|
|
+ aOut = sqlite3_malloc64(nNew+70);
|
|
+ if( aOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ nOut = delta_create(aOrig, nOrig, aNew, nNew, aOut);
|
|
+ if( nOut<0 ){
|
|
+ sqlite3_free(aOut);
|
|
+ sqlite3_result_error(context, "cannot create fossil delta", -1);
|
|
+ }else{
|
|
+ sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL functions: delta_apply(X,D)
|
|
+**
|
|
+** Return the result of applying delta D to input X.
|
|
+*/
|
|
+static void deltaApplyFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *aOrig; int nOrig; /* The X input */
|
|
+ const char *aDelta; int nDelta; /* The input delta (D) */
|
|
+ char *aOut; int nOut, nOut2; /* The output */
|
|
+
|
|
+ assert( argc==2 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
|
+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
|
|
+ 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 */
|
|
+ nOut = delta_output_size(aDelta, nDelta);
|
|
+ if( nOut<0 ){
|
|
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
|
|
+ return;
|
|
+ }
|
|
+ aOut = sqlite3_malloc64((sqlite3_int64)nOut+1);
|
|
+ if( aOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ nOut2 = delta_apply(aOrig, nOrig, aDelta, nDelta, aOut);
|
|
+ if( nOut2!=nOut ){
|
|
+ sqlite3_free(aOut);
|
|
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
|
|
+ }else{
|
|
+ sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** SQL functions: delta_output_size(D)
|
|
+**
|
|
+** Return the size of the output that results from applying delta D.
|
|
+*/
|
|
+static void deltaOutputSizeFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *aDelta; int nDelta; /* The input delta (D) */
|
|
+ int nOut; /* Size of output */
|
|
+ assert( argc==1 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
|
+ nDelta = sqlite3_value_bytes(argv[0]);
|
|
+ aDelta = (const char*)sqlite3_value_blob(argv[0]);
|
|
+
|
|
+ /* Figure out the size of the output */
|
|
+ nOut = delta_output_size(aDelta, nDelta);
|
|
+ if( nOut<0 ){
|
|
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
|
|
+ return;
|
|
+ }else{
|
|
+ sqlite3_result_int(context, nOut);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+** Table-valued SQL function: delta_parse(DELTA)
|
|
+**
|
|
+** Schema:
|
|
+**
|
|
+** CREATE TABLE delta_parse(
|
|
+** op TEXT,
|
|
+** a1 INT,
|
|
+** a2 ANY,
|
|
+** delta HIDDEN BLOB
|
|
+** );
|
|
+**
|
|
+** Given an input DELTA, this function parses the delta and returns
|
|
+** rows for each entry in the delta. The op column has one of the
|
|
+** values SIZE, COPY, INSERT, CHECKSUM, ERROR.
|
|
+**
|
|
+** Assuming no errors, the first row has op='SIZE'. a1 is the size of
|
|
+** the output in bytes and a2 is NULL.
|
|
+**
|
|
+** After the initial SIZE row, there are zero or more 'COPY' and/or 'INSERT'
|
|
+** rows. A COPY row means content is copied from the source into the
|
|
+** output. Column a1 is the number of bytes to copy and a2 is the offset
|
|
+** into source from which to begin copying. An INSERT row means to
|
|
+** insert text into the output stream. Column a1 is the number of bytes
|
|
+** to insert and column is a BLOB that contains the text to be inserted.
|
|
+**
|
|
+** The last row of a well-formed delta will have an op value of 'CHECKSUM'.
|
|
+** The a1 column will be the value of the checksum and a2 will be NULL.
|
|
+**
|
|
+** If the input delta is not well-formed, then a row with an op value
|
|
+** of 'ERROR' is returned. The a1 value of the ERROR row is the offset
|
|
+** into the delta where the error was encountered and a2 is NULL.
|
|
+*/
|
|
+typedef struct deltaparsevtab_vtab deltaparsevtab_vtab;
|
|
+typedef struct deltaparsevtab_cursor deltaparsevtab_cursor;
|
|
+struct deltaparsevtab_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ /* No additional information needed */
|
|
+};
|
|
+struct deltaparsevtab_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ char *aDelta; /* The delta being parsed */
|
|
+ int nDelta; /* Number of bytes in the delta */
|
|
+ int iCursor; /* Current cursor location */
|
|
+ int eOp; /* Name of current operator */
|
|
+ unsigned int a1, a2; /* Arguments to current operator */
|
|
+ int iNext; /* Next cursor value */
|
|
+};
|
|
+
|
|
+/* Operator names:
|
|
+*/
|
|
+static const char *azOp[] = {
|
|
+ "SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF"
|
|
+};
|
|
+#define DELTAPARSE_OP_SIZE 0
|
|
+#define DELTAPARSE_OP_COPY 1
|
|
+#define DELTAPARSE_OP_INSERT 2
|
|
+#define DELTAPARSE_OP_CHECKSUM 3
|
|
+#define DELTAPARSE_OP_ERROR 4
|
|
+#define DELTAPARSE_OP_EOF 5
|
|
+
|
|
+/*
|
|
+** The deltaparsevtabConnect() method is invoked to create a new
|
|
+** deltaparse virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for deltaparsevtab_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the deltaparsevtab_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against the virtual table will look like.
|
|
+*/
|
|
+static int deltaparsevtabConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ deltaparsevtab_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(op,a1,a2,delta HIDDEN)"
|
|
+ );
|
|
+ /* For convenience, define symbolic names for the index to each column. */
|
|
+#define DELTAPARSEVTAB_OP 0
|
|
+#define DELTAPARSEVTAB_A1 1
|
|
+#define DELTAPARSEVTAB_A2 2
|
|
+#define DELTAPARSEVTAB_DELTA 3
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc64( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for deltaparsevtab_vtab objects.
|
|
+*/
|
|
+static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
|
|
+ deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab;
|
|
+ sqlite3_free(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new deltaparsevtab_cursor object.
|
|
+*/
|
|
+static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ deltaparsevtab_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a deltaparsevtab_cursor.
|
|
+*/
|
|
+static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
|
|
+ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a deltaparsevtab_cursor to its next row of output.
|
|
+*/
|
|
+static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
|
|
+ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
|
|
+ const char *z;
|
|
+ int i = 0;
|
|
+
|
|
+ pCur->iCursor = pCur->iNext;
|
|
+ z = pCur->aDelta + pCur->iCursor;
|
|
+ pCur->a1 = deltaGetInt(&z, &i);
|
|
+ switch( z[0] ){
|
|
+ case '@': {
|
|
+ z++;
|
|
+ pCur->a2 = deltaGetInt(&z, &i);
|
|
+ pCur->eOp = DELTAPARSE_OP_COPY;
|
|
+ pCur->iNext = (int)(&z[1] - pCur->aDelta);
|
|
+ break;
|
|
+ }
|
|
+ case ':': {
|
|
+ z++;
|
|
+ pCur->a2 = (unsigned int)(z - pCur->aDelta);
|
|
+ pCur->eOp = DELTAPARSE_OP_INSERT;
|
|
+ pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
|
|
+ break;
|
|
+ }
|
|
+ case ';': {
|
|
+ pCur->eOp = DELTAPARSE_OP_CHECKSUM;
|
|
+ pCur->iNext = pCur->nDelta;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ if( pCur->iNext==pCur->nDelta ){
|
|
+ pCur->eOp = DELTAPARSE_OP_EOF;
|
|
+ }else{
|
|
+ pCur->eOp = DELTAPARSE_OP_ERROR;
|
|
+ pCur->iNext = pCur->nDelta;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the deltaparsevtab_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int deltaparsevtabColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case DELTAPARSEVTAB_OP: {
|
|
+ sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case DELTAPARSEVTAB_A1: {
|
|
+ sqlite3_result_int(ctx, pCur->a1);
|
|
+ break;
|
|
+ }
|
|
+ case DELTAPARSEVTAB_A2: {
|
|
+ if( pCur->eOp==DELTAPARSE_OP_COPY ){
|
|
+ sqlite3_result_int(ctx, pCur->a2);
|
|
+ }else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
|
|
+ sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
|
|
+ SQLITE_TRANSIENT);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case DELTAPARSEVTAB_DELTA: {
|
|
+ sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
|
|
+ *pRowid = pCur->iCursor;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
|
|
+ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
|
|
+ return pCur->eOp==DELTAPARSE_OP_EOF;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the deltaparsevtab_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or
|
|
+** deltaparsevtabEof().
|
|
+*/
|
|
+static int deltaparsevtabFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor;
|
|
+ const char *a;
|
|
+ int i = 0;
|
|
+ pCur->eOp = DELTAPARSE_OP_ERROR;
|
|
+ if( idxNum!=1 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ pCur->nDelta = sqlite3_value_bytes(argv[0]);
|
|
+ a = (const char*)sqlite3_value_blob(argv[0]);
|
|
+ if( pCur->nDelta==0 || a==0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 );
|
|
+ if( pCur->aDelta==0 ){
|
|
+ pCur->nDelta = 0;
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ memcpy(pCur->aDelta, a, pCur->nDelta);
|
|
+ pCur->aDelta[pCur->nDelta] = 0;
|
|
+ a = pCur->aDelta;
|
|
+ pCur->eOp = DELTAPARSE_OP_SIZE;
|
|
+ pCur->a1 = deltaGetInt(&a, &i);
|
|
+ if( a[0]!='\n' ){
|
|
+ pCur->eOp = DELTAPARSE_OP_ERROR;
|
|
+ pCur->a1 = pCur->a2 = 0;
|
|
+ pCur->iNext = pCur->nDelta;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ a++;
|
|
+ pCur->iNext = (unsigned int)(a - pCur->aDelta);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int deltaparsevtabBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue;
|
|
+ if( pIdxInfo->aConstraint[i].usable==0 ) continue;
|
|
+ if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ pIdxInfo->estimatedCost = (double)1;
|
|
+ pIdxInfo->estimatedRows = 10;
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ pIdxInfo->estimatedCost = (double)0x7fffffff;
|
|
+ pIdxInfo->estimatedRows = 0x7fffffff;
|
|
+ return SQLITE_CONSTRAINT;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** virtual table.
|
|
+*/
|
|
+static sqlite3_module deltaparsevtabModule = {
|
|
+ /* iVersion */ 0,
|
|
+ /* xCreate */ 0,
|
|
+ /* xConnect */ deltaparsevtabConnect,
|
|
+ /* xBestIndex */ deltaparsevtabBestIndex,
|
|
+ /* xDisconnect */ deltaparsevtabDisconnect,
|
|
+ /* xDestroy */ 0,
|
|
+ /* xOpen */ deltaparsevtabOpen,
|
|
+ /* xClose */ deltaparsevtabClose,
|
|
+ /* xFilter */ deltaparsevtabFilter,
|
|
+ /* xNext */ deltaparsevtabNext,
|
|
+ /* xEof */ deltaparsevtabEof,
|
|
+ /* xColumn */ deltaparsevtabColumn,
|
|
+ /* xRowid */ deltaparsevtabRowid,
|
|
+ /* xUpdate */ 0,
|
|
+ /* xBegin */ 0,
|
|
+ /* xSync */ 0,
|
|
+ /* xCommit */ 0,
|
|
+ /* xRollback */ 0,
|
|
+ /* xFindMethod */ 0,
|
|
+ /* xRename */ 0,
|
|
+ /* xSavepoint */ 0,
|
|
+ /* xRelease */ 0,
|
|
+ /* xRollbackTo */ 0,
|
|
+ /* xShadowName */ 0
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_fossildelta_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0,
|
|
+ deltaCreateFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
|
|
+ deltaApplyFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
|
|
+ deltaOutputSizeFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0,
|
|
+ deltaCreateFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
|
|
+ deltaApplyFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
|
|
+ deltaOutputSizeFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/fuzzer.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/fuzzer.c 2019-05-14 16:22:31.239067000 +0200
|
|
@@ -0,0 +1,1199 @@
|
|
+/*
|
|
+** 2011 March 24
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** Code for a demonstration virtual table that generates variations
|
|
+** on an input word at increasing edit distances from the original.
|
|
+**
|
|
+** A fuzzer virtual table is created like this:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE f USING fuzzer(<fuzzer-data-table>);
|
|
+**
|
|
+** When it is created, the new fuzzer table must be supplied with the
|
|
+** name of a "fuzzer data table", which must reside in the same database
|
|
+** file as the new fuzzer table. The fuzzer data table contains the various
|
|
+** transformations and their costs that the fuzzer logic uses to generate
|
|
+** variations.
|
|
+**
|
|
+** The fuzzer data table must contain exactly four columns (more precisely,
|
|
+** the statement "SELECT * FROM <fuzzer_data_table>" must return records
|
|
+** that consist of four columns). It does not matter what the columns are
|
|
+** named.
|
|
+**
|
|
+** Each row in the fuzzer data table represents a single character
|
|
+** transformation. The left most column of the row (column 0) contains an
|
|
+** integer value - the identifier of the ruleset to which the transformation
|
|
+** rule belongs (see "MULTIPLE RULE SETS" below). The second column of the
|
|
+** row (column 0) contains the input character or characters. The third
|
|
+** column contains the output character or characters. And the fourth column
|
|
+** contains the integer cost of making the transformation. For example:
|
|
+**
|
|
+** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
|
|
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
|
|
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
|
|
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
|
|
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
|
|
+**
|
|
+** The first row inserted into the fuzzer data table by the SQL script
|
|
+** above indicates that the cost of inserting a letter 'a' is 100. (All
|
|
+** costs are integers. We recommend that costs be scaled so that the
|
|
+** average cost is around 100.) The second INSERT statement creates a rule
|
|
+** saying that the cost of deleting a single letter 'b' is 87. The third
|
|
+** and fourth INSERT statements mean that the cost of transforming a
|
|
+** single letter "o" into the two-letter sequence "oe" is 38 and that the
|
|
+** cost of transforming "oe" back into "o" is 40.
|
|
+**
|
|
+** The contents of the fuzzer data table are loaded into main memory when
|
|
+** a fuzzer table is first created, and may be internally reloaded by the
|
|
+** system at any subsequent time. Therefore, the fuzzer data table should be
|
|
+** populated before the fuzzer table is created and not modified thereafter.
|
|
+** If you do need to modify the contents of the fuzzer data table, it is
|
|
+** recommended that the associated fuzzer table be dropped, the fuzzer data
|
|
+** table edited, and the fuzzer table recreated within a single transaction.
|
|
+** Alternatively, the fuzzer data table can be edited then the database
|
|
+** connection can be closed and reopened.
|
|
+**
|
|
+** Once it has been created, the fuzzer table can be queried as follows:
|
|
+**
|
|
+** SELECT word, distance FROM f
|
|
+** WHERE word MATCH 'abcdefg'
|
|
+** AND distance<200;
|
|
+**
|
|
+** This first query outputs the string "abcdefg" and all strings that
|
|
+** can be derived from that string by appling the specified transformations.
|
|
+** The strings are output together with their total transformation cost
|
|
+** (called "distance") and appear in order of increasing cost. No string
|
|
+** is output more than once. If there are multiple ways to transform the
|
|
+** target string into the output string then the lowest cost transform is
|
|
+** the one that is returned. In the example, the search is limited to
|
|
+** strings with a total distance of less than 200.
|
|
+**
|
|
+** The fuzzer is a read-only table. Any attempt to DELETE, INSERT, or
|
|
+** UPDATE on a fuzzer table will throw an error.
|
|
+**
|
|
+** It is important to put some kind of a limit on the fuzzer output. This
|
|
+** can be either in the form of a LIMIT clause at the end of the query,
|
|
+** or better, a "distance<NNN" constraint where NNN is some number. The
|
|
+** running time and memory requirement is exponential in the value of NNN
|
|
+** so you want to make sure that NNN is not too big. A value of NNN that
|
|
+** is about twice the average transformation cost seems to give good results.
|
|
+**
|
|
+** The fuzzer table can be useful for tasks such as spelling correction.
|
|
+** Suppose there is a second table vocabulary(w) where the w column contains
|
|
+** all correctly spelled words. Let $word be a word you want to look up.
|
|
+**
|
|
+** SELECT vocabulary.w FROM f, vocabulary
|
|
+** WHERE f.word MATCH $word
|
|
+** AND f.distance<=200
|
|
+** AND f.word=vocabulary.w
|
|
+** LIMIT 20
|
|
+**
|
|
+** The query above gives the 20 closest words to the $word being tested.
|
|
+** (Note that for good performance, the vocubulary.w column should be
|
|
+** indexed.)
|
|
+**
|
|
+** A similar query can be used to find all words in the dictionary that
|
|
+** begin with some prefix $prefix:
|
|
+**
|
|
+** SELECT vocabulary.w FROM f, vocabulary
|
|
+** WHERE f.word MATCH $prefix
|
|
+** AND f.distance<=200
|
|
+** AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF')
|
|
+** LIMIT 50
|
|
+**
|
|
+** This last query will show up to 50 words out of the vocabulary that
|
|
+** match or nearly match the $prefix.
|
|
+**
|
|
+** MULTIPLE RULE SETS
|
|
+**
|
|
+** Normally, the "ruleset" value associated with all character transformations
|
|
+** in the fuzzer data table is zero. However, if required, the fuzzer table
|
|
+** allows multiple rulesets to be defined. Each query uses only a single
|
|
+** ruleset. This allows, for example, a single fuzzer table to support
|
|
+** multiple languages.
|
|
+**
|
|
+** By default, only the rules from ruleset 0 are used. To specify an
|
|
+** alternative ruleset, a "ruleset = ?" expression must be added to the
|
|
+** WHERE clause of a SELECT, where ? is the identifier of the desired
|
|
+** ruleset. For example:
|
|
+**
|
|
+** SELECT vocabulary.w FROM f, vocabulary
|
|
+** WHERE f.word MATCH $word
|
|
+** AND f.distance<=200
|
|
+** AND f.word=vocabulary.w
|
|
+** AND f.ruleset=1 -- Specify the ruleset to use here
|
|
+** LIMIT 20
|
|
+**
|
|
+** If no "ruleset = ?" constraint is specified in the WHERE clause, ruleset
|
|
+** 0 is used.
|
|
+**
|
|
+** LIMITS
|
|
+**
|
|
+** The maximum ruleset number is 2147483647. The maximum length of either
|
|
+** of the strings in the second or third column of the fuzzer data table
|
|
+** is 50 bytes. The maximum cost on a rule is 1000.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+
|
|
+/* If SQLITE_DEBUG is not defined, disable assert statements. */
|
|
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
|
|
+# define NDEBUG
|
|
+#endif
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this implementation
|
|
+*/
|
|
+typedef struct fuzzer_vtab fuzzer_vtab;
|
|
+typedef struct fuzzer_cursor fuzzer_cursor;
|
|
+typedef struct fuzzer_rule fuzzer_rule;
|
|
+typedef struct fuzzer_seen fuzzer_seen;
|
|
+typedef struct fuzzer_stem fuzzer_stem;
|
|
+
|
|
+/*
|
|
+** Various types.
|
|
+**
|
|
+** fuzzer_cost is the "cost" of an edit operation.
|
|
+**
|
|
+** fuzzer_len is the length of a matching string.
|
|
+**
|
|
+** fuzzer_ruleid is an ruleset identifier.
|
|
+*/
|
|
+typedef int fuzzer_cost;
|
|
+typedef signed char fuzzer_len;
|
|
+typedef int fuzzer_ruleid;
|
|
+
|
|
+/*
|
|
+** Limits
|
|
+*/
|
|
+#define FUZZER_MX_LENGTH 50 /* Maximum length of a rule string */
|
|
+#define FUZZER_MX_RULEID 2147483647 /* Maximum rule ID */
|
|
+#define FUZZER_MX_COST 1000 /* Maximum single-rule cost */
|
|
+#define FUZZER_MX_OUTPUT_LENGTH 100 /* Maximum length of an output string */
|
|
+
|
|
+
|
|
+/*
|
|
+** Each transformation rule is stored as an instance of this object.
|
|
+** All rules are kept on a linked list sorted by rCost.
|
|
+*/
|
|
+struct fuzzer_rule {
|
|
+ fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
|
|
+ char *zFrom; /* Transform from */
|
|
+ fuzzer_cost rCost; /* Cost of this transformation */
|
|
+ fuzzer_len nFrom, nTo; /* Length of the zFrom and zTo strings */
|
|
+ fuzzer_ruleid iRuleset; /* The rule set to which this rule belongs */
|
|
+ char zTo[4]; /* Transform to (extra space appended) */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A stem object is used to generate variants. It is also used to record
|
|
+** previously generated outputs.
|
|
+**
|
|
+** Every stem is added to a hash table as it is output. Generation of
|
|
+** duplicate stems is suppressed.
|
|
+**
|
|
+** Active stems (those that might generate new outputs) are kepts on a linked
|
|
+** list sorted by increasing cost. The cost is the sum of rBaseCost and
|
|
+** pRule->rCost.
|
|
+*/
|
|
+struct fuzzer_stem {
|
|
+ char *zBasis; /* Word being fuzzed */
|
|
+ const fuzzer_rule *pRule; /* Current rule to apply */
|
|
+ fuzzer_stem *pNext; /* Next stem in rCost order */
|
|
+ fuzzer_stem *pHash; /* Next stem with same hash on zBasis */
|
|
+ fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */
|
|
+ fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */
|
|
+ fuzzer_len nBasis; /* Length of the zBasis string */
|
|
+ fuzzer_len n; /* Apply pRule at this character offset */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A fuzzer virtual-table object
|
|
+*/
|
|
+struct fuzzer_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ char *zClassName; /* Name of this class. Default: "fuzzer" */
|
|
+ fuzzer_rule *pRule; /* All active rules in this fuzzer */
|
|
+ int nCursor; /* Number of active cursors */
|
|
+};
|
|
+
|
|
+#define FUZZER_HASH 4001 /* Hash table size */
|
|
+#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */
|
|
+
|
|
+/* A fuzzer cursor object */
|
|
+struct fuzzer_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_int64 iRowid; /* The rowid of the current word */
|
|
+ fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */
|
|
+ fuzzer_cost rLimit; /* Maximum cost of any term */
|
|
+ fuzzer_stem *pStem; /* Stem with smallest rCostX */
|
|
+ fuzzer_stem *pDone; /* Stems already processed to completion */
|
|
+ fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */
|
|
+ int mxQueue; /* Largest used index in aQueue[] */
|
|
+ char *zBuf; /* Temporary use buffer */
|
|
+ int nBuf; /* Bytes allocated for zBuf */
|
|
+ int nStem; /* Number of stems allocated */
|
|
+ int iRuleset; /* Only process rules from this ruleset */
|
|
+ fuzzer_rule nullRule; /* Null rule used first */
|
|
+ fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The two input rule lists are both sorted in order of increasing
|
|
+** cost. Merge them together into a single list, sorted by cost, and
|
|
+** return a pointer to the head of that list.
|
|
+*/
|
|
+static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
|
|
+ fuzzer_rule head;
|
|
+ fuzzer_rule *pTail;
|
|
+
|
|
+ pTail = &head;
|
|
+ while( pA && pB ){
|
|
+ if( pA->rCost<=pB->rCost ){
|
|
+ pTail->pNext = pA;
|
|
+ pTail = pA;
|
|
+ pA = pA->pNext;
|
|
+ }else{
|
|
+ pTail->pNext = pB;
|
|
+ pTail = pB;
|
|
+ pB = pB->pNext;
|
|
+ }
|
|
+ }
|
|
+ if( pA==0 ){
|
|
+ pTail->pNext = pB;
|
|
+ }else{
|
|
+ pTail->pNext = pA;
|
|
+ }
|
|
+ return head.pNext;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Statement pStmt currently points to a row in the fuzzer data table. This
|
|
+** function allocates and populates a fuzzer_rule structure according to
|
|
+** the content of the row.
|
|
+**
|
|
+** If successful, *ppRule is set to point to the new object and SQLITE_OK
|
|
+** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
|
|
+** to an error message and an SQLite error code returned.
|
|
+*/
|
|
+static int fuzzerLoadOneRule(
|
|
+ fuzzer_vtab *p, /* Fuzzer virtual table handle */
|
|
+ sqlite3_stmt *pStmt, /* Base rule on statements current row */
|
|
+ fuzzer_rule **ppRule, /* OUT: New rule object */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ sqlite3_int64 iRuleset = sqlite3_column_int64(pStmt, 0);
|
|
+ const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
|
|
+ const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
|
|
+ int nCost = sqlite3_column_int(pStmt, 3);
|
|
+
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ int nFrom; /* Size of string zFrom, in bytes */
|
|
+ int nTo; /* Size of string zTo, in bytes */
|
|
+ fuzzer_rule *pRule = 0; /* New rule object to return */
|
|
+
|
|
+ if( zFrom==0 ) zFrom = "";
|
|
+ if( zTo==0 ) zTo = "";
|
|
+ nFrom = (int)strlen(zFrom);
|
|
+ nTo = (int)strlen(zTo);
|
|
+
|
|
+ /* Silently ignore null transformations */
|
|
+ if( strcmp(zFrom, zTo)==0 ){
|
|
+ *ppRule = 0;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+
|
|
+ if( nCost<=0 || nCost>FUZZER_MX_COST ){
|
|
+ *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
|
|
+ p->zClassName, FUZZER_MX_COST
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else
|
|
+ if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
|
|
+ *pzErr = sqlite3_mprintf("%s: maximum string length is %d",
|
|
+ p->zClassName, FUZZER_MX_LENGTH
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else
|
|
+ if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){
|
|
+ *pzErr = sqlite3_mprintf("%s: ruleset must be between 0 and %d",
|
|
+ p->zClassName, FUZZER_MX_RULEID
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+
|
|
+ pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
|
|
+ if( pRule==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pRule, 0, sizeof(*pRule));
|
|
+ pRule->zFrom = pRule->zTo;
|
|
+ pRule->zFrom += nTo + 1;
|
|
+ pRule->nFrom = (fuzzer_len)nFrom;
|
|
+ memcpy(pRule->zFrom, zFrom, nFrom+1);
|
|
+ memcpy(pRule->zTo, zTo, nTo+1);
|
|
+ pRule->nTo = (fuzzer_len)nTo;
|
|
+ pRule->rCost = nCost;
|
|
+ pRule->iRuleset = (int)iRuleset;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *ppRule = pRule;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Load the content of the fuzzer data table into memory.
|
|
+*/
|
|
+static int fuzzerLoadRules(
|
|
+ sqlite3 *db, /* Database handle */
|
|
+ fuzzer_vtab *p, /* Virtual fuzzer table to configure */
|
|
+ const char *zDb, /* Database containing rules data */
|
|
+ const char *zData, /* Table containing rules data */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ char *zSql; /* SELECT used to read from rules table */
|
|
+ fuzzer_rule *pHead = 0;
|
|
+
|
|
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData);
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int rc2; /* finalize() return code */
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
|
|
+ }else if( sqlite3_column_count(pStmt)!=4 ){
|
|
+ *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
|
|
+ p->zClassName, zData, sqlite3_column_count(pStmt)
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ fuzzer_rule *pRule = 0;
|
|
+ rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr);
|
|
+ if( pRule ){
|
|
+ pRule->pNext = pHead;
|
|
+ pHead = pRule;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ rc2 = sqlite3_finalize(pStmt);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+
|
|
+ /* All rules are now in a singly linked list starting at pHead. This
|
|
+ ** block sorts them by cost and then sets fuzzer_vtab.pRule to point to
|
|
+ ** point to the head of the sorted list.
|
|
+ */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ unsigned int i;
|
|
+ fuzzer_rule *pX;
|
|
+ fuzzer_rule *a[15];
|
|
+ for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
|
|
+ while( (pX = pHead)!=0 ){
|
|
+ pHead = pX->pNext;
|
|
+ pX->pNext = 0;
|
|
+ for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
|
|
+ pX = fuzzerMergeRules(a[i], pX);
|
|
+ a[i] = 0;
|
|
+ }
|
|
+ a[i] = fuzzerMergeRules(a[i], pX);
|
|
+ }
|
|
+ for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
|
|
+ pX = fuzzerMergeRules(a[i], pX);
|
|
+ }
|
|
+ p->pRule = fuzzerMergeRules(p->pRule, pX);
|
|
+ }else{
|
|
+ /* An error has occurred. Setting p->pRule to point to the head of the
|
|
+ ** allocated list ensures that the list will be cleaned up in this case.
|
|
+ */
|
|
+ assert( p->pRule==0 );
|
|
+ p->pRule = pHead;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function converts an SQL quoted string into an unquoted string
|
|
+** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
|
+** containing the result. The caller should eventually free this buffer
|
|
+** using sqlite3_free.
|
|
+**
|
|
+** Examples:
|
|
+**
|
|
+** "abc" becomes abc
|
|
+** 'xyz' becomes xyz
|
|
+** [pqr] becomes pqr
|
|
+** `mno` becomes mno
|
|
+*/
|
|
+static char *fuzzerDequote(const char *zIn){
|
|
+ size_t nIn; /* Size of input string, in bytes */
|
|
+ char *zOut; /* Output (dequoted) string */
|
|
+
|
|
+ nIn = strlen(zIn);
|
|
+ zOut = sqlite3_malloc(nIn+1);
|
|
+ if( zOut ){
|
|
+ char q = zIn[0]; /* Quote character (if any ) */
|
|
+
|
|
+ if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
|
+ memcpy(zOut, zIn, (size_t)(nIn+1));
|
|
+ }else{
|
|
+ size_t iOut = 0; /* Index of next byte to write to output */
|
|
+ size_t iIn; /* Index of next byte to read from input */
|
|
+
|
|
+ if( q=='[' ) q = ']';
|
|
+ for(iIn=1; iIn<nIn; iIn++){
|
|
+ if( zIn[iIn]==q ) iIn++;
|
|
+ zOut[iOut++] = zIn[iIn];
|
|
+ }
|
|
+ }
|
|
+ assert( strlen(zOut)<=nIn );
|
|
+ }
|
|
+ return zOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xDisconnect/xDestroy method for the fuzzer module.
|
|
+*/
|
|
+static int fuzzerDisconnect(sqlite3_vtab *pVtab){
|
|
+ fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
|
|
+ assert( p->nCursor==0 );
|
|
+ while( p->pRule ){
|
|
+ fuzzer_rule *pRule = p->pRule;
|
|
+ p->pRule = pRule->pNext;
|
|
+ sqlite3_free(pRule);
|
|
+ }
|
|
+ sqlite3_free(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xConnect/xCreate method for the fuzzer module. Arguments are:
|
|
+**
|
|
+** argv[0] -> module name ("fuzzer")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[3] -> fuzzer rule table name
|
|
+*/
|
|
+static int fuzzerConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
+ fuzzer_vtab *pNew = 0; /* New virtual table */
|
|
+ const char *zModule = argv[0];
|
|
+ const char *zDb = argv[1];
|
|
+
|
|
+ if( argc!=4 ){
|
|
+ *pzErr = sqlite3_mprintf(
|
|
+ "%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ size_t nModule; /* Length of zModule, in bytes */
|
|
+
|
|
+ nModule = strlen(zModule);
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) + nModule + 1);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ char *zTab; /* Dequoted name of fuzzer data table */
|
|
+
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->zClassName = (char*)&pNew[1];
|
|
+ memcpy(pNew->zClassName, zModule, (size_t)(nModule+1));
|
|
+
|
|
+ zTab = fuzzerDequote(argv[3]);
|
|
+ if( zTab==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = fuzzerLoadRules(db, pNew, zDb, zTab, pzErr);
|
|
+ sqlite3_free(zTab);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,ruleset)");
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ fuzzerDisconnect((sqlite3_vtab *)pNew);
|
|
+ pNew = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *ppVtab = (sqlite3_vtab *)pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a new fuzzer cursor.
|
|
+*/
|
|
+static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
|
|
+ fuzzer_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->pVtab = p;
|
|
+ *ppCursor = &pCur->base;
|
|
+ p->nCursor++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free all stems in a list.
|
|
+*/
|
|
+static void fuzzerClearStemList(fuzzer_stem *pStem){
|
|
+ while( pStem ){
|
|
+ fuzzer_stem *pNext = pStem->pNext;
|
|
+ sqlite3_free(pStem);
|
|
+ pStem = pNext;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
|
+** to indicate that it is at EOF.
|
|
+*/
|
|
+static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){
|
|
+ int i;
|
|
+ fuzzerClearStemList(pCur->pStem);
|
|
+ fuzzerClearStemList(pCur->pDone);
|
|
+ for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]);
|
|
+ pCur->rLimit = (fuzzer_cost)0;
|
|
+ if( clearHash && pCur->nStem ){
|
|
+ pCur->mxQueue = 0;
|
|
+ pCur->pStem = 0;
|
|
+ pCur->pDone = 0;
|
|
+ memset(pCur->aQueue, 0, sizeof(pCur->aQueue));
|
|
+ memset(pCur->apHash, 0, sizeof(pCur->apHash));
|
|
+ }
|
|
+ pCur->nStem = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close a fuzzer cursor.
|
|
+*/
|
|
+static int fuzzerClose(sqlite3_vtab_cursor *cur){
|
|
+ fuzzer_cursor *pCur = (fuzzer_cursor *)cur;
|
|
+ fuzzerClearCursor(pCur, 0);
|
|
+ sqlite3_free(pCur->zBuf);
|
|
+ pCur->pVtab->nCursor--;
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute the current output term for a fuzzer_stem.
|
|
+*/
|
|
+static int fuzzerRender(
|
|
+ fuzzer_stem *pStem, /* The stem to be rendered */
|
|
+ char **pzBuf, /* Write results into this buffer. realloc if needed */
|
|
+ int *pnBuf /* Size of the buffer */
|
|
+){
|
|
+ const fuzzer_rule *pRule = pStem->pRule;
|
|
+ int n; /* Size of output term without nul-term */
|
|
+ char *z; /* Buffer to assemble output term in */
|
|
+
|
|
+ n = pStem->nBasis + pRule->nTo - pRule->nFrom;
|
|
+ if( (*pnBuf)<n+1 ){
|
|
+ (*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
|
|
+ if( (*pzBuf)==0 ) return SQLITE_NOMEM;
|
|
+ (*pnBuf) = n+100;
|
|
+ }
|
|
+ n = pStem->n;
|
|
+ z = *pzBuf;
|
|
+ if( n<0 ){
|
|
+ memcpy(z, pStem->zBasis, pStem->nBasis+1);
|
|
+ }else{
|
|
+ memcpy(z, pStem->zBasis, n);
|
|
+ memcpy(&z[n], pRule->zTo, pRule->nTo);
|
|
+ memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
|
|
+ pStem->nBasis-n-pRule->nFrom+1);
|
|
+ }
|
|
+
|
|
+ assert( z[pStem->nBasis + pRule->nTo - pRule->nFrom]==0 );
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute a hash on zBasis.
|
|
+*/
|
|
+static unsigned int fuzzerHash(const char *z){
|
|
+ unsigned int h = 0;
|
|
+ while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); }
|
|
+ return h % FUZZER_HASH;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Current cost of a stem
|
|
+*/
|
|
+static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){
|
|
+ return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+/*
|
|
+** Print a description of a fuzzer_stem on stderr.
|
|
+*/
|
|
+static void fuzzerStemPrint(
|
|
+ const char *zPrefix,
|
|
+ fuzzer_stem *pStem,
|
|
+ const char *zSuffix
|
|
+){
|
|
+ if( pStem->n<0 ){
|
|
+ fprintf(stderr, "%s[%s](%d)-->self%s",
|
|
+ zPrefix,
|
|
+ pStem->zBasis, pStem->rBaseCost,
|
|
+ zSuffix
|
|
+ );
|
|
+ }else{
|
|
+ char *zBuf = 0;
|
|
+ int nBuf = 0;
|
|
+ if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return;
|
|
+ fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s",
|
|
+ zPrefix,
|
|
+ pStem->zBasis, pStem->rBaseCost, zBuf, pStem->,
|
|
+ zSuffix
|
|
+ );
|
|
+ sqlite3_free(zBuf);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Return 1 if the string to which the cursor is point has already
|
|
+** been emitted. Return 0 if not. Return -1 on a memory allocation
|
|
+** failures.
|
|
+*/
|
|
+static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
|
|
+ unsigned int h;
|
|
+ fuzzer_stem *pLookup;
|
|
+
|
|
+ if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
|
|
+ return -1;
|
|
+ }
|
|
+ h = fuzzerHash(pCur->zBuf);
|
|
+ pLookup = pCur->apHash[h];
|
|
+ while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
|
|
+ pLookup = pLookup->pHash;
|
|
+ }
|
|
+ return pLookup!=0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If argument pRule is NULL, this function returns false.
|
|
+**
|
|
+** Otherwise, it returns true if rule pRule should be skipped. A rule
|
|
+** should be skipped if it does not belong to rule-set iRuleset, or if
|
|
+** applying it to stem pStem would create a string longer than
|
|
+** FUZZER_MX_OUTPUT_LENGTH bytes.
|
|
+*/
|
|
+static int fuzzerSkipRule(
|
|
+ const fuzzer_rule *pRule, /* Determine whether or not to skip this */
|
|
+ fuzzer_stem *pStem, /* Stem rule may be applied to */
|
|
+ int iRuleset /* Rule-set used by the current query */
|
|
+){
|
|
+ return pRule && (
|
|
+ (pRule->iRuleset!=iRuleset)
|
|
+ || (pStem->nBasis + pRule->nTo - pRule->nFrom)>FUZZER_MX_OUTPUT_LENGTH
|
|
+ );
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance a fuzzer_stem to its next value. Return 0 if there are
|
|
+** no more values that can be generated by this fuzzer_stem. Return
|
|
+** -1 on a memory allocation failure.
|
|
+*/
|
|
+static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
|
|
+ const fuzzer_rule *pRule;
|
|
+ while( (pRule = pStem->pRule)!=0 ){
|
|
+ assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
|
|
+ while( pStem->n < pStem->nBasis - pRule->nFrom ){
|
|
+ pStem->n++;
|
|
+ if( pRule->nFrom==0
|
|
+ || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
|
|
+ ){
|
|
+ /* Found a rewrite case. Make sure it is not a duplicate */
|
|
+ int rc = fuzzerSeen(pCur, pStem);
|
|
+ if( rc<0 ) return -1;
|
|
+ if( rc==0 ){
|
|
+ fuzzerCost(pStem);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ pStem->n = -1;
|
|
+ do{
|
|
+ pRule = pRule->pNext;
|
|
+ }while( fuzzerSkipRule(pRule, pStem, pCur->iRuleset) );
|
|
+ pStem->pRule = pRule;
|
|
+ if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The two input stem lists are both sorted in order of increasing
|
|
+** rCostX. Merge them together into a single list, sorted by rCostX, and
|
|
+** return a pointer to the head of that new list.
|
|
+*/
|
|
+static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){
|
|
+ fuzzer_stem head;
|
|
+ fuzzer_stem *pTail;
|
|
+
|
|
+ pTail = &head;
|
|
+ while( pA && pB ){
|
|
+ if( pA->rCostX<=pB->rCostX ){
|
|
+ pTail->pNext = pA;
|
|
+ pTail = pA;
|
|
+ pA = pA->pNext;
|
|
+ }else{
|
|
+ pTail->pNext = pB;
|
|
+ pTail = pB;
|
|
+ pB = pB->pNext;
|
|
+ }
|
|
+ }
|
|
+ if( pA==0 ){
|
|
+ pTail->pNext = pB;
|
|
+ }else{
|
|
+ pTail->pNext = pA;
|
|
+ }
|
|
+ return head.pNext;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Load pCur->pStem with the lowest-cost stem. Return a pointer
|
|
+** to the lowest-cost stem.
|
|
+*/
|
|
+static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){
|
|
+ fuzzer_stem *pBest, *pX;
|
|
+ int iBest;
|
|
+ int i;
|
|
+
|
|
+ if( pCur->pStem==0 ){
|
|
+ iBest = -1;
|
|
+ pBest = 0;
|
|
+ for(i=0; i<=pCur->mxQueue; i++){
|
|
+ pX = pCur->aQueue[i];
|
|
+ if( pX==0 ) continue;
|
|
+ if( pBest==0 || pBest->rCostX>pX->rCostX ){
|
|
+ pBest = pX;
|
|
+ iBest = i;
|
|
+ }
|
|
+ }
|
|
+ if( pBest ){
|
|
+ pCur->aQueue[iBest] = pBest->pNext;
|
|
+ pBest->pNext = 0;
|
|
+ pCur->pStem = pBest;
|
|
+ }
|
|
+ }
|
|
+ return pCur->pStem;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Insert pNew into queue of pending stems. Then find the stem
|
|
+** with the lowest rCostX and move it into pCur->pStem.
|
|
+** list. The insert is done such the pNew is in the correct order
|
|
+** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost.
|
|
+*/
|
|
+static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){
|
|
+ fuzzer_stem *pX;
|
|
+ int i;
|
|
+
|
|
+ /* If pCur->pStem exists and is greater than pNew, then make pNew
|
|
+ ** the new pCur->pStem and insert the old pCur->pStem instead.
|
|
+ */
|
|
+ if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){
|
|
+ pNew->pNext = 0;
|
|
+ pCur->pStem = pNew;
|
|
+ pNew = pX;
|
|
+ }
|
|
+
|
|
+ /* Insert the new value */
|
|
+ pNew->pNext = 0;
|
|
+ pX = pNew;
|
|
+ for(i=0; i<=pCur->mxQueue; i++){
|
|
+ if( pCur->aQueue[i] ){
|
|
+ pX = fuzzerMergeStems(pX, pCur->aQueue[i]);
|
|
+ pCur->aQueue[i] = 0;
|
|
+ }else{
|
|
+ pCur->aQueue[i] = pX;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( i>pCur->mxQueue ){
|
|
+ if( i<FUZZER_NQUEUE ){
|
|
+ pCur->mxQueue = i;
|
|
+ pCur->aQueue[i] = pX;
|
|
+ }else{
|
|
+ assert( pCur->mxQueue==FUZZER_NQUEUE-1 );
|
|
+ pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]);
|
|
+ pCur->aQueue[FUZZER_NQUEUE-1] = pX;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return fuzzerLowestCostStem(pCur);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate a new fuzzer_stem. Add it to the hash table but do not
|
|
+** link it into either the pCur->pStem or pCur->pDone lists.
|
|
+*/
|
|
+static fuzzer_stem *fuzzerNewStem(
|
|
+ fuzzer_cursor *pCur,
|
|
+ const char *zWord,
|
|
+ fuzzer_cost rBaseCost
|
|
+){
|
|
+ fuzzer_stem *pNew;
|
|
+ fuzzer_rule *pRule;
|
|
+ unsigned int h;
|
|
+
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 );
|
|
+ if( pNew==0 ) return 0;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->zBasis = (char*)&pNew[1];
|
|
+ pNew->nBasis = (fuzzer_len)strlen(zWord);
|
|
+ memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
|
|
+ pRule = pCur->pVtab->pRule;
|
|
+ while( fuzzerSkipRule(pRule, pNew, pCur->iRuleset) ){
|
|
+ pRule = pRule->pNext;
|
|
+ }
|
|
+ pNew->pRule = pRule;
|
|
+ pNew->n = -1;
|
|
+ pNew->rBaseCost = pNew->rCostX = rBaseCost;
|
|
+ h = fuzzerHash(pNew->zBasis);
|
|
+ pNew->pHash = pCur->apHash[h];
|
|
+ pCur->apHash[h] = pNew;
|
|
+ pCur->nStem++;
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a cursor to its next row of output
|
|
+*/
|
|
+static int fuzzerNext(sqlite3_vtab_cursor *cur){
|
|
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
|
+ int rc;
|
|
+ fuzzer_stem *pStem, *pNew;
|
|
+
|
|
+ pCur->iRowid++;
|
|
+
|
|
+ /* Use the element the cursor is currently point to to create
|
|
+ ** a new stem and insert the new stem into the priority queue.
|
|
+ */
|
|
+ pStem = pCur->pStem;
|
|
+ if( pStem->rCostX>0 ){
|
|
+ rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf);
|
|
+ if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
|
|
+ pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX);
|
|
+ if( pNew ){
|
|
+ if( fuzzerAdvance(pCur, pNew)==0 ){
|
|
+ pNew->pNext = pCur->pDone;
|
|
+ pCur->pDone = pNew;
|
|
+ }else{
|
|
+ if( fuzzerInsert(pCur, pNew)==pNew ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Adjust the priority queue so that the first element of the
|
|
+ ** stem list is the next lowest cost word.
|
|
+ */
|
|
+ while( (pStem = pCur->pStem)!=0 ){
|
|
+ int res = fuzzerAdvance(pCur, pStem);
|
|
+ if( res<0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }else if( res>0 ){
|
|
+ pCur->pStem = 0;
|
|
+ pStem = fuzzerInsert(pCur, pStem);
|
|
+ if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
|
|
+ if( rc<0 ) return SQLITE_NOMEM;
|
|
+ continue;
|
|
+ }
|
|
+ return SQLITE_OK; /* New word found */
|
|
+ }
|
|
+ pCur->pStem = 0;
|
|
+ pStem->pNext = pCur->pDone;
|
|
+ pCur->pDone = pStem;
|
|
+ if( fuzzerLowestCostStem(pCur) ){
|
|
+ rc = fuzzerSeen(pCur, pCur->pStem);
|
|
+ if( rc<0 ) return SQLITE_NOMEM;
|
|
+ if( rc==0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Reach this point only if queue has been exhausted and there is
|
|
+ ** nothing left to be output. */
|
|
+ pCur->rLimit = (fuzzer_cost)0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called to "rewind" a cursor back to the beginning so that
|
|
+** it starts its output over again. Always called at least once
|
|
+** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call.
|
|
+*/
|
|
+static int fuzzerFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
|
|
+ const char *zWord = "";
|
|
+ fuzzer_stem *pStem;
|
|
+ int idx;
|
|
+
|
|
+ fuzzerClearCursor(pCur, 1);
|
|
+ pCur->rLimit = 2147483647;
|
|
+ idx = 0;
|
|
+ if( idxNum & 1 ){
|
|
+ zWord = (const char*)sqlite3_value_text(argv[0]);
|
|
+ idx++;
|
|
+ }
|
|
+ if( idxNum & 2 ){
|
|
+ pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[idx]);
|
|
+ idx++;
|
|
+ }
|
|
+ if( idxNum & 4 ){
|
|
+ pCur->iRuleset = (fuzzer_cost)sqlite3_value_int(argv[idx]);
|
|
+ idx++;
|
|
+ }
|
|
+ pCur->nullRule.pNext = pCur->pVtab->pRule;
|
|
+ pCur->nullRule.rCost = 0;
|
|
+ pCur->nullRule.nFrom = 0;
|
|
+ pCur->nullRule.nTo = 0;
|
|
+ pCur->nullRule.zFrom = "";
|
|
+ pCur->iRowid = 1;
|
|
+ assert( pCur->pStem==0 );
|
|
+
|
|
+ /* If the query term is longer than FUZZER_MX_OUTPUT_LENGTH bytes, this
|
|
+ ** query will return zero rows. */
|
|
+ if( (int)strlen(zWord)<FUZZER_MX_OUTPUT_LENGTH ){
|
|
+ pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
|
|
+ if( pStem==0 ) return SQLITE_NOMEM;
|
|
+ pStem->pRule = &pCur->nullRule;
|
|
+ pStem->n = pStem->nBasis;
|
|
+ }else{
|
|
+ pCur->rLimit = 0;
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only the word and distance columns have values. All other columns
|
|
+** return NULL
|
|
+*/
|
|
+static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
|
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
|
+ if( i==0 ){
|
|
+ /* the "word" column */
|
|
+ if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
|
|
+ }else if( i==1 ){
|
|
+ /* the "distance" column */
|
|
+ sqlite3_result_int(ctx, pCur->pStem->rCostX);
|
|
+ }else{
|
|
+ /* All other columns are NULL */
|
|
+ sqlite3_result_null(ctx);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The rowid.
|
|
+*/
|
|
+static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
|
|
+** that the cursor has nothing more to output.
|
|
+*/
|
|
+static int fuzzerEof(sqlite3_vtab_cursor *cur){
|
|
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
|
+ return pCur->rLimit<=(fuzzer_cost)0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Search for terms of these forms:
|
|
+**
|
|
+** (A) word MATCH $str
|
|
+** (B1) distance < $value
|
|
+** (B2) distance <= $value
|
|
+** (C) ruleid == $ruleid
|
|
+**
|
|
+** The distance< and distance<= are both treated as distance<=.
|
|
+** The query plan number is a bit vector:
|
|
+**
|
|
+** bit 1: Term of the form (A) found
|
|
+** bit 2: Term like (B1) or (B2) found
|
|
+** bit 3: Term like (C) found
|
|
+**
|
|
+** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set
|
|
+** then $value is in filter.argv[0] if bit-1 is clear and is in
|
|
+** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is
|
|
+** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
|
|
+** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
|
|
+** filter.argv[2] if both bit-1 and bit-2 are set.
|
|
+*/
|
|
+static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
|
+ int iPlan = 0;
|
|
+ int iDistTerm = -1;
|
|
+ int iRulesetTerm = -1;
|
|
+ int i;
|
|
+ int seenMatch = 0;
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+ double rCost = 1e12;
|
|
+
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->iColumn==0
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
|
|
+ seenMatch = 1;
|
|
+ }
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+ if( (iPlan & 1)==0
|
|
+ && pConstraint->iColumn==0
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
|
+ ){
|
|
+ iPlan |= 1;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ rCost /= 1e6;
|
|
+ }
|
|
+ if( (iPlan & 2)==0
|
|
+ && pConstraint->iColumn==1
|
|
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
|
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
|
|
+ ){
|
|
+ iPlan |= 2;
|
|
+ iDistTerm = i;
|
|
+ rCost /= 10.0;
|
|
+ }
|
|
+ if( (iPlan & 4)==0
|
|
+ && pConstraint->iColumn==2
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= 4;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ iRulesetTerm = i;
|
|
+ rCost /= 10.0;
|
|
+ }
|
|
+ }
|
|
+ if( iPlan & 2 ){
|
|
+ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
|
|
+ }
|
|
+ if( iPlan & 4 ){
|
|
+ int idx = 1;
|
|
+ if( iPlan & 1 ) idx++;
|
|
+ if( iPlan & 2 ) idx++;
|
|
+ pIdxInfo->aConstraintUsage[iRulesetTerm].argvIndex = idx;
|
|
+ }
|
|
+ pIdxInfo->idxNum = iPlan;
|
|
+ if( pIdxInfo->nOrderBy==1
|
|
+ && pIdxInfo->aOrderBy[0].iColumn==1
|
|
+ && pIdxInfo->aOrderBy[0].desc==0
|
|
+ ){
|
|
+ pIdxInfo->orderByConsumed = 1;
|
|
+ }
|
|
+ if( seenMatch && (iPlan&1)==0 ) rCost = 1e99;
|
|
+ pIdxInfo->estimatedCost = rCost;
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** A virtual table module that implements the "fuzzer".
|
|
+*/
|
|
+static const sqlite3_module fuzzerModule = {
|
|
+ 0, /* iVersion */
|
|
+ fuzzerConnect,
|
|
+ fuzzerConnect,
|
|
+ fuzzerBestIndex,
|
|
+ fuzzerDisconnect,
|
|
+ fuzzerDisconnect,
|
|
+ fuzzerOpen, /* xOpen - open a cursor */
|
|
+ fuzzerClose, /* xClose - close a cursor */
|
|
+ fuzzerFilter, /* xFilter - configure scan constraints */
|
|
+ fuzzerNext, /* xNext - advance a cursor */
|
|
+ fuzzerEof, /* xEof - check for end of scan */
|
|
+ fuzzerColumn, /* xColumn - read data */
|
|
+ fuzzerRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_fuzzer_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/icu.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/icu.c 2019-03-05 13:14:40.593116800 +0100
|
|
@@ -0,0 +1,563 @@
|
|
+/*
|
|
+** 2007 May 6
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
|
|
+**
|
|
+** This file implements an integration between the ICU library
|
|
+** ("International Components for Unicode", an open-source library
|
|
+** for handling unicode data) and SQLite. The integration uses
|
|
+** ICU to provide the following to SQLite:
|
|
+**
|
|
+** * An implementation of the SQL regexp() function (and hence REGEXP
|
|
+** operator) using the ICU uregex_XX() APIs.
|
|
+**
|
|
+** * Implementations of the SQL scalar upper() and lower() functions
|
|
+** for case mapping.
|
|
+**
|
|
+** * Integration of ICU and SQLite collation sequences.
|
|
+**
|
|
+** * An implementation of the LIKE operator that uses ICU to
|
|
+** provide case-independent matching.
|
|
+*/
|
|
+
|
|
+#if !defined(SQLITE_CORE) \
|
|
+ || defined(SQLITE_ENABLE_ICU) \
|
|
+ || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
|
+
|
|
+/* Include ICU headers */
|
|
+#include <unicode/utypes.h>
|
|
+#include <unicode/uregex.h>
|
|
+#include <unicode/ustring.h>
|
|
+#include <unicode/ucol.h>
|
|
+
|
|
+#include <assert.h>
|
|
+
|
|
+#ifndef SQLITE_CORE
|
|
+ #include "sqlite3ext.h"
|
|
+ SQLITE_EXTENSION_INIT1
|
|
+#else
|
|
+ #include "sqlite3.h"
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** This function is called when an ICU function called from within
|
|
+** the implementation of an SQL scalar function returns an error.
|
|
+**
|
|
+** The scalar function context passed as the first argument is
|
|
+** loaded with an error message based on the following two args.
|
|
+*/
|
|
+static void icuFunctionError(
|
|
+ sqlite3_context *pCtx, /* SQLite scalar function context */
|
|
+ const char *zName, /* Name of ICU function that failed */
|
|
+ UErrorCode e /* Error code returned by ICU function */
|
|
+){
|
|
+ char zBuf[128];
|
|
+ sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
|
|
+ zBuf[127] = '\0';
|
|
+ sqlite3_result_error(pCtx, zBuf, -1);
|
|
+}
|
|
+
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
|
+
|
|
+/*
|
|
+** Maximum length (in bytes) of the pattern in a LIKE or GLOB
|
|
+** operator.
|
|
+*/
|
|
+#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
|
|
+# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Version of sqlite3_free() that is always a function, never a macro.
|
|
+*/
|
|
+static void xFree(void *p){
|
|
+ sqlite3_free(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This lookup table is used to help decode the first byte of
|
|
+** a multi-byte UTF8 character. It is copied here from SQLite source
|
|
+** code file utf8.c.
|
|
+*/
|
|
+static const unsigned char icuUtf8Trans1[] = {
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
|
+};
|
|
+
|
|
+#define SQLITE_ICU_READ_UTF8(zIn, c) \
|
|
+ c = *(zIn++); \
|
|
+ if( c>=0xc0 ){ \
|
|
+ c = icuUtf8Trans1[c-0xc0]; \
|
|
+ while( (*zIn & 0xc0)==0x80 ){ \
|
|
+ c = (c<<6) + (0x3f & *(zIn++)); \
|
|
+ } \
|
|
+ }
|
|
+
|
|
+#define SQLITE_ICU_SKIP_UTF8(zIn) \
|
|
+ assert( *zIn ); \
|
|
+ if( *(zIn++)>=0xc0 ){ \
|
|
+ while( (*zIn & 0xc0)==0x80 ){zIn++;} \
|
|
+ }
|
|
+
|
|
+
|
|
+/*
|
|
+** Compare two UTF-8 strings for equality where the first string is
|
|
+** a "LIKE" expression. Return true (1) if they are the same and
|
|
+** false (0) if they are different.
|
|
+*/
|
|
+static int icuLikeCompare(
|
|
+ const uint8_t *zPattern, /* LIKE pattern */
|
|
+ const uint8_t *zString, /* The UTF-8 string to compare against */
|
|
+ const UChar32 uEsc /* The escape character */
|
|
+){
|
|
+ static const uint32_t MATCH_ONE = (uint32_t)'_';
|
|
+ static const uint32_t MATCH_ALL = (uint32_t)'%';
|
|
+
|
|
+ int prevEscape = 0; /* True if the previous character was uEsc */
|
|
+
|
|
+ while( 1 ){
|
|
+
|
|
+ /* Read (and consume) the next character from the input pattern. */
|
|
+ uint32_t uPattern;
|
|
+ SQLITE_ICU_READ_UTF8(zPattern, uPattern);
|
|
+ if( uPattern==0 ) break;
|
|
+
|
|
+ /* There are now 4 possibilities:
|
|
+ **
|
|
+ ** 1. uPattern is an unescaped match-all character "%",
|
|
+ ** 2. uPattern is an unescaped match-one character "_",
|
|
+ ** 3. uPattern is an unescaped escape character, or
|
|
+ ** 4. uPattern is to be handled as an ordinary character
|
|
+ */
|
|
+ if( !prevEscape && uPattern==MATCH_ALL ){
|
|
+ /* Case 1. */
|
|
+ uint8_t c;
|
|
+
|
|
+ /* Skip any MATCH_ALL or MATCH_ONE characters that follow a
|
|
+ ** MATCH_ALL. For each MATCH_ONE, skip one character in the
|
|
+ ** test string.
|
|
+ */
|
|
+ while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
|
|
+ if( c==MATCH_ONE ){
|
|
+ if( *zString==0 ) return 0;
|
|
+ SQLITE_ICU_SKIP_UTF8(zString);
|
|
+ }
|
|
+ zPattern++;
|
|
+ }
|
|
+
|
|
+ if( *zPattern==0 ) return 1;
|
|
+
|
|
+ while( *zString ){
|
|
+ if( icuLikeCompare(zPattern, zString, uEsc) ){
|
|
+ return 1;
|
|
+ }
|
|
+ SQLITE_ICU_SKIP_UTF8(zString);
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+ }else if( !prevEscape && uPattern==MATCH_ONE ){
|
|
+ /* Case 2. */
|
|
+ if( *zString==0 ) return 0;
|
|
+ SQLITE_ICU_SKIP_UTF8(zString);
|
|
+
|
|
+ }else if( !prevEscape && uPattern==(uint32_t)uEsc){
|
|
+ /* Case 3. */
|
|
+ prevEscape = 1;
|
|
+
|
|
+ }else{
|
|
+ /* Case 4. */
|
|
+ uint32_t uString;
|
|
+ SQLITE_ICU_READ_UTF8(zString, uString);
|
|
+ uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT);
|
|
+ uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT);
|
|
+ if( uString!=uPattern ){
|
|
+ return 0;
|
|
+ }
|
|
+ prevEscape = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return *zString==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the like() SQL function. This function implements
|
|
+** the build-in LIKE operator. The first argument to the function is the
|
|
+** pattern and the second argument is the string. So, the SQL statements:
|
|
+**
|
|
+** A LIKE B
|
|
+**
|
|
+** is implemented as like(B, A). If there is an escape character E,
|
|
+**
|
|
+** A LIKE B ESCAPE E
|
|
+**
|
|
+** is mapped to like(B, A, E).
|
|
+*/
|
|
+static void icuLikeFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *zA = sqlite3_value_text(argv[0]);
|
|
+ const unsigned char *zB = sqlite3_value_text(argv[1]);
|
|
+ UChar32 uEsc = 0;
|
|
+
|
|
+ /* Limit the length of the LIKE or GLOB pattern to avoid problems
|
|
+ ** of deep recursion and N*N behavior in patternCompare().
|
|
+ */
|
|
+ if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
|
|
+ sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+
|
|
+ if( argc==3 ){
|
|
+ /* The escape character string must consist of a single UTF-8 character.
|
|
+ ** Otherwise, return an error.
|
|
+ */
|
|
+ size_t nE= sqlite3_value_bytes(argv[2]);
|
|
+ const unsigned char *zE = sqlite3_value_text(argv[2]);
|
|
+ int i = 0;
|
|
+ if( zE==0 ) return;
|
|
+ U8_NEXT(zE, i, nE, uEsc);
|
|
+ if( i!=nE){
|
|
+ sqlite3_result_error(context,
|
|
+ "ESCAPE expression must be a single character", -1);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( zA && zB ){
|
|
+ sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc));
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Function to delete compiled regexp objects. Registered as
|
|
+** a destructor function with sqlite3_set_auxdata().
|
|
+*/
|
|
+static void icuRegexpDelete(void *p){
|
|
+ URegularExpression *pExpr = (URegularExpression *)p;
|
|
+ uregex_close(pExpr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of SQLite REGEXP operator. This scalar function takes
|
|
+** two arguments. The first is a regular expression pattern to compile
|
|
+** the second is a string to match against that pattern. If either
|
|
+** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
|
|
+** is 1 if the string matches the pattern, or 0 otherwise.
|
|
+**
|
|
+** SQLite maps the regexp() function to the regexp() operator such
|
|
+** that the following two are equivalent:
|
|
+**
|
|
+** zString REGEXP zPattern
|
|
+** regexp(zPattern, zString)
|
|
+**
|
|
+** Uses the following ICU regexp APIs:
|
|
+**
|
|
+** uregex_open()
|
|
+** uregex_matches()
|
|
+** uregex_close()
|
|
+*/
|
|
+static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
|
+ UErrorCode status = U_ZERO_ERROR;
|
|
+ URegularExpression *pExpr;
|
|
+ UBool res;
|
|
+ const UChar *zString = sqlite3_value_text16(apArg[1]);
|
|
+
|
|
+ (void)nArg; /* Unused parameter */
|
|
+
|
|
+ /* If the left hand side of the regexp operator is NULL,
|
|
+ ** then the result is also NULL.
|
|
+ */
|
|
+ if( !zString ){
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pExpr = sqlite3_get_auxdata(p, 0);
|
|
+ if( !pExpr ){
|
|
+ const UChar *zPattern = sqlite3_value_text16(apArg[0]);
|
|
+ if( !zPattern ){
|
|
+ return;
|
|
+ }
|
|
+ pExpr = uregex_open(zPattern, -1, 0, 0, &status);
|
|
+
|
|
+ if( U_SUCCESS(status) ){
|
|
+ sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
|
|
+ }else{
|
|
+ assert(!pExpr);
|
|
+ icuFunctionError(p, "uregex_open", status);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Configure the text that the regular expression operates on. */
|
|
+ uregex_setText(pExpr, zString, -1, &status);
|
|
+ if( !U_SUCCESS(status) ){
|
|
+ icuFunctionError(p, "uregex_setText", status);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Attempt the match */
|
|
+ res = uregex_matches(pExpr, 0, &status);
|
|
+ if( !U_SUCCESS(status) ){
|
|
+ icuFunctionError(p, "uregex_matches", status);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Set the text that the regular expression operates on to a NULL
|
|
+ ** pointer. This is not really necessary, but it is tidier than
|
|
+ ** leaving the regular expression object configured with an invalid
|
|
+ ** pointer after this function returns.
|
|
+ */
|
|
+ uregex_setText(pExpr, 0, 0, &status);
|
|
+
|
|
+ /* Return 1 or 0. */
|
|
+ sqlite3_result_int(p, res ? 1 : 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementations of scalar functions for case mapping - upper() and
|
|
+** lower(). Function upper() converts its input to upper-case (ABC).
|
|
+** Function lower() converts to lower-case (abc).
|
|
+**
|
|
+** ICU provides two types of case mapping, "general" case mapping and
|
|
+** "language specific". Refer to ICU documentation for the differences
|
|
+** between the two.
|
|
+**
|
|
+** To utilise "general" case mapping, the upper() or lower() scalar
|
|
+** functions are invoked with one argument:
|
|
+**
|
|
+** upper('ABC') -> 'abc'
|
|
+** lower('abc') -> 'ABC'
|
|
+**
|
|
+** To access ICU "language specific" case mapping, upper() or lower()
|
|
+** should be invoked with two arguments. The second argument is the name
|
|
+** of the locale to use. Passing an empty string ("") or SQL NULL value
|
|
+** as the second argument is the same as invoking the 1 argument version
|
|
+** of upper() or lower().
|
|
+**
|
|
+** lower('I', 'en_us') -> 'i'
|
|
+** lower('I', 'tr_tr') -> '\u131' (small dotless i)
|
|
+**
|
|
+** http://www.icu-project.org/userguide/posix.html#case_mappings
|
|
+*/
|
|
+static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
|
+ const UChar *zInput; /* Pointer to input string */
|
|
+ UChar *zOutput = 0; /* Pointer to output buffer */
|
|
+ size_t nInput; /* Size of utf-16 input string in bytes */
|
|
+ size_t nOut; /* Size of output buffer in bytes */
|
|
+ int cnt;
|
|
+ int bToUpper; /* True for toupper(), false for tolower() */
|
|
+ UErrorCode status;
|
|
+ const char *zLocale = 0;
|
|
+
|
|
+ assert(nArg==1 || nArg==2);
|
|
+ bToUpper = (sqlite3_user_data(p)!=0);
|
|
+ if( nArg==2 ){
|
|
+ zLocale = (const char *)sqlite3_value_text(apArg[1]);
|
|
+ }
|
|
+
|
|
+ zInput = sqlite3_value_text16(apArg[0]);
|
|
+ if( !zInput ){
|
|
+ return;
|
|
+ }
|
|
+ nOut = nInput = sqlite3_value_bytes16(apArg[0]);
|
|
+ if( nOut==0 ){
|
|
+ sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for(cnt=0; cnt<2; cnt++){
|
|
+ UChar *zNew = sqlite3_realloc(zOutput, nOut);
|
|
+ if( zNew==0 ){
|
|
+ sqlite3_free(zOutput);
|
|
+ sqlite3_result_error_nomem(p);
|
|
+ return;
|
|
+ }
|
|
+ zOutput = zNew;
|
|
+ status = U_ZERO_ERROR;
|
|
+ if( bToUpper ){
|
|
+ nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
|
+ }else{
|
|
+ nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
|
+ }
|
|
+
|
|
+ if( U_SUCCESS(status) ){
|
|
+ sqlite3_result_text16(p, zOutput, nOut, xFree);
|
|
+ }else if( status==U_BUFFER_OVERFLOW_ERROR ){
|
|
+ assert( cnt==0 );
|
|
+ continue;
|
|
+ }else{
|
|
+ icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ assert( 0 ); /* Unreachable */
|
|
+}
|
|
+
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
|
+
|
|
+/*
|
|
+** Collation sequence destructor function. The pCtx argument points to
|
|
+** a UCollator structure previously allocated using ucol_open().
|
|
+*/
|
|
+static void icuCollationDel(void *pCtx){
|
|
+ UCollator *p = (UCollator *)pCtx;
|
|
+ ucol_close(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Collation sequence comparison function. The pCtx argument points to
|
|
+** a UCollator structure previously allocated using ucol_open().
|
|
+*/
|
|
+static int icuCollationColl(
|
|
+ void *pCtx,
|
|
+ int nLeft,
|
|
+ const void *zLeft,
|
|
+ int nRight,
|
|
+ const void *zRight
|
|
+){
|
|
+ UCollationResult res;
|
|
+ UCollator *p = (UCollator *)pCtx;
|
|
+ res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2);
|
|
+ switch( res ){
|
|
+ case UCOL_LESS: return -1;
|
|
+ case UCOL_GREATER: return +1;
|
|
+ case UCOL_EQUAL: return 0;
|
|
+ }
|
|
+ assert(!"Unexpected return value from ucol_strcoll()");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the scalar function icu_load_collation().
|
|
+**
|
|
+** This scalar function is used to add ICU collation based collation
|
|
+** types to an SQLite database connection. It is intended to be called
|
|
+** as follows:
|
|
+**
|
|
+** SELECT icu_load_collation(<locale>, <collation-name>);
|
|
+**
|
|
+** Where <locale> is a string containing an ICU locale identifier (i.e.
|
|
+** "en_AU", "tr_TR" etc.) and <collation-name> is the name of the
|
|
+** collation sequence to create.
|
|
+*/
|
|
+static void icuLoadCollation(
|
|
+ sqlite3_context *p,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
|
|
+ UErrorCode status = U_ZERO_ERROR;
|
|
+ const char *zLocale; /* Locale identifier - (eg. "jp_JP") */
|
|
+ const char *zName; /* SQL Collation sequence name (eg. "japanese") */
|
|
+ UCollator *pUCollator; /* ICU library collation object */
|
|
+ int rc; /* Return code from sqlite3_create_collation_x() */
|
|
+
|
|
+ assert(nArg==2);
|
|
+ (void)nArg; /* Unused parameter */
|
|
+ zLocale = (const char *)sqlite3_value_text(apArg[0]);
|
|
+ zName = (const char *)sqlite3_value_text(apArg[1]);
|
|
+
|
|
+ if( !zLocale || !zName ){
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pUCollator = ucol_open(zLocale, &status);
|
|
+ if( !U_SUCCESS(status) ){
|
|
+ icuFunctionError(p, "ucol_open", status);
|
|
+ return;
|
|
+ }
|
|
+ assert(p);
|
|
+
|
|
+ rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
|
|
+ icuCollationColl, icuCollationDel
|
|
+ );
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ ucol_close(pUCollator);
|
|
+ sqlite3_result_error(p, "Error registering collation function", -1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register the ICU extension functions with database db.
|
|
+*/
|
|
+int sqlite3IcuInit(sqlite3 *db){
|
|
+ static const struct IcuScalar {
|
|
+ const char *zName; /* Function name */
|
|
+ unsigned char nArg; /* Number of arguments */
|
|
+ unsigned short enc; /* Optimal text encoding */
|
|
+ unsigned char iContext; /* sqlite3_user_data() context */
|
|
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
|
+ } scalars[] = {
|
|
+ {"icu_load_collation", 2, SQLITE_UTF8, 1, icuLoadCollation},
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
|
+ {"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
|
|
+ {"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
|
+ {"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
|
+ {"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
|
+ {"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
|
+ {"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
|
+ {"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
|
+ {"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
|
+ {"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
|
+ {"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
|
+ {"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
|
+ };
|
|
+ int rc = SQLITE_OK;
|
|
+ int i;
|
|
+
|
|
+ for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
|
+ const struct IcuScalar *p = &scalars[i];
|
|
+ rc = sqlite3_create_function_v2(
|
|
+ db, p->zName, p->nArg, p->enc,
|
|
+ p->iContext ? (void*)db : (void*)0,
|
|
+ p->xFunc, 0, 0, 0
|
|
+ );
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#if !SQLITE_CORE
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_icu_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi)
|
|
+ return sqlite3IcuInit(db);
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi)
|
|
+ return sqlite3IcuInit(db);
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/ieee754.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/ieee754.c 2019-05-14 16:22:31.241019600 +0200
|
|
@@ -0,0 +1,149 @@
|
|
+/*
|
|
+** 2013-04-17
|
|
+**
|
|
+** 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 for the exact display
|
|
+** and input of IEEE754 Binary64 floating-point numbers.
|
|
+**
|
|
+** ieee754(X)
|
|
+** ieee754(Y,Z)
|
|
+**
|
|
+** In the first form, the value X should be a floating-point number.
|
|
+** The function will return a string of the form 'ieee754(Y,Z)' where
|
|
+** Y and Z are integers such that X==Y*pow(2,Z).
|
|
+**
|
|
+** In the second form, Y and Z are integers which are the mantissa and
|
|
+** base-2 exponent of a new floating point number. The function returns
|
|
+** a floating-point value equal to Y*pow(2,Z).
|
|
+**
|
|
+** Examples:
|
|
+**
|
|
+** ieee754(2.0) -> 'ieee754(2,0)'
|
|
+** ieee754(45.25) -> 'ieee754(181,-2)'
|
|
+** ieee754(2, 0) -> 2.0
|
|
+** ieee754(181, -2) -> 45.25
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Implementation of the ieee754() function
|
|
+*/
|
|
+static void ieee754func(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ if( argc==1 ){
|
|
+ sqlite3_int64 m, a;
|
|
+ double r;
|
|
+ int e;
|
|
+ int isNeg;
|
|
+ char zResult[100];
|
|
+ assert( sizeof(m)==sizeof(r) );
|
|
+ if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
|
|
+ r = sqlite3_value_double(argv[0]);
|
|
+ if( r<0.0 ){
|
|
+ isNeg = 1;
|
|
+ r = -r;
|
|
+ }else{
|
|
+ isNeg = 0;
|
|
+ }
|
|
+ memcpy(&a,&r,sizeof(a));
|
|
+ if( a==0 ){
|
|
+ e = 0;
|
|
+ m = 0;
|
|
+ }else{
|
|
+ e = a>>52;
|
|
+ m = a & ((((sqlite3_int64)1)<<52)-1);
|
|
+ m |= ((sqlite3_int64)1)<<52;
|
|
+ while( e<1075 && m>0 && (m&1)==0 ){
|
|
+ m >>= 1;
|
|
+ e++;
|
|
+ }
|
|
+ if( isNeg ) m = -m;
|
|
+ }
|
|
+ sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
|
|
+ m, e-1075);
|
|
+ sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
|
|
+ }else if( argc==2 ){
|
|
+ sqlite3_int64 m, e, a;
|
|
+ double r;
|
|
+ int isNeg = 0;
|
|
+ m = sqlite3_value_int64(argv[0]);
|
|
+ e = sqlite3_value_int64(argv[1]);
|
|
+ if( m<0 ){
|
|
+ isNeg = 1;
|
|
+ m = -m;
|
|
+ if( m<0 ) return;
|
|
+ }else if( m==0 && e>1000 && e<1000 ){
|
|
+ sqlite3_result_double(context, 0.0);
|
|
+ return;
|
|
+ }
|
|
+ while( (m>>32)&0xffe00000 ){
|
|
+ m >>= 1;
|
|
+ e++;
|
|
+ }
|
|
+ while( m!=0 && ((m>>32)&0xfff00000)==0 ){
|
|
+ m <<= 1;
|
|
+ e--;
|
|
+ }
|
|
+ e += 1075;
|
|
+ if( e<0 ) e = m = 0;
|
|
+ if( e>0x7ff ) e = 0x7ff;
|
|
+ a = m & ((((sqlite3_int64)1)<<52)-1);
|
|
+ a |= e<<52;
|
|
+ if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
|
|
+ memcpy(&r, &a, sizeof(r));
|
|
+ sqlite3_result_double(context, r);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_ieee_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
|
+ ieee754func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
|
+ ieee754func, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
|
+ ieee754func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
|
+ ieee754func, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/json1.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/json1.c 2019-05-14 16:22:31.242972300 +0200
|
|
@@ -0,0 +1,2540 @@
|
|
+/*
|
|
+** 2015-08-12
|
|
+**
|
|
+** 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 JSON functions. The interface is
|
|
+** modeled after MySQL JSON functions:
|
|
+**
|
|
+** https://dev.mysql.com/doc/refman/5.7/en/json.html
|
|
+**
|
|
+** For the time being, all JSON is stored as pure text. (We might add
|
|
+** a JSONB type in the future which stores a binary encoding of JSON in
|
|
+** a BLOB, but there is no support for JSONB in the current implementation.
|
|
+** This implementation parses JSON text at 250 MB/s, so it is hard to see
|
|
+** how JSONB might improve on that.)
|
|
+*/
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdarg.h>
|
|
+
|
|
+/* Mark a function parameter as unused, to suppress nuisance compiler
|
|
+** warnings. */
|
|
+#ifndef UNUSED_PARAM
|
|
+# define UNUSED_PARAM(X) (void)(X)
|
|
+#endif
|
|
+
|
|
+#ifndef LARGEST_INT64
|
|
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
|
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
|
|
+** to pass signed char values.
|
|
+*/
|
|
+#ifdef sqlite3Isdigit
|
|
+ /* Use the SQLite core versions if this routine is part of the
|
|
+ ** SQLite amalgamation */
|
|
+# define safe_isdigit(x) sqlite3Isdigit(x)
|
|
+# define safe_isalnum(x) sqlite3Isalnum(x)
|
|
+# define safe_isxdigit(x) sqlite3Isxdigit(x)
|
|
+#else
|
|
+ /* Use the standard library for separate compilation */
|
|
+#include <ctype.h> /* amalgamator: keep */
|
|
+# define safe_isdigit(x) isdigit((unsigned char)(x))
|
|
+# define safe_isalnum(x) isalnum((unsigned char)(x))
|
|
+# define safe_isxdigit(x) isxdigit((unsigned char)(x))
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Growing our own isspace() routine this way is twice as fast as
|
|
+** the library isspace() function, resulting in a 7% overall performance
|
|
+** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
|
|
+*/
|
|
+static const char jsonIsSpace[] = {
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+};
|
|
+#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
|
|
+
|
|
+#ifndef SQLITE_AMALGAMATION
|
|
+ /* Unsigned integer types. These are already defined in the sqliteInt.h,
|
|
+ ** but the definitions need to be repeated for separate compilation. */
|
|
+ typedef unsigned int u32;
|
|
+ typedef unsigned short int u16;
|
|
+ typedef unsigned char u8;
|
|
+#endif
|
|
+
|
|
+/* Objects */
|
|
+typedef struct JsonString JsonString;
|
|
+typedef struct JsonNode JsonNode;
|
|
+typedef struct JsonParse JsonParse;
|
|
+
|
|
+/* An instance of this object represents a JSON string
|
|
+** under construction. Really, this is a generic string accumulator
|
|
+** that can be and is used to create strings other than JSON.
|
|
+*/
|
|
+struct JsonString {
|
|
+ sqlite3_context *pCtx; /* Function context - put error messages here */
|
|
+ char *zBuf; /* Append JSON content here */
|
|
+ size_t nAlloc; /* Bytes of storage available in zBuf[] */
|
|
+ size_t nUsed; /* Bytes of zBuf[] currently used */
|
|
+ u8 bStatic; /* True if zBuf is static space */
|
|
+ u8 bErr; /* True if an error has been encountered */
|
|
+ char zSpace[100]; /* Initial static space */
|
|
+};
|
|
+
|
|
+/* JSON type values
|
|
+*/
|
|
+#define JSON_NULL 0
|
|
+#define JSON_TRUE 1
|
|
+#define JSON_FALSE 2
|
|
+#define JSON_INT 3
|
|
+#define JSON_REAL 4
|
|
+#define JSON_STRING 5
|
|
+#define JSON_ARRAY 6
|
|
+#define JSON_OBJECT 7
|
|
+
|
|
+/* The "subtype" set for JSON values */
|
|
+#define JSON_SUBTYPE 74 /* Ascii for "J" */
|
|
+
|
|
+/*
|
|
+** Names of the various JSON types:
|
|
+*/
|
|
+static const char jsonType[][8] = {
|
|
+ "null", "true", "false", "integer", "real", "text", "array", "object"
|
|
+};
|
|
+
|
|
+/* Bit values for the JsonNode.jnFlag field
|
|
+*/
|
|
+#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
|
|
+#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
|
|
+#define JNODE_REMOVE 0x04 /* Do not output */
|
|
+#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
|
|
+#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
|
|
+#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
|
|
+#define JNODE_LABEL 0x40 /* Is a label of an object */
|
|
+
|
|
+
|
|
+/* A single node of parsed JSON
|
|
+*/
|
|
+struct JsonNode {
|
|
+ u8 eType; /* One of the JSON_ type values */
|
|
+ u8 jnFlags; /* JNODE flags */
|
|
+ u32 n; /* Bytes of content, or number of sub-nodes */
|
|
+ union {
|
|
+ const char *zJContent; /* Content for INT, REAL, and STRING */
|
|
+ u32 iAppend; /* More terms for ARRAY and OBJECT */
|
|
+ u32 iKey; /* Key for ARRAY objects in json_tree() */
|
|
+ u32 iReplace; /* Replacement content for JNODE_REPLACE */
|
|
+ JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */
|
|
+ } u;
|
|
+};
|
|
+
|
|
+/* A completely parsed JSON string
|
|
+*/
|
|
+struct JsonParse {
|
|
+ u32 nNode; /* Number of slots of aNode[] used */
|
|
+ u32 nAlloc; /* Number of slots of aNode[] allocated */
|
|
+ JsonNode *aNode; /* Array of nodes containing the parse */
|
|
+ const char *zJson; /* Original JSON string */
|
|
+ u32 *aUp; /* Index of parent of each node */
|
|
+ u8 oom; /* Set to true if out of memory */
|
|
+ u8 nErr; /* Number of errors seen */
|
|
+ u16 iDepth; /* Nesting depth */
|
|
+ int nJson; /* Length of the zJson string in bytes */
|
|
+ u32 iHold; /* Replace cache line with the lowest iHold value */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Maximum nesting depth of JSON for this implementation.
|
|
+**
|
|
+** This limit is needed to avoid a stack overflow in the recursive
|
|
+** descent parser. A depth of 2000 is far deeper than any sane JSON
|
|
+** should go.
|
|
+*/
|
|
+#define JSON_MAX_DEPTH 2000
|
|
+
|
|
+/**************************************************************************
|
|
+** Utility routines for dealing with JsonString objects
|
|
+**************************************************************************/
|
|
+
|
|
+/* Set the JsonString object to an empty string
|
|
+*/
|
|
+static void jsonZero(JsonString *p){
|
|
+ p->zBuf = p->zSpace;
|
|
+ p->nAlloc = sizeof(p->zSpace);
|
|
+ p->nUsed = 0;
|
|
+ p->bStatic = 1;
|
|
+}
|
|
+
|
|
+/* Initialize the JsonString object
|
|
+*/
|
|
+static void jsonInit(JsonString *p, sqlite3_context *pCtx){
|
|
+ p->pCtx = pCtx;
|
|
+ p->bErr = 0;
|
|
+ jsonZero(p);
|
|
+}
|
|
+
|
|
+
|
|
+/* Free all allocated memory and reset the JsonString object back to its
|
|
+** initial state.
|
|
+*/
|
|
+static void jsonReset(JsonString *p){
|
|
+ if( !p->bStatic ) sqlite3_free(p->zBuf);
|
|
+ jsonZero(p);
|
|
+}
|
|
+
|
|
+
|
|
+/* Report an out-of-memory (OOM) condition
|
|
+*/
|
|
+static void jsonOom(JsonString *p){
|
|
+ p->bErr = 1;
|
|
+ sqlite3_result_error_nomem(p->pCtx);
|
|
+ jsonReset(p);
|
|
+}
|
|
+
|
|
+/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
|
|
+** Return zero on success. Return non-zero on an OOM error
|
|
+*/
|
|
+static int jsonGrow(JsonString *p, u32 N){
|
|
+ size_t nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
|
|
+ char *zNew;
|
|
+ if( p->bStatic ){
|
|
+ if( p->bErr ) return 1;
|
|
+ zNew = sqlite3_malloc(nTotal);
|
|
+ if( zNew==0 ){
|
|
+ jsonOom(p);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ memcpy(zNew, p->zBuf, (size_t)p->nUsed);
|
|
+ p->zBuf = zNew;
|
|
+ p->bStatic = 0;
|
|
+ }else{
|
|
+ zNew = sqlite3_realloc(p->zBuf, nTotal);
|
|
+ if( zNew==0 ){
|
|
+ jsonOom(p);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ p->zBuf = zNew;
|
|
+ }
|
|
+ p->nAlloc = nTotal;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Append N bytes from zIn onto the end of the JsonString string.
|
|
+*/
|
|
+static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
|
|
+ if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
|
|
+ memcpy(p->zBuf+p->nUsed, zIn, N);
|
|
+ p->nUsed += N;
|
|
+}
|
|
+
|
|
+/* Append formatted text (not to exceed N bytes) to the JsonString.
|
|
+*/
|
|
+static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
|
|
+ va_start(ap, zFormat);
|
|
+ sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
|
|
+ va_end(ap);
|
|
+ p->nUsed += (int)strlen(p->zBuf+p->nUsed);
|
|
+}
|
|
+
|
|
+/* Append a single character
|
|
+*/
|
|
+static void jsonAppendChar(JsonString *p, char c){
|
|
+ if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = c;
|
|
+}
|
|
+
|
|
+/* Append a comma separator to the output buffer, if the previous
|
|
+** character is not '[' or '{'.
|
|
+*/
|
|
+static void jsonAppendSeparator(JsonString *p){
|
|
+ char c;
|
|
+ if( p->nUsed==0 ) return;
|
|
+ c = p->zBuf[p->nUsed-1];
|
|
+ if( c!='[' && c!='{' ) jsonAppendChar(p, ',');
|
|
+}
|
|
+
|
|
+/* Append the N-byte string in zIn to the end of the JsonString string
|
|
+** under construction. Enclose the string in "..." and escape
|
|
+** any double-quotes or backslash characters contained within the
|
|
+** string.
|
|
+*/
|
|
+static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
|
|
+ u32 i;
|
|
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = '"';
|
|
+ for(i=0; i<N; i++){
|
|
+ unsigned char c = ((unsigned const char*)zIn)[i];
|
|
+ if( c=='"' || c=='\\' ){
|
|
+ json_simple_escape:
|
|
+ if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = '\\';
|
|
+ }else if( c<=0x1f ){
|
|
+ static const char aSpecial[] = {
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
+ };
|
|
+ assert( sizeof(aSpecial)==32 );
|
|
+ assert( aSpecial['\b']=='b' );
|
|
+ assert( aSpecial['\f']=='f' );
|
|
+ assert( aSpecial['\n']=='n' );
|
|
+ assert( aSpecial['\r']=='r' );
|
|
+ assert( aSpecial['\t']=='t' );
|
|
+ if( aSpecial[c] ){
|
|
+ c = aSpecial[c];
|
|
+ goto json_simple_escape;
|
|
+ }
|
|
+ if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = '\\';
|
|
+ p->zBuf[p->nUsed++] = 'u';
|
|
+ p->zBuf[p->nUsed++] = '0';
|
|
+ p->zBuf[p->nUsed++] = '0';
|
|
+ p->zBuf[p->nUsed++] = '0' + (c>>4);
|
|
+ c = "0123456789abcdef"[c&0xf];
|
|
+ }
|
|
+ p->zBuf[p->nUsed++] = c;
|
|
+ }
|
|
+ p->zBuf[p->nUsed++] = '"';
|
|
+ assert( p->nUsed<p->nAlloc );
|
|
+}
|
|
+
|
|
+/*
|
|
+** Append a function parameter value to the JSON string under
|
|
+** construction.
|
|
+*/
|
|
+static void jsonAppendValue(
|
|
+ JsonString *p, /* Append to this JSON string */
|
|
+ sqlite3_value *pValue /* Value to append */
|
|
+){
|
|
+ switch( sqlite3_value_type(pValue) ){
|
|
+ case SQLITE_NULL: {
|
|
+ jsonAppendRaw(p, "null", 4);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INTEGER:
|
|
+ case SQLITE_FLOAT: {
|
|
+ const char *z = (const char*)sqlite3_value_text(pValue);
|
|
+ u32 n = (u32)sqlite3_value_bytes(pValue);
|
|
+ jsonAppendRaw(p, z, n);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ const char *z = (const char*)sqlite3_value_text(pValue);
|
|
+ u32 n = (u32)sqlite3_value_bytes(pValue);
|
|
+ if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
|
|
+ jsonAppendRaw(p, z, n);
|
|
+ }else{
|
|
+ jsonAppendString(p, z, n);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ if( p->bErr==0 ){
|
|
+ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
|
|
+ p->bErr = 2;
|
|
+ jsonReset(p);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/* Make the JSON in p the result of the SQL function.
|
|
+*/
|
|
+static void jsonResult(JsonString *p){
|
|
+ if( p->bErr==0 ){
|
|
+ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
|
|
+ p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
|
|
+ SQLITE_UTF8);
|
|
+ jsonZero(p);
|
|
+ }
|
|
+ assert( p->bStatic );
|
|
+}
|
|
+
|
|
+/**************************************************************************
|
|
+** Utility routines for dealing with JsonNode and JsonParse objects
|
|
+**************************************************************************/
|
|
+
|
|
+/*
|
|
+** Return the number of consecutive JsonNode slots need to represent
|
|
+** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
|
|
+** OBJECT types, the number might be larger.
|
|
+**
|
|
+** Appended elements are not counted. The value returned is the number
|
|
+** by which the JsonNode counter should increment in order to go to the
|
|
+** next peer value.
|
|
+*/
|
|
+static u32 jsonNodeSize(JsonNode *pNode){
|
|
+ return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reclaim all memory allocated by a JsonParse object. But do not
|
|
+** delete the JsonParse object itself.
|
|
+*/
|
|
+static void jsonParseReset(JsonParse *pParse){
|
|
+ sqlite3_free(pParse->aNode);
|
|
+ pParse->aNode = 0;
|
|
+ pParse->nNode = 0;
|
|
+ pParse->nAlloc = 0;
|
|
+ sqlite3_free(pParse->aUp);
|
|
+ pParse->aUp = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free a JsonParse object that was obtained from sqlite3_malloc().
|
|
+*/
|
|
+static void jsonParseFree(JsonParse *pParse){
|
|
+ jsonParseReset(pParse);
|
|
+ sqlite3_free(pParse);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert the JsonNode pNode into a pure JSON string and
|
|
+** append to pOut. Subsubstructure is also included. Return
|
|
+** the number of JsonNode objects that are encoded.
|
|
+*/
|
|
+static void jsonRenderNode(
|
|
+ JsonNode *pNode, /* The node to render */
|
|
+ JsonString *pOut, /* Write JSON here */
|
|
+ sqlite3_value **aReplace /* Replacement values */
|
|
+){
|
|
+ if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
|
|
+ if( pNode->jnFlags & JNODE_REPLACE ){
|
|
+ jsonAppendValue(pOut, aReplace[pNode->u.iReplace]);
|
|
+ return;
|
|
+ }
|
|
+ pNode = pNode->u.pPatch;
|
|
+ }
|
|
+ switch( pNode->eType ){
|
|
+ default: {
|
|
+ assert( pNode->eType==JSON_NULL );
|
|
+ jsonAppendRaw(pOut, "null", 4);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_TRUE: {
|
|
+ jsonAppendRaw(pOut, "true", 4);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_FALSE: {
|
|
+ jsonAppendRaw(pOut, "false", 5);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_STRING: {
|
|
+ if( pNode->jnFlags & JNODE_RAW ){
|
|
+ jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
|
|
+ break;
|
|
+ }
|
|
+ /* Fall through into the next case */
|
|
+ }
|
|
+ case JSON_REAL:
|
|
+ case JSON_INT: {
|
|
+ jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_ARRAY: {
|
|
+ u32 j = 1;
|
|
+ jsonAppendChar(pOut, '[');
|
|
+ for(;;){
|
|
+ while( j<=pNode->n ){
|
|
+ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){
|
|
+ jsonAppendSeparator(pOut);
|
|
+ jsonRenderNode(&pNode[j], pOut, aReplace);
|
|
+ }
|
|
+ j += jsonNodeSize(&pNode[j]);
|
|
+ }
|
|
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ pNode = &pNode[pNode->u.iAppend];
|
|
+ j = 1;
|
|
+ }
|
|
+ jsonAppendChar(pOut, ']');
|
|
+ break;
|
|
+ }
|
|
+ case JSON_OBJECT: {
|
|
+ u32 j = 1;
|
|
+ jsonAppendChar(pOut, '{');
|
|
+ for(;;){
|
|
+ while( j<=pNode->n ){
|
|
+ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
|
|
+ jsonAppendSeparator(pOut);
|
|
+ jsonRenderNode(&pNode[j], pOut, aReplace);
|
|
+ jsonAppendChar(pOut, ':');
|
|
+ jsonRenderNode(&pNode[j+1], pOut, aReplace);
|
|
+ }
|
|
+ j += 1 + jsonNodeSize(&pNode[j+1]);
|
|
+ }
|
|
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ pNode = &pNode[pNode->u.iAppend];
|
|
+ j = 1;
|
|
+ }
|
|
+ jsonAppendChar(pOut, '}');
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a JsonNode and all its descendents as a JSON string.
|
|
+*/
|
|
+static void jsonReturnJson(
|
|
+ JsonNode *pNode, /* Node to return */
|
|
+ sqlite3_context *pCtx, /* Return value for this function */
|
|
+ sqlite3_value **aReplace /* Array of replacement values */
|
|
+){
|
|
+ JsonString s;
|
|
+ jsonInit(&s, pCtx);
|
|
+ jsonRenderNode(pNode, &s, aReplace);
|
|
+ jsonResult(&s);
|
|
+ sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Make the JsonNode the return value of the function.
|
|
+*/
|
|
+static void jsonReturn(
|
|
+ JsonNode *pNode, /* Node to return */
|
|
+ sqlite3_context *pCtx, /* Return value for this function */
|
|
+ sqlite3_value **aReplace /* Array of replacement values */
|
|
+){
|
|
+ switch( pNode->eType ){
|
|
+ default: {
|
|
+ assert( pNode->eType==JSON_NULL );
|
|
+ sqlite3_result_null(pCtx);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_TRUE: {
|
|
+ sqlite3_result_int(pCtx, 1);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_FALSE: {
|
|
+ sqlite3_result_int(pCtx, 0);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_INT: {
|
|
+ sqlite3_int64 i = 0;
|
|
+ const char *z = pNode->u.zJContent;
|
|
+ if( z[0]=='-' ){ z++; }
|
|
+ while( z[0]>='0' && z[0]<='9' ){
|
|
+ unsigned v = *(z++) - '0';
|
|
+ if( i>=LARGEST_INT64/10 ){
|
|
+ if( i>LARGEST_INT64/10 ) goto int_as_real;
|
|
+ if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
|
|
+ if( v==9 ) goto int_as_real;
|
|
+ if( v==8 ){
|
|
+ if( pNode->u.zJContent[0]=='-' ){
|
|
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
|
|
+ goto int_done;
|
|
+ }else{
|
|
+ goto int_as_real;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ i = i*10 + v;
|
|
+ }
|
|
+ if( pNode->u.zJContent[0]=='-' ){ i = -i; }
|
|
+ sqlite3_result_int64(pCtx, i);
|
|
+ int_done:
|
|
+ break;
|
|
+ int_as_real: /* fall through to real */;
|
|
+ }
|
|
+ case JSON_REAL: {
|
|
+ double r;
|
|
+#ifdef SQLITE_AMALGAMATION
|
|
+ const char *z = pNode->u.zJContent;
|
|
+ sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
|
|
+#else
|
|
+ r = strtod(pNode->u.zJContent, 0);
|
|
+#endif
|
|
+ sqlite3_result_double(pCtx, r);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_STRING: {
|
|
+#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
|
|
+ ** json_insert() and json_replace() and those routines do not
|
|
+ ** call jsonReturn() */
|
|
+ if( pNode->jnFlags & JNODE_RAW ){
|
|
+ sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
|
|
+ SQLITE_TRANSIENT);
|
|
+ }else
|
|
+#endif
|
|
+ assert( (pNode->jnFlags & JNODE_RAW)==0 );
|
|
+ if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
|
|
+ /* JSON formatted without any backslash-escapes */
|
|
+ sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
|
|
+ SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ /* Translate JSON formatted string into raw text */
|
|
+ u32 i;
|
|
+ u32 n = pNode->n;
|
|
+ const char *z = pNode->u.zJContent;
|
|
+ char *zOut;
|
|
+ u32 j;
|
|
+ zOut = sqlite3_malloc( n+1 );
|
|
+ if( zOut==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ break;
|
|
+ }
|
|
+ for(i=1, j=0; i<n-1; i++){
|
|
+ char c = z[i];
|
|
+ if( c!='\\' ){
|
|
+ zOut[j++] = c;
|
|
+ }else{
|
|
+ c = z[++i];
|
|
+ if( c=='u' ){
|
|
+ u32 v = 0, k;
|
|
+ for(k=0; k<4; i++, k++){
|
|
+ assert( i<n-2 );
|
|
+ c = z[i+1];
|
|
+ assert( safe_isxdigit(c) );
|
|
+ if( c<='9' ) v = v*16 + c - '0';
|
|
+ else if( c<='F' ) v = v*16 + c - 'A' + 10;
|
|
+ else v = v*16 + c - 'a' + 10;
|
|
+ }
|
|
+ if( v==0 ) break;
|
|
+ if( v<=0x7f ){
|
|
+ zOut[j++] = (char)v;
|
|
+ }else if( v<=0x7ff ){
|
|
+ zOut[j++] = (char)(0xc0 | (v>>6));
|
|
+ zOut[j++] = 0x80 | (v&0x3f);
|
|
+ }else{
|
|
+ zOut[j++] = (char)(0xe0 | (v>>12));
|
|
+ zOut[j++] = 0x80 | ((v>>6)&0x3f);
|
|
+ zOut[j++] = 0x80 | (v&0x3f);
|
|
+ }
|
|
+ }else{
|
|
+ if( c=='b' ){
|
|
+ c = '\b';
|
|
+ }else if( c=='f' ){
|
|
+ c = '\f';
|
|
+ }else if( c=='n' ){
|
|
+ c = '\n';
|
|
+ }else if( c=='r' ){
|
|
+ c = '\r';
|
|
+ }else if( c=='t' ){
|
|
+ c = '\t';
|
|
+ }
|
|
+ zOut[j++] = c;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ zOut[j] = 0;
|
|
+ sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JSON_ARRAY:
|
|
+ case JSON_OBJECT: {
|
|
+ jsonReturnJson(pNode, pCtx, aReplace);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Forward reference */
|
|
+static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
|
|
+
|
|
+/*
|
|
+** A macro to hint to the compiler that a function should not be
|
|
+** inlined.
|
|
+*/
|
|
+#if defined(__GNUC__)
|
|
+# define JSON_NOINLINE __attribute__((noinline))
|
|
+#elif defined(_MSC_VER) && _MSC_VER>=1310
|
|
+# define JSON_NOINLINE __declspec(noinline)
|
|
+#else
|
|
+# define JSON_NOINLINE
|
|
+#endif
|
|
+
|
|
+
|
|
+static JSON_NOINLINE int jsonParseAddNodeExpand(
|
|
+ JsonParse *pParse, /* Append the node to this object */
|
|
+ u32 eType, /* Node type */
|
|
+ u32 n, /* Content size or sub-node count */
|
|
+ const char *zContent /* Content */
|
|
+){
|
|
+ u32 nNew;
|
|
+ JsonNode *pNew;
|
|
+ assert( pParse->nNode>=pParse->nAlloc );
|
|
+ if( pParse->oom ) return -1;
|
|
+ nNew = pParse->nAlloc*2 + 10;
|
|
+ pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew);
|
|
+ if( pNew==0 ){
|
|
+ pParse->oom = 1;
|
|
+ return -1;
|
|
+ }
|
|
+ pParse->nAlloc = nNew;
|
|
+ pParse->aNode = pNew;
|
|
+ assert( pParse->nNode<pParse->nAlloc );
|
|
+ return jsonParseAddNode(pParse, eType, n, zContent);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create a new JsonNode instance based on the arguments and append that
|
|
+** instance to the JsonParse. Return the index in pParse->aNode[] of the
|
|
+** new node, or -1 if a memory allocation fails.
|
|
+*/
|
|
+static int jsonParseAddNode(
|
|
+ JsonParse *pParse, /* Append the node to this object */
|
|
+ u32 eType, /* Node type */
|
|
+ u32 n, /* Content size or sub-node count */
|
|
+ const char *zContent /* Content */
|
|
+){
|
|
+ JsonNode *p;
|
|
+ if( pParse->nNode>=pParse->nAlloc ){
|
|
+ return jsonParseAddNodeExpand(pParse, eType, n, zContent);
|
|
+ }
|
|
+ p = &pParse->aNode[pParse->nNode];
|
|
+ p->eType = (u8)eType;
|
|
+ p->jnFlags = 0;
|
|
+ p->n = n;
|
|
+ p->u.zJContent = zContent;
|
|
+ return pParse->nNode++;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true if z[] begins with 4 (or more) hexadecimal digits
|
|
+*/
|
|
+static int jsonIs4Hex(const char *z){
|
|
+ int i;
|
|
+ for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parse a single JSON value which begins at pParse->zJson[i]. Return the
|
|
+** index of the first character past the end of the value parsed.
|
|
+**
|
|
+** Return negative for a syntax error. Special cases: return -2 if the
|
|
+** first non-whitespace character is '}' and return -3 if the first
|
|
+** non-whitespace character is ']'.
|
|
+*/
|
|
+static int jsonParseValue(JsonParse *pParse, u32 i){
|
|
+ char c;
|
|
+ u32 j;
|
|
+ int iThis;
|
|
+ int x;
|
|
+ JsonNode *pNode;
|
|
+ const char *z = pParse->zJson;
|
|
+ while( safe_isspace(z[i]) ){ i++; }
|
|
+ if( (c = z[i])=='{' ){
|
|
+ /* Parse object */
|
|
+ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
|
+ if( iThis<0 ) return -1;
|
|
+ for(j=i+1;;j++){
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
|
|
+ x = jsonParseValue(pParse, j);
|
|
+ if( x<0 ){
|
|
+ pParse->iDepth--;
|
|
+ if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
|
+ return -1;
|
|
+ }
|
|
+ if( pParse->oom ) return -1;
|
|
+ pNode = &pParse->aNode[pParse->nNode-1];
|
|
+ if( pNode->eType!=JSON_STRING ) return -1;
|
|
+ pNode->jnFlags |= JNODE_LABEL;
|
|
+ j = x;
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ if( z[j]!=':' ) return -1;
|
|
+ j++;
|
|
+ x = jsonParseValue(pParse, j);
|
|
+ pParse->iDepth--;
|
|
+ if( x<0 ) return -1;
|
|
+ j = x;
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ c = z[j];
|
|
+ if( c==',' ) continue;
|
|
+ if( c!='}' ) return -1;
|
|
+ break;
|
|
+ }
|
|
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
|
|
+ return j+1;
|
|
+ }else if( c=='[' ){
|
|
+ /* Parse array */
|
|
+ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
|
+ if( iThis<0 ) return -1;
|
|
+ for(j=i+1;;j++){
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
|
|
+ x = jsonParseValue(pParse, j);
|
|
+ pParse->iDepth--;
|
|
+ if( x<0 ){
|
|
+ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
|
+ return -1;
|
|
+ }
|
|
+ j = x;
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ c = z[j];
|
|
+ if( c==',' ) continue;
|
|
+ if( c!=']' ) return -1;
|
|
+ break;
|
|
+ }
|
|
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
|
|
+ return j+1;
|
|
+ }else if( c=='"' ){
|
|
+ /* Parse string */
|
|
+ u8 jnFlags = 0;
|
|
+ j = i+1;
|
|
+ for(;;){
|
|
+ c = z[j];
|
|
+ if( (c & ~0x1f)==0 ){
|
|
+ /* Control characters are not allowed in strings */
|
|
+ return -1;
|
|
+ }
|
|
+ if( c=='\\' ){
|
|
+ c = z[++j];
|
|
+ if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|
|
+ || c=='n' || c=='r' || c=='t'
|
|
+ || (c=='u' && jsonIs4Hex(z+j+1)) ){
|
|
+ jnFlags = JNODE_ESCAPE;
|
|
+ }else{
|
|
+ return -1;
|
|
+ }
|
|
+ }else if( c=='"' ){
|
|
+ break;
|
|
+ }
|
|
+ j++;
|
|
+ }
|
|
+ jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]);
|
|
+ if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
|
|
+ return j+1;
|
|
+ }else if( c=='n'
|
|
+ && strncmp(z+i,"null",4)==0
|
|
+ && !safe_isalnum(z[i+4]) ){
|
|
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
|
+ return i+4;
|
|
+ }else if( c=='t'
|
|
+ && strncmp(z+i,"true",4)==0
|
|
+ && !safe_isalnum(z[i+4]) ){
|
|
+ jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
|
+ return i+4;
|
|
+ }else if( c=='f'
|
|
+ && strncmp(z+i,"false",5)==0
|
|
+ && !safe_isalnum(z[i+5]) ){
|
|
+ jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
|
|
+ return i+5;
|
|
+ }else if( c=='-' || (c>='0' && c<='9') ){
|
|
+ /* Parse number */
|
|
+ u8 seenDP = 0;
|
|
+ u8 seenE = 0;
|
|
+ assert( '-' < '0' );
|
|
+ if( c<='0' ){
|
|
+ j = c=='-' ? i+1 : i;
|
|
+ if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1;
|
|
+ }
|
|
+ j = i+1;
|
|
+ for(;; j++){
|
|
+ c = z[j];
|
|
+ if( c>='0' && c<='9' ) continue;
|
|
+ if( c=='.' ){
|
|
+ if( z[j-1]=='-' ) return -1;
|
|
+ if( seenDP ) return -1;
|
|
+ seenDP = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if( c=='e' || c=='E' ){
|
|
+ if( z[j-1]<'0' ) return -1;
|
|
+ if( seenE ) return -1;
|
|
+ seenDP = seenE = 1;
|
|
+ c = z[j+1];
|
|
+ if( c=='+' || c=='-' ){
|
|
+ j++;
|
|
+ c = z[j+1];
|
|
+ }
|
|
+ if( c<'0' || c>'9' ) return -1;
|
|
+ continue;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if( z[j-1]<'0' ) return -1;
|
|
+ jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
|
|
+ j - i, &z[i]);
|
|
+ return j;
|
|
+ }else if( c=='}' ){
|
|
+ return -2; /* End of {...} */
|
|
+ }else if( c==']' ){
|
|
+ return -3; /* End of [...] */
|
|
+ }else if( c==0 ){
|
|
+ return 0; /* End of file */
|
|
+ }else{
|
|
+ return -1; /* Syntax error */
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parse a complete JSON string. Return 0 on success or non-zero if there
|
|
+** are any errors. If an error occurs, free all memory associated with
|
|
+** pParse.
|
|
+**
|
|
+** pParse is uninitialized when this routine is called.
|
|
+*/
|
|
+static int jsonParse(
|
|
+ JsonParse *pParse, /* Initialize and fill this JsonParse object */
|
|
+ sqlite3_context *pCtx, /* Report errors here */
|
|
+ const char *zJson /* Input JSON text to be parsed */
|
|
+){
|
|
+ int i;
|
|
+ memset(pParse, 0, sizeof(*pParse));
|
|
+ if( zJson==0 ) return 1;
|
|
+ pParse->zJson = zJson;
|
|
+ i = jsonParseValue(pParse, 0);
|
|
+ if( pParse->oom ) i = -1;
|
|
+ if( i>0 ){
|
|
+ assert( pParse->iDepth==0 );
|
|
+ while( safe_isspace(zJson[i]) ) i++;
|
|
+ if( zJson[i] ) i = -1;
|
|
+ }
|
|
+ if( i<=0 ){
|
|
+ if( pCtx!=0 ){
|
|
+ if( pParse->oom ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
|
|
+ }
|
|
+ }
|
|
+ jsonParseReset(pParse);
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Mark node i of pParse as being a child of iParent. Call recursively
|
|
+** to fill in all the descendants of node i.
|
|
+*/
|
|
+static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
|
|
+ JsonNode *pNode = &pParse->aNode[i];
|
|
+ u32 j;
|
|
+ pParse->aUp[i] = iParent;
|
|
+ switch( pNode->eType ){
|
|
+ case JSON_ARRAY: {
|
|
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
|
|
+ jsonParseFillInParentage(pParse, i+j, i);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JSON_OBJECT: {
|
|
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
|
|
+ pParse->aUp[i+j] = i;
|
|
+ jsonParseFillInParentage(pParse, i+j+1, i);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute the parentage of all nodes in a completed parse.
|
|
+*/
|
|
+static int jsonParseFindParents(JsonParse *pParse){
|
|
+ u32 *aUp;
|
|
+ assert( pParse->aUp==0 );
|
|
+ aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
|
|
+ if( aUp==0 ){
|
|
+ pParse->oom = 1;
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ jsonParseFillInParentage(pParse, 0, 0);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
|
|
+*/
|
|
+#define JSON_CACHE_ID (-429938) /* First cache entry */
|
|
+#define JSON_CACHE_SZ 4 /* Max number of cache entries */
|
|
+
|
|
+/*
|
|
+** Obtain a complete parse of the JSON found in the first argument
|
|
+** of the argv array. Use the sqlite3_get_auxdata() cache for this
|
|
+** parse if it is available. If the cache is not available or if it
|
|
+** is no longer valid, parse the JSON again and return the new parse,
|
|
+** and also register the new parse so that it will be available for
|
|
+** future sqlite3_get_auxdata() calls.
|
|
+*/
|
|
+static JsonParse *jsonParseCached(
|
|
+ sqlite3_context *pCtx,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite3_context *pErrCtx
|
|
+){
|
|
+ const char *zJson = (const char*)sqlite3_value_text(argv[0]);
|
|
+ int nJson = sqlite3_value_bytes(argv[0]);
|
|
+ JsonParse *p;
|
|
+ JsonParse *pMatch = 0;
|
|
+ int iKey;
|
|
+ int iMinKey = 0;
|
|
+ u32 iMinHold = 0xffffffff;
|
|
+ u32 iMaxHold = 0;
|
|
+ if( zJson==0 ) return 0;
|
|
+ for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
|
|
+ p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
|
|
+ if( p==0 ){
|
|
+ iMinKey = iKey;
|
|
+ break;
|
|
+ }
|
|
+ if( pMatch==0
|
|
+ && p->nJson==nJson
|
|
+ && memcmp(p->zJson,zJson,nJson)==0
|
|
+ ){
|
|
+ p->nErr = 0;
|
|
+ pMatch = p;
|
|
+ }else if( p->iHold<iMinHold ){
|
|
+ iMinHold = p->iHold;
|
|
+ iMinKey = iKey;
|
|
+ }
|
|
+ if( p->iHold>iMaxHold ){
|
|
+ iMaxHold = p->iHold;
|
|
+ }
|
|
+ }
|
|
+ if( pMatch ){
|
|
+ pMatch->nErr = 0;
|
|
+ pMatch->iHold = iMaxHold+1;
|
|
+ return pMatch;
|
|
+ }
|
|
+ p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
|
|
+ if( p==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ return 0;
|
|
+ }
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ p->zJson = (char*)&p[1];
|
|
+ memcpy((char*)p->zJson, zJson, nJson+1);
|
|
+ if( jsonParse(p, pErrCtx, p->zJson) ){
|
|
+ sqlite3_free(p);
|
|
+ return 0;
|
|
+ }
|
|
+ p->nJson = nJson;
|
|
+ p->iHold = iMaxHold+1;
|
|
+ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
|
|
+ (void(*)(void*))jsonParseFree);
|
|
+ return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compare the OBJECT label at pNode against zKey,nKey. Return true on
|
|
+** a match.
|
|
+*/
|
|
+static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
|
|
+ if( pNode->jnFlags & JNODE_RAW ){
|
|
+ if( pNode->n!=nKey ) return 0;
|
|
+ return strncmp(pNode->u.zJContent, zKey, nKey)==0;
|
|
+ }else{
|
|
+ if( pNode->n!=nKey+2 ) return 0;
|
|
+ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* forward declaration */
|
|
+static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
|
|
+
|
|
+/*
|
|
+** Search along zPath to find the node specified. Return a pointer
|
|
+** to that node, or NULL if zPath is malformed or if there is no such
|
|
+** node.
|
|
+**
|
|
+** If pApnd!=0, then try to append new nodes to complete zPath if it is
|
|
+** possible to do so and if no existing node corresponds to zPath. If
|
|
+** new nodes are appended *pApnd is set to 1.
|
|
+*/
|
|
+static JsonNode *jsonLookupStep(
|
|
+ JsonParse *pParse, /* The JSON to search */
|
|
+ u32 iRoot, /* Begin the search at this node */
|
|
+ const char *zPath, /* The path to search */
|
|
+ int *pApnd, /* Append nodes to complete path if not NULL */
|
|
+ const char **pzErr /* Make *pzErr point to any syntax error in zPath */
|
|
+){
|
|
+ u32 i, j, nKey;
|
|
+ const char *zKey;
|
|
+ JsonNode *pRoot = &pParse->aNode[iRoot];
|
|
+ if( zPath[0]==0 ) return pRoot;
|
|
+ if( zPath[0]=='.' ){
|
|
+ if( pRoot->eType!=JSON_OBJECT ) return 0;
|
|
+ zPath++;
|
|
+ if( zPath[0]=='"' ){
|
|
+ zKey = zPath + 1;
|
|
+ for(i=1; zPath[i] && zPath[i]!='"'; i++){}
|
|
+ nKey = i-1;
|
|
+ if( zPath[i] ){
|
|
+ i++;
|
|
+ }else{
|
|
+ *pzErr = zPath;
|
|
+ return 0;
|
|
+ }
|
|
+ }else{
|
|
+ zKey = zPath;
|
|
+ for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
|
|
+ nKey = i;
|
|
+ }
|
|
+ if( nKey==0 ){
|
|
+ *pzErr = zPath;
|
|
+ return 0;
|
|
+ }
|
|
+ j = 1;
|
|
+ for(;;){
|
|
+ while( j<=pRoot->n ){
|
|
+ if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
|
|
+ return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
|
|
+ }
|
|
+ j++;
|
|
+ j += jsonNodeSize(&pRoot[j]);
|
|
+ }
|
|
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ iRoot += pRoot->u.iAppend;
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ j = 1;
|
|
+ }
|
|
+ if( pApnd ){
|
|
+ u32 iStart, iLabel;
|
|
+ JsonNode *pNode;
|
|
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
|
+ iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
|
|
+ zPath += i;
|
|
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
|
+ if( pParse->oom ) return 0;
|
|
+ if( pNode ){
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ pRoot->u.iAppend = iStart - iRoot;
|
|
+ pRoot->jnFlags |= JNODE_APPEND;
|
|
+ pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
|
|
+ }
|
|
+ return pNode;
|
|
+ }
|
|
+ }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
|
|
+ if( pRoot->eType!=JSON_ARRAY ) return 0;
|
|
+ i = 0;
|
|
+ j = 1;
|
|
+ while( safe_isdigit(zPath[j]) ){
|
|
+ i = i*10 + zPath[j] - '0';
|
|
+ j++;
|
|
+ }
|
|
+ if( zPath[j]!=']' ){
|
|
+ *pzErr = zPath;
|
|
+ return 0;
|
|
+ }
|
|
+ zPath += j + 1;
|
|
+ j = 1;
|
|
+ for(;;){
|
|
+ while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
|
|
+ if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
|
|
+ j += jsonNodeSize(&pRoot[j]);
|
|
+ }
|
|
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ iRoot += pRoot->u.iAppend;
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ j = 1;
|
|
+ }
|
|
+ if( j<=pRoot->n ){
|
|
+ return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
|
|
+ }
|
|
+ if( i==0 && pApnd ){
|
|
+ u32 iStart;
|
|
+ JsonNode *pNode;
|
|
+ iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
|
|
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
|
+ if( pParse->oom ) return 0;
|
|
+ if( pNode ){
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ pRoot->u.iAppend = iStart - iRoot;
|
|
+ pRoot->jnFlags |= JNODE_APPEND;
|
|
+ }
|
|
+ return pNode;
|
|
+ }
|
|
+ }else{
|
|
+ *pzErr = zPath;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Append content to pParse that will complete zPath. Return a pointer
|
|
+** to the inserted node, or return NULL if the append fails.
|
|
+*/
|
|
+static JsonNode *jsonLookupAppend(
|
|
+ JsonParse *pParse, /* Append content to the JSON parse */
|
|
+ const char *zPath, /* Description of content to append */
|
|
+ int *pApnd, /* Set this flag to 1 */
|
|
+ const char **pzErr /* Make this point to any syntax error */
|
|
+){
|
|
+ *pApnd = 1;
|
|
+ if( zPath[0]==0 ){
|
|
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
|
+ return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
|
|
+ }
|
|
+ if( zPath[0]=='.' ){
|
|
+ jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
|
+ }else if( strncmp(zPath,"[0]",3)==0 ){
|
|
+ jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
|
+ }else{
|
|
+ return 0;
|
|
+ }
|
|
+ if( pParse->oom ) return 0;
|
|
+ return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the text of a syntax error message on a JSON path. Space is
|
|
+** obtained from sqlite3_malloc().
|
|
+*/
|
|
+static char *jsonPathSyntaxError(const char *zErr){
|
|
+ return sqlite3_mprintf("JSON path error near '%q'", zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Do a node lookup using zPath. Return a pointer to the node on success.
|
|
+** Return NULL if not found or if there is an error.
|
|
+**
|
|
+** On an error, write an error message into pCtx and increment the
|
|
+** pParse->nErr counter.
|
|
+**
|
|
+** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
|
|
+** nodes are appended.
|
|
+*/
|
|
+static JsonNode *jsonLookup(
|
|
+ JsonParse *pParse, /* The JSON to search */
|
|
+ const char *zPath, /* The path to search */
|
|
+ int *pApnd, /* Append nodes to complete path if not NULL */
|
|
+ sqlite3_context *pCtx /* Report errors here, if not NULL */
|
|
+){
|
|
+ const char *zErr = 0;
|
|
+ JsonNode *pNode = 0;
|
|
+ char *zMsg;
|
|
+
|
|
+ if( zPath==0 ) return 0;
|
|
+ if( zPath[0]!='$' ){
|
|
+ zErr = zPath;
|
|
+ goto lookup_err;
|
|
+ }
|
|
+ zPath++;
|
|
+ pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
|
|
+ if( zErr==0 ) return pNode;
|
|
+
|
|
+lookup_err:
|
|
+ pParse->nErr++;
|
|
+ assert( zErr!=0 && pCtx!=0 );
|
|
+ zMsg = jsonPathSyntaxError(zErr);
|
|
+ if( zMsg ){
|
|
+ sqlite3_result_error(pCtx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+ }else{
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Report the wrong number of arguments for json_insert(), json_replace()
|
|
+** or json_set().
|
|
+*/
|
|
+static void jsonWrongNumArgs(
|
|
+ sqlite3_context *pCtx,
|
|
+ const char *zFuncName
|
|
+){
|
|
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
|
|
+ zFuncName);
|
|
+ sqlite3_result_error(pCtx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
|
|
+*/
|
|
+static void jsonRemoveAllNulls(JsonNode *pNode){
|
|
+ int i, n;
|
|
+ assert( pNode->eType==JSON_OBJECT );
|
|
+ n = pNode->n;
|
|
+ for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
|
|
+ switch( pNode[i].eType ){
|
|
+ case JSON_NULL:
|
|
+ pNode[i].jnFlags |= JNODE_REMOVE;
|
|
+ break;
|
|
+ case JSON_OBJECT:
|
|
+ jsonRemoveAllNulls(&pNode[i]);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/****************************************************************************
|
|
+** SQL functions used for testing and debugging
|
|
+****************************************************************************/
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+/*
|
|
+** The json_parse(JSON) function returns a string which describes
|
|
+** a parse of the JSON provided. Or it returns NULL if JSON is not
|
|
+** well-formed.
|
|
+*/
|
|
+static void jsonParseFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString s; /* Output string - not real JSON */
|
|
+ JsonParse x; /* The parse */
|
|
+ u32 i;
|
|
+
|
|
+ assert( argc==1 );
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ jsonParseFindParents(&x);
|
|
+ jsonInit(&s, ctx);
|
|
+ for(i=0; i<x.nNode; i++){
|
|
+ const char *zType;
|
|
+ if( x.aNode[i].jnFlags & JNODE_LABEL ){
|
|
+ assert( x.aNode[i].eType==JSON_STRING );
|
|
+ zType = "label";
|
|
+ }else{
|
|
+ zType = jsonType[x.aNode[i].eType];
|
|
+ }
|
|
+ jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
|
|
+ i, zType, x.aNode[i].n, x.aUp[i]);
|
|
+ if( x.aNode[i].u.zJContent!=0 ){
|
|
+ jsonAppendRaw(&s, " ", 1);
|
|
+ jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
|
|
+ }
|
|
+ jsonAppendRaw(&s, "\n", 1);
|
|
+ }
|
|
+ jsonParseReset(&x);
|
|
+ jsonResult(&s);
|
|
+}
|
|
+
|
|
+/*
|
|
+** The json_test1(JSON) function return true (1) if the input is JSON
|
|
+** text generated by another json function. It returns (0) if the input
|
|
+** is not known to be JSON.
|
|
+*/
|
|
+static void jsonTest1Func(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ UNUSED_PARAM(argc);
|
|
+ sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
|
|
+}
|
|
+#endif /* SQLITE_DEBUG */
|
|
+
|
|
+/****************************************************************************
|
|
+** Scalar SQL function implementations
|
|
+****************************************************************************/
|
|
+
|
|
+/*
|
|
+** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
|
|
+** corresponding to the SQL value input. Mostly this means putting
|
|
+** double-quotes around strings and returning the unquoted string "null"
|
|
+** when given a NULL input.
|
|
+*/
|
|
+static void jsonQuoteFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString jx;
|
|
+ UNUSED_PARAM(argc);
|
|
+
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendValue(&jx, argv[0]);
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the json_array(VALUE,...) function. Return a JSON
|
|
+** array that contains all values given in arguments. Or if any argument
|
|
+** is a BLOB, throw an error.
|
|
+*/
|
|
+static void jsonArrayFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ JsonString jx;
|
|
+
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendChar(&jx, '[');
|
|
+ for(i=0; i<argc; i++){
|
|
+ jsonAppendSeparator(&jx);
|
|
+ jsonAppendValue(&jx, argv[i]);
|
|
+ }
|
|
+ jsonAppendChar(&jx, ']');
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** json_array_length(JSON)
|
|
+** json_array_length(JSON, PATH)
|
|
+**
|
|
+** Return the number of elements in the top-level JSON array.
|
|
+** Return 0 if the input is not a well-formed JSON array.
|
|
+*/
|
|
+static void jsonArrayLengthFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ sqlite3_int64 n = 0;
|
|
+ u32 i;
|
|
+ JsonNode *pNode;
|
|
+
|
|
+ p = jsonParseCached(ctx, argv, ctx);
|
|
+ if( p==0 ) return;
|
|
+ assert( p->nNode );
|
|
+ if( argc==2 ){
|
|
+ const char *zPath = (const char*)sqlite3_value_text(argv[1]);
|
|
+ pNode = jsonLookup(p, zPath, 0, ctx);
|
|
+ }else{
|
|
+ pNode = p->aNode;
|
|
+ }
|
|
+ if( pNode==0 ){
|
|
+ return;
|
|
+ }
|
|
+ if( pNode->eType==JSON_ARRAY ){
|
|
+ assert( (pNode->jnFlags & JNODE_APPEND)==0 );
|
|
+ for(i=1; i<=pNode->n; n++){
|
|
+ i += jsonNodeSize(&pNode[i]);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_int64(ctx, n);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_extract(JSON, PATH, ...)
|
|
+**
|
|
+** Return the element described by PATH. Return NULL if there is no
|
|
+** PATH element. If there are multiple PATHs, then return a JSON array
|
|
+** with the result from each path. Throw an error if the JSON or any PATH
|
|
+** is malformed.
|
|
+*/
|
|
+static void jsonExtractFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ JsonString jx;
|
|
+ int i;
|
|
+
|
|
+ if( argc<2 ) return;
|
|
+ p = jsonParseCached(ctx, argv, ctx);
|
|
+ if( p==0 ) return;
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendChar(&jx, '[');
|
|
+ for(i=1; i<argc; i++){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ pNode = jsonLookup(p, zPath, 0, ctx);
|
|
+ if( p->nErr ) break;
|
|
+ if( argc>2 ){
|
|
+ jsonAppendSeparator(&jx);
|
|
+ if( pNode ){
|
|
+ jsonRenderNode(pNode, &jx, 0);
|
|
+ }else{
|
|
+ jsonAppendRaw(&jx, "null", 4);
|
|
+ }
|
|
+ }else if( pNode ){
|
|
+ jsonReturn(pNode, ctx, 0);
|
|
+ }
|
|
+ }
|
|
+ if( argc>2 && i==argc ){
|
|
+ jsonAppendChar(&jx, ']');
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+ }
|
|
+ jsonReset(&jx);
|
|
+}
|
|
+
|
|
+/* This is the RFC 7396 MergePatch algorithm.
|
|
+*/
|
|
+static JsonNode *jsonMergePatch(
|
|
+ JsonParse *pParse, /* The JSON parser that contains the TARGET */
|
|
+ u32 iTarget, /* Node of the TARGET in pParse */
|
|
+ JsonNode *pPatch /* The PATCH */
|
|
+){
|
|
+ u32 i, j;
|
|
+ u32 iRoot;
|
|
+ JsonNode *pTarget;
|
|
+ if( pPatch->eType!=JSON_OBJECT ){
|
|
+ return pPatch;
|
|
+ }
|
|
+ assert( iTarget>=0 && iTarget<pParse->nNode );
|
|
+ pTarget = &pParse->aNode[iTarget];
|
|
+ assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
|
|
+ if( pTarget->eType!=JSON_OBJECT ){
|
|
+ jsonRemoveAllNulls(pPatch);
|
|
+ return pPatch;
|
|
+ }
|
|
+ iRoot = iTarget;
|
|
+ for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
|
|
+ u32 nKey;
|
|
+ const char *zKey;
|
|
+ assert( pPatch[i].eType==JSON_STRING );
|
|
+ assert( pPatch[i].jnFlags & JNODE_LABEL );
|
|
+ nKey = pPatch[i].n;
|
|
+ zKey = pPatch[i].u.zJContent;
|
|
+ assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
|
|
+ for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
|
|
+ assert( pTarget[j].eType==JSON_STRING );
|
|
+ assert( pTarget[j].jnFlags & JNODE_LABEL );
|
|
+ assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
|
|
+ if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){
|
|
+ if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break;
|
|
+ if( pPatch[i+1].eType==JSON_NULL ){
|
|
+ pTarget[j+1].jnFlags |= JNODE_REMOVE;
|
|
+ }else{
|
|
+ JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
|
|
+ if( pNew==0 ) return 0;
|
|
+ pTarget = &pParse->aNode[iTarget];
|
|
+ if( pNew!=&pTarget[j+1] ){
|
|
+ pTarget[j+1].u.pPatch = pNew;
|
|
+ pTarget[j+1].jnFlags |= JNODE_PATCH;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
|
|
+ int iStart, iPatch;
|
|
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
|
+ jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
|
|
+ iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
|
+ if( pParse->oom ) return 0;
|
|
+ jsonRemoveAllNulls(pPatch);
|
|
+ pTarget = &pParse->aNode[iTarget];
|
|
+ pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
|
|
+ pParse->aNode[iRoot].u.iAppend = iStart - iRoot;
|
|
+ iRoot = iStart;
|
|
+ pParse->aNode[iPatch].jnFlags |= JNODE_PATCH;
|
|
+ pParse->aNode[iPatch].u.pPatch = &pPatch[i+1];
|
|
+ }
|
|
+ }
|
|
+ return pTarget;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
|
|
+** object that is the result of running the RFC 7396 MergePatch() algorithm
|
|
+** on the two arguments.
|
|
+*/
|
|
+static void jsonPatchFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The JSON that is being patched */
|
|
+ JsonParse y; /* The patch */
|
|
+ JsonNode *pResult; /* The result of the merge */
|
|
+
|
|
+ UNUSED_PARAM(argc);
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){
|
|
+ jsonParseReset(&x);
|
|
+ return;
|
|
+ }
|
|
+ pResult = jsonMergePatch(&x, 0, y.aNode);
|
|
+ assert( pResult!=0 || x.oom );
|
|
+ if( pResult ){
|
|
+ jsonReturnJson(pResult, ctx, 0);
|
|
+ }else{
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ }
|
|
+ jsonParseReset(&x);
|
|
+ jsonParseReset(&y);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON
|
|
+** object that contains all name/value given in arguments. Or if any name
|
|
+** is not a string or if any value is a BLOB, throw an error.
|
|
+*/
|
|
+static void jsonObjectFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ JsonString jx;
|
|
+ const char *z;
|
|
+ u32 n;
|
|
+
|
|
+ if( argc&1 ){
|
|
+ sqlite3_result_error(ctx, "json_object() requires an even number "
|
|
+ "of arguments", -1);
|
|
+ return;
|
|
+ }
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendChar(&jx, '{');
|
|
+ for(i=0; i<argc; i+=2){
|
|
+ if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
|
|
+ sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
|
|
+ jsonReset(&jx);
|
|
+ return;
|
|
+ }
|
|
+ jsonAppendSeparator(&jx);
|
|
+ z = (const char*)sqlite3_value_text(argv[i]);
|
|
+ n = (u32)sqlite3_value_bytes(argv[i]);
|
|
+ jsonAppendString(&jx, z, n);
|
|
+ jsonAppendChar(&jx, ':');
|
|
+ jsonAppendValue(&jx, argv[i+1]);
|
|
+ }
|
|
+ jsonAppendChar(&jx, '}');
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** json_remove(JSON, PATH, ...)
|
|
+**
|
|
+** Remove the named elements from JSON and return the result. malformed
|
|
+** JSON or PATH arguments result in an error.
|
|
+*/
|
|
+static void jsonRemoveFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ u32 i;
|
|
+
|
|
+ if( argc<1 ) return;
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ assert( x.nNode );
|
|
+ for(i=1; i<(u32)argc; i++){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ if( zPath==0 ) goto remove_done;
|
|
+ pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
+ if( x.nErr ) goto remove_done;
|
|
+ if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
|
|
+ }
|
|
+ if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
|
+ jsonReturnJson(x.aNode, ctx, 0);
|
|
+ }
|
|
+remove_done:
|
|
+ jsonParseReset(&x);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_replace(JSON, PATH, VALUE, ...)
|
|
+**
|
|
+** Replace the value at PATH with VALUE. If PATH does not already exist,
|
|
+** this routine is a no-op. If JSON or PATH is malformed, throw an error.
|
|
+*/
|
|
+static void jsonReplaceFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ u32 i;
|
|
+
|
|
+ if( argc<1 ) return;
|
|
+ if( (argc&1)==0 ) {
|
|
+ jsonWrongNumArgs(ctx, "replace");
|
|
+ return;
|
|
+ }
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ assert( x.nNode );
|
|
+ for(i=1; i<(u32)argc; i+=2){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
+ if( x.nErr ) goto replace_err;
|
|
+ if( pNode ){
|
|
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
|
|
+ pNode->u.iReplace = i + 1;
|
|
+ }
|
|
+ }
|
|
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
|
+ sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
|
+ }else{
|
|
+ jsonReturnJson(x.aNode, ctx, argv);
|
|
+ }
|
|
+replace_err:
|
|
+ jsonParseReset(&x);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_set(JSON, PATH, VALUE, ...)
|
|
+**
|
|
+** Set the value at PATH to VALUE. Create the PATH if it does not already
|
|
+** exist. Overwrite existing values that do exist.
|
|
+** If JSON or PATH is malformed, throw an error.
|
|
+**
|
|
+** json_insert(JSON, PATH, VALUE, ...)
|
|
+**
|
|
+** Create PATH and initialize it to VALUE. If PATH already exists, this
|
|
+** routine is a no-op. If JSON or PATH is malformed, throw an error.
|
|
+*/
|
|
+static void jsonSetFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ u32 i;
|
|
+ int bApnd;
|
|
+ int bIsSet = *(int*)sqlite3_user_data(ctx);
|
|
+
|
|
+ if( argc<1 ) return;
|
|
+ if( (argc&1)==0 ) {
|
|
+ jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
|
|
+ return;
|
|
+ }
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ assert( x.nNode );
|
|
+ for(i=1; i<(u32)argc; i+=2){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ bApnd = 0;
|
|
+ pNode = jsonLookup(&x, zPath, &bApnd, ctx);
|
|
+ if( x.oom ){
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ goto jsonSetDone;
|
|
+ }else if( x.nErr ){
|
|
+ goto jsonSetDone;
|
|
+ }else if( pNode && (bApnd || bIsSet) ){
|
|
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
|
|
+ pNode->u.iReplace = i + 1;
|
|
+ }
|
|
+ }
|
|
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
|
+ sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
|
+ }else{
|
|
+ jsonReturnJson(x.aNode, ctx, argv);
|
|
+ }
|
|
+jsonSetDone:
|
|
+ jsonParseReset(&x);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_type(JSON)
|
|
+** json_type(JSON, PATH)
|
|
+**
|
|
+** Return the top-level "type" of a JSON string. Throw an error if
|
|
+** either the JSON or PATH inputs are not well-formed.
|
|
+*/
|
|
+static void jsonTypeFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ const char *zPath;
|
|
+ JsonNode *pNode;
|
|
+
|
|
+ p = jsonParseCached(ctx, argv, ctx);
|
|
+ if( p==0 ) return;
|
|
+ if( argc==2 ){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[1]);
|
|
+ pNode = jsonLookup(p, zPath, 0, ctx);
|
|
+ }else{
|
|
+ pNode = p->aNode;
|
|
+ }
|
|
+ if( pNode ){
|
|
+ sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_valid(JSON)
|
|
+**
|
|
+** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
|
|
+** Return 0 otherwise.
|
|
+*/
|
|
+static void jsonValidFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ UNUSED_PARAM(argc);
|
|
+ p = jsonParseCached(ctx, argv, 0);
|
|
+ sqlite3_result_int(ctx, p!=0);
|
|
+}
|
|
+
|
|
+
|
|
+/****************************************************************************
|
|
+** Aggregate SQL function implementations
|
|
+****************************************************************************/
|
|
+/*
|
|
+** json_group_array(VALUE)
|
|
+**
|
|
+** Return a JSON array composed of all values in the aggregate.
|
|
+*/
|
|
+static void jsonArrayStep(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString *pStr;
|
|
+ UNUSED_PARAM(argc);
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
|
|
+ if( pStr ){
|
|
+ if( pStr->zBuf==0 ){
|
|
+ jsonInit(pStr, ctx);
|
|
+ jsonAppendChar(pStr, '[');
|
|
+ }else{
|
|
+ jsonAppendChar(pStr, ',');
|
|
+ pStr->pCtx = ctx;
|
|
+ }
|
|
+ jsonAppendValue(pStr, argv[0]);
|
|
+ }
|
|
+}
|
|
+static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
|
|
+ JsonString *pStr;
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
+ if( pStr ){
|
|
+ pStr->pCtx = ctx;
|
|
+ jsonAppendChar(pStr, ']');
|
|
+ if( pStr->bErr ){
|
|
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
|
+ assert( pStr->bStatic );
|
|
+ }else if( isFinal ){
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
|
|
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
|
|
+ pStr->bStatic = 1;
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
|
|
+ pStr->nUsed--;
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
|
|
+ }
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+static void jsonArrayValue(sqlite3_context *ctx){
|
|
+ jsonArrayCompute(ctx, 0);
|
|
+}
|
|
+static void jsonArrayFinal(sqlite3_context *ctx){
|
|
+ jsonArrayCompute(ctx, 1);
|
|
+}
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+/*
|
|
+** This method works for both json_group_array() and json_group_object().
|
|
+** It works by removing the first element of the group by searching forward
|
|
+** to the first comma (",") that is not within a string and deleting all
|
|
+** text through that comma.
|
|
+*/
|
|
+static void jsonGroupInverse(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ int inStr = 0;
|
|
+ char *z;
|
|
+ JsonString *pStr;
|
|
+ UNUSED_PARAM(argc);
|
|
+ UNUSED_PARAM(argv);
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
+#ifdef NEVER
|
|
+ /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will
|
|
+ ** always have been called to initalize it */
|
|
+ if( NEVER(!pStr) ) return;
|
|
+#endif
|
|
+ z = pStr->zBuf;
|
|
+ for(i=1; z[i]!=',' || inStr; i++){
|
|
+ assert( i<pStr->nUsed );
|
|
+ if( z[i]=='"' ){
|
|
+ inStr = !inStr;
|
|
+ }else if( z[i]=='\\' ){
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ pStr->nUsed -= i;
|
|
+ memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1);
|
|
+}
|
|
+#else
|
|
+# define jsonGroupInverse 0
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+** json_group_obj(NAME,VALUE)
|
|
+**
|
|
+** Return a JSON object composed of all names and values in the aggregate.
|
|
+*/
|
|
+static void jsonObjectStep(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString *pStr;
|
|
+ const char *z;
|
|
+ u32 n;
|
|
+ UNUSED_PARAM(argc);
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
|
|
+ if( pStr ){
|
|
+ if( pStr->zBuf==0 ){
|
|
+ jsonInit(pStr, ctx);
|
|
+ jsonAppendChar(pStr, '{');
|
|
+ }else{
|
|
+ jsonAppendChar(pStr, ',');
|
|
+ pStr->pCtx = ctx;
|
|
+ }
|
|
+ z = (const char*)sqlite3_value_text(argv[0]);
|
|
+ n = (u32)sqlite3_value_bytes(argv[0]);
|
|
+ jsonAppendString(pStr, z, n);
|
|
+ jsonAppendChar(pStr, ':');
|
|
+ jsonAppendValue(pStr, argv[1]);
|
|
+ }
|
|
+}
|
|
+static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
|
|
+ JsonString *pStr;
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
+ if( pStr ){
|
|
+ jsonAppendChar(pStr, '}');
|
|
+ if( pStr->bErr ){
|
|
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
|
+ assert( pStr->bStatic );
|
|
+ }else if( isFinal ){
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
|
|
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
|
|
+ pStr->bStatic = 1;
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
|
|
+ pStr->nUsed--;
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
|
|
+ }
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+static void jsonObjectValue(sqlite3_context *ctx){
|
|
+ jsonObjectCompute(ctx, 0);
|
|
+}
|
|
+static void jsonObjectFinal(sqlite3_context *ctx){
|
|
+ jsonObjectCompute(ctx, 1);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+/****************************************************************************
|
|
+** The json_each virtual table
|
|
+****************************************************************************/
|
|
+typedef struct JsonEachCursor JsonEachCursor;
|
|
+struct JsonEachCursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ u32 iRowid; /* The rowid */
|
|
+ u32 iBegin; /* The first node of the scan */
|
|
+ u32 i; /* Index in sParse.aNode[] of current row */
|
|
+ u32 iEnd; /* EOF when i equals or exceeds this value */
|
|
+ u8 eType; /* Type of top-level element */
|
|
+ u8 bRecursive; /* True for json_tree(). False for json_each() */
|
|
+ char *zJson; /* Input JSON */
|
|
+ char *zRoot; /* Path by which to filter zJson */
|
|
+ JsonParse sParse; /* Parse of the input JSON */
|
|
+};
|
|
+
|
|
+/* Constructor for the json_each virtual table */
|
|
+static int jsonEachConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define JEACH_KEY 0
|
|
+#define JEACH_VALUE 1
|
|
+#define JEACH_TYPE 2
|
|
+#define JEACH_ATOM 3
|
|
+#define JEACH_ID 4
|
|
+#define JEACH_PARENT 5
|
|
+#define JEACH_FULLKEY 6
|
|
+#define JEACH_PATH 7
|
|
+/* The xBestIndex method assumes that the JSON and ROOT columns are
|
|
+** the last two columns in the table. Should this ever changes, be
|
|
+** sure to update the xBestIndex method. */
|
|
+#define JEACH_JSON 8
|
|
+#define JEACH_ROOT 9
|
|
+
|
|
+ UNUSED_PARAM(pzErr);
|
|
+ UNUSED_PARAM(argv);
|
|
+ UNUSED_PARAM(argc);
|
|
+ UNUSED_PARAM(pAux);
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
|
|
+ "json HIDDEN,root HIDDEN)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* destructor for json_each virtual table */
|
|
+static int jsonEachDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* constructor for a JsonEachCursor object for json_each(). */
|
|
+static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ JsonEachCursor *pCur;
|
|
+
|
|
+ UNUSED_PARAM(p);
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* constructor for a JsonEachCursor object for json_tree(). */
|
|
+static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ int rc = jsonEachOpenEach(p, ppCursor);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
|
|
+ pCur->bRecursive = 1;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Reset a JsonEachCursor back to its original state. Free any memory
|
|
+** held. */
|
|
+static void jsonEachCursorReset(JsonEachCursor *p){
|
|
+ sqlite3_free(p->zJson);
|
|
+ sqlite3_free(p->zRoot);
|
|
+ jsonParseReset(&p->sParse);
|
|
+ p->iRowid = 0;
|
|
+ p->i = 0;
|
|
+ p->iEnd = 0;
|
|
+ p->eType = 0;
|
|
+ p->zJson = 0;
|
|
+ p->zRoot = 0;
|
|
+}
|
|
+
|
|
+/* Destructor for a jsonEachCursor object */
|
|
+static int jsonEachClose(sqlite3_vtab_cursor *cur){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ jsonEachCursorReset(p);
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Return TRUE if the jsonEachCursor object has been advanced off the end
|
|
+** of the JSON object */
|
|
+static int jsonEachEof(sqlite3_vtab_cursor *cur){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ return p->i >= p->iEnd;
|
|
+}
|
|
+
|
|
+/* Advance the cursor to the next element for json_tree() */
|
|
+static int jsonEachNext(sqlite3_vtab_cursor *cur){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ if( p->bRecursive ){
|
|
+ if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
|
|
+ p->i++;
|
|
+ p->iRowid++;
|
|
+ if( p->i<p->iEnd ){
|
|
+ u32 iUp = p->sParse.aUp[p->i];
|
|
+ JsonNode *pUp = &p->sParse.aNode[iUp];
|
|
+ p->eType = pUp->eType;
|
|
+ if( pUp->eType==JSON_ARRAY ){
|
|
+ if( iUp==p->i-1 ){
|
|
+ pUp->u.iKey = 0;
|
|
+ }else{
|
|
+ pUp->u.iKey++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ switch( p->eType ){
|
|
+ case JSON_ARRAY: {
|
|
+ p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
|
|
+ p->iRowid++;
|
|
+ break;
|
|
+ }
|
|
+ case JSON_OBJECT: {
|
|
+ p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
|
|
+ p->iRowid++;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ p->i = p->iEnd;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Append the name of the path for element i to pStr
|
|
+*/
|
|
+static void jsonEachComputePath(
|
|
+ JsonEachCursor *p, /* The cursor */
|
|
+ JsonString *pStr, /* Write the path here */
|
|
+ u32 i /* Path to this element */
|
|
+){
|
|
+ JsonNode *pNode, *pUp;
|
|
+ u32 iUp;
|
|
+ if( i==0 ){
|
|
+ jsonAppendChar(pStr, '$');
|
|
+ return;
|
|
+ }
|
|
+ iUp = p->sParse.aUp[i];
|
|
+ jsonEachComputePath(p, pStr, iUp);
|
|
+ pNode = &p->sParse.aNode[i];
|
|
+ pUp = &p->sParse.aNode[iUp];
|
|
+ if( pUp->eType==JSON_ARRAY ){
|
|
+ jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
|
|
+ }else{
|
|
+ assert( pUp->eType==JSON_OBJECT );
|
|
+ if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
|
|
+ assert( pNode->eType==JSON_STRING );
|
|
+ assert( pNode->jnFlags & JNODE_LABEL );
|
|
+ jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Return the value of a column */
|
|
+static int jsonEachColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ JsonNode *pThis = &p->sParse.aNode[p->i];
|
|
+ switch( i ){
|
|
+ case JEACH_KEY: {
|
|
+ if( p->i==0 ) break;
|
|
+ if( p->eType==JSON_OBJECT ){
|
|
+ jsonReturn(pThis, ctx, 0);
|
|
+ }else if( p->eType==JSON_ARRAY ){
|
|
+ u32 iKey;
|
|
+ if( p->bRecursive ){
|
|
+ if( p->iRowid==0 ) break;
|
|
+ iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
|
|
+ }else{
|
|
+ iKey = p->iRowid;
|
|
+ }
|
|
+ sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_VALUE: {
|
|
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
+ jsonReturn(pThis, ctx, 0);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_TYPE: {
|
|
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
+ sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_ATOM: {
|
|
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
+ if( pThis->eType>=JSON_ARRAY ) break;
|
|
+ jsonReturn(pThis, ctx, 0);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_ID: {
|
|
+ sqlite3_result_int64(ctx,
|
|
+ (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_PARENT: {
|
|
+ if( p->i>p->iBegin && p->bRecursive ){
|
|
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_FULLKEY: {
|
|
+ JsonString x;
|
|
+ jsonInit(&x, ctx);
|
|
+ if( p->bRecursive ){
|
|
+ jsonEachComputePath(p, &x, p->i);
|
|
+ }else{
|
|
+ if( p->zRoot ){
|
|
+ jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
|
|
+ }else{
|
|
+ jsonAppendChar(&x, '$');
|
|
+ }
|
|
+ if( p->eType==JSON_ARRAY ){
|
|
+ jsonPrintf(30, &x, "[%d]", p->iRowid);
|
|
+ }else if( p->eType==JSON_OBJECT ){
|
|
+ jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
|
|
+ }
|
|
+ }
|
|
+ jsonResult(&x);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_PATH: {
|
|
+ if( p->bRecursive ){
|
|
+ JsonString x;
|
|
+ jsonInit(&x, ctx);
|
|
+ jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
|
|
+ jsonResult(&x);
|
|
+ break;
|
|
+ }
|
|
+ /* For json_each() path and root are the same so fall through
|
|
+ ** into the root case */
|
|
+ }
|
|
+ default: {
|
|
+ const char *zRoot = p->zRoot;
|
|
+ if( zRoot==0 ) zRoot = "$";
|
|
+ sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_JSON: {
|
|
+ assert( i==JEACH_JSON );
|
|
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Return the current rowid value */
|
|
+static int jsonEachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ *pRowid = p->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* The query strategy is to look for an equality constraint on the json
|
|
+** column. Without such a constraint, the table cannot operate. idxNum is
|
|
+** 1 if the constraint is found, 3 if the constraint and zRoot are found,
|
|
+** and 0 otherwise.
|
|
+*/
|
|
+static int jsonEachBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop counter or computed array index */
|
|
+ int aIdx[2]; /* Index of constraints for JSON and ROOT */
|
|
+ int unusableMask = 0; /* Mask of unusable JSON and ROOT constraints */
|
|
+ int idxMask = 0; /* Mask of usable == constraints JSON and ROOT */
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ /* This implementation assumes that JSON and ROOT are the last two
|
|
+ ** columns in the table */
|
|
+ assert( JEACH_ROOT == JEACH_JSON+1 );
|
|
+ UNUSED_PARAM(tab);
|
|
+ aIdx[0] = aIdx[1] = -1;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ int iCol;
|
|
+ int iMask;
|
|
+ if( pConstraint->iColumn < JEACH_JSON ) continue;
|
|
+ iCol = pConstraint->iColumn - JEACH_JSON;
|
|
+ assert( iCol==0 || iCol==1 );
|
|
+ iMask = 1 << iCol;
|
|
+ if( pConstraint->usable==0 ){
|
|
+ unusableMask |= iMask;
|
|
+ }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ aIdx[iCol] = i;
|
|
+ idxMask |= iMask;
|
|
+ }
|
|
+ }
|
|
+ if( (unusableMask & ~idxMask)!=0 ){
|
|
+ /* If there are any unusable constraints on JSON or ROOT, then reject
|
|
+ ** this entire plan */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ if( aIdx[0]<0 ){
|
|
+ /* No JSON input. Leave estimatedCost at the huge value that it was
|
|
+ ** initialized to to discourage the query planner from selecting this
|
|
+ ** plan. */
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ }else{
|
|
+ pIdxInfo->estimatedCost = 1.0;
|
|
+ i = aIdx[0];
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ if( aIdx[1]<0 ){
|
|
+ pIdxInfo->idxNum = 1; /* Only JSON supplied. Plan 1 */
|
|
+ }else{
|
|
+ i = aIdx[1];
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 2;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ pIdxInfo->idxNum = 3; /* Both JSON and ROOT are supplied. Plan 3 */
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Start a search on a new JSON string */
|
|
+static int jsonEachFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ const char *z;
|
|
+ const char *zRoot = 0;
|
|
+ size_t n;
|
|
+
|
|
+ UNUSED_PARAM(idxStr);
|
|
+ UNUSED_PARAM(argc);
|
|
+ jsonEachCursorReset(p);
|
|
+ if( idxNum==0 ) return SQLITE_OK;
|
|
+ z = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( z==0 ) return SQLITE_OK;
|
|
+ n = (size_t)sqlite3_value_bytes(argv[0]);
|
|
+ p->zJson = sqlite3_malloc( n+1 );
|
|
+ if( p->zJson==0 ) return SQLITE_NOMEM;
|
|
+ memcpy(p->zJson, z, n+1);
|
|
+ if( jsonParse(&p->sParse, 0, p->zJson) ){
|
|
+ int rc = SQLITE_NOMEM;
|
|
+ if( p->sParse.oom==0 ){
|
|
+ sqlite3_free(cur->pVtab->zErrMsg);
|
|
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
|
|
+ if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
|
|
+ }
|
|
+ jsonEachCursorReset(p);
|
|
+ return rc;
|
|
+ }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
|
|
+ jsonEachCursorReset(p);
|
|
+ return SQLITE_NOMEM;
|
|
+ }else{
|
|
+ JsonNode *pNode = 0;
|
|
+ if( idxNum==3 ){
|
|
+ const char *zErr = 0;
|
|
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
|
|
+ if( zRoot==0 ) return SQLITE_OK;
|
|
+ n = (size_t)sqlite3_value_bytes(argv[1]);
|
|
+ p->zRoot = sqlite3_malloc( n+1 );
|
|
+ if( p->zRoot==0 ) return SQLITE_NOMEM;
|
|
+ memcpy(p->zRoot, zRoot, n+1);
|
|
+ if( zRoot[0]!='$' ){
|
|
+ zErr = zRoot;
|
|
+ }else{
|
|
+ pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
|
|
+ }
|
|
+ if( zErr ){
|
|
+ sqlite3_free(cur->pVtab->zErrMsg);
|
|
+ cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
|
|
+ jsonEachCursorReset(p);
|
|
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
|
|
+ }else if( pNode==0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ }else{
|
|
+ pNode = p->sParse.aNode;
|
|
+ }
|
|
+ p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
|
|
+ p->eType = pNode->eType;
|
|
+ if( p->eType>=JSON_ARRAY ){
|
|
+ pNode->u.iKey = 0;
|
|
+ p->iEnd = p->i + pNode->n + 1;
|
|
+ if( p->bRecursive ){
|
|
+ p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
|
|
+ if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
|
|
+ p->i--;
|
|
+ }
|
|
+ }else{
|
|
+ p->i++;
|
|
+ }
|
|
+ }else{
|
|
+ p->iEnd = p->i+1;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* The methods of the json_each virtual table */
|
|
+static const sqlite3_module jsonEachModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ jsonEachConnect, /* xConnect */
|
|
+ jsonEachBestIndex, /* xBestIndex */
|
|
+ jsonEachDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ jsonEachOpenEach, /* xOpen - open a cursor */
|
|
+ jsonEachClose, /* xClose - close a cursor */
|
|
+ jsonEachFilter, /* xFilter - configure scan constraints */
|
|
+ jsonEachNext, /* xNext - advance a cursor */
|
|
+ jsonEachEof, /* xEof - check for end of scan */
|
|
+ jsonEachColumn, /* xColumn - read data */
|
|
+ jsonEachRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+
|
|
+/* The methods of the json_tree virtual table. */
|
|
+static const sqlite3_module jsonTreeModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ jsonEachConnect, /* xConnect */
|
|
+ jsonEachBestIndex, /* xBestIndex */
|
|
+ jsonEachDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ jsonEachOpenTree, /* xOpen - open a cursor */
|
|
+ jsonEachClose, /* xClose - close a cursor */
|
|
+ jsonEachFilter, /* xFilter - configure scan constraints */
|
|
+ jsonEachNext, /* xNext - advance a cursor */
|
|
+ jsonEachEof, /* xEof - check for end of scan */
|
|
+ jsonEachColumn, /* xColumn - read data */
|
|
+ jsonEachRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+/****************************************************************************
|
|
+** The following routines are the only publically visible identifiers in this
|
|
+** file. Call the following routines in order to register the various SQL
|
|
+** functions and the virtual table implemented by this file.
|
|
+****************************************************************************/
|
|
+
|
|
+int sqlite3Json1Init(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+ unsigned int i;
|
|
+ static const struct {
|
|
+ const char *zName;
|
|
+ int nArg;
|
|
+ int flag;
|
|
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
|
+ } aFunc[] = {
|
|
+ { "json", 1, 0, jsonRemoveFunc },
|
|
+ { "json_array", -1, 0, jsonArrayFunc },
|
|
+ { "json_array_length", 1, 0, jsonArrayLengthFunc },
|
|
+ { "json_array_length", 2, 0, jsonArrayLengthFunc },
|
|
+ { "json_extract", -1, 0, jsonExtractFunc },
|
|
+ { "json_insert", -1, 0, jsonSetFunc },
|
|
+ { "json_object", -1, 0, jsonObjectFunc },
|
|
+ { "json_patch", 2, 0, jsonPatchFunc },
|
|
+ { "json_quote", 1, 0, jsonQuoteFunc },
|
|
+ { "json_remove", -1, 0, jsonRemoveFunc },
|
|
+ { "json_replace", -1, 0, jsonReplaceFunc },
|
|
+ { "json_set", -1, 1, jsonSetFunc },
|
|
+ { "json_type", 1, 0, jsonTypeFunc },
|
|
+ { "json_type", 2, 0, jsonTypeFunc },
|
|
+ { "json_valid", 1, 0, jsonValidFunc },
|
|
+
|
|
+#if SQLITE_DEBUG
|
|
+ /* DEBUG and TESTING functions */
|
|
+ { "json_parse", 1, 0, jsonParseFunc },
|
|
+ { "json_test1", 1, 0, jsonTest1Func },
|
|
+#endif
|
|
+ };
|
|
+ static const struct {
|
|
+ const char *zName;
|
|
+ int nArg;
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
|
|
+ void (*xFinal)(sqlite3_context*);
|
|
+ void (*xValue)(sqlite3_context*);
|
|
+ } aAgg[] = {
|
|
+ { "json_group_array", 1,
|
|
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue },
|
|
+ { "json_group_object", 2,
|
|
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue },
|
|
+ };
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ static const struct {
|
|
+ const char *zName;
|
|
+ const sqlite3_module *pModule;
|
|
+ } aMod[] = {
|
|
+ { "json_each", &jsonEachModule },
|
|
+ { "json_tree", &jsonTreeModule },
|
|
+ };
|
|
+#endif
|
|
+ for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
|
|
+ rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
|
|
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
|
+ (void*)&aFunc[i].flag,
|
|
+ aFunc[i].xFunc, 0, 0);
|
|
+ }
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
|
|
+ rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
|
|
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
|
|
+ aAgg[i].xStep, aAgg[i].xFinal,
|
|
+ aAgg[i].xValue, jsonGroupInverse, 0);
|
|
+ }
|
|
+#endif
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
|
|
+ rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
|
|
+ }
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+#ifndef SQLITE_CORE
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_json_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ return sqlite3Json1Init(db);
|
|
+}
|
|
+#endif
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) */
|
|
--- origsrc/sqlite-autoconf-3280000/memstat.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/memstat.c 2019-05-14 16:22:31.243948600 +0200
|
|
@@ -0,0 +1,428 @@
|
|
+/*
|
|
+** 2018-09-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 demonstrates an eponymous virtual table that returns information
|
|
+** from sqlite3_status64() and sqlite3_db_status().
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** .load ./memstat
|
|
+** .mode quote
|
|
+** .header on
|
|
+** SELECT * FROM memstat;
|
|
+*/
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_MEMSTATVTAB)
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/* memstat_vtab is a subclass of sqlite3_vtab which will
|
|
+** serve as the underlying representation of a memstat virtual table
|
|
+*/
|
|
+typedef struct memstat_vtab memstat_vtab;
|
|
+struct memstat_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this memstat vtab */
|
|
+};
|
|
+
|
|
+/* memstat_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct memstat_cursor memstat_cursor;
|
|
+struct memstat_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this cursor */
|
|
+ int iRowid; /* Current row in aMemstatColumn[] */
|
|
+ int iDb; /* Which schema we are looking at */
|
|
+ int nDb; /* Number of schemas */
|
|
+ char **azDb; /* Names of all schemas */
|
|
+ sqlite3_int64 aVal[2]; /* Result values */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The memstatConnect() method is invoked to create a new
|
|
+** memstat_vtab that describes the memstat virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for memstat_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the memstat_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against memstat will look like.
|
|
+*/
|
|
+static int memstatConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ memstat_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define MSV_COLUMN_NAME 0 /* Name of quantity being measured */
|
|
+#define MSV_COLUMN_SCHEMA 1 /* schema name */
|
|
+#define MSV_COLUMN_VALUE 2 /* Current value */
|
|
+#define MSV_COLUMN_HIWTR 3 /* Highwater mark */
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,"CREATE TABLE x(name,schema,value,hiwtr)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->db = db;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for memstat_cursor objects.
|
|
+*/
|
|
+static int memstatDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new memstat_cursor object.
|
|
+*/
|
|
+static int memstatOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ memstat_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->db = ((memstat_vtab*)p)->db;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Clear all the schema names from a cursor
|
|
+*/
|
|
+static void memstatClearSchema(memstat_cursor *pCur){
|
|
+ int i;
|
|
+ if( pCur->azDb==0 ) return;
|
|
+ for(i=0; i<pCur->nDb; i++){
|
|
+ sqlite3_free(pCur->azDb[i]);
|
|
+ }
|
|
+ sqlite3_free(pCur->azDb);
|
|
+ pCur->azDb = 0;
|
|
+ pCur->nDb = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Fill in the azDb[] array for the cursor.
|
|
+*/
|
|
+static int memstatFindSchemas(memstat_cursor *pCur){
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int rc;
|
|
+ if( pCur->nDb ) return SQLITE_OK;
|
|
+ rc = sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pStmt, 0);
|
|
+ if( rc ){
|
|
+ sqlite3_finalize(pStmt);
|
|
+ return rc;
|
|
+ }
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ char **az, *z;
|
|
+ az = sqlite3_realloc64(pCur->azDb, sizeof(char*)*(pCur->nDb+1));
|
|
+ if( az==0 ){
|
|
+ memstatClearSchema(pCur);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ pCur->azDb = az;
|
|
+ z = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
|
+ if( z==0 ){
|
|
+ memstatClearSchema(pCur);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ pCur->azDb[pCur->nDb] = z;
|
|
+ pCur->nDb++;
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Destructor for a memstat_cursor.
|
|
+*/
|
|
+static int memstatClose(sqlite3_vtab_cursor *cur){
|
|
+ memstat_cursor *pCur = (memstat_cursor*)cur;
|
|
+ memstatClearSchema(pCur);
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Allowed values for aMemstatColumn[].eType
|
|
+*/
|
|
+#define MSV_GSTAT 0 /* sqlite3_status64() information */
|
|
+#define MSV_DB 1 /* sqlite3_db_status() information */
|
|
+#define MSV_ZIPVFS 2 /* ZIPVFS file-control with 64-bit return */
|
|
+
|
|
+/*
|
|
+** An array of quantities that can be measured and reported by
|
|
+** this virtual table
|
|
+*/
|
|
+static const struct MemstatColumns {
|
|
+ const char *zName; /* Symbolic name */
|
|
+ unsigned char eType; /* Type of interface */
|
|
+ unsigned char mNull; /* Bitmask of which columns are NULL */
|
|
+ /* 2: dbname, 4: current, 8: hiwtr */
|
|
+ int eOp; /* Opcode */
|
|
+} aMemstatColumn[] = {
|
|
+ {"MEMORY_USED", MSV_GSTAT, 2, SQLITE_STATUS_MEMORY_USED },
|
|
+ {"MALLOC_SIZE", MSV_GSTAT, 6, SQLITE_STATUS_MALLOC_SIZE },
|
|
+ {"MALLOC_COUNT", MSV_GSTAT, 2, SQLITE_STATUS_MALLOC_COUNT },
|
|
+ {"PAGECACHE_USED", MSV_GSTAT, 2, SQLITE_STATUS_PAGECACHE_USED },
|
|
+ {"PAGECACHE_OVERFLOW", MSV_GSTAT, 2, SQLITE_STATUS_PAGECACHE_OVERFLOW },
|
|
+ {"PAGECACHE_SIZE", MSV_GSTAT, 6, SQLITE_STATUS_PAGECACHE_SIZE },
|
|
+ {"PARSER_STACK", MSV_GSTAT, 6, SQLITE_STATUS_PARSER_STACK },
|
|
+ {"DB_LOOKASIDE_USED", MSV_DB, 2, SQLITE_DBSTATUS_LOOKASIDE_USED },
|
|
+ {"DB_LOOKASIDE_HIT", MSV_DB, 6, SQLITE_DBSTATUS_LOOKASIDE_HIT },
|
|
+ {"DB_LOOKASIDE_MISS_SIZE", MSV_DB, 6, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE},
|
|
+ {"DB_LOOKASIDE_MISS_FULL", MSV_DB, 6, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL},
|
|
+ {"DB_CACHE_USED", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_USED },
|
|
+#if SQLITE_VERSION_NUMBER >= 3140000
|
|
+ {"DB_CACHE_USED_SHARED", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_USED_SHARED },
|
|
+#endif
|
|
+ {"DB_SCHEMA_USED", MSV_DB, 10, SQLITE_DBSTATUS_SCHEMA_USED },
|
|
+ {"DB_STMT_USED", MSV_DB, 10, SQLITE_DBSTATUS_STMT_USED },
|
|
+ {"DB_CACHE_HIT", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_HIT },
|
|
+ {"DB_CACHE_MISS", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_MISS },
|
|
+ {"DB_CACHE_WRITE", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_WRITE },
|
|
+#if SQLITE_VERSION_NUMBER >= 3230000
|
|
+ {"DB_CACHE_SPILL", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_SPILL },
|
|
+#endif
|
|
+ {"DB_DEFERRED_FKS", MSV_DB, 10, SQLITE_DBSTATUS_DEFERRED_FKS },
|
|
+#ifdef SQLITE_ENABLE_ZIPVFS
|
|
+ {"ZIPVFS_CACHE_USED", MSV_ZIPVFS, 8, 231454 },
|
|
+ {"ZIPVFS_CACHE_HIT", MSV_ZIPVFS, 8, 231455 },
|
|
+ {"ZIPVFS_CACHE_MISS", MSV_ZIPVFS, 8, 231456 },
|
|
+ {"ZIPVFS_CACHE_WRITE", MSV_ZIPVFS, 8, 231457 },
|
|
+ {"ZIPVFS_DIRECT_READ", MSV_ZIPVFS, 8, 231458 },
|
|
+ {"ZIPVFS_DIRECT_BYTES", MSV_ZIPVFS, 8, 231459 },
|
|
+#endif /* SQLITE_ENABLE_ZIPVFS */
|
|
+};
|
|
+#define MSV_NROW (sizeof(aMemstatColumn)/sizeof(aMemstatColumn[0]))
|
|
+
|
|
+/*
|
|
+** Advance a memstat_cursor to its next row of output.
|
|
+*/
|
|
+static int memstatNext(sqlite3_vtab_cursor *cur){
|
|
+ memstat_cursor *pCur = (memstat_cursor*)cur;
|
|
+ int i;
|
|
+ assert( pCur->iRowid<=MSV_NROW );
|
|
+ while(1){
|
|
+ i = (int)pCur->iRowid - 1;
|
|
+ if( i<0 || (aMemstatColumn[i].mNull & 2)!=0 || (++pCur->iDb)>=pCur->nDb ){
|
|
+ pCur->iRowid++;
|
|
+ if( pCur->iRowid>MSV_NROW ) return SQLITE_OK; /* End of the table */
|
|
+ pCur->iDb = 0;
|
|
+ i++;
|
|
+ }
|
|
+ pCur->aVal[0] = 0;
|
|
+ pCur->aVal[1] = 0;
|
|
+ switch( aMemstatColumn[i].eType ){
|
|
+ case MSV_GSTAT: {
|
|
+ if( sqlite3_libversion_number()>=3010000 ){
|
|
+ sqlite3_status64(aMemstatColumn[i].eOp,
|
|
+ &pCur->aVal[0], &pCur->aVal[1],0);
|
|
+ }else{
|
|
+ int xCur, xHiwtr;
|
|
+ sqlite3_status(aMemstatColumn[i].eOp, &xCur, &xHiwtr, 0);
|
|
+ pCur->aVal[0] = xCur;
|
|
+ pCur->aVal[1] = xHiwtr;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case MSV_DB: {
|
|
+ int xCur, xHiwtr;
|
|
+ sqlite3_db_status(pCur->db, aMemstatColumn[i].eOp, &xCur, &xHiwtr, 0);
|
|
+ pCur->aVal[0] = xCur;
|
|
+ pCur->aVal[1] = xHiwtr;
|
|
+ break;
|
|
+ }
|
|
+ case MSV_ZIPVFS: {
|
|
+ int rc;
|
|
+ rc = sqlite3_file_control(pCur->db, pCur->azDb[pCur->iDb],
|
|
+ aMemstatColumn[i].eOp, (void*)&pCur->aVal[0]);
|
|
+ if( rc!=SQLITE_OK ) continue;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the memstat_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int memstatColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int iCol /* Which column to return */
|
|
+){
|
|
+ memstat_cursor *pCur = (memstat_cursor*)cur;
|
|
+ int i;
|
|
+ assert( pCur->iRowid>0 && pCur->iRowid<=MSV_NROW );
|
|
+ i = (int)pCur->iRowid - 1;
|
|
+ if( (aMemstatColumn[i].mNull & (1<<iCol))!=0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ switch( iCol ){
|
|
+ case MSV_COLUMN_NAME: {
|
|
+ sqlite3_result_text(ctx, aMemstatColumn[i].zName, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case MSV_COLUMN_SCHEMA: {
|
|
+ sqlite3_result_text(ctx, pCur->azDb[pCur->iDb], -1, 0);
|
|
+ break;
|
|
+ }
|
|
+ case MSV_COLUMN_VALUE: {
|
|
+ sqlite3_result_int64(ctx, pCur->aVal[0]);
|
|
+ break;
|
|
+ }
|
|
+ case MSV_COLUMN_HIWTR: {
|
|
+ sqlite3_result_int64(ctx, pCur->aVal[1]);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int memstatRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ memstat_cursor *pCur = (memstat_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid*1000 + pCur->iDb;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int memstatEof(sqlite3_vtab_cursor *cur){
|
|
+ memstat_cursor *pCur = (memstat_cursor*)cur;
|
|
+ return pCur->iRowid>MSV_NROW;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the memstat_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to memstatColumn() or memstatRowid() or
|
|
+** memstatEof().
|
|
+*/
|
|
+static int memstatFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ memstat_cursor *pCur = (memstat_cursor *)pVtabCursor;
|
|
+ int rc = memstatFindSchemas(pCur);
|
|
+ if( rc ) return rc;
|
|
+ pCur->iRowid = 0;
|
|
+ pCur->iDb = 0;
|
|
+ return memstatNext(pVtabCursor);
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the memstat virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int memstatBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ pIdxInfo->estimatedCost = (double)500;
|
|
+ pIdxInfo->estimatedRows = 500;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** memstat virtual table.
|
|
+*/
|
|
+static sqlite3_module memstatModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ memstatConnect, /* xConnect */
|
|
+ memstatBestIndex, /* xBestIndex */
|
|
+ memstatDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ memstatOpen, /* xOpen - open a cursor */
|
|
+ memstatClose, /* xClose - close a cursor */
|
|
+ memstatFilter, /* xFilter - configure scan constraints */
|
|
+ memstatNext, /* xNext - advance a cursor */
|
|
+ memstatEof, /* xEof - check for end of scan */
|
|
+ memstatColumn, /* xColumn - read data */
|
|
+ memstatRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+int sqlite3MemstatVtabInit(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "sqlite_memstat", &memstatModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#ifndef SQLITE_CORE
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_memstat_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3MemstatVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif /* SQLITE_CORE */
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_MEMSTATVTAB) */
|
|
--- origsrc/sqlite-autoconf-3280000/memtrace.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/memtrace.c 2019-05-14 16:22:31.245901300 +0200
|
|
@@ -0,0 +1,108 @@
|
|
+/*
|
|
+** 2019-01-21
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements an extension that uses the SQLITE_CONFIG_MALLOC
|
|
+** mechanism to add a tracing layer on top of SQLite. If this extension
|
|
+** is registered prior to sqlite3_initialize(), it will cause all memory
|
|
+** allocation activities to be logged on standard output, or to some other
|
|
+** FILE specified by the initializer.
|
|
+**
|
|
+** This file needs to be compiled into the application that uses it.
|
|
+**
|
|
+** This extension is used to implement the --memtrace option of the
|
|
+** command-line shell.
|
|
+*/
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+/* The original memory allocation routines */
|
|
+static sqlite3_mem_methods memtraceBase;
|
|
+static FILE *memtraceOut;
|
|
+
|
|
+/* Methods that trace memory allocations */
|
|
+static void *memtraceMalloc(int n){
|
|
+ if( memtraceOut ){
|
|
+ fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n",
|
|
+ memtraceBase.xRoundup(n));
|
|
+ }
|
|
+ return memtraceBase.xMalloc(n);
|
|
+}
|
|
+static void memtraceFree(void *p){
|
|
+ if( p==0 ) return;
|
|
+ if( memtraceOut ){
|
|
+ fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p));
|
|
+ }
|
|
+ memtraceBase.xFree(p);
|
|
+}
|
|
+static void *memtraceRealloc(void *p, int n){
|
|
+ if( p==0 ) return memtraceMalloc(n);
|
|
+ if( n==0 ){
|
|
+ memtraceFree(p);
|
|
+ return 0;
|
|
+ }
|
|
+ if( memtraceOut ){
|
|
+ fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n",
|
|
+ memtraceBase.xSize(p), memtraceBase.xRoundup(n));
|
|
+ }
|
|
+ return memtraceBase.xRealloc(p, n);
|
|
+}
|
|
+static int memtraceSize(void *p){
|
|
+ return memtraceBase.xSize(p);
|
|
+}
|
|
+static int memtraceRoundup(int n){
|
|
+ return memtraceBase.xRoundup(n);
|
|
+}
|
|
+static int memtraceInit(void *p){
|
|
+ return memtraceBase.xInit(p);
|
|
+}
|
|
+static void memtraceShutdown(void *p){
|
|
+ memtraceBase.xShutdown(p);
|
|
+}
|
|
+
|
|
+/* The substitute memory allocator */
|
|
+static sqlite3_mem_methods ersaztMethods = {
|
|
+ memtraceMalloc,
|
|
+ memtraceFree,
|
|
+ memtraceRealloc,
|
|
+ memtraceSize,
|
|
+ memtraceRoundup,
|
|
+ memtraceInit,
|
|
+ memtraceShutdown,
|
|
+ 0
|
|
+};
|
|
+
|
|
+/* Begin tracing memory allocations to out. */
|
|
+int sqlite3MemTraceActivate(FILE *out){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( memtraceBase.xMalloc==0 ){
|
|
+ rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods);
|
|
+ }
|
|
+ }
|
|
+ memtraceOut = out;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Deactivate memory tracing */
|
|
+int sqlite3MemTraceDeactivate(void){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( memtraceBase.xMalloc!=0 ){
|
|
+ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ memset(&memtraceBase, 0, sizeof(memtraceBase));
|
|
+ }
|
|
+ }
|
|
+ memtraceOut = 0;
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/memvfs.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/memvfs.c 2019-05-14 16:22:31.246877700 +0200
|
|
@@ -0,0 +1,594 @@
|
|
+/*
|
|
+** 2016-09-07
|
|
+**
|
|
+** 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 is an in-memory VFS implementation. The application supplies
|
|
+** a chunk of memory to hold the database file.
|
|
+**
|
|
+** Because there is place to store a rollback or wal journal, the database
|
|
+** must use one of journal_mode=MEMORY or journal_mode=NONE.
|
|
+**
|
|
+** USAGE:
|
|
+**
|
|
+** sqlite3_open_v2("file:/whatever?ptr=0xf05538&sz=14336&max=65536", &db,
|
|
+** SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI,
|
|
+** "memvfs");
|
|
+**
|
|
+** These are the query parameters:
|
|
+**
|
|
+** ptr= The address of the memory buffer that holds the database.
|
|
+**
|
|
+** sz= The current size the database file
|
|
+**
|
|
+** maxsz= The maximum size of the database. In other words, the
|
|
+** amount of space allocated for the ptr= buffer.
|
|
+**
|
|
+** freeonclose= If true, then sqlite3_free() is called on the ptr=
|
|
+** value when the connection closes.
|
|
+**
|
|
+** The ptr= and sz= query parameters are required. If maxsz= is omitted,
|
|
+** then it defaults to the sz= value. Parameter values can be in either
|
|
+** decimal or hexadecimal. The filename in the URI is ignored.
|
|
+*/
|
|
+#include <sqlite3ext.h>
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this utility
|
|
+*/
|
|
+typedef struct sqlite3_vfs MemVfs;
|
|
+typedef struct MemFile MemFile;
|
|
+
|
|
+/* Access to a lower-level VFS that (might) implement dynamic loading,
|
|
+** access to randomness, etc.
|
|
+*/
|
|
+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
|
+
|
|
+/* An open file */
|
|
+struct MemFile {
|
|
+ sqlite3_file base; /* IO methods */
|
|
+ sqlite3_int64 sz; /* Size of the file */
|
|
+ sqlite3_int64 szMax; /* Space allocated to aData */
|
|
+ unsigned char *aData; /* content of the file */
|
|
+ int bFreeOnClose; /* Invoke sqlite3_free() on aData at close */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Methods for MemFile
|
|
+*/
|
|
+static int memClose(sqlite3_file*);
|
|
+static int memRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
+static int memWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
+static int memTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
+static int memSync(sqlite3_file*, int flags);
|
|
+static int memFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
+static int memLock(sqlite3_file*, int);
|
|
+static int memUnlock(sqlite3_file*, int);
|
|
+static int memCheckReservedLock(sqlite3_file*, int *pResOut);
|
|
+static int memFileControl(sqlite3_file*, int op, void *pArg);
|
|
+static int memSectorSize(sqlite3_file*);
|
|
+static int memDeviceCharacteristics(sqlite3_file*);
|
|
+static int memShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
|
+static int memShmLock(sqlite3_file*, int offset, int n, int flags);
|
|
+static void memShmBarrier(sqlite3_file*);
|
|
+static int memShmUnmap(sqlite3_file*, int deleteFlag);
|
|
+static int memFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
|
+static int memUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
|
+
|
|
+/*
|
|
+** Methods for MemVfs
|
|
+*/
|
|
+static int memOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
+static int memDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
+static int memAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
+static int memFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
+static void *memDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
+static void memDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
+static void (*memDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
+static void memDlClose(sqlite3_vfs*, void*);
|
|
+static int memRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
+static int memSleep(sqlite3_vfs*, int microseconds);
|
|
+static int memCurrentTime(sqlite3_vfs*, double*);
|
|
+static int memGetLastError(sqlite3_vfs*, int, char *);
|
|
+static int memCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
+
|
|
+static sqlite3_vfs mem_vfs = {
|
|
+ 2, /* iVersion */
|
|
+ 0, /* szOsFile (set when registered) */
|
|
+ 1024, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ "memvfs", /* zName */
|
|
+ 0, /* pAppData (set when registered) */
|
|
+ memOpen, /* xOpen */
|
|
+ memDelete, /* xDelete */
|
|
+ memAccess, /* xAccess */
|
|
+ memFullPathname, /* xFullPathname */
|
|
+ memDlOpen, /* xDlOpen */
|
|
+ memDlError, /* xDlError */
|
|
+ memDlSym, /* xDlSym */
|
|
+ memDlClose, /* xDlClose */
|
|
+ memRandomness, /* xRandomness */
|
|
+ memSleep, /* xSleep */
|
|
+ memCurrentTime, /* xCurrentTime */
|
|
+ memGetLastError, /* xGetLastError */
|
|
+ memCurrentTimeInt64 /* xCurrentTimeInt64 */
|
|
+};
|
|
+
|
|
+static const sqlite3_io_methods mem_io_methods = {
|
|
+ 3, /* iVersion */
|
|
+ memClose, /* xClose */
|
|
+ memRead, /* xRead */
|
|
+ memWrite, /* xWrite */
|
|
+ memTruncate, /* xTruncate */
|
|
+ memSync, /* xSync */
|
|
+ memFileSize, /* xFileSize */
|
|
+ memLock, /* xLock */
|
|
+ memUnlock, /* xUnlock */
|
|
+ memCheckReservedLock, /* xCheckReservedLock */
|
|
+ memFileControl, /* xFileControl */
|
|
+ memSectorSize, /* xSectorSize */
|
|
+ memDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ memShmMap, /* xShmMap */
|
|
+ memShmLock, /* xShmLock */
|
|
+ memShmBarrier, /* xShmBarrier */
|
|
+ memShmUnmap, /* xShmUnmap */
|
|
+ memFetch, /* xFetch */
|
|
+ memUnfetch /* xUnfetch */
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Close an mem-file.
|
|
+**
|
|
+** The pData pointer is owned by the application, so there is nothing
|
|
+** to free.
|
|
+*/
|
|
+static int memClose(sqlite3_file *pFile){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( p->bFreeOnClose ) sqlite3_free(p->aData);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read data from an mem-file.
|
|
+*/
|
|
+static int memRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ memcpy(zBuf, p->aData+iOfst, iAmt);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an mem-file.
|
|
+*/
|
|
+static int memWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *z,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( iOfst+iAmt>p->sz ){
|
|
+ if( iOfst+iAmt>p->szMax ) return SQLITE_FULL;
|
|
+ if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz);
|
|
+ p->sz = iOfst+iAmt;
|
|
+ }
|
|
+ memcpy(p->aData+iOfst, z, iAmt);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an mem-file.
|
|
+*/
|
|
+static int memTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( size>p->sz ){
|
|
+ if( size>p->szMax ) return SQLITE_FULL;
|
|
+ memset(p->aData+p->sz, 0, size-p->sz);
|
|
+ }
|
|
+ p->sz = size;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an mem-file.
|
|
+*/
|
|
+static int memSync(sqlite3_file *pFile, int flags){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an mem-file.
|
|
+*/
|
|
+static int memFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ *pSize = p->sz;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an mem-file.
|
|
+*/
|
|
+static int memLock(sqlite3_file *pFile, int eLock){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unlock an mem-file.
|
|
+*/
|
|
+static int memUnlock(sqlite3_file *pFile, int eLock){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an mem-file.
|
|
+*/
|
|
+static int memCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ *pResOut = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an mem-file.
|
|
+*/
|
|
+static int memFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ int rc = SQLITE_NOTFOUND;
|
|
+ if( op==SQLITE_FCNTL_VFSNAME ){
|
|
+ *(char**)pArg = sqlite3_mprintf("mem(%p,%lld)", p->aData, p->sz);
|
|
+ rc = SQLITE_OK;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the sector-size in bytes for an mem-file.
|
|
+*/
|
|
+static int memSectorSize(sqlite3_file *pFile){
|
|
+ return 1024;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an mem-file.
|
|
+*/
|
|
+static int memDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ return SQLITE_IOCAP_ATOMIC |
|
|
+ SQLITE_IOCAP_POWERSAFE_OVERWRITE |
|
|
+ SQLITE_IOCAP_SAFE_APPEND |
|
|
+ SQLITE_IOCAP_SEQUENTIAL;
|
|
+}
|
|
+
|
|
+/* Create a shared memory file mapping */
|
|
+static int memShmMap(
|
|
+ sqlite3_file *pFile,
|
|
+ int iPg,
|
|
+ int pgsz,
|
|
+ int bExtend,
|
|
+ void volatile **pp
|
|
+){
|
|
+ return SQLITE_IOERR_SHMMAP;
|
|
+}
|
|
+
|
|
+/* Perform locking on a shared-memory segment */
|
|
+static int memShmLock(sqlite3_file *pFile, int offset, int n, int flags){
|
|
+ return SQLITE_IOERR_SHMLOCK;
|
|
+}
|
|
+
|
|
+/* Memory barrier operation on shared memory */
|
|
+static void memShmBarrier(sqlite3_file *pFile){
|
|
+ return;
|
|
+}
|
|
+
|
|
+/* Unmap a shared memory segment */
|
|
+static int memShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Fetch a page of a memory-mapped file */
|
|
+static int memFetch(
|
|
+ sqlite3_file *pFile,
|
|
+ sqlite3_int64 iOfst,
|
|
+ int iAmt,
|
|
+ void **pp
|
|
+){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ *pp = (void*)(p->aData + iOfst);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Release a memory-mapped page */
|
|
+static int memUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an mem file handle.
|
|
+*/
|
|
+static int memOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ MemFile *p = (MemFile*)pFile;
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ if( (flags & SQLITE_OPEN_MAIN_DB)==0 ) return SQLITE_CANTOPEN;
|
|
+ p->aData = (unsigned char*)(size_t)sqlite3_uri_int64(zName,"ptr",0);
|
|
+ if( p->aData==0 ) return SQLITE_CANTOPEN;
|
|
+ p->sz = sqlite3_uri_int64(zName,"sz",0);
|
|
+ if( p->sz<0 ) return SQLITE_CANTOPEN;
|
|
+ p->szMax = sqlite3_uri_int64(zName,"max",p->sz);
|
|
+ if( p->szMax<p->sz ) return SQLITE_CANTOPEN;
|
|
+ p->bFreeOnClose = sqlite3_uri_boolean(zName,"freeonclose",0);
|
|
+ pFile->pMethods = &mem_io_methods;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** 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 memDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ return SQLITE_IOERR_DELETE;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Test for access permissions. Return true if the requested permission
|
|
+** is available, or false otherwise.
|
|
+*/
|
|
+static int memAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ *pResOut = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** 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 (INST_MAX_PATHNAME+1) bytes.
|
|
+*/
|
|
+static int memFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open the dynamic library located at zPath and return a handle.
|
|
+*/
|
|
+static void *memDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), 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 memDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
|
+*/
|
|
+static void (*memDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close the dynamic library handle pHandle.
|
|
+*/
|
|
+static void memDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
+** random data.
|
|
+*/
|
|
+static int memRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sleep for nMicro microseconds. Return the number of microseconds
|
|
+** actually slept.
|
|
+*/
|
|
+static int memSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current time as a Julian Day number in *pTimeOut.
|
|
+*/
|
|
+static int memCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
|
+}
|
|
+
|
|
+static int memGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
|
+}
|
|
+static int memCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
+ return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
|
+}
|
|
+
|
|
+#ifdef MEMVFS_TEST
|
|
+/*
|
|
+** memvfs_from_file(FILENAME, MAXSIZE)
|
|
+**
|
|
+** This an SQL function used to help in testing the memvfs VFS. The
|
|
+** function reads the content of a file into memory and then returns
|
|
+** a URI that can be handed to ATTACH to attach the memory buffer as
|
|
+** a database. Example:
|
|
+**
|
|
+** ATTACH memvfs_from_file('test.db',1048576) AS inmem;
|
|
+**
|
|
+** The optional MAXSIZE argument gives the size of the memory allocation
|
|
+** used to hold the database. If omitted, it defaults to the size of the
|
|
+** file on disk.
|
|
+*/
|
|
+#include <stdio.h>
|
|
+static void memvfsFromFileFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ unsigned char *p;
|
|
+ size_t sz;
|
|
+ size_t szMax;
|
|
+ FILE *in;
|
|
+ const char *zFilename = (const char*)sqlite3_value_text(argv[0]);
|
|
+ char *zUri;
|
|
+
|
|
+ if( zFilename==0 ) return;
|
|
+ in = fopen(zFilename, "rb");
|
|
+ if( in==0 ) return;
|
|
+ fseek(in, 0, SEEK_END);
|
|
+ szMax = sz = ftell(in);
|
|
+ rewind(in);
|
|
+ if( argc>=2 ){
|
|
+ szMax = sqlite3_value_int64(argv[1]);
|
|
+ if( szMax<sz ) szMax = sz;
|
|
+ }
|
|
+ p = sqlite3_malloc( szMax );
|
|
+ if( p==0 ){
|
|
+ fclose(in);
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ fread(p, sz, 1, in);
|
|
+ fclose(in);
|
|
+ zUri = sqlite3_mprintf(
|
|
+ "file:/mem?vfs=memvfs&ptr=%lld&sz=%lld&max=%lld&freeonclose=1",
|
|
+ (sqlite3_int64)p, (sqlite3_int64)sz, (sqlite3_int64)szMax);
|
|
+ sqlite3_result_text(context, zUri, -1, sqlite3_free);
|
|
+}
|
|
+#endif /* MEMVFS_TEST */
|
|
+
|
|
+#ifdef MEMVFS_TEST
|
|
+/*
|
|
+** memvfs_to_file(SCHEMA, FILENAME)
|
|
+**
|
|
+** The schema identified by SCHEMA must be a memvfs database. Write
|
|
+** the content of this database into FILENAME.
|
|
+*/
|
|
+static void memvfsToFileFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ MemFile *p = 0;
|
|
+ FILE *out;
|
|
+ int rc;
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ sqlite3_vfs *pVfs = 0;
|
|
+ const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
|
+ const char *zFilename = (const char*)sqlite3_value_text(argv[1]);
|
|
+
|
|
+ if( zFilename==0 ) return;
|
|
+ out = fopen(zFilename, "wb");
|
|
+ if( out==0 ) return;
|
|
+ rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_VFS_POINTER, &pVfs);
|
|
+ if( rc || pVfs==0 ) return;
|
|
+ if( strcmp(pVfs->zName,"memvfs")!=0 ) return;
|
|
+ rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p);
|
|
+ if( rc ) return;
|
|
+ fwrite(p->aData, 1, (size_t)p->sz, out);
|
|
+ fclose(out);
|
|
+}
|
|
+#endif /* MEMVFS_TEST */
|
|
+
|
|
+#ifdef MEMVFS_TEST
|
|
+/* Called for each new database connection */
|
|
+static int memvfsRegister(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const struct sqlite3_api_routines *pThunk
|
|
+){
|
|
+ sqlite3_create_function(db, "memvfs_from_file", 1, SQLITE_UTF8, 0,
|
|
+ memvfsFromFileFunc, 0, 0);
|
|
+ sqlite3_create_function(db, "memvfs_from_file", 2, SQLITE_UTF8, 0,
|
|
+ memvfsFromFileFunc, 0, 0);
|
|
+ sqlite3_create_function(db, "memvfs_to_file", 2, SQLITE_UTF8, 0,
|
|
+ memvfsToFileFunc, 0, 0);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#endif /* MEMVFS_TEST */
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+/*
|
|
+** This routine is called when the extension is loaded.
|
|
+** Register the new VFS.
|
|
+*/
|
|
+int sqlite3_memvfs_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ mem_vfs.pAppData = sqlite3_vfs_find(0);
|
|
+ mem_vfs.szOsFile = sizeof(MemFile);
|
|
+ rc = sqlite3_vfs_register(&mem_vfs, 1);
|
|
+#ifdef MEMVFS_TEST
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_auto_extension((void(*)(void))memvfsRegister);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = memvfsRegister(db, pzErrMsg, pApi);
|
|
+ }
|
|
+#endif
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ mem_vfs.pAppData = sqlite3_vfs_find(0);
|
|
+ mem_vfs.szOsFile = sizeof(MemFile);
|
|
+ rc = sqlite3_vfs_register(&mem_vfs, 1);
|
|
+#ifdef MEMVFS_TEST
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_auto_extension((void(*)(void))memvfsRegister);
|
|
+ }
|
|
+#endif
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/mmapwarm.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/mmapwarm.c 2019-05-14 16:22:31.248830300 +0200
|
|
@@ -0,0 +1,107 @@
|
|
+/*
|
|
+** 2017-09-18
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+*/
|
|
+
|
|
+#include "sqlite3.h"
|
|
+
|
|
+
|
|
+/*
|
|
+** This function is used to touch each page of a mapping of a memory
|
|
+** mapped SQLite database. Assuming that the system has sufficient free
|
|
+** memory and supports sufficiently large mappings, this causes the OS
|
|
+** to cache the entire database in main memory, making subsequent
|
|
+** database accesses faster.
|
|
+**
|
|
+** If the second parameter to this function is not NULL, it is the name of
|
|
+** the specific database to operate on (i.e. "main" or the name of an
|
|
+** attached database).
|
|
+**
|
|
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
|
+** It is not considered an error if the file is not memory-mapped, or if
|
|
+** the mapping does not span the entire file. If an error does occur, a
|
|
+** transaction may be left open on the database file.
|
|
+**
|
|
+** It is illegal to call this function when the database handle has an
|
|
+** open transaction. SQLITE_MISUSE is returned in this case.
|
|
+*/
|
|
+int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zSql = 0;
|
|
+ int pgsz = 0;
|
|
+ int nTotal = 0;
|
|
+
|
|
+ if( 0==sqlite3_get_autocommit(db) ) return SQLITE_MISUSE;
|
|
+
|
|
+ /* Open a read-only transaction on the file in question */
|
|
+ zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_master",
|
|
+ (zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "")
|
|
+ );
|
|
+ if( zSql==0 ) return SQLITE_NOMEM;
|
|
+ rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
|
+ sqlite3_free(zSql);
|
|
+
|
|
+ /* Find the SQLite page size of the file */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ zSql = sqlite3_mprintf("PRAGMA %s%q%spage_size",
|
|
+ (zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "")
|
|
+ );
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ sqlite3_stmt *pPgsz = 0;
|
|
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pPgsz, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( sqlite3_step(pPgsz)==SQLITE_ROW ){
|
|
+ pgsz = sqlite3_column_int(pPgsz, 0);
|
|
+ }
|
|
+ rc = sqlite3_finalize(pPgsz);
|
|
+ }
|
|
+ if( rc==SQLITE_OK && pgsz==0 ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Touch each mmap'd page of the file */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int rc2;
|
|
+ sqlite3_file *pFd = 0;
|
|
+ rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFd);
|
|
+ if( rc==SQLITE_OK && pFd->pMethods->iVersion>=3 ){
|
|
+ sqlite3_int64 iPg = 1;
|
|
+ sqlite3_io_methods const *p = pFd->pMethods;
|
|
+ while( 1 ){
|
|
+ unsigned char *pMap;
|
|
+ rc = p->xFetch(pFd, pgsz*iPg, pgsz, (void**)&pMap);
|
|
+ if( rc!=SQLITE_OK || pMap==0 ) break;
|
|
+
|
|
+ nTotal += pMap[0];
|
|
+ nTotal += pMap[pgsz-1];
|
|
+
|
|
+ rc = p->xUnfetch(pFd, pgsz*iPg, (void*)pMap);
|
|
+ if( rc!=SQLITE_OK ) break;
|
|
+ iPg++;
|
|
+ }
|
|
+ sqlite3_log(SQLITE_OK,
|
|
+ "sqlite3_mmap_warm_cache: Warmed up %d pages of %s", iPg==1?0:iPg,
|
|
+ sqlite3_db_filename(db, zDb)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ rc2 = sqlite3_exec(db, "END", 0, 0, 0);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/nextchar.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/nextchar.c 2019-05-14 16:22:31.250783000 +0200
|
|
@@ -0,0 +1,333 @@
|
|
+/*
|
|
+** 2013-02-28
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file contains code to implement the next_char(A,T,F,W,C) SQL function.
|
|
+**
|
|
+** The next_char(A,T,F,W,C) function finds all valid "next" characters for
|
|
+** string A given the vocabulary in T.F. If the W value exists and is a
|
|
+** non-empty string, then it is an SQL expression that limits the entries
|
|
+** in T.F that will be considered. If C exists and is a non-empty string,
|
|
+** then it is the name of the collating sequence to use for comparison. If
|
|
+**
|
|
+** Only the first three arguments are required. If the C parameter is
|
|
+** omitted or is NULL or is an empty string, then the default collating
|
|
+** sequence of T.F is used for comparision. If the W parameter is omitted
|
|
+** or is NULL or is an empty string, then no filtering of the output is
|
|
+** done.
|
|
+**
|
|
+** The T.F column should be indexed using collation C or else this routine
|
|
+** will be quite slow.
|
|
+**
|
|
+** For example, suppose an application has a dictionary like this:
|
|
+**
|
|
+** CREATE TABLE dictionary(word TEXT UNIQUE);
|
|
+**
|
|
+** Further suppose that for user keypad entry, it is desired to disable
|
|
+** (gray out) keys that are not valid as the next character. If the
|
|
+** the user has previously entered (say) 'cha' then to find all allowed
|
|
+** next characters (and thereby determine when keys should not be grayed
|
|
+** out) run the following query:
|
|
+**
|
|
+** SELECT next_char('cha','dictionary','word');
|
|
+**
|
|
+** IMPLEMENTATION NOTES:
|
|
+**
|
|
+** The next_char function is implemented using recursive SQL that makes
|
|
+** use of the table name and column name as part of a query. If either
|
|
+** the table name or column name are keywords or contain special characters,
|
|
+** then they should be escaped. For example:
|
|
+**
|
|
+** SELECT next_char('cha','[dictionary]','[word]');
|
|
+**
|
|
+** This also means that the table name can be a subquery:
|
|
+**
|
|
+** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** A structure to hold context of the next_char() computation across
|
|
+** nested function calls.
|
|
+*/
|
|
+typedef struct nextCharContext nextCharContext;
|
|
+struct nextCharContext {
|
|
+ sqlite3 *db; /* Database connection */
|
|
+ sqlite3_stmt *pStmt; /* Prepared statement used to query */
|
|
+ const unsigned char *zPrefix; /* Prefix to scan */
|
|
+ int nPrefix; /* Size of zPrefix in bytes */
|
|
+ int nAlloc; /* Space allocated to aResult */
|
|
+ int nUsed; /* Space used in aResult */
|
|
+ unsigned int *aResult; /* Array of next characters */
|
|
+ int mallocFailed; /* True if malloc fails */
|
|
+ int otherError; /* True for any other failure */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Append a result character if the character is not already in the
|
|
+** result.
|
|
+*/
|
|
+static void nextCharAppend(nextCharContext *p, unsigned c){
|
|
+ int i;
|
|
+ for(i=0; i<p->nUsed; i++){
|
|
+ if( p->aResult[i]==c ) return;
|
|
+ }
|
|
+ if( p->nUsed+1 > p->nAlloc ){
|
|
+ unsigned int *aNew;
|
|
+ int n = p->nAlloc*2 + 30;
|
|
+ aNew = sqlite3_realloc64(p->aResult, n*sizeof(unsigned int));
|
|
+ if( aNew==0 ){
|
|
+ p->mallocFailed = 1;
|
|
+ return;
|
|
+ }else{
|
|
+ p->aResult = aNew;
|
|
+ p->nAlloc = n;
|
|
+ }
|
|
+ }
|
|
+ p->aResult[p->nUsed++] = c;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write a character into z[] as UTF8. Return the number of bytes needed
|
|
+** to hold the character
|
|
+*/
|
|
+static int writeUtf8(unsigned char *z, unsigned c){
|
|
+ if( c<0x00080 ){
|
|
+ z[0] = (unsigned char)(c&0xff);
|
|
+ return 1;
|
|
+ }
|
|
+ if( c<0x00800 ){
|
|
+ z[0] = 0xC0 + (unsigned char)((c>>6)&0x1F);
|
|
+ z[1] = 0x80 + (unsigned char)(c & 0x3F);
|
|
+ return 2;
|
|
+ }
|
|
+ if( c<0x10000 ){
|
|
+ z[0] = 0xE0 + (unsigned char)((c>>12)&0x0F);
|
|
+ z[1] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
|
+ z[2] = 0x80 + (unsigned char)(c & 0x3F);
|
|
+ return 3;
|
|
+ }
|
|
+ z[0] = 0xF0 + (unsigned char)((c>>18) & 0x07);
|
|
+ z[1] = 0x80 + (unsigned char)((c>>12) & 0x3F);
|
|
+ z[2] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
|
+ z[3] = 0x80 + (unsigned char)(c & 0x3F);
|
|
+ return 4;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read a UTF8 character out of z[] and write it into *pOut. Return
|
|
+** the number of bytes in z[] that were used to construct the character.
|
|
+*/
|
|
+static int readUtf8(const unsigned char *z, unsigned *pOut){
|
|
+ static const unsigned char validBits[] = {
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
|
+ };
|
|
+ unsigned c = z[0];
|
|
+ if( c<0xc0 ){
|
|
+ *pOut = c;
|
|
+ return 1;
|
|
+ }else{
|
|
+ int n = 1;
|
|
+ c = validBits[c-0xc0];
|
|
+ while( (z[n] & 0xc0)==0x80 ){
|
|
+ c = (c<<6) + (0x3f & z[n++]);
|
|
+ }
|
|
+ if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){
|
|
+ c = 0xFFFD;
|
|
+ }
|
|
+ *pOut = c;
|
|
+ return n;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The nextCharContext structure has been set up. Add all "next" characters
|
|
+** to the result set.
|
|
+*/
|
|
+static void findNextChars(nextCharContext *p){
|
|
+ unsigned cPrev = 0;
|
|
+ unsigned char zPrev[8];
|
|
+ int n, rc;
|
|
+
|
|
+ for(;;){
|
|
+ sqlite3_bind_text(p->pStmt, 1, (char*)p->zPrefix, p->nPrefix,
|
|
+ SQLITE_STATIC);
|
|
+ n = writeUtf8(zPrev, cPrev+1);
|
|
+ sqlite3_bind_text(p->pStmt, 2, (char*)zPrev, n, SQLITE_STATIC);
|
|
+ rc = sqlite3_step(p->pStmt);
|
|
+ if( rc==SQLITE_DONE ){
|
|
+ sqlite3_reset(p->pStmt);
|
|
+ return;
|
|
+ }else if( rc!=SQLITE_ROW ){
|
|
+ p->otherError = rc;
|
|
+ return;
|
|
+ }else{
|
|
+ const unsigned char *zOut = sqlite3_column_text(p->pStmt, 0);
|
|
+ unsigned cNext;
|
|
+ n = readUtf8(zOut+p->nPrefix, &cNext);
|
|
+ sqlite3_reset(p->pStmt);
|
|
+ nextCharAppend(p, cNext);
|
|
+ cPrev = cNext;
|
|
+ if( p->mallocFailed ) return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** next_character(A,T,F,W)
|
|
+**
|
|
+** Return a string composted of all next possible characters after
|
|
+** A for elements of T.F. If W is supplied, then it is an SQL expression
|
|
+** that limits the elements in T.F that are considered.
|
|
+*/
|
|
+static void nextCharFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ nextCharContext c;
|
|
+ const unsigned char *zTable = sqlite3_value_text(argv[1]);
|
|
+ const unsigned char *zField = sqlite3_value_text(argv[2]);
|
|
+ const unsigned char *zWhere;
|
|
+ const unsigned char *zCollName;
|
|
+ char *zWhereClause = 0;
|
|
+ char *zColl = 0;
|
|
+ char *zSql;
|
|
+ int rc;
|
|
+
|
|
+ memset(&c, 0, sizeof(c));
|
|
+ c.db = sqlite3_context_db_handle(context);
|
|
+ c.zPrefix = sqlite3_value_text(argv[0]);
|
|
+ c.nPrefix = sqlite3_value_bytes(argv[0]);
|
|
+ if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
|
|
+ if( argc>=4
|
|
+ && (zWhere = sqlite3_value_text(argv[3]))!=0
|
|
+ && zWhere[0]!=0
|
|
+ ){
|
|
+ zWhereClause = sqlite3_mprintf("AND (%s)", zWhere);
|
|
+ if( zWhereClause==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ }else{
|
|
+ zWhereClause = "";
|
|
+ }
|
|
+ if( argc>=5
|
|
+ && (zCollName = sqlite3_value_text(argv[4]))!=0
|
|
+ && zCollName[0]!=0
|
|
+ ){
|
|
+ zColl = sqlite3_mprintf("collate \"%w\"", zCollName);
|
|
+ if( zColl==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
|
|
+ return;
|
|
+ }
|
|
+ }else{
|
|
+ zColl = "";
|
|
+ }
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT %s FROM %s"
|
|
+ " WHERE %s>=(?1 || ?2) %s"
|
|
+ " AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
|
|
+ " %s"
|
|
+ " ORDER BY 1 %s ASC LIMIT 1",
|
|
+ zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl
|
|
+ );
|
|
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
|
|
+ if( zColl[0] ) sqlite3_free(zColl);
|
|
+ if( zSql==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rc = sqlite3_prepare_v2(c.db, zSql, -1, &c.pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ){
|
|
+ sqlite3_result_error(context, sqlite3_errmsg(c.db), -1);
|
|
+ return;
|
|
+ }
|
|
+ findNextChars(&c);
|
|
+ if( c.mallocFailed ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ unsigned char *pRes;
|
|
+ pRes = sqlite3_malloc( c.nUsed*4 + 1 );
|
|
+ if( pRes==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ int i;
|
|
+ int n = 0;
|
|
+ for(i=0; i<c.nUsed; i++){
|
|
+ n += writeUtf8(pRes+n, c.aResult[i]);
|
|
+ }
|
|
+ pRes[n] = 0;
|
|
+ sqlite3_result_text(context, (const char*)pRes, n, sqlite3_free);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(c.pStmt);
|
|
+ sqlite3_free(c.aResult);
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_nextchar_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
|
+ nextCharFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
|
+ nextCharFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
|
|
+ nextCharFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
|
+ nextCharFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
|
+ nextCharFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
|
|
+ nextCharFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/normalize.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/normalize.c 2019-05-14 16:22:31.251759200 +0200
|
|
@@ -0,0 +1,707 @@
|
|
+/*
|
|
+** 2018-01-08
|
|
+**
|
|
+** 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 to implement the sqlite3_normalize() function.
|
|
+**
|
|
+** char *sqlite3_normalize(const char *zSql);
|
|
+**
|
|
+** This function takes an SQL string as input and returns a "normalized"
|
|
+** version of that string in memory obtained from sqlite3_malloc64(). The
|
|
+** caller is responsible for ensuring that the returned memory is freed.
|
|
+**
|
|
+** If a memory allocation error occurs, this routine returns NULL.
|
|
+**
|
|
+** The normalization consists of the following transformations:
|
|
+**
|
|
+** (1) Convert every literal (string, blob literal, numeric constant,
|
|
+** or "NULL" constant) into a ?
|
|
+**
|
|
+** (2) Remove all superfluous whitespace, including comments. Change
|
|
+** all required whitespace to a single space character.
|
|
+**
|
|
+** (3) Lowercase all ASCII characters.
|
|
+**
|
|
+** (4) If an IN or NOT IN operator is followed by a list of 1 or more
|
|
+** values, convert that list into "(?,?,?)".
|
|
+**
|
|
+** The purpose of normalization is two-fold:
|
|
+**
|
|
+** (1) Sanitize queries by removing potentially private or sensitive
|
|
+** information contained in literals.
|
|
+**
|
|
+** (2) Identify structurally identical queries by comparing their
|
|
+** normalized forms.
|
|
+**
|
|
+** Command-Line Utility
|
|
+** --------------------
|
|
+**
|
|
+** This file also contains code for a command-line utility that converts
|
|
+** SQL queries in text files into their normalized forms. To build the
|
|
+** command-line program, compile this file with -DSQLITE_NORMALIZE_CLI
|
|
+** and link it against the SQLite library.
|
|
+*/
|
|
+#include <sqlite3.h>
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Implementation note:
|
|
+**
|
|
+** Much of the tokenizer logic is copied out of the tokenize.c source file
|
|
+** of SQLite. That logic could be simplified for this particular application,
|
|
+** but that would impose a risk of introducing subtle errors. It is best to
|
|
+** keep the code as close to the original as possible.
|
|
+**
|
|
+** The tokenize code is in sync with the SQLite core as of 2018-01-08.
|
|
+** Any future changes to the core tokenizer might require corresponding
|
|
+** adjustments to the tokenizer logic in this module.
|
|
+*/
|
|
+
|
|
+
|
|
+/* Character classes for tokenizing
|
|
+**
|
|
+** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented
|
|
+** using a lookup table, whereas a switch() directly on c uses a binary search.
|
|
+** The lookup table is much faster. To maximize speed, and to ensure that
|
|
+** a lookup table is used, all of the classes need to be small integers and
|
|
+** all of them need to be used within the switch.
|
|
+*/
|
|
+#define CC_X 0 /* The letter 'x', or start of BLOB literal */
|
|
+#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */
|
|
+#define CC_ID 2 /* unicode characters usable in IDs */
|
|
+#define CC_DIGIT 3 /* Digits */
|
|
+#define CC_DOLLAR 4 /* '$' */
|
|
+#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
|
|
+#define CC_VARNUM 6 /* '?'. Numeric SQL variables */
|
|
+#define CC_SPACE 7 /* Space characters */
|
|
+#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */
|
|
+#define CC_QUOTE2 9 /* '['. [...] style quoted ids */
|
|
+#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */
|
|
+#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */
|
|
+#define CC_LT 12 /* '<'. Part of < or <= or <> */
|
|
+#define CC_GT 13 /* '>'. Part of > or >= */
|
|
+#define CC_EQ 14 /* '='. Part of = or == */
|
|
+#define CC_BANG 15 /* '!'. Part of != */
|
|
+#define CC_SLASH 16 /* '/'. / or c-style comment */
|
|
+#define CC_LP 17 /* '(' */
|
|
+#define CC_RP 18 /* ')' */
|
|
+#define CC_SEMI 19 /* ';' */
|
|
+#define CC_PLUS 20 /* '+' */
|
|
+#define CC_STAR 21 /* '*' */
|
|
+#define CC_PERCENT 22 /* '%' */
|
|
+#define CC_COMMA 23 /* ',' */
|
|
+#define CC_AND 24 /* '&' */
|
|
+#define CC_TILDA 25 /* '~' */
|
|
+#define CC_DOT 26 /* '.' */
|
|
+#define CC_ILLEGAL 27 /* Illegal character */
|
|
+
|
|
+static const unsigned char aiClass[] = {
|
|
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
|
|
+/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
|
|
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
|
+/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
|
|
+/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
|
|
+/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1,
|
|
+/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27,
|
|
+/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
+/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
|
|
+};
|
|
+
|
|
+/* An array to map all upper-case characters into their corresponding
|
|
+** lower-case character.
|
|
+**
|
|
+** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
|
|
+** handle case conversions for the UTF character set since the tables
|
|
+** involved are nearly as big or bigger than SQLite itself.
|
|
+*/
|
|
+static const unsigned char sqlite3UpperToLower[] = {
|
|
+ 0, 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, 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, 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
|
|
+};
|
|
+
|
|
+/*
|
|
+** The following 256 byte lookup table is used to support SQLites built-in
|
|
+** equivalents to the following standard library functions:
|
|
+**
|
|
+** isspace() 0x01
|
|
+** isalpha() 0x02
|
|
+** isdigit() 0x04
|
|
+** isalnum() 0x06
|
|
+** isxdigit() 0x08
|
|
+** toupper() 0x20
|
|
+** SQLite identifier character 0x40
|
|
+** Quote character 0x80
|
|
+**
|
|
+** Bit 0x20 is set if the mapped character requires translation to upper
|
|
+** case. i.e. if the character is a lower-case ASCII character.
|
|
+** If x is a lower-case ASCII character, then its upper-case equivalent
|
|
+** is (x - 0x20). Therefore toupper() can be implemented as:
|
|
+**
|
|
+** (x & ~(map[x]&0x20))
|
|
+**
|
|
+** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
|
|
+** array. tolower() is used more often than toupper() by SQLite.
|
|
+**
|
|
+** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
|
|
+** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
|
|
+** non-ASCII UTF character. Hence the test for whether or not a character is
|
|
+** part of an identifier is 0x46.
|
|
+*/
|
|
+static const unsigned char sqlite3CtypeMap[256] = {
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
|
|
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
|
|
+ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
|
|
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
|
|
+ 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
|
|
+
|
|
+ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
|
|
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
|
|
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
|
|
+ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
|
|
+ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
|
|
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
|
|
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
|
|
+ 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
|
|
+
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */
|
|
+
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
|
|
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
|
|
+};
|
|
+#define sqlite3Toupper(x) ((x)&~(sqlite3CtypeMap[(unsigned char)(x)]&0x20))
|
|
+#define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01)
|
|
+#define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06)
|
|
+#define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02)
|
|
+#define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04)
|
|
+#define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
|
|
+#define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)])
|
|
+#define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80)
|
|
+
|
|
+
|
|
+/*
|
|
+** If X is a character that can be used in an identifier then
|
|
+** IdChar(X) will be true. Otherwise it is false.
|
|
+**
|
|
+** For ASCII, any character with the high-order bit set is
|
|
+** allowed in an identifier. For 7-bit characters,
|
|
+** sqlite3IsIdChar[X] must be 1.
|
|
+**
|
|
+** For EBCDIC, the rules are more complex but have the same
|
|
+** end result.
|
|
+**
|
|
+** Ticket #1066. the SQL standard does not allow '$' in the
|
|
+** middle of identifiers. But many SQL implementations do.
|
|
+** SQLite will allow '$' in identifiers for compatibility.
|
|
+** But the feature is undocumented.
|
|
+*/
|
|
+#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
|
|
+
|
|
+/*
|
|
+** Ignore testcase() macros
|
|
+*/
|
|
+#define testcase(X)
|
|
+
|
|
+/*
|
|
+** Token values
|
|
+*/
|
|
+#define TK_SPACE 0
|
|
+#define TK_NAME 1
|
|
+#define TK_LITERAL 2
|
|
+#define TK_PUNCT 3
|
|
+#define TK_ERROR 4
|
|
+
|
|
+#define TK_MINUS TK_PUNCT
|
|
+#define TK_LP TK_PUNCT
|
|
+#define TK_RP TK_PUNCT
|
|
+#define TK_SEMI TK_PUNCT
|
|
+#define TK_PLUS TK_PUNCT
|
|
+#define TK_STAR TK_PUNCT
|
|
+#define TK_SLASH TK_PUNCT
|
|
+#define TK_REM TK_PUNCT
|
|
+#define TK_EQ TK_PUNCT
|
|
+#define TK_LE TK_PUNCT
|
|
+#define TK_NE TK_PUNCT
|
|
+#define TK_LSHIFT TK_PUNCT
|
|
+#define TK_LT TK_PUNCT
|
|
+#define TK_GE TK_PUNCT
|
|
+#define TK_RSHIFT TK_PUNCT
|
|
+#define TK_GT TK_PUNCT
|
|
+#define TK_GE TK_PUNCT
|
|
+#define TK_BITOR TK_PUNCT
|
|
+#define TK_CONCAT TK_PUNCT
|
|
+#define TK_COMMA TK_PUNCT
|
|
+#define TK_BITAND TK_PUNCT
|
|
+#define TK_BITNOT TK_PUNCT
|
|
+#define TK_STRING TK_LITERAL
|
|
+#define TK_ID TK_NAME
|
|
+#define TK_ILLEGAL TK_ERROR
|
|
+#define TK_DOT TK_PUNCT
|
|
+#define TK_INTEGER TK_LITERAL
|
|
+#define TK_FLOAT TK_LITERAL
|
|
+#define TK_VARIABLE TK_LITERAL
|
|
+#define TK_BLOB TK_LITERAL
|
|
+
|
|
+/*
|
|
+** Return the length (in bytes) of the token that begins at z[0].
|
|
+** Store the token type in *tokenType before returning.
|
|
+*/
|
|
+static int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
|
+ int i, c;
|
|
+ switch( aiClass[*z] ){ /* Switch on the character-class of the first byte
|
|
+ ** of the token. See the comment on the CC_ defines
|
|
+ ** above. */
|
|
+ case CC_SPACE: {
|
|
+ for(i=1; sqlite3Isspace(z[i]); i++){}
|
|
+ *tokenType = TK_SPACE;
|
|
+ return i;
|
|
+ }
|
|
+ case CC_MINUS: {
|
|
+ if( z[1]=='-' ){
|
|
+ for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
|
|
+ *tokenType = TK_SPACE;
|
|
+ return i;
|
|
+ }
|
|
+ *tokenType = TK_MINUS;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_LP: {
|
|
+ *tokenType = TK_LP;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_RP: {
|
|
+ *tokenType = TK_RP;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_SEMI: {
|
|
+ *tokenType = TK_SEMI;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_PLUS: {
|
|
+ *tokenType = TK_PLUS;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_STAR: {
|
|
+ *tokenType = TK_STAR;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_SLASH: {
|
|
+ if( z[1]!='*' || z[2]==0 ){
|
|
+ *tokenType = TK_SLASH;
|
|
+ return 1;
|
|
+ }
|
|
+ for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
|
|
+ if( c ) i++;
|
|
+ *tokenType = TK_SPACE;
|
|
+ return i;
|
|
+ }
|
|
+ case CC_PERCENT: {
|
|
+ *tokenType = TK_REM;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_EQ: {
|
|
+ *tokenType = TK_EQ;
|
|
+ return 1 + (z[1]=='=');
|
|
+ }
|
|
+ case CC_LT: {
|
|
+ if( (c=z[1])=='=' ){
|
|
+ *tokenType = TK_LE;
|
|
+ return 2;
|
|
+ }else if( c=='>' ){
|
|
+ *tokenType = TK_NE;
|
|
+ return 2;
|
|
+ }else if( c=='<' ){
|
|
+ *tokenType = TK_LSHIFT;
|
|
+ return 2;
|
|
+ }else{
|
|
+ *tokenType = TK_LT;
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ case CC_GT: {
|
|
+ if( (c=z[1])=='=' ){
|
|
+ *tokenType = TK_GE;
|
|
+ return 2;
|
|
+ }else if( c=='>' ){
|
|
+ *tokenType = TK_RSHIFT;
|
|
+ return 2;
|
|
+ }else{
|
|
+ *tokenType = TK_GT;
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ case CC_BANG: {
|
|
+ if( z[1]!='=' ){
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ return 1;
|
|
+ }else{
|
|
+ *tokenType = TK_NE;
|
|
+ return 2;
|
|
+ }
|
|
+ }
|
|
+ case CC_PIPE: {
|
|
+ if( z[1]!='|' ){
|
|
+ *tokenType = TK_BITOR;
|
|
+ return 1;
|
|
+ }else{
|
|
+ *tokenType = TK_CONCAT;
|
|
+ return 2;
|
|
+ }
|
|
+ }
|
|
+ case CC_COMMA: {
|
|
+ *tokenType = TK_COMMA;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_AND: {
|
|
+ *tokenType = TK_BITAND;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_TILDA: {
|
|
+ *tokenType = TK_BITNOT;
|
|
+ return 1;
|
|
+ }
|
|
+ case CC_QUOTE: {
|
|
+ int delim = z[0];
|
|
+ testcase( delim=='`' );
|
|
+ testcase( delim=='\'' );
|
|
+ testcase( delim=='"' );
|
|
+ for(i=1; (c=z[i])!=0; i++){
|
|
+ if( c==delim ){
|
|
+ if( z[i+1]==delim ){
|
|
+ i++;
|
|
+ }else{
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( c=='\'' ){
|
|
+ *tokenType = TK_STRING;
|
|
+ return i+1;
|
|
+ }else if( c!=0 ){
|
|
+ *tokenType = TK_ID;
|
|
+ return i+1;
|
|
+ }else{
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+ case CC_DOT: {
|
|
+ if( !sqlite3Isdigit(z[1]) ){
|
|
+ *tokenType = TK_DOT;
|
|
+ return 1;
|
|
+ }
|
|
+ /* If the next character is a digit, this is a floating point
|
|
+ ** number that begins with ".". Fall thru into the next case */
|
|
+ }
|
|
+ case CC_DIGIT: {
|
|
+ *tokenType = TK_INTEGER;
|
|
+ if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
|
|
+ for(i=3; sqlite3Isxdigit(z[i]); i++){}
|
|
+ return i;
|
|
+ }
|
|
+ for(i=0; sqlite3Isdigit(z[i]); i++){}
|
|
+ if( z[i]=='.' ){
|
|
+ i++;
|
|
+ while( sqlite3Isdigit(z[i]) ){ i++; }
|
|
+ *tokenType = TK_FLOAT;
|
|
+ }
|
|
+ if( (z[i]=='e' || z[i]=='E') &&
|
|
+ ( sqlite3Isdigit(z[i+1])
|
|
+ || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
|
|
+ )
|
|
+ ){
|
|
+ i += 2;
|
|
+ while( sqlite3Isdigit(z[i]) ){ i++; }
|
|
+ *tokenType = TK_FLOAT;
|
|
+ }
|
|
+ while( IdChar(z[i]) ){
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ i++;
|
|
+ }
|
|
+ return i;
|
|
+ }
|
|
+ case CC_QUOTE2: {
|
|
+ for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
|
|
+ *tokenType = c==']' ? TK_ID : TK_ILLEGAL;
|
|
+ return i;
|
|
+ }
|
|
+ case CC_VARNUM: {
|
|
+ *tokenType = TK_VARIABLE;
|
|
+ for(i=1; sqlite3Isdigit(z[i]); i++){}
|
|
+ return i;
|
|
+ }
|
|
+ case CC_DOLLAR:
|
|
+ case CC_VARALPHA: {
|
|
+ int n = 0;
|
|
+ testcase( z[0]=='$' ); testcase( z[0]=='@' );
|
|
+ testcase( z[0]==':' ); testcase( z[0]=='#' );
|
|
+ *tokenType = TK_VARIABLE;
|
|
+ for(i=1; (c=z[i])!=0; i++){
|
|
+ if( IdChar(c) ){
|
|
+ n++;
|
|
+ }else if( c=='(' && n>0 ){
|
|
+ do{
|
|
+ i++;
|
|
+ }while( (c=z[i])!=0 && !sqlite3Isspace(c) && c!=')' );
|
|
+ if( c==')' ){
|
|
+ i++;
|
|
+ }else{
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ }
|
|
+ break;
|
|
+ }else if( c==':' && z[i+1]==':' ){
|
|
+ i++;
|
|
+ }else{
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( n==0 ) *tokenType = TK_ILLEGAL;
|
|
+ return i;
|
|
+ }
|
|
+ case CC_KYWD: {
|
|
+ for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
|
|
+ if( IdChar(z[i]) ){
|
|
+ /* This token started out using characters that can appear in keywords,
|
|
+ ** but z[i] is a character not allowed within keywords, so this must
|
|
+ ** be an identifier instead */
|
|
+ i++;
|
|
+ break;
|
|
+ }
|
|
+ *tokenType = TK_ID;
|
|
+ return i;
|
|
+ }
|
|
+ case CC_X: {
|
|
+ testcase( z[0]=='x' ); testcase( z[0]=='X' );
|
|
+ if( z[1]=='\'' ){
|
|
+ *tokenType = TK_BLOB;
|
|
+ for(i=2; sqlite3Isxdigit(z[i]); i++){}
|
|
+ if( z[i]!='\'' || i%2 ){
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ while( z[i] && z[i]!='\'' ){ i++; }
|
|
+ }
|
|
+ if( z[i] ) i++;
|
|
+ return i;
|
|
+ }
|
|
+ /* If it is not a BLOB literal, then it must be an ID, since no
|
|
+ ** SQL keywords start with the letter 'x'. Fall through */
|
|
+ }
|
|
+ case CC_ID: {
|
|
+ i = 1;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ while( IdChar(z[i]) ){ i++; }
|
|
+ *tokenType = TK_ID;
|
|
+ return i;
|
|
+}
|
|
+
|
|
+char *sqlite3_normalize(const char *zSql){
|
|
+ char *z; /* The output string */
|
|
+ size_t nZ; /* Size of the output string in bytes */
|
|
+ size_t nSql; /* Size of the input string in bytes */
|
|
+ int i; /* Next character to read from zSql[] */
|
|
+ int j; /* Next slot to fill in on z[] */
|
|
+ int tokenType; /* Type of the next token */
|
|
+ int n; /* Size of the next token */
|
|
+ int k; /* Loop counter */
|
|
+
|
|
+ nSql = strlen(zSql);
|
|
+ nZ = nSql;
|
|
+ z = sqlite3_malloc( nZ+2 );
|
|
+ if( z==0 ) return 0;
|
|
+ for(i=j=0; zSql[i]; i += n){
|
|
+ n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType);
|
|
+ switch( tokenType ){
|
|
+ case TK_SPACE: {
|
|
+ break;
|
|
+ }
|
|
+ case TK_ERROR: {
|
|
+ sqlite3_free(z);
|
|
+ return 0;
|
|
+ }
|
|
+ case TK_LITERAL: {
|
|
+ z[j++] = '?';
|
|
+ break;
|
|
+ }
|
|
+ case TK_PUNCT:
|
|
+ case TK_NAME: {
|
|
+ if( n==4 && sqlite3_strnicmp(zSql+i,"NULL",4)==0 ){
|
|
+ if( (j>=3 && strncmp(z+j-2,"is",2)==0 && !IdChar(z[j-3]))
|
|
+ || (j>=4 && strncmp(z+j-3,"not",3)==0 && !IdChar(z[j-4]))
|
|
+ ){
|
|
+ /* NULL is a keyword in this case, not a literal value */
|
|
+ }else{
|
|
+ /* Here the NULL is a literal value */
|
|
+ z[j++] = '?';
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( j>0 && IdChar(z[j-1]) && IdChar(zSql[i]) ) z[j++] = ' ';
|
|
+ for(k=0; k<n; k++){
|
|
+ z[j++] = sqlite3Tolower(zSql[i+k]);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ while( j>0 && z[j-1]==' ' ){ j--; }
|
|
+ if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; }
|
|
+ z[j] = 0;
|
|
+
|
|
+ /* Make a second pass converting "in(...)" where the "..." is not a
|
|
+ ** SELECT statement into "in(?,?,?)" */
|
|
+ for(i=0; i<j; i=n){
|
|
+ char *zIn = strstr(z+i, "in(");
|
|
+ int nParen;
|
|
+ if( zIn==0 ) break;
|
|
+ n = (int)(zIn-z)+3; /* Index of first char past "in(" */
|
|
+ if( n && IdChar(zIn[-1]) ) continue;
|
|
+ if( strncmp(zIn, "in(select",9)==0 && !IdChar(zIn[9]) ) continue;
|
|
+ if( strncmp(zIn, "in(with",7)==0 && !IdChar(zIn[7]) ) continue;
|
|
+ for(nParen=1, k=0; z[n+k]; k++){
|
|
+ if( z[n+k]=='(' ) nParen++;
|
|
+ if( z[n+k]==')' ){
|
|
+ nParen--;
|
|
+ if( nParen==0 ) break;
|
|
+ }
|
|
+ }
|
|
+ /* k is the number of bytes in the "..." within "in(...)" */
|
|
+ if( k<5 ){
|
|
+ z = sqlite3_realloc64(z, j+(5-k)+1);
|
|
+ if( z==0 ) return 0;
|
|
+ memmove(z+n+5, z+n+k, j-(n+k));
|
|
+ }else if( k>5 ){
|
|
+ memmove(z+n+5, z+n+k, j-(n+k));
|
|
+ }
|
|
+ j = j-k+5;
|
|
+ z[j] = 0;
|
|
+ memcpy(z+n, "?,?,?", 5);
|
|
+ }
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/*
|
|
+** For testing purposes, or to build a stand-alone SQL normalizer program,
|
|
+** compile this one source file with the -DSQLITE_NORMALIZE_CLI and link
|
|
+** it against any SQLite library. The resulting command-line program will
|
|
+** run sqlite3_normalize() over the text of all files named on the command-
|
|
+** line and show the result on standard output.
|
|
+*/
|
|
+#ifdef SQLITE_NORMALIZE_CLI
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+/*
|
|
+** Break zIn up into separate SQL statements and run sqlite3_normalize()
|
|
+** on each one. Print the result of each run.
|
|
+*/
|
|
+static void normalizeFile(char *zIn){
|
|
+ int i;
|
|
+ if( zIn==0 ) return;
|
|
+ for(i=0; zIn[i]; i++){
|
|
+ char cSaved;
|
|
+ if( zIn[i]!=';' ) continue;
|
|
+ cSaved = zIn[i+1];
|
|
+ zIn[i+1] = 0;
|
|
+ if( sqlite3_complete(zIn) ){
|
|
+ char *zOut = sqlite3_normalize(zIn);
|
|
+ if( zOut ){
|
|
+ printf("%s\n", zOut);
|
|
+ sqlite3_free(zOut);
|
|
+ }else{
|
|
+ fprintf(stderr, "ERROR: %s\n", zIn);
|
|
+ }
|
|
+ zIn[i+1] = cSaved;
|
|
+ zIn += i+1;
|
|
+ i = -1;
|
|
+ }else{
|
|
+ zIn[i+1] = cSaved;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The main routine for "sql_normalize". Read files named on the
|
|
+** command-line and run the text of each through sqlite3_normalize().
|
|
+*/
|
|
+int main(int argc, char **argv){
|
|
+ int i;
|
|
+ FILE *in;
|
|
+ char *zBuf = 0;
|
|
+ sqlite3_int64 sz, got;
|
|
+
|
|
+ for(i=1; i<argc; i++){
|
|
+ in = fopen(argv[i], "rb");
|
|
+ if( in==0 ){
|
|
+ fprintf(stderr, "cannot open \"%s\"\n", argv[i]);
|
|
+ continue;
|
|
+ }
|
|
+ fseek(in, 0, SEEK_END);
|
|
+ sz = ftell(in);
|
|
+ rewind(in);
|
|
+ zBuf = sqlite3_realloc64(zBuf, sz+1);
|
|
+ if( zBuf==0 ){
|
|
+ fprintf(stderr, "failed to malloc for %lld bytes\n", sz);
|
|
+ exit(1);
|
|
+ }
|
|
+ got = fread(zBuf, 1, sz, in);
|
|
+ fclose(in);
|
|
+ if( got!=sz ){
|
|
+ fprintf(stderr, "only able to read %lld of %lld bytes from \"%s\"\n",
|
|
+ got, sz, argv[i]);
|
|
+ }else{
|
|
+ zBuf[got] = 0;
|
|
+ normalizeFile(zBuf);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zBuf);
|
|
+}
|
|
+#endif /* SQLITE_NORMALIZE_CLI */
|
|
--- origsrc/sqlite-autoconf-3280000/percentile.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/percentile.c 2019-05-14 16:22:31.253711900 +0200
|
|
@@ -0,0 +1,233 @@
|
|
+/*
|
|
+** 2013-05-28
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file contains code to implement the percentile(Y,P) SQL function
|
|
+** as described below:
|
|
+**
|
|
+** (1) The percentile(Y,P) function is an aggregate function taking
|
|
+** exactly two arguments.
|
|
+**
|
|
+** (2) If the P argument to percentile(Y,P) is not the same for every
|
|
+** row in the aggregate then an error is thrown. The word "same"
|
|
+** in the previous sentence means that the value differ by less
|
|
+** than 0.001.
|
|
+**
|
|
+** (3) If the P argument to percentile(Y,P) evaluates to anything other
|
|
+** than a number in the range of 0.0 to 100.0 inclusive then an
|
|
+** error is thrown.
|
|
+**
|
|
+** (4) If any Y argument to percentile(Y,P) evaluates to a value that
|
|
+** is not NULL and is not numeric then an error is thrown.
|
|
+**
|
|
+** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
|
|
+** infinity then an error is thrown. (SQLite always interprets NaN
|
|
+** values as NULL.)
|
|
+**
|
|
+** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
|
|
+** including CASE WHEN expressions.
|
|
+**
|
|
+** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
|
|
+** one million (1,000,000) rows.
|
|
+**
|
|
+** (8) If there are no non-NULL values for Y, then percentile(Y,P)
|
|
+** returns NULL.
|
|
+**
|
|
+** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
|
|
+** returns the one Y value.
|
|
+**
|
|
+** (10) If there N non-NULL values of Y where N is two or more and
|
|
+** the Y values are ordered from least to greatest and a graph is
|
|
+** drawn from 0 to N-1 such that the height of the graph at J is
|
|
+** the J-th Y value and such that straight lines are drawn between
|
|
+** adjacent Y values, then the percentile(Y,P) function returns
|
|
+** the height of the graph at P*(N-1)/100.
|
|
+**
|
|
+** (11) The percentile(Y,P) function always returns either a floating
|
|
+** point number or NULL.
|
|
+**
|
|
+** (12) The percentile(Y,P) is implemented as a single C99 source-code
|
|
+** file that compiles into a shared-library or DLL that can be loaded
|
|
+** into SQLite using the sqlite3_load_extension() interface.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+/* The following object is the session context for a single percentile()
|
|
+** function. We have to remember all input Y values until the very end.
|
|
+** Those values are accumulated in the Percentile.a[] array.
|
|
+*/
|
|
+typedef struct Percentile Percentile;
|
|
+struct Percentile {
|
|
+ unsigned nAlloc; /* Number of slots allocated for a[] */
|
|
+ unsigned nUsed; /* Number of slots actually used in a[] */
|
|
+ double rPct; /* 1.0 more than the value for P */
|
|
+ double *a; /* Array of Y values */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Return TRUE if the input floating-point number is an infinity.
|
|
+*/
|
|
+static int isInfinity(double r){
|
|
+ sqlite3_uint64 u;
|
|
+ assert( sizeof(u)==sizeof(r) );
|
|
+ memcpy(&u, &r, sizeof(u));
|
|
+ return ((u>>52)&0x7ff)==0x7ff;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if two doubles differ by 0.001 or less
|
|
+*/
|
|
+static int sameValue(double a, double b){
|
|
+ a -= b;
|
|
+ return a>=-0.001 && a<=0.001;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The "step" function for percentile(Y,P) is called once for each
|
|
+** input row.
|
|
+*/
|
|
+static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
|
+ Percentile *p;
|
|
+ double rPct;
|
|
+ int eType;
|
|
+ double y;
|
|
+ assert( argc==2 );
|
|
+
|
|
+ /* Requirement 3: P must be a number between 0 and 100 */
|
|
+ eType = sqlite3_value_numeric_type(argv[1]);
|
|
+ rPct = sqlite3_value_double(argv[1]);
|
|
+ if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|
|
+ || rPct<0.0 || rPct>100.0 ){
|
|
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
|
|
+ "a number between 0.0 and 100.0", -1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Allocate the session context. */
|
|
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p==0 ) return;
|
|
+
|
|
+ /* Remember the P value. Throw an error if the P value is different
|
|
+ ** from any prior row, per Requirement (2). */
|
|
+ if( p->rPct==0.0 ){
|
|
+ p->rPct = rPct+1.0;
|
|
+ }else if( !sameValue(p->rPct,rPct+1.0) ){
|
|
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
|
|
+ "same for all input rows", -1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Ignore rows for which Y is NULL */
|
|
+ eType = sqlite3_value_type(argv[0]);
|
|
+ if( eType==SQLITE_NULL ) return;
|
|
+
|
|
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
|
|
+ ** Requirement 4 */
|
|
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
|
|
+ sqlite3_result_error(pCtx, "1st argument to percentile() is not "
|
|
+ "numeric", -1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Throw an error if the Y value is infinity or NaN */
|
|
+ y = sqlite3_value_double(argv[0]);
|
|
+ if( isInfinity(y) ){
|
|
+ sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Allocate and store the Y */
|
|
+ if( p->nUsed>=p->nAlloc ){
|
|
+ unsigned n = p->nAlloc*2 + 250;
|
|
+ double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
|
|
+ if( a==0 ){
|
|
+ sqlite3_free(p->a);
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ return;
|
|
+ }
|
|
+ p->nAlloc = n;
|
|
+ p->a = a;
|
|
+ }
|
|
+ p->a[p->nUsed++] = y;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compare to doubles for sorting using qsort()
|
|
+*/
|
|
+static int SQLITE_CDECL doubleCmp(const void *pA, const void *pB){
|
|
+ double a = *(double*)pA;
|
|
+ double b = *(double*)pB;
|
|
+ if( a==b ) return 0;
|
|
+ if( a<b ) return -1;
|
|
+ return +1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called to compute the final output of percentile() and to clean
|
|
+** up all allocated memory.
|
|
+*/
|
|
+static void percentFinal(sqlite3_context *pCtx){
|
|
+ Percentile *p;
|
|
+ unsigned i1, i2;
|
|
+ double v1, v2;
|
|
+ double ix, vx;
|
|
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
|
|
+ if( p==0 ) return;
|
|
+ if( p->a==0 ) return;
|
|
+ if( p->nUsed ){
|
|
+ qsort(p->a, p->nUsed, sizeof(double), doubleCmp);
|
|
+ ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
|
|
+ i1 = (unsigned)ix;
|
|
+ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
|
|
+ v1 = p->a[i1];
|
|
+ v2 = p->a[i2];
|
|
+ vx = v1 + (v2-v1)*(ix-i1);
|
|
+ sqlite3_result_double(pCtx, vx);
|
|
+ }
|
|
+ sqlite3_free(p->a);
|
|
+ memset(p, 0, sizeof(*p));
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_percentile_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
|
|
+ 0, percentStep, percentFinal);
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
|
|
+ 0, percentStep, percentFinal);
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/prefixes.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/prefixes.c 2019-05-14 16:22:31.255664600 +0200
|
|
@@ -0,0 +1,319 @@
|
|
+/*
|
|
+** 2018-04-19
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements a table-valued function:
|
|
+**
|
|
+** prefixes('abcdefg')
|
|
+**
|
|
+** The function has a single (non-HIDDEN) column named prefix that takes
|
|
+** on all prefixes of the string in its argument, including an empty string
|
|
+** and the input string itself. The order of prefixes is from longest
|
|
+** to shortest.
|
|
+*/
|
|
+#if !defined(SQLITE_CORE) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+/* prefixes_vtab is a subclass of sqlite3_vtab which is
|
|
+** underlying representation of the virtual table
|
|
+*/
|
|
+typedef struct prefixes_vtab prefixes_vtab;
|
|
+struct prefixes_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ /* No additional fields are necessary */
|
|
+};
|
|
+
|
|
+/* prefixes_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct prefixes_cursor prefixes_cursor;
|
|
+struct prefixes_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+ char *zStr; /* Original string to be prefixed */
|
|
+ int nStr; /* Length of the string in bytes */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The prefixesConnect() method is invoked to create a new
|
|
+** template virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for prefixes_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the prefixes_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against the virtual table will look like.
|
|
+*/
|
|
+static int prefixesConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ prefixes_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE prefixes(prefix TEXT, original_string TEXT HIDDEN)"
|
|
+ );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for prefixes_vtab objects.
|
|
+*/
|
|
+static int prefixesDisconnect(sqlite3_vtab *pVtab){
|
|
+ prefixes_vtab *p = (prefixes_vtab*)pVtab;
|
|
+ sqlite3_free(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new prefixes_cursor object.
|
|
+*/
|
|
+static int prefixesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ prefixes_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a prefixes_cursor.
|
|
+*/
|
|
+static int prefixesClose(sqlite3_vtab_cursor *cur){
|
|
+ prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
+ sqlite3_free(pCur->zStr);
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a prefixes_cursor to its next row of output.
|
|
+*/
|
|
+static int prefixesNext(sqlite3_vtab_cursor *cur){
|
|
+ prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
+ pCur->iRowid++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the prefixes_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int prefixesColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case 0:
|
|
+ sqlite3_result_text(ctx, pCur->zStr, pCur->nStr - (int)pCur->iRowid,
|
|
+ 0);
|
|
+ break;
|
|
+ default:
|
|
+ sqlite3_result_text(ctx, pCur->zStr, pCur->nStr, 0);
|
|
+ break;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int prefixesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int prefixesEof(sqlite3_vtab_cursor *cur){
|
|
+ prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
+ return pCur->iRowid>pCur->nStr;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the prefixes_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to prefixesColumn() or prefixesRowid() or
|
|
+** prefixesEof().
|
|
+*/
|
|
+static int prefixesFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ prefixes_cursor *pCur = (prefixes_cursor *)pVtabCursor;
|
|
+ sqlite3_free(pCur->zStr);
|
|
+ if( argc>0 ){
|
|
+ pCur->zStr = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
|
+ pCur->nStr = pCur->zStr ? (int)strlen(pCur->zStr) : 0;
|
|
+ }else{
|
|
+ pCur->zStr = 0;
|
|
+ pCur->nStr = 0;
|
|
+ }
|
|
+ pCur->iRowid = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int prefixesBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ /* Search for a usable equality constraint against column 1
|
|
+ ** (original_string) and use it if at all possible */
|
|
+ int i;
|
|
+ const struct sqlite3_index_constraint *p;
|
|
+
|
|
+ for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
|
|
+ if( p->iColumn!=1 ) continue;
|
|
+ if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ if( !p->usable ) continue;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ pIdxInfo->estimatedCost = (double)10;
|
|
+ pIdxInfo->estimatedRows = 10;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ pIdxInfo->estimatedCost = (double)1000000000;
|
|
+ pIdxInfo->estimatedRows = 1000000000;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** virtual table.
|
|
+*/
|
|
+static sqlite3_module prefixesModule = {
|
|
+ /* iVersion */ 0,
|
|
+ /* xCreate */ 0,
|
|
+ /* xConnect */ prefixesConnect,
|
|
+ /* xBestIndex */ prefixesBestIndex,
|
|
+ /* xDisconnect */ prefixesDisconnect,
|
|
+ /* xDestroy */ 0,
|
|
+ /* xOpen */ prefixesOpen,
|
|
+ /* xClose */ prefixesClose,
|
|
+ /* xFilter */ prefixesFilter,
|
|
+ /* xNext */ prefixesNext,
|
|
+ /* xEof */ prefixesEof,
|
|
+ /* xColumn */ prefixesColumn,
|
|
+ /* xRowid */ prefixesRowid,
|
|
+ /* xUpdate */ 0,
|
|
+ /* xBegin */ 0,
|
|
+ /* xSync */ 0,
|
|
+ /* xCommit */ 0,
|
|
+ /* xRollback */ 0,
|
|
+ /* xFindMethod */ 0,
|
|
+ /* xRename */ 0,
|
|
+ /* xSavepoint */ 0,
|
|
+ /* xRelease */ 0,
|
|
+ /* xRollbackTo */ 0,
|
|
+ /* xShadowName */ 0
|
|
+};
|
|
+
|
|
+/*
|
|
+** This is a copy of the SQLITE_SKIP_UTF8(zIn) macro in sqliteInt.h.
|
|
+**
|
|
+** Assuming zIn points to the first byte of a UTF-8 character,
|
|
+** advance zIn to point to the first byte of the next UTF-8 character.
|
|
+*/
|
|
+#define PREFIX_SKIP_UTF8(zIn) { \
|
|
+ if( (*(zIn++))>=0xc0 ){ \
|
|
+ while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
|
|
+ } \
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of function prefix_length(). This function accepts two
|
|
+** strings as arguments and returns the length in characters (not bytes),
|
|
+** of the longest prefix shared by the two strings. For example:
|
|
+**
|
|
+** prefix_length('abcdxxx', 'abcyy') == 3
|
|
+** prefix_length('abcdxxx', 'bcyyy') == 0
|
|
+** prefix_length('abcdxxx', 'ab') == 2
|
|
+** prefix_length('ab', 'abcd') == 2
|
|
+**
|
|
+** This function assumes the input is well-formed utf-8. If it is not,
|
|
+** it is possible for this function to return -1.
|
|
+*/
|
|
+static void prefixLengthFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal
|
|
+){
|
|
+ int nByte; /* Number of bytes to compare */
|
|
+ int nRet = 0; /* Return value */
|
|
+ const unsigned char *zL = sqlite3_value_text(apVal[0]);
|
|
+ const unsigned char *zR = sqlite3_value_text(apVal[1]);
|
|
+ int nL = sqlite3_value_bytes(apVal[0]);
|
|
+ int nR = sqlite3_value_bytes(apVal[1]);
|
|
+ int i;
|
|
+
|
|
+ nByte = (nL > nR ? nL : nR);
|
|
+ for(i=0; i<nByte; i++){
|
|
+ if( zL[i]!=zR[i] ) break;
|
|
+ if( (zL[i] & 0xC0)!=0x80 ) nRet++;
|
|
+ }
|
|
+
|
|
+ if( (zL[i] & 0xC0)==0x80 ) nRet--;
|
|
+ sqlite3_result_int(ctx, nRet);
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_prefixes_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_module(db, "prefixes", &prefixesModule, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(
|
|
+ db, "prefix_length", 2, SQLITE_UTF8, 0, prefixLengthFunc, 0, 0
|
|
+ );
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif /* !defined(SQLITE_CORE) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
|
--- origsrc/sqlite-autoconf-3280000/rbu.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/rbu.c 2019-03-05 13:14:40.707349900 +0100
|
|
@@ -0,0 +1,188 @@
|
|
+/*
|
|
+** 2014 August 30
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file contains a command-line application that uses the RBU
|
|
+** extension. See the usage() function below for an explanation.
|
|
+*/
|
|
+
|
|
+#include "sqlite3rbu.h"
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Print a usage message and exit.
|
|
+*/
|
|
+void usage(const char *zArgv0){
|
|
+ fprintf(stderr,
|
|
+"Usage: %s ?OPTIONS? TARGET-DB RBU-DB\n"
|
|
+"\n"
|
|
+"Where options are:\n"
|
|
+"\n"
|
|
+" -step NSTEP\n"
|
|
+" -statstep NSTATSTEP\n"
|
|
+" -vacuum\n"
|
|
+" -presql SQL\n"
|
|
+"\n"
|
|
+" If the -vacuum switch is not present, argument RBU-DB must be an RBU\n"
|
|
+" database containing an update suitable for target database TARGET-DB.\n"
|
|
+" Or, if -vacuum is specified, then TARGET-DB is a database to vacuum using\n"
|
|
+" RBU, and RBU-DB is used as the state database for the vacuum (refer to\n"
|
|
+" API documentation for details).\n"
|
|
+"\n"
|
|
+" If NSTEP is set to less than or equal to zero (the default value), this \n"
|
|
+" program attempts to perform the entire update or vacuum operation before\n"
|
|
+" exiting\n"
|
|
+"\n"
|
|
+" If NSTEP is greater than zero, then a maximum of NSTEP calls are made\n"
|
|
+" to sqlite3rbu_step(). If the RBU update has not been completely applied\n"
|
|
+" after the NSTEP'th call is made, the state is saved in the database RBU-DB\n"
|
|
+" and the program exits. Subsequent invocations of this (or any other RBU)\n"
|
|
+" application will use this state to resume applying the RBU update to the\n"
|
|
+" target db.\n"
|
|
+"\n"
|
|
+, zArgv0);
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+void report_default_vfs(){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
|
|
+ fprintf(stdout, "default vfs is \"%s\"\n", pVfs->zName);
|
|
+}
|
|
+
|
|
+void report_rbu_vfs(sqlite3rbu *pRbu){
|
|
+ sqlite3 *db = sqlite3rbu_db(pRbu, 0);
|
|
+ if( db ){
|
|
+ char *zName = 0;
|
|
+ sqlite3_file_control(db, "main", SQLITE_FCNTL_VFSNAME, &zName);
|
|
+ if( zName ){
|
|
+ fprintf(stdout, "using vfs \"%s\"\n", zName);
|
|
+ }else{
|
|
+ fprintf(stdout, "vfs name not available\n");
|
|
+ }
|
|
+ sqlite3_free(zName);
|
|
+ }
|
|
+}
|
|
+
|
|
+int main(int argc, char **argv){
|
|
+ int i;
|
|
+ const char *zTarget; /* Target database to apply RBU to */
|
|
+ const char *zRbu; /* Database containing RBU */
|
|
+ char zBuf[200]; /* Buffer for printf() */
|
|
+ char *zErrmsg = 0; /* Error message, if any */
|
|
+ sqlite3rbu *pRbu; /* RBU handle */
|
|
+ int nStep = 0; /* Maximum number of step() calls */
|
|
+ int nStatStep = 0; /* Report stats after this many step calls */
|
|
+ int bVacuum = 0;
|
|
+ const char *zPreSql = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3_int64 nProgress = 0;
|
|
+ int nArgc = argc-2;
|
|
+
|
|
+ if( argc<3 ) usage(argv[0]);
|
|
+ for(i=1; i<nArgc; i++){
|
|
+ const char *zArg = argv[i];
|
|
+ int nArg = strlen(zArg);
|
|
+ if( nArg>1 && nArg<=8 && 0==memcmp(zArg, "-vacuum", nArg) ){
|
|
+ bVacuum = 1;
|
|
+ }else if( nArg>1 && nArg<=7
|
|
+ && 0==memcmp(zArg, "-presql", nArg) && i<nArg-1 ){
|
|
+ i++;
|
|
+ zPreSql = argv[i];
|
|
+ }else if( nArg>1 && nArg<=5 && 0==memcmp(zArg, "-step", nArg) && i<nArg-1 ){
|
|
+ i++;
|
|
+ nStep = atoi(argv[i]);
|
|
+ }else if( nArg>1 && nArg<=9
|
|
+ && 0==memcmp(zArg, "-statstep", nArg) && i<nArg-1
|
|
+ ){
|
|
+ i++;
|
|
+ nStatStep = atoi(argv[i]);
|
|
+ }else{
|
|
+ usage(argv[0]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ zTarget = argv[argc-2];
|
|
+ zRbu = argv[argc-1];
|
|
+
|
|
+ report_default_vfs();
|
|
+
|
|
+ /* Open an RBU handle. A vacuum handle if -vacuum was specified, or a
|
|
+ ** regular RBU update handle otherwise. */
|
|
+ if( bVacuum ){
|
|
+ pRbu = sqlite3rbu_vacuum(zTarget, zRbu);
|
|
+ }else{
|
|
+ pRbu = sqlite3rbu_open(zTarget, zRbu, 0);
|
|
+ }
|
|
+ report_rbu_vfs(pRbu);
|
|
+
|
|
+ if( zPreSql && pRbu ){
|
|
+ sqlite3 *dbMain = sqlite3rbu_db(pRbu, 0);
|
|
+ rc = sqlite3_exec(dbMain, zPreSql, 0, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3 *dbRbu = sqlite3rbu_db(pRbu, 1);
|
|
+ rc = sqlite3_exec(dbRbu, zPreSql, 0, 0, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If nStep is less than or equal to zero, call
|
|
+ ** sqlite3rbu_step() until either the RBU has been completely applied
|
|
+ ** or an error occurs. Or, if nStep is greater than zero, call
|
|
+ ** sqlite3rbu_step() a maximum of nStep times. */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ for(i=0; (nStep<=0 || i<nStep) && sqlite3rbu_step(pRbu)==SQLITE_OK; i++){
|
|
+ if( nStatStep>0 && (i % nStatStep)==0 ){
|
|
+ sqlite3_int64 nUsed;
|
|
+ sqlite3_int64 nHighwater;
|
|
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &nUsed, &nHighwater, 0);
|
|
+ fprintf(stdout, "memory used=%lld highwater=%lld", nUsed, nHighwater);
|
|
+ if( bVacuum==0 ){
|
|
+ int one;
|
|
+ int two;
|
|
+ sqlite3rbu_bp_progress(pRbu, &one, &two);
|
|
+ fprintf(stdout, " progress=%d/%d\n", one, two);
|
|
+ }else{
|
|
+ fprintf(stdout, "\n");
|
|
+ }
|
|
+ fflush(stdout);
|
|
+ }
|
|
+ }
|
|
+ nProgress = sqlite3rbu_progress(pRbu);
|
|
+ rc = sqlite3rbu_close(pRbu, &zErrmsg);
|
|
+ }
|
|
+
|
|
+ /* Let the user know what happened. */
|
|
+ switch( rc ){
|
|
+ case SQLITE_OK:
|
|
+ sqlite3_snprintf(sizeof(zBuf), zBuf,
|
|
+ "SQLITE_OK: rbu update incomplete (%lld operations so far)\n",
|
|
+ nProgress
|
|
+ );
|
|
+ fprintf(stdout, "%s", zBuf);
|
|
+ break;
|
|
+
|
|
+ case SQLITE_DONE:
|
|
+ sqlite3_snprintf(sizeof(zBuf), zBuf,
|
|
+ "SQLITE_DONE: rbu update completed (%lld operations)\n",
|
|
+ nProgress
|
|
+ );
|
|
+ fprintf(stdout, "%s", zBuf);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ fprintf(stderr, "error=%d: %s\n", rc, zErrmsg);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zErrmsg);
|
|
+ return (rc==SQLITE_OK || rc==SQLITE_DONE) ? 0 : 1;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/regexp.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/regexp.c 2019-05-14 16:22:31.256641000 +0200
|
|
@@ -0,0 +1,773 @@
|
|
+/*
|
|
+** 2012-11-13
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** The code in this file implements a compact but reasonably
|
|
+** efficient regular-expression matcher for posix extended regular
|
|
+** expressions against UTF8 text.
|
|
+**
|
|
+** This file is an SQLite extension. It registers a single function
|
|
+** named "regexp(A,B)" where A is the regular expression and B is the
|
|
+** string to be matched. By registering this function, SQLite will also
|
|
+** then implement the "B regexp A" operator. Note that with the function
|
|
+** the regular expression comes first, but with the operator it comes
|
|
+** second.
|
|
+**
|
|
+** The following regular expression syntax is supported:
|
|
+**
|
|
+** X* zero or more occurrences of X
|
|
+** X+ one or more occurrences of X
|
|
+** X? zero or one occurrences of X
|
|
+** X{p,q} between p and q occurrences of X
|
|
+** (X) match X
|
|
+** X|Y X or Y
|
|
+** ^X X occurring at the beginning of the string
|
|
+** X$ X occurring at the end of the string
|
|
+** . Match any single character
|
|
+** \c Character c where c is one of \{}()[]|*+?.
|
|
+** \c C-language escapes for c in afnrtv. ex: \t or \n
|
|
+** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
|
|
+** \xXX Where XX is exactly 2 hex digits, unicode value XX
|
|
+** [abc] Any single character from the set abc
|
|
+** [^abc] Any single character not in the set abc
|
|
+** [a-z] Any single character in the range a-z
|
|
+** [^a-z] Any single character not in the range a-z
|
|
+** \b Word boundary
|
|
+** \w Word character. [A-Za-z0-9_]
|
|
+** \W Non-word character
|
|
+** \d Digit
|
|
+** \D Non-digit
|
|
+** \s Whitespace character
|
|
+** \S Non-whitespace character
|
|
+**
|
|
+** A nondeterministic finite automaton (NFA) is used for matching, so the
|
|
+** performance is bounded by O(N*M) where N is the size of the regular
|
|
+** expression and M is the size of the input string. The matcher never
|
|
+** exhibits exponential behavior. Note that the X{p,q} operator expands
|
|
+** to p copies of X following by q-p copies of X? and that the size of the
|
|
+** regular expression in the O(N*M) performance bound is computed after
|
|
+** this expansion.
|
|
+*/
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+
|
|
+/*
|
|
+** The following #defines change the names of some functions implemented in
|
|
+** this file to prevent name collisions with C-library functions of the
|
|
+** same name.
|
|
+*/
|
|
+#define re_match sqlite3re_match
|
|
+#define re_compile sqlite3re_compile
|
|
+#define re_free sqlite3re_free
|
|
+
|
|
+/* The end-of-input character */
|
|
+#define RE_EOF 0 /* End of input */
|
|
+
|
|
+/* The NFA is implemented as sequence of opcodes taken from the following
|
|
+** set. Each opcode has a single integer argument.
|
|
+*/
|
|
+#define RE_OP_MATCH 1 /* Match the one character in the argument */
|
|
+#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
|
|
+#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
|
|
+#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
|
|
+#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
|
|
+#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
|
|
+#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
|
|
+#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
|
|
+#define RE_OP_CC_VALUE 9 /* Single value in a character class */
|
|
+#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
|
|
+#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
|
|
+#define RE_OP_NOTWORD 12 /* Not a perl word character */
|
|
+#define RE_OP_DIGIT 13 /* digit: [0-9] */
|
|
+#define RE_OP_NOTDIGIT 14 /* Not a digit */
|
|
+#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
|
|
+#define RE_OP_NOTSPACE 16 /* Not a digit */
|
|
+#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
|
|
+
|
|
+/* Each opcode is a "state" in the NFA */
|
|
+typedef unsigned short ReStateNumber;
|
|
+
|
|
+/* Because this is an NFA and not a DFA, multiple states can be active at
|
|
+** once. An instance of the following object records all active states in
|
|
+** the NFA. The implementation is optimized for the common case where the
|
|
+** number of actives states is small.
|
|
+*/
|
|
+typedef struct ReStateSet {
|
|
+ unsigned nState; /* Number of current states */
|
|
+ ReStateNumber *aState; /* Current states */
|
|
+} ReStateSet;
|
|
+
|
|
+/* An input string read one character at a time.
|
|
+*/
|
|
+typedef struct ReInput ReInput;
|
|
+struct ReInput {
|
|
+ const unsigned char *z; /* All text */
|
|
+ int i; /* Next byte to read */
|
|
+ int mx; /* EOF when i>=mx */
|
|
+};
|
|
+
|
|
+/* A compiled NFA (or an NFA that is in the process of being compiled) is
|
|
+** an instance of the following object.
|
|
+*/
|
|
+typedef struct ReCompiled ReCompiled;
|
|
+struct ReCompiled {
|
|
+ ReInput sIn; /* Regular expression text */
|
|
+ const char *zErr; /* Error message to return */
|
|
+ char *aOp; /* Operators for the virtual machine */
|
|
+ int *aArg; /* Arguments to each operator */
|
|
+ unsigned (*xNextChar)(ReInput*); /* Next character function */
|
|
+ unsigned char zInit[12]; /* Initial text to match */
|
|
+ int nInit; /* Number of characters in zInit */
|
|
+ unsigned nState; /* Number of entries in aOp[] and aArg[] */
|
|
+ unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
|
|
+};
|
|
+
|
|
+/* Add a state to the given state set if it is not already there */
|
|
+static void re_add_state(ReStateSet *pSet, int newState){
|
|
+ unsigned i;
|
|
+ for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
|
|
+ pSet->aState[pSet->nState++] = (ReStateNumber)newState;
|
|
+}
|
|
+
|
|
+/* Extract the next unicode character from *pzIn and return it. Advance
|
|
+** *pzIn to the first byte past the end of the character returned. To
|
|
+** be clear: this routine converts utf8 to unicode. This routine is
|
|
+** optimized for the common case where the next character is a single byte.
|
|
+*/
|
|
+static unsigned re_next_char(ReInput *p){
|
|
+ unsigned c;
|
|
+ if( p->i>=p->mx ) return 0;
|
|
+ c = p->z[p->i++];
|
|
+ if( c>=0x80 ){
|
|
+ if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
|
|
+ c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
|
|
+ if( c<0x80 ) c = 0xfffd;
|
|
+ }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
|
|
+ && (p->z[p->i+1]&0xc0)==0x80 ){
|
|
+ c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
|
+ p->i += 2;
|
|
+ if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
|
+ }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
|
|
+ && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
|
+ c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
|
+ | (p->z[p->i+2]&0x3f);
|
|
+ p->i += 3;
|
|
+ if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
|
|
+ }else{
|
|
+ c = 0xfffd;
|
|
+ }
|
|
+ }
|
|
+ return c;
|
|
+}
|
|
+static unsigned re_next_char_nocase(ReInput *p){
|
|
+ unsigned c = re_next_char(p);
|
|
+ if( c>='A' && c<='Z' ) c += 'a' - 'A';
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
|
|
+static int re_word_char(int c){
|
|
+ return (c>='0' && c<='9') || (c>='a' && c<='z')
|
|
+ || (c>='A' && c<='Z') || c=='_';
|
|
+}
|
|
+
|
|
+/* Return true if c is a "digit" character: [0-9] */
|
|
+static int re_digit_char(int c){
|
|
+ return (c>='0' && c<='9');
|
|
+}
|
|
+
|
|
+/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
|
|
+static int re_space_char(int c){
|
|
+ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
|
|
+}
|
|
+
|
|
+/* Run a compiled regular expression on the zero-terminated input
|
|
+** string zIn[]. Return true on a match and false if there is no match.
|
|
+*/
|
|
+int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
|
|
+ ReStateSet aStateSet[2], *pThis, *pNext;
|
|
+ ReStateNumber aSpace[100];
|
|
+ ReStateNumber *pToFree;
|
|
+ unsigned int i = 0;
|
|
+ unsigned int iSwap = 0;
|
|
+ int c = RE_EOF+1;
|
|
+ int cPrev = 0;
|
|
+ int rc = 0;
|
|
+ ReInput in;
|
|
+
|
|
+ in.z = zIn;
|
|
+ in.i = 0;
|
|
+ in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
|
|
+
|
|
+ /* Look for the initial prefix match, if there is one. */
|
|
+ if( pRe->nInit ){
|
|
+ unsigned char x = pRe->zInit[0];
|
|
+ while( in.i+pRe->nInit<=in.mx
|
|
+ && (zIn[in.i]!=x ||
|
|
+ strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
|
|
+ ){
|
|
+ in.i++;
|
|
+ }
|
|
+ if( in.i+pRe->nInit>in.mx ) return 0;
|
|
+ }
|
|
+
|
|
+ if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
|
|
+ pToFree = 0;
|
|
+ aStateSet[0].aState = aSpace;
|
|
+ }else{
|
|
+ pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState );
|
|
+ if( pToFree==0 ) return -1;
|
|
+ aStateSet[0].aState = pToFree;
|
|
+ }
|
|
+ aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
|
|
+ pNext = &aStateSet[1];
|
|
+ pNext->nState = 0;
|
|
+ re_add_state(pNext, 0);
|
|
+ while( c!=RE_EOF && pNext->nState>0 ){
|
|
+ cPrev = c;
|
|
+ c = pRe->xNextChar(&in);
|
|
+ pThis = pNext;
|
|
+ pNext = &aStateSet[iSwap];
|
|
+ iSwap = 1 - iSwap;
|
|
+ pNext->nState = 0;
|
|
+ for(i=0; i<pThis->nState; i++){
|
|
+ int x = pThis->aState[i];
|
|
+ switch( pRe->aOp[x] ){
|
|
+ case RE_OP_MATCH: {
|
|
+ if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_ANY: {
|
|
+ re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_WORD: {
|
|
+ if( re_word_char(c) ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_NOTWORD: {
|
|
+ if( !re_word_char(c) ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_DIGIT: {
|
|
+ if( re_digit_char(c) ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_NOTDIGIT: {
|
|
+ if( !re_digit_char(c) ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_SPACE: {
|
|
+ if( re_space_char(c) ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_NOTSPACE: {
|
|
+ if( !re_space_char(c) ) re_add_state(pNext, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_BOUNDARY: {
|
|
+ if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_ANYSTAR: {
|
|
+ re_add_state(pNext, x);
|
|
+ re_add_state(pThis, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_FORK: {
|
|
+ re_add_state(pThis, x+pRe->aArg[x]);
|
|
+ re_add_state(pThis, x+1);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_GOTO: {
|
|
+ re_add_state(pThis, x+pRe->aArg[x]);
|
|
+ break;
|
|
+ }
|
|
+ case RE_OP_ACCEPT: {
|
|
+ rc = 1;
|
|
+ goto re_match_end;
|
|
+ }
|
|
+ case RE_OP_CC_INC:
|
|
+ case RE_OP_CC_EXC: {
|
|
+ int j = 1;
|
|
+ int n = pRe->aArg[x];
|
|
+ int hit = 0;
|
|
+ for(j=1; j>0 && j<n; j++){
|
|
+ if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
|
|
+ if( pRe->aArg[x+j]==c ){
|
|
+ hit = 1;
|
|
+ j = -1;
|
|
+ }
|
|
+ }else{
|
|
+ if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
|
|
+ hit = 1;
|
|
+ j = -1;
|
|
+ }else{
|
|
+ j++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
|
|
+ if( hit ) re_add_state(pNext, x+n);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ for(i=0; i<pNext->nState; i++){
|
|
+ if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
|
|
+ }
|
|
+re_match_end:
|
|
+ sqlite3_free(pToFree);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Resize the opcode and argument arrays for an RE under construction.
|
|
+*/
|
|
+static int re_resize(ReCompiled *p, int N){
|
|
+ char *aOp;
|
|
+ int *aArg;
|
|
+ aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
|
|
+ if( aOp==0 ) return 1;
|
|
+ p->aOp = aOp;
|
|
+ aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
|
|
+ if( aArg==0 ) return 1;
|
|
+ p->aArg = aArg;
|
|
+ p->nAlloc = N;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Insert a new opcode and argument into an RE under construction. The
|
|
+** insertion point is just prior to existing opcode iBefore.
|
|
+*/
|
|
+static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
|
|
+ int i;
|
|
+ if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
|
|
+ for(i=p->nState; i>iBefore; i--){
|
|
+ p->aOp[i] = p->aOp[i-1];
|
|
+ p->aArg[i] = p->aArg[i-1];
|
|
+ }
|
|
+ p->nState++;
|
|
+ p->aOp[iBefore] = (char)op;
|
|
+ p->aArg[iBefore] = arg;
|
|
+ return iBefore;
|
|
+}
|
|
+
|
|
+/* Append a new opcode and argument to the end of the RE under construction.
|
|
+*/
|
|
+static int re_append(ReCompiled *p, int op, int arg){
|
|
+ return re_insert(p, p->nState, op, arg);
|
|
+}
|
|
+
|
|
+/* Make a copy of N opcodes starting at iStart onto the end of the RE
|
|
+** under construction.
|
|
+*/
|
|
+static void re_copy(ReCompiled *p, int iStart, int N){
|
|
+ if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
|
|
+ memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
|
|
+ memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
|
|
+ p->nState += N;
|
|
+}
|
|
+
|
|
+/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
|
|
+** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If
|
|
+** c is not a hex digit *pV is unchanged.
|
|
+*/
|
|
+static int re_hex(int c, int *pV){
|
|
+ if( c>='0' && c<='9' ){
|
|
+ c -= '0';
|
|
+ }else if( c>='a' && c<='f' ){
|
|
+ c -= 'a' - 10;
|
|
+ }else if( c>='A' && c<='F' ){
|
|
+ c -= 'A' - 10;
|
|
+ }else{
|
|
+ return 0;
|
|
+ }
|
|
+ *pV = (*pV)*16 + (c & 0xff);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* A backslash character has been seen, read the next character and
|
|
+** return its interpretation.
|
|
+*/
|
|
+static unsigned re_esc_char(ReCompiled *p){
|
|
+ static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
|
|
+ static const char zTrans[] = "\a\f\n\r\t\v";
|
|
+ int i, v = 0;
|
|
+ char c;
|
|
+ if( p->sIn.i>=p->sIn.mx ) return 0;
|
|
+ c = p->sIn.z[p->sIn.i];
|
|
+ if( c=='u' && p->sIn.i+4<p->sIn.mx ){
|
|
+ const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
|
+ if( re_hex(zIn[1],&v)
|
|
+ && re_hex(zIn[2],&v)
|
|
+ && re_hex(zIn[3],&v)
|
|
+ && re_hex(zIn[4],&v)
|
|
+ ){
|
|
+ p->sIn.i += 5;
|
|
+ return v;
|
|
+ }
|
|
+ }
|
|
+ if( c=='x' && p->sIn.i+2<p->sIn.mx ){
|
|
+ const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
|
+ if( re_hex(zIn[1],&v)
|
|
+ && re_hex(zIn[2],&v)
|
|
+ ){
|
|
+ p->sIn.i += 3;
|
|
+ return v;
|
|
+ }
|
|
+ }
|
|
+ for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
|
|
+ if( zEsc[i] ){
|
|
+ if( i<6 ) c = zTrans[i];
|
|
+ p->sIn.i++;
|
|
+ }else{
|
|
+ p->zErr = "unknown \\ escape";
|
|
+ }
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/* Forward declaration */
|
|
+static const char *re_subcompile_string(ReCompiled*);
|
|
+
|
|
+/* Peek at the next byte of input */
|
|
+static unsigned char rePeek(ReCompiled *p){
|
|
+ return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
|
|
+}
|
|
+
|
|
+/* Compile RE text into a sequence of opcodes. Continue up to the
|
|
+** first unmatched ")" character, then return. If an error is found,
|
|
+** return a pointer to the error message string.
|
|
+*/
|
|
+static const char *re_subcompile_re(ReCompiled *p){
|
|
+ const char *zErr;
|
|
+ int iStart, iEnd, iGoto;
|
|
+ iStart = p->nState;
|
|
+ zErr = re_subcompile_string(p);
|
|
+ if( zErr ) return zErr;
|
|
+ while( rePeek(p)=='|' ){
|
|
+ iEnd = p->nState;
|
|
+ re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
|
|
+ iGoto = re_append(p, RE_OP_GOTO, 0);
|
|
+ p->sIn.i++;
|
|
+ zErr = re_subcompile_string(p);
|
|
+ if( zErr ) return zErr;
|
|
+ p->aArg[iGoto] = p->nState - iGoto;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Compile an element of regular expression text (anything that can be
|
|
+** an operand to the "|" operator). Return NULL on success or a pointer
|
|
+** to the error message if there is a problem.
|
|
+*/
|
|
+static const char *re_subcompile_string(ReCompiled *p){
|
|
+ int iPrev = -1;
|
|
+ int iStart;
|
|
+ unsigned c;
|
|
+ const char *zErr;
|
|
+ while( (c = p->xNextChar(&p->sIn))!=0 ){
|
|
+ iStart = p->nState;
|
|
+ switch( c ){
|
|
+ case '|':
|
|
+ case '$':
|
|
+ case ')': {
|
|
+ p->sIn.i--;
|
|
+ return 0;
|
|
+ }
|
|
+ case '(': {
|
|
+ zErr = re_subcompile_re(p);
|
|
+ if( zErr ) return zErr;
|
|
+ if( rePeek(p)!=')' ) return "unmatched '('";
|
|
+ p->sIn.i++;
|
|
+ break;
|
|
+ }
|
|
+ case '.': {
|
|
+ if( rePeek(p)=='*' ){
|
|
+ re_append(p, RE_OP_ANYSTAR, 0);
|
|
+ p->sIn.i++;
|
|
+ }else{
|
|
+ re_append(p, RE_OP_ANY, 0);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case '*': {
|
|
+ if( iPrev<0 ) return "'*' without operand";
|
|
+ re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
|
|
+ re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
|
|
+ break;
|
|
+ }
|
|
+ case '+': {
|
|
+ if( iPrev<0 ) return "'+' without operand";
|
|
+ re_append(p, RE_OP_FORK, iPrev - p->nState);
|
|
+ break;
|
|
+ }
|
|
+ case '?': {
|
|
+ if( iPrev<0 ) return "'?' without operand";
|
|
+ re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
|
|
+ break;
|
|
+ }
|
|
+ case '{': {
|
|
+ int m = 0, n = 0;
|
|
+ int sz, j;
|
|
+ if( iPrev<0 ) return "'{m,n}' without operand";
|
|
+ while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
|
|
+ n = m;
|
|
+ if( c==',' ){
|
|
+ p->sIn.i++;
|
|
+ n = 0;
|
|
+ while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
|
|
+ }
|
|
+ if( c!='}' ) return "unmatched '{'";
|
|
+ if( n>0 && n<m ) return "n less than m in '{m,n}'";
|
|
+ p->sIn.i++;
|
|
+ sz = p->nState - iPrev;
|
|
+ if( m==0 ){
|
|
+ if( n==0 ) return "both m and n are zero in '{m,n}'";
|
|
+ re_insert(p, iPrev, RE_OP_FORK, sz+1);
|
|
+ n--;
|
|
+ }else{
|
|
+ for(j=1; j<m; j++) re_copy(p, iPrev, sz);
|
|
+ }
|
|
+ for(j=m; j<n; j++){
|
|
+ re_append(p, RE_OP_FORK, sz+1);
|
|
+ re_copy(p, iPrev, sz);
|
|
+ }
|
|
+ if( n==0 && m>0 ){
|
|
+ re_append(p, RE_OP_FORK, -sz);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case '[': {
|
|
+ int iFirst = p->nState;
|
|
+ if( rePeek(p)=='^' ){
|
|
+ re_append(p, RE_OP_CC_EXC, 0);
|
|
+ p->sIn.i++;
|
|
+ }else{
|
|
+ re_append(p, RE_OP_CC_INC, 0);
|
|
+ }
|
|
+ while( (c = p->xNextChar(&p->sIn))!=0 ){
|
|
+ if( c=='[' && rePeek(p)==':' ){
|
|
+ return "POSIX character classes not supported";
|
|
+ }
|
|
+ if( c=='\\' ) c = re_esc_char(p);
|
|
+ if( rePeek(p)=='-' ){
|
|
+ re_append(p, RE_OP_CC_RANGE, c);
|
|
+ p->sIn.i++;
|
|
+ c = p->xNextChar(&p->sIn);
|
|
+ if( c=='\\' ) c = re_esc_char(p);
|
|
+ re_append(p, RE_OP_CC_RANGE, c);
|
|
+ }else{
|
|
+ re_append(p, RE_OP_CC_VALUE, c);
|
|
+ }
|
|
+ if( rePeek(p)==']' ){ p->sIn.i++; break; }
|
|
+ }
|
|
+ if( c==0 ) return "unclosed '['";
|
|
+ p->aArg[iFirst] = p->nState - iFirst;
|
|
+ break;
|
|
+ }
|
|
+ case '\\': {
|
|
+ int specialOp = 0;
|
|
+ switch( rePeek(p) ){
|
|
+ case 'b': specialOp = RE_OP_BOUNDARY; break;
|
|
+ case 'd': specialOp = RE_OP_DIGIT; break;
|
|
+ case 'D': specialOp = RE_OP_NOTDIGIT; break;
|
|
+ case 's': specialOp = RE_OP_SPACE; break;
|
|
+ case 'S': specialOp = RE_OP_NOTSPACE; break;
|
|
+ case 'w': specialOp = RE_OP_WORD; break;
|
|
+ case 'W': specialOp = RE_OP_NOTWORD; break;
|
|
+ }
|
|
+ if( specialOp ){
|
|
+ p->sIn.i++;
|
|
+ re_append(p, specialOp, 0);
|
|
+ }else{
|
|
+ c = re_esc_char(p);
|
|
+ re_append(p, RE_OP_MATCH, c);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ re_append(p, RE_OP_MATCH, c);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ iPrev = iStart;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Free and reclaim all the memory used by a previously compiled
|
|
+** regular expression. Applications should invoke this routine once
|
|
+** for every call to re_compile() to avoid memory leaks.
|
|
+*/
|
|
+void re_free(ReCompiled *pRe){
|
|
+ if( pRe ){
|
|
+ sqlite3_free(pRe->aOp);
|
|
+ sqlite3_free(pRe->aArg);
|
|
+ sqlite3_free(pRe);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compile a textual regular expression in zIn[] into a compiled regular
|
|
+** expression suitable for us by re_match() and return a pointer to the
|
|
+** compiled regular expression in *ppRe. Return NULL on success or an
|
|
+** error message if something goes wrong.
|
|
+*/
|
|
+const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
|
|
+ ReCompiled *pRe;
|
|
+ const char *zErr;
|
|
+ int i, j;
|
|
+
|
|
+ *ppRe = 0;
|
|
+ pRe = sqlite3_malloc( sizeof(*pRe) );
|
|
+ if( pRe==0 ){
|
|
+ return "out of memory";
|
|
+ }
|
|
+ memset(pRe, 0, sizeof(*pRe));
|
|
+ pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
|
|
+ if( re_resize(pRe, 30) ){
|
|
+ re_free(pRe);
|
|
+ return "out of memory";
|
|
+ }
|
|
+ if( zIn[0]=='^' ){
|
|
+ zIn++;
|
|
+ }else{
|
|
+ re_append(pRe, RE_OP_ANYSTAR, 0);
|
|
+ }
|
|
+ pRe->sIn.z = (unsigned char*)zIn;
|
|
+ pRe->sIn.i = 0;
|
|
+ pRe->sIn.mx = (int)strlen(zIn);
|
|
+ zErr = re_subcompile_re(pRe);
|
|
+ if( zErr ){
|
|
+ re_free(pRe);
|
|
+ return zErr;
|
|
+ }
|
|
+ if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
|
|
+ re_append(pRe, RE_OP_MATCH, RE_EOF);
|
|
+ re_append(pRe, RE_OP_ACCEPT, 0);
|
|
+ *ppRe = pRe;
|
|
+ }else if( pRe->sIn.i>=pRe->sIn.mx ){
|
|
+ re_append(pRe, RE_OP_ACCEPT, 0);
|
|
+ *ppRe = pRe;
|
|
+ }else{
|
|
+ re_free(pRe);
|
|
+ return "unrecognized character";
|
|
+ }
|
|
+
|
|
+ /* The following is a performance optimization. If the regex begins with
|
|
+ ** ".*" (if the input regex lacks an initial "^") and afterwards there are
|
|
+ ** one or more matching characters, enter those matching characters into
|
|
+ ** zInit[]. The re_match() routine can then search ahead in the input
|
|
+ ** string looking for the initial match without having to run the whole
|
|
+ ** regex engine over the string. Do not worry able trying to match
|
|
+ ** unicode characters beyond plane 0 - those are very rare and this is
|
|
+ ** just an optimization. */
|
|
+ if( pRe->aOp[0]==RE_OP_ANYSTAR ){
|
|
+ for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
|
|
+ unsigned x = pRe->aArg[i];
|
|
+ if( x<=127 ){
|
|
+ pRe->zInit[j++] = (unsigned char)x;
|
|
+ }else if( x<=0xfff ){
|
|
+ pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
|
|
+ pRe->zInit[j++] = 0x80 | (x&0x3f);
|
|
+ }else if( x<=0xffff ){
|
|
+ pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12));
|
|
+ pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
|
|
+ pRe->zInit[j++] = 0x80 | (x&0x3f);
|
|
+ }else{
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( j>0 && pRe->zInit[j-1]==0 ) j--;
|
|
+ pRe->nInit = j;
|
|
+ }
|
|
+ return pRe->zErr;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the regexp() SQL function. This function implements
|
|
+** the build-in REGEXP operator. The first argument to the function is the
|
|
+** pattern and the second argument is the string. So, the SQL statements:
|
|
+**
|
|
+** A REGEXP B
|
|
+**
|
|
+** is implemented as regexp(B,A).
|
|
+*/
|
|
+static void re_sql_func(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ ReCompiled *pRe; /* Compiled regular expression */
|
|
+ const char *zPattern; /* The regular expression */
|
|
+ const unsigned char *zStr;/* String being searched */
|
|
+ const char *zErr; /* Compile error message */
|
|
+ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
|
|
+
|
|
+ pRe = sqlite3_get_auxdata(context, 0);
|
|
+ if( pRe==0 ){
|
|
+ zPattern = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zPattern==0 ) return;
|
|
+ zErr = re_compile(&pRe, zPattern, 0);
|
|
+ if( zErr ){
|
|
+ re_free(pRe);
|
|
+ sqlite3_result_error(context, zErr, -1);
|
|
+ return;
|
|
+ }
|
|
+ if( pRe==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ setAux = 1;
|
|
+ }
|
|
+ zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
|
|
+ if( zStr!=0 ){
|
|
+ sqlite3_result_int(context, re_match(pRe, zStr, -1));
|
|
+ }
|
|
+ if( setAux ){
|
|
+ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Invoke this routine to register the regexp() function with the
|
|
+** SQLite database connection.
|
|
+*/
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_regexp_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
|
+ re_sql_func, 0, 0);
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
|
+ re_sql_func, 0, 0);
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/remember.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/remember.c 2019-05-14 16:22:31.258593600 +0200
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+** 2016-08-09
|
|
+**
|
|
+** 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 demonstrates how to create an SQL function that is a pass-through
|
|
+** for integer values (it returns a copy of its argument) but also saves the
|
|
+** value that is passed through into a C-language variable. The address of
|
|
+** the C-language variable is supplied as the second argument.
|
|
+**
|
|
+** This allows, for example, a counter to incremented and the original
|
|
+** value retrieved, atomically, using a single statement:
|
|
+**
|
|
+** UPDATE counterTab SET cnt=remember(cnt,$PTR)+1 WHERE id=$ID
|
|
+**
|
|
+** Prepare the above statement once. Then to use it, bind the address
|
|
+** of the output variable to $PTR using sqlite3_bind_pointer() with a
|
|
+** pointer type of "carray" and bind the id of the counter to $ID and
|
|
+** run the prepared statement.
|
|
+**
|
|
+** This implementation of the remember() function uses a "carray"
|
|
+** pointer so that it can share pointers with the carray() extension.
|
|
+**
|
|
+** One can imagine doing similar things with floating-point values and
|
|
+** strings, but this demonstration extension will stick to using just
|
|
+** integers.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <stddef.h>
|
|
+
|
|
+/*
|
|
+** remember(V,PTR)
|
|
+**
|
|
+** Return the integer value V. Also save the value of V in a
|
|
+** C-language variable whose address is PTR.
|
|
+*/
|
|
+static void rememberFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3_int64 v;
|
|
+ sqlite3_int64 *ptr;
|
|
+ assert( argc==2 );
|
|
+ v = sqlite3_value_int64(argv[0]);
|
|
+ ptr = sqlite3_value_pointer(argv[1], "carray");
|
|
+ if( ptr ) *ptr = v;
|
|
+ sqlite3_result_int64(pCtx, v);
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_remember_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
|
|
+ rememberFunc, 0, 0);
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
|
|
+ rememberFunc, 0, 0);
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/rot13.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/rot13.c 2019-05-14 16:22:31.259570000 +0200
|
|
@@ -0,0 +1,131 @@
|
|
+/*
|
|
+** 2013-05-15
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This SQLite extension implements a rot13() function and a rot13
|
|
+** collating sequence.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Perform rot13 encoding on a single ASCII character.
|
|
+*/
|
|
+static unsigned char rot13(unsigned char c){
|
|
+ if( c>='a' && c<='z' ){
|
|
+ c += 13;
|
|
+ if( c>'z' ) c -= 26;
|
|
+ }else if( c>='A' && c<='Z' ){
|
|
+ c += 13;
|
|
+ if( c>'Z' ) c -= 26;
|
|
+ }
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the rot13() function.
|
|
+**
|
|
+** Rotate ASCII alphabetic characters by 13 character positions.
|
|
+** Non-ASCII characters are unchanged. rot13(rot13(X)) should always
|
|
+** equal X.
|
|
+*/
|
|
+static void rot13func(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *zIn;
|
|
+ size_t nIn;
|
|
+ unsigned char *zOut;
|
|
+ unsigned char *zToFree = 0;
|
|
+ size_t i;
|
|
+ unsigned char zTemp[100];
|
|
+ assert( argc==1 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
|
+ zIn = (const unsigned char*)sqlite3_value_text(argv[0]);
|
|
+ nIn = sqlite3_value_bytes(argv[0]);
|
|
+ if( nIn<sizeof(zTemp)-1 ){
|
|
+ zOut = zTemp;
|
|
+ }else{
|
|
+ zOut = zToFree = (unsigned char*)sqlite3_malloc( nIn+1 );
|
|
+ if( zOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ for(i=0; i<nIn; i++) zOut[i] = rot13(zIn[i]);
|
|
+ zOut[i] = 0;
|
|
+ sqlite3_result_text(context, (char*)zOut, i, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(zToFree);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implement the rot13 collating sequence so that if
|
|
+**
|
|
+** x=y COLLATE rot13
|
|
+**
|
|
+** Then
|
|
+**
|
|
+** rot13(x)=rot13(y) COLLATE binary
|
|
+*/
|
|
+static int rot13CollFunc(
|
|
+ void *notUsed,
|
|
+ int nKey1, const void *pKey1,
|
|
+ int nKey2, const void *pKey2
|
|
+){
|
|
+ const char *zA = (const char*)pKey1;
|
|
+ const char *zB = (const char*)pKey2;
|
|
+ int i, x;
|
|
+ for(i=0; i<nKey1 && i<nKey2; i++){
|
|
+ x = (int)rot13(zA[i]) - (int)rot13(zB[i]);
|
|
+ if( x!=0 ) return x;
|
|
+ }
|
|
+ return nKey1 - nKey2;
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_rot_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "rot13", 1, SQLITE_UTF8, 0,
|
|
+ rot13func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "rot13", 1, SQLITE_UTF8, 0,
|
|
+ rot13func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/scrub.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/scrub.c 2019-05-14 16:22:31.261522600 +0200
|
|
@@ -0,0 +1,610 @@
|
|
+/*
|
|
+** 2016-05-05
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements a utility function (and a utility program) that
|
|
+** makes a copy of an SQLite database while simultaneously zeroing out all
|
|
+** deleted content.
|
|
+**
|
|
+** Normally (when PRAGMA secure_delete=OFF, which is the default) when SQLite
|
|
+** deletes content, it does not overwrite the deleted content but rather marks
|
|
+** the region of the file that held that content as being reusable. This can
|
|
+** cause deleted content to recoverable from the database file. This stale
|
|
+** content is removed by the VACUUM command, but VACUUM can be expensive for
|
|
+** large databases. When in PRAGMA secure_delete=ON mode, the deleted content
|
|
+** is zeroed, but secure_delete=ON has overhead as well.
|
|
+**
|
|
+** This utility attempts to make a copy of a complete SQLite database where
|
|
+** all of the deleted content is zeroed out in the copy, and it attempts to
|
|
+** do so while being faster than running VACUUM.
|
|
+**
|
|
+** Usage:
|
|
+**
|
|
+** int sqlite3_scrub_backup(
|
|
+** const char *zSourceFile, // Source database filename
|
|
+** const char *zDestFile, // Destination database filename
|
|
+** char **pzErrMsg // Write error message here
|
|
+** );
|
|
+**
|
|
+** Simply call the API above specifying the filename of the source database
|
|
+** and the name of the backup copy. The source database must already exist
|
|
+** and can be in active use. (A read lock is held during the backup.) The
|
|
+** destination file should not previously exist. If the pzErrMsg parameter
|
|
+** is non-NULL and if an error occurs, then an error message might be written
|
|
+** into memory obtained from sqlite3_malloc() and *pzErrMsg made to point to
|
|
+** that error message. But if the error is an OOM, the error might not be
|
|
+** reported. The routine always returns non-zero if there is an error.
|
|
+**
|
|
+** If compiled with -DSCRUB_STANDALONE then a main() procedure is added and
|
|
+** this file becomes a standalone program that can be run as follows:
|
|
+**
|
|
+** ./sqlite3scrub SOURCE DEST
|
|
+*/
|
|
+#include "sqlite3.h"
|
|
+#include <assert.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdarg.h>
|
|
+#include <string.h>
|
|
+
|
|
+typedef struct ScrubState ScrubState;
|
|
+typedef unsigned char u8;
|
|
+typedef unsigned short u16;
|
|
+typedef unsigned int u32;
|
|
+
|
|
+
|
|
+/* State information for a scrub-and-backup operation */
|
|
+struct ScrubState {
|
|
+ const char *zSrcFile; /* Name of the source file */
|
|
+ const char *zDestFile; /* Name of the destination file */
|
|
+ int rcErr; /* Error code */
|
|
+ char *zErr; /* Error message text */
|
|
+ sqlite3 *dbSrc; /* Source database connection */
|
|
+ sqlite3_file *pSrc; /* Source file handle */
|
|
+ sqlite3 *dbDest; /* Destination database connection */
|
|
+ sqlite3_file *pDest; /* Destination file handle */
|
|
+ u32 szPage; /* Page size */
|
|
+ u32 szUsable; /* Usable bytes on each page */
|
|
+ u32 nPage; /* Number of pages */
|
|
+ u32 iLastPage; /* Page number of last page written so far*/
|
|
+ u8 *page1; /* Content of page 1 */
|
|
+};
|
|
+
|
|
+/* Store an error message */
|
|
+static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ sqlite3_free(p->zErr);
|
|
+ va_start(ap, zFormat);
|
|
+ p->zErr = sqlite3_vmprintf(zFormat, ap);
|
|
+ va_end(ap);
|
|
+ if( p->rcErr==0 ) p->rcErr = SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/* Allocate memory to hold a single page of content */
|
|
+static u8 *scrubBackupAllocPage(ScrubState *p){
|
|
+ u8 *pPage;
|
|
+ if( p->rcErr ) return 0;
|
|
+ pPage = sqlite3_malloc( p->szPage );
|
|
+ if( pPage==0 ) p->rcErr = SQLITE_NOMEM;
|
|
+ return pPage;
|
|
+}
|
|
+
|
|
+/* Read a page from the source database into memory. Use the memory
|
|
+** provided by pBuf if not NULL or allocate a new page if pBuf==NULL.
|
|
+*/
|
|
+static u8 *scrubBackupRead(ScrubState *p, int pgno, u8 *pBuf){
|
|
+ int rc;
|
|
+ sqlite3_int64 iOff;
|
|
+ u8 *pOut = pBuf;
|
|
+ if( p->rcErr ) return 0;
|
|
+ if( pOut==0 ){
|
|
+ pOut = scrubBackupAllocPage(p);
|
|
+ if( pOut==0 ) return 0;
|
|
+ }
|
|
+ iOff = (pgno-1)*(sqlite3_int64)p->szPage;
|
|
+ rc = p->pSrc->pMethods->xRead(p->pSrc, pOut, p->szPage, iOff);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ if( pBuf==0 ) sqlite3_free(pOut);
|
|
+ pOut = 0;
|
|
+ scrubBackupErr(p, "read failed for page %d", pgno);
|
|
+ p->rcErr = SQLITE_IOERR;
|
|
+ }
|
|
+ return pOut;
|
|
+}
|
|
+
|
|
+/* Write a page to the destination database */
|
|
+static void scrubBackupWrite(ScrubState *p, int pgno, const u8 *pData){
|
|
+ int rc;
|
|
+ sqlite3_int64 iOff;
|
|
+ if( p->rcErr ) return;
|
|
+ iOff = (pgno-1)*(sqlite3_int64)p->szPage;
|
|
+ rc = p->pDest->pMethods->xWrite(p->pDest, pData, p->szPage, iOff);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ scrubBackupErr(p, "write failed for page %d", pgno);
|
|
+ p->rcErr = SQLITE_IOERR;
|
|
+ }
|
|
+ if( (u32)pgno>p->iLastPage ) p->iLastPage = pgno;
|
|
+}
|
|
+
|
|
+/* Prepare a statement against the "db" database. */
|
|
+static sqlite3_stmt *scrubBackupPrepare(
|
|
+ ScrubState *p, /* Backup context */
|
|
+ sqlite3 *db, /* Database to prepare against */
|
|
+ const char *zSql /* SQL statement */
|
|
+){
|
|
+ sqlite3_stmt *pStmt;
|
|
+ if( p->rcErr ) return 0;
|
|
+ p->rcErr = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
+ if( p->rcErr ){
|
|
+ scrubBackupErr(p, "SQL error \"%s\" on \"%s\"",
|
|
+ sqlite3_errmsg(db), zSql);
|
|
+ sqlite3_finalize(pStmt);
|
|
+ return 0;
|
|
+ }
|
|
+ return pStmt;
|
|
+}
|
|
+
|
|
+
|
|
+/* Open the source database file */
|
|
+static void scrubBackupOpenSrc(ScrubState *p){
|
|
+ sqlite3_stmt *pStmt;
|
|
+ int rc;
|
|
+ /* Open the source database file */
|
|
+ p->rcErr = sqlite3_open_v2(p->zSrcFile, &p->dbSrc,
|
|
+ SQLITE_OPEN_READWRITE |
|
|
+ SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0);
|
|
+ if( p->rcErr ){
|
|
+ scrubBackupErr(p, "cannot open source database: %s",
|
|
+ sqlite3_errmsg(p->dbSrc));
|
|
+ return;
|
|
+ }
|
|
+ p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_master; BEGIN;",
|
|
+ 0, 0, 0);
|
|
+ if( p->rcErr ){
|
|
+ scrubBackupErr(p,
|
|
+ "cannot start a read transaction on the source database: %s",
|
|
+ sqlite3_errmsg(p->dbSrc));
|
|
+ return;
|
|
+ }
|
|
+ rc = sqlite3_wal_checkpoint_v2(p->dbSrc, "main", SQLITE_CHECKPOINT_FULL,
|
|
+ 0, 0);
|
|
+ if( rc ){
|
|
+ scrubBackupErr(p, "cannot checkpoint the source database");
|
|
+ return;
|
|
+ }
|
|
+ pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_size");
|
|
+ if( pStmt==0 ) return;
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ p->szPage = sqlite3_column_int(pStmt, 0);
|
|
+ }else{
|
|
+ scrubBackupErr(p, "unable to determine the page size");
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( p->rcErr ) return;
|
|
+ pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_count");
|
|
+ if( pStmt==0 ) return;
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ p->nPage = sqlite3_column_int(pStmt, 0);
|
|
+ }else{
|
|
+ scrubBackupErr(p, "unable to determine the size of the source database");
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ sqlite3_file_control(p->dbSrc, "main", SQLITE_FCNTL_FILE_POINTER, &p->pSrc);
|
|
+ if( p->pSrc==0 || p->pSrc->pMethods==0 ){
|
|
+ scrubBackupErr(p, "cannot get the source file handle");
|
|
+ p->rcErr = SQLITE_ERROR;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Create and open the destination file */
|
|
+static void scrubBackupOpenDest(ScrubState *p){
|
|
+ sqlite3_stmt *pStmt;
|
|
+ int rc;
|
|
+ char *zSql;
|
|
+ if( p->rcErr ) return;
|
|
+ p->rcErr = sqlite3_open_v2(p->zDestFile, &p->dbDest,
|
|
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
|
|
+ SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0);
|
|
+ if( p->rcErr ){
|
|
+ scrubBackupErr(p, "cannot open destination database: %s",
|
|
+ sqlite3_errmsg(p->dbDest));
|
|
+ return;
|
|
+ }
|
|
+ zSql = sqlite3_mprintf("PRAGMA page_size(%u);", p->szPage);
|
|
+ if( zSql==0 ){
|
|
+ p->rcErr = SQLITE_NOMEM;
|
|
+ return;
|
|
+ }
|
|
+ p->rcErr = sqlite3_exec(p->dbDest, zSql, 0, 0, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( p->rcErr ){
|
|
+ scrubBackupErr(p,
|
|
+ "cannot set the page size on the destination database: %s",
|
|
+ sqlite3_errmsg(p->dbDest));
|
|
+ return;
|
|
+ }
|
|
+ sqlite3_exec(p->dbDest, "PRAGMA journal_mode=OFF;", 0, 0, 0);
|
|
+ p->rcErr = sqlite3_exec(p->dbDest, "BEGIN EXCLUSIVE;", 0, 0, 0);
|
|
+ if( p->rcErr ){
|
|
+ scrubBackupErr(p,
|
|
+ "cannot start a write transaction on the destination database: %s",
|
|
+ sqlite3_errmsg(p->dbDest));
|
|
+ return;
|
|
+ }
|
|
+ pStmt = scrubBackupPrepare(p, p->dbDest, "PRAGMA page_count;");
|
|
+ if( pStmt==0 ) return;
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ if( rc!=SQLITE_ROW ){
|
|
+ scrubBackupErr(p, "cannot measure the size of the destination");
|
|
+ }else if( sqlite3_column_int(pStmt, 0)>1 ){
|
|
+ scrubBackupErr(p, "destination database is not empty - holds %d pages",
|
|
+ sqlite3_column_int(pStmt, 0));
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ sqlite3_file_control(p->dbDest, "main", SQLITE_FCNTL_FILE_POINTER, &p->pDest);
|
|
+ if( p->pDest==0 || p->pDest->pMethods==0 ){
|
|
+ scrubBackupErr(p, "cannot get the destination file handle");
|
|
+ p->rcErr = SQLITE_ERROR;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Read a 32-bit big-endian integer */
|
|
+static u32 scrubBackupInt32(const u8 *a){
|
|
+ u32 v = a[3];
|
|
+ v += ((u32)a[2])<<8;
|
|
+ v += ((u32)a[1])<<16;
|
|
+ v += ((u32)a[0])<<24;
|
|
+ return v;
|
|
+}
|
|
+
|
|
+/* Read a 16-bit big-endian integer */
|
|
+static u32 scrubBackupInt16(const u8 *a){
|
|
+ return (a[0]<<8) + a[1];
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read a varint. Put the value in *pVal and return the number of bytes.
|
|
+*/
|
|
+static int scrubBackupVarint(const u8 *z, sqlite3_int64 *pVal){
|
|
+ sqlite3_int64 v = 0;
|
|
+ int i;
|
|
+ for(i=0; i<8; i++){
|
|
+ v = (v<<7) + (z[i]&0x7f);
|
|
+ if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
|
|
+ }
|
|
+ v = (v<<8) + (z[i]&0xff);
|
|
+ *pVal = v;
|
|
+ return 9;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the number of bytes in a varint.
|
|
+*/
|
|
+static int scrubBackupVarintSize(const u8 *z){
|
|
+ int i;
|
|
+ for(i=0; i<8; i++){
|
|
+ if( (z[i]&0x80)==0 ){ return i+1; }
|
|
+ }
|
|
+ return 9;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Copy the freelist trunk page given, and all its descendents,
|
|
+** zeroing out as much as possible in the process.
|
|
+*/
|
|
+static void scrubBackupFreelist(ScrubState *p, int pgno, u32 nFree){
|
|
+ u8 *a, *aBuf;
|
|
+ u32 n, mx;
|
|
+
|
|
+ if( p->rcErr ) return;
|
|
+ aBuf = scrubBackupAllocPage(p);
|
|
+ if( aBuf==0 ) return;
|
|
+
|
|
+ while( pgno && nFree){
|
|
+ a = scrubBackupRead(p, pgno, aBuf);
|
|
+ if( a==0 ) break;
|
|
+ n = scrubBackupInt32(&a[4]);
|
|
+ mx = p->szUsable/4 - 2;
|
|
+ if( n<mx ){
|
|
+ memset(&a[n*4+8], 0, 4*(mx-n));
|
|
+ }
|
|
+ scrubBackupWrite(p, pgno, a);
|
|
+ pgno = scrubBackupInt32(a);
|
|
+#if 0
|
|
+ /* There is really no point in copying the freelist leaf pages.
|
|
+ ** Simply leave them uninitialized in the destination database. The
|
|
+ ** OS filesystem should zero those pages for us automatically.
|
|
+ */
|
|
+ for(i=0; i<n && nFree; i++){
|
|
+ u32 iLeaf = scrubBackupInt32(&a[i*4+8]);
|
|
+ if( aZero==0 ){
|
|
+ aZero = scrubBackupAllocPage(p);
|
|
+ if( aZero==0 ){ pgno = 0; break; }
|
|
+ memset(aZero, 0, p->szPage);
|
|
+ }
|
|
+ scrubBackupWrite(p, iLeaf, aZero);
|
|
+ nFree--;
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+ sqlite3_free(aBuf);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Copy an overflow chain from source to destination. Zero out any
|
|
+** unused tail at the end of the overflow chain.
|
|
+*/
|
|
+static void scrubBackupOverflow(ScrubState *p, int pgno, u32 nByte){
|
|
+ u8 *a, *aBuf;
|
|
+
|
|
+ aBuf = scrubBackupAllocPage(p);
|
|
+ if( aBuf==0 ) return;
|
|
+ while( nByte>0 && pgno!=0 ){
|
|
+ a = scrubBackupRead(p, pgno, aBuf);
|
|
+ if( a==0 ) break;
|
|
+ if( nByte >= (p->szUsable)-4 ){
|
|
+ nByte -= (p->szUsable) - 4;
|
|
+ }else{
|
|
+ u32 x = (p->szUsable - 4) - nByte;
|
|
+ u32 i = p->szUsable - x;
|
|
+ memset(&a[i], 0, x);
|
|
+ nByte = 0;
|
|
+ }
|
|
+ scrubBackupWrite(p, pgno, a);
|
|
+ pgno = scrubBackupInt32(a);
|
|
+ }
|
|
+ sqlite3_free(aBuf);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Copy B-Tree page pgno, and all of its children, from source to destination.
|
|
+** Zero out deleted content during the copy.
|
|
+*/
|
|
+static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){
|
|
+ u8 *a;
|
|
+ u32 i, n, pc;
|
|
+ u32 nCell;
|
|
+ u32 nPrefix;
|
|
+ u32 szHdr;
|
|
+ u32 iChild;
|
|
+ u8 *aTop;
|
|
+ u8 *aCell;
|
|
+ u32 x, y;
|
|
+ int ln = 0;
|
|
+
|
|
+
|
|
+ if( p->rcErr ) return;
|
|
+ if( iDepth>50 ){
|
|
+ scrubBackupErr(p, "corrupt: b-tree too deep at page %d", pgno);
|
|
+ return;
|
|
+ }
|
|
+ if( pgno==1 ){
|
|
+ a = p->page1;
|
|
+ }else{
|
|
+ a = scrubBackupRead(p, pgno, 0);
|
|
+ if( a==0 ) return;
|
|
+ }
|
|
+ nPrefix = pgno==1 ? 100 : 0;
|
|
+ aTop = &a[nPrefix];
|
|
+ szHdr = 8 + 4*(aTop[0]==0x02 || aTop[0]==0x05);
|
|
+ aCell = aTop + szHdr;
|
|
+ nCell = scrubBackupInt16(&aTop[3]);
|
|
+
|
|
+ /* Zero out the gap between the cell index and the start of the
|
|
+ ** cell content area */
|
|
+ x = scrubBackupInt16(&aTop[5]); /* First byte of cell content area */
|
|
+ if( x>p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ y = szHdr + nPrefix + nCell*2;
|
|
+ if( y>x ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ if( y<x ) memset(a+y, 0, x-y); /* Zero the gap */
|
|
+
|
|
+ /* Zero out all the free blocks */
|
|
+ pc = scrubBackupInt16(&aTop[1]);
|
|
+ if( pc>0 && pc<x ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ while( pc ){
|
|
+ if( pc>(p->szUsable)-4 ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ n = scrubBackupInt16(&a[pc+2]);
|
|
+ if( pc+n>(p->szUsable) ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ if( n>4 ) memset(&a[pc+4], 0, n-4);
|
|
+ x = scrubBackupInt16(&a[pc]);
|
|
+ if( x<pc+4 && x>0 ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ pc = x;
|
|
+ }
|
|
+
|
|
+ /* Write this one page */
|
|
+ scrubBackupWrite(p, pgno, a);
|
|
+
|
|
+ /* Walk the tree and process child pages */
|
|
+ for(i=0; i<nCell; i++){
|
|
+ u32 X, M, K, nLocal;
|
|
+ sqlite3_int64 P;
|
|
+ pc = scrubBackupInt16(&aCell[i*2]);
|
|
+ if( pc <= szHdr ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ if( pc > p->szUsable-3 ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ if( aTop[0]==0x05 || aTop[0]==0x02 ){
|
|
+ if( pc+4 > p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ iChild = scrubBackupInt32(&a[pc]);
|
|
+ pc += 4;
|
|
+ scrubBackupBtree(p, iChild, iDepth+1);
|
|
+ if( aTop[0]==0x05 ) continue;
|
|
+ }
|
|
+ pc += scrubBackupVarint(&a[pc], &P);
|
|
+ if( pc >= p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ if( aTop[0]==0x0d ){
|
|
+ X = p->szUsable - 35;
|
|
+ }else{
|
|
+ X = ((p->szUsable - 12)*64/255) - 23;
|
|
+ }
|
|
+ if( P<=X ){
|
|
+ /* All content is local. No overflow */
|
|
+ continue;
|
|
+ }
|
|
+ M = ((p->szUsable - 12)*32/255)-23;
|
|
+ K = M + ((P-M)%(p->szUsable-4));
|
|
+ if( aTop[0]==0x0d ){
|
|
+ pc += scrubBackupVarintSize(&a[pc]);
|
|
+ if( pc > (p->szUsable-4) ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ }
|
|
+ nLocal = K<=X ? K : M;
|
|
+ if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; }
|
|
+ iChild = scrubBackupInt32(&a[pc+nLocal]);
|
|
+ scrubBackupOverflow(p, iChild, (u32)(P-nLocal));
|
|
+ }
|
|
+
|
|
+ /* Walk the right-most tree */
|
|
+ if( aTop[0]==0x05 || aTop[0]==0x02 ){
|
|
+ iChild = scrubBackupInt32(&aTop[8]);
|
|
+ scrubBackupBtree(p, iChild, iDepth+1);
|
|
+ }
|
|
+
|
|
+ /* All done */
|
|
+ if( pgno>1 ) sqlite3_free(a);
|
|
+ return;
|
|
+
|
|
+btree_corrupt:
|
|
+ scrubBackupErr(p, "corruption on page %d of source database (errid=%d)",
|
|
+ pgno, ln);
|
|
+ if( pgno>1 ) sqlite3_free(a);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Copy all ptrmap pages from source to destination.
|
|
+** This routine is only called if the source database is in autovacuum
|
|
+** or incremental vacuum mode.
|
|
+*/
|
|
+static void scrubBackupPtrmap(ScrubState *p){
|
|
+ u32 pgno = 2;
|
|
+ u32 J = p->szUsable/5;
|
|
+ u32 iLock = (1073742335/p->szPage)+1;
|
|
+ u8 *a, *pBuf;
|
|
+ if( p->rcErr ) return;
|
|
+ pBuf = scrubBackupAllocPage(p);
|
|
+ if( pBuf==0 ) return;
|
|
+ while( pgno<=p->nPage ){
|
|
+ a = scrubBackupRead(p, pgno, pBuf);
|
|
+ if( a==0 ) break;
|
|
+ scrubBackupWrite(p, pgno, a);
|
|
+ pgno += J+1;
|
|
+ if( pgno==iLock ) pgno++;
|
|
+ }
|
|
+ sqlite3_free(pBuf);
|
|
+}
|
|
+
|
|
+int sqlite3_scrub_backup(
|
|
+ const char *zSrcFile, /* Source file */
|
|
+ const char *zDestFile, /* Destination file */
|
|
+ char **pzErr /* Write error here if non-NULL */
|
|
+){
|
|
+ ScrubState s;
|
|
+ u32 n, i;
|
|
+ sqlite3_stmt *pStmt;
|
|
+
|
|
+ memset(&s, 0, sizeof(s));
|
|
+ s.zSrcFile = zSrcFile;
|
|
+ s.zDestFile = zDestFile;
|
|
+
|
|
+ /* Open both source and destination databases */
|
|
+ scrubBackupOpenSrc(&s);
|
|
+ scrubBackupOpenDest(&s);
|
|
+
|
|
+ /* Read in page 1 */
|
|
+ s.page1 = scrubBackupRead(&s, 1, 0);
|
|
+ if( s.page1==0 ) goto scrub_abort;
|
|
+ s.szUsable = s.szPage - s.page1[20];
|
|
+
|
|
+ /* Copy the freelist */
|
|
+ n = scrubBackupInt32(&s.page1[36]);
|
|
+ i = scrubBackupInt32(&s.page1[32]);
|
|
+ if( n ) scrubBackupFreelist(&s, i, n);
|
|
+
|
|
+ /* Copy ptrmap pages */
|
|
+ n = scrubBackupInt32(&s.page1[52]);
|
|
+ if( n ) scrubBackupPtrmap(&s);
|
|
+
|
|
+ /* Copy all of the btrees */
|
|
+ scrubBackupBtree(&s, 1, 0);
|
|
+ pStmt = scrubBackupPrepare(&s, s.dbSrc,
|
|
+ "SELECT rootpage FROM sqlite_master WHERE coalesce(rootpage,0)>0");
|
|
+ if( pStmt==0 ) goto scrub_abort;
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ i = (u32)sqlite3_column_int(pStmt, 0);
|
|
+ scrubBackupBtree(&s, i, 0);
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+
|
|
+ /* If the last page of the input db file is a free-list leaf, then the
|
|
+ ** backup file on disk is still smaller than the size indicated within
|
|
+ ** the database header. In this case, write a page of zeroes to the
|
|
+ ** last page of the backup database so that SQLite does not mistakenly
|
|
+ ** think the db is corrupt. */
|
|
+ if( s.iLastPage<s.nPage ){
|
|
+ u8 *aZero = scrubBackupAllocPage(&s);
|
|
+ if( aZero ){
|
|
+ memset(aZero, 0, s.szPage);
|
|
+ scrubBackupWrite(&s, s.nPage, aZero);
|
|
+ sqlite3_free(aZero);
|
|
+ }
|
|
+ }
|
|
+
|
|
+scrub_abort:
|
|
+ /* Close the destination database without closing the transaction. If we
|
|
+ ** commit, page zero will be overwritten. */
|
|
+ sqlite3_close(s.dbDest);
|
|
+
|
|
+ /* But do close out the read-transaction on the source database */
|
|
+ sqlite3_exec(s.dbSrc, "COMMIT;", 0, 0, 0);
|
|
+ sqlite3_close(s.dbSrc);
|
|
+ sqlite3_free(s.page1);
|
|
+ if( pzErr ){
|
|
+ *pzErr = s.zErr;
|
|
+ }else{
|
|
+ sqlite3_free(s.zErr);
|
|
+ }
|
|
+ return s.rcErr;
|
|
+}
|
|
+
|
|
+#ifdef SCRUB_STANDALONE
|
|
+/* Error and warning log */
|
|
+static void errorLogCallback(void *pNotUsed, int iErr, const char *zMsg){
|
|
+ const char *zType;
|
|
+ switch( iErr&0xff ){
|
|
+ case SQLITE_WARNING: zType = "WARNING"; break;
|
|
+ case SQLITE_NOTICE: zType = "NOTICE"; break;
|
|
+ default: zType = "ERROR"; break;
|
|
+ }
|
|
+ fprintf(stderr, "%s: %s\n", zType, zMsg);
|
|
+}
|
|
+
|
|
+/* The main() routine when this utility is run as a stand-alone program */
|
|
+int main(int argc, char **argv){
|
|
+ char *zErr = 0;
|
|
+ int rc;
|
|
+ if( argc!=3 ){
|
|
+ fprintf(stderr,"Usage: %s SOURCE DESTINATION\n", argv[0]);
|
|
+ exit(1);
|
|
+ }
|
|
+ sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, 0);
|
|
+ rc = sqlite3_scrub_backup(argv[1], argv[2], &zErr);
|
|
+ if( rc==SQLITE_NOMEM ){
|
|
+ fprintf(stderr, "%s: out of memory\n", argv[0]);
|
|
+ exit(1);
|
|
+ }
|
|
+ if( zErr ){
|
|
+ fprintf(stderr, "%s: %s\n", argv[0], zErr);
|
|
+ sqlite3_free(zErr);
|
|
+ exit(1);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/series.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/series.c 2019-05-14 16:22:31.263475500 +0200
|
|
@@ -0,0 +1,442 @@
|
|
+/*
|
|
+** 2015-08-18
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file demonstrates how to create a table-valued-function using
|
|
+** a virtual table. This demo implements the generate_series() function
|
|
+** which gives similar results to the eponymous function in PostgreSQL.
|
|
+** Examples:
|
|
+**
|
|
+** SELECT * FROM generate_series(0,100,5);
|
|
+**
|
|
+** The query above returns integers from 0 through 100 counting by steps
|
|
+** of 5.
|
|
+**
|
|
+** SELECT * FROM generate_series(0,100);
|
|
+**
|
|
+** Integers from 0 through 100 with a step size of 1.
|
|
+**
|
|
+** SELECT * FROM generate_series(20) LIMIT 10;
|
|
+**
|
|
+** Integers 20 through 29.
|
|
+**
|
|
+** HOW IT WORKS
|
|
+**
|
|
+** The generate_series "function" is really a virtual table with the
|
|
+** following schema:
|
|
+**
|
|
+** CREATE TABLE generate_series(
|
|
+** value,
|
|
+** start HIDDEN,
|
|
+** stop HIDDEN,
|
|
+** step HIDDEN
|
|
+** );
|
|
+**
|
|
+** Function arguments in queries against this virtual table are translated
|
|
+** into equality constraints against successive hidden columns. In other
|
|
+** words, the following pairs of queries are equivalent to each other:
|
|
+**
|
|
+** SELECT * FROM generate_series(0,100,5);
|
|
+** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5;
|
|
+**
|
|
+** SELECT * FROM generate_series(0,100);
|
|
+** SELECT * FROM generate_series WHERE start=0 AND stop=100;
|
|
+**
|
|
+** SELECT * FROM generate_series(20) LIMIT 10;
|
|
+** SELECT * FROM generate_series WHERE start=20 LIMIT 10;
|
|
+**
|
|
+** The generate_series virtual table implementation leaves the xCreate method
|
|
+** set to NULL. This means that it is not possible to do a CREATE VIRTUAL
|
|
+** TABLE command with "generate_series" as the USING argument. Instead, there
|
|
+** is a single generate_series virtual table that is always available without
|
|
+** having to be created first.
|
|
+**
|
|
+** The xBestIndex method looks for equality constraints against the hidden
|
|
+** start, stop, and step columns, and if present, it uses those constraints
|
|
+** to bound the sequence of generated values. If the equality constraints
|
|
+** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step.
|
|
+** xBestIndex returns a small cost when both start and stop are available,
|
|
+** and a very large cost if either start or stop are unavailable. This
|
|
+** encourages the query planner to order joins such that the bounds of the
|
|
+** series are well-defined.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+
|
|
+/* series_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct series_cursor series_cursor;
|
|
+struct series_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ int isDesc; /* True to count down rather than up */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+ sqlite3_int64 iValue; /* Current value ("value") */
|
|
+ sqlite3_int64 mnValue; /* Mimimum value ("start") */
|
|
+ sqlite3_int64 mxValue; /* Maximum value ("stop") */
|
|
+ sqlite3_int64 iStep; /* Increment ("step") */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The seriesConnect() method is invoked to create a new
|
|
+** series_vtab that describes the generate_series virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for series_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the series_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against generate_series will look like.
|
|
+*/
|
|
+static int seriesConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define SERIES_COLUMN_VALUE 0
|
|
+#define SERIES_COLUMN_START 1
|
|
+#define SERIES_COLUMN_STOP 2
|
|
+#define SERIES_COLUMN_STEP 3
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for series_cursor objects.
|
|
+*/
|
|
+static int seriesDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new series_cursor object.
|
|
+*/
|
|
+static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ series_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a series_cursor.
|
|
+*/
|
|
+static int seriesClose(sqlite3_vtab_cursor *cur){
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a series_cursor to its next row of output.
|
|
+*/
|
|
+static int seriesNext(sqlite3_vtab_cursor *cur){
|
|
+ series_cursor *pCur = (series_cursor*)cur;
|
|
+ if( pCur->isDesc ){
|
|
+ pCur->iValue -= pCur->iStep;
|
|
+ }else{
|
|
+ pCur->iValue += pCur->iStep;
|
|
+ }
|
|
+ pCur->iRowid++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the series_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int seriesColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ series_cursor *pCur = (series_cursor*)cur;
|
|
+ sqlite3_int64 x = 0;
|
|
+ switch( i ){
|
|
+ case SERIES_COLUMN_START: x = pCur->mnValue; break;
|
|
+ case SERIES_COLUMN_STOP: x = pCur->mxValue; break;
|
|
+ case SERIES_COLUMN_STEP: x = pCur->iStep; break;
|
|
+ default: x = pCur->iValue; break;
|
|
+ }
|
|
+ sqlite3_result_int64(ctx, x);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** first row returned is assigned rowid value 1, and each subsequent
|
|
+** row a value 1 more than that of the previous.
|
|
+*/
|
|
+static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ series_cursor *pCur = (series_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int seriesEof(sqlite3_vtab_cursor *cur){
|
|
+ series_cursor *pCur = (series_cursor*)cur;
|
|
+ if( pCur->isDesc ){
|
|
+ return pCur->iValue < pCur->mnValue;
|
|
+ }else{
|
|
+ return pCur->iValue > pCur->mxValue;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* True to cause run-time checking of the start=, stop=, and/or step=
|
|
+** parameters. The only reason to do this is for testing the
|
|
+** constraint checking logic for virtual tables in the SQLite core.
|
|
+*/
|
|
+#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
|
|
+# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the series_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to seriesColumn() or seriesRowid() or
|
|
+** seriesEof().
|
|
+**
|
|
+** The query plan selected by seriesBestIndex is passed in the idxNum
|
|
+** parameter. (idxStr is not used in this implementation.) idxNum
|
|
+** is a bitmask showing which constraints are available:
|
|
+**
|
|
+** 1: start=VALUE
|
|
+** 2: stop=VALUE
|
|
+** 4: step=VALUE
|
|
+**
|
|
+** Also, if bit 8 is set, that means that the series should be output
|
|
+** in descending order rather than in ascending order.
|
|
+**
|
|
+** This routine should initialize the cursor and position it so that it
|
|
+** is pointing at the first row, or pointing off the end of the table
|
|
+** (so that seriesEof() will return true) if the table is empty.
|
|
+*/
|
|
+static int seriesFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ series_cursor *pCur = (series_cursor *)pVtabCursor;
|
|
+ int i = 0;
|
|
+ if( idxNum & 1 ){
|
|
+ pCur->mnValue = sqlite3_value_int64(argv[i++]);
|
|
+ }else{
|
|
+ pCur->mnValue = 0;
|
|
+ }
|
|
+ if( idxNum & 2 ){
|
|
+ pCur->mxValue = sqlite3_value_int64(argv[i++]);
|
|
+ }else{
|
|
+ pCur->mxValue = 0xffffffff;
|
|
+ }
|
|
+ if( idxNum & 4 ){
|
|
+ pCur->iStep = sqlite3_value_int64(argv[i++]);
|
|
+ if( pCur->iStep<1 ) pCur->iStep = 1;
|
|
+ }else{
|
|
+ pCur->iStep = 1;
|
|
+ }
|
|
+ for(i=0; i<argc; i++){
|
|
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
|
|
+ /* If any of the constraints have a NULL value, then return no rows.
|
|
+ ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
|
|
+ pCur->mnValue = 1;
|
|
+ pCur->mxValue = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( idxNum & 8 ){
|
|
+ pCur->isDesc = 1;
|
|
+ pCur->iValue = pCur->mxValue;
|
|
+ if( pCur->iStep>0 ){
|
|
+ pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep;
|
|
+ }
|
|
+ }else{
|
|
+ pCur->isDesc = 0;
|
|
+ pCur->iValue = pCur->mnValue;
|
|
+ }
|
|
+ pCur->iRowid = 1;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the generate_series virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+**
|
|
+** In this implementation idxNum is used to represent the
|
|
+** query plan. idxStr is unused.
|
|
+**
|
|
+** The query plan is represented by bits in idxNum:
|
|
+**
|
|
+** (1) start = $value -- constraint exists
|
|
+** (2) stop = $value -- constraint exists
|
|
+** (4) step = $value -- constraint exists
|
|
+** (8) output in descending order
|
|
+*/
|
|
+static int seriesBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i, j; /* Loop over constraints */
|
|
+ int idxNum = 0; /* The query plan bitmask */
|
|
+ int unusableMask = 0; /* Mask of unusable constraints */
|
|
+ int nArg = 0; /* Number of arguments that seriesFilter() expects */
|
|
+ int aIdx[3]; /* Constraints on start, stop, and step */
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ /* This implementation assumes that the start, stop, and step columns
|
|
+ ** are the last three columns in the virtual table. */
|
|
+ assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
|
|
+ assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
|
|
+ aIdx[0] = aIdx[1] = aIdx[2] = -1;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ int iCol; /* 0 for start, 1 for stop, 2 for step */
|
|
+ int iMask; /* bitmask for those column */
|
|
+ if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
|
|
+ iCol = pConstraint->iColumn - SERIES_COLUMN_START;
|
|
+ assert( iCol>=0 && iCol<=2 );
|
|
+ iMask = 1 << iCol;
|
|
+ if( pConstraint->usable==0 ){
|
|
+ unusableMask |= iMask;
|
|
+ continue;
|
|
+ }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ idxNum |= iMask;
|
|
+ aIdx[iCol] = i;
|
|
+ }
|
|
+ }
|
|
+ for(i=0; i<3; i++){
|
|
+ if( (j = aIdx[i])>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
|
|
+ pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
|
|
+ }
|
|
+ }
|
|
+ if( (unusableMask & ~idxNum)!=0 ){
|
|
+ /* The start, stop, and step columns are inputs. Therefore if there
|
|
+ ** are unusable constraints on any of start, stop, or step then
|
|
+ ** this plan is unusable */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ if( (idxNum & 3)==3 ){
|
|
+ /* Both start= and stop= boundaries are available. This is the
|
|
+ ** the preferred case */
|
|
+ pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
|
|
+ pIdxInfo->estimatedRows = 1000;
|
|
+ if( pIdxInfo->nOrderBy==1 ){
|
|
+ if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8;
|
|
+ pIdxInfo->orderByConsumed = 1;
|
|
+ }
|
|
+ }else{
|
|
+ /* If either boundary is missing, we have to generate a huge span
|
|
+ ** of numbers. Make this case very expensive so that the query
|
|
+ ** planner will work hard to avoid it. */
|
|
+ pIdxInfo->estimatedRows = 2147483647;
|
|
+ }
|
|
+ pIdxInfo->idxNum = idxNum;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** generate_series virtual table.
|
|
+*/
|
|
+static const sqlite3_module seriesModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ seriesConnect, /* xConnect */
|
|
+ seriesBestIndex, /* xBestIndex */
|
|
+ seriesDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ seriesOpen, /* xOpen - open a cursor */
|
|
+ seriesClose, /* xClose - close a cursor */
|
|
+ seriesFilter, /* xFilter - configure scan constraints */
|
|
+ seriesNext, /* xNext - advance a cursor */
|
|
+ seriesEof, /* xEof - check for end of scan */
|
|
+ seriesColumn, /* xColumn - read data */
|
|
+ seriesRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_series_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( sqlite3_libversion_number()<3008012 ){
|
|
+ *pzErrMsg = sqlite3_mprintf(
|
|
+ "generate_series() requires SQLite 3.8.12 or later");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( sqlite3_libversion_number()<3008012 ){
|
|
+ *pzErrMsg = sqlite3_mprintf(
|
|
+ "generate_series() requires SQLite 3.8.12 or later");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/sha1.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/sha1.c 2019-05-14 16:22:31.264451600 +0200
|
|
@@ -0,0 +1,425 @@
|
|
+/*
|
|
+** 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];
|
|
+};
|
|
+
|
|
+
|
|
+#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
|
|
+/*
|
|
+ * GCC by itself only generates left rotates. Use right rotates if
|
|
+ * possible to be kinder to dinky implementations with iterative rotate
|
|
+ * instructions.
|
|
+ */
|
|
+#define SHA_ROT(op, x, k) \
|
|
+ ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
|
|
+#define rol(x,k) SHA_ROT("roll", x, k)
|
|
+#define ror(x,k) SHA_ROT("rorl", x, k)
|
|
+
|
|
+#else
|
|
+/* Generic C equivalent */
|
|
+#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)
|
|
+#endif
|
|
+
|
|
+
|
|
+#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.
|
|
+ */
|
|
+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 hexadecimal hash here */
|
|
+){
|
|
+ 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);
|
|
+ }
|
|
+ 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);
|
|
+ }
|
|
+ hash_finish(&cx, zOut);
|
|
+ sqlite3_result_text(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);
|
|
+ sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_sha_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0,
|
|
+ sha1Func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0,
|
|
+ sha1QueryFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0,
|
|
+ sha1Func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0,
|
|
+ sha1QueryFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/shathree.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/shathree.c 2019-05-14 16:22:31.266404300 +0200
|
|
@@ -0,0 +1,744 @@
|
|
+/*
|
|
+** 2017-03-08
|
|
+**
|
|
+** 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 SHA3 hashes.
|
|
+** Two SQL functions are implemented:
|
|
+**
|
|
+** sha3(X,SIZE)
|
|
+** sha3_query(Y,SIZE)
|
|
+**
|
|
+** The sha3(X) function computes the SHA3 hash of the input X, or NULL if
|
|
+** X is NULL.
|
|
+**
|
|
+** The sha3_query(Y) function evalutes all queries in the SQL statements of Y
|
|
+** and returns a hash of their results.
|
|
+**
|
|
+** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm
|
|
+** is used. If SIZE is included it must be one of the integers 224, 256,
|
|
+** 384, or 512, to determine SHA3 hash variant that is computed.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdarg.h>
|
|
+typedef sqlite3_uint64 u64;
|
|
+
|
|
+/******************************************************************************
|
|
+** The Hash Engine
|
|
+*/
|
|
+/*
|
|
+** Macros to determine whether the machine is big or little endian,
|
|
+** and whether or not that determination is run-time or compile-time.
|
|
+**
|
|
+** For best performance, an attempt is made to guess at the byte-order
|
|
+** using C-preprocessor macros. If that is unsuccessful, or if
|
|
+** -DSHA3_BYTEORDER=0 is set, then byte-order is determined
|
|
+** at run-time.
|
|
+*/
|
|
+#ifndef SHA3_BYTEORDER
|
|
+# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
|
|
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
|
|
+ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
|
|
+ defined(__arm__)
|
|
+# define SHA3_BYTEORDER 1234
|
|
+# elif defined(sparc) || defined(__ppc__)
|
|
+# define SHA3_BYTEORDER 4321
|
|
+# else
|
|
+# define SHA3_BYTEORDER 0
|
|
+# endif
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+** State structure for a SHA3 hash in progress
|
|
+*/
|
|
+typedef struct SHA3Context SHA3Context;
|
|
+struct SHA3Context {
|
|
+ union {
|
|
+ u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */
|
|
+ unsigned char x[1600]; /* ... or 1600 bytes */
|
|
+ } u;
|
|
+ unsigned nRate; /* Bytes of input accepted per Keccak iteration */
|
|
+ unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */
|
|
+ unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */
|
|
+};
|
|
+
|
|
+/* Allow the following routine to use the B0 variable, which is also
|
|
+** a macro in the termios.h header file */
|
|
+#undef B0
|
|
+
|
|
+/*
|
|
+** A single step of the Keccak mixing function for a 1600-bit state
|
|
+*/
|
|
+static void KeccakF1600Step(SHA3Context *p){
|
|
+ int i;
|
|
+ u64 b0, b1, b2, b3, b4;
|
|
+ u64 c0, c1, c2, c3, c4;
|
|
+ u64 d0, d1, d2, d3, d4;
|
|
+ static const u64 RC[] = {
|
|
+ 0x0000000000000001ULL, 0x0000000000008082ULL,
|
|
+ 0x800000000000808aULL, 0x8000000080008000ULL,
|
|
+ 0x000000000000808bULL, 0x0000000080000001ULL,
|
|
+ 0x8000000080008081ULL, 0x8000000000008009ULL,
|
|
+ 0x000000000000008aULL, 0x0000000000000088ULL,
|
|
+ 0x0000000080008009ULL, 0x000000008000000aULL,
|
|
+ 0x000000008000808bULL, 0x800000000000008bULL,
|
|
+ 0x8000000000008089ULL, 0x8000000000008003ULL,
|
|
+ 0x8000000000008002ULL, 0x8000000000000080ULL,
|
|
+ 0x000000000000800aULL, 0x800000008000000aULL,
|
|
+ 0x8000000080008081ULL, 0x8000000000008080ULL,
|
|
+ 0x0000000080000001ULL, 0x8000000080008008ULL
|
|
+ };
|
|
+# define a00 (p->u.s[0])
|
|
+# define a01 (p->u.s[1])
|
|
+# define a02 (p->u.s[2])
|
|
+# define a03 (p->u.s[3])
|
|
+# define a04 (p->u.s[4])
|
|
+# define a10 (p->u.s[5])
|
|
+# define a11 (p->u.s[6])
|
|
+# define a12 (p->u.s[7])
|
|
+# define a13 (p->u.s[8])
|
|
+# define a14 (p->u.s[9])
|
|
+# define a20 (p->u.s[10])
|
|
+# define a21 (p->u.s[11])
|
|
+# define a22 (p->u.s[12])
|
|
+# define a23 (p->u.s[13])
|
|
+# define a24 (p->u.s[14])
|
|
+# define a30 (p->u.s[15])
|
|
+# define a31 (p->u.s[16])
|
|
+# define a32 (p->u.s[17])
|
|
+# define a33 (p->u.s[18])
|
|
+# define a34 (p->u.s[19])
|
|
+# define a40 (p->u.s[20])
|
|
+# define a41 (p->u.s[21])
|
|
+# define a42 (p->u.s[22])
|
|
+# define a43 (p->u.s[23])
|
|
+# define a44 (p->u.s[24])
|
|
+# define ROL64(a,x) ((a<<x)|(a>>(64-x)))
|
|
+
|
|
+ for(i=0; i<24; i+=4){
|
|
+ c0 = a00^a10^a20^a30^a40;
|
|
+ c1 = a01^a11^a21^a31^a41;
|
|
+ c2 = a02^a12^a22^a32^a42;
|
|
+ c3 = a03^a13^a23^a33^a43;
|
|
+ c4 = a04^a14^a24^a34^a44;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
+
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a11^d1), 44);
|
|
+ b2 = ROL64((a22^d2), 43);
|
|
+ b3 = ROL64((a33^d3), 21);
|
|
+ b4 = ROL64((a44^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i];
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b2 = ROL64((a20^d0), 3);
|
|
+ b3 = ROL64((a31^d1), 45);
|
|
+ b4 = ROL64((a42^d2), 61);
|
|
+ b0 = ROL64((a03^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b4 = ROL64((a40^d0), 18);
|
|
+ b0 = ROL64((a01^d1), 1);
|
|
+ b1 = ROL64((a12^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a34^d4), 8);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b1 = ROL64((a10^d0), 36);
|
|
+ b2 = ROL64((a21^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a43^d3), 56);
|
|
+ b0 = ROL64((a04^d4), 27);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b3 = ROL64((a30^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a02^d2), 62);
|
|
+ b1 = ROL64((a13^d3), 55);
|
|
+ b2 = ROL64((a24^d4), 39);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ c0 = a00^a20^a40^a10^a30;
|
|
+ c1 = a11^a31^a01^a21^a41;
|
|
+ c2 = a22^a42^a12^a32^a02;
|
|
+ c3 = a33^a03^a23^a43^a13;
|
|
+ c4 = a44^a14^a34^a04^a24;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
+
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a31^d1), 44);
|
|
+ b2 = ROL64((a12^d2), 43);
|
|
+ b3 = ROL64((a43^d3), 21);
|
|
+ b4 = ROL64((a24^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i+1];
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b2 = ROL64((a40^d0), 3);
|
|
+ b3 = ROL64((a21^d1), 45);
|
|
+ b4 = ROL64((a02^d2), 61);
|
|
+ b0 = ROL64((a33^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b4 = ROL64((a30^d0), 18);
|
|
+ b0 = ROL64((a11^d1), 1);
|
|
+ b1 = ROL64((a42^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a04^d4), 8);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b1 = ROL64((a20^d0), 36);
|
|
+ b2 = ROL64((a01^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a13^d3), 56);
|
|
+ b0 = ROL64((a44^d4), 27);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b3 = ROL64((a10^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a22^d2), 62);
|
|
+ b1 = ROL64((a03^d3), 55);
|
|
+ b2 = ROL64((a34^d4), 39);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ c0 = a00^a40^a30^a20^a10;
|
|
+ c1 = a31^a21^a11^a01^a41;
|
|
+ c2 = a12^a02^a42^a32^a22;
|
|
+ c3 = a43^a33^a23^a13^a03;
|
|
+ c4 = a24^a14^a04^a44^a34;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
+
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a21^d1), 44);
|
|
+ b2 = ROL64((a42^d2), 43);
|
|
+ b3 = ROL64((a13^d3), 21);
|
|
+ b4 = ROL64((a34^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i+2];
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b2 = ROL64((a30^d0), 3);
|
|
+ b3 = ROL64((a01^d1), 45);
|
|
+ b4 = ROL64((a22^d2), 61);
|
|
+ b0 = ROL64((a43^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b4 = ROL64((a10^d0), 18);
|
|
+ b0 = ROL64((a31^d1), 1);
|
|
+ b1 = ROL64((a02^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a44^d4), 8);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b1 = ROL64((a40^d0), 36);
|
|
+ b2 = ROL64((a11^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a03^d3), 56);
|
|
+ b0 = ROL64((a24^d4), 27);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b3 = ROL64((a20^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a12^d2), 62);
|
|
+ b1 = ROL64((a33^d3), 55);
|
|
+ b2 = ROL64((a04^d4), 39);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ c0 = a00^a30^a10^a40^a20;
|
|
+ c1 = a21^a01^a31^a11^a41;
|
|
+ c2 = a42^a22^a02^a32^a12;
|
|
+ c3 = a13^a43^a23^a03^a33;
|
|
+ c4 = a34^a14^a44^a24^a04;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
+
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a01^d1), 44);
|
|
+ b2 = ROL64((a02^d2), 43);
|
|
+ b3 = ROL64((a03^d3), 21);
|
|
+ b4 = ROL64((a04^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i+3];
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b2 = ROL64((a10^d0), 3);
|
|
+ b3 = ROL64((a11^d1), 45);
|
|
+ b4 = ROL64((a12^d2), 61);
|
|
+ b0 = ROL64((a13^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b4 = ROL64((a20^d0), 18);
|
|
+ b0 = ROL64((a21^d1), 1);
|
|
+ b1 = ROL64((a22^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a24^d4), 8);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b1 = ROL64((a30^d0), 36);
|
|
+ b2 = ROL64((a31^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a33^d3), 56);
|
|
+ b0 = ROL64((a34^d4), 27);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
+
|
|
+ b3 = ROL64((a40^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a42^d2), 62);
|
|
+ b1 = ROL64((a43^d3), 55);
|
|
+ b2 = ROL64((a44^d4), 39);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Initialize a new hash. iSize determines the size of the hash
|
|
+** in bits and should be one of 224, 256, 384, or 512. Or iSize
|
|
+** can be zero to use the default hash size of 256 bits.
|
|
+*/
|
|
+static void SHA3Init(SHA3Context *p, int iSize){
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ if( iSize>=128 && iSize<=512 ){
|
|
+ p->nRate = (1600 - ((iSize + 31)&~31)*2)/8;
|
|
+ }else{
|
|
+ p->nRate = (1600 - 2*256)/8;
|
|
+ }
|
|
+#if SHA3_BYTEORDER==1234
|
|
+ /* Known to be little-endian at compile-time. No-op */
|
|
+#elif SHA3_BYTEORDER==4321
|
|
+ p->ixMask = 7; /* Big-endian */
|
|
+#else
|
|
+ {
|
|
+ static unsigned int one = 1;
|
|
+ if( 1==*(unsigned char*)&one ){
|
|
+ /* Little endian. No byte swapping. */
|
|
+ p->ixMask = 0;
|
|
+ }else{
|
|
+ /* Big endian. Byte swap. */
|
|
+ p->ixMask = 7;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** Make consecutive calls to the SHA3Update function to add new content
|
|
+** to the hash
|
|
+*/
|
|
+static void SHA3Update(
|
|
+ SHA3Context *p,
|
|
+ const unsigned char *aData,
|
|
+ unsigned int nData
|
|
+){
|
|
+ unsigned int i = 0;
|
|
+#if SHA3_BYTEORDER==1234
|
|
+ if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){
|
|
+ for(; i+7<nData; i+=8){
|
|
+ p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i];
|
|
+ p->nLoaded += 8;
|
|
+ if( p->nLoaded>=p->nRate ){
|
|
+ KeccakF1600Step(p);
|
|
+ p->nLoaded = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ for(; i<nData; i++){
|
|
+#if SHA3_BYTEORDER==1234
|
|
+ p->u.x[p->nLoaded] ^= aData[i];
|
|
+#elif SHA3_BYTEORDER==4321
|
|
+ p->u.x[p->nLoaded^0x07] ^= aData[i];
|
|
+#else
|
|
+ p->u.x[p->nLoaded^p->ixMask] ^= aData[i];
|
|
+#endif
|
|
+ p->nLoaded++;
|
|
+ if( p->nLoaded==p->nRate ){
|
|
+ KeccakF1600Step(p);
|
|
+ p->nLoaded = 0;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** After all content has been added, invoke SHA3Final() to compute
|
|
+** the final hash. The function returns a pointer to the binary
|
|
+** hash value.
|
|
+*/
|
|
+static unsigned char *SHA3Final(SHA3Context *p){
|
|
+ unsigned int i;
|
|
+ if( p->nLoaded==p->nRate-1 ){
|
|
+ const unsigned char c1 = 0x86;
|
|
+ SHA3Update(p, &c1, 1);
|
|
+ }else{
|
|
+ const unsigned char c2 = 0x06;
|
|
+ const unsigned char c3 = 0x80;
|
|
+ SHA3Update(p, &c2, 1);
|
|
+ p->nLoaded = p->nRate - 1;
|
|
+ SHA3Update(p, &c3, 1);
|
|
+ }
|
|
+ for(i=0; i<p->nRate; i++){
|
|
+ p->u.x[i+p->nRate] = p->u.x[i^p->ixMask];
|
|
+ }
|
|
+ return &p->u.x[p->nRate];
|
|
+}
|
|
+/* End of the hashing logic
|
|
+*****************************************************************************/
|
|
+
|
|
+/*
|
|
+** Implementation of the sha3(X,SIZE) function.
|
|
+**
|
|
+** Return a BLOB which is the SIZE-bit SHA3 hash of X. The default
|
|
+** size is 256. If X is a BLOB, it is hashed as is.
|
|
+** For all other non-NULL types of input, X is converted into a UTF-8 string
|
|
+** and the string is hashed without the trailing 0x00 terminator. The hash
|
|
+** of a NULL value is NULL.
|
|
+*/
|
|
+static void sha3Func(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ SHA3Context cx;
|
|
+ int eType = sqlite3_value_type(argv[0]);
|
|
+ int nByte = sqlite3_value_bytes(argv[0]);
|
|
+ int iSize;
|
|
+ if( argc==1 ){
|
|
+ iSize = 256;
|
|
+ }else{
|
|
+ iSize = sqlite3_value_int(argv[1]);
|
|
+ if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){
|
|
+ sqlite3_result_error(context, "SHA3 size should be one of: 224 256 "
|
|
+ "384 512", -1);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if( eType==SQLITE_NULL ) return;
|
|
+ SHA3Init(&cx, iSize);
|
|
+ if( eType==SQLITE_BLOB ){
|
|
+ SHA3Update(&cx, sqlite3_value_blob(argv[0]), nByte);
|
|
+ }else{
|
|
+ SHA3Update(&cx, sqlite3_value_text(argv[0]), nByte);
|
|
+ }
|
|
+ sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT);
|
|
+}
|
|
+
|
|
+/* Compute a string using sqlite3_vsnprintf() with a maximum length
|
|
+** of 50 bytes and add it to the hash.
|
|
+*/
|
|
+static void hash_step_vformat(
|
|
+ SHA3Context *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);
|
|
+ SHA3Update(p, (unsigned char*)zBuf, n);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the sha3_query(SQL,SIZE) function.
|
|
+**
|
|
+** This function compiles and runs the SQL statement(s) given in the
|
|
+** argument. The results are hashed using a SIZE-bit SHA3. The default
|
|
+** size is 256.
|
|
+**
|
|
+** The format of the byte stream that is hashed is summarized as follows:
|
|
+**
|
|
+** S<n>:<sql>
|
|
+** R
|
|
+** N
|
|
+** I<int>
|
|
+** F<ieee-float>
|
|
+** B<size>:<bytes>
|
|
+** T<size>:<text>
|
|
+**
|
|
+** <sql> is the original SQL text for each statement run and <n> is
|
|
+** the size of that text. The SQL text is UTF-8. A single R character
|
|
+** occurs before the start of each row. N means a NULL value.
|
|
+** I mean an 8-byte little-endian integer <int>. F is a floating point
|
|
+** number with an 8-byte little-endian IEEE floating point value <ieee-float>.
|
|
+** B means blobs of <size> bytes. T means text rendered as <size>
|
|
+** bytes of UTF-8. The <n> and <size> values are expressed as an ASCII
|
|
+** text integers.
|
|
+**
|
|
+** For each SQL statement in the X input, there is one S segment. Each
|
|
+** S segment is followed by zero or more R segments, one for each row in the
|
|
+** result set. After each R, there are one or more N, I, F, B, or T segments,
|
|
+** one for each column in the result set. Segments are concatentated directly
|
|
+** with no delimiters of any kind.
|
|
+*/
|
|
+static void sha3QueryFunc(
|
|
+ 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;
|
|
+ SHA3Context cx;
|
|
+ int iSize;
|
|
+
|
|
+ if( argc==1 ){
|
|
+ iSize = 256;
|
|
+ }else{
|
|
+ iSize = sqlite3_value_int(argv[1]);
|
|
+ if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){
|
|
+ sqlite3_result_error(context, "SHA3 size should be one of: 224 256 "
|
|
+ "384 512", -1);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if( zSql==0 ) return;
|
|
+ SHA3Init(&cx, iSize);
|
|
+ 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);
|
|
+ SHA3Update(&cx,(unsigned char*)z,n);
|
|
+
|
|
+ /* Compute a hash over the result of the query */
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ SHA3Update(&cx,(const unsigned char*)"R",1);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ switch( sqlite3_column_type(pStmt,i) ){
|
|
+ case SQLITE_NULL: {
|
|
+ SHA3Update(&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';
|
|
+ SHA3Update(&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';
|
|
+ SHA3Update(&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);
|
|
+ SHA3Update(&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);
|
|
+ SHA3Update(&cx, z2, n2);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ }
|
|
+ sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_shathree_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "sha3", 1, SQLITE_UTF8, 0,
|
|
+ sha3Func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3", 2, SQLITE_UTF8, 0,
|
|
+ sha3Func, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8, 0,
|
|
+ sha3QueryFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3_query", 2, SQLITE_UTF8, 0,
|
|
+ sha3QueryFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "sha3", 1, SQLITE_UTF8, 0,
|
|
+ sha3Func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3", 2, SQLITE_UTF8, 0,
|
|
+ sha3Func, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8, 0,
|
|
+ sha3QueryFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3_query", 2, SQLITE_UTF8, 0,
|
|
+ sha3QueryFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/shell.c 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/shell.c 2019-05-14 16:22:31.214658700 +0200
|
|
@@ -200,6 +200,11 @@ extern char *sqlite3_win32_utf8_to_mbcs_
|
|
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
|
|
#endif
|
|
|
|
+/* Prevent sqlite3_extension_init() to be multiply defined */
|
|
+#ifndef SQLITE_TEST
|
|
+# define SQLITE_TEST 1
|
|
+#endif
|
|
+
|
|
/* On Windows, we normally run with output mode of TEXT so that \n characters
|
|
** are automatically translated into \r\n. However, this behavior needs
|
|
** to be disabled in some cases (ex: when generating CSV output and when
|
|
@@ -298,45 +303,14 @@ static HANDLE hProcess;
|
|
static FILETIME ftKernelBegin;
|
|
static FILETIME ftUserBegin;
|
|
static sqlite3_int64 ftWallBegin;
|
|
-typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME,
|
|
- LPFILETIME, LPFILETIME);
|
|
-static GETPROCTIMES getProcessTimesAddr = NULL;
|
|
-
|
|
-/*
|
|
-** Check to see if we have timer support. Return 1 if necessary
|
|
-** support found (or found previously).
|
|
-*/
|
|
-static int hasTimer(void){
|
|
- if( getProcessTimesAddr ){
|
|
- return 1;
|
|
- } else {
|
|
- /* GetProcessTimes() isn't supported in WIN95 and some other Windows
|
|
- ** versions. See if the version we are running on has it, and if it
|
|
- ** does, save off a pointer to it and the current process handle.
|
|
- */
|
|
- hProcess = GetCurrentProcess();
|
|
- if( hProcess ){
|
|
- HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
|
|
- if( NULL != hinstLib ){
|
|
- getProcessTimesAddr =
|
|
- (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
|
|
- if( NULL != getProcessTimesAddr ){
|
|
- return 1;
|
|
- }
|
|
- FreeLibrary(hinstLib);
|
|
- }
|
|
- }
|
|
- }
|
|
- return 0;
|
|
-}
|
|
|
|
/*
|
|
** Begin timing an operation
|
|
*/
|
|
static void beginTimer(void){
|
|
- if( enableTimer && getProcessTimesAddr ){
|
|
+ if( enableTimer ){
|
|
FILETIME ftCreation, ftExit;
|
|
- getProcessTimesAddr(hProcess,&ftCreation,&ftExit,
|
|
+ GetProcessTimes(hProcess,&ftCreation,&ftExit,
|
|
&ftKernelBegin,&ftUserBegin);
|
|
ftWallBegin = timeOfDay();
|
|
}
|
|
@@ -353,10 +327,10 @@ static double timeDiff(FILETIME *pStart,
|
|
** Print the timing results.
|
|
*/
|
|
static void endTimer(void){
|
|
- if( enableTimer && getProcessTimesAddr){
|
|
+ if( enableTimer){
|
|
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
|
|
sqlite3_int64 ftWallEnd = timeOfDay();
|
|
- getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
|
|
+ GetProcessTimes(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
|
|
printf("Run Time: real %.3f user %f sys %f\n",
|
|
(ftWallEnd - ftWallBegin)*0.001,
|
|
timeDiff(&ftUserBegin, &ftUserEnd),
|
|
@@ -366,7 +340,7 @@ static void endTimer(void){
|
|
|
|
#define BEGIN_TIMER beginTimer()
|
|
#define END_TIMER endTimer()
|
|
-#define HAS_TIMER hasTimer()
|
|
+#define HAS_TIMER 1
|
|
|
|
#else
|
|
#define BEGIN_TIMER
|
|
@@ -382,7 +356,7 @@ static void endTimer(void){
|
|
/*
|
|
** Number of elements in an array
|
|
*/
|
|
-#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
|
|
+#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))
|
|
|
|
/*
|
|
** If the following flag is set, then command execution stops
|
|
@@ -1382,6 +1356,10 @@ struct SHA3Context {
|
|
unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */
|
|
};
|
|
|
|
+/* Allow the following routine to use the B0 variable, which is also
|
|
+** a macro in the termios.h header file */
|
|
+#undef B0
|
|
+
|
|
/*
|
|
** A single step of the Keccak mixing function for a 1600-bit state
|
|
*/
|
|
@@ -2021,6 +1999,32 @@ int sqlite3_shathree_init(
|
|
}
|
|
return rc;
|
|
}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "sha3", 1, SQLITE_UTF8, 0,
|
|
+ sha3Func, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3", 2, SQLITE_UTF8, 0,
|
|
+ sha3Func, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8, 0,
|
|
+ sha3QueryFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sha3_query", 2, SQLITE_UTF8, 0,
|
|
+ sha3QueryFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
|
|
/************************* End ../ext/misc/shathree.c ********************/
|
|
/************************* Begin ../ext/misc/fileio.c ******************/
|
|
@@ -2107,9 +2111,9 @@ SQLITE_EXTENSION_INIT1
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
+#include <dirent.h>
|
|
#if !defined(_WIN32) && !defined(WIN32)
|
|
# include <unistd.h>
|
|
-# include <dirent.h>
|
|
# include <utime.h>
|
|
# include <sys/time.h>
|
|
#else
|
|
@@ -2117,7 +2121,6 @@ SQLITE_EXTENSION_INIT1
|
|
# include <io.h>
|
|
# include <direct.h>
|
|
/* # include "test_windirent.h" */
|
|
-# define dirent DIRENT
|
|
# ifndef chmod
|
|
# define chmod _chmod
|
|
# endif
|
|
@@ -2177,7 +2180,7 @@ static void readFileContents(sqlite3_con
|
|
fclose(in);
|
|
return;
|
|
}
|
|
- pBuf = sqlite3_malloc64( nIn ? nIn : 1 );
|
|
+ pBuf = sqlite3_malloc( nIn ? nIn : 1 );
|
|
if( pBuf==0 ){
|
|
sqlite3_result_error_nomem(ctx);
|
|
fclose(in);
|
|
@@ -2511,9 +2514,12 @@ static void writefileFunc(
|
|
}
|
|
|
|
if( argc>2 && res!=0 ){
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
if( S_ISLNK(mode) ){
|
|
ctxErrorMsg(context, "failed to create symlink: %s", zFile);
|
|
- }else if( S_ISDIR(mode) ){
|
|
+ }else
|
|
+#endif
|
|
+ if( S_ISDIR(mode) ){
|
|
ctxErrorMsg(context, "failed to create directory: %s", zFile);
|
|
}else{
|
|
ctxErrorMsg(context, "failed to write file: %s", zFile);
|
|
@@ -2536,9 +2542,12 @@ static void lsModeFunc(
|
|
int iMode = sqlite3_value_int(argv[0]);
|
|
char z[16];
|
|
(void)argc;
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
if( S_ISLNK(iMode) ){
|
|
z[0] = 'l';
|
|
- }else if( S_ISREG(iMode) ){
|
|
+ }else
|
|
+#endif
|
|
+ if( S_ISREG(iMode) ){
|
|
z[0] = '-';
|
|
}else if( S_ISDIR(iMode) ){
|
|
z[0] = 'd';
|
|
@@ -2696,8 +2705,8 @@ static int fsdirNext(sqlite3_vtab_cursor
|
|
FsdirLevel *pLvl;
|
|
if( iNew>=pCur->nLvl ){
|
|
int nNew = iNew+1;
|
|
- sqlite3_int64 nByte = nNew*sizeof(FsdirLevel);
|
|
- FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(pCur->aLvl, nByte);
|
|
+ size_t nByte = nNew*sizeof(FsdirLevel);
|
|
+ FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc(pCur->aLvl, nByte);
|
|
if( aNew==0 ) return SQLITE_NOMEM;
|
|
memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl));
|
|
pCur->aLvl = aNew;
|
|
@@ -2777,15 +2786,15 @@ static int fsdirColumn(
|
|
}else if( S_ISLNK(m) ){
|
|
char aStatic[64];
|
|
char *aBuf = aStatic;
|
|
- sqlite3_int64 nBuf = 64;
|
|
- int n;
|
|
+ size_t nBuf = 64;
|
|
+ size_t n;
|
|
|
|
while( 1 ){
|
|
n = readlink(pCur->zPath, aBuf, nBuf);
|
|
if( n<nBuf ) break;
|
|
if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
|
nBuf = nBuf*2;
|
|
- aBuf = sqlite3_malloc64(nBuf);
|
|
+ aBuf = sqlite3_malloc(nBuf);
|
|
if( aBuf==0 ){
|
|
sqlite3_result_error_nomem(ctx);
|
|
return SQLITE_NOMEM;
|
|
@@ -2958,7 +2967,7 @@ static int fsdirBestIndex(
|
|
** Register the "fsdir" virtual table.
|
|
*/
|
|
static int fsdirRegister(sqlite3 *db){
|
|
- static sqlite3_module fsdirModule = {
|
|
+ static const sqlite3_module fsdirModule = {
|
|
0, /* iVersion */
|
|
0, /* xCreate */
|
|
fsdirConnect, /* xConnect */
|
|
@@ -3018,6 +3027,24 @@ int sqlite3_fileio_init(
|
|
}
|
|
return rc;
|
|
}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
|
+ readfileFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
|
+ writefileFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
|
|
/************************* End ../ext/misc/fileio.c ********************/
|
|
/************************* Begin ../ext/misc/completion.c ******************/
|
|
@@ -3467,7 +3494,7 @@ static int completionBestIndex(
|
|
** This following structure defines all the methods for the
|
|
** completion virtual table.
|
|
*/
|
|
-static sqlite3_module completionModule = {
|
|
+static const sqlite3_module completionModule = {
|
|
0, /* iVersion */
|
|
0, /* xCreate */
|
|
completionConnect, /* xConnect */
|
|
@@ -3520,6 +3547,21 @@ int sqlite3_completion_init(
|
|
#endif
|
|
return rc;
|
|
}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)(pzErrMsg); /* Unused parameter */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3CompletionVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
|
|
/************************* End ../ext/misc/completion.c ********************/
|
|
/************************* Begin ../ext/misc/appendvfs.c ******************/
|
|
@@ -4242,8 +4284,13 @@ SQLITE_EXTENSION_INIT1
|
|
/* typedef sqlite3_int64 i64; */
|
|
/* typedef unsigned char u8; */
|
|
typedef unsigned short u16;
|
|
-typedef unsigned long u32;
|
|
-#define MIN(a,b) ((a)<(b) ? (a) : (b))
|
|
+typedef unsigned int u32;
|
|
+#ifndef MIN
|
|
+# define MIN(A,B) ((A)<(B)?(A):(B))
|
|
+#endif
|
|
+#ifndef MAX
|
|
+# define MAX(A,B) ((A)>(B)?(A):(B))
|
|
+#endif
|
|
|
|
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
|
# define ALWAYS(X) (1)
|
|
@@ -4533,8 +4580,8 @@ static int zipfileConnect(
|
|
sqlite3_vtab **ppVtab,
|
|
char **pzErr
|
|
){
|
|
- int nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE;
|
|
- int nFile = 0;
|
|
+ size_t nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE;
|
|
+ size_t nFile = 0;
|
|
const char *zFile = 0;
|
|
ZipfileTab *pNew = 0;
|
|
int rc;
|
|
@@ -4556,12 +4603,12 @@ static int zipfileConnect(
|
|
|
|
if( argc>3 ){
|
|
zFile = argv[3];
|
|
- nFile = (int)strlen(zFile)+1;
|
|
+ nFile = strlen(zFile)+1;
|
|
}
|
|
|
|
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
|
|
if( rc==SQLITE_OK ){
|
|
- pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
|
|
+ pNew = (ZipfileTab*)sqlite3_malloc(nByte+nFile);
|
|
if( pNew==0 ) return SQLITE_NOMEM;
|
|
memset(pNew, 0, nByte+nFile);
|
|
pNew->db = db;
|
|
@@ -5009,7 +5056,7 @@ static int zipfileGetEntry(
|
|
}
|
|
|
|
if( rc==SQLITE_OK ){
|
|
- sqlite3_int64 nAlloc;
|
|
+ size_t nAlloc;
|
|
ZipfileEntry *pNew;
|
|
|
|
int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]);
|
|
@@ -5021,7 +5068,7 @@ static int zipfileGetEntry(
|
|
nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]);
|
|
}
|
|
|
|
- pNew = (ZipfileEntry*)sqlite3_malloc64(nAlloc);
|
|
+ pNew = (ZipfileEntry*)sqlite3_malloc(nAlloc);
|
|
if( pNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -5180,15 +5227,15 @@ static void zipfileInflate(
|
|
** case.
|
|
*/
|
|
static int zipfileDeflate(
|
|
- const u8 *aIn, int nIn, /* Input */
|
|
- u8 **ppOut, int *pnOut, /* Output */
|
|
+ const u8 *aIn, size_t nIn, /* Input */
|
|
+ u8 **ppOut, size_t *pnOut, /* Output */
|
|
char **pzErr /* OUT: Error message */
|
|
){
|
|
- sqlite3_int64 nAlloc = compressBound(nIn);
|
|
+ size_t nAlloc = compressBound(nIn);
|
|
u8 *aOut;
|
|
int rc = SQLITE_OK;
|
|
|
|
- aOut = (u8*)sqlite3_malloc64(nAlloc);
|
|
+ aOut = (u8*)sqlite3_malloc(nAlloc);
|
|
if( aOut==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -5205,7 +5252,7 @@ static int zipfileDeflate(
|
|
|
|
if( res==Z_STREAM_END ){
|
|
*ppOut = aOut;
|
|
- *pnOut = (int)str.total_out;
|
|
+ *pnOut = str.total_out;
|
|
}else{
|
|
sqlite3_free(aOut);
|
|
*pzErr = sqlite3_mprintf("zipfile: deflate() error");
|
|
@@ -5253,15 +5300,15 @@ static int zipfileColumn(
|
|
if( sqlite3_vtab_nochange(ctx) ) break;
|
|
case 5: { /* data */
|
|
if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){
|
|
- int sz = pCDS->szCompressed;
|
|
- int szFinal = pCDS->szUncompressed;
|
|
+ size_t sz = pCDS->szCompressed;
|
|
+ size_t szFinal = pCDS->szUncompressed;
|
|
if( szFinal>0 ){
|
|
u8 *aBuf;
|
|
u8 *aFree = 0;
|
|
if( pCsr->pCurrent->aData ){
|
|
aBuf = pCsr->pCurrent->aData;
|
|
}else{
|
|
- aBuf = aFree = sqlite3_malloc64(sz);
|
|
+ aBuf = aFree = sqlite3_malloc(sz);
|
|
if( aBuf==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -5736,7 +5783,7 @@ static int zipfileUpdate(
|
|
const char *zPath = 0; /* Path for new entry */
|
|
int nPath = 0; /* strlen(zPath) */
|
|
const u8 *pData = 0; /* Pointer to buffer containing content */
|
|
- int nData = 0; /* Size of pData buffer in bytes */
|
|
+ size_t nData = 0; /* Size of pData buffer in bytes */
|
|
int iMethod = 0; /* Compression method for new entry */
|
|
u8 *pFree = 0; /* Free this */
|
|
char *zFree = 0; /* Also free this */
|
|
@@ -5788,7 +5835,7 @@ static int zipfileUpdate(
|
|
/* Value specified for "data", and possibly "method". This must be
|
|
** a regular file or a symlink. */
|
|
const u8 *aIn = sqlite3_value_blob(apVal[7]);
|
|
- int nIn = sqlite3_value_bytes(apVal[7]);
|
|
+ size_t nIn = sqlite3_value_bytes(apVal[7]);
|
|
int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL;
|
|
|
|
iMethod = sqlite3_value_int(apVal[8]);
|
|
@@ -5800,7 +5847,7 @@ static int zipfileUpdate(
|
|
rc = SQLITE_CONSTRAINT;
|
|
}else{
|
|
if( bAuto || iMethod ){
|
|
- int nCmp;
|
|
+ size_t nCmp;
|
|
rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg);
|
|
if( rc==SQLITE_OK ){
|
|
if( iMethod || nCmp<nIn ){
|
|
@@ -6086,8 +6133,8 @@ static int zipfileFindFunction(
|
|
typedef struct ZipfileBuffer ZipfileBuffer;
|
|
struct ZipfileBuffer {
|
|
u8 *a; /* Pointer to buffer */
|
|
- int n; /* Size of buffer in bytes */
|
|
- int nAlloc; /* Byte allocated at a[] */
|
|
+ size_t n; /* Size of buffer in bytes */
|
|
+ size_t nAlloc; /* Byte allocated at a[] */
|
|
};
|
|
|
|
typedef struct ZipfileCtx ZipfileCtx;
|
|
@@ -6097,14 +6144,14 @@ struct ZipfileCtx {
|
|
ZipfileBuffer cds;
|
|
};
|
|
|
|
-static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
|
|
+static int zipfileBufferGrow(ZipfileBuffer *pBuf, size_t nByte){
|
|
if( pBuf->n+nByte>pBuf->nAlloc ){
|
|
u8 *aNew;
|
|
- sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
|
|
- int nReq = pBuf->n + nByte;
|
|
+ size_t nNew = pBuf->n ? pBuf->n*2 : 512;
|
|
+ size_t nReq = pBuf->n + nByte;
|
|
|
|
while( nNew<nReq ) nNew = nNew*2;
|
|
- aNew = sqlite3_realloc64(pBuf->a, nNew);
|
|
+ aNew = sqlite3_realloc(pBuf->a, nNew);
|
|
if( aNew==0 ) return SQLITE_NOMEM;
|
|
pBuf->a = aNew;
|
|
pBuf->nAlloc = (int)nNew;
|
|
@@ -6138,7 +6185,7 @@ void zipfileStep(sqlite3_context *pCtx,
|
|
int iMethod = -1; /* Compression method to use (0 or 8) */
|
|
|
|
const u8 *aData = 0; /* Possibly compressed data for new entry */
|
|
- int nData = 0; /* Size of aData[] in bytes */
|
|
+ size_t nData = 0; /* Size of aData[] in bytes */
|
|
int szUncompressed = 0; /* Size of data before compression */
|
|
u8 *aFree = 0; /* Free this before returning */
|
|
u32 iCrc32 = 0; /* crc32 of uncompressed data */
|
|
@@ -6146,7 +6193,7 @@ void zipfileStep(sqlite3_context *pCtx,
|
|
char *zName = 0; /* Path (name) of new entry */
|
|
int nName = 0; /* Size of zName in bytes */
|
|
char *zFree = 0; /* Free this before returning */
|
|
- int nByte;
|
|
+ size_t nByte;
|
|
|
|
memset(&e, 0, sizeof(e));
|
|
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
|
@@ -6201,7 +6248,7 @@ void zipfileStep(sqlite3_context *pCtx,
|
|
szUncompressed = nData = sqlite3_value_bytes(pData);
|
|
iCrc32 = crc32(0, aData, nData);
|
|
if( iMethod<0 || iMethod==8 ){
|
|
- int nOut = 0;
|
|
+ size_t nOut = 0;
|
|
rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr);
|
|
if( rc!=SQLITE_OK ){
|
|
goto zipfile_step_out;
|
|
@@ -6298,7 +6345,7 @@ void zipfileStep(sqlite3_context *pCtx,
|
|
void zipfileFinal(sqlite3_context *pCtx){
|
|
ZipfileCtx *p;
|
|
ZipfileEOCD eocd;
|
|
- sqlite3_int64 nZip;
|
|
+ size_t nZip;
|
|
u8 *aZip;
|
|
|
|
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
|
@@ -6311,14 +6358,14 @@ void zipfileFinal(sqlite3_context *pCtx)
|
|
eocd.iOffset = p->body.n;
|
|
|
|
nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ;
|
|
- aZip = (u8*)sqlite3_malloc64(nZip);
|
|
+ aZip = (u8*)sqlite3_malloc(nZip);
|
|
if( aZip==0 ){
|
|
sqlite3_result_error_nomem(pCtx);
|
|
}else{
|
|
memcpy(aZip, p->body.a, p->body.n);
|
|
memcpy(&aZip[p->body.n], p->cds.a, p->cds.n);
|
|
zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]);
|
|
- sqlite3_result_blob(pCtx, aZip, (int)nZip, zipfileFree);
|
|
+ sqlite3_result_blob(pCtx, aZip, nZip, zipfileFree);
|
|
}
|
|
}
|
|
|
|
@@ -6331,7 +6378,7 @@ void zipfileFinal(sqlite3_context *pCtx)
|
|
** Register the "zipfile" virtual table.
|
|
*/
|
|
static int zipfileRegister(sqlite3 *db){
|
|
- static sqlite3_module zipfileModule = {
|
|
+ static const sqlite3_module zipfileModule = {
|
|
1, /* iVersion */
|
|
zipfileConnect, /* xCreate */
|
|
zipfileConnect, /* xConnect */
|
|
@@ -7297,7 +7344,7 @@ static int expertFilter(
|
|
}
|
|
|
|
static int idxRegisterVtab(sqlite3expert *p){
|
|
- static sqlite3_module expertModule = {
|
|
+ static const sqlite3_module expertModule = {
|
|
2, /* iVersion */
|
|
expertConnect, /* xCreate - create a table */
|
|
expertConnect, /* xConnect - connect to an existing table */
|
|
@@ -8810,7 +8857,7 @@ struct ShellState {
|
|
#define MODE_Pretty 11 /* Pretty-print schemas */
|
|
#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */
|
|
|
|
-static const char *modeDescr[] = {
|
|
+static const char *const modeDescr[] = {
|
|
"line",
|
|
"column",
|
|
"list",
|
|
@@ -8896,8 +8943,8 @@ static void editFunc(
|
|
int rc;
|
|
int hasCRNL = 0;
|
|
FILE *f = 0;
|
|
- sqlite3_int64 sz;
|
|
- sqlite3_int64 x;
|
|
+ size_t sz;
|
|
+ size_t x;
|
|
unsigned char *p = 0;
|
|
|
|
if( argc==2 ){
|
|
@@ -8968,7 +9015,7 @@ static void editFunc(
|
|
fseek(f, 0, SEEK_END);
|
|
sz = ftell(f);
|
|
rewind(f);
|
|
- p = sqlite3_malloc64( sz+(bBin==0) );
|
|
+ p = sqlite3_malloc( sz+(bBin==0) );
|
|
if( p==0 ){
|
|
sqlite3_result_error_nomem(context);
|
|
goto edit_func_end;
|
|
@@ -9320,7 +9367,7 @@ static int shellAuth(
|
|
const char *zA4
|
|
){
|
|
ShellState *p = (ShellState*)pClientData;
|
|
- static const char *azAction[] = { 0,
|
|
+ static const char *const azAction[] = { 0,
|
|
"CREATE_INDEX", "CREATE_TABLE", "CREATE_TEMP_INDEX",
|
|
"CREATE_TEMP_TABLE", "CREATE_TEMP_TRIGGER", "CREATE_TEMP_VIEW",
|
|
"CREATE_TRIGGER", "CREATE_VIEW", "DELETE",
|
|
@@ -9397,7 +9444,7 @@ static void eqp_append(ShellState *p, in
|
|
if( p->autoEQPtest ){
|
|
utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
|
|
}
|
|
- pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) + nText );
|
|
if( pNew==0 ) shell_out_of_memory();
|
|
pNew->iEqpId = iEqpId;
|
|
pNew->iParentId = p2;
|
|
@@ -10030,7 +10077,7 @@ static char *save_err_msg(
|
|
sqlite3 *db /* Database to query */
|
|
){
|
|
int nErrMsg = 1+strlen30(sqlite3_errmsg(db));
|
|
- char *zErrMsg = sqlite3_malloc64(nErrMsg);
|
|
+ char *zErrMsg = sqlite3_malloc(nErrMsg);
|
|
if( zErrMsg ){
|
|
memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
|
|
}
|
|
@@ -10284,7 +10331,7 @@ static void display_scanstats(
|
|
** is equal, according to strcmp(), to any of the strings in the array.
|
|
** Otherwise, return zero.
|
|
*/
|
|
-static int str_in_array(const char *zStr, const char **azArray){
|
|
+static int str_in_array(const char *zStr, const char *const *azArray){
|
|
int i;
|
|
for(i=0; azArray[i]; i++){
|
|
if( 0==strcmp(zStr, azArray[i]) ) return 1;
|
|
@@ -10317,10 +10364,10 @@ static void explain_data_prepare(ShellSt
|
|
int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
|
|
int iOp; /* Index of operation in p->aiIndent[] */
|
|
|
|
- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 };
|
|
- const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
|
|
+ const char *const azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 };
|
|
+ const char *const azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
|
|
"Rewind", 0 };
|
|
- const char *azGoto[] = { "Goto", 0 };
|
|
+ const char *const azGoto[] = { "Goto", 0 };
|
|
|
|
/* Try to figure out if this is really an EXPLAIN statement. If this
|
|
** cannot be verified, return early. */
|
|
@@ -10354,7 +10401,7 @@ static void explain_data_prepare(ShellSt
|
|
if( iOp==0 ){
|
|
/* Do further verfication that this is explain output. Abort if
|
|
** it is not */
|
|
- static const char *explainCols[] = {
|
|
+ static const char *const explainCols[] = {
|
|
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" };
|
|
int jj;
|
|
for(jj=0; jj<ArraySize(explainCols); jj++){
|
|
@@ -10366,9 +10413,9 @@ static void explain_data_prepare(ShellSt
|
|
}
|
|
}
|
|
nAlloc += 100;
|
|
- p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int));
|
|
+ p->aiIndent = (int*)sqlite3_realloc(p->aiIndent, nAlloc*sizeof(int));
|
|
if( p->aiIndent==0 ) shell_out_of_memory();
|
|
- abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
|
|
+ abYield = (int*)sqlite3_realloc(abYield, nAlloc*sizeof(int));
|
|
if( abYield==0 ) shell_out_of_memory();
|
|
}
|
|
abYield[iOp] = str_in_array(zOp, azYield);
|
|
@@ -10507,7 +10554,7 @@ static void exec_prepared_stmt(
|
|
if( SQLITE_ROW == rc ){
|
|
/* allocate space for col name ptr, value ptr, and type */
|
|
int nCol = sqlite3_column_count(pStmt);
|
|
- void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
|
|
+ void *pData = sqlite3_malloc(3*nCol*sizeof(const char*) + 1);
|
|
if( !pData ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -10742,7 +10789,7 @@ static int shell_exec(
|
|
}
|
|
|
|
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
|
|
- if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
|
|
+ if( pArg && pArg->autoEQP && sqlite3_strlike("EXPLAIN%",zStmtSql,0)!=0 ){
|
|
sqlite3_stmt *pExplain;
|
|
char *zEQP;
|
|
int triggerEQP = 0;
|
|
@@ -10791,10 +10838,13 @@ static int shell_exec(
|
|
if( pArg ){
|
|
pArg->cMode = pArg->mode;
|
|
if( pArg->autoExplain ){
|
|
- if( sqlite3_stmt_isexplain(pStmt)==1 ){
|
|
+ if( sqlite3_column_count(pStmt)==8
|
|
+ && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
|
|
+ ){
|
|
pArg->cMode = MODE_Explain;
|
|
}
|
|
- if( sqlite3_stmt_isexplain(pStmt)==2 ){
|
|
+ if( sqlite3_column_count(pStmt)==4
|
|
+ && sqlite3_strlike("EXPLAIN QUERY PLAN%", zStmtSql,0)==0 ){
|
|
pArg->cMode = MODE_EQP;
|
|
}
|
|
}
|
|
@@ -11172,7 +11222,7 @@ static const char *(azHelp[]) = {
|
|
" Options:",
|
|
" --preserve-rowids Include ROWID values in the output",
|
|
" --newlines Allow unescaped newline characters in output",
|
|
- " TABLE is LIKE pattern for the tables to dump",
|
|
+ " TABLE is a LIKE pattern for the tables to dump",
|
|
".echo on|off Turn command echo on or off",
|
|
".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
|
|
" Other Modes:",
|
|
@@ -11263,10 +11313,12 @@ static const char *(azHelp[]) = {
|
|
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
|
|
" Options:",
|
|
" --indent Try to pretty-print the schema",
|
|
+#ifdef SQLITE_DEBUG
|
|
".selftest ?OPTIONS? Run tests defined in the SELFTEST table",
|
|
" Options:",
|
|
" --init Create a new SELFTEST table",
|
|
" -v Verbose output",
|
|
+#endif
|
|
".separator COL ?ROW? Change the column and row separators",
|
|
#if defined(SQLITE_ENABLE_SESSION)
|
|
".session ?NAME? CMD ... Create or control sessions",
|
|
@@ -11413,16 +11465,16 @@ static int process_input(ShellState *p);
|
|
** 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){
|
|
+static char *readFile(const char *zName, size_t *pnByte){
|
|
FILE *in = fopen(zName, "rb");
|
|
- long nIn;
|
|
+ size_t nIn;
|
|
size_t nRead;
|
|
char *pBuf;
|
|
if( in==0 ) return 0;
|
|
fseek(in, 0, SEEK_END);
|
|
nIn = ftell(in);
|
|
rewind(in);
|
|
- pBuf = sqlite3_malloc64( nIn+1 );
|
|
+ pBuf = sqlite3_malloc( nIn+1 );
|
|
if( pBuf==0 ){ fclose(in); return 0; }
|
|
nRead = fread(pBuf, nIn, 1, in);
|
|
fclose(in);
|
|
@@ -11532,7 +11584,7 @@ int deduceDatabaseType(const char *zName
|
|
** program. Read content from the file in p->zDbFilename. If p->zDbFilename
|
|
** is 0, then read from standard input.
|
|
*/
|
|
-static unsigned char *readHexDb(ShellState *p, int *pnData){
|
|
+static unsigned char *readHexDb(ShellState *p, size_t *pnData){
|
|
unsigned char *a = 0;
|
|
int nLine;
|
|
int n = 0;
|
|
@@ -11541,7 +11593,7 @@ static unsigned char *readHexDb(ShellSta
|
|
int j, k;
|
|
int rc;
|
|
FILE *in;
|
|
- unsigned char x[16];
|
|
+ unsigned int x[16];
|
|
char zLine[1000];
|
|
if( p->zDbFilename ){
|
|
in = fopen(p->zDbFilename, "r");
|
|
@@ -11553,6 +11605,7 @@ static unsigned char *readHexDb(ShellSta
|
|
}else{
|
|
in = p->in;
|
|
nLine = p->lineno;
|
|
+ if( in==0 ) in = stdin;
|
|
}
|
|
*pnData = 0;
|
|
nLine++;
|
|
@@ -11579,14 +11632,14 @@ static unsigned char *readHexDb(ShellSta
|
|
if( strncmp(zLine, "| end ", 6)==0 ){
|
|
break;
|
|
}
|
|
- rc = sscanf(zLine,"| %d: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx"
|
|
- " %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx",
|
|
+ rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
|
|
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
|
|
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
|
|
if( rc==17 ){
|
|
k = iOffset+j;
|
|
if( k+16<=n ){
|
|
- memcpy(a+k, x, 16);
|
|
+ int ii;
|
|
+ for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
|
|
}
|
|
}
|
|
}
|
|
@@ -11708,7 +11761,7 @@ static void open_db(ShellState *p, int o
|
|
else
|
|
if( p->openMode==SHELL_OPEN_DESERIALIZE || p->openMode==SHELL_OPEN_HEXDB ){
|
|
int rc;
|
|
- int nData = 0;
|
|
+ size_t nData = 0;
|
|
unsigned char *aData;
|
|
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
|
|
aData = (unsigned char*)readFile(p->zDbFilename, &nData);
|
|
@@ -12020,7 +12073,7 @@ struct ImportCtx {
|
|
static void import_append_char(ImportCtx *p, int c){
|
|
if( p->n+1>=p->nAlloc ){
|
|
p->nAlloc += p->nAlloc + 100;
|
|
- p->z = sqlite3_realloc64(p->z, p->nAlloc);
|
|
+ p->z = sqlite3_realloc(p->z, p->nAlloc);
|
|
if( p->z==0 ) shell_out_of_memory();
|
|
}
|
|
p->z[p->n++] = (char)c;
|
|
@@ -12039,7 +12092,7 @@ static void import_append_char(ImportCtx
|
|
** EOF on end-of-file.
|
|
** + Report syntax errors on stderr
|
|
*/
|
|
-static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
|
|
+static const char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
|
|
int c;
|
|
int cSep = p->cColSep;
|
|
int rSep = p->cRowSep;
|
|
@@ -12129,7 +12182,7 @@ static char *SQLITE_CDECL csv_read_one_f
|
|
** EOF on end-of-file.
|
|
** + Report syntax errors on stderr
|
|
*/
|
|
-static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
|
|
+static const char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
|
|
int c;
|
|
int cSep = p->cColSep;
|
|
int rSep = p->cRowSep;
|
|
@@ -12181,7 +12234,7 @@ static void tryToCloneData(
|
|
goto end_data_xfer;
|
|
}
|
|
n = sqlite3_column_count(pQuery);
|
|
- zInsert = sqlite3_malloc64(200 + nTable + n*3);
|
|
+ zInsert = sqlite3_malloc(200 + nTable + n*3);
|
|
if( zInsert==0 ) shell_out_of_memory();
|
|
sqlite3_snprintf(200+nTable,zInsert,
|
|
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
|
|
@@ -12947,9 +13000,6 @@ static int lintDotCommand(
|
|
}
|
|
|
|
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
|
-/*********************************************************************************
|
|
-** The ".archive" or ".ar" command.
|
|
-*/
|
|
static void shellPrepare(
|
|
sqlite3 *db,
|
|
int *pRc,
|
|
@@ -13020,6 +13070,10 @@ static void shellReset(
|
|
*pRc = rc;
|
|
}
|
|
}
|
|
+
|
|
+/*********************************************************************************
|
|
+** The ".archive" or ".ar" command.
|
|
+*/
|
|
/*
|
|
** Structure representing a single ".ar" command.
|
|
*/
|
|
@@ -13918,7 +13972,7 @@ static int do_meta_command(char *zLine,
|
|
if( nArg!=2 ){
|
|
raw_printf(stderr, "Usage: .check GLOB-PATTERN\n");
|
|
rc = 2;
|
|
- }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
|
|
+ }else if( (zRes = readFile("testcase-out.txt", NULL))==0 ){
|
|
raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n");
|
|
rc = 2;
|
|
}else if( testcase_glob(azArg[1],zRes)==0 ){
|
|
@@ -14033,7 +14087,9 @@ static int do_meta_command(char *zLine,
|
|
zLike = azArg[i];
|
|
}
|
|
}
|
|
+
|
|
open_db(p, 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. */
|
|
@@ -14081,7 +14137,7 @@ static int do_meta_command(char *zLine,
|
|
}
|
|
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
|
|
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
|
|
- raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
|
|
+ raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");
|
|
p->showHeader = savedShowHeader;
|
|
p->shellFlgs = savedShellFlags;
|
|
}else
|
|
@@ -14245,7 +14301,7 @@ static int do_meta_command(char *zLine,
|
|
int nSep; /* Number of bytes in p->colSeparator[] */
|
|
char *zSql; /* An SQL statement */
|
|
ImportCtx sCtx; /* Reader context */
|
|
- char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
|
|
+ const char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
|
|
int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close file */
|
|
|
|
if( nArg!=3 ){
|
|
@@ -14358,7 +14414,7 @@ static int do_meta_command(char *zLine,
|
|
sqlite3_finalize(pStmt);
|
|
pStmt = 0;
|
|
if( nCol==0 ) return 0; /* no columns, no error */
|
|
- zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 );
|
|
+ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 );
|
|
if( zSql==0 ){
|
|
xCloser(sCtx.in);
|
|
shell_out_of_memory();
|
|
@@ -14384,7 +14440,7 @@ static int do_meta_command(char *zLine,
|
|
do{
|
|
int startLine = sCtx.nLine;
|
|
for(i=0; i<nCol; i++){
|
|
- char *z = xRead(&sCtx);
|
|
+ const char *z = xRead(&sCtx);
|
|
/*
|
|
** Did we reach end-of-file before finding any columns?
|
|
** If so, stop instead of NULL filling the remaining columns.
|
|
@@ -15688,7 +15744,7 @@ static int do_meta_command(char *zLine,
|
|
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
|
|
|
|
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
|
|
- static const char *azBool[] = { "off", "on", "trigger", "full"};
|
|
+ static const char *const azBool[] = { "off", "on", "trigger", "full"};
|
|
int i;
|
|
if( nArg!=1 ){
|
|
raw_printf(stderr, "Usage: .show\n");
|
|
@@ -15801,7 +15857,7 @@ static int do_meta_command(char *zLine,
|
|
if( nRow>=nAlloc ){
|
|
char **azNew;
|
|
int n2 = nAlloc*2 + 10;
|
|
- azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
|
|
+ azNew = sqlite3_realloc(azResult, sizeof(azResult[0])*n2);
|
|
if( azNew==0 ) shell_out_of_memory();
|
|
nAlloc = n2;
|
|
azResult = azNew;
|
|
@@ -16575,7 +16631,9 @@ static const char zOptions[] =
|
|
" -interactive force interactive I/O\n"
|
|
" -line set output mode to 'line'\n"
|
|
" -list set output mode to 'list'\n"
|
|
+#ifndef SQLITE_OMIT_LOOKASIDE
|
|
" -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
|
|
+#endif
|
|
#if defined(SQLITE_ENABLE_DESERIALIZE)
|
|
" -maxsize N maximum size for a --deserialize database\n"
|
|
#endif
|
|
@@ -16589,6 +16647,7 @@ static const char zOptions[] =
|
|
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
|
|
" -quote set output mode to 'quote'\n"
|
|
" -readonly open the database read-only\n"
|
|
+ " -scanstats print scan stats before each finalize\n"
|
|
" -separator SEP set output column separator. Default: '|'\n"
|
|
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
" -sorterref SIZE sorter references threshold size\n"
|
|
@@ -16596,6 +16655,9 @@ static const char zOptions[] =
|
|
" -stats print memory stats before each finalize\n"
|
|
" -version show SQLite version\n"
|
|
" -vfs NAME use NAME as the default VFS\n"
|
|
+#ifdef SQLITE_USE_FCNTL_TRACE
|
|
+ " -vfslog enable the vfslog extension\n"
|
|
+#endif
|
|
#ifdef SQLITE_ENABLE_VFSTRACE
|
|
" -vfstrace enable tracing of all VFS calls\n"
|
|
#endif
|
|
@@ -16639,6 +16701,8 @@ static void main_init(ShellState *data)
|
|
data->showHeader = 0;
|
|
data->shellFlgs = SHFLG_Lookaside;
|
|
verify_uninitialized();
|
|
+ sqlite3_config(64); /* SQLITE_CONFIG_EXPLAIN_COMMENTS */
|
|
+ sqlite3_config(65); /* SQLITE_CONFIG_ENABLE_UNKOWN_SQL_FUNCTION */
|
|
sqlite3_config(SQLITE_CONFIG_URI, 1);
|
|
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
|
|
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
|
@@ -16679,8 +16743,12 @@ static char *cmdline_option_value(int ar
|
|
return argv[i];
|
|
}
|
|
|
|
+#if defined(_WIN32) && defined(__MSVCRT__) && !defined(main)
|
|
+int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
|
|
+#endif
|
|
+
|
|
#ifndef SQLITE_SHELL_IS_UTF8
|
|
-# if (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER)
|
|
+# if (defined(_WIN32) || defined(WIN32)) && (defined(_MSC_VER) || defined(__MSVCRT__))
|
|
# define SQLITE_SHELL_IS_UTF8 (0)
|
|
# else
|
|
# define SQLITE_SHELL_IS_UTF8 (1)
|
|
@@ -16838,6 +16906,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t
|
|
#else
|
|
(void)cmdline_option_value(argc, argv, ++i);
|
|
#endif
|
|
+#ifdef SQLITE_USE_FCNTL_TRACE
|
|
+ }else if( strcmp(z,"-vfslog")==0 ){
|
|
+ extern int sqlite3_register_vfslog(const char *);
|
|
+ sqlite3_register_vfslog(NULL);
|
|
+#endif
|
|
}else if( strcmp(z,"-pagecache")==0 ){
|
|
int n, sz;
|
|
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
|
|
@@ -17059,6 +17132,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t
|
|
#endif
|
|
}else if( strcmp(z,"-vfs")==0 ){
|
|
i++;
|
|
+#ifdef SQLITE_USE_FCNTL_TRACE
|
|
+ }else if( strcmp(z,"-vfslog")==0 ){
|
|
+ i++;
|
|
+#endif
|
|
#ifdef SQLITE_ENABLE_VFSTRACE
|
|
}else if( strcmp(z,"-vfstrace")==0 ){
|
|
i++;
|
|
--- origsrc/sqlite-autoconf-3280000/showauth.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/showauth.c 2019-05-14 16:22:31.267380600 +0200
|
|
@@ -0,0 +1,116 @@
|
|
+/*
|
|
+** 2014-09-21
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This SQLite extension adds a debug "authorizer" callback to the database
|
|
+** connection. The callback merely writes the authorization request to
|
|
+** standard output and returns SQLITE_OK.
|
|
+**
|
|
+** This extension can be used (for example) in the command-line shell to
|
|
+** trace the operation of the authorizer.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdio.h>
|
|
+
|
|
+/*
|
|
+** Display the authorization request
|
|
+*/
|
|
+static int authCallback(
|
|
+ void *pClientData,
|
|
+ int op,
|
|
+ const char *z1,
|
|
+ const char *z2,
|
|
+ const char *z3,
|
|
+ const char *z4
|
|
+){
|
|
+ const char *zOp;
|
|
+ char zOpSpace[50];
|
|
+ switch( op ){
|
|
+ case SQLITE_CREATE_INDEX: zOp = "CREATE_INDEX"; break;
|
|
+ case SQLITE_CREATE_TABLE: zOp = "CREATE_TABLE"; break;
|
|
+ case SQLITE_CREATE_TEMP_INDEX: zOp = "CREATE_TEMP_INDEX"; break;
|
|
+ case SQLITE_CREATE_TEMP_TABLE: zOp = "CREATE_TEMP_TABLE"; break;
|
|
+ case SQLITE_CREATE_TEMP_TRIGGER: zOp = "CREATE_TEMP_TRIGGER"; break;
|
|
+ case SQLITE_CREATE_TEMP_VIEW: zOp = "CREATE_TEMP_VIEW"; break;
|
|
+ case SQLITE_CREATE_TRIGGER: zOp = "CREATE_TRIGGER"; break;
|
|
+ case SQLITE_CREATE_VIEW: zOp = "CREATE_VIEW"; break;
|
|
+ case SQLITE_DELETE: zOp = "DELETE"; break;
|
|
+ case SQLITE_DROP_INDEX: zOp = "DROP_INDEX"; break;
|
|
+ case SQLITE_DROP_TABLE: zOp = "DROP_TABLE"; break;
|
|
+ case SQLITE_DROP_TEMP_INDEX: zOp = "DROP_TEMP_INDEX"; break;
|
|
+ case SQLITE_DROP_TEMP_TABLE: zOp = "DROP_TEMP_TABLE"; break;
|
|
+ case SQLITE_DROP_TEMP_TRIGGER: zOp = "DROP_TEMP_TRIGGER"; break;
|
|
+ case SQLITE_DROP_TEMP_VIEW: zOp = "DROP_TEMP_VIEW"; break;
|
|
+ case SQLITE_DROP_TRIGGER: zOp = "DROP_TRIGGER"; break;
|
|
+ case SQLITE_DROP_VIEW: zOp = "DROP_VIEW"; break;
|
|
+ case SQLITE_INSERT: zOp = "INSERT"; break;
|
|
+ case SQLITE_PRAGMA: zOp = "PRAGMA"; break;
|
|
+ case SQLITE_READ: zOp = "READ"; break;
|
|
+ case SQLITE_SELECT: zOp = "SELECT"; break;
|
|
+ case SQLITE_TRANSACTION: zOp = "TRANSACTION"; break;
|
|
+ case SQLITE_UPDATE: zOp = "UPDATE"; break;
|
|
+ case SQLITE_ATTACH: zOp = "ATTACH"; break;
|
|
+ case SQLITE_DETACH: zOp = "DETACH"; break;
|
|
+ case SQLITE_ALTER_TABLE: zOp = "ALTER_TABLE"; break;
|
|
+ case SQLITE_REINDEX: zOp = "REINDEX"; break;
|
|
+ case SQLITE_ANALYZE: zOp = "ANALYZE"; break;
|
|
+ case SQLITE_CREATE_VTABLE: zOp = "CREATE_VTABLE"; break;
|
|
+ case SQLITE_DROP_VTABLE: zOp = "DROP_VTABLE"; break;
|
|
+ case SQLITE_FUNCTION: zOp = "FUNCTION"; break;
|
|
+ case SQLITE_SAVEPOINT: zOp = "SAVEPOINT"; break;
|
|
+ case SQLITE_COPY: zOp = "COPY"; break;
|
|
+ case SQLITE_RECURSIVE: zOp = "RECURSIVE"; break;
|
|
+
|
|
+
|
|
+ default: {
|
|
+ sqlite3_snprintf(sizeof(zOpSpace), zOpSpace, "%d", op);
|
|
+ zOp = zOpSpace;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( z1==0 ) z1 = "NULL";
|
|
+ if( z2==0 ) z2 = "NULL";
|
|
+ if( z3==0 ) z3 = "NULL";
|
|
+ if( z4==0 ) z4 = "NULL";
|
|
+ printf("AUTH: %s,%s,%s,%s,%s\n", zOp, z1, z2, z3, z4);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_showauth_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_set_authorizer(db, authCallback, 0);
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_set_authorizer(db, authCallback, 0);
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/spaceanal.tcl 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/spaceanal.tcl 2019-03-05 13:14:40.824498900 +0100
|
|
@@ -0,0 +1,896 @@
|
|
+#! /bin/sh
|
|
+# restart with tclsh \
|
|
+exec tclsh "$0" ${1+"$@"}
|
|
+package require sqlite3
|
|
+
|
|
+# Run this TCL script using an SQLite-enabled TCL interpreter to get a report
|
|
+# on how much disk space is used by a particular data to actually store data
|
|
+# versus how much space is unused.
|
|
+#
|
|
+# The dbstat virtual table is required.
|
|
+#
|
|
+
|
|
+if {[catch {
|
|
+
|
|
+# Argument $tname is the name of a table within the database opened by
|
|
+# database handle [db]. Return true if it is a WITHOUT ROWID table, or
|
|
+# false otherwise.
|
|
+#
|
|
+proc is_without_rowid {tname} {
|
|
+ set t [string map {' ''} $tname]
|
|
+ db eval "PRAGMA index_list = '$t'" o {
|
|
+ if {$o(origin) == "pk"} {
|
|
+ set n $o(name)
|
|
+ if {0==[db one { SELECT count(*) FROM sqlite_master WHERE name=$n }]} {
|
|
+ return 1
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return 0
|
|
+}
|
|
+
|
|
+# Read and run TCL commands from standard input. Used to implement
|
|
+# the --tclsh option.
|
|
+#
|
|
+proc tclsh {} {
|
|
+ set line {}
|
|
+ while {![eof stdin]} {
|
|
+ if {$line!=""} {
|
|
+ puts -nonewline "> "
|
|
+ } else {
|
|
+ puts -nonewline "% "
|
|
+ }
|
|
+ flush stdout
|
|
+ append line [gets stdin]
|
|
+ if {[info complete $line]} {
|
|
+ if {[catch {uplevel #0 $line} result]} {
|
|
+ puts stderr "Error: $result"
|
|
+ } elseif {$result!=""} {
|
|
+ puts $result
|
|
+ }
|
|
+ set line {}
|
|
+ } else {
|
|
+ append line \n
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+# Get the name of the database to analyze
|
|
+#
|
|
+proc usage {} {
|
|
+ set argv0 [file rootname [file tail [info script]]]
|
|
+ puts stderr "Usage: $argv0 ?--pageinfo? ?--stats? database-filename"
|
|
+ puts stderr {
|
|
+Analyze the SQLite3 database file specified by the "database-filename"
|
|
+argument and output a report detailing size and storage efficiency
|
|
+information for the database and its constituent tables and indexes.
|
|
+
|
|
+Options:
|
|
+
|
|
+ --pageinfo Show how each page of the database-file is used
|
|
+
|
|
+ --stats Output SQL text that creates a new database containing
|
|
+ statistics about the database that was analyzed
|
|
+
|
|
+ --tclsh Run the built-in TCL interpreter interactively (for debugging)
|
|
+
|
|
+ --version Show the version number of SQLite
|
|
+}
|
|
+ exit 1
|
|
+}
|
|
+set file_to_analyze {}
|
|
+set flags(-pageinfo) 0
|
|
+set flags(-stats) 0
|
|
+set flags(-debug) 0
|
|
+append argv {}
|
|
+foreach arg $argv {
|
|
+ if {[regexp {^-+pageinfo$} $arg]} {
|
|
+ set flags(-pageinfo) 1
|
|
+ } elseif {[regexp {^-+stats$} $arg]} {
|
|
+ set flags(-stats) 1
|
|
+ } elseif {[regexp {^-+debug$} $arg]} {
|
|
+ set flags(-debug) 1
|
|
+ } elseif {[regexp {^-+tclsh$} $arg]} {
|
|
+ tclsh
|
|
+ exit 0
|
|
+ } elseif {[regexp {^-+version$} $arg]} {
|
|
+ sqlite3 mem :memory:
|
|
+ puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
|
|
+ mem close
|
|
+ exit 0
|
|
+ } elseif {[regexp {^-} $arg]} {
|
|
+ puts stderr "Unknown option: $arg"
|
|
+ usage
|
|
+ } elseif {$file_to_analyze!=""} {
|
|
+ usage
|
|
+ } else {
|
|
+ set file_to_analyze $arg
|
|
+ }
|
|
+}
|
|
+if {$file_to_analyze==""} usage
|
|
+set root_filename $file_to_analyze
|
|
+regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
|
|
+if {![file exists $root_filename]} {
|
|
+ puts stderr "No such file: $root_filename"
|
|
+ exit 1
|
|
+}
|
|
+if {![file readable $root_filename]} {
|
|
+ puts stderr "File is not readable: $root_filename"
|
|
+ exit 1
|
|
+}
|
|
+set true_file_size [file size $root_filename]
|
|
+if {$true_file_size<512} {
|
|
+ puts stderr "Empty or malformed database: $root_filename"
|
|
+ exit 1
|
|
+}
|
|
+
|
|
+# Compute the total file size assuming test_multiplexor is being used.
|
|
+# Assume that SQLITE_ENABLE_8_3_NAMES might be enabled
|
|
+#
|
|
+set extension [file extension $root_filename]
|
|
+set pattern $root_filename
|
|
+append pattern {[0-3][0-9][0-9]}
|
|
+foreach f [glob -nocomplain $pattern] {
|
|
+ incr true_file_size [file size $f]
|
|
+ set extension {}
|
|
+}
|
|
+if {[string length $extension]>=2 && [string length $extension]<=4} {
|
|
+ set pattern [file rootname $root_filename]
|
|
+ append pattern {.[0-3][0-9][0-9]}
|
|
+ foreach f [glob -nocomplain $pattern] {
|
|
+ incr true_file_size [file size $f]
|
|
+ }
|
|
+}
|
|
+
|
|
+# Open the database
|
|
+#
|
|
+if {[catch {sqlite3 db $file_to_analyze -uri 1} msg]} {
|
|
+ puts stderr "error trying to open $file_to_analyze: $msg"
|
|
+ exit 1
|
|
+}
|
|
+if {$flags(-debug)} {
|
|
+ proc dbtrace {txt} {puts $txt; flush stdout;}
|
|
+ db trace ::dbtrace
|
|
+}
|
|
+
|
|
+# Make sure all required compile-time options are available
|
|
+#
|
|
+if {![db exists {SELECT 1 FROM pragma_compile_options
|
|
+ WHERE compile_options='ENABLE_DBSTAT_VTAB'}]} {
|
|
+ puts "The SQLite database engine linked with this application\
|
|
+ lacks required capabilities. Recompile using the\
|
|
+ -DSQLITE_ENABLE_DBSTAT_VTAB compile-time option to fix\
|
|
+ this problem."
|
|
+ exit 1
|
|
+}
|
|
+
|
|
+db eval {SELECT count(*) FROM sqlite_master}
|
|
+set pageSize [expr {wide([db one {PRAGMA page_size}])}]
|
|
+
|
|
+if {$flags(-pageinfo)} {
|
|
+ db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
|
+ db eval {SELECT name, path, pageno FROM temp.stat ORDER BY pageno} {
|
|
+ puts "$pageno $name $path"
|
|
+ }
|
|
+ exit 0
|
|
+}
|
|
+if {$flags(-stats)} {
|
|
+ db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
|
+ puts "BEGIN;"
|
|
+ puts "CREATE TABLE stats("
|
|
+ puts " name STRING, /* Name of table or index */"
|
|
+ puts " path INTEGER, /* Path to page from root */"
|
|
+ puts " pageno INTEGER, /* Page number */"
|
|
+ puts " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */"
|
|
+ puts " ncell INTEGER, /* Cells on page (0 for overflow) */"
|
|
+ puts " payload INTEGER, /* Bytes of payload on this page */"
|
|
+ puts " unused INTEGER, /* Bytes of unused space on this page */"
|
|
+ puts " mx_payload INTEGER, /* Largest payload size of all cells */"
|
|
+ puts " pgoffset INTEGER, /* Offset of page in file */"
|
|
+ puts " pgsize INTEGER /* Size of the page */"
|
|
+ puts ");"
|
|
+ db eval {SELECT quote(name) || ',' ||
|
|
+ quote(path) || ',' ||
|
|
+ quote(pageno) || ',' ||
|
|
+ quote(pagetype) || ',' ||
|
|
+ quote(ncell) || ',' ||
|
|
+ quote(payload) || ',' ||
|
|
+ quote(unused) || ',' ||
|
|
+ quote(mx_payload) || ',' ||
|
|
+ quote(pgoffset) || ',' ||
|
|
+ quote(pgsize) AS x FROM stat} {
|
|
+ puts "INSERT INTO stats VALUES($x);"
|
|
+ }
|
|
+ puts "COMMIT;"
|
|
+ exit 0
|
|
+}
|
|
+
|
|
+
|
|
+# In-memory database for collecting statistics. This script loops through
|
|
+# the tables and indices in the database being analyzed, adding a row for each
|
|
+# to an in-memory database (for which the schema is shown below). It then
|
|
+# queries the in-memory db to produce the space-analysis report.
|
|
+#
|
|
+sqlite3 mem :memory:
|
|
+if {$flags(-debug)} {
|
|
+ proc dbtrace {txt} {puts $txt; flush stdout;}
|
|
+ mem trace ::dbtrace
|
|
+}
|
|
+set tabledef {CREATE TABLE space_used(
|
|
+ name clob, -- Name of a table or index in the database file
|
|
+ tblname clob, -- Name of associated table
|
|
+ is_index boolean, -- TRUE if it is an index, false for a table
|
|
+ is_without_rowid boolean, -- TRUE if WITHOUT ROWID table
|
|
+ nentry int, -- Number of entries in the BTree
|
|
+ leaf_entries int, -- Number of leaf entries
|
|
+ depth int, -- Depth of the b-tree
|
|
+ payload int, -- Total amount of data stored in this table or index
|
|
+ ovfl_payload int, -- Total amount of data stored on overflow pages
|
|
+ ovfl_cnt int, -- Number of entries that use overflow
|
|
+ mx_payload int, -- Maximum payload size
|
|
+ int_pages int, -- Number of interior pages used
|
|
+ leaf_pages int, -- Number of leaf pages used
|
|
+ ovfl_pages int, -- Number of overflow pages used
|
|
+ int_unused int, -- Number of unused bytes on interior pages
|
|
+ leaf_unused int, -- Number of unused bytes on primary pages
|
|
+ ovfl_unused int, -- Number of unused bytes on overflow pages
|
|
+ gap_cnt int, -- Number of gaps in the page layout
|
|
+ compressed_size int -- Total bytes stored on disk
|
|
+);}
|
|
+mem eval $tabledef
|
|
+
|
|
+# Create a temporary "dbstat" virtual table.
|
|
+#
|
|
+db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
|
+db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat
|
|
+ ORDER BY name, path}
|
|
+db eval {DROP TABLE temp.stat}
|
|
+
|
|
+set isCompressed 0
|
|
+set compressOverhead 0
|
|
+set depth 0
|
|
+set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
|
|
+foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
|
+
|
|
+ set is_index [expr {$name!=$tblname}]
|
|
+ set is_without_rowid [is_without_rowid $name]
|
|
+ db eval {
|
|
+ SELECT
|
|
+ sum(ncell) AS nentry,
|
|
+ sum((pagetype=='leaf')*ncell) AS leaf_entries,
|
|
+ sum(payload) AS payload,
|
|
+ sum((pagetype=='overflow') * payload) AS ovfl_payload,
|
|
+ sum(path LIKE '%+000000') AS ovfl_cnt,
|
|
+ max(mx_payload) AS mx_payload,
|
|
+ sum(pagetype=='internal') AS int_pages,
|
|
+ sum(pagetype=='leaf') AS leaf_pages,
|
|
+ sum(pagetype=='overflow') AS ovfl_pages,
|
|
+ sum((pagetype=='internal') * unused) AS int_unused,
|
|
+ sum((pagetype=='leaf') * unused) AS leaf_unused,
|
|
+ sum((pagetype=='overflow') * unused) AS ovfl_unused,
|
|
+ sum(pgsize) AS compressed_size,
|
|
+ max((length(CASE WHEN path LIKE '%+%' THEN '' ELSE path END)+3)/4)
|
|
+ AS depth
|
|
+ FROM temp.dbstat WHERE name = $name
|
|
+ } break
|
|
+
|
|
+ set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
|
|
+ set storage [expr {$total_pages*$pageSize}]
|
|
+ if {!$isCompressed && $storage>$compressed_size} {
|
|
+ set isCompressed 1
|
|
+ set compressOverhead 14
|
|
+ }
|
|
+
|
|
+ # Column 'gap_cnt' is set to the number of non-contiguous entries in the
|
|
+ # list of pages visited if the b-tree structure is traversed in a top-down
|
|
+ # fashion (each node visited before its child-tree is passed). Any overflow
|
|
+ # chains present are traversed from start to finish before any child-tree
|
|
+ # is.
|
|
+ #
|
|
+ set gap_cnt 0
|
|
+ set prev 0
|
|
+ db eval {
|
|
+ SELECT pageno, pagetype FROM temp.dbstat
|
|
+ WHERE name=$name
|
|
+ ORDER BY pageno
|
|
+ } {
|
|
+ if {$prev>0 && $pagetype=="leaf" && $pageno!=$prev+1} {
|
|
+ incr gap_cnt
|
|
+ }
|
|
+ set prev $pageno
|
|
+ }
|
|
+ mem eval {
|
|
+ INSERT INTO space_used VALUES(
|
|
+ $name,
|
|
+ $tblname,
|
|
+ $is_index,
|
|
+ $is_without_rowid,
|
|
+ $nentry,
|
|
+ $leaf_entries,
|
|
+ $depth,
|
|
+ $payload,
|
|
+ $ovfl_payload,
|
|
+ $ovfl_cnt,
|
|
+ $mx_payload,
|
|
+ $int_pages,
|
|
+ $leaf_pages,
|
|
+ $ovfl_pages,
|
|
+ $int_unused,
|
|
+ $leaf_unused,
|
|
+ $ovfl_unused,
|
|
+ $gap_cnt,
|
|
+ $compressed_size
|
|
+ );
|
|
+ }
|
|
+}
|
|
+
|
|
+proc integerify {real} {
|
|
+ if {[string is double -strict $real]} {
|
|
+ return [expr {wide($real)}]
|
|
+ } else {
|
|
+ return 0
|
|
+ }
|
|
+}
|
|
+mem function int integerify
|
|
+
|
|
+# Quote a string for use in an SQL query. Examples:
|
|
+#
|
|
+# [quote {hello world}] == {'hello world'}
|
|
+# [quote {hello world's}] == {'hello world''s'}
|
|
+#
|
|
+proc quote {txt} {
|
|
+ return [string map {' ''} $txt]
|
|
+}
|
|
+
|
|
+# Output a title line
|
|
+#
|
|
+proc titleline {title} {
|
|
+ if {$title==""} {
|
|
+ puts [string repeat * 79]
|
|
+ } else {
|
|
+ set len [string length $title]
|
|
+ set stars [string repeat * [expr 79-$len-5]]
|
|
+ puts "*** $title $stars"
|
|
+ }
|
|
+}
|
|
+
|
|
+# Generate a single line of output in the statistics section of the
|
|
+# report.
|
|
+#
|
|
+proc statline {title value {extra {}}} {
|
|
+ set len [string length $title]
|
|
+ set dots [string repeat . [expr 50-$len]]
|
|
+ set len [string length $value]
|
|
+ set sp2 [string range { } $len end]
|
|
+ if {$extra ne ""} {
|
|
+ set extra " $extra"
|
|
+ }
|
|
+ puts "$title$dots $value$sp2$extra"
|
|
+}
|
|
+
|
|
+# Generate a formatted percentage value for $num/$denom
|
|
+#
|
|
+proc percent {num denom {of {}}} {
|
|
+ if {$denom==0.0} {return ""}
|
|
+ set v [expr {$num*100.0/$denom}]
|
|
+ set of {}
|
|
+ if {$v==100.0 || $v<0.001 || ($v>1.0 && $v<99.0)} {
|
|
+ return [format {%5.1f%% %s} $v $of]
|
|
+ } elseif {$v<0.1 || $v>99.9} {
|
|
+ return [format {%7.3f%% %s} $v $of]
|
|
+ } else {
|
|
+ return [format {%6.2f%% %s} $v $of]
|
|
+ }
|
|
+}
|
|
+
|
|
+proc divide {num denom} {
|
|
+ if {$denom==0} {return 0.0}
|
|
+ return [format %.2f [expr double($num)/double($denom)]]
|
|
+}
|
|
+
|
|
+# Generate a subreport that covers some subset of the database.
|
|
+# the $where clause determines which subset to analyze.
|
|
+#
|
|
+proc subreport {title where showFrag} {
|
|
+ global pageSize file_pgcnt compressOverhead
|
|
+
|
|
+ # Query the in-memory database for the sum of various statistics
|
|
+ # for the subset of tables/indices identified by the WHERE clause in
|
|
+ # $where. Note that even if the WHERE clause matches no rows, the
|
|
+ # following query returns exactly one row (because it is an aggregate).
|
|
+ #
|
|
+ # The results of the query are stored directly by SQLite into local
|
|
+ # variables (i.e. $nentry, $payload etc.).
|
|
+ #
|
|
+ mem eval "
|
|
+ SELECT
|
|
+ int(sum(
|
|
+ CASE WHEN (is_without_rowid OR is_index) THEN nentry
|
|
+ ELSE leaf_entries
|
|
+ END
|
|
+ )) AS nentry,
|
|
+ int(sum(payload)) AS payload,
|
|
+ int(sum(ovfl_payload)) AS ovfl_payload,
|
|
+ max(mx_payload) AS mx_payload,
|
|
+ int(sum(ovfl_cnt)) as ovfl_cnt,
|
|
+ int(sum(leaf_pages)) AS leaf_pages,
|
|
+ int(sum(int_pages)) AS int_pages,
|
|
+ int(sum(ovfl_pages)) AS ovfl_pages,
|
|
+ int(sum(leaf_unused)) AS leaf_unused,
|
|
+ int(sum(int_unused)) AS int_unused,
|
|
+ int(sum(ovfl_unused)) AS ovfl_unused,
|
|
+ int(sum(gap_cnt)) AS gap_cnt,
|
|
+ int(sum(compressed_size)) AS compressed_size,
|
|
+ int(max(depth)) AS depth,
|
|
+ count(*) AS cnt
|
|
+ FROM space_used WHERE $where" {} {}
|
|
+
|
|
+ # Output the sub-report title, nicely decorated with * characters.
|
|
+ #
|
|
+ puts ""
|
|
+ titleline $title
|
|
+ puts ""
|
|
+
|
|
+ # Calculate statistics and store the results in TCL variables, as follows:
|
|
+ #
|
|
+ # total_pages: Database pages consumed.
|
|
+ # total_pages_percent: Pages consumed as a percentage of the file.
|
|
+ # storage: Bytes consumed.
|
|
+ # payload_percent: Payload bytes used as a percentage of $storage.
|
|
+ # total_unused: Unused bytes on pages.
|
|
+ # avg_payload: Average payload per btree entry.
|
|
+ # avg_fanout: Average fanout for internal pages.
|
|
+ # avg_unused: Average unused bytes per btree entry.
|
|
+ # avg_meta: Average metadata overhead per entry.
|
|
+ # ovfl_cnt_percent: Percentage of btree entries that use overflow pages.
|
|
+ #
|
|
+ set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
|
|
+ set total_pages_percent [percent $total_pages $file_pgcnt]
|
|
+ set storage [expr {$total_pages*$pageSize}]
|
|
+ set payload_percent [percent $payload $storage {of storage consumed}]
|
|
+ set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
|
|
+ set avg_payload [divide $payload $nentry]
|
|
+ set avg_unused [divide $total_unused $nentry]
|
|
+ set total_meta [expr {$storage - $payload - $total_unused}]
|
|
+ set total_meta [expr {$total_meta + 4*($ovfl_pages - $ovfl_cnt)}]
|
|
+ set meta_percent [percent $total_meta $storage {of metadata}]
|
|
+ set avg_meta [divide $total_meta $nentry]
|
|
+ if {$int_pages>0} {
|
|
+ # TODO: Is this formula correct?
|
|
+ set nTab [mem eval "
|
|
+ SELECT count(*) FROM (
|
|
+ SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0
|
|
+ )
|
|
+ "]
|
|
+ set avg_fanout [mem eval "
|
|
+ SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used
|
|
+ WHERE $where
|
|
+ "]
|
|
+ set avg_fanout [format %.2f $avg_fanout]
|
|
+ }
|
|
+ set ovfl_cnt_percent [percent $ovfl_cnt $nentry {of all entries}]
|
|
+
|
|
+ # Print out the sub-report statistics.
|
|
+ #
|
|
+ statline {Percentage of total database} $total_pages_percent
|
|
+ statline {Number of entries} $nentry
|
|
+ statline {Bytes of storage consumed} $storage
|
|
+ if {$compressed_size!=$storage} {
|
|
+ set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}]
|
|
+ set pct [expr {$compressed_size*100.0/$storage}]
|
|
+ set pct [format {%5.1f%%} $pct]
|
|
+ statline {Bytes used after compression} $compressed_size $pct
|
|
+ }
|
|
+ statline {Bytes of payload} $payload $payload_percent
|
|
+ statline {Bytes of metadata} $total_meta $meta_percent
|
|
+ if {$cnt==1} {statline {B-tree depth} $depth}
|
|
+ statline {Average payload per entry} $avg_payload
|
|
+ statline {Average unused bytes per entry} $avg_unused
|
|
+ statline {Average metadata per entry} $avg_meta
|
|
+ if {[info exists avg_fanout]} {
|
|
+ statline {Average fanout} $avg_fanout
|
|
+ }
|
|
+ if {$showFrag && $total_pages>1} {
|
|
+ set fragmentation [percent $gap_cnt [expr {$total_pages-1}]]
|
|
+ statline {Non-sequential pages} $gap_cnt $fragmentation
|
|
+ }
|
|
+ statline {Maximum payload per entry} $mx_payload
|
|
+ statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
|
|
+ if {$int_pages>0} {
|
|
+ statline {Index pages used} $int_pages
|
|
+ }
|
|
+ statline {Primary pages used} $leaf_pages
|
|
+ statline {Overflow pages used} $ovfl_pages
|
|
+ statline {Total pages used} $total_pages
|
|
+ if {$int_unused>0} {
|
|
+ set int_unused_percent [
|
|
+ percent $int_unused [expr {$int_pages*$pageSize}] {of index space}]
|
|
+ statline "Unused bytes on index pages" $int_unused $int_unused_percent
|
|
+ }
|
|
+ statline "Unused bytes on primary pages" $leaf_unused [
|
|
+ percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}]
|
|
+ statline "Unused bytes on overflow pages" $ovfl_unused [
|
|
+ percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}]
|
|
+ statline "Unused bytes on all pages" $total_unused [
|
|
+ percent $total_unused $storage {of all space}]
|
|
+ return 1
|
|
+}
|
|
+
|
|
+# Calculate the overhead in pages caused by auto-vacuum.
|
|
+#
|
|
+# This procedure calculates and returns the number of pages used by the
|
|
+# auto-vacuum 'pointer-map'. If the database does not support auto-vacuum,
|
|
+# then 0 is returned. The two arguments are the size of the database file in
|
|
+# pages and the page size used by the database (in bytes).
|
|
+proc autovacuum_overhead {filePages pageSize} {
|
|
+
|
|
+ # Set $autovacuum to non-zero for databases that support auto-vacuum.
|
|
+ set autovacuum [db one {PRAGMA auto_vacuum}]
|
|
+
|
|
+ # If the database is not an auto-vacuum database or the file consists
|
|
+ # of one page only then there is no overhead for auto-vacuum. Return zero.
|
|
+ if {0==$autovacuum || $filePages==1} {
|
|
+ return 0
|
|
+ }
|
|
+
|
|
+ # The number of entries on each pointer map page. The layout of the
|
|
+ # database file is one pointer-map page, followed by $ptrsPerPage other
|
|
+ # pages, followed by a pointer-map page etc. The first pointer-map page
|
|
+ # is the second page of the file overall.
|
|
+ set ptrsPerPage [expr double($pageSize/5)]
|
|
+
|
|
+ # Return the number of pointer map pages in the database.
|
|
+ return [expr wide(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
|
|
+}
|
|
+
|
|
+
|
|
+# Calculate the summary statistics for the database and store the results
|
|
+# in TCL variables. They are output below. Variables are as follows:
|
|
+#
|
|
+# pageSize: Size of each page in bytes.
|
|
+# file_bytes: File size in bytes.
|
|
+# file_pgcnt: Number of pages in the file.
|
|
+# file_pgcnt2: Number of pages in the file (calculated).
|
|
+# av_pgcnt: Pages consumed by the auto-vacuum pointer-map.
|
|
+# av_percent: Percentage of the file consumed by auto-vacuum pointer-map.
|
|
+# inuse_pgcnt: Data pages in the file.
|
|
+# inuse_percent: Percentage of pages used to store data.
|
|
+# free_pgcnt: Free pages calculated as (<total pages> - <in-use pages>)
|
|
+# free_pgcnt2: Free pages in the file according to the file header.
|
|
+# free_percent: Percentage of file consumed by free pages (calculated).
|
|
+# free_percent2: Percentage of file consumed by free pages (header).
|
|
+# ntable: Number of tables in the db.
|
|
+# nindex: Number of indices in the db.
|
|
+# nautoindex: Number of indices created automatically.
|
|
+# nmanindex: Number of indices created manually.
|
|
+# user_payload: Number of bytes of payload in table btrees
|
|
+# (not including sqlite_master)
|
|
+# user_percent: $user_payload as a percentage of total file size.
|
|
+
|
|
+### The following, setting $file_bytes based on the actual size of the file
|
|
+### on disk, causes this tool to choke on zipvfs databases. So set it based
|
|
+### on the return of [PRAGMA page_count] instead.
|
|
+if 0 {
|
|
+ set file_bytes [file size $file_to_analyze]
|
|
+ set file_pgcnt [expr {$file_bytes/$pageSize}]
|
|
+}
|
|
+set file_pgcnt [db one {PRAGMA page_count}]
|
|
+set file_bytes [expr {$file_pgcnt * $pageSize}]
|
|
+
|
|
+set av_pgcnt [autovacuum_overhead $file_pgcnt $pageSize]
|
|
+set av_percent [percent $av_pgcnt $file_pgcnt]
|
|
+
|
|
+set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
|
|
+set inuse_pgcnt [expr wide([mem eval $sql])]
|
|
+set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
|
|
+
|
|
+set free_pgcnt [expr {$file_pgcnt-$inuse_pgcnt-$av_pgcnt}]
|
|
+set free_percent [percent $free_pgcnt $file_pgcnt]
|
|
+set free_pgcnt2 [db one {PRAGMA freelist_count}]
|
|
+set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
|
|
+
|
|
+set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}]
|
|
+
|
|
+set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}]
|
|
+set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}]
|
|
+set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
|
|
+set nautoindex [db eval $sql]
|
|
+set nmanindex [expr {$nindex-$nautoindex}]
|
|
+
|
|
+# set total_payload [mem eval "SELECT sum(payload) FROM space_used"]
|
|
+set user_payload [mem one {SELECT int(sum(payload)) FROM space_used
|
|
+ WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}]
|
|
+set user_percent [percent $user_payload $file_bytes]
|
|
+
|
|
+# Output the summary statistics calculated above.
|
|
+#
|
|
+puts "/** Disk-Space Utilization Report For $root_filename"
|
|
+puts ""
|
|
+statline {Page size in bytes} $pageSize
|
|
+statline {Pages in the whole file (measured)} $file_pgcnt
|
|
+statline {Pages in the whole file (calculated)} $file_pgcnt2
|
|
+statline {Pages that store data} $inuse_pgcnt $inuse_percent
|
|
+statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2
|
|
+statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
|
|
+statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
|
|
+statline {Number of tables in the database} $ntable
|
|
+statline {Number of indices} $nindex
|
|
+statline {Number of defined indices} $nmanindex
|
|
+statline {Number of implied indices} $nautoindex
|
|
+if {$isCompressed} {
|
|
+ statline {Size of uncompressed content in bytes} $file_bytes
|
|
+ set efficiency [percent $true_file_size $file_bytes]
|
|
+ statline {Size of compressed file on disk} $true_file_size $efficiency
|
|
+} else {
|
|
+ statline {Size of the file in bytes} $file_bytes
|
|
+}
|
|
+statline {Bytes of user payload stored} $user_payload $user_percent
|
|
+
|
|
+# Output table rankings
|
|
+#
|
|
+puts ""
|
|
+titleline "Page counts for all tables with their indices"
|
|
+puts ""
|
|
+mem eval {SELECT tblname, count(*) AS cnt,
|
|
+ int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
|
|
+ FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
|
|
+ statline [string toupper $tblname] $size [percent $size $file_pgcnt]
|
|
+}
|
|
+puts ""
|
|
+titleline "Page counts for all tables and indices separately"
|
|
+puts ""
|
|
+mem eval {
|
|
+ SELECT
|
|
+ upper(name) AS nm,
|
|
+ int(int_pages+leaf_pages+ovfl_pages) AS size
|
|
+ FROM space_used
|
|
+ ORDER BY size+0 DESC, name} {} {
|
|
+ statline $nm $size [percent $size $file_pgcnt]
|
|
+}
|
|
+if {$isCompressed} {
|
|
+ puts ""
|
|
+ titleline "Bytes of disk space used after compression"
|
|
+ puts ""
|
|
+ set csum 0
|
|
+ mem eval {SELECT tblname,
|
|
+ int(sum(compressed_size)) +
|
|
+ $compressOverhead*sum(int_pages+leaf_pages+ovfl_pages)
|
|
+ AS csize
|
|
+ FROM space_used GROUP BY tblname ORDER BY csize+0 DESC, tblname} {} {
|
|
+ incr csum $csize
|
|
+ statline [string toupper $tblname] $csize [percent $csize $true_file_size]
|
|
+ }
|
|
+ set overhead [expr {$true_file_size - $csum}]
|
|
+ if {$overhead>0} {
|
|
+ statline {Header and free space} $overhead [percent $overhead $true_file_size]
|
|
+ }
|
|
+}
|
|
+
|
|
+# Output subreports
|
|
+#
|
|
+if {$nindex>0} {
|
|
+ subreport {All tables and indices} 1 0
|
|
+}
|
|
+subreport {All tables} {NOT is_index} 0
|
|
+if {$nindex>0} {
|
|
+ subreport {All indices} {is_index} 0
|
|
+}
|
|
+foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
|
|
+ ORDER BY name}] {
|
|
+ set qn [quote $tbl]
|
|
+ set name [string toupper $tbl]
|
|
+ set n [mem eval {SELECT count(*) FROM space_used WHERE tblname=$tbl}]
|
|
+ if {$n>1} {
|
|
+ set idxlist [mem eval "SELECT name FROM space_used
|
|
+ WHERE tblname='$qn' AND is_index
|
|
+ ORDER BY 1"]
|
|
+ subreport "Table $name and all its indices" "tblname='$qn'" 0
|
|
+ subreport "Table $name w/o any indices" "name='$qn'" 1
|
|
+ if {[llength $idxlist]>1} {
|
|
+ subreport "Indices of table $name" "tblname='$qn' AND is_index" 0
|
|
+ }
|
|
+ foreach idx $idxlist {
|
|
+ set qidx [quote $idx]
|
|
+ subreport "Index [string toupper $idx] of table $name" "name='$qidx'" 1
|
|
+ }
|
|
+ } else {
|
|
+ subreport "Table $name" "name='$qn'" 1
|
|
+ }
|
|
+}
|
|
+
|
|
+# Output instructions on what the numbers above mean.
|
|
+#
|
|
+puts ""
|
|
+titleline Definitions
|
|
+puts {
|
|
+Page size in bytes
|
|
+
|
|
+ The number of bytes in a single page of the database file.
|
|
+ Usually 1024.
|
|
+
|
|
+Number of pages in the whole file
|
|
+}
|
|
+puts " The number of $pageSize-byte pages that go into forming the complete
|
|
+ database"
|
|
+puts {
|
|
+Pages that store data
|
|
+
|
|
+ The number of pages that store data, either as primary B*Tree pages or
|
|
+ as overflow pages. The number at the right is the data pages divided by
|
|
+ the total number of pages in the file.
|
|
+
|
|
+Pages on the freelist
|
|
+
|
|
+ The number of pages that are not currently in use but are reserved for
|
|
+ future use. The percentage at the right is the number of freelist pages
|
|
+ divided by the total number of pages in the file.
|
|
+
|
|
+Pages of auto-vacuum overhead
|
|
+
|
|
+ The number of pages that store data used by the database to facilitate
|
|
+ auto-vacuum. This is zero for databases that do not support auto-vacuum.
|
|
+
|
|
+Number of tables in the database
|
|
+
|
|
+ The number of tables in the database, including the SQLITE_MASTER table
|
|
+ used to store schema information.
|
|
+
|
|
+Number of indices
|
|
+
|
|
+ The total number of indices in the database.
|
|
+
|
|
+Number of defined indices
|
|
+
|
|
+ The number of indices created using an explicit CREATE INDEX statement.
|
|
+
|
|
+Number of implied indices
|
|
+
|
|
+ The number of indices used to implement PRIMARY KEY or UNIQUE constraints
|
|
+ on tables.
|
|
+
|
|
+Size of the file in bytes
|
|
+
|
|
+ The total amount of disk space used by the entire database files.
|
|
+
|
|
+Bytes of user payload stored
|
|
+
|
|
+ The total number of bytes of user payload stored in the database. The
|
|
+ schema information in the SQLITE_MASTER table is not counted when
|
|
+ computing this number. The percentage at the right shows the payload
|
|
+ divided by the total file size.
|
|
+
|
|
+Percentage of total database
|
|
+
|
|
+ The amount of the complete database file that is devoted to storing
|
|
+ information described by this category.
|
|
+
|
|
+Number of entries
|
|
+
|
|
+ The total number of B-Tree key/value pairs stored under this category.
|
|
+
|
|
+Bytes of storage consumed
|
|
+
|
|
+ The total amount of disk space required to store all B-Tree entries
|
|
+ under this category. The is the total number of pages used times
|
|
+ the pages size.
|
|
+
|
|
+Bytes of payload
|
|
+
|
|
+ The amount of payload stored under this category. Payload is the data
|
|
+ part of table entries and the key part of index entries. The percentage
|
|
+ at the right is the bytes of payload divided by the bytes of storage
|
|
+ consumed.
|
|
+
|
|
+Bytes of metadata
|
|
+
|
|
+ The amount of formatting and structural information stored in the
|
|
+ table or index. Metadata includes the btree page header, the cell pointer
|
|
+ array, the size field for each cell, the left child pointer or non-leaf
|
|
+ cells, the overflow pointers for overflow cells, and the rowid value for
|
|
+ rowid table cells. In other words, metadata is everything that is neither
|
|
+ unused space nor content. The record header in the payload is counted as
|
|
+ content, not metadata.
|
|
+
|
|
+Average payload per entry
|
|
+
|
|
+ The average amount of payload on each entry. This is just the bytes of
|
|
+ payload divided by the number of entries.
|
|
+
|
|
+Average unused bytes per entry
|
|
+
|
|
+ The average amount of free space remaining on all pages under this
|
|
+ category on a per-entry basis. This is the number of unused bytes on
|
|
+ all pages divided by the number of entries.
|
|
+
|
|
+Non-sequential pages
|
|
+
|
|
+ The number of pages in the table or index that are out of sequence.
|
|
+ Many filesystems are optimized for sequential file access so a small
|
|
+ number of non-sequential pages might result in faster queries,
|
|
+ especially for larger database files that do not fit in the disk cache.
|
|
+ Note that after running VACUUM, the root page of each table or index is
|
|
+ at the beginning of the database file and all other pages are in a
|
|
+ separate part of the database file, resulting in a single non-
|
|
+ sequential page.
|
|
+
|
|
+Maximum payload per entry
|
|
+
|
|
+ The largest payload size of any entry.
|
|
+
|
|
+Entries that use overflow
|
|
+
|
|
+ The number of entries that user one or more overflow pages.
|
|
+
|
|
+Total pages used
|
|
+
|
|
+ This is the number of pages used to hold all information in the current
|
|
+ category. This is the sum of index, primary, and overflow pages.
|
|
+
|
|
+Index pages used
|
|
+
|
|
+ This is the number of pages in a table B-tree that hold only key (rowid)
|
|
+ information and no data.
|
|
+
|
|
+Primary pages used
|
|
+
|
|
+ This is the number of B-tree pages that hold both key and data.
|
|
+
|
|
+Overflow pages used
|
|
+
|
|
+ The total number of overflow pages used for this category.
|
|
+
|
|
+Unused bytes on index pages
|
|
+
|
|
+ The total number of bytes of unused space on all index pages. The
|
|
+ percentage at the right is the number of unused bytes divided by the
|
|
+ total number of bytes on index pages.
|
|
+
|
|
+Unused bytes on primary pages
|
|
+
|
|
+ The total number of bytes of unused space on all primary pages. The
|
|
+ percentage at the right is the number of unused bytes divided by the
|
|
+ total number of bytes on primary pages.
|
|
+
|
|
+Unused bytes on overflow pages
|
|
+
|
|
+ The total number of bytes of unused space on all overflow pages. The
|
|
+ percentage at the right is the number of unused bytes divided by the
|
|
+ total number of bytes on overflow pages.
|
|
+
|
|
+Unused bytes on all pages
|
|
+
|
|
+ The total number of bytes of unused space on all primary and overflow
|
|
+ pages. The percentage at the right is the number of unused bytes
|
|
+ divided by the total number of bytes.
|
|
+}
|
|
+
|
|
+# Output a dump of the in-memory database. This can be used for more
|
|
+# complex offline analysis.
|
|
+#
|
|
+titleline {}
|
|
+puts "The entire text of this report can be sourced into any SQL database"
|
|
+puts "engine for further analysis. All of the text above is an SQL comment."
|
|
+puts "The data used to generate this report follows:"
|
|
+puts "*/"
|
|
+puts "BEGIN;"
|
|
+puts $tabledef
|
|
+unset -nocomplain x
|
|
+mem eval {SELECT * FROM space_used} x {
|
|
+ puts -nonewline "INSERT INTO space_used VALUES"
|
|
+ set sep (
|
|
+ foreach col $x(*) {
|
|
+ set v $x($col)
|
|
+ if {$v=="" || ![string is double $v]} {set v '[quote $v]'}
|
|
+ puts -nonewline $sep$v
|
|
+ set sep ,
|
|
+ }
|
|
+ puts ");"
|
|
+}
|
|
+puts "COMMIT;"
|
|
+
|
|
+} err]} {
|
|
+ puts "ERROR: $err"
|
|
+ puts $errorInfo
|
|
+ exit 1
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/spellfix.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/spellfix.c 2019-05-14 16:22:31.269333500 +0200
|
|
@@ -0,0 +1,3083 @@
|
|
+/*
|
|
+** 2012 April 10
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This module implements the spellfix1 VIRTUAL TABLE that can be used
|
|
+** to search a large vocabulary for close matches. See separate
|
|
+** documentation (http://www.sqlite.org/spellfix1.html) for details.
|
|
+*/
|
|
+#include <string.h>
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+
|
|
+#ifndef SQLITE_AMALGAMATION
|
|
+# if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
|
|
+# define NDEBUG 1
|
|
+# endif
|
|
+# if defined(NDEBUG) && defined(SQLITE_DEBUG)
|
|
+# undef NDEBUG
|
|
+# endif
|
|
+# include <stdio.h>
|
|
+# include <stdlib.h>
|
|
+# include <assert.h>
|
|
+# define ALWAYS(X) 1
|
|
+# define NEVER(X) 0
|
|
+ typedef unsigned char u8;
|
|
+ typedef unsigned short u16;
|
|
+#endif
|
|
+#include <ctype.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Character classes for ASCII characters:
|
|
+**
|
|
+** 0 '' Silent letters: H W
|
|
+** 1 'A' Any vowel: A E I O U (Y)
|
|
+** 2 'B' A bilabeal stop or fricative: B F P V W
|
|
+** 3 'C' Other fricatives or back stops: C G J K Q S X Z
|
|
+** 4 'D' Alveolar stops: D T
|
|
+** 5 'H' Letter H at the beginning of a word
|
|
+** 6 'L' Glide: L
|
|
+** 7 'R' Semivowel: R
|
|
+** 8 'M' Nasals: M N
|
|
+** 9 'Y' Letter Y at the beginning of a word.
|
|
+** 10 '9' Digits: 0 1 2 3 4 5 6 7 8 9
|
|
+** 11 ' ' White space
|
|
+** 12 '?' Other.
|
|
+*/
|
|
+#define CCLASS_SILENT 0
|
|
+#define CCLASS_VOWEL 1
|
|
+#define CCLASS_B 2
|
|
+#define CCLASS_C 3
|
|
+#define CCLASS_D 4
|
|
+#define CCLASS_H 5
|
|
+#define CCLASS_L 6
|
|
+#define CCLASS_R 7
|
|
+#define CCLASS_M 8
|
|
+#define CCLASS_Y 9
|
|
+#define CCLASS_DIGIT 10
|
|
+#define CCLASS_SPACE 11
|
|
+#define CCLASS_OTHER 12
|
|
+
|
|
+/*
|
|
+** The following table gives the character class for non-initial ASCII
|
|
+** characters.
|
|
+*/
|
|
+static const unsigned char midClass[] = {
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE,
|
|
+ /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER,
|
|
+ /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER,
|
|
+ /* ' */ CCLASS_SILENT, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER,
|
|
+ /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER,
|
|
+ /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER,
|
|
+ /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT,
|
|
+ /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT,
|
|
+ /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT,
|
|
+ /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER,
|
|
+ /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER,
|
|
+ /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL,
|
|
+ /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D,
|
|
+ /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C,
|
|
+ /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C,
|
|
+ /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M,
|
|
+ /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B,
|
|
+ /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C,
|
|
+ /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B,
|
|
+ /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_VOWEL,
|
|
+ /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER,
|
|
+ /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER,
|
|
+ /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B,
|
|
+ /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL,
|
|
+ /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT,
|
|
+ /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C,
|
|
+ /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M,
|
|
+ /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C,
|
|
+ /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D,
|
|
+ /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B,
|
|
+ /* x */ CCLASS_C, /* y */ CCLASS_VOWEL, /* z */ CCLASS_C,
|
|
+ /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER,
|
|
+ /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+};
|
|
+/*
|
|
+** This tables gives the character class for ASCII characters that form the
|
|
+** initial character of a word. The only difference from midClass is with
|
|
+** the letters H, W, and Y.
|
|
+*/
|
|
+static const unsigned char initClass[] = {
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE,
|
|
+ /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER,
|
|
+ /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER,
|
|
+ /* ' */ CCLASS_OTHER, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER,
|
|
+ /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER,
|
|
+ /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER,
|
|
+ /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT,
|
|
+ /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT,
|
|
+ /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT,
|
|
+ /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER,
|
|
+ /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER,
|
|
+ /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL,
|
|
+ /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D,
|
|
+ /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C,
|
|
+ /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C,
|
|
+ /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M,
|
|
+ /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B,
|
|
+ /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C,
|
|
+ /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B,
|
|
+ /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_Y,
|
|
+ /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER,
|
|
+ /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER,
|
|
+ /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B,
|
|
+ /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL,
|
|
+ /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT,
|
|
+ /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C,
|
|
+ /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M,
|
|
+ /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C,
|
|
+ /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D,
|
|
+ /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B,
|
|
+ /* x */ CCLASS_C, /* y */ CCLASS_Y, /* z */ CCLASS_C,
|
|
+ /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER,
|
|
+ /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
|
+};
|
|
+
|
|
+/*
|
|
+** Mapping from the character class number (0-13) to a symbol for each
|
|
+** character class. Note that initClass[] can be used to map the class
|
|
+** symbol back into the class number.
|
|
+*/
|
|
+static const unsigned char className[] = ".ABCDHLRMY9 ?";
|
|
+
|
|
+/*
|
|
+** Generate a "phonetic hash" from a string of ASCII characters
|
|
+** in zIn[0..nIn-1].
|
|
+**
|
|
+** * Map characters by character class as defined above.
|
|
+** * Omit double-letters
|
|
+** * Omit vowels beside R and L
|
|
+** * Omit T when followed by CH
|
|
+** * Omit W when followed by R
|
|
+** * Omit D when followed by J or G
|
|
+** * Omit K in KN or G in GN at the beginning of a word
|
|
+**
|
|
+** Space to hold the result is obtained from sqlite3_malloc()
|
|
+**
|
|
+** Return NULL if memory allocation fails.
|
|
+*/
|
|
+static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){
|
|
+ unsigned char *zOut = sqlite3_malloc( nIn + 1 );
|
|
+ int i;
|
|
+ int nOut = 0;
|
|
+ char cPrev = 0x77;
|
|
+ char cPrevX = 0x77;
|
|
+ const unsigned char *aClass = initClass;
|
|
+
|
|
+ if( zOut==0 ) return 0;
|
|
+ if( nIn>2 ){
|
|
+ switch( zIn[0] ){
|
|
+ case 'g':
|
|
+ case 'k': {
|
|
+ if( zIn[1]=='n' ){ zIn++; nIn--; }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ for(i=0; i<nIn; i++){
|
|
+ unsigned char c = zIn[i];
|
|
+ if( i+1<nIn ){
|
|
+ if( c=='w' && zIn[i+1]=='r' ) continue;
|
|
+ if( c=='d' && (zIn[i+1]=='j' || zIn[i+1]=='g') ) continue;
|
|
+ if( i+2<nIn ){
|
|
+ if( c=='t' && zIn[i+1]=='c' && zIn[i+2]=='h' ) continue;
|
|
+ }
|
|
+ }
|
|
+ c = aClass[c&0x7f];
|
|
+ if( c==CCLASS_SPACE ) continue;
|
|
+ if( c==CCLASS_OTHER && cPrev!=CCLASS_DIGIT ) continue;
|
|
+ aClass = midClass;
|
|
+ if( c==CCLASS_VOWEL && (cPrevX==CCLASS_R || cPrevX==CCLASS_L) ){
|
|
+ continue; /* No vowels beside L or R */
|
|
+ }
|
|
+ if( (c==CCLASS_R || c==CCLASS_L) && cPrevX==CCLASS_VOWEL ){
|
|
+ nOut--; /* No vowels beside L or R */
|
|
+ }
|
|
+ cPrev = c;
|
|
+ if( c==CCLASS_SILENT ) continue;
|
|
+ cPrevX = c;
|
|
+ c = className[c];
|
|
+ assert( nOut>=0 );
|
|
+ if( nOut==0 || c!=zOut[nOut-1] ) zOut[nOut++] = c;
|
|
+ }
|
|
+ zOut[nOut] = 0;
|
|
+ return zOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is an SQL function wrapper around phoneticHash(). See
|
|
+** the description of phoneticHash() for additional information.
|
|
+*/
|
|
+static void phoneticHashSqlFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *zIn;
|
|
+ unsigned char *zOut;
|
|
+
|
|
+ zIn = sqlite3_value_text(argv[0]);
|
|
+ if( zIn==0 ) return;
|
|
+ zOut = phoneticHash(zIn, sqlite3_value_bytes(argv[0]));
|
|
+ if( zOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the character class number for a character given its
|
|
+** context.
|
|
+*/
|
|
+static char characterClass(char cPrev, char c){
|
|
+ return cPrev==0 ? initClass[c&0x7f] : midClass[c&0x7f];
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the cost of inserting or deleting character c immediately
|
|
+** following character cPrev. If cPrev==0, that means c is the first
|
|
+** character of the word.
|
|
+*/
|
|
+static int insertOrDeleteCost(char cPrev, char c, char cNext){
|
|
+ char classC = characterClass(cPrev, c);
|
|
+ char classCprev;
|
|
+
|
|
+ if( classC==CCLASS_SILENT ){
|
|
+ /* Insert or delete "silent" characters such as H or W */
|
|
+ return 1;
|
|
+ }
|
|
+ if( cPrev==c ){
|
|
+ /* Repeated characters, or miss a repeat */
|
|
+ return 10;
|
|
+ }
|
|
+ if( classC==CCLASS_VOWEL && (cPrev=='r' || cNext=='r') ){
|
|
+ return 20; /* Insert a vowel before or after 'r' */
|
|
+ }
|
|
+ classCprev = characterClass(cPrev, cPrev);
|
|
+ if( classC==classCprev ){
|
|
+ if( classC==CCLASS_VOWEL ){
|
|
+ /* Remove or add a new vowel to a vowel cluster */
|
|
+ return 15;
|
|
+ }else{
|
|
+ /* Remove or add a consonant not in the same class */
|
|
+ return 50;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* any other character insertion or deletion */
|
|
+ return 100;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Divide the insertion cost by this factor when appending to the
|
|
+** end of the word.
|
|
+*/
|
|
+#define FINAL_INS_COST_DIV 4
|
|
+
|
|
+/*
|
|
+** Return the cost of substituting cTo in place of cFrom assuming
|
|
+** the previous character is cPrev. If cPrev==0 then cTo is the first
|
|
+** character of the word.
|
|
+*/
|
|
+static int substituteCost(char cPrev, char cFrom, char cTo){
|
|
+ char classFrom, classTo;
|
|
+ if( cFrom==cTo ){
|
|
+ /* Exact match */
|
|
+ return 0;
|
|
+ }
|
|
+ if( cFrom==(cTo^0x20) && ((cTo>='A' && cTo<='Z') || (cTo>='a' && cTo<='z')) ){
|
|
+ /* differ only in case */
|
|
+ return 0;
|
|
+ }
|
|
+ classFrom = characterClass(cPrev, cFrom);
|
|
+ classTo = characterClass(cPrev, cTo);
|
|
+ if( classFrom==classTo ){
|
|
+ /* Same character class */
|
|
+ return 40;
|
|
+ }
|
|
+ if( classFrom>=CCLASS_B && classFrom<=CCLASS_Y
|
|
+ && classTo>=CCLASS_B && classTo<=CCLASS_Y ){
|
|
+ /* Convert from one consonant to another, but in a different class */
|
|
+ return 75;
|
|
+ }
|
|
+ /* Any other subsitution */
|
|
+ return 100;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Given two strings zA and zB which are pure ASCII, return the cost
|
|
+** of transforming zA into zB. If zA ends with '*' assume that it is
|
|
+** a prefix of zB and give only minimal penalty for extra characters
|
|
+** on the end of zB.
|
|
+**
|
|
+** Smaller numbers mean a closer match.
|
|
+**
|
|
+** Negative values indicate an error:
|
|
+** -1 One of the inputs is NULL
|
|
+** -2 Non-ASCII characters on input
|
|
+** -3 Unable to allocate memory
|
|
+**
|
|
+** If pnMatch is not NULL, then *pnMatch is set to the number of bytes
|
|
+** of zB that matched the pattern in zA. If zA does not end with a '*',
|
|
+** then this value is always the number of bytes in zB (i.e. strlen(zB)).
|
|
+** If zA does end in a '*', then it is the number of bytes in the prefix
|
|
+** of zB that was deemed to match zA.
|
|
+*/
|
|
+static int editdist1(const char *zA, const char *zB, int *pnMatch){
|
|
+ int nA, nB; /* Number of characters in zA[] and zB[] */
|
|
+ int xA, xB; /* Loop counters for zA[] and zB[] */
|
|
+ char cA = 0, cB; /* Current character of zA and zB */
|
|
+ char cAprev, cBprev; /* Previous character of zA and zB */
|
|
+ char cAnext, cBnext; /* Next character in zA and zB */
|
|
+ int d; /* North-west cost value */
|
|
+ int dc = 0; /* North-west character value */
|
|
+ int res; /* Final result */
|
|
+ int *m; /* The cost matrix */
|
|
+ char *cx; /* Corresponding character values */
|
|
+ int *toFree = 0; /* Malloced space */
|
|
+ int nMatch = 0;
|
|
+ int mStack[60+15]; /* Stack space to use if not too much is needed */
|
|
+
|
|
+ /* Early out if either input is NULL */
|
|
+ if( zA==0 || zB==0 ) return -1;
|
|
+
|
|
+ /* Skip any common prefix */
|
|
+ while( zA[0] && zA[0]==zB[0] ){ dc = zA[0]; zA++; zB++; nMatch++; }
|
|
+ if( pnMatch ) *pnMatch = nMatch;
|
|
+ if( zA[0]==0 && zB[0]==0 ) return 0;
|
|
+
|
|
+#if 0
|
|
+ printf("A=\"%s\" B=\"%s\" dc=%c\n", zA, zB, dc?dc:' ');
|
|
+#endif
|
|
+
|
|
+ /* Verify input strings and measure their lengths */
|
|
+ for(nA=0; zA[nA]; nA++){
|
|
+ if( zA[nA]&0x80 ) return -2;
|
|
+ }
|
|
+ for(nB=0; zB[nB]; nB++){
|
|
+ if( zB[nB]&0x80 ) return -2;
|
|
+ }
|
|
+
|
|
+ /* Special processing if either string is empty */
|
|
+ if( nA==0 ){
|
|
+ cBprev = (char)dc;
|
|
+ for(xB=res=0; (cB = zB[xB])!=0; xB++){
|
|
+ res += insertOrDeleteCost(cBprev, cB, zB[xB+1])/FINAL_INS_COST_DIV;
|
|
+ cBprev = cB;
|
|
+ }
|
|
+ return res;
|
|
+ }
|
|
+ if( nB==0 ){
|
|
+ cAprev = (char)dc;
|
|
+ for(xA=res=0; (cA = zA[xA])!=0; xA++){
|
|
+ res += insertOrDeleteCost(cAprev, cA, zA[xA+1]);
|
|
+ cAprev = cA;
|
|
+ }
|
|
+ return res;
|
|
+ }
|
|
+
|
|
+ /* A is a prefix of B */
|
|
+ if( zA[0]=='*' && zA[1]==0 ) return 0;
|
|
+
|
|
+ /* Allocate and initialize the Wagner matrix */
|
|
+ if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ){
|
|
+ m = mStack;
|
|
+ }else{
|
|
+ m = toFree = sqlite3_malloc( (nB+1)*5*sizeof(m[0])/4 );
|
|
+ if( m==0 ) return -3;
|
|
+ }
|
|
+ cx = (char*)&m[nB+1];
|
|
+
|
|
+ /* Compute the Wagner edit distance */
|
|
+ m[0] = 0;
|
|
+ cx[0] = (char)dc;
|
|
+ cBprev = (char)dc;
|
|
+ for(xB=1; xB<=nB; xB++){
|
|
+ cBnext = zB[xB];
|
|
+ cB = zB[xB-1];
|
|
+ cx[xB] = cB;
|
|
+ m[xB] = m[xB-1] + insertOrDeleteCost(cBprev, cB, cBnext);
|
|
+ cBprev = cB;
|
|
+ }
|
|
+ cAprev = (char)dc;
|
|
+ for(xA=1; xA<=nA; xA++){
|
|
+ int lastA = (xA==nA);
|
|
+ cA = zA[xA-1];
|
|
+ cAnext = zA[xA];
|
|
+ if( cA=='*' && lastA ) break;
|
|
+ d = m[0];
|
|
+ dc = cx[0];
|
|
+ m[0] = d + insertOrDeleteCost(cAprev, cA, cAnext);
|
|
+ cBprev = 0;
|
|
+ for(xB=1; xB<=nB; xB++){
|
|
+ int totalCost, insCost, delCost, subCost, ncx;
|
|
+ cB = zB[xB-1];
|
|
+ cBnext = zB[xB];
|
|
+
|
|
+ /* Cost to insert cB */
|
|
+ insCost = insertOrDeleteCost(cx[xB-1], cB, cBnext);
|
|
+ if( lastA ) insCost /= FINAL_INS_COST_DIV;
|
|
+
|
|
+ /* Cost to delete cA */
|
|
+ delCost = insertOrDeleteCost(cx[xB], cA, cBnext);
|
|
+
|
|
+ /* Cost to substitute cA->cB */
|
|
+ subCost = substituteCost(cx[xB-1], cA, cB);
|
|
+
|
|
+ /* Best cost */
|
|
+ totalCost = insCost + m[xB-1];
|
|
+ ncx = cB;
|
|
+ if( (delCost + m[xB])<totalCost ){
|
|
+ totalCost = delCost + m[xB];
|
|
+ ncx = cA;
|
|
+ }
|
|
+ if( (subCost + d)<totalCost ){
|
|
+ totalCost = subCost + d;
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ printf("%d,%d d=%4d u=%4d r=%4d dc=%c cA=%c cB=%c"
|
|
+ " ins=%4d del=%4d sub=%4d t=%4d ncx=%c\n",
|
|
+ xA, xB, d, m[xB], m[xB-1], dc?dc:' ', cA, cB,
|
|
+ insCost, delCost, subCost, totalCost, ncx?ncx:' ');
|
|
+#endif
|
|
+
|
|
+ /* Update the matrix */
|
|
+ d = m[xB];
|
|
+ dc = cx[xB];
|
|
+ m[xB] = totalCost;
|
|
+ cx[xB] = (char)ncx;
|
|
+ cBprev = cB;
|
|
+ }
|
|
+ cAprev = cA;
|
|
+ }
|
|
+
|
|
+ /* Free the wagner matrix and return the result */
|
|
+ if( cA=='*' ){
|
|
+ res = m[1];
|
|
+ for(xB=1; xB<=nB; xB++){
|
|
+ if( m[xB]<res ){
|
|
+ res = m[xB];
|
|
+ if( pnMatch ) *pnMatch = xB+nMatch;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ res = m[nB];
|
|
+ /* In the current implementation, pnMatch is always NULL if zA does
|
|
+ ** not end in "*" */
|
|
+ assert( pnMatch==0 );
|
|
+ }
|
|
+ sqlite3_free(toFree);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Function: editdist(A,B)
|
|
+**
|
|
+** Return the cost of transforming string A into string B. Both strings
|
|
+** must be pure ASCII text. If A ends with '*' then it is assumed to be
|
|
+** a prefix of B and extra characters on the end of B have minimal additional
|
|
+** cost.
|
|
+*/
|
|
+static void editdistSqlFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int res = editdist1(
|
|
+ (const char*)sqlite3_value_text(argv[0]),
|
|
+ (const char*)sqlite3_value_text(argv[1]),
|
|
+ 0);
|
|
+ if( res<0 ){
|
|
+ if( res==(-3) ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else if( res==(-2) ){
|
|
+ sqlite3_result_error(context, "non-ASCII input to editdist()", -1);
|
|
+ }else{
|
|
+ sqlite3_result_error(context, "NULL input to editdist()", -1);
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_int(context, res);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* End of the fixed-cost edit distance implementation
|
|
+******************************************************************************
|
|
+*****************************************************************************
|
|
+** Begin: Configurable cost unicode edit distance routines
|
|
+*/
|
|
+/* Forward declaration of structures */
|
|
+typedef struct EditDist3Cost EditDist3Cost;
|
|
+typedef struct EditDist3Config EditDist3Config;
|
|
+typedef struct EditDist3Point EditDist3Point;
|
|
+typedef struct EditDist3From EditDist3From;
|
|
+typedef struct EditDist3FromString EditDist3FromString;
|
|
+typedef struct EditDist3To EditDist3To;
|
|
+typedef struct EditDist3ToString EditDist3ToString;
|
|
+typedef struct EditDist3Lang EditDist3Lang;
|
|
+
|
|
+
|
|
+/*
|
|
+** An entry in the edit cost table
|
|
+*/
|
|
+struct EditDist3Cost {
|
|
+ EditDist3Cost *pNext; /* Next cost element */
|
|
+ u8 nFrom; /* Number of bytes in aFrom */
|
|
+ u8 nTo; /* Number of bytes in aTo */
|
|
+ u16 iCost; /* Cost of this transformation */
|
|
+ char a[4] ; /* FROM string followed by TO string */
|
|
+ /* Additional TO and FROM string bytes appended as necessary */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Edit costs for a particular language ID
|
|
+*/
|
|
+struct EditDist3Lang {
|
|
+ int iLang; /* Language ID */
|
|
+ int iInsCost; /* Default insertion cost */
|
|
+ int iDelCost; /* Default deletion cost */
|
|
+ int iSubCost; /* Default substitution cost */
|
|
+ EditDist3Cost *pCost; /* Costs */
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+** The default EditDist3Lang object, with default costs.
|
|
+*/
|
|
+static const EditDist3Lang editDist3Lang = { 0, 100, 100, 150, 0 };
|
|
+
|
|
+/*
|
|
+** Complete configuration
|
|
+*/
|
|
+struct EditDist3Config {
|
|
+ int nLang; /* Number of language IDs. Size of a[] */
|
|
+ EditDist3Lang *a; /* One for each distinct language ID */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Extra information about each character in the FROM string.
|
|
+*/
|
|
+struct EditDist3From {
|
|
+ int nSubst; /* Number of substitution cost entries */
|
|
+ int nDel; /* Number of deletion cost entries */
|
|
+ int nByte; /* Number of bytes in this character */
|
|
+ EditDist3Cost **apSubst; /* Array of substitution costs for this element */
|
|
+ EditDist3Cost **apDel; /* Array of deletion cost entries */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A precompiled FROM string.
|
|
+*
|
|
+** In the common case we expect the FROM string to be reused multiple times.
|
|
+** In other words, the common case will be to measure the edit distance
|
|
+** from a single origin string to multiple target strings.
|
|
+*/
|
|
+struct EditDist3FromString {
|
|
+ char *z; /* The complete text of the FROM string */
|
|
+ int n; /* Number of characters in the FROM string */
|
|
+ int isPrefix; /* True if ends with '*' character */
|
|
+ EditDist3From *a; /* Extra info about each char of the FROM string */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Extra information about each character in the TO string.
|
|
+*/
|
|
+struct EditDist3To {
|
|
+ int nIns; /* Number of insertion cost entries */
|
|
+ int nByte; /* Number of bytes in this character */
|
|
+ EditDist3Cost **apIns; /* Array of deletion cost entries */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A precompiled FROM string
|
|
+*/
|
|
+struct EditDist3ToString {
|
|
+ char *z; /* The complete text of the TO string */
|
|
+ int n; /* Number of characters in the TO string */
|
|
+ EditDist3To *a; /* Extra info about each char of the TO string */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Clear or delete an instance of the object that records all edit-distance
|
|
+** weights.
|
|
+*/
|
|
+static void editDist3ConfigClear(EditDist3Config *p){
|
|
+ int i;
|
|
+ if( p==0 ) return;
|
|
+ for(i=0; i<p->nLang; i++){
|
|
+ EditDist3Cost *pCost, *pNext;
|
|
+ pCost = p->a[i].pCost;
|
|
+ while( pCost ){
|
|
+ pNext = pCost->pNext;
|
|
+ sqlite3_free(pCost);
|
|
+ pCost = pNext;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(p->a);
|
|
+ memset(p, 0, sizeof(*p));
|
|
+}
|
|
+static void editDist3ConfigDelete(void *pIn){
|
|
+ EditDist3Config *p = (EditDist3Config*)pIn;
|
|
+ editDist3ConfigClear(p);
|
|
+ sqlite3_free(p);
|
|
+}
|
|
+
|
|
+/* Compare the FROM values of two EditDist3Cost objects, for sorting.
|
|
+** Return negative, zero, or positive if the A is less than, equal to,
|
|
+** or greater than B.
|
|
+*/
|
|
+static int editDist3CostCompare(EditDist3Cost *pA, EditDist3Cost *pB){
|
|
+ int n = pA->nFrom;
|
|
+ int rc;
|
|
+ if( n>pB->nFrom ) n = pB->nFrom;
|
|
+ rc = strncmp(pA->a, pB->a, n);
|
|
+ if( rc==0 ) rc = pA->nFrom - pB->nFrom;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Merge together two sorted lists of EditDist3Cost objects, in order
|
|
+** of increasing FROM.
|
|
+*/
|
|
+static EditDist3Cost *editDist3CostMerge(
|
|
+ EditDist3Cost *pA,
|
|
+ EditDist3Cost *pB
|
|
+){
|
|
+ EditDist3Cost *pHead = 0;
|
|
+ EditDist3Cost **ppTail = &pHead;
|
|
+ EditDist3Cost *p;
|
|
+ while( pA && pB ){
|
|
+ if( editDist3CostCompare(pA,pB)<=0 ){
|
|
+ p = pA;
|
|
+ pA = pA->pNext;
|
|
+ }else{
|
|
+ p = pB;
|
|
+ pB = pB->pNext;
|
|
+ }
|
|
+ *ppTail = p;
|
|
+ ppTail = &p->pNext;
|
|
+ }
|
|
+ if( pA ){
|
|
+ *ppTail = pA;
|
|
+ }else{
|
|
+ *ppTail = pB;
|
|
+ }
|
|
+ return pHead;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sort a list of EditDist3Cost objects into order of increasing FROM
|
|
+*/
|
|
+static EditDist3Cost *editDist3CostSort(EditDist3Cost *pList){
|
|
+ EditDist3Cost *ap[60], *p;
|
|
+ int i;
|
|
+ int mx = 0;
|
|
+ ap[0] = 0;
|
|
+ ap[1] = 0;
|
|
+ while( pList ){
|
|
+ p = pList;
|
|
+ pList = p->pNext;
|
|
+ p->pNext = 0;
|
|
+ for(i=0; ap[i]; i++){
|
|
+ p = editDist3CostMerge(ap[i],p);
|
|
+ ap[i] = 0;
|
|
+ }
|
|
+ ap[i] = p;
|
|
+ if( i>mx ){
|
|
+ mx = i;
|
|
+ ap[i+1] = 0;
|
|
+ }
|
|
+ }
|
|
+ p = 0;
|
|
+ for(i=0; i<=mx; i++){
|
|
+ if( ap[i] ) p = editDist3CostMerge(p,ap[i]);
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Load all edit-distance weights from a table.
|
|
+*/
|
|
+static int editDist3ConfigLoad(
|
|
+ EditDist3Config *p, /* The edit distance configuration to load */
|
|
+ sqlite3 *db, /* Load from this database */
|
|
+ const char *zTable /* Name of the table from which to load */
|
|
+){
|
|
+ sqlite3_stmt *pStmt;
|
|
+ int rc, rc2;
|
|
+ char *zSql;
|
|
+ int iLangPrev = -9999;
|
|
+ EditDist3Lang *pLang = 0;
|
|
+
|
|
+ zSql = sqlite3_mprintf("SELECT iLang, cFrom, cTo, iCost"
|
|
+ " FROM \"%w\" WHERE iLang>=0 ORDER BY iLang", zTable);
|
|
+ if( zSql==0 ) return SQLITE_NOMEM;
|
|
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) return rc;
|
|
+ editDist3ConfigClear(p);
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ int iLang = sqlite3_column_int(pStmt, 0);
|
|
+ const char *zFrom = (const char*)sqlite3_column_text(pStmt, 1);
|
|
+ int nFrom = zFrom ? sqlite3_column_bytes(pStmt, 1) : 0;
|
|
+ const char *zTo = (const char*)sqlite3_column_text(pStmt, 2);
|
|
+ int nTo = zTo ? sqlite3_column_bytes(pStmt, 2) : 0;
|
|
+ int iCost = sqlite3_column_int(pStmt, 3);
|
|
+
|
|
+ assert( zFrom!=0 || nFrom==0 );
|
|
+ assert( zTo!=0 || nTo==0 );
|
|
+ if( nFrom>100 || nTo>100 ) continue;
|
|
+ if( iCost<0 ) continue;
|
|
+ if( iCost>=10000 ) continue; /* Costs above 10K are considered infinite */
|
|
+ if( pLang==0 || iLang!=iLangPrev ){
|
|
+ EditDist3Lang *pNew;
|
|
+ pNew = sqlite3_realloc(p->a, (p->nLang+1)*sizeof(p->a[0]));
|
|
+ if( pNew==0 ){ rc = SQLITE_NOMEM; break; }
|
|
+ p->a = pNew;
|
|
+ pLang = &p->a[p->nLang];
|
|
+ p->nLang++;
|
|
+ pLang->iLang = iLang;
|
|
+ pLang->iInsCost = 100;
|
|
+ pLang->iDelCost = 100;
|
|
+ pLang->iSubCost = 150;
|
|
+ pLang->pCost = 0;
|
|
+ iLangPrev = iLang;
|
|
+ }
|
|
+ if( nFrom==1 && zFrom[0]=='?' && nTo==0 ){
|
|
+ pLang->iDelCost = iCost;
|
|
+ }else if( nFrom==0 && nTo==1 && zTo[0]=='?' ){
|
|
+ pLang->iInsCost = iCost;
|
|
+ }else if( nFrom==1 && nTo==1 && zFrom[0]=='?' && zTo[0]=='?' ){
|
|
+ pLang->iSubCost = iCost;
|
|
+ }else{
|
|
+ EditDist3Cost *pCost;
|
|
+ int nExtra = nFrom + nTo - 4;
|
|
+ if( nExtra<0 ) nExtra = 0;
|
|
+ pCost = sqlite3_malloc( sizeof(*pCost) + nExtra );
|
|
+ if( pCost==0 ){ rc = SQLITE_NOMEM; break; }
|
|
+ pCost->nFrom = (u8)nFrom;
|
|
+ pCost->nTo = (u8)nTo;
|
|
+ pCost->iCost = (u16)iCost;
|
|
+ memcpy(pCost->a, zFrom, nFrom);
|
|
+ memcpy(pCost->a + nFrom, zTo, nTo);
|
|
+ pCost->pNext = pLang->pCost;
|
|
+ pLang->pCost = pCost;
|
|
+ }
|
|
+ }
|
|
+ rc2 = sqlite3_finalize(pStmt);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int iLang;
|
|
+ for(iLang=0; iLang<p->nLang; iLang++){
|
|
+ p->a[iLang].pCost = editDist3CostSort(p->a[iLang].pCost);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the length (in bytes) of a utf-8 character. Or return a maximum
|
|
+** of N.
|
|
+*/
|
|
+static int utf8Len(unsigned char c, int N){
|
|
+ int len = 1;
|
|
+ if( c>0x7f ){
|
|
+ if( (c&0xe0)==0xc0 ){
|
|
+ len = 2;
|
|
+ }else if( (c&0xf0)==0xe0 ){
|
|
+ len = 3;
|
|
+ }else{
|
|
+ len = 4;
|
|
+ }
|
|
+ }
|
|
+ if( len>N ) len = N;
|
|
+ return len;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE (non-zero) if the To side of the given cost matches
|
|
+** the given string.
|
|
+*/
|
|
+static int matchTo(EditDist3Cost *p, const char *z, int n){
|
|
+ assert( n>0 );
|
|
+ if( p->a[p->nFrom]!=z[0] ) return 0;
|
|
+ if( p->nTo>n ) return 0;
|
|
+ if( strncmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE (non-zero) if the From side of the given cost matches
|
|
+** the given string.
|
|
+*/
|
|
+static int matchFrom(EditDist3Cost *p, const char *z, int n){
|
|
+ assert( p->nFrom<=n );
|
|
+ if( p->nFrom ){
|
|
+ if( p->a[0]!=z[0] ) return 0;
|
|
+ if( strncmp(p->a, z, p->nFrom)!=0 ) return 0;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE (non-zero) of the next FROM character and the next TO
|
|
+** character are the same.
|
|
+*/
|
|
+static int matchFromTo(
|
|
+ EditDist3FromString *pStr, /* Left hand string */
|
|
+ int n1, /* Index of comparison character on the left */
|
|
+ const char *z2, /* Right-handl comparison character */
|
|
+ int n2 /* Bytes remaining in z2[] */
|
|
+){
|
|
+ int b1 = pStr->a[n1].nByte;
|
|
+ if( b1>n2 ) return 0;
|
|
+ assert( b1>0 );
|
|
+ if( pStr->z[n1]!=z2[0] ) return 0;
|
|
+ if( strncmp(pStr->z+n1, z2, b1)!=0 ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Delete an EditDist3FromString objecct
|
|
+*/
|
|
+static void editDist3FromStringDelete(EditDist3FromString *p){
|
|
+ int i;
|
|
+ if( p ){
|
|
+ for(i=0; i<p->n; i++){
|
|
+ sqlite3_free(p->a[i].apDel);
|
|
+ sqlite3_free(p->a[i].apSubst);
|
|
+ }
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create a EditDist3FromString object.
|
|
+*/
|
|
+static EditDist3FromString *editDist3FromStringNew(
|
|
+ const EditDist3Lang *pLang,
|
|
+ const char *z,
|
|
+ int n
|
|
+){
|
|
+ EditDist3FromString *pStr;
|
|
+ EditDist3Cost *p;
|
|
+ int i;
|
|
+
|
|
+ if( z==0 ) return 0;
|
|
+ if( n<0 ) n = (int)strlen(z);
|
|
+ pStr = sqlite3_malloc( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 );
|
|
+ if( pStr==0 ) return 0;
|
|
+ pStr->a = (EditDist3From*)&pStr[1];
|
|
+ memset(pStr->a, 0, sizeof(pStr->a[0])*n);
|
|
+ pStr->n = n;
|
|
+ pStr->z = (char*)&pStr->a[n];
|
|
+ memcpy(pStr->z, z, n+1);
|
|
+ if( n && z[n-1]=='*' ){
|
|
+ pStr->isPrefix = 1;
|
|
+ n--;
|
|
+ pStr->n--;
|
|
+ pStr->z[n] = 0;
|
|
+ }else{
|
|
+ pStr->isPrefix = 0;
|
|
+ }
|
|
+
|
|
+ for(i=0; i<n; i++){
|
|
+ EditDist3From *pFrom = &pStr->a[i];
|
|
+ memset(pFrom, 0, sizeof(*pFrom));
|
|
+ pFrom->nByte = utf8Len((unsigned char)z[i], n-i);
|
|
+ for(p=pLang->pCost; p; p=p->pNext){
|
|
+ EditDist3Cost **apNew;
|
|
+ if( i+p->nFrom>n ) continue;
|
|
+ if( matchFrom(p, z+i, n-i)==0 ) continue;
|
|
+ if( p->nTo==0 ){
|
|
+ apNew = sqlite3_realloc(pFrom->apDel,
|
|
+ sizeof(*apNew)*(pFrom->nDel+1));
|
|
+ if( apNew==0 ) break;
|
|
+ pFrom->apDel = apNew;
|
|
+ apNew[pFrom->nDel++] = p;
|
|
+ }else{
|
|
+ apNew = sqlite3_realloc(pFrom->apSubst,
|
|
+ sizeof(*apNew)*(pFrom->nSubst+1));
|
|
+ if( apNew==0 ) break;
|
|
+ pFrom->apSubst = apNew;
|
|
+ apNew[pFrom->nSubst++] = p;
|
|
+ }
|
|
+ }
|
|
+ if( p ){
|
|
+ editDist3FromStringDelete(pStr);
|
|
+ pStr = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return pStr;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Update entry m[i] such that it is the minimum of its current value
|
|
+** and m[j]+iCost.
|
|
+*/
|
|
+static void updateCost(
|
|
+ unsigned int *m,
|
|
+ int i,
|
|
+ int j,
|
|
+ int iCost
|
|
+){
|
|
+ unsigned int b;
|
|
+ assert( iCost>=0 );
|
|
+ assert( iCost<10000 );
|
|
+ b = m[j] + iCost;
|
|
+ if( b<m[i] ) m[i] = b;
|
|
+}
|
|
+
|
|
+/*
|
|
+** How much stack space (int bytes) to use for Wagner matrix in
|
|
+** editDist3Core(). If more space than this is required, the entire
|
|
+** matrix is taken from the heap. To reduce the load on the memory
|
|
+** allocator, make this value as large as practical for the
|
|
+** architecture in use.
|
|
+*/
|
|
+#ifndef SQLITE_SPELLFIX_STACKALLOC_SZ
|
|
+# define SQLITE_SPELLFIX_STACKALLOC_SZ (1024)
|
|
+#endif
|
|
+
|
|
+/* Compute the edit distance between two strings.
|
|
+**
|
|
+** If an error occurs, return a negative number which is the error code.
|
|
+**
|
|
+** If pnMatch is not NULL, then *pnMatch is set to the number of characters
|
|
+** (not bytes) in z2 that matched the search pattern in *pFrom. If pFrom does
|
|
+** not contain the pattern for a prefix-search, then this is always the number
|
|
+** of characters in z2. If pFrom does contain a prefix search pattern, then
|
|
+** it is the number of characters in the prefix of z2 that was deemed to
|
|
+** match pFrom.
|
|
+*/
|
|
+static int editDist3Core(
|
|
+ EditDist3FromString *pFrom, /* The FROM string */
|
|
+ const char *z2, /* The TO string */
|
|
+ int n2, /* Length of the TO string */
|
|
+ const EditDist3Lang *pLang, /* Edit weights for a particular language ID */
|
|
+ int *pnMatch /* OUT: Characters in matched prefix */
|
|
+){
|
|
+ int k, n;
|
|
+ int i1, b1;
|
|
+ int i2, b2;
|
|
+ EditDist3FromString f = *pFrom;
|
|
+ EditDist3To *a2;
|
|
+ unsigned int *m;
|
|
+ unsigned int *pToFree;
|
|
+ int szRow;
|
|
+ EditDist3Cost *p;
|
|
+ int res;
|
|
+ sqlite3_uint64 nByte;
|
|
+ unsigned int stackSpace[SQLITE_SPELLFIX_STACKALLOC_SZ/sizeof(unsigned int)];
|
|
+
|
|
+ /* allocate the Wagner matrix and the aTo[] array for the TO string */
|
|
+ n = (f.n+1)*(n2+1);
|
|
+ n = (n+1)&~1;
|
|
+ nByte = n*sizeof(m[0]) + sizeof(a2[0])*n2;
|
|
+ if( nByte<=sizeof(stackSpace) ){
|
|
+ m = stackSpace;
|
|
+ pToFree = 0;
|
|
+ }else{
|
|
+ m = pToFree = sqlite3_malloc( nByte );
|
|
+ if( m==0 ) return -1; /* Out of memory */
|
|
+ }
|
|
+ a2 = (EditDist3To*)&m[n];
|
|
+ memset(a2, 0, sizeof(a2[0])*n2);
|
|
+
|
|
+ /* Fill in the a1[] matrix for all characters of the TO string */
|
|
+ for(i2=0; i2<n2; i2++){
|
|
+ a2[i2].nByte = utf8Len((unsigned char)z2[i2], n2-i2);
|
|
+ for(p=pLang->pCost; p; p=p->pNext){
|
|
+ EditDist3Cost **apNew;
|
|
+ if( p->nFrom>0 ) break;
|
|
+ if( i2+p->nTo>n2 ) continue;
|
|
+ if( p->a[0]>z2[i2] ) break;
|
|
+ if( matchTo(p, z2+i2, n2-i2)==0 ) continue;
|
|
+ a2[i2].nIns++;
|
|
+ apNew = sqlite3_realloc(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns);
|
|
+ if( apNew==0 ){
|
|
+ res = -1; /* Out of memory */
|
|
+ goto editDist3Abort;
|
|
+ }
|
|
+ a2[i2].apIns = apNew;
|
|
+ a2[i2].apIns[a2[i2].nIns-1] = p;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Prepare to compute the minimum edit distance */
|
|
+ szRow = f.n+1;
|
|
+ memset(m, 0x01, (n2+1)*szRow*sizeof(m[0]));
|
|
+ m[0] = 0;
|
|
+
|
|
+ /* First fill in the top-row of the matrix with FROM deletion costs */
|
|
+ for(i1=0; i1<f.n; i1 += b1){
|
|
+ b1 = f.a[i1].nByte;
|
|
+ updateCost(m, i1+b1, i1, pLang->iDelCost);
|
|
+ for(k=0; k<f.a[i1].nDel; k++){
|
|
+ p = f.a[i1].apDel[k];
|
|
+ updateCost(m, i1+p->nFrom, i1, p->iCost);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Fill in all subsequent rows, top-to-bottom, left-to-right */
|
|
+ for(i2=0; i2<n2; i2 += b2){
|
|
+ int rx; /* Starting index for current row */
|
|
+ int rxp; /* Starting index for previous row */
|
|
+ b2 = a2[i2].nByte;
|
|
+ rx = szRow*(i2+b2);
|
|
+ rxp = szRow*i2;
|
|
+ updateCost(m, rx, rxp, pLang->iInsCost);
|
|
+ for(k=0; k<a2[i2].nIns; k++){
|
|
+ p = a2[i2].apIns[k];
|
|
+ updateCost(m, szRow*(i2+p->nTo), rxp, p->iCost);
|
|
+ }
|
|
+ for(i1=0; i1<f.n; i1+=b1){
|
|
+ int cx; /* Index of current cell */
|
|
+ int cxp; /* Index of cell immediately to the left */
|
|
+ int cxd; /* Index of cell to the left and one row above */
|
|
+ int cxu; /* Index of cell immediately above */
|
|
+ b1 = f.a[i1].nByte;
|
|
+ cxp = rx + i1;
|
|
+ cx = cxp + b1;
|
|
+ cxd = rxp + i1;
|
|
+ cxu = cxd + b1;
|
|
+ updateCost(m, cx, cxp, pLang->iDelCost);
|
|
+ for(k=0; k<f.a[i1].nDel; k++){
|
|
+ p = f.a[i1].apDel[k];
|
|
+ updateCost(m, cxp+p->nFrom, cxp, p->iCost);
|
|
+ }
|
|
+ updateCost(m, cx, cxu, pLang->iInsCost);
|
|
+ if( matchFromTo(&f, i1, z2+i2, n2-i2) ){
|
|
+ updateCost(m, cx, cxd, 0);
|
|
+ }
|
|
+ updateCost(m, cx, cxd, pLang->iSubCost);
|
|
+ for(k=0; k<f.a[i1].nSubst; k++){
|
|
+ p = f.a[i1].apSubst[k];
|
|
+ if( matchTo(p, z2+i2, n2-i2) ){
|
|
+ updateCost(m, cxd+p->nFrom+szRow*p->nTo, cxd, p->iCost);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+#if 0 /* Enable for debugging */
|
|
+ printf(" ^");
|
|
+ for(i1=0; i1<f.n; i1++) printf(" %c-%2x", f.z[i1], f.z[i1]&0xff);
|
|
+ printf("\n ^:");
|
|
+ for(i1=0; i1<szRow; i1++){
|
|
+ int v = m[i1];
|
|
+ if( v>9999 ) printf(" ****");
|
|
+ else printf(" %4d", v);
|
|
+ }
|
|
+ printf("\n");
|
|
+ for(i2=0; i2<n2; i2++){
|
|
+ printf("%c-%02x:", z2[i2], z2[i2]&0xff);
|
|
+ for(i1=0; i1<szRow; i1++){
|
|
+ int v = m[(i2+1)*szRow+i1];
|
|
+ if( v>9999 ) printf(" ****");
|
|
+ else printf(" %4d", v);
|
|
+ }
|
|
+ printf("\n");
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* Free memory allocations and return the result */
|
|
+ res = (int)m[szRow*(n2+1)-1];
|
|
+ n = n2;
|
|
+ if( f.isPrefix ){
|
|
+ for(i2=1; i2<=n2; i2++){
|
|
+ int b = m[szRow*i2-1];
|
|
+ if( b<=res ){
|
|
+ res = b;
|
|
+ n = i2 - 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( pnMatch ){
|
|
+ int nExtra = 0;
|
|
+ for(k=0; k<n; k++){
|
|
+ if( (z2[k] & 0xc0)==0x80 ) nExtra++;
|
|
+ }
|
|
+ *pnMatch = n - nExtra;
|
|
+ }
|
|
+
|
|
+editDist3Abort:
|
|
+ for(i2=0; i2<n2; i2++) sqlite3_free(a2[i2].apIns);
|
|
+ sqlite3_free(pToFree);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Get an appropriate EditDist3Lang object.
|
|
+*/
|
|
+static const EditDist3Lang *editDist3FindLang(
|
|
+ EditDist3Config *pConfig,
|
|
+ int iLang
|
|
+){
|
|
+ int i;
|
|
+ for(i=0; i<pConfig->nLang; i++){
|
|
+ if( pConfig->a[i].iLang==iLang ) return &pConfig->a[i];
|
|
+ }
|
|
+ return &editDist3Lang;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Function: editdist3(A,B,iLang)
|
|
+** editdist3(tablename)
|
|
+**
|
|
+** Return the cost of transforming string A into string B using edit
|
|
+** weights for iLang.
|
|
+**
|
|
+** The second form loads edit weights into memory from a table.
|
|
+*/
|
|
+static void editDist3SqlFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ EditDist3Config *pConfig = (EditDist3Config*)sqlite3_user_data(context);
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ int rc;
|
|
+ if( argc==1 ){
|
|
+ const char *zTable = (const char*)sqlite3_value_text(argv[0]);
|
|
+ rc = editDist3ConfigLoad(pConfig, db, zTable);
|
|
+ if( rc ) sqlite3_result_error_code(context, rc);
|
|
+ }else{
|
|
+ const char *zA = (const char*)sqlite3_value_text(argv[0]);
|
|
+ const char *zB = (const char*)sqlite3_value_text(argv[1]);
|
|
+ int nA = sqlite3_value_bytes(argv[0]);
|
|
+ int nB = sqlite3_value_bytes(argv[1]);
|
|
+ int iLang = argc==3 ? sqlite3_value_int(argv[2]) : 0;
|
|
+ const EditDist3Lang *pLang = editDist3FindLang(pConfig, iLang);
|
|
+ EditDist3FromString *pFrom;
|
|
+ int dist;
|
|
+
|
|
+ pFrom = editDist3FromStringNew(pLang, zA, nA);
|
|
+ if( pFrom==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ dist = editDist3Core(pFrom, zB, nB, pLang, 0);
|
|
+ editDist3FromStringDelete(pFrom);
|
|
+ if( dist==(-1) ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_int(context, dist);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register the editDist3 function with SQLite
|
|
+*/
|
|
+static int editDist3Install(sqlite3 *db){
|
|
+ int rc;
|
|
+ EditDist3Config *pConfig = sqlite3_malloc( sizeof(*pConfig) );
|
|
+ if( pConfig==0 ) return SQLITE_NOMEM;
|
|
+ memset(pConfig, 0, sizeof(*pConfig));
|
|
+ rc = sqlite3_create_function_v2(db, "editdist3",
|
|
+ 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig,
|
|
+ editDist3SqlFunc, 0, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function_v2(db, "editdist3",
|
|
+ 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig,
|
|
+ editDist3SqlFunc, 0, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function_v2(db, "editdist3",
|
|
+ 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig,
|
|
+ editDist3SqlFunc, 0, 0, editDist3ConfigDelete);
|
|
+ }else{
|
|
+ sqlite3_free(pConfig);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+/* End configurable cost unicode edit distance routines
|
|
+******************************************************************************
|
|
+******************************************************************************
|
|
+** Begin transliterate unicode-to-ascii implementation
|
|
+*/
|
|
+
|
|
+#if !SQLITE_AMALGAMATION
|
|
+/*
|
|
+** This lookup table is used to help decode the first byte of
|
|
+** a multi-byte UTF8 character.
|
|
+*/
|
|
+static const unsigned char sqlite3Utf8Trans1[] = {
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
|
+};
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Return the value of the first UTF-8 character in the string.
|
|
+*/
|
|
+static int utf8Read(const unsigned char *z, int n, int *pSize){
|
|
+ int c, i;
|
|
+
|
|
+ /* All callers to this routine (in the current implementation)
|
|
+ ** always have n>0. */
|
|
+ if( NEVER(n==0) ){
|
|
+ c = i = 0;
|
|
+ }else{
|
|
+ c = z[0];
|
|
+ i = 1;
|
|
+ if( c>=0xc0 ){
|
|
+ c = sqlite3Utf8Trans1[c-0xc0];
|
|
+ while( i<n && (z[i] & 0xc0)==0x80 ){
|
|
+ c = (c<<6) + (0x3f & z[i++]);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ *pSize = i;
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the number of characters in the utf-8 string in the nIn byte
|
|
+** buffer pointed to by zIn.
|
|
+*/
|
|
+static int utf8Charlen(const char *zIn, int nIn){
|
|
+ int i;
|
|
+ int nChar = 0;
|
|
+ for(i=0; i<nIn; nChar++){
|
|
+ int sz;
|
|
+ utf8Read((const unsigned char *)&zIn[i], nIn-i, &sz);
|
|
+ i += sz;
|
|
+ }
|
|
+ return nChar;
|
|
+}
|
|
+
|
|
+typedef struct Transliteration Transliteration;
|
|
+struct Transliteration {
|
|
+ unsigned short int cFrom;
|
|
+ unsigned char cTo0, cTo1, cTo2, cTo3;
|
|
+#ifdef SQLITE_SPELLFIX_5BYTE_MAPPINGS
|
|
+ unsigned char cTo4;
|
|
+#endif
|
|
+};
|
|
+
|
|
+/*
|
|
+** Table of translations from unicode characters into ASCII.
|
|
+*/
|
|
+static const Transliteration translit[] = {
|
|
+ { 0x00A0, 0x20, 0x00, 0x00, 0x00 }, /* Â to */
|
|
+ { 0x00B5, 0x75, 0x00, 0x00, 0x00 }, /* µ to u */
|
|
+ { 0x00C0, 0x41, 0x00, 0x00, 0x00 }, /* À to A */
|
|
+ { 0x00C1, 0x41, 0x00, 0x00, 0x00 }, /* � to A */
|
|
+ { 0x00C2, 0x41, 0x00, 0x00, 0x00 }, /* Â to A */
|
|
+ { 0x00C3, 0x41, 0x00, 0x00, 0x00 }, /* Ã to A */
|
|
+ { 0x00C4, 0x41, 0x65, 0x00, 0x00 }, /* Ä to Ae */
|
|
+ { 0x00C5, 0x41, 0x61, 0x00, 0x00 }, /* Ã… to Aa */
|
|
+ { 0x00C6, 0x41, 0x45, 0x00, 0x00 }, /* Æ to AE */
|
|
+ { 0x00C7, 0x43, 0x00, 0x00, 0x00 }, /* Ç to C */
|
|
+ { 0x00C8, 0x45, 0x00, 0x00, 0x00 }, /* È to E */
|
|
+ { 0x00C9, 0x45, 0x00, 0x00, 0x00 }, /* É to E */
|
|
+ { 0x00CA, 0x45, 0x00, 0x00, 0x00 }, /* Ê to E */
|
|
+ { 0x00CB, 0x45, 0x00, 0x00, 0x00 }, /* Ë to E */
|
|
+ { 0x00CC, 0x49, 0x00, 0x00, 0x00 }, /* Ì to I */
|
|
+ { 0x00CD, 0x49, 0x00, 0x00, 0x00 }, /* � to I */
|
|
+ { 0x00CE, 0x49, 0x00, 0x00, 0x00 }, /* ÃŽ to I */
|
|
+ { 0x00CF, 0x49, 0x00, 0x00, 0x00 }, /* � to I */
|
|
+ { 0x00D0, 0x44, 0x00, 0x00, 0x00 }, /* � to D */
|
|
+ { 0x00D1, 0x4E, 0x00, 0x00, 0x00 }, /* Ñ to N */
|
|
+ { 0x00D2, 0x4F, 0x00, 0x00, 0x00 }, /* Ã’ to O */
|
|
+ { 0x00D3, 0x4F, 0x00, 0x00, 0x00 }, /* Ó to O */
|
|
+ { 0x00D4, 0x4F, 0x00, 0x00, 0x00 }, /* Ô to O */
|
|
+ { 0x00D5, 0x4F, 0x00, 0x00, 0x00 }, /* Õ to O */
|
|
+ { 0x00D6, 0x4F, 0x65, 0x00, 0x00 }, /* Ö to Oe */
|
|
+ { 0x00D7, 0x78, 0x00, 0x00, 0x00 }, /* × to x */
|
|
+ { 0x00D8, 0x4F, 0x00, 0x00, 0x00 }, /* Ø to O */
|
|
+ { 0x00D9, 0x55, 0x00, 0x00, 0x00 }, /* Ù to U */
|
|
+ { 0x00DA, 0x55, 0x00, 0x00, 0x00 }, /* Ú to U */
|
|
+ { 0x00DB, 0x55, 0x00, 0x00, 0x00 }, /* Û to U */
|
|
+ { 0x00DC, 0x55, 0x65, 0x00, 0x00 }, /* Ü to Ue */
|
|
+ { 0x00DD, 0x59, 0x00, 0x00, 0x00 }, /* � to Y */
|
|
+ { 0x00DE, 0x54, 0x68, 0x00, 0x00 }, /* Þ to Th */
|
|
+ { 0x00DF, 0x73, 0x73, 0x00, 0x00 }, /* ß to ss */
|
|
+ { 0x00E0, 0x61, 0x00, 0x00, 0x00 }, /* Ã to a */
|
|
+ { 0x00E1, 0x61, 0x00, 0x00, 0x00 }, /* á to a */
|
|
+ { 0x00E2, 0x61, 0x00, 0x00, 0x00 }, /* â to a */
|
|
+ { 0x00E3, 0x61, 0x00, 0x00, 0x00 }, /* ã to a */
|
|
+ { 0x00E4, 0x61, 0x65, 0x00, 0x00 }, /* ä to ae */
|
|
+ { 0x00E5, 0x61, 0x61, 0x00, 0x00 }, /* å to aa */
|
|
+ { 0x00E6, 0x61, 0x65, 0x00, 0x00 }, /* æ to ae */
|
|
+ { 0x00E7, 0x63, 0x00, 0x00, 0x00 }, /* ç to c */
|
|
+ { 0x00E8, 0x65, 0x00, 0x00, 0x00 }, /* è to e */
|
|
+ { 0x00E9, 0x65, 0x00, 0x00, 0x00 }, /* é to e */
|
|
+ { 0x00EA, 0x65, 0x00, 0x00, 0x00 }, /* ê to e */
|
|
+ { 0x00EB, 0x65, 0x00, 0x00, 0x00 }, /* ë to e */
|
|
+ { 0x00EC, 0x69, 0x00, 0x00, 0x00 }, /* ì to i */
|
|
+ { 0x00ED, 0x69, 0x00, 0x00, 0x00 }, /* Ã to i */
|
|
+ { 0x00EE, 0x69, 0x00, 0x00, 0x00 }, /* î to i */
|
|
+ { 0x00EF, 0x69, 0x00, 0x00, 0x00 }, /* ï to i */
|
|
+ { 0x00F0, 0x64, 0x00, 0x00, 0x00 }, /* ð to d */
|
|
+ { 0x00F1, 0x6E, 0x00, 0x00, 0x00 }, /* ñ to n */
|
|
+ { 0x00F2, 0x6F, 0x00, 0x00, 0x00 }, /* ò to o */
|
|
+ { 0x00F3, 0x6F, 0x00, 0x00, 0x00 }, /* ó to o */
|
|
+ { 0x00F4, 0x6F, 0x00, 0x00, 0x00 }, /* ô to o */
|
|
+ { 0x00F5, 0x6F, 0x00, 0x00, 0x00 }, /* õ to o */
|
|
+ { 0x00F6, 0x6F, 0x65, 0x00, 0x00 }, /* ö to oe */
|
|
+ { 0x00F7, 0x3A, 0x00, 0x00, 0x00 }, /* ÷ to : */
|
|
+ { 0x00F8, 0x6F, 0x00, 0x00, 0x00 }, /* ø to o */
|
|
+ { 0x00F9, 0x75, 0x00, 0x00, 0x00 }, /* ù to u */
|
|
+ { 0x00FA, 0x75, 0x00, 0x00, 0x00 }, /* ú to u */
|
|
+ { 0x00FB, 0x75, 0x00, 0x00, 0x00 }, /* û to u */
|
|
+ { 0x00FC, 0x75, 0x65, 0x00, 0x00 }, /* ü to ue */
|
|
+ { 0x00FD, 0x79, 0x00, 0x00, 0x00 }, /* ý to y */
|
|
+ { 0x00FE, 0x74, 0x68, 0x00, 0x00 }, /* þ to th */
|
|
+ { 0x00FF, 0x79, 0x00, 0x00, 0x00 }, /* ÿ to y */
|
|
+ { 0x0100, 0x41, 0x00, 0x00, 0x00 }, /* Ā to A */
|
|
+ { 0x0101, 0x61, 0x00, 0x00, 0x00 }, /* � to a */
|
|
+ { 0x0102, 0x41, 0x00, 0x00, 0x00 }, /* Ä‚ to A */
|
|
+ { 0x0103, 0x61, 0x00, 0x00, 0x00 }, /* ă to a */
|
|
+ { 0x0104, 0x41, 0x00, 0x00, 0x00 }, /* Ä„ to A */
|
|
+ { 0x0105, 0x61, 0x00, 0x00, 0x00 }, /* Ä… to a */
|
|
+ { 0x0106, 0x43, 0x00, 0x00, 0x00 }, /* Ć to C */
|
|
+ { 0x0107, 0x63, 0x00, 0x00, 0x00 }, /* ć to c */
|
|
+ { 0x0108, 0x43, 0x68, 0x00, 0x00 }, /* Ĉ to Ch */
|
|
+ { 0x0109, 0x63, 0x68, 0x00, 0x00 }, /* ĉ to ch */
|
|
+ { 0x010A, 0x43, 0x00, 0x00, 0x00 }, /* ÄŠ to C */
|
|
+ { 0x010B, 0x63, 0x00, 0x00, 0x00 }, /* Ä‹ to c */
|
|
+ { 0x010C, 0x43, 0x00, 0x00, 0x00 }, /* Č to C */
|
|
+ { 0x010D, 0x63, 0x00, 0x00, 0x00 }, /* � to c */
|
|
+ { 0x010E, 0x44, 0x00, 0x00, 0x00 }, /* ÄŽ to D */
|
|
+ { 0x010F, 0x64, 0x00, 0x00, 0x00 }, /* � to d */
|
|
+ { 0x0110, 0x44, 0x00, 0x00, 0x00 }, /* � to D */
|
|
+ { 0x0111, 0x64, 0x00, 0x00, 0x00 }, /* Ä‘ to d */
|
|
+ { 0x0112, 0x45, 0x00, 0x00, 0x00 }, /* Ä’ to E */
|
|
+ { 0x0113, 0x65, 0x00, 0x00, 0x00 }, /* Ä“ to e */
|
|
+ { 0x0114, 0x45, 0x00, 0x00, 0x00 }, /* Ä” to E */
|
|
+ { 0x0115, 0x65, 0x00, 0x00, 0x00 }, /* Ä• to e */
|
|
+ { 0x0116, 0x45, 0x00, 0x00, 0x00 }, /* Ä– to E */
|
|
+ { 0x0117, 0x65, 0x00, 0x00, 0x00 }, /* Ä— to e */
|
|
+ { 0x0118, 0x45, 0x00, 0x00, 0x00 }, /* Ę to E */
|
|
+ { 0x0119, 0x65, 0x00, 0x00, 0x00 }, /* Ä™ to e */
|
|
+ { 0x011A, 0x45, 0x00, 0x00, 0x00 }, /* Äš to E */
|
|
+ { 0x011B, 0x65, 0x00, 0x00, 0x00 }, /* Ä› to e */
|
|
+ { 0x011C, 0x47, 0x68, 0x00, 0x00 }, /* Ĝ to Gh */
|
|
+ { 0x011D, 0x67, 0x68, 0x00, 0x00 }, /* � to gh */
|
|
+ { 0x011E, 0x47, 0x00, 0x00, 0x00 }, /* Äž to G */
|
|
+ { 0x011F, 0x67, 0x00, 0x00, 0x00 }, /* ÄŸ to g */
|
|
+ { 0x0120, 0x47, 0x00, 0x00, 0x00 }, /* Ä to G */
|
|
+ { 0x0121, 0x67, 0x00, 0x00, 0x00 }, /* Ä¡ to g */
|
|
+ { 0x0122, 0x47, 0x00, 0x00, 0x00 }, /* Ģ to G */
|
|
+ { 0x0123, 0x67, 0x00, 0x00, 0x00 }, /* ģ to g */
|
|
+ { 0x0124, 0x48, 0x68, 0x00, 0x00 }, /* Ĥ to Hh */
|
|
+ { 0x0125, 0x68, 0x68, 0x00, 0x00 }, /* ĥ to hh */
|
|
+ { 0x0126, 0x48, 0x00, 0x00, 0x00 }, /* Ħ to H */
|
|
+ { 0x0127, 0x68, 0x00, 0x00, 0x00 }, /* ħ to h */
|
|
+ { 0x0128, 0x49, 0x00, 0x00, 0x00 }, /* Ĩ to I */
|
|
+ { 0x0129, 0x69, 0x00, 0x00, 0x00 }, /* Ä© to i */
|
|
+ { 0x012A, 0x49, 0x00, 0x00, 0x00 }, /* Ī to I */
|
|
+ { 0x012B, 0x69, 0x00, 0x00, 0x00 }, /* Ä« to i */
|
|
+ { 0x012C, 0x49, 0x00, 0x00, 0x00 }, /* Ĭ to I */
|
|
+ { 0x012D, 0x69, 0x00, 0x00, 0x00 }, /* Ä to i */
|
|
+ { 0x012E, 0x49, 0x00, 0x00, 0x00 }, /* Ä® to I */
|
|
+ { 0x012F, 0x69, 0x00, 0x00, 0x00 }, /* į to i */
|
|
+ { 0x0130, 0x49, 0x00, 0x00, 0x00 }, /* İ to I */
|
|
+ { 0x0131, 0x69, 0x00, 0x00, 0x00 }, /* ı to i */
|
|
+ { 0x0132, 0x49, 0x4A, 0x00, 0x00 }, /* IJ to IJ */
|
|
+ { 0x0133, 0x69, 0x6A, 0x00, 0x00 }, /* ij to ij */
|
|
+ { 0x0134, 0x4A, 0x68, 0x00, 0x00 }, /* Ä´ to Jh */
|
|
+ { 0x0135, 0x6A, 0x68, 0x00, 0x00 }, /* ĵ to jh */
|
|
+ { 0x0136, 0x4B, 0x00, 0x00, 0x00 }, /* Ķ to K */
|
|
+ { 0x0137, 0x6B, 0x00, 0x00, 0x00 }, /* Ä· to k */
|
|
+ { 0x0138, 0x6B, 0x00, 0x00, 0x00 }, /* ĸ to k */
|
|
+ { 0x0139, 0x4C, 0x00, 0x00, 0x00 }, /* Ĺ to L */
|
|
+ { 0x013A, 0x6C, 0x00, 0x00, 0x00 }, /* ĺ to l */
|
|
+ { 0x013B, 0x4C, 0x00, 0x00, 0x00 }, /* Ä» to L */
|
|
+ { 0x013C, 0x6C, 0x00, 0x00, 0x00 }, /* ļ to l */
|
|
+ { 0x013D, 0x4C, 0x00, 0x00, 0x00 }, /* Ľ to L */
|
|
+ { 0x013E, 0x6C, 0x00, 0x00, 0x00 }, /* ľ to l */
|
|
+ { 0x013F, 0x4C, 0x2E, 0x00, 0x00 }, /* Ä¿ to L. */
|
|
+ { 0x0140, 0x6C, 0x2E, 0x00, 0x00 }, /* ŀ to l. */
|
|
+ { 0x0141, 0x4C, 0x00, 0x00, 0x00 }, /* � to L */
|
|
+ { 0x0142, 0x6C, 0x00, 0x00, 0x00 }, /* Å‚ to l */
|
|
+ { 0x0143, 0x4E, 0x00, 0x00, 0x00 }, /* Ń to N */
|
|
+ { 0x0144, 0x6E, 0x00, 0x00, 0x00 }, /* Å„ to n */
|
|
+ { 0x0145, 0x4E, 0x00, 0x00, 0x00 }, /* Å… to N */
|
|
+ { 0x0146, 0x6E, 0x00, 0x00, 0x00 }, /* ņ to n */
|
|
+ { 0x0147, 0x4E, 0x00, 0x00, 0x00 }, /* Ň to N */
|
|
+ { 0x0148, 0x6E, 0x00, 0x00, 0x00 }, /* ň to n */
|
|
+ { 0x0149, 0x27, 0x6E, 0x00, 0x00 }, /* ʼn to 'n */
|
|
+ { 0x014A, 0x4E, 0x47, 0x00, 0x00 }, /* ÅŠ to NG */
|
|
+ { 0x014B, 0x6E, 0x67, 0x00, 0x00 }, /* Å‹ to ng */
|
|
+ { 0x014C, 0x4F, 0x00, 0x00, 0x00 }, /* Ō to O */
|
|
+ { 0x014D, 0x6F, 0x00, 0x00, 0x00 }, /* � to o */
|
|
+ { 0x014E, 0x4F, 0x00, 0x00, 0x00 }, /* ÅŽ to O */
|
|
+ { 0x014F, 0x6F, 0x00, 0x00, 0x00 }, /* � to o */
|
|
+ { 0x0150, 0x4F, 0x00, 0x00, 0x00 }, /* � to O */
|
|
+ { 0x0151, 0x6F, 0x00, 0x00, 0x00 }, /* Å‘ to o */
|
|
+ { 0x0152, 0x4F, 0x45, 0x00, 0x00 }, /* Å’ to OE */
|
|
+ { 0x0153, 0x6F, 0x65, 0x00, 0x00 }, /* Å“ to oe */
|
|
+ { 0x0154, 0x52, 0x00, 0x00, 0x00 }, /* Å” to R */
|
|
+ { 0x0155, 0x72, 0x00, 0x00, 0x00 }, /* Å• to r */
|
|
+ { 0x0156, 0x52, 0x00, 0x00, 0x00 }, /* Å– to R */
|
|
+ { 0x0157, 0x72, 0x00, 0x00, 0x00 }, /* Å— to r */
|
|
+ { 0x0158, 0x52, 0x00, 0x00, 0x00 }, /* Ř to R */
|
|
+ { 0x0159, 0x72, 0x00, 0x00, 0x00 }, /* Å™ to r */
|
|
+ { 0x015A, 0x53, 0x00, 0x00, 0x00 }, /* Åš to S */
|
|
+ { 0x015B, 0x73, 0x00, 0x00, 0x00 }, /* Å› to s */
|
|
+ { 0x015C, 0x53, 0x68, 0x00, 0x00 }, /* Ŝ to Sh */
|
|
+ { 0x015D, 0x73, 0x68, 0x00, 0x00 }, /* � to sh */
|
|
+ { 0x015E, 0x53, 0x00, 0x00, 0x00 }, /* Åž to S */
|
|
+ { 0x015F, 0x73, 0x00, 0x00, 0x00 }, /* ÅŸ to s */
|
|
+ { 0x0160, 0x53, 0x00, 0x00, 0x00 }, /* Å to S */
|
|
+ { 0x0161, 0x73, 0x00, 0x00, 0x00 }, /* Å¡ to s */
|
|
+ { 0x0162, 0x54, 0x00, 0x00, 0x00 }, /* Ţ to T */
|
|
+ { 0x0163, 0x74, 0x00, 0x00, 0x00 }, /* ţ to t */
|
|
+ { 0x0164, 0x54, 0x00, 0x00, 0x00 }, /* Ť to T */
|
|
+ { 0x0165, 0x74, 0x00, 0x00, 0x00 }, /* ť to t */
|
|
+ { 0x0166, 0x54, 0x00, 0x00, 0x00 }, /* Ŧ to T */
|
|
+ { 0x0167, 0x74, 0x00, 0x00, 0x00 }, /* ŧ to t */
|
|
+ { 0x0168, 0x55, 0x00, 0x00, 0x00 }, /* Ũ to U */
|
|
+ { 0x0169, 0x75, 0x00, 0x00, 0x00 }, /* Å© to u */
|
|
+ { 0x016A, 0x55, 0x00, 0x00, 0x00 }, /* Ū to U */
|
|
+ { 0x016B, 0x75, 0x00, 0x00, 0x00 }, /* Å« to u */
|
|
+ { 0x016C, 0x55, 0x00, 0x00, 0x00 }, /* Ŭ to U */
|
|
+ { 0x016D, 0x75, 0x00, 0x00, 0x00 }, /* Å to u */
|
|
+ { 0x016E, 0x55, 0x00, 0x00, 0x00 }, /* Å® to U */
|
|
+ { 0x016F, 0x75, 0x00, 0x00, 0x00 }, /* ů to u */
|
|
+ { 0x0170, 0x55, 0x00, 0x00, 0x00 }, /* Ű to U */
|
|
+ { 0x0171, 0x75, 0x00, 0x00, 0x00 }, /* ű to u */
|
|
+ { 0x0172, 0x55, 0x00, 0x00, 0x00 }, /* Ų to U */
|
|
+ { 0x0173, 0x75, 0x00, 0x00, 0x00 }, /* ų to u */
|
|
+ { 0x0174, 0x57, 0x00, 0x00, 0x00 }, /* Å´ to W */
|
|
+ { 0x0175, 0x77, 0x00, 0x00, 0x00 }, /* ŵ to w */
|
|
+ { 0x0176, 0x59, 0x00, 0x00, 0x00 }, /* Ŷ to Y */
|
|
+ { 0x0177, 0x79, 0x00, 0x00, 0x00 }, /* Å· to y */
|
|
+ { 0x0178, 0x59, 0x00, 0x00, 0x00 }, /* Ÿ to Y */
|
|
+ { 0x0179, 0x5A, 0x00, 0x00, 0x00 }, /* Ź to Z */
|
|
+ { 0x017A, 0x7A, 0x00, 0x00, 0x00 }, /* ź to z */
|
|
+ { 0x017B, 0x5A, 0x00, 0x00, 0x00 }, /* Å» to Z */
|
|
+ { 0x017C, 0x7A, 0x00, 0x00, 0x00 }, /* ż to z */
|
|
+ { 0x017D, 0x5A, 0x00, 0x00, 0x00 }, /* Ž to Z */
|
|
+ { 0x017E, 0x7A, 0x00, 0x00, 0x00 }, /* ž to z */
|
|
+ { 0x017F, 0x73, 0x00, 0x00, 0x00 }, /* Å¿ to s */
|
|
+ { 0x0192, 0x66, 0x00, 0x00, 0x00 }, /* Æ’ to f */
|
|
+ { 0x0218, 0x53, 0x00, 0x00, 0x00 }, /* Ș to S */
|
|
+ { 0x0219, 0x73, 0x00, 0x00, 0x00 }, /* È™ to s */
|
|
+ { 0x021A, 0x54, 0x00, 0x00, 0x00 }, /* Èš to T */
|
|
+ { 0x021B, 0x74, 0x00, 0x00, 0x00 }, /* È› to t */
|
|
+ { 0x0386, 0x41, 0x00, 0x00, 0x00 }, /* Ά to A */
|
|
+ { 0x0388, 0x45, 0x00, 0x00, 0x00 }, /* Έ to E */
|
|
+ { 0x0389, 0x49, 0x00, 0x00, 0x00 }, /* Ή to I */
|
|
+ { 0x038A, 0x49, 0x00, 0x00, 0x00 }, /* Ί to I */
|
|
+ { 0x038C, 0x4f, 0x00, 0x00, 0x00 }, /* Ό to O */
|
|
+ { 0x038E, 0x59, 0x00, 0x00, 0x00 }, /* ÎŽ to Y */
|
|
+ { 0x038F, 0x4f, 0x00, 0x00, 0x00 }, /* � to O */
|
|
+ { 0x0390, 0x69, 0x00, 0x00, 0x00 }, /* � to i */
|
|
+ { 0x0391, 0x41, 0x00, 0x00, 0x00 }, /* Α to A */
|
|
+ { 0x0392, 0x42, 0x00, 0x00, 0x00 }, /* Î’ to B */
|
|
+ { 0x0393, 0x47, 0x00, 0x00, 0x00 }, /* Γ to G */
|
|
+ { 0x0394, 0x44, 0x00, 0x00, 0x00 }, /* Δ to D */
|
|
+ { 0x0395, 0x45, 0x00, 0x00, 0x00 }, /* Ε to E */
|
|
+ { 0x0396, 0x5a, 0x00, 0x00, 0x00 }, /* Ζ to Z */
|
|
+ { 0x0397, 0x49, 0x00, 0x00, 0x00 }, /* Η to I */
|
|
+ { 0x0398, 0x54, 0x68, 0x00, 0x00 }, /* Θ to Th */
|
|
+ { 0x0399, 0x49, 0x00, 0x00, 0x00 }, /* Ι to I */
|
|
+ { 0x039A, 0x4b, 0x00, 0x00, 0x00 }, /* Κ to K */
|
|
+ { 0x039B, 0x4c, 0x00, 0x00, 0x00 }, /* Λ to L */
|
|
+ { 0x039C, 0x4d, 0x00, 0x00, 0x00 }, /* Μ to M */
|
|
+ { 0x039D, 0x4e, 0x00, 0x00, 0x00 }, /* � to N */
|
|
+ { 0x039E, 0x58, 0x00, 0x00, 0x00 }, /* Ξ to X */
|
|
+ { 0x039F, 0x4f, 0x00, 0x00, 0x00 }, /* Ο to O */
|
|
+ { 0x03A0, 0x50, 0x00, 0x00, 0x00 }, /* Î to P */
|
|
+ { 0x03A1, 0x52, 0x00, 0x00, 0x00 }, /* Ρ to R */
|
|
+ { 0x03A3, 0x53, 0x00, 0x00, 0x00 }, /* Σ to S */
|
|
+ { 0x03A4, 0x54, 0x00, 0x00, 0x00 }, /* Τ to T */
|
|
+ { 0x03A5, 0x59, 0x00, 0x00, 0x00 }, /* Υ to Y */
|
|
+ { 0x03A6, 0x46, 0x00, 0x00, 0x00 }, /* Φ to F */
|
|
+ { 0x03A7, 0x43, 0x68, 0x00, 0x00 }, /* Χ to Ch */
|
|
+ { 0x03A8, 0x50, 0x73, 0x00, 0x00 }, /* Ψ to Ps */
|
|
+ { 0x03A9, 0x4f, 0x00, 0x00, 0x00 }, /* Ω to O */
|
|
+ { 0x03AA, 0x49, 0x00, 0x00, 0x00 }, /* Ϊ to I */
|
|
+ { 0x03AB, 0x59, 0x00, 0x00, 0x00 }, /* Ϋ to Y */
|
|
+ { 0x03AC, 0x61, 0x00, 0x00, 0x00 }, /* ά to a */
|
|
+ { 0x03AD, 0x65, 0x00, 0x00, 0x00 }, /* Î to e */
|
|
+ { 0x03AE, 0x69, 0x00, 0x00, 0x00 }, /* ή to i */
|
|
+ { 0x03AF, 0x69, 0x00, 0x00, 0x00 }, /* ί to i */
|
|
+ { 0x03B1, 0x61, 0x00, 0x00, 0x00 }, /* α to a */
|
|
+ { 0x03B2, 0x62, 0x00, 0x00, 0x00 }, /* β to b */
|
|
+ { 0x03B3, 0x67, 0x00, 0x00, 0x00 }, /* γ to g */
|
|
+ { 0x03B4, 0x64, 0x00, 0x00, 0x00 }, /* δ to d */
|
|
+ { 0x03B5, 0x65, 0x00, 0x00, 0x00 }, /* ε to e */
|
|
+ { 0x03B6, 0x7a, 0x00, 0x00, 0x00 }, /* ζ to z */
|
|
+ { 0x03B7, 0x69, 0x00, 0x00, 0x00 }, /* η to i */
|
|
+ { 0x03B8, 0x74, 0x68, 0x00, 0x00 }, /* θ to th */
|
|
+ { 0x03B9, 0x69, 0x00, 0x00, 0x00 }, /* ι to i */
|
|
+ { 0x03BA, 0x6b, 0x00, 0x00, 0x00 }, /* κ to k */
|
|
+ { 0x03BB, 0x6c, 0x00, 0x00, 0x00 }, /* λ to l */
|
|
+ { 0x03BC, 0x6d, 0x00, 0x00, 0x00 }, /* μ to m */
|
|
+ { 0x03BD, 0x6e, 0x00, 0x00, 0x00 }, /* ν to n */
|
|
+ { 0x03BE, 0x78, 0x00, 0x00, 0x00 }, /* ξ to x */
|
|
+ { 0x03BF, 0x6f, 0x00, 0x00, 0x00 }, /* ο to o */
|
|
+ { 0x03C0, 0x70, 0x00, 0x00, 0x00 }, /* π to p */
|
|
+ { 0x03C1, 0x72, 0x00, 0x00, 0x00 }, /* � to r */
|
|
+ { 0x03C3, 0x73, 0x00, 0x00, 0x00 }, /* σ to s */
|
|
+ { 0x03C4, 0x74, 0x00, 0x00, 0x00 }, /* Ï„ to t */
|
|
+ { 0x03C5, 0x79, 0x00, 0x00, 0x00 }, /* Ï… to y */
|
|
+ { 0x03C6, 0x66, 0x00, 0x00, 0x00 }, /* φ to f */
|
|
+ { 0x03C7, 0x63, 0x68, 0x00, 0x00 }, /* χ to ch */
|
|
+ { 0x03C8, 0x70, 0x73, 0x00, 0x00 }, /* ψ to ps */
|
|
+ { 0x03C9, 0x6f, 0x00, 0x00, 0x00 }, /* ω to o */
|
|
+ { 0x03CA, 0x69, 0x00, 0x00, 0x00 }, /* ÏŠ to i */
|
|
+ { 0x03CB, 0x79, 0x00, 0x00, 0x00 }, /* Ï‹ to y */
|
|
+ { 0x03CC, 0x6f, 0x00, 0x00, 0x00 }, /* ό to o */
|
|
+ { 0x03CD, 0x79, 0x00, 0x00, 0x00 }, /* � to y */
|
|
+ { 0x03CE, 0x69, 0x00, 0x00, 0x00 }, /* ÏŽ to i */
|
|
+ { 0x0400, 0x45, 0x00, 0x00, 0x00 }, /* Ѐ to E */
|
|
+ { 0x0401, 0x45, 0x00, 0x00, 0x00 }, /* � to E */
|
|
+ { 0x0402, 0x44, 0x00, 0x00, 0x00 }, /* Ђ to D */
|
|
+ { 0x0403, 0x47, 0x00, 0x00, 0x00 }, /* Ѓ to G */
|
|
+ { 0x0404, 0x45, 0x00, 0x00, 0x00 }, /* Є to E */
|
|
+ { 0x0405, 0x5a, 0x00, 0x00, 0x00 }, /* Ð… to Z */
|
|
+ { 0x0406, 0x49, 0x00, 0x00, 0x00 }, /* І to I */
|
|
+ { 0x0407, 0x49, 0x00, 0x00, 0x00 }, /* Ї to I */
|
|
+ { 0x0408, 0x4a, 0x00, 0x00, 0x00 }, /* Ј to J */
|
|
+ { 0x0409, 0x49, 0x00, 0x00, 0x00 }, /* Љ to I */
|
|
+ { 0x040A, 0x4e, 0x00, 0x00, 0x00 }, /* Њ to N */
|
|
+ { 0x040B, 0x44, 0x00, 0x00, 0x00 }, /* Ћ to D */
|
|
+ { 0x040C, 0x4b, 0x00, 0x00, 0x00 }, /* Ќ to K */
|
|
+ { 0x040D, 0x49, 0x00, 0x00, 0x00 }, /* � to I */
|
|
+ { 0x040E, 0x55, 0x00, 0x00, 0x00 }, /* ÐŽ to U */
|
|
+ { 0x040F, 0x44, 0x00, 0x00, 0x00 }, /* � to D */
|
|
+ { 0x0410, 0x41, 0x00, 0x00, 0x00 }, /* � to A */
|
|
+ { 0x0411, 0x42, 0x00, 0x00, 0x00 }, /* Б to B */
|
|
+ { 0x0412, 0x56, 0x00, 0x00, 0x00 }, /* Ð’ to V */
|
|
+ { 0x0413, 0x47, 0x00, 0x00, 0x00 }, /* Г to G */
|
|
+ { 0x0414, 0x44, 0x00, 0x00, 0x00 }, /* Д to D */
|
|
+ { 0x0415, 0x45, 0x00, 0x00, 0x00 }, /* Е to E */
|
|
+ { 0x0416, 0x5a, 0x68, 0x00, 0x00 }, /* Ж to Zh */
|
|
+ { 0x0417, 0x5a, 0x00, 0x00, 0x00 }, /* З to Z */
|
|
+ { 0x0418, 0x49, 0x00, 0x00, 0x00 }, /* И to I */
|
|
+ { 0x0419, 0x49, 0x00, 0x00, 0x00 }, /* Й to I */
|
|
+ { 0x041A, 0x4b, 0x00, 0x00, 0x00 }, /* К to K */
|
|
+ { 0x041B, 0x4c, 0x00, 0x00, 0x00 }, /* Л to L */
|
|
+ { 0x041C, 0x4d, 0x00, 0x00, 0x00 }, /* М to M */
|
|
+ { 0x041D, 0x4e, 0x00, 0x00, 0x00 }, /* � to N */
|
|
+ { 0x041E, 0x4f, 0x00, 0x00, 0x00 }, /* О to O */
|
|
+ { 0x041F, 0x50, 0x00, 0x00, 0x00 }, /* П to P */
|
|
+ { 0x0420, 0x52, 0x00, 0x00, 0x00 }, /* Ð to R */
|
|
+ { 0x0421, 0x53, 0x00, 0x00, 0x00 }, /* С to S */
|
|
+ { 0x0422, 0x54, 0x00, 0x00, 0x00 }, /* Т to T */
|
|
+ { 0x0423, 0x55, 0x00, 0x00, 0x00 }, /* У to U */
|
|
+ { 0x0424, 0x46, 0x00, 0x00, 0x00 }, /* Ф to F */
|
|
+ { 0x0425, 0x4b, 0x68, 0x00, 0x00 }, /* Х to Kh */
|
|
+ { 0x0426, 0x54, 0x63, 0x00, 0x00 }, /* Ц to Tc */
|
|
+ { 0x0427, 0x43, 0x68, 0x00, 0x00 }, /* Ч to Ch */
|
|
+ { 0x0428, 0x53, 0x68, 0x00, 0x00 }, /* Ш to Sh */
|
|
+ { 0x0429, 0x53, 0x68, 0x63, 0x68 }, /* Щ to Shch */
|
|
+ { 0x042A, 0x61, 0x00, 0x00, 0x00 }, /* to A */
|
|
+ { 0x042B, 0x59, 0x00, 0x00, 0x00 }, /* Ы to Y */
|
|
+ { 0x042C, 0x59, 0x00, 0x00, 0x00 }, /* to Y */
|
|
+ { 0x042D, 0x45, 0x00, 0x00, 0x00 }, /* Ð to E */
|
|
+ { 0x042E, 0x49, 0x75, 0x00, 0x00 }, /* Ю to Iu */
|
|
+ { 0x042F, 0x49, 0x61, 0x00, 0x00 }, /* Я to Ia */
|
|
+ { 0x0430, 0x61, 0x00, 0x00, 0x00 }, /* а to a */
|
|
+ { 0x0431, 0x62, 0x00, 0x00, 0x00 }, /* б to b */
|
|
+ { 0x0432, 0x76, 0x00, 0x00, 0x00 }, /* в to v */
|
|
+ { 0x0433, 0x67, 0x00, 0x00, 0x00 }, /* г to g */
|
|
+ { 0x0434, 0x64, 0x00, 0x00, 0x00 }, /* д to d */
|
|
+ { 0x0435, 0x65, 0x00, 0x00, 0x00 }, /* е to e */
|
|
+ { 0x0436, 0x7a, 0x68, 0x00, 0x00 }, /* ж to zh */
|
|
+ { 0x0437, 0x7a, 0x00, 0x00, 0x00 }, /* з to z */
|
|
+ { 0x0438, 0x69, 0x00, 0x00, 0x00 }, /* и to i */
|
|
+ { 0x0439, 0x69, 0x00, 0x00, 0x00 }, /* й to i */
|
|
+ { 0x043A, 0x6b, 0x00, 0x00, 0x00 }, /* к to k */
|
|
+ { 0x043B, 0x6c, 0x00, 0x00, 0x00 }, /* л to l */
|
|
+ { 0x043C, 0x6d, 0x00, 0x00, 0x00 }, /* м to m */
|
|
+ { 0x043D, 0x6e, 0x00, 0x00, 0x00 }, /* н to n */
|
|
+ { 0x043E, 0x6f, 0x00, 0x00, 0x00 }, /* о to o */
|
|
+ { 0x043F, 0x70, 0x00, 0x00, 0x00 }, /* п to p */
|
|
+ { 0x0440, 0x72, 0x00, 0x00, 0x00 }, /* р to r */
|
|
+ { 0x0441, 0x73, 0x00, 0x00, 0x00 }, /* � to s */
|
|
+ { 0x0442, 0x74, 0x00, 0x00, 0x00 }, /* Ñ‚ to t */
|
|
+ { 0x0443, 0x75, 0x00, 0x00, 0x00 }, /* у to u */
|
|
+ { 0x0444, 0x66, 0x00, 0x00, 0x00 }, /* Ñ„ to f */
|
|
+ { 0x0445, 0x6b, 0x68, 0x00, 0x00 }, /* Ñ… to kh */
|
|
+ { 0x0446, 0x74, 0x63, 0x00, 0x00 }, /* ц to tc */
|
|
+ { 0x0447, 0x63, 0x68, 0x00, 0x00 }, /* ч to ch */
|
|
+ { 0x0448, 0x73, 0x68, 0x00, 0x00 }, /* ш to sh */
|
|
+ { 0x0449, 0x73, 0x68, 0x63, 0x68 }, /* щ to shch */
|
|
+ { 0x044A, 0x61, 0x00, 0x00, 0x00 }, /* to a */
|
|
+ { 0x044B, 0x79, 0x00, 0x00, 0x00 }, /* Ñ‹ to y */
|
|
+ { 0x044C, 0x79, 0x00, 0x00, 0x00 }, /* to y */
|
|
+ { 0x044D, 0x65, 0x00, 0x00, 0x00 }, /* � to e */
|
|
+ { 0x044E, 0x69, 0x75, 0x00, 0x00 }, /* ÑŽ to iu */
|
|
+ { 0x044F, 0x69, 0x61, 0x00, 0x00 }, /* � to ia */
|
|
+ { 0x0450, 0x65, 0x00, 0x00, 0x00 }, /* � to e */
|
|
+ { 0x0451, 0x65, 0x00, 0x00, 0x00 }, /* Ñ‘ to e */
|
|
+ { 0x0452, 0x64, 0x00, 0x00, 0x00 }, /* Ñ’ to d */
|
|
+ { 0x0453, 0x67, 0x00, 0x00, 0x00 }, /* Ñ“ to g */
|
|
+ { 0x0454, 0x65, 0x00, 0x00, 0x00 }, /* Ñ” to e */
|
|
+ { 0x0455, 0x7a, 0x00, 0x00, 0x00 }, /* Ñ• to z */
|
|
+ { 0x0456, 0x69, 0x00, 0x00, 0x00 }, /* Ñ– to i */
|
|
+ { 0x0457, 0x69, 0x00, 0x00, 0x00 }, /* Ñ— to i */
|
|
+ { 0x0458, 0x6a, 0x00, 0x00, 0x00 }, /* ј to j */
|
|
+ { 0x0459, 0x69, 0x00, 0x00, 0x00 }, /* Ñ™ to i */
|
|
+ { 0x045A, 0x6e, 0x00, 0x00, 0x00 }, /* Ñš to n */
|
|
+ { 0x045B, 0x64, 0x00, 0x00, 0x00 }, /* Ñ› to d */
|
|
+ { 0x045C, 0x6b, 0x00, 0x00, 0x00 }, /* ќ to k */
|
|
+ { 0x045D, 0x69, 0x00, 0x00, 0x00 }, /* � to i */
|
|
+ { 0x045E, 0x75, 0x00, 0x00, 0x00 }, /* Ñž to u */
|
|
+ { 0x045F, 0x64, 0x00, 0x00, 0x00 }, /* ÑŸ to d */
|
|
+ { 0x1E02, 0x42, 0x00, 0x00, 0x00 }, /* Ḃ to B */
|
|
+ { 0x1E03, 0x62, 0x00, 0x00, 0x00 }, /* ḃ to b */
|
|
+ { 0x1E0A, 0x44, 0x00, 0x00, 0x00 }, /* Ḋ to D */
|
|
+ { 0x1E0B, 0x64, 0x00, 0x00, 0x00 }, /* ḋ to d */
|
|
+ { 0x1E1E, 0x46, 0x00, 0x00, 0x00 }, /* Ḟ to F */
|
|
+ { 0x1E1F, 0x66, 0x00, 0x00, 0x00 }, /* ḟ to f */
|
|
+ { 0x1E40, 0x4D, 0x00, 0x00, 0x00 }, /* á¹€ to M */
|
|
+ { 0x1E41, 0x6D, 0x00, 0x00, 0x00 }, /* � to m */
|
|
+ { 0x1E56, 0x50, 0x00, 0x00, 0x00 }, /* á¹– to P */
|
|
+ { 0x1E57, 0x70, 0x00, 0x00, 0x00 }, /* á¹— to p */
|
|
+ { 0x1E60, 0x53, 0x00, 0x00, 0x00 }, /* á¹ to S */
|
|
+ { 0x1E61, 0x73, 0x00, 0x00, 0x00 }, /* ṡ to s */
|
|
+ { 0x1E6A, 0x54, 0x00, 0x00, 0x00 }, /* Ṫ to T */
|
|
+ { 0x1E6B, 0x74, 0x00, 0x00, 0x00 }, /* ṫ to t */
|
|
+ { 0x1E80, 0x57, 0x00, 0x00, 0x00 }, /* Ẁ to W */
|
|
+ { 0x1E81, 0x77, 0x00, 0x00, 0x00 }, /* � to w */
|
|
+ { 0x1E82, 0x57, 0x00, 0x00, 0x00 }, /* Ẃ to W */
|
|
+ { 0x1E83, 0x77, 0x00, 0x00, 0x00 }, /* ẃ to w */
|
|
+ { 0x1E84, 0x57, 0x00, 0x00, 0x00 }, /* Ẅ to W */
|
|
+ { 0x1E85, 0x77, 0x00, 0x00, 0x00 }, /* ẅ to w */
|
|
+ { 0x1EF2, 0x59, 0x00, 0x00, 0x00 }, /* Ỳ to Y */
|
|
+ { 0x1EF3, 0x79, 0x00, 0x00, 0x00 }, /* ỳ to y */
|
|
+ { 0xFB00, 0x66, 0x66, 0x00, 0x00 }, /* ff to ff */
|
|
+ { 0xFB01, 0x66, 0x69, 0x00, 0x00 }, /* � to fi */
|
|
+ { 0xFB02, 0x66, 0x6C, 0x00, 0x00 }, /* fl to fl */
|
|
+ { 0xFB05, 0x73, 0x74, 0x00, 0x00 }, /* ſt to st */
|
|
+ { 0xFB06, 0x73, 0x74, 0x00, 0x00 }, /* st to st */
|
|
+};
|
|
+
|
|
+static const Transliteration *spellfixFindTranslit(int c, int *pxTop){
|
|
+ *pxTop = (sizeof(translit)/sizeof(translit[0])) - 1;
|
|
+ return translit;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert the input string from UTF-8 into pure ASCII by converting
|
|
+** all non-ASCII characters to some combination of characters in the
|
|
+** ASCII subset.
|
|
+**
|
|
+** The returned string might contain more characters than the input.
|
|
+**
|
|
+** Space to hold the returned string comes from sqlite3_malloc() and
|
|
+** should be freed by the caller.
|
|
+*/
|
|
+static unsigned char *transliterate(const unsigned char *zIn, int nIn){
|
|
+#ifdef SQLITE_SPELLFIX_5BYTE_MAPPINGS
|
|
+ unsigned char *zOut = sqlite3_malloc( nIn*5 + 1 );
|
|
+#else
|
|
+ unsigned char *zOut = sqlite3_malloc( nIn*4 + 1 );
|
|
+#endif
|
|
+ int c, sz, nOut;
|
|
+ if( zOut==0 ) return 0;
|
|
+ nOut = 0;
|
|
+ while( nIn>0 ){
|
|
+ c = utf8Read(zIn, nIn, &sz);
|
|
+ zIn += sz;
|
|
+ nIn -= sz;
|
|
+ if( c<=127 ){
|
|
+ zOut[nOut++] = (unsigned char)c;
|
|
+ }else{
|
|
+ int xTop, xBtm, x;
|
|
+ const Transliteration *tbl = spellfixFindTranslit(c, &xTop);
|
|
+ xBtm = 0;
|
|
+ while( xTop>=xBtm ){
|
|
+ x = (xTop + xBtm)/2;
|
|
+ if( tbl[x].cFrom==c ){
|
|
+ zOut[nOut++] = tbl[x].cTo0;
|
|
+ if( tbl[x].cTo1 ){
|
|
+ zOut[nOut++] = tbl[x].cTo1;
|
|
+ if( tbl[x].cTo2 ){
|
|
+ zOut[nOut++] = tbl[x].cTo2;
|
|
+ if( tbl[x].cTo3 ){
|
|
+ zOut[nOut++] = tbl[x].cTo3;
|
|
+#ifdef SQLITE_SPELLFIX_5BYTE_MAPPINGS
|
|
+ if( tbl[x].cTo4 ){
|
|
+ zOut[nOut++] = tbl[x].cTo4;
|
|
+ }
|
|
+#endif /* SQLITE_SPELLFIX_5BYTE_MAPPINGS */
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ c = 0;
|
|
+ break;
|
|
+ }else if( tbl[x].cFrom>c ){
|
|
+ xTop = x-1;
|
|
+ }else{
|
|
+ xBtm = x+1;
|
|
+ }
|
|
+ }
|
|
+ if( c ) zOut[nOut++] = '?';
|
|
+ }
|
|
+ }
|
|
+ zOut[nOut] = 0;
|
|
+ return zOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the number of characters in the shortest prefix of the input
|
|
+** string that transliterates to an ASCII string nTrans bytes or longer.
|
|
+** Or, if the transliteration of the input string is less than nTrans
|
|
+** bytes in size, return the number of characters in the input string.
|
|
+*/
|
|
+static int translen_to_charlen(const char *zIn, int nIn, int nTrans){
|
|
+ int i, c, sz, nOut;
|
|
+ int nChar;
|
|
+
|
|
+ i = nOut = 0;
|
|
+ for(nChar=0; i<nIn && nOut<nTrans; nChar++){
|
|
+ c = utf8Read((const unsigned char *)&zIn[i], nIn-i, &sz);
|
|
+ i += sz;
|
|
+
|
|
+ nOut++;
|
|
+ if( c>=128 ){
|
|
+ int xTop, xBtm, x;
|
|
+ const Transliteration *tbl = spellfixFindTranslit(c, &xTop);
|
|
+ xBtm = 0;
|
|
+ while( xTop>=xBtm ){
|
|
+ x = (xTop + xBtm)/2;
|
|
+ if( tbl[x].cFrom==c ){
|
|
+ if( tbl[x].cTo1 ){
|
|
+ nOut++;
|
|
+ if( tbl[x].cTo2 ){
|
|
+ nOut++;
|
|
+ if( tbl[x].cTo3 ){
|
|
+ nOut++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }else if( tbl[x].cFrom>c ){
|
|
+ xTop = x-1;
|
|
+ }else{
|
|
+ xBtm = x+1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return nChar;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** spellfix1_translit(X)
|
|
+**
|
|
+** Convert a string that contains non-ASCII Roman characters into
|
|
+** pure ASCII.
|
|
+*/
|
|
+static void transliterateSqlFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *zIn = sqlite3_value_text(argv[0]);
|
|
+ int nIn = sqlite3_value_bytes(argv[0]);
|
|
+ unsigned char *zOut = transliterate(zIn, nIn);
|
|
+ if( zOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** spellfix1_scriptcode(X)
|
|
+**
|
|
+** Try to determine the dominant script used by the word X and return
|
|
+** its ISO 15924 numeric code.
|
|
+**
|
|
+** The current implementation only understands the following scripts:
|
|
+**
|
|
+** 215 (Latin)
|
|
+** 220 (Cyrillic)
|
|
+** 200 (Greek)
|
|
+**
|
|
+** This routine will return 998 if the input X contains characters from
|
|
+** two or more of the above scripts or 999 if X contains no characters
|
|
+** from any of the above scripts.
|
|
+*/
|
|
+static void scriptCodeSqlFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const unsigned char *zIn = sqlite3_value_text(argv[0]);
|
|
+ int nIn = sqlite3_value_bytes(argv[0]);
|
|
+ int c, sz;
|
|
+ int scriptMask = 0;
|
|
+ int res;
|
|
+ int seenDigit = 0;
|
|
+# define SCRIPT_LATIN 0x0001
|
|
+# define SCRIPT_CYRILLIC 0x0002
|
|
+# define SCRIPT_GREEK 0x0004
|
|
+# define SCRIPT_HEBREW 0x0008
|
|
+# define SCRIPT_ARABIC 0x0010
|
|
+
|
|
+ while( nIn>0 ){
|
|
+ c = utf8Read(zIn, nIn, &sz);
|
|
+ zIn += sz;
|
|
+ nIn -= sz;
|
|
+ if( c<0x02af ){
|
|
+ if( c>=0x80 || midClass[c&0x7f]<CCLASS_DIGIT ){
|
|
+ scriptMask |= SCRIPT_LATIN;
|
|
+ }else if( c>='0' && c<='9' ){
|
|
+ seenDigit = 1;
|
|
+ }
|
|
+ }else if( c>=0x0400 && c<=0x04ff ){
|
|
+ scriptMask |= SCRIPT_CYRILLIC;
|
|
+ }else if( c>=0x0386 && c<=0x03ce ){
|
|
+ scriptMask |= SCRIPT_GREEK;
|
|
+ }else if( c>=0x0590 && c<=0x05ff ){
|
|
+ scriptMask |= SCRIPT_HEBREW;
|
|
+ }else if( c>=0x0600 && c<=0x06ff ){
|
|
+ scriptMask |= SCRIPT_ARABIC;
|
|
+ }
|
|
+ }
|
|
+ if( scriptMask==0 && seenDigit ) scriptMask = SCRIPT_LATIN;
|
|
+ switch( scriptMask ){
|
|
+ case 0: res = 999; break;
|
|
+ case SCRIPT_LATIN: res = 215; break;
|
|
+ case SCRIPT_CYRILLIC: res = 220; break;
|
|
+ case SCRIPT_GREEK: res = 200; break;
|
|
+ case SCRIPT_HEBREW: res = 125; break;
|
|
+ case SCRIPT_ARABIC: res = 160; break;
|
|
+ default: res = 998; break;
|
|
+ }
|
|
+ sqlite3_result_int(context, res);
|
|
+}
|
|
+
|
|
+/* End transliterate
|
|
+******************************************************************************
|
|
+******************************************************************************
|
|
+** Begin spellfix1 virtual table.
|
|
+*/
|
|
+
|
|
+/* Maximum length of a phonehash used for querying the shadow table */
|
|
+#define SPELLFIX_MX_HASH 32
|
|
+
|
|
+/* Maximum number of hash strings to examine per query */
|
|
+#define SPELLFIX_MX_RUN 1
|
|
+
|
|
+typedef struct spellfix1_vtab spellfix1_vtab;
|
|
+typedef struct spellfix1_cursor spellfix1_cursor;
|
|
+
|
|
+/* Fuzzy-search virtual table object */
|
|
+struct spellfix1_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection */
|
|
+ char *zDbName; /* Name of database holding this table */
|
|
+ char *zTableName; /* Name of the virtual table */
|
|
+ char *zCostTable; /* Table holding edit-distance cost numbers */
|
|
+ EditDist3Config *pConfig3; /* Parsed edit distance costs */
|
|
+};
|
|
+
|
|
+/* Fuzzy-search cursor object */
|
|
+struct spellfix1_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ spellfix1_vtab *pVTab; /* The table to which this cursor belongs */
|
|
+ char *zPattern; /* rhs of MATCH clause */
|
|
+ int idxNum; /* idxNum value passed to xFilter() */
|
|
+ int nRow; /* Number of rows of content */
|
|
+ int nAlloc; /* Number of allocated rows */
|
|
+ int iRow; /* Current row of content */
|
|
+ int iLang; /* Value of the langid= constraint */
|
|
+ int iTop; /* Value of the top= constraint */
|
|
+ int iScope; /* Value of the scope= constraint */
|
|
+ int nSearch; /* Number of vocabulary items checked */
|
|
+ sqlite3_stmt *pFullScan; /* Shadow query for a full table scan */
|
|
+ struct spellfix1_row { /* For each row of content */
|
|
+ sqlite3_int64 iRowid; /* Rowid for this row */
|
|
+ char *zWord; /* Text for this row */
|
|
+ int iRank; /* Rank for this row */
|
|
+ int iDistance; /* Distance from pattern for this row */
|
|
+ int iScore; /* Score for sorting */
|
|
+ int iMatchlen; /* Value of matchlen column (or -1) */
|
|
+ char zHash[SPELLFIX_MX_HASH]; /* the phonehash used for this match */
|
|
+ } *a;
|
|
+};
|
|
+
|
|
+/*
|
|
+** Construct one or more SQL statements from the format string given
|
|
+** and then evaluate those statements. The success code is written
|
|
+** into *pRc.
|
|
+**
|
|
+** If *pRc is initially non-zero then this routine is a no-op.
|
|
+*/
|
|
+static void spellfix1DbExec(
|
|
+ int *pRc, /* Success code */
|
|
+ sqlite3 *db, /* Database in which to run SQL */
|
|
+ const char *zFormat, /* Format string for SQL */
|
|
+ ... /* Arguments to the format string */
|
|
+){
|
|
+ va_list ap;
|
|
+ char *zSql;
|
|
+ if( *pRc ) return;
|
|
+ va_start(ap, zFormat);
|
|
+ zSql = sqlite3_vmprintf(zFormat, ap);
|
|
+ va_end(ap);
|
|
+ if( zSql==0 ){
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ *pRc = sqlite3_exec(db, zSql, 0, 0, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xDisconnect/xDestroy method for the fuzzy-search module.
|
|
+*/
|
|
+static int spellfix1Uninit(int isDestroy, sqlite3_vtab *pVTab){
|
|
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( isDestroy ){
|
|
+ sqlite3 *db = p->db;
|
|
+ spellfix1DbExec(&rc, db, "DROP TABLE IF EXISTS \"%w\".\"%w_vocab\"",
|
|
+ p->zDbName, p->zTableName);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_free(p->zTableName);
|
|
+ editDist3ConfigDelete(p->pConfig3);
|
|
+ sqlite3_free(p->zCostTable);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+static int spellfix1Disconnect(sqlite3_vtab *pVTab){
|
|
+ return spellfix1Uninit(0, pVTab);
|
|
+}
|
|
+static int spellfix1Destroy(sqlite3_vtab *pVTab){
|
|
+ return spellfix1Uninit(1, pVTab);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Make a copy of a string. Remove leading and trailing whitespace
|
|
+** and dequote it.
|
|
+*/
|
|
+static char *spellfix1Dequote(const char *zIn){
|
|
+ char *zOut;
|
|
+ int i, j;
|
|
+ char c;
|
|
+ while( isspace((unsigned char)zIn[0]) ) zIn++;
|
|
+ zOut = sqlite3_mprintf("%s", zIn);
|
|
+ if( zOut==0 ) return 0;
|
|
+ i = (int)strlen(zOut);
|
|
+#if 0 /* The parser will never leave spaces at the end */
|
|
+ while( i>0 && isspace(zOut[i-1]) ){ i--; }
|
|
+#endif
|
|
+ zOut[i] = 0;
|
|
+ c = zOut[0];
|
|
+ if( c=='\'' || c=='"' ){
|
|
+ for(i=1, j=0; ALWAYS(zOut[i]); i++){
|
|
+ zOut[j++] = zOut[i];
|
|
+ if( zOut[i]==c ){
|
|
+ if( zOut[i+1]==c ){
|
|
+ i++;
|
|
+ }else{
|
|
+ zOut[j-1] = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return zOut;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** xConnect/xCreate method for the spellfix1 module. Arguments are:
|
|
+**
|
|
+** argv[0] -> module name ("spellfix1")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[3].. -> optional arguments (i.e. "edit_cost_table" parameter)
|
|
+*/
|
|
+static int spellfix1Init(
|
|
+ int isCreate,
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVTab,
|
|
+ char **pzErr
|
|
+){
|
|
+ spellfix1_vtab *pNew = 0;
|
|
+ /* const char *zModule = argv[0]; // not used */
|
|
+ const char *zDbName = argv[1];
|
|
+ const char *zTableName = argv[2];
|
|
+ int nDbName;
|
|
+ int rc = SQLITE_OK;
|
|
+ int i;
|
|
+
|
|
+ nDbName = (int)strlen(zDbName);
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) + nDbName + 1);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->zDbName = (char*)&pNew[1];
|
|
+ memcpy(pNew->zDbName, zDbName, nDbName+1);
|
|
+ pNew->zTableName = sqlite3_mprintf("%s", zTableName);
|
|
+ pNew->db = db;
|
|
+ if( pNew->zTableName==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(word,rank,distance,langid, "
|
|
+ "score, matchlen, phonehash HIDDEN, "
|
|
+ "top HIDDEN, scope HIDDEN, srchcnt HIDDEN, "
|
|
+ "soundslike HIDDEN, command HIDDEN)"
|
|
+ );
|
|
+#define SPELLFIX_COL_WORD 0
|
|
+#define SPELLFIX_COL_RANK 1
|
|
+#define SPELLFIX_COL_DISTANCE 2
|
|
+#define SPELLFIX_COL_LANGID 3
|
|
+#define SPELLFIX_COL_SCORE 4
|
|
+#define SPELLFIX_COL_MATCHLEN 5
|
|
+#define SPELLFIX_COL_PHONEHASH 6
|
|
+#define SPELLFIX_COL_TOP 7
|
|
+#define SPELLFIX_COL_SCOPE 8
|
|
+#define SPELLFIX_COL_SRCHCNT 9
|
|
+#define SPELLFIX_COL_SOUNDSLIKE 10
|
|
+#define SPELLFIX_COL_COMMAND 11
|
|
+ }
|
|
+ if( rc==SQLITE_OK && isCreate ){
|
|
+ spellfix1DbExec(&rc, db,
|
|
+ "CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n"
|
|
+ " id INTEGER PRIMARY KEY,\n"
|
|
+ " rank INT,\n"
|
|
+ " langid INT,\n"
|
|
+ " word TEXT,\n"
|
|
+ " k1 TEXT,\n"
|
|
+ " k2 TEXT\n"
|
|
+ ");\n",
|
|
+ zDbName, zTableName
|
|
+ );
|
|
+ spellfix1DbExec(&rc, db,
|
|
+ "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" "
|
|
+ "ON \"%w_vocab\"(langid,k2);",
|
|
+ zDbName, zTableName, zTableName
|
|
+ );
|
|
+ }
|
|
+ for(i=3; rc==SQLITE_OK && i<argc; i++){
|
|
+ if( strncmp(argv[i],"edit_cost_table=",16)==0 && pNew->zCostTable==0 ){
|
|
+ pNew->zCostTable = spellfix1Dequote(&argv[i][16]);
|
|
+ if( pNew->zCostTable==0 ) rc = SQLITE_NOMEM;
|
|
+ continue;
|
|
+ }
|
|
+ *pzErr = sqlite3_mprintf("bad argument to spellfix1(): \"%s\"", argv[i]);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc && pNew ){
|
|
+ *ppVTab = 0;
|
|
+ spellfix1Uninit(0, &pNew->base);
|
|
+ }else{
|
|
+ *ppVTab = (sqlite3_vtab *)pNew;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The xConnect and xCreate methods
|
|
+*/
|
|
+static int spellfix1Connect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVTab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return spellfix1Init(0, db, pAux, argc, argv, ppVTab, pzErr);
|
|
+}
|
|
+static int spellfix1Create(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVTab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return spellfix1Init(1, db, pAux, argc, argv, ppVTab, pzErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Clear all of the content from a cursor.
|
|
+*/
|
|
+static void spellfix1ResetCursor(spellfix1_cursor *pCur){
|
|
+ int i;
|
|
+ for(i=0; i<pCur->nRow; i++){
|
|
+ sqlite3_free(pCur->a[i].zWord);
|
|
+ }
|
|
+ pCur->nRow = 0;
|
|
+ pCur->iRow = 0;
|
|
+ pCur->nSearch = 0;
|
|
+ if( pCur->pFullScan ){
|
|
+ sqlite3_finalize(pCur->pFullScan);
|
|
+ pCur->pFullScan = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Resize the cursor to hold up to N rows of content
|
|
+*/
|
|
+static void spellfix1ResizeCursor(spellfix1_cursor *pCur, int N){
|
|
+ struct spellfix1_row *aNew;
|
|
+ assert( N>=pCur->nRow );
|
|
+ aNew = sqlite3_realloc(pCur->a, sizeof(pCur->a[0])*N);
|
|
+ if( aNew==0 && N>0 ){
|
|
+ spellfix1ResetCursor(pCur);
|
|
+ sqlite3_free(pCur->a);
|
|
+ pCur->nAlloc = 0;
|
|
+ pCur->a = 0;
|
|
+ }else{
|
|
+ pCur->nAlloc = N;
|
|
+ pCur->a = aNew;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Close a fuzzy-search cursor.
|
|
+*/
|
|
+static int spellfix1Close(sqlite3_vtab_cursor *cur){
|
|
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
|
+ spellfix1ResetCursor(pCur);
|
|
+ spellfix1ResizeCursor(pCur, 0);
|
|
+ sqlite3_free(pCur->zPattern);
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+#define SPELLFIX_IDXNUM_MATCH 0x01 /* word MATCH $str */
|
|
+#define SPELLFIX_IDXNUM_LANGID 0x02 /* langid == $langid */
|
|
+#define SPELLFIX_IDXNUM_TOP 0x04 /* top = $top */
|
|
+#define SPELLFIX_IDXNUM_SCOPE 0x08 /* scope = $scope */
|
|
+#define SPELLFIX_IDXNUM_DISTLT 0x10 /* distance < $distance */
|
|
+#define SPELLFIX_IDXNUM_DISTLE 0x20 /* distance <= $distance */
|
|
+#define SPELLFIX_IDXNUM_ROWID 0x40 /* rowid = $rowid */
|
|
+#define SPELLFIX_IDXNUM_DIST (0x10|0x20) /* DISTLT and DISTLE */
|
|
+
|
|
+/*
|
|
+**
|
|
+** The plan number is a bitmask of the SPELLFIX_IDXNUM_* values defined
|
|
+** above.
|
|
+**
|
|
+** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid
|
|
+** if specified and in that order.
|
|
+*/
|
|
+static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
|
+ int iPlan = 0;
|
|
+ int iLangTerm = -1;
|
|
+ int iTopTerm = -1;
|
|
+ int iScopeTerm = -1;
|
|
+ int iDistTerm = -1;
|
|
+ int iRowidTerm = -1;
|
|
+ int i;
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+
|
|
+ /* Terms of the form: word MATCH $str */
|
|
+ if( (iPlan & SPELLFIX_IDXNUM_MATCH)==0
|
|
+ && pConstraint->iColumn==SPELLFIX_COL_WORD
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
|
+ ){
|
|
+ iPlan |= SPELLFIX_IDXNUM_MATCH;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ }
|
|
+
|
|
+ /* Terms of the form: langid = $langid */
|
|
+ if( (iPlan & SPELLFIX_IDXNUM_LANGID)==0
|
|
+ && pConstraint->iColumn==SPELLFIX_COL_LANGID
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= SPELLFIX_IDXNUM_LANGID;
|
|
+ iLangTerm = i;
|
|
+ }
|
|
+
|
|
+ /* Terms of the form: top = $top */
|
|
+ if( (iPlan & SPELLFIX_IDXNUM_TOP)==0
|
|
+ && pConstraint->iColumn==SPELLFIX_COL_TOP
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= SPELLFIX_IDXNUM_TOP;
|
|
+ iTopTerm = i;
|
|
+ }
|
|
+
|
|
+ /* Terms of the form: scope = $scope */
|
|
+ if( (iPlan & SPELLFIX_IDXNUM_SCOPE)==0
|
|
+ && pConstraint->iColumn==SPELLFIX_COL_SCOPE
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= SPELLFIX_IDXNUM_SCOPE;
|
|
+ iScopeTerm = i;
|
|
+ }
|
|
+
|
|
+ /* Terms of the form: distance < $dist or distance <= $dist */
|
|
+ if( (iPlan & SPELLFIX_IDXNUM_DIST)==0
|
|
+ && pConstraint->iColumn==SPELLFIX_COL_DISTANCE
|
|
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
|
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
|
|
+ ){
|
|
+ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
|
+ iPlan |= SPELLFIX_IDXNUM_DISTLT;
|
|
+ }else{
|
|
+ iPlan |= SPELLFIX_IDXNUM_DISTLE;
|
|
+ }
|
|
+ iDistTerm = i;
|
|
+ }
|
|
+
|
|
+ /* Terms of the form: distance < $dist or distance <= $dist */
|
|
+ if( (iPlan & SPELLFIX_IDXNUM_ROWID)==0
|
|
+ && pConstraint->iColumn<0
|
|
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ ){
|
|
+ iPlan |= SPELLFIX_IDXNUM_ROWID;
|
|
+ iRowidTerm = i;
|
|
+ }
|
|
+ }
|
|
+ if( iPlan&SPELLFIX_IDXNUM_MATCH ){
|
|
+ int idx = 2;
|
|
+ pIdxInfo->idxNum = iPlan;
|
|
+ if( pIdxInfo->nOrderBy==1
|
|
+ && pIdxInfo->aOrderBy[0].iColumn==SPELLFIX_COL_SCORE
|
|
+ && pIdxInfo->aOrderBy[0].desc==0
|
|
+ ){
|
|
+ pIdxInfo->orderByConsumed = 1; /* Default order by iScore */
|
|
+ }
|
|
+ if( iPlan&SPELLFIX_IDXNUM_LANGID ){
|
|
+ pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++;
|
|
+ pIdxInfo->aConstraintUsage[iLangTerm].omit = 1;
|
|
+ }
|
|
+ if( iPlan&SPELLFIX_IDXNUM_TOP ){
|
|
+ pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++;
|
|
+ pIdxInfo->aConstraintUsage[iTopTerm].omit = 1;
|
|
+ }
|
|
+ if( iPlan&SPELLFIX_IDXNUM_SCOPE ){
|
|
+ pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++;
|
|
+ pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1;
|
|
+ }
|
|
+ if( iPlan&SPELLFIX_IDXNUM_DIST ){
|
|
+ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++;
|
|
+ pIdxInfo->aConstraintUsage[iDistTerm].omit = 1;
|
|
+ }
|
|
+ pIdxInfo->estimatedCost = 1e5;
|
|
+ }else if( (iPlan & SPELLFIX_IDXNUM_ROWID) ){
|
|
+ pIdxInfo->idxNum = SPELLFIX_IDXNUM_ROWID;
|
|
+ pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
|
|
+ pIdxInfo->estimatedCost = 5;
|
|
+ }else{
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ pIdxInfo->estimatedCost = 1e50;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a new fuzzy-search cursor.
|
|
+*/
|
|
+static int spellfix1Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
|
+ spellfix1_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->pVTab = p;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Adjust a distance measurement by the words rank in order to show
|
|
+** preference to common words.
|
|
+*/
|
|
+static int spellfix1Score(int iDistance, int iRank){
|
|
+ int iLog2;
|
|
+ for(iLog2=0; iRank>0; iLog2++, iRank>>=1){}
|
|
+ return iDistance + 32 - iLog2;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compare two spellfix1_row objects for sorting purposes in qsort() such
|
|
+** that they sort in order of increasing distance.
|
|
+*/
|
|
+static int SQLITE_CDECL spellfix1RowCompare(const void *A, const void *B){
|
|
+ const struct spellfix1_row *a = (const struct spellfix1_row*)A;
|
|
+ const struct spellfix1_row *b = (const struct spellfix1_row*)B;
|
|
+ return a->iScore - b->iScore;
|
|
+}
|
|
+
|
|
+/*
|
|
+** A structure used to pass information from spellfix1FilterForMatch()
|
|
+** into spellfix1RunQuery().
|
|
+*/
|
|
+typedef struct MatchQuery {
|
|
+ spellfix1_cursor *pCur; /* The cursor being queried */
|
|
+ sqlite3_stmt *pStmt; /* shadow table query statment */
|
|
+ char zHash[SPELLFIX_MX_HASH]; /* The current phonehash for zPattern */
|
|
+ const char *zPattern; /* Transliterated input string */
|
|
+ int nPattern; /* Length of zPattern */
|
|
+ EditDist3FromString *pMatchStr3; /* Original unicode string */
|
|
+ EditDist3Config *pConfig3; /* Edit-distance cost coefficients */
|
|
+ const EditDist3Lang *pLang; /* The selected language coefficients */
|
|
+ int iLang; /* The language id */
|
|
+ int iScope; /* Default scope */
|
|
+ int iMaxDist; /* Maximum allowed edit distance, or -1 */
|
|
+ int rc; /* Error code */
|
|
+ int nRun; /* Number of prior runs for the same zPattern */
|
|
+ char azPrior[SPELLFIX_MX_RUN][SPELLFIX_MX_HASH]; /* Prior hashes */
|
|
+} MatchQuery;
|
|
+
|
|
+/*
|
|
+** Run a query looking for the best matches against zPattern using
|
|
+** zHash as the character class seed hash.
|
|
+*/
|
|
+static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){
|
|
+ const char *zK1;
|
|
+ const char *zWord;
|
|
+ int iDist;
|
|
+ int iRank;
|
|
+ int iScore;
|
|
+ int iWorst = 0;
|
|
+ int idx;
|
|
+ int idxWorst = -1;
|
|
+ int i;
|
|
+ int iScope = p->iScope;
|
|
+ spellfix1_cursor *pCur = p->pCur;
|
|
+ sqlite3_stmt *pStmt = p->pStmt;
|
|
+ char zHash1[SPELLFIX_MX_HASH];
|
|
+ char zHash2[SPELLFIX_MX_HASH];
|
|
+ char *zClass;
|
|
+ int nClass;
|
|
+ int rc;
|
|
+
|
|
+ if( pCur->a==0 || p->rc ) return; /* Prior memory allocation failure */
|
|
+ zClass = (char*)phoneticHash((unsigned char*)zQuery, nQuery);
|
|
+ if( zClass==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ return;
|
|
+ }
|
|
+ nClass = (int)strlen(zClass);
|
|
+ if( nClass>SPELLFIX_MX_HASH-2 ){
|
|
+ nClass = SPELLFIX_MX_HASH-2;
|
|
+ zClass[nClass] = 0;
|
|
+ }
|
|
+ if( nClass<=iScope ){
|
|
+ if( nClass>2 ){
|
|
+ iScope = nClass-1;
|
|
+ }else{
|
|
+ iScope = nClass;
|
|
+ }
|
|
+ }
|
|
+ memcpy(zHash1, zClass, iScope);
|
|
+ sqlite3_free(zClass);
|
|
+ zHash1[iScope] = 0;
|
|
+ memcpy(zHash2, zHash1, iScope);
|
|
+ zHash2[iScope] = 'Z';
|
|
+ zHash2[iScope+1] = 0;
|
|
+#if SPELLFIX_MX_RUN>1
|
|
+ for(i=0; i<p->nRun; i++){
|
|
+ if( strcmp(p->azPrior[i], zHash1)==0 ) return;
|
|
+ }
|
|
+#endif
|
|
+ assert( p->nRun<SPELLFIX_MX_RUN );
|
|
+ memcpy(p->azPrior[p->nRun++], zHash1, iScope+1);
|
|
+ if( sqlite3_bind_text(pStmt, 1, zHash1, -1, SQLITE_STATIC)==SQLITE_NOMEM
|
|
+ || sqlite3_bind_text(pStmt, 2, zHash2, -1, SQLITE_STATIC)==SQLITE_NOMEM
|
|
+ ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ return;
|
|
+ }
|
|
+#if SPELLFIX_MX_RUN>1
|
|
+ for(i=0; i<pCur->nRow; i++){
|
|
+ if( pCur->a[i].iScore>iWorst ){
|
|
+ iWorst = pCur->a[i].iScore;
|
|
+ idxWorst = i;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ int iMatchlen = -1;
|
|
+ iRank = sqlite3_column_int(pStmt, 2);
|
|
+ if( p->pMatchStr3 ){
|
|
+ int nWord = sqlite3_column_bytes(pStmt, 1);
|
|
+ zWord = (const char*)sqlite3_column_text(pStmt, 1);
|
|
+ iDist = editDist3Core(p->pMatchStr3, zWord, nWord, p->pLang, &iMatchlen);
|
|
+ }else{
|
|
+ zK1 = (const char*)sqlite3_column_text(pStmt, 3);
|
|
+ if( zK1==0 ) continue;
|
|
+ iDist = editdist1(p->zPattern, zK1, 0);
|
|
+ }
|
|
+ if( iDist<0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }
|
|
+ pCur->nSearch++;
|
|
+
|
|
+ /* If there is a "distance < $dist" or "distance <= $dist" constraint,
|
|
+ ** check if this row meets it. If not, jump back up to the top of the
|
|
+ ** loop to process the next row. Otherwise, if the row does match the
|
|
+ ** distance constraint, check if the pCur->a[] array is already full.
|
|
+ ** If it is and no explicit "top = ?" constraint was present in the
|
|
+ ** query, grow the array to ensure there is room for the new entry. */
|
|
+ assert( (p->iMaxDist>=0)==((pCur->idxNum & SPELLFIX_IDXNUM_DIST) ? 1 : 0) );
|
|
+ if( p->iMaxDist>=0 ){
|
|
+ if( iDist>p->iMaxDist ) continue;
|
|
+ if( pCur->nRow>=pCur->nAlloc && (pCur->idxNum & SPELLFIX_IDXNUM_TOP)==0 ){
|
|
+ spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10);
|
|
+ if( pCur->a==0 ) break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ iScore = spellfix1Score(iDist,iRank);
|
|
+ if( pCur->nRow<pCur->nAlloc ){
|
|
+ idx = pCur->nRow;
|
|
+ }else if( iScore<iWorst ){
|
|
+ idx = idxWorst;
|
|
+ sqlite3_free(pCur->a[idx].zWord);
|
|
+ }else{
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
|
+ if( pCur->a[idx].zWord==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }
|
|
+ pCur->a[idx].iRowid = sqlite3_column_int64(pStmt, 0);
|
|
+ pCur->a[idx].iRank = iRank;
|
|
+ pCur->a[idx].iDistance = iDist;
|
|
+ pCur->a[idx].iScore = iScore;
|
|
+ pCur->a[idx].iMatchlen = iMatchlen;
|
|
+ memcpy(pCur->a[idx].zHash, zHash1, iScope+1);
|
|
+ if( pCur->nRow<pCur->nAlloc ) pCur->nRow++;
|
|
+ if( pCur->nRow==pCur->nAlloc ){
|
|
+ iWorst = pCur->a[0].iScore;
|
|
+ idxWorst = 0;
|
|
+ for(i=1; i<pCur->nRow; i++){
|
|
+ iScore = pCur->a[i].iScore;
|
|
+ if( iWorst<iScore ){
|
|
+ iWorst = iScore;
|
|
+ idxWorst = i;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ rc = sqlite3_reset(pStmt);
|
|
+ if( rc ) p->rc = rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This version of the xFilter method work if the MATCH term is present
|
|
+** and we are doing a scan.
|
|
+*/
|
|
+static int spellfix1FilterForMatch(
|
|
+ spellfix1_cursor *pCur,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int idxNum = pCur->idxNum;
|
|
+ const unsigned char *zMatchThis; /* RHS of the MATCH operator */
|
|
+ EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */
|
|
+ char *zPattern; /* Transliteration of zMatchThis */
|
|
+ int nPattern; /* Length of zPattern */
|
|
+ int iLimit = 20; /* Max number of rows of output */
|
|
+ int iScope = 3; /* Use this many characters of zClass */
|
|
+ int iLang = 0; /* Language code */
|
|
+ char *zSql; /* SQL of shadow table query */
|
|
+ sqlite3_stmt *pStmt = 0; /* Shadow table query */
|
|
+ int rc; /* Result code */
|
|
+ int idx = 1; /* Next available filter parameter */
|
|
+ spellfix1_vtab *p = pCur->pVTab; /* The virtual table that owns pCur */
|
|
+ MatchQuery x; /* For passing info to RunQuery() */
|
|
+
|
|
+ /* Load the cost table if we have not already done so */
|
|
+ if( p->zCostTable!=0 && p->pConfig3==0 ){
|
|
+ p->pConfig3 = sqlite3_malloc( sizeof(p->pConfig3[0]) );
|
|
+ if( p->pConfig3==0 ) return SQLITE_NOMEM;
|
|
+ memset(p->pConfig3, 0, sizeof(p->pConfig3[0]));
|
|
+ rc = editDist3ConfigLoad(p->pConfig3, p->db, p->zCostTable);
|
|
+ if( rc ) return rc;
|
|
+ }
|
|
+ memset(&x, 0, sizeof(x));
|
|
+ x.iScope = 3; /* Default scope if none specified by "WHERE scope=N" */
|
|
+ x.iMaxDist = -1; /* Maximum allowed edit distance */
|
|
+
|
|
+ if( idxNum&2 ){
|
|
+ iLang = sqlite3_value_int(argv[idx++]);
|
|
+ }
|
|
+ if( idxNum&4 ){
|
|
+ iLimit = sqlite3_value_int(argv[idx++]);
|
|
+ if( iLimit<1 ) iLimit = 1;
|
|
+ }
|
|
+ if( idxNum&8 ){
|
|
+ x.iScope = sqlite3_value_int(argv[idx++]);
|
|
+ if( x.iScope<1 ) x.iScope = 1;
|
|
+ if( x.iScope>SPELLFIX_MX_HASH-2 ) x.iScope = SPELLFIX_MX_HASH-2;
|
|
+ }
|
|
+ if( idxNum&(16|32) ){
|
|
+ x.iMaxDist = sqlite3_value_int(argv[idx++]);
|
|
+ if( idxNum&16 ) x.iMaxDist--;
|
|
+ if( x.iMaxDist<0 ) x.iMaxDist = 0;
|
|
+ }
|
|
+ spellfix1ResetCursor(pCur);
|
|
+ spellfix1ResizeCursor(pCur, iLimit);
|
|
+ zMatchThis = sqlite3_value_text(argv[0]);
|
|
+ if( zMatchThis==0 ) return SQLITE_OK;
|
|
+ if( p->pConfig3 ){
|
|
+ x.pLang = editDist3FindLang(p->pConfig3, iLang);
|
|
+ pMatchStr3 = editDist3FromStringNew(x.pLang, (const char*)zMatchThis, -1);
|
|
+ if( pMatchStr3==0 ){
|
|
+ x.rc = SQLITE_NOMEM;
|
|
+ goto filter_exit;
|
|
+ }
|
|
+ }else{
|
|
+ x.pLang = 0;
|
|
+ }
|
|
+ zPattern = (char*)transliterate(zMatchThis, sqlite3_value_bytes(argv[0]));
|
|
+ sqlite3_free(pCur->zPattern);
|
|
+ pCur->zPattern = zPattern;
|
|
+ if( zPattern==0 ){
|
|
+ x.rc = SQLITE_NOMEM;
|
|
+ goto filter_exit;
|
|
+ }
|
|
+ nPattern = (int)strlen(zPattern);
|
|
+ if( zPattern[nPattern-1]=='*' ) nPattern--;
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT id, word, rank, coalesce(k1,word)"
|
|
+ " FROM \"%w\".\"%w_vocab\""
|
|
+ " WHERE langid=%d AND k2>=?1 AND k2<?2",
|
|
+ p->zDbName, p->zTableName, iLang
|
|
+ );
|
|
+ if( zSql==0 ){
|
|
+ x.rc = SQLITE_NOMEM;
|
|
+ pStmt = 0;
|
|
+ goto filter_exit;
|
|
+ }
|
|
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ pCur->iLang = iLang;
|
|
+ x.pCur = pCur;
|
|
+ x.pStmt = pStmt;
|
|
+ x.zPattern = zPattern;
|
|
+ x.nPattern = nPattern;
|
|
+ x.pMatchStr3 = pMatchStr3;
|
|
+ x.iLang = iLang;
|
|
+ x.rc = rc;
|
|
+ x.pConfig3 = p->pConfig3;
|
|
+ if( x.rc==SQLITE_OK ){
|
|
+ spellfix1RunQuery(&x, zPattern, nPattern);
|
|
+ }
|
|
+
|
|
+ if( pCur->a ){
|
|
+ qsort(pCur->a, pCur->nRow, sizeof(pCur->a[0]), spellfix1RowCompare);
|
|
+ pCur->iTop = iLimit;
|
|
+ pCur->iScope = iScope;
|
|
+ }else{
|
|
+ x.rc = SQLITE_NOMEM;
|
|
+ }
|
|
+
|
|
+filter_exit:
|
|
+ sqlite3_finalize(pStmt);
|
|
+ editDist3FromStringDelete(pMatchStr3);
|
|
+ return x.rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This version of xFilter handles a full-table scan case
|
|
+*/
|
|
+static int spellfix1FilterForFullScan(
|
|
+ spellfix1_cursor *pCur,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ int idxNum = pCur->idxNum;
|
|
+ char *zSql;
|
|
+ spellfix1_vtab *pVTab = pCur->pVTab;
|
|
+ spellfix1ResetCursor(pCur);
|
|
+ assert( idxNum==0 || idxNum==64 );
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s",
|
|
+ pVTab->zDbName, pVTab->zTableName,
|
|
+ ((idxNum & 64) ? " WHERE rowid=?" : "")
|
|
+ );
|
|
+ if( zSql==0 ) return SQLITE_NOMEM;
|
|
+ rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc==SQLITE_OK && (idxNum & 64) ){
|
|
+ assert( argc==1 );
|
|
+ rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]);
|
|
+ }
|
|
+ pCur->nRow = pCur->iRow = 0;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_step(pCur->pFullScan);
|
|
+ if( rc==SQLITE_ROW ){ pCur->iRow = -1; rc = SQLITE_OK; }
|
|
+ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; }
|
|
+ }else{
|
|
+ pCur->iRow = 0;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Called to "rewind" a cursor back to the beginning so that
|
|
+** it starts its output over again. Always called at least once
|
|
+** prior to any spellfix1Column, spellfix1Rowid, or spellfix1Eof call.
|
|
+*/
|
|
+static int spellfix1Filter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
|
+ int rc;
|
|
+ pCur->idxNum = idxNum;
|
|
+ if( idxNum & 1 ){
|
|
+ rc = spellfix1FilterForMatch(pCur, argc, argv);
|
|
+ }else{
|
|
+ rc = spellfix1FilterForFullScan(pCur, argc, argv);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a cursor to its next row of output
|
|
+*/
|
|
+static int spellfix1Next(sqlite3_vtab_cursor *cur){
|
|
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( pCur->iRow < pCur->nRow ){
|
|
+ if( pCur->pFullScan ){
|
|
+ rc = sqlite3_step(pCur->pFullScan);
|
|
+ if( rc!=SQLITE_ROW ) pCur->iRow = pCur->nRow;
|
|
+ if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
|
|
+ }else{
|
|
+ pCur->iRow++;
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if we are at the end-of-file
|
|
+*/
|
|
+static int spellfix1Eof(sqlite3_vtab_cursor *cur){
|
|
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
|
+ return pCur->iRow>=pCur->nRow;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return columns from the current row.
|
|
+*/
|
|
+static int spellfix1Column(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ sqlite3_context *ctx,
|
|
+ int i
|
|
+){
|
|
+ spellfix1_cursor *pCur = (spellfix1_cursor*)cur;
|
|
+ if( pCur->pFullScan ){
|
|
+ if( i<=SPELLFIX_COL_LANGID ){
|
|
+ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pFullScan, i));
|
|
+ }else{
|
|
+ sqlite3_result_null(ctx);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ switch( i ){
|
|
+ case SPELLFIX_COL_WORD: {
|
|
+ sqlite3_result_text(ctx, pCur->a[pCur->iRow].zWord, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_RANK: {
|
|
+ sqlite3_result_int(ctx, pCur->a[pCur->iRow].iRank);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_DISTANCE: {
|
|
+ sqlite3_result_int(ctx, pCur->a[pCur->iRow].iDistance);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_LANGID: {
|
|
+ sqlite3_result_int(ctx, pCur->iLang);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_SCORE: {
|
|
+ sqlite3_result_int(ctx, pCur->a[pCur->iRow].iScore);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_MATCHLEN: {
|
|
+ int iMatchlen = pCur->a[pCur->iRow].iMatchlen;
|
|
+ if( iMatchlen<0 ){
|
|
+ int nPattern = (int)strlen(pCur->zPattern);
|
|
+ char *zWord = pCur->a[pCur->iRow].zWord;
|
|
+ int nWord = (int)strlen(zWord);
|
|
+
|
|
+ if( nPattern>0 && pCur->zPattern[nPattern-1]=='*' ){
|
|
+ char *zTranslit;
|
|
+ int res;
|
|
+ zTranslit = (char *)transliterate((unsigned char *)zWord, nWord);
|
|
+ if( !zTranslit ) return SQLITE_NOMEM;
|
|
+ res = editdist1(pCur->zPattern, zTranslit, &iMatchlen);
|
|
+ sqlite3_free(zTranslit);
|
|
+ if( res<0 ) return SQLITE_NOMEM;
|
|
+ iMatchlen = translen_to_charlen(zWord, nWord, iMatchlen);
|
|
+ }else{
|
|
+ iMatchlen = utf8Charlen(zWord, nWord);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_result_int(ctx, iMatchlen);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_PHONEHASH: {
|
|
+ sqlite3_result_text(ctx, pCur->a[pCur->iRow].zHash, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_TOP: {
|
|
+ sqlite3_result_int(ctx, pCur->iTop);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_SCOPE: {
|
|
+ sqlite3_result_int(ctx, pCur->iScope);
|
|
+ break;
|
|
+ }
|
|
+ case SPELLFIX_COL_SRCHCNT: {
|
|
+ sqlite3_result_int(ctx, pCur->nSearch);
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ sqlite3_result_null(ctx);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The rowid.
|
|
+*/
|
|
+static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ spellfix1_cursor *pCur = (spellfix1_cursor*)cur;
|
|
+ if( pCur->pFullScan ){
|
|
+ *pRowid = sqlite3_column_int64(pCur->pFullScan, 4);
|
|
+ }else{
|
|
+ *pRowid = pCur->a[pCur->iRow].iRowid;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called by the xUpdate() method. It returns a string
|
|
+** containing the conflict mode that xUpdate() should use for the current
|
|
+** operation. One of: "ROLLBACK", "IGNORE", "ABORT" or "REPLACE".
|
|
+*/
|
|
+static const char *spellfix1GetConflict(sqlite3 *db){
|
|
+ static const char *const azConflict[] = {
|
|
+ /* Note: Instead of "FAIL" - "ABORT". */
|
|
+ "ROLLBACK", "IGNORE", "ABORT", "ABORT", "REPLACE"
|
|
+ };
|
|
+ int eConflict = sqlite3_vtab_on_conflict(db);
|
|
+
|
|
+ assert( eConflict==SQLITE_ROLLBACK || eConflict==SQLITE_IGNORE
|
|
+ || eConflict==SQLITE_FAIL || eConflict==SQLITE_ABORT
|
|
+ || eConflict==SQLITE_REPLACE
|
|
+ );
|
|
+ assert( SQLITE_ROLLBACK==1 );
|
|
+ assert( SQLITE_IGNORE==2 );
|
|
+ assert( SQLITE_FAIL==3 );
|
|
+ assert( SQLITE_ABORT==4 );
|
|
+ assert( SQLITE_REPLACE==5 );
|
|
+
|
|
+ return azConflict[eConflict-1];
|
|
+}
|
|
+
|
|
+/*
|
|
+** The xUpdate() method.
|
|
+*/
|
|
+static int spellfix1Update(
|
|
+ sqlite3_vtab *pVTab,
|
|
+ int argc,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3_int64 rowid, newRowid;
|
|
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
|
+ sqlite3 *db = p->db;
|
|
+
|
|
+ if( argc==1 ){
|
|
+ /* A delete operation on the rowid given by argv[0] */
|
|
+ rowid = *pRowid = sqlite3_value_int64(argv[0]);
|
|
+ spellfix1DbExec(&rc, db, "DELETE FROM \"%w\".\"%w_vocab\" "
|
|
+ " WHERE id=%lld",
|
|
+ p->zDbName, p->zTableName, rowid);
|
|
+ }else{
|
|
+ const unsigned char *zWord = sqlite3_value_text(argv[SPELLFIX_COL_WORD+2]);
|
|
+ int nWord = sqlite3_value_bytes(argv[SPELLFIX_COL_WORD+2]);
|
|
+ int iLang = sqlite3_value_int(argv[SPELLFIX_COL_LANGID+2]);
|
|
+ int iRank = sqlite3_value_int(argv[SPELLFIX_COL_RANK+2]);
|
|
+ const unsigned char *zSoundslike =
|
|
+ sqlite3_value_text(argv[SPELLFIX_COL_SOUNDSLIKE+2]);
|
|
+ int nSoundslike = sqlite3_value_bytes(argv[SPELLFIX_COL_SOUNDSLIKE+2]);
|
|
+ char *zK1, *zK2;
|
|
+ int i;
|
|
+ char c;
|
|
+ const char *zConflict = spellfix1GetConflict(db);
|
|
+
|
|
+ if( zWord==0 ){
|
|
+ /* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy');
|
|
+ ** cause zWord to be NULL, so we look at the "command" column to see
|
|
+ ** what special actions to take */
|
|
+ const char *zCmd =
|
|
+ (const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]);
|
|
+ if( zCmd==0 ){
|
|
+ pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word",
|
|
+ p->zTableName);
|
|
+ return SQLITE_CONSTRAINT_NOTNULL;
|
|
+ }
|
|
+ if( strcmp(zCmd,"reset")==0 ){
|
|
+ /* Reset the edit cost table (if there is one). */
|
|
+ editDist3ConfigDelete(p->pConfig3);
|
|
+ p->pConfig3 = 0;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ if( strncmp(zCmd,"edit_cost_table=",16)==0 ){
|
|
+ editDist3ConfigDelete(p->pConfig3);
|
|
+ p->pConfig3 = 0;
|
|
+ sqlite3_free(p->zCostTable);
|
|
+ p->zCostTable = spellfix1Dequote(zCmd+16);
|
|
+ if( p->zCostTable==0 ) return SQLITE_NOMEM;
|
|
+ if( p->zCostTable[0]==0 || sqlite3_stricmp(p->zCostTable,"null")==0 ){
|
|
+ sqlite3_free(p->zCostTable);
|
|
+ p->zCostTable = 0;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ pVTab->zErrMsg = sqlite3_mprintf("unknown value for %s.command: \"%w\"",
|
|
+ p->zTableName, zCmd);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ if( iRank<1 ) iRank = 1;
|
|
+ if( zSoundslike ){
|
|
+ zK1 = (char*)transliterate(zSoundslike, nSoundslike);
|
|
+ }else{
|
|
+ zK1 = (char*)transliterate(zWord, nWord);
|
|
+ }
|
|
+ if( zK1==0 ) return SQLITE_NOMEM;
|
|
+ for(i=0; (c = zK1[i])!=0; i++){
|
|
+ if( c>='A' && c<='Z' ) zK1[i] += 'a' - 'A';
|
|
+ }
|
|
+ zK2 = (char*)phoneticHash((const unsigned char*)zK1, i);
|
|
+ if( zK2==0 ){
|
|
+ sqlite3_free(zK1);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
|
+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ){
|
|
+ spellfix1DbExec(&rc, db,
|
|
+ "INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) "
|
|
+ "VALUES(%d,%d,%Q,nullif(%Q,%Q),%Q)",
|
|
+ p->zDbName, p->zTableName,
|
|
+ iRank, iLang, zWord, zK1, zWord, zK2
|
|
+ );
|
|
+ }else{
|
|
+ newRowid = sqlite3_value_int64(argv[1]);
|
|
+ spellfix1DbExec(&rc, db,
|
|
+ "INSERT OR %s INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) "
|
|
+ "VALUES(%lld,%d,%d,%Q,nullif(%Q,%Q),%Q)",
|
|
+ zConflict, p->zDbName, p->zTableName,
|
|
+ newRowid, iRank, iLang, zWord, zK1, zWord, zK2
|
|
+ );
|
|
+ }
|
|
+ *pRowid = sqlite3_last_insert_rowid(db);
|
|
+ }else{
|
|
+ rowid = sqlite3_value_int64(argv[0]);
|
|
+ newRowid = *pRowid = sqlite3_value_int64(argv[1]);
|
|
+ spellfix1DbExec(&rc, db,
|
|
+ "UPDATE OR %s \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d,"
|
|
+ " word=%Q, k1=nullif(%Q,%Q), k2=%Q WHERE id=%lld",
|
|
+ zConflict, p->zDbName, p->zTableName, newRowid, iRank, iLang,
|
|
+ zWord, zK1, zWord, zK2, rowid
|
|
+ );
|
|
+ }
|
|
+ sqlite3_free(zK1);
|
|
+ sqlite3_free(zK2);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Rename the spellfix1 table.
|
|
+*/
|
|
+static int spellfix1Rename(sqlite3_vtab *pVTab, const char *zNew){
|
|
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
|
+ sqlite3 *db = p->db;
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zNewName = sqlite3_mprintf("%s", zNew);
|
|
+ if( zNewName==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ spellfix1DbExec(&rc, db,
|
|
+ "ALTER TABLE \"%w\".\"%w_vocab\" RENAME TO \"%w_vocab\"",
|
|
+ p->zDbName, p->zTableName, zNewName
|
|
+ );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_free(p->zTableName);
|
|
+ p->zTableName = zNewName;
|
|
+ }else{
|
|
+ sqlite3_free(zNewName);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** A virtual table module that provides fuzzy search.
|
|
+*/
|
|
+static const sqlite3_module spellfix1Module = {
|
|
+ 0, /* iVersion */
|
|
+ spellfix1Create, /* xCreate - handle CREATE VIRTUAL TABLE */
|
|
+ spellfix1Connect, /* xConnect - reconnected to an existing table */
|
|
+ spellfix1BestIndex, /* xBestIndex - figure out how to do a query */
|
|
+ spellfix1Disconnect, /* xDisconnect - close a connection */
|
|
+ spellfix1Destroy, /* xDestroy - handle DROP TABLE */
|
|
+ spellfix1Open, /* xOpen - open a cursor */
|
|
+ spellfix1Close, /* xClose - close a cursor */
|
|
+ spellfix1Filter, /* xFilter - configure scan constraints */
|
|
+ spellfix1Next, /* xNext - advance a cursor */
|
|
+ spellfix1Eof, /* xEof - check for end of scan */
|
|
+ spellfix1Column, /* xColumn - read data */
|
|
+ spellfix1Rowid, /* xRowid - read data */
|
|
+ spellfix1Update, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ spellfix1Rename, /* xRename */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Register the various functions and the virtual table.
|
|
+*/
|
|
+static int spellfix1Register(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+ int i;
|
|
+ rc = sqlite3_create_function(db, "spellfix1_translit", 1,
|
|
+ SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,
|
|
+ transliterateSqlFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "spellfix1_editdist", 2,
|
|
+ SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,
|
|
+ editdistSqlFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "spellfix1_phonehash", 1,
|
|
+ SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,
|
|
+ phoneticHashSqlFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "spellfix1_scriptcode", 1,
|
|
+ SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,
|
|
+ scriptCodeSqlFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_module(db, "spellfix1", &spellfix1Module, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = editDist3Install(db);
|
|
+ }
|
|
+
|
|
+ /* Verify sanity of the translit[] table */
|
|
+ for(i=0; i<sizeof(translit)/sizeof(translit[0])-1; i++){
|
|
+ assert( translit[i].cFrom<translit[i+1].cFrom );
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+/*
|
|
+** Extension load function.
|
|
+*/
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_spellfix_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ return spellfix1Register(db);
|
|
+#endif
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ return spellfix1Register(db);
|
|
+#endif
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/sqlar.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/sqlar.c 2019-05-14 16:22:31.271286000 +0200
|
|
@@ -0,0 +1,121 @@
|
|
+/*
|
|
+** 2017-12-17
|
|
+**
|
|
+** 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.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** Utility functions sqlar_compress() and sqlar_uncompress(). Useful
|
|
+** for working with sqlar archives and used by the shell tool's built-in
|
|
+** sqlar support.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <zlib.h>
|
|
+
|
|
+/*
|
|
+** Implementation of the "sqlar_compress(X)" SQL function.
|
|
+**
|
|
+** If the type of X is SQLITE_BLOB, and compressing that blob using
|
|
+** zlib utility function compress() yields a smaller blob, return the
|
|
+** compressed blob. Otherwise, return a copy of X.
|
|
+**
|
|
+** SQLar uses the "zlib format" for compressed content. The zlib format
|
|
+** contains a two-byte identification header and a four-byte checksum at
|
|
+** the end. This is different from ZIP which uses the raw deflate format.
|
|
+**
|
|
+** Future enhancements to SQLar might add support for new compression formats.
|
|
+** If so, those new formats will be identified by alternative headers in the
|
|
+** compressed data.
|
|
+*/
|
|
+static void sqlarCompressFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ assert( argc==1 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
|
+ const Bytef *pData = sqlite3_value_blob(argv[0]);
|
|
+ uLong nData = sqlite3_value_bytes(argv[0]);
|
|
+ uLongf nOut = compressBound(nData);
|
|
+ Bytef *pOut;
|
|
+
|
|
+ pOut = (Bytef*)sqlite3_malloc(nOut);
|
|
+ if( pOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }else{
|
|
+ if( Z_OK!=compress(pOut, &nOut, pData, nData) ){
|
|
+ sqlite3_result_error(context, "error in compress()", -1);
|
|
+ }else if( nOut<nData ){
|
|
+ sqlite3_result_blob(context, pOut, nOut, SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ sqlite3_result_value(context, argv[0]);
|
|
+ }
|
|
+ sqlite3_free(pOut);
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_value(context, argv[0]);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the "sqlar_uncompress(X,SZ)" SQL function
|
|
+**
|
|
+** Parameter SZ is interpreted as an integer. If it is less than or
|
|
+** equal to zero, then this function returns a copy of X. Or, if
|
|
+** SZ is equal to the size of X when interpreted as a blob, also
|
|
+** return a copy of X. Otherwise, decompress blob X using zlib
|
|
+** utility function uncompress() and return the results (another
|
|
+** blob).
|
|
+*/
|
|
+static void sqlarUncompressFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ uLong nData;
|
|
+ uLongf sz;
|
|
+
|
|
+ assert( argc==2 );
|
|
+ sz = sqlite3_value_int(argv[1]);
|
|
+
|
|
+ if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
|
|
+ sqlite3_result_value(context, argv[0]);
|
|
+ }else{
|
|
+ const Bytef *pData= sqlite3_value_blob(argv[0]);
|
|
+ Bytef *pOut = sqlite3_malloc(sz);
|
|
+ if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){
|
|
+ sqlite3_result_error(context, "error in uncompress()", -1);
|
|
+ }else{
|
|
+ sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT);
|
|
+ }
|
|
+ sqlite3_free(pOut);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_sqlar_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "sqlar_compress", 1, SQLITE_UTF8, 0,
|
|
+ sqlarCompressFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sqlar_uncompress", 2, SQLITE_UTF8, 0,
|
|
+ sqlarUncompressFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/sqldiff.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/sqldiff.c 2019-03-05 13:14:40.864546200 +0100
|
|
@@ -0,0 +1,2006 @@
|
|
+/*
|
|
+** 2015-04-06
|
|
+**
|
|
+** 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 is a utility program that computes the differences in content
|
|
+** between two SQLite databases.
|
|
+**
|
|
+** To compile, simply link against SQLite.
|
|
+**
|
|
+** See the showHelp() routine below for a brief description of how to
|
|
+** run the utility.
|
|
+*/
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdarg.h>
|
|
+#include <ctype.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include "sqlite3.h"
|
|
+
|
|
+/*
|
|
+** All global variables are gathered into the "g" singleton.
|
|
+*/
|
|
+struct GlobalVars {
|
|
+ const char *zArgv0; /* Name of program */
|
|
+ int bSchemaOnly; /* Only show schema differences */
|
|
+ int bSchemaPK; /* Use the schema-defined PK, not the true PK */
|
|
+ int bHandleVtab; /* Handle fts3, fts4, fts5 and rtree vtabs */
|
|
+ unsigned fDebug; /* Debug flags */
|
|
+ sqlite3 *db; /* The database connection */
|
|
+} g;
|
|
+
|
|
+/*
|
|
+** Allowed values for g.fDebug
|
|
+*/
|
|
+#define DEBUG_COLUMN_NAMES 0x000001
|
|
+#define DEBUG_DIFF_SQL 0x000002
|
|
+
|
|
+/*
|
|
+** Dynamic string object
|
|
+*/
|
|
+typedef struct Str Str;
|
|
+struct Str {
|
|
+ char *z; /* Text of the string */
|
|
+ int nAlloc; /* Bytes allocated in z[] */
|
|
+ int nUsed; /* Bytes actually used in z[] */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Initialize a Str object
|
|
+*/
|
|
+static void strInit(Str *p){
|
|
+ p->z = 0;
|
|
+ p->nAlloc = 0;
|
|
+ p->nUsed = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Print an error resulting from faulting command-line arguments and
|
|
+** abort the program.
|
|
+*/
|
|
+static void cmdlineError(const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ fprintf(stderr, "%s: ", g.zArgv0);
|
|
+ va_start(ap, zFormat);
|
|
+ vfprintf(stderr, zFormat, ap);
|
|
+ va_end(ap);
|
|
+ fprintf(stderr, "\n\"%s --help\" for more help\n", g.zArgv0);
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Print an error message for an error that occurs at runtime, then
|
|
+** abort the program.
|
|
+*/
|
|
+static void runtimeError(const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ fprintf(stderr, "%s: ", g.zArgv0);
|
|
+ va_start(ap, zFormat);
|
|
+ vfprintf(stderr, zFormat, ap);
|
|
+ va_end(ap);
|
|
+ fprintf(stderr, "\n");
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free all memory held by a Str object
|
|
+*/
|
|
+static void strFree(Str *p){
|
|
+ sqlite3_free(p->z);
|
|
+ strInit(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add formatted text to the end of a Str object
|
|
+*/
|
|
+static void strPrintf(Str *p, const char *zFormat, ...){
|
|
+ int nNew;
|
|
+ for(;;){
|
|
+ if( p->z ){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFormat);
|
|
+ sqlite3_vsnprintf(p->nAlloc-p->nUsed, p->z+p->nUsed, zFormat, ap);
|
|
+ va_end(ap);
|
|
+ nNew = (int)strlen(p->z + p->nUsed);
|
|
+ }else{
|
|
+ nNew = p->nAlloc;
|
|
+ }
|
|
+ if( p->nUsed+nNew < p->nAlloc-1 ){
|
|
+ p->nUsed += nNew;
|
|
+ break;
|
|
+ }
|
|
+ p->nAlloc = p->nAlloc*2 + 1000;
|
|
+ p->z = sqlite3_realloc(p->z, p->nAlloc);
|
|
+ if( p->z==0 ) runtimeError("out of memory");
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/* Safely quote an SQL identifier. Use the minimum amount of transformation
|
|
+** necessary to allow the string to be used with %s.
|
|
+**
|
|
+** Space to hold the returned string is obtained from sqlite3_malloc(). The
|
|
+** caller is responsible for ensuring this space is freed when no longer
|
|
+** needed.
|
|
+*/
|
|
+static char *safeId(const char *zId){
|
|
+ int i, x;
|
|
+ char c;
|
|
+ if( zId[0]==0 ) return sqlite3_mprintf("\"\"");
|
|
+ for(i=x=0; (c = zId[i])!=0; i++){
|
|
+ if( !isalpha(c) && c!='_' ){
|
|
+ if( i>0 && isdigit(c) ){
|
|
+ x++;
|
|
+ }else{
|
|
+ return sqlite3_mprintf("\"%w\"", zId);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( x || !sqlite3_keyword_check(zId,i) ){
|
|
+ return sqlite3_mprintf("%s", zId);
|
|
+ }
|
|
+ return sqlite3_mprintf("\"%w\"", zId);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Prepare a new SQL statement. Print an error and abort if anything
|
|
+** goes wrong.
|
|
+*/
|
|
+static sqlite3_stmt *db_vprepare(const char *zFormat, va_list ap){
|
|
+ char *zSql;
|
|
+ int rc;
|
|
+ sqlite3_stmt *pStmt;
|
|
+
|
|
+ zSql = sqlite3_vmprintf(zFormat, ap);
|
|
+ if( zSql==0 ) runtimeError("out of memory");
|
|
+ rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0);
|
|
+ if( rc ){
|
|
+ runtimeError("SQL statement error: %s\n\"%s\"", sqlite3_errmsg(g.db),
|
|
+ zSql);
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+ return pStmt;
|
|
+}
|
|
+static sqlite3_stmt *db_prepare(const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ sqlite3_stmt *pStmt;
|
|
+ va_start(ap, zFormat);
|
|
+ pStmt = db_vprepare(zFormat, ap);
|
|
+ va_end(ap);
|
|
+ return pStmt;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free a list of strings
|
|
+*/
|
|
+static void namelistFree(char **az){
|
|
+ if( az ){
|
|
+ int i;
|
|
+ for(i=0; az[i]; i++) sqlite3_free(az[i]);
|
|
+ sqlite3_free(az);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a list of column names for the table zDb.zTab. Space to
|
|
+** hold the list is obtained from sqlite3_malloc() and should released
|
|
+** using namelistFree() when no longer needed.
|
|
+**
|
|
+** Primary key columns are listed first, followed by data columns.
|
|
+** The number of columns in the primary key is returned in *pnPkey.
|
|
+**
|
|
+** Normally, the "primary key" in the previous sentence is the true
|
|
+** primary key - the rowid or INTEGER PRIMARY KEY for ordinary tables
|
|
+** or the declared PRIMARY KEY for WITHOUT ROWID tables. However, if
|
|
+** the g.bSchemaPK flag is set, then the schema-defined PRIMARY KEY is
|
|
+** used in all cases. In that case, entries that have NULL values in
|
|
+** any of their primary key fields will be excluded from the analysis.
|
|
+**
|
|
+** If the primary key for a table is the rowid but rowid is inaccessible,
|
|
+** then this routine returns a NULL pointer.
|
|
+**
|
|
+** Examples:
|
|
+** CREATE TABLE t1(a INT UNIQUE, b INTEGER, c TEXT, PRIMARY KEY(c));
|
|
+** *pnPKey = 1;
|
|
+** az = { "rowid", "a", "b", "c", 0 } // Normal case
|
|
+** az = { "c", "a", "b", 0 } // g.bSchemaPK==1
|
|
+**
|
|
+** CREATE TABLE t2(a INT UNIQUE, b INTEGER, c TEXT, PRIMARY KEY(b));
|
|
+** *pnPKey = 1;
|
|
+** az = { "b", "a", "c", 0 }
|
|
+**
|
|
+** CREATE TABLE t3(x,y,z,PRIMARY KEY(y,z));
|
|
+** *pnPKey = 1 // Normal case
|
|
+** az = { "rowid", "x", "y", "z", 0 } // Normal case
|
|
+** *pnPKey = 2 // g.bSchemaPK==1
|
|
+** az = { "y", "x", "z", 0 } // g.bSchemaPK==1
|
|
+**
|
|
+** CREATE TABLE t4(x,y,z,PRIMARY KEY(y,z)) WITHOUT ROWID;
|
|
+** *pnPKey = 2
|
|
+** az = { "y", "z", "x", 0 }
|
|
+**
|
|
+** CREATE TABLE t5(rowid,_rowid_,oid);
|
|
+** az = 0 // The rowid is not accessible
|
|
+*/
|
|
+static char **columnNames(
|
|
+ const char *zDb, /* Database ("main" or "aux") to query */
|
|
+ const char *zTab, /* Name of table to return details of */
|
|
+ int *pnPKey, /* OUT: Number of PK columns */
|
|
+ int *pbRowid /* OUT: True if PK is an implicit rowid */
|
|
+){
|
|
+ char **az = 0; /* List of column names to be returned */
|
|
+ int naz = 0; /* Number of entries in az[] */
|
|
+ sqlite3_stmt *pStmt; /* SQL statement being run */
|
|
+ char *zPkIdxName = 0; /* Name of the PRIMARY KEY index */
|
|
+ int truePk = 0; /* PRAGMA table_info indentifies the PK to use */
|
|
+ int nPK = 0; /* Number of PRIMARY KEY columns */
|
|
+ int i, j; /* Loop counters */
|
|
+
|
|
+ if( g.bSchemaPK==0 ){
|
|
+ /* Normal case: Figure out what the true primary key is for the table.
|
|
+ ** * For WITHOUT ROWID tables, the true primary key is the same as
|
|
+ ** the schema PRIMARY KEY, which is guaranteed to be present.
|
|
+ ** * For rowid tables with an INTEGER PRIMARY KEY, the true primary
|
|
+ ** key is the INTEGER PRIMARY KEY.
|
|
+ ** * For all other rowid tables, the rowid is the true primary key.
|
|
+ */
|
|
+ pStmt = db_prepare("PRAGMA %s.index_list=%Q", zDb, zTab);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ if( sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,3),"pk")==0 ){
|
|
+ zPkIdxName = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( zPkIdxName ){
|
|
+ int nKey = 0;
|
|
+ int nCol = 0;
|
|
+ truePk = 0;
|
|
+ pStmt = db_prepare("PRAGMA %s.index_xinfo=%Q", zDb, zPkIdxName);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ nCol++;
|
|
+ if( sqlite3_column_int(pStmt,5) ){ nKey++; continue; }
|
|
+ if( sqlite3_column_int(pStmt,1)>=0 ) truePk = 1;
|
|
+ }
|
|
+ if( nCol==nKey ) truePk = 1;
|
|
+ if( truePk ){
|
|
+ nPK = nKey;
|
|
+ }else{
|
|
+ nPK = 1;
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ sqlite3_free(zPkIdxName);
|
|
+ }else{
|
|
+ truePk = 1;
|
|
+ nPK = 1;
|
|
+ }
|
|
+ pStmt = db_prepare("PRAGMA %s.table_info=%Q", zDb, zTab);
|
|
+ }else{
|
|
+ /* The g.bSchemaPK==1 case: Use whatever primary key is declared
|
|
+ ** in the schema. The "rowid" will still be used as the primary key
|
|
+ ** if the table definition does not contain a PRIMARY KEY.
|
|
+ */
|
|
+ nPK = 0;
|
|
+ pStmt = db_prepare("PRAGMA %s.table_info=%Q", zDb, zTab);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ if( sqlite3_column_int(pStmt,5)>0 ) nPK++;
|
|
+ }
|
|
+ sqlite3_reset(pStmt);
|
|
+ if( nPK==0 ) nPK = 1;
|
|
+ truePk = 1;
|
|
+ }
|
|
+ *pnPKey = nPK;
|
|
+ naz = nPK;
|
|
+ az = sqlite3_malloc( sizeof(char*)*(nPK+1) );
|
|
+ if( az==0 ) runtimeError("out of memory");
|
|
+ memset(az, 0, sizeof(char*)*(nPK+1));
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ int iPKey;
|
|
+ if( truePk && (iPKey = sqlite3_column_int(pStmt,5))>0 ){
|
|
+ az[iPKey-1] = safeId((char*)sqlite3_column_text(pStmt,1));
|
|
+ }else{
|
|
+ az = sqlite3_realloc(az, sizeof(char*)*(naz+2) );
|
|
+ if( az==0 ) runtimeError("out of memory");
|
|
+ az[naz++] = safeId((char*)sqlite3_column_text(pStmt,1));
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( az ) az[naz] = 0;
|
|
+
|
|
+ /* If it is non-NULL, set *pbRowid to indicate whether or not the PK of
|
|
+ ** this table is an implicit rowid (*pbRowid==1) or not (*pbRowid==0). */
|
|
+ if( pbRowid ) *pbRowid = (az[0]==0);
|
|
+
|
|
+ /* If this table has an implicit rowid for a PK, figure out how to refer
|
|
+ ** to it. There are three options - "rowid", "_rowid_" and "oid". Any
|
|
+ ** of these will work, unless the table has an explicit column of the
|
|
+ ** same name. */
|
|
+ if( az[0]==0 ){
|
|
+ const char *azRowid[] = { "rowid", "_rowid_", "oid" };
|
|
+ for(i=0; i<sizeof(azRowid)/sizeof(azRowid[0]); i++){
|
|
+ for(j=1; j<naz; j++){
|
|
+ if( sqlite3_stricmp(az[j], azRowid[i])==0 ) break;
|
|
+ }
|
|
+ if( j>=naz ){
|
|
+ az[0] = sqlite3_mprintf("%s", azRowid[i]);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( az[0]==0 ){
|
|
+ for(i=1; i<naz; i++) sqlite3_free(az[i]);
|
|
+ sqlite3_free(az);
|
|
+ az = 0;
|
|
+ }
|
|
+ }
|
|
+ return az;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Print the sqlite3_value X as an SQL literal.
|
|
+*/
|
|
+static void printQuoted(FILE *out, sqlite3_value *X){
|
|
+ switch( sqlite3_value_type(X) ){
|
|
+ case SQLITE_FLOAT: {
|
|
+ double r1;
|
|
+ char zBuf[50];
|
|
+ r1 = sqlite3_value_double(X);
|
|
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
|
|
+ fprintf(out, "%s", zBuf);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INTEGER: {
|
|
+ fprintf(out, "%lld", sqlite3_value_int64(X));
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_BLOB: {
|
|
+ const unsigned char *zBlob = sqlite3_value_blob(X);
|
|
+ int nBlob = sqlite3_value_bytes(X);
|
|
+ if( zBlob ){
|
|
+ int i;
|
|
+ fprintf(out, "x'");
|
|
+ for(i=0; i<nBlob; i++){
|
|
+ fprintf(out, "%02x", zBlob[i]);
|
|
+ }
|
|
+ fprintf(out, "'");
|
|
+ }else{
|
|
+ /* Could be an OOM, could be a zero-byte blob */
|
|
+ fprintf(out, "X''");
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ const unsigned char *zArg = sqlite3_value_text(X);
|
|
+ int i, j;
|
|
+
|
|
+ if( zArg==0 ){
|
|
+ fprintf(out, "NULL");
|
|
+ }else{
|
|
+ fprintf(out, "'");
|
|
+ for(i=j=0; zArg[i]; i++){
|
|
+ if( zArg[i]=='\'' ){
|
|
+ fprintf(out, "%.*s'", i-j+1, &zArg[j]);
|
|
+ j = i+1;
|
|
+ }
|
|
+ }
|
|
+ fprintf(out, "%s'", &zArg[j]);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_NULL: {
|
|
+ fprintf(out, "NULL");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Output SQL that will recreate the aux.zTab table.
|
|
+*/
|
|
+static void dump_table(const char *zTab, FILE *out){
|
|
+ char *zId = safeId(zTab); /* Name of the table */
|
|
+ char **az = 0; /* List of columns */
|
|
+ int nPk; /* Number of true primary key columns */
|
|
+ int nCol; /* Number of data columns */
|
|
+ int i; /* Loop counter */
|
|
+ sqlite3_stmt *pStmt; /* SQL statement */
|
|
+ const char *zSep; /* Separator string */
|
|
+ Str ins; /* Beginning of the INSERT statement */
|
|
+
|
|
+ pStmt = db_prepare("SELECT sql FROM aux.sqlite_master WHERE name=%Q", zTab);
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( !g.bSchemaOnly ){
|
|
+ az = columnNames("aux", zTab, &nPk, 0);
|
|
+ strInit(&ins);
|
|
+ if( az==0 ){
|
|
+ pStmt = db_prepare("SELECT * FROM aux.%s", zId);
|
|
+ strPrintf(&ins,"INSERT INTO %s VALUES", zId);
|
|
+ }else{
|
|
+ Str sql;
|
|
+ strInit(&sql);
|
|
+ zSep = "SELECT";
|
|
+ for(i=0; az[i]; i++){
|
|
+ strPrintf(&sql, "%s %s", zSep, az[i]);
|
|
+ zSep = ",";
|
|
+ }
|
|
+ strPrintf(&sql," FROM aux.%s", zId);
|
|
+ zSep = " ORDER BY";
|
|
+ for(i=1; i<=nPk; i++){
|
|
+ strPrintf(&sql, "%s %d", zSep, i);
|
|
+ zSep = ",";
|
|
+ }
|
|
+ pStmt = db_prepare("%s", sql.z);
|
|
+ strFree(&sql);
|
|
+ strPrintf(&ins, "INSERT INTO %s", zId);
|
|
+ zSep = "(";
|
|
+ for(i=0; az[i]; i++){
|
|
+ strPrintf(&ins, "%s%s", zSep, az[i]);
|
|
+ zSep = ",";
|
|
+ }
|
|
+ strPrintf(&ins,") VALUES");
|
|
+ namelistFree(az);
|
|
+ }
|
|
+ nCol = sqlite3_column_count(pStmt);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ fprintf(out, "%s",ins.z);
|
|
+ zSep = "(";
|
|
+ for(i=0; i<nCol; i++){
|
|
+ fprintf(out, "%s",zSep);
|
|
+ printQuoted(out, sqlite3_column_value(pStmt,i));
|
|
+ zSep = ",";
|
|
+ }
|
|
+ fprintf(out, ");\n");
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ strFree(&ins);
|
|
+ } /* endif !g.bSchemaOnly */
|
|
+ pStmt = db_prepare("SELECT sql FROM aux.sqlite_master"
|
|
+ " WHERE type='index' AND tbl_name=%Q AND sql IS NOT NULL",
|
|
+ zTab);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Compute all differences for a single table.
|
|
+*/
|
|
+static void diff_one_table(const char *zTab, FILE *out){
|
|
+ char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
|
|
+ char **az = 0; /* Columns in main */
|
|
+ char **az2 = 0; /* Columns in aux */
|
|
+ int nPk; /* Primary key columns in main */
|
|
+ int nPk2; /* Primary key columns in aux */
|
|
+ int n = 0; /* Number of columns in main */
|
|
+ int n2; /* Number of columns in aux */
|
|
+ int nQ; /* Number of output columns in the diff query */
|
|
+ int i; /* Loop counter */
|
|
+ const char *zSep; /* Separator string */
|
|
+ Str sql; /* Comparison query */
|
|
+ sqlite3_stmt *pStmt; /* Query statement to do the diff */
|
|
+
|
|
+ strInit(&sql);
|
|
+ if( g.fDebug==DEBUG_COLUMN_NAMES ){
|
|
+ /* Simply run columnNames() on all tables of the origin
|
|
+ ** database and show the results. This is used for testing
|
|
+ ** and debugging of the columnNames() function.
|
|
+ */
|
|
+ az = columnNames("aux",zTab, &nPk, 0);
|
|
+ if( az==0 ){
|
|
+ printf("Rowid not accessible for %s\n", zId);
|
|
+ }else{
|
|
+ printf("%s:", zId);
|
|
+ for(i=0; az[i]; i++){
|
|
+ printf(" %s", az[i]);
|
|
+ if( i+1==nPk ) printf(" *");
|
|
+ }
|
|
+ printf("\n");
|
|
+ }
|
|
+ goto end_diff_one_table;
|
|
+ }
|
|
+
|
|
+
|
|
+ if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){
|
|
+ if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
|
|
+ /* Table missing from second database. */
|
|
+ fprintf(out, "DROP TABLE %s;\n", zId);
|
|
+ }
|
|
+ goto end_diff_one_table;
|
|
+ }
|
|
+
|
|
+ if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
|
|
+ /* Table missing from source */
|
|
+ dump_table(zTab, out);
|
|
+ goto end_diff_one_table;
|
|
+ }
|
|
+
|
|
+ az = columnNames("main", zTab, &nPk, 0);
|
|
+ az2 = columnNames("aux", zTab, &nPk2, 0);
|
|
+ if( az && az2 ){
|
|
+ for(n=0; az[n] && az2[n]; n++){
|
|
+ if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
|
|
+ }
|
|
+ }
|
|
+ if( az==0
|
|
+ || az2==0
|
|
+ || nPk!=nPk2
|
|
+ || az[n]
|
|
+ ){
|
|
+ /* Schema mismatch */
|
|
+ fprintf(out, "DROP TABLE %s; -- due to schema mismatch\n", zId);
|
|
+ dump_table(zTab, out);
|
|
+ goto end_diff_one_table;
|
|
+ }
|
|
+
|
|
+ /* Build the comparison query */
|
|
+ for(n2=n; az2[n2]; n2++){
|
|
+ fprintf(out, "ALTER TABLE %s ADD COLUMN %s;\n", zId, safeId(az2[n2]));
|
|
+ }
|
|
+ nQ = nPk2+1+2*(n2-nPk2);
|
|
+ if( n2>nPk2 ){
|
|
+ zSep = "SELECT ";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%sB.%s", zSep, az[i]);
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ strPrintf(&sql, ", 1%s -- changed row\n", nPk==n ? "" : ",");
|
|
+ while( az[i] ){
|
|
+ strPrintf(&sql, " A.%s IS NOT B.%s, B.%s%s\n",
|
|
+ az[i], az2[i], az2[i], az2[i+1]==0 ? "" : ",");
|
|
+ i++;
|
|
+ }
|
|
+ while( az2[i] ){
|
|
+ strPrintf(&sql, " B.%s IS NOT NULL, B.%s%s\n",
|
|
+ az2[i], az2[i], az2[i+1]==0 ? "" : ",");
|
|
+ i++;
|
|
+ }
|
|
+ strPrintf(&sql, " FROM main.%s A, aux.%s B\n", zId, zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ zSep = "\n AND (";
|
|
+ while( az[i] ){
|
|
+ strPrintf(&sql, "%sA.%s IS NOT B.%s%s\n",
|
|
+ zSep, az[i], az2[i], az2[i+1]==0 ? ")" : "");
|
|
+ zSep = " OR ";
|
|
+ i++;
|
|
+ }
|
|
+ while( az2[i] ){
|
|
+ strPrintf(&sql, "%sB.%s IS NOT NULL%s\n",
|
|
+ zSep, az2[i], az2[i+1]==0 ? ")" : "");
|
|
+ zSep = " OR ";
|
|
+ i++;
|
|
+ }
|
|
+ strPrintf(&sql, " UNION ALL\n");
|
|
+ }
|
|
+ zSep = "SELECT ";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%sA.%s", zSep, az[i]);
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ strPrintf(&sql, ", 2%s -- deleted row\n", nPk==n ? "" : ",");
|
|
+ while( az2[i] ){
|
|
+ strPrintf(&sql, " NULL, NULL%s\n", i==n2-1 ? "" : ",");
|
|
+ i++;
|
|
+ }
|
|
+ strPrintf(&sql, " FROM main.%s A\n", zId);
|
|
+ strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B\n", zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n");
|
|
+ zSep = " UNION ALL\nSELECT ";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%sB.%s", zSep, az[i]);
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ strPrintf(&sql, ", 3%s -- inserted row\n", nPk==n ? "" : ",");
|
|
+ while( az2[i] ){
|
|
+ strPrintf(&sql, " 1, B.%s%s\n", az2[i], az2[i+1]==0 ? "" : ",");
|
|
+ i++;
|
|
+ }
|
|
+ strPrintf(&sql, " FROM aux.%s B\n", zId);
|
|
+ strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A\n", zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n ORDER BY");
|
|
+ zSep = " ";
|
|
+ for(i=1; i<=nPk; i++){
|
|
+ strPrintf(&sql, "%s%d", zSep, i);
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ strPrintf(&sql, ";\n");
|
|
+
|
|
+ if( g.fDebug & DEBUG_DIFF_SQL ){
|
|
+ printf("SQL for %s:\n%s\n", zId, sql.z);
|
|
+ goto end_diff_one_table;
|
|
+ }
|
|
+
|
|
+ /* Drop indexes that are missing in the destination */
|
|
+ pStmt = db_prepare(
|
|
+ "SELECT name FROM main.sqlite_master"
|
|
+ " WHERE type='index' AND tbl_name=%Q"
|
|
+ " AND sql IS NOT NULL"
|
|
+ " AND sql NOT IN (SELECT sql FROM aux.sqlite_master"
|
|
+ " WHERE type='index' AND tbl_name=%Q"
|
|
+ " AND sql IS NOT NULL)",
|
|
+ zTab, zTab);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ char *z = safeId((const char*)sqlite3_column_text(pStmt,0));
|
|
+ fprintf(out, "DROP INDEX %s;\n", z);
|
|
+ sqlite3_free(z);
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+
|
|
+ /* Run the query and output differences */
|
|
+ if( !g.bSchemaOnly ){
|
|
+ pStmt = db_prepare("%s", sql.z);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ int iType = sqlite3_column_int(pStmt, nPk);
|
|
+ if( iType==1 || iType==2 ){
|
|
+ if( iType==1 ){ /* Change the content of a row */
|
|
+ fprintf(out, "UPDATE %s", zId);
|
|
+ zSep = " SET";
|
|
+ for(i=nPk+1; i<nQ; i+=2){
|
|
+ if( sqlite3_column_int(pStmt,i)==0 ) continue;
|
|
+ fprintf(out, "%s %s=", zSep, az2[(i+nPk-1)/2]);
|
|
+ zSep = ",";
|
|
+ printQuoted(out, sqlite3_column_value(pStmt,i+1));
|
|
+ }
|
|
+ }else{ /* Delete a row */
|
|
+ fprintf(out, "DELETE FROM %s", zId);
|
|
+ }
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ fprintf(out, "%s %s=", zSep, az2[i]);
|
|
+ printQuoted(out, sqlite3_column_value(pStmt,i));
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ fprintf(out, ";\n");
|
|
+ }else{ /* Insert a row */
|
|
+ fprintf(out, "INSERT INTO %s(%s", zId, az2[0]);
|
|
+ for(i=1; az2[i]; i++) fprintf(out, ",%s", az2[i]);
|
|
+ fprintf(out, ") VALUES");
|
|
+ zSep = "(";
|
|
+ for(i=0; i<nPk2; i++){
|
|
+ fprintf(out, "%s", zSep);
|
|
+ zSep = ",";
|
|
+ printQuoted(out, sqlite3_column_value(pStmt,i));
|
|
+ }
|
|
+ for(i=nPk2+2; i<nQ; i+=2){
|
|
+ fprintf(out, ",");
|
|
+ printQuoted(out, sqlite3_column_value(pStmt,i));
|
|
+ }
|
|
+ fprintf(out, ");\n");
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ } /* endif !g.bSchemaOnly */
|
|
+
|
|
+ /* Create indexes that are missing in the source */
|
|
+ pStmt = db_prepare(
|
|
+ "SELECT sql FROM aux.sqlite_master"
|
|
+ " WHERE type='index' AND tbl_name=%Q"
|
|
+ " AND sql IS NOT NULL"
|
|
+ " AND sql NOT IN (SELECT sql FROM main.sqlite_master"
|
|
+ " WHERE type='index' AND tbl_name=%Q"
|
|
+ " AND sql IS NOT NULL)",
|
|
+ zTab, zTab);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+
|
|
+end_diff_one_table:
|
|
+ strFree(&sql);
|
|
+ sqlite3_free(zId);
|
|
+ namelistFree(az);
|
|
+ namelistFree(az2);
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check that table zTab exists and has the same schema in both the "main"
|
|
+** and "aux" databases currently opened by the global db handle. If they
|
|
+** do not, output an error message on stderr and exit(1). Otherwise, if
|
|
+** the schemas do match, return control to the caller.
|
|
+*/
|
|
+static void checkSchemasMatch(const char *zTab){
|
|
+ sqlite3_stmt *pStmt = db_prepare(
|
|
+ "SELECT A.sql=B.sql FROM main.sqlite_master A, aux.sqlite_master B"
|
|
+ " WHERE A.name=%Q AND B.name=%Q", zTab, zTab
|
|
+ );
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ if( sqlite3_column_int(pStmt,0)==0 ){
|
|
+ runtimeError("schema changes for table %s", safeId(zTab));
|
|
+ }
|
|
+ }else{
|
|
+ runtimeError("table %s missing from one or both databases", safeId(zTab));
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+}
|
|
+
|
|
+/**************************************************************************
|
|
+** The following code is copied from fossil. It is used to generate the
|
|
+** fossil delta blobs sometimes used in RBU update records.
|
|
+*/
|
|
+
|
|
+typedef unsigned short u16;
|
|
+typedef unsigned int u32;
|
|
+typedef unsigned char u8;
|
|
+
|
|
+/*
|
|
+** The width of a hash window in bytes. The algorithm only works if this
|
|
+** is a power of 2.
|
|
+*/
|
|
+#define NHASH 16
|
|
+
|
|
+/*
|
|
+** The current state of the rolling hash.
|
|
+**
|
|
+** z[] holds the values that have been hashed. z[] is a circular buffer.
|
|
+** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of
|
|
+** the window.
|
|
+**
|
|
+** Hash.a is the sum of all elements of hash.z[]. Hash.b is a weighted
|
|
+** sum. Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1.
|
|
+** (Each index for z[] should be module NHASH, of course. The %NHASH operator
|
|
+** is omitted in the prior expression for brevity.)
|
|
+*/
|
|
+typedef struct hash hash;
|
|
+struct hash {
|
|
+ u16 a, b; /* Hash values */
|
|
+ u16 i; /* Start of the hash window */
|
|
+ char z[NHASH]; /* The values that have been hashed */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Initialize the rolling hash using the first NHASH characters of z[]
|
|
+*/
|
|
+static void hash_init(hash *pHash, const char *z){
|
|
+ u16 a, b, i;
|
|
+ a = b = 0;
|
|
+ for(i=0; i<NHASH; i++){
|
|
+ a += z[i];
|
|
+ b += (NHASH-i)*z[i];
|
|
+ pHash->z[i] = z[i];
|
|
+ }
|
|
+ pHash->a = a & 0xffff;
|
|
+ pHash->b = b & 0xffff;
|
|
+ pHash->i = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance the rolling hash by a single character "c"
|
|
+*/
|
|
+static void hash_next(hash *pHash, int c){
|
|
+ u16 old = pHash->z[pHash->i];
|
|
+ pHash->z[pHash->i] = (char)c;
|
|
+ pHash->i = (pHash->i+1)&(NHASH-1);
|
|
+ pHash->a = pHash->a - old + (char)c;
|
|
+ pHash->b = pHash->b - NHASH*old + pHash->a;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a 32-bit hash value
|
|
+*/
|
|
+static u32 hash_32bit(hash *pHash){
|
|
+ return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write an base-64 integer into the given buffer.
|
|
+*/
|
|
+static void putInt(unsigned int v, char **pz){
|
|
+ static const char zDigits[] =
|
|
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
|
|
+ /* 123456789 123456789 123456789 123456789 123456789 123456789 123 */
|
|
+ int i, j;
|
|
+ char zBuf[20];
|
|
+ if( v==0 ){
|
|
+ *(*pz)++ = '0';
|
|
+ return;
|
|
+ }
|
|
+ for(i=0; v>0; i++, v>>=6){
|
|
+ zBuf[i] = zDigits[v&0x3f];
|
|
+ }
|
|
+ for(j=i-1; j>=0; j--){
|
|
+ *(*pz)++ = zBuf[j];
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the number digits in the base-64 representation of a positive integer
|
|
+*/
|
|
+static int digit_count(int v){
|
|
+ unsigned int i, x;
|
|
+ for(i=1, x=64; (unsigned int)v>=x; i++, x <<= 6){}
|
|
+ return i;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute a 32-bit checksum on the N-byte buffer. Return the result.
|
|
+*/
|
|
+static unsigned int checksum(const char *zIn, size_t N){
|
|
+ const unsigned char *z = (const unsigned char *)zIn;
|
|
+ unsigned sum0 = 0;
|
|
+ unsigned sum1 = 0;
|
|
+ unsigned sum2 = 0;
|
|
+ unsigned sum3 = 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]);
|
|
+ sum3 += ((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];
|
|
+ sum3 += z[3];
|
|
+ z += 4;
|
|
+ N -= 4;
|
|
+ }
|
|
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
|
|
+ switch(N){
|
|
+ case 3: sum3 += (z[2] << 8);
|
|
+ case 2: sum3 += (z[1] << 16);
|
|
+ case 1: sum3 += (z[0] << 24);
|
|
+ default: ;
|
|
+ }
|
|
+ return sum3;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create a new delta.
|
|
+**
|
|
+** The delta is written into a preallocated buffer, zDelta, which
|
|
+** should be at least 60 bytes longer than the target file, zOut.
|
|
+** The delta string will be NUL-terminated, but it might also contain
|
|
+** embedded NUL characters if either the zSrc or zOut files are
|
|
+** binary. This function returns the length of the delta string
|
|
+** in bytes, excluding the final NUL terminator character.
|
|
+**
|
|
+** Output Format:
|
|
+**
|
|
+** The delta begins with a base64 number followed by a newline. This
|
|
+** number is the number of bytes in the TARGET file. Thus, given a
|
|
+** delta file z, a program can compute the size of the output file
|
|
+** simply by reading the first line and decoding the base-64 number
|
|
+** found there. The delta_output_size() routine does exactly this.
|
|
+**
|
|
+** After the initial size number, the delta consists of a series of
|
|
+** literal text segments and commands to copy from the SOURCE file.
|
|
+** A copy command looks like this:
|
|
+**
|
|
+** NNN@MMM,
|
|
+**
|
|
+** where NNN is the number of bytes to be copied and MMM is the offset
|
|
+** into the source file of the first byte (both base-64). If NNN is 0
|
|
+** it means copy the rest of the input file. Literal text is like this:
|
|
+**
|
|
+** NNN:TTTTT
|
|
+**
|
|
+** where NNN is the number of bytes of text (base-64) and TTTTT is the text.
|
|
+**
|
|
+** The last term is of the form
|
|
+**
|
|
+** NNN;
|
|
+**
|
|
+** In this case, NNN is a 32-bit bigendian checksum of the output file
|
|
+** that can be used to verify that the delta applied correctly. All
|
|
+** numbers are in base-64.
|
|
+**
|
|
+** Pure text files generate a pure text delta. Binary files generate a
|
|
+** delta that may contain some binary data.
|
|
+**
|
|
+** Algorithm:
|
|
+**
|
|
+** The encoder first builds a hash table to help it find matching
|
|
+** patterns in the source file. 16-byte chunks of the source file
|
|
+** sampled at evenly spaced intervals are used to populate the hash
|
|
+** table.
|
|
+**
|
|
+** Next we begin scanning the target file using a sliding 16-byte
|
|
+** window. The hash of the 16-byte window in the target is used to
|
|
+** search for a matching section in the source file. When a match
|
|
+** is found, a copy command is added to the delta. An effort is
|
|
+** made to extend the matching section to regions that come before
|
|
+** and after the 16-byte hash window. A copy command is only issued
|
|
+** if the result would use less space that just quoting the text
|
|
+** literally. Literal text is added to the delta for sections that
|
|
+** do not match or which can not be encoded efficiently using copy
|
|
+** commands.
|
|
+*/
|
|
+static int rbuDeltaCreate(
|
|
+ const char *zSrc, /* The source or pattern file */
|
|
+ unsigned int lenSrc, /* Length of the source file */
|
|
+ const char *zOut, /* The target file */
|
|
+ unsigned int lenOut, /* Length of the target file */
|
|
+ char *zDelta /* Write the delta into this buffer */
|
|
+){
|
|
+ unsigned int i, base;
|
|
+ char *zOrigDelta = zDelta;
|
|
+ hash h;
|
|
+ int nHash; /* Number of hash table entries */
|
|
+ int *landmark; /* Primary hash table */
|
|
+ int *collide; /* Collision chain */
|
|
+ int lastRead = -1; /* Last byte of zSrc read by a COPY command */
|
|
+
|
|
+ /* Add the target file size to the beginning of the delta
|
|
+ */
|
|
+ putInt(lenOut, &zDelta);
|
|
+ *(zDelta++) = '\n';
|
|
+
|
|
+ /* If the source file is very small, it means that we have no
|
|
+ ** chance of ever doing a copy command. Just output a single
|
|
+ ** literal segment for the entire target and exit.
|
|
+ */
|
|
+ if( lenSrc<=NHASH ){
|
|
+ putInt(lenOut, &zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, zOut, lenOut);
|
|
+ zDelta += lenOut;
|
|
+ putInt(checksum(zOut, lenOut), &zDelta);
|
|
+ *(zDelta++) = ';';
|
|
+ return (int)(zDelta - zOrigDelta);
|
|
+ }
|
|
+
|
|
+ /* Compute the hash table used to locate matching sections in the
|
|
+ ** source file.
|
|
+ */
|
|
+ nHash = lenSrc/NHASH;
|
|
+ collide = sqlite3_malloc( nHash*2*sizeof(int) );
|
|
+ landmark = &collide[nHash];
|
|
+ memset(landmark, -1, nHash*sizeof(int));
|
|
+ memset(collide, -1, nHash*sizeof(int));
|
|
+ for(i=0; i<lenSrc-NHASH; i+=NHASH){
|
|
+ int hv;
|
|
+ hash_init(&h, &zSrc[i]);
|
|
+ hv = hash_32bit(&h) % nHash;
|
|
+ collide[i/NHASH] = landmark[hv];
|
|
+ landmark[hv] = i/NHASH;
|
|
+ }
|
|
+
|
|
+ /* Begin scanning the target file and generating copy commands and
|
|
+ ** literal sections of the delta.
|
|
+ */
|
|
+ base = 0; /* We have already generated everything before zOut[base] */
|
|
+ while( base+NHASH<lenOut ){
|
|
+ int iSrc, iBlock;
|
|
+ int bestCnt, bestOfst=0, bestLitsz=0;
|
|
+ hash_init(&h, &zOut[base]);
|
|
+ i = 0; /* Trying to match a landmark against zOut[base+i] */
|
|
+ bestCnt = 0;
|
|
+ while( 1 ){
|
|
+ int hv;
|
|
+ int limit = 250;
|
|
+
|
|
+ hv = hash_32bit(&h) % nHash;
|
|
+ iBlock = landmark[hv];
|
|
+ while( iBlock>=0 && (limit--)>0 ){
|
|
+ /*
|
|
+ ** The hash window has identified a potential match against
|
|
+ ** landmark block iBlock. But we need to investigate further.
|
|
+ **
|
|
+ ** Look for a region in zOut that matches zSrc. Anchor the search
|
|
+ ** at zSrc[iSrc] and zOut[base+i]. Do not include anything prior to
|
|
+ ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen].
|
|
+ **
|
|
+ ** Set cnt equal to the length of the match and set ofst so that
|
|
+ ** zSrc[ofst] is the first element of the match. litsz is the number
|
|
+ ** of characters between zOut[base] and the beginning of the match.
|
|
+ ** sz will be the overhead (in bytes) needed to encode the copy
|
|
+ ** command. Only generate copy command if the overhead of the
|
|
+ ** copy command is less than the amount of literal text to be copied.
|
|
+ */
|
|
+ int cnt, ofst, litsz;
|
|
+ int j, k, x, y;
|
|
+ int sz;
|
|
+
|
|
+ /* Beginning at iSrc, match forwards as far as we can. j counts
|
|
+ ** the number of characters that match */
|
|
+ iSrc = iBlock*NHASH;
|
|
+ for(
|
|
+ j=0, x=iSrc, y=base+i;
|
|
+ (unsigned int)x<lenSrc && (unsigned int)y<lenOut;
|
|
+ j++, x++, y++
|
|
+ ){
|
|
+ if( zSrc[x]!=zOut[y] ) break;
|
|
+ }
|
|
+ j--;
|
|
+
|
|
+ /* Beginning at iSrc-1, match backwards as far as we can. k counts
|
|
+ ** the number of characters that match */
|
|
+ for(k=1; k<iSrc && (unsigned int)k<=i; k++){
|
|
+ if( zSrc[iSrc-k]!=zOut[base+i-k] ) break;
|
|
+ }
|
|
+ k--;
|
|
+
|
|
+ /* Compute the offset and size of the matching region */
|
|
+ ofst = iSrc-k;
|
|
+ cnt = j+k+1;
|
|
+ litsz = i-k; /* Number of bytes of literal text before the copy */
|
|
+ /* sz will hold the number of bytes needed to encode the "insert"
|
|
+ ** command and the copy command, not counting the "insert" text */
|
|
+ sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3;
|
|
+ if( cnt>=sz && cnt>bestCnt ){
|
|
+ /* Remember this match only if it is the best so far and it
|
|
+ ** does not increase the file size */
|
|
+ bestCnt = cnt;
|
|
+ bestOfst = iSrc-k;
|
|
+ bestLitsz = litsz;
|
|
+ }
|
|
+
|
|
+ /* Check the next matching block */
|
|
+ iBlock = collide[iBlock];
|
|
+ }
|
|
+
|
|
+ /* We have a copy command that does not cause the delta to be larger
|
|
+ ** than a literal insert. So add the copy command to the delta.
|
|
+ */
|
|
+ if( bestCnt>0 ){
|
|
+ if( bestLitsz>0 ){
|
|
+ /* Add an insert command before the copy */
|
|
+ putInt(bestLitsz,&zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, &zOut[base], bestLitsz);
|
|
+ zDelta += bestLitsz;
|
|
+ base += bestLitsz;
|
|
+ }
|
|
+ base += bestCnt;
|
|
+ putInt(bestCnt, &zDelta);
|
|
+ *(zDelta++) = '@';
|
|
+ putInt(bestOfst, &zDelta);
|
|
+ *(zDelta++) = ',';
|
|
+ if( bestOfst + bestCnt -1 > lastRead ){
|
|
+ lastRead = bestOfst + bestCnt - 1;
|
|
+ }
|
|
+ bestCnt = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* If we reach this point, it means no match is found so far */
|
|
+ if( base+i+NHASH>=lenOut ){
|
|
+ /* We have reached the end of the file and have not found any
|
|
+ ** matches. Do an "insert" for everything that does not match */
|
|
+ putInt(lenOut-base, &zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, &zOut[base], lenOut-base);
|
|
+ zDelta += lenOut-base;
|
|
+ base = lenOut;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Advance the hash by one character. Keep looking for a match */
|
|
+ hash_next(&h, zOut[base+i+NHASH]);
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ /* Output a final "insert" record to get all the text at the end of
|
|
+ ** the file that does not match anything in the source file.
|
|
+ */
|
|
+ if( base<lenOut ){
|
|
+ putInt(lenOut-base, &zDelta);
|
|
+ *(zDelta++) = ':';
|
|
+ memcpy(zDelta, &zOut[base], lenOut-base);
|
|
+ zDelta += lenOut-base;
|
|
+ }
|
|
+ /* Output the final checksum record. */
|
|
+ putInt(checksum(zOut, lenOut), &zDelta);
|
|
+ *(zDelta++) = ';';
|
|
+ sqlite3_free(collide);
|
|
+ return (int)(zDelta - zOrigDelta);
|
|
+}
|
|
+
|
|
+/*
|
|
+** End of code copied from fossil.
|
|
+**************************************************************************/
|
|
+
|
|
+static void strPrintfArray(
|
|
+ Str *pStr, /* String object to append to */
|
|
+ const char *zSep, /* Separator string */
|
|
+ const char *zFmt, /* Format for each entry */
|
|
+ char **az, int n /* Array of strings & its size (or -1) */
|
|
+){
|
|
+ int i;
|
|
+ for(i=0; az[i] && (i<n || n<0); i++){
|
|
+ if( i!=0 ) strPrintf(pStr, "%s", zSep);
|
|
+ strPrintf(pStr, zFmt, az[i], az[i], az[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void getRbudiffQuery(
|
|
+ const char *zTab,
|
|
+ char **azCol,
|
|
+ int nPK,
|
|
+ int bOtaRowid,
|
|
+ Str *pSql
|
|
+){
|
|
+ int i;
|
|
+
|
|
+ /* First the newly inserted rows: **/
|
|
+ strPrintf(pSql, "SELECT ");
|
|
+ strPrintfArray(pSql, ", ", "%s", azCol, -1);
|
|
+ strPrintf(pSql, ", 0, "); /* Set ota_control to 0 for an insert */
|
|
+ strPrintfArray(pSql, ", ", "NULL", azCol, -1);
|
|
+ strPrintf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab);
|
|
+ strPrintf(pSql, " SELECT 1 FROM ", zTab);
|
|
+ strPrintf(pSql, " main.%Q AS o WHERE ", zTab);
|
|
+ strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
|
+ strPrintf(pSql, "\n) AND ");
|
|
+ strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
|
|
+
|
|
+ /* Deleted rows: */
|
|
+ strPrintf(pSql, "\nUNION ALL\nSELECT ");
|
|
+ strPrintfArray(pSql, ", ", "%s", azCol, nPK);
|
|
+ if( azCol[nPK] ){
|
|
+ strPrintf(pSql, ", ");
|
|
+ strPrintfArray(pSql, ", ", "NULL", &azCol[nPK], -1);
|
|
+ }
|
|
+ strPrintf(pSql, ", 1, "); /* Set ota_control to 1 for a delete */
|
|
+ strPrintfArray(pSql, ", ", "NULL", azCol, -1);
|
|
+ strPrintf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab);
|
|
+ strPrintf(pSql, " SELECT 1 FROM ", zTab);
|
|
+ strPrintf(pSql, " aux.%Q AS o WHERE ", zTab);
|
|
+ strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
|
+ strPrintf(pSql, "\n) AND ");
|
|
+ strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
|
|
+
|
|
+ /* Updated rows. If all table columns are part of the primary key, there
|
|
+ ** can be no updates. In this case this part of the compound SELECT can
|
|
+ ** be omitted altogether. */
|
|
+ if( azCol[nPK] ){
|
|
+ strPrintf(pSql, "\nUNION ALL\nSELECT ");
|
|
+ strPrintfArray(pSql, ", ", "n.%s", azCol, nPK);
|
|
+ strPrintf(pSql, ",\n");
|
|
+ strPrintfArray(pSql, " ,\n",
|
|
+ " CASE WHEN n.%s IS o.%s THEN NULL ELSE n.%s END", &azCol[nPK], -1
|
|
+ );
|
|
+
|
|
+ if( bOtaRowid==0 ){
|
|
+ strPrintf(pSql, ", '");
|
|
+ strPrintfArray(pSql, "", ".", azCol, nPK);
|
|
+ strPrintf(pSql, "' ||\n");
|
|
+ }else{
|
|
+ strPrintf(pSql, ",\n");
|
|
+ }
|
|
+ strPrintfArray(pSql, " ||\n",
|
|
+ " CASE WHEN n.%s IS o.%s THEN '.' ELSE 'x' END", &azCol[nPK], -1
|
|
+ );
|
|
+ strPrintf(pSql, "\nAS ota_control, ");
|
|
+ strPrintfArray(pSql, ", ", "NULL", azCol, nPK);
|
|
+ strPrintf(pSql, ",\n");
|
|
+ strPrintfArray(pSql, " ,\n",
|
|
+ " CASE WHEN n.%s IS o.%s THEN NULL ELSE o.%s END", &azCol[nPK], -1
|
|
+ );
|
|
+
|
|
+ strPrintf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab);
|
|
+ strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
|
+ strPrintf(pSql, " AND ota_control LIKE '%%x%%'");
|
|
+ }
|
|
+
|
|
+ /* Now add an ORDER BY clause to sort everything by PK. */
|
|
+ strPrintf(pSql, "\nORDER BY ");
|
|
+ for(i=1; i<=nPK; i++) strPrintf(pSql, "%s%d", ((i>1)?", ":""), i);
|
|
+}
|
|
+
|
|
+static void rbudiff_one_table(const char *zTab, FILE *out){
|
|
+ int bOtaRowid; /* True to use an ota_rowid column */
|
|
+ int nPK; /* Number of primary key columns in table */
|
|
+ char **azCol; /* NULL terminated array of col names */
|
|
+ int i;
|
|
+ int nCol;
|
|
+ Str ct = {0, 0, 0}; /* The "CREATE TABLE data_xxx" statement */
|
|
+ Str sql = {0, 0, 0}; /* Query to find differences */
|
|
+ Str insert = {0, 0, 0}; /* First part of output INSERT statement */
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int nRow = 0; /* Total rows in data_xxx table */
|
|
+
|
|
+ /* --rbu mode must use real primary keys. */
|
|
+ g.bSchemaPK = 1;
|
|
+
|
|
+ /* Check that the schemas of the two tables match. Exit early otherwise. */
|
|
+ checkSchemasMatch(zTab);
|
|
+
|
|
+ /* Grab the column names and PK details for the table(s). If no usable PK
|
|
+ ** columns are found, bail out early. */
|
|
+ azCol = columnNames("main", zTab, &nPK, &bOtaRowid);
|
|
+ if( azCol==0 ){
|
|
+ runtimeError("table %s has no usable PK columns", zTab);
|
|
+ }
|
|
+ for(nCol=0; azCol[nCol]; nCol++);
|
|
+
|
|
+ /* Build and output the CREATE TABLE statement for the data_xxx table */
|
|
+ strPrintf(&ct, "CREATE TABLE IF NOT EXISTS 'data_%q'(", zTab);
|
|
+ if( bOtaRowid ) strPrintf(&ct, "rbu_rowid, ");
|
|
+ strPrintfArray(&ct, ", ", "%s", &azCol[bOtaRowid], -1);
|
|
+ strPrintf(&ct, ", rbu_control);");
|
|
+
|
|
+ /* Get the SQL for the query to retrieve data from the two databases */
|
|
+ getRbudiffQuery(zTab, azCol, nPK, bOtaRowid, &sql);
|
|
+
|
|
+ /* Build the first part of the INSERT statement output for each row
|
|
+ ** in the data_xxx table. */
|
|
+ strPrintf(&insert, "INSERT INTO 'data_%q' (", zTab);
|
|
+ if( bOtaRowid ) strPrintf(&insert, "rbu_rowid, ");
|
|
+ strPrintfArray(&insert, ", ", "%s", &azCol[bOtaRowid], -1);
|
|
+ strPrintf(&insert, ", rbu_control) VALUES(");
|
|
+
|
|
+ pStmt = db_prepare("%s", sql.z);
|
|
+
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+
|
|
+ /* If this is the first row output, print out the CREATE TABLE
|
|
+ ** statement first. And then set ct.z to NULL so that it is not
|
|
+ ** printed again. */
|
|
+ if( ct.z ){
|
|
+ fprintf(out, "%s\n", ct.z);
|
|
+ strFree(&ct);
|
|
+ }
|
|
+
|
|
+ /* Output the first part of the INSERT statement */
|
|
+ fprintf(out, "%s", insert.z);
|
|
+ nRow++;
|
|
+
|
|
+ if( sqlite3_column_type(pStmt, nCol)==SQLITE_INTEGER ){
|
|
+ for(i=0; i<=nCol; i++){
|
|
+ if( i>0 ) fprintf(out, ", ");
|
|
+ printQuoted(out, sqlite3_column_value(pStmt, i));
|
|
+ }
|
|
+ }else{
|
|
+ char *zOtaControl;
|
|
+ int nOtaControl = sqlite3_column_bytes(pStmt, nCol);
|
|
+
|
|
+ zOtaControl = (char*)sqlite3_malloc(nOtaControl+1);
|
|
+ memcpy(zOtaControl, sqlite3_column_text(pStmt, nCol), nOtaControl+1);
|
|
+
|
|
+ for(i=0; i<nCol; i++){
|
|
+ int bDone = 0;
|
|
+ if( i>=nPK
|
|
+ && sqlite3_column_type(pStmt, i)==SQLITE_BLOB
|
|
+ && sqlite3_column_type(pStmt, nCol+1+i)==SQLITE_BLOB
|
|
+ ){
|
|
+ const char *aSrc = sqlite3_column_blob(pStmt, nCol+1+i);
|
|
+ int nSrc = sqlite3_column_bytes(pStmt, nCol+1+i);
|
|
+ const char *aFinal = sqlite3_column_blob(pStmt, i);
|
|
+ int nFinal = sqlite3_column_bytes(pStmt, i);
|
|
+ char *aDelta;
|
|
+ int nDelta;
|
|
+
|
|
+ aDelta = sqlite3_malloc(nFinal + 60);
|
|
+ nDelta = rbuDeltaCreate(aSrc, nSrc, aFinal, nFinal, aDelta);
|
|
+ if( nDelta<nFinal ){
|
|
+ int j;
|
|
+ fprintf(out, "x'");
|
|
+ for(j=0; j<nDelta; j++) fprintf(out, "%02x", (u8)aDelta[j]);
|
|
+ fprintf(out, "'");
|
|
+ zOtaControl[i-bOtaRowid] = 'f';
|
|
+ bDone = 1;
|
|
+ }
|
|
+ sqlite3_free(aDelta);
|
|
+ }
|
|
+
|
|
+ if( bDone==0 ){
|
|
+ printQuoted(out, sqlite3_column_value(pStmt, i));
|
|
+ }
|
|
+ fprintf(out, ", ");
|
|
+ }
|
|
+ fprintf(out, "'%s'", zOtaControl);
|
|
+ sqlite3_free(zOtaControl);
|
|
+ }
|
|
+
|
|
+ /* And the closing bracket of the insert statement */
|
|
+ fprintf(out, ");\n");
|
|
+ }
|
|
+
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( nRow>0 ){
|
|
+ Str cnt = {0, 0, 0};
|
|
+ strPrintf(&cnt, "INSERT INTO rbu_count VALUES('data_%q', %d);", zTab, nRow);
|
|
+ fprintf(out, "%s\n", cnt.z);
|
|
+ strFree(&cnt);
|
|
+ }
|
|
+
|
|
+ strFree(&ct);
|
|
+ strFree(&sql);
|
|
+ strFree(&insert);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Display a summary of differences between two versions of the same
|
|
+** table table.
|
|
+**
|
|
+** * Number of rows changed
|
|
+** * Number of rows added
|
|
+** * Number of rows deleted
|
|
+** * Number of identical rows
|
|
+*/
|
|
+static void summarize_one_table(const char *zTab, FILE *out){
|
|
+ char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
|
|
+ char **az = 0; /* Columns in main */
|
|
+ char **az2 = 0; /* Columns in aux */
|
|
+ int nPk; /* Primary key columns in main */
|
|
+ int nPk2; /* Primary key columns in aux */
|
|
+ int n = 0; /* Number of columns in main */
|
|
+ int n2; /* Number of columns in aux */
|
|
+ int i; /* Loop counter */
|
|
+ const char *zSep; /* Separator string */
|
|
+ Str sql; /* Comparison query */
|
|
+ sqlite3_stmt *pStmt; /* Query statement to do the diff */
|
|
+ sqlite3_int64 nUpdate; /* Number of updated rows */
|
|
+ sqlite3_int64 nUnchanged; /* Number of unmodified rows */
|
|
+ sqlite3_int64 nDelete; /* Number of deleted rows */
|
|
+ sqlite3_int64 nInsert; /* Number of inserted rows */
|
|
+
|
|
+ strInit(&sql);
|
|
+ if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){
|
|
+ if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
|
|
+ /* Table missing from second database. */
|
|
+ fprintf(out, "%s: missing from second database\n", zTab);
|
|
+ }
|
|
+ goto end_summarize_one_table;
|
|
+ }
|
|
+
|
|
+ if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
|
|
+ /* Table missing from source */
|
|
+ fprintf(out, "%s: missing from first database\n", zTab);
|
|
+ goto end_summarize_one_table;
|
|
+ }
|
|
+
|
|
+ az = columnNames("main", zTab, &nPk, 0);
|
|
+ az2 = columnNames("aux", zTab, &nPk2, 0);
|
|
+ if( az && az2 ){
|
|
+ for(n=0; az[n]; n++){
|
|
+ if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
|
|
+ }
|
|
+ }
|
|
+ if( az==0
|
|
+ || az2==0
|
|
+ || nPk!=nPk2
|
|
+ || az[n]
|
|
+ ){
|
|
+ /* Schema mismatch */
|
|
+ fprintf(out, "%s: incompatible schema\n", zTab);
|
|
+ goto end_summarize_one_table;
|
|
+ }
|
|
+
|
|
+ /* Build the comparison query */
|
|
+ for(n2=n; az[n2]; n2++){}
|
|
+ strPrintf(&sql, "SELECT 1, count(*)");
|
|
+ if( n2==nPk2 ){
|
|
+ strPrintf(&sql, ", 0\n");
|
|
+ }else{
|
|
+ zSep = ", sum(";
|
|
+ for(i=nPk; az[i]; i++){
|
|
+ strPrintf(&sql, "%sA.%s IS NOT B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " OR ";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n");
|
|
+ }
|
|
+ strPrintf(&sql, " FROM main.%s A, aux.%s B\n", zId, zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, " UNION ALL\n");
|
|
+ strPrintf(&sql, "SELECT 2, count(*), 0\n");
|
|
+ strPrintf(&sql, " FROM main.%s A\n", zId);
|
|
+ strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B ", zId);
|
|
+ zSep = "WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n");
|
|
+ strPrintf(&sql, " UNION ALL\n");
|
|
+ strPrintf(&sql, "SELECT 3, count(*), 0\n");
|
|
+ strPrintf(&sql, " FROM aux.%s B\n", zId);
|
|
+ strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A ", zId);
|
|
+ zSep = "WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n ORDER BY 1;\n");
|
|
+
|
|
+ if( (g.fDebug & DEBUG_DIFF_SQL)!=0 ){
|
|
+ printf("SQL for %s:\n%s\n", zId, sql.z);
|
|
+ goto end_summarize_one_table;
|
|
+ }
|
|
+
|
|
+ /* Run the query and output difference summary */
|
|
+ pStmt = db_prepare("%s", sql.z);
|
|
+ nUpdate = 0;
|
|
+ nInsert = 0;
|
|
+ nDelete = 0;
|
|
+ nUnchanged = 0;
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ switch( sqlite3_column_int(pStmt,0) ){
|
|
+ case 1:
|
|
+ nUpdate = sqlite3_column_int64(pStmt,2);
|
|
+ nUnchanged = sqlite3_column_int64(pStmt,1) - nUpdate;
|
|
+ break;
|
|
+ case 2:
|
|
+ nDelete = sqlite3_column_int64(pStmt,1);
|
|
+ break;
|
|
+ case 3:
|
|
+ nInsert = sqlite3_column_int64(pStmt,1);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ fprintf(out, "%s: %lld changes, %lld inserts, %lld deletes, %lld unchanged\n",
|
|
+ zTab, nUpdate, nInsert, nDelete, nUnchanged);
|
|
+
|
|
+end_summarize_one_table:
|
|
+ strFree(&sql);
|
|
+ sqlite3_free(zId);
|
|
+ namelistFree(az);
|
|
+ namelistFree(az2);
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write a 64-bit signed integer as a varint onto out
|
|
+*/
|
|
+static void putsVarint(FILE *out, sqlite3_uint64 v){
|
|
+ int i, n;
|
|
+ unsigned char p[12];
|
|
+ if( v & (((sqlite3_uint64)0xff000000)<<32) ){
|
|
+ p[8] = (unsigned char)v;
|
|
+ v >>= 8;
|
|
+ for(i=7; i>=0; i--){
|
|
+ p[i] = (unsigned char)((v & 0x7f) | 0x80);
|
|
+ v >>= 7;
|
|
+ }
|
|
+ fwrite(p, 8, 1, out);
|
|
+ }else{
|
|
+ n = 9;
|
|
+ do{
|
|
+ p[n--] = (unsigned char)((v & 0x7f) | 0x80);
|
|
+ v >>= 7;
|
|
+ }while( v!=0 );
|
|
+ p[9] &= 0x7f;
|
|
+ fwrite(p+n+1, 9-n, 1, out);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write an SQLite value onto out.
|
|
+*/
|
|
+static void putValue(FILE *out, sqlite3_stmt *pStmt, int k){
|
|
+ int iDType = sqlite3_column_type(pStmt, k);
|
|
+ sqlite3_int64 iX;
|
|
+ double rX;
|
|
+ sqlite3_uint64 uX;
|
|
+ int j;
|
|
+
|
|
+ putc(iDType, out);
|
|
+ switch( iDType ){
|
|
+ case SQLITE_INTEGER:
|
|
+ iX = sqlite3_column_int64(pStmt, k);
|
|
+ memcpy(&uX, &iX, 8);
|
|
+ for(j=56; j>=0; j-=8) putc((uX>>j)&0xff, out);
|
|
+ break;
|
|
+ case SQLITE_FLOAT:
|
|
+ rX = sqlite3_column_double(pStmt, k);
|
|
+ memcpy(&uX, &rX, 8);
|
|
+ for(j=56; j>=0; j-=8) putc((uX>>j)&0xff, out);
|
|
+ break;
|
|
+ case SQLITE_TEXT:
|
|
+ iX = sqlite3_column_bytes(pStmt, k);
|
|
+ putsVarint(out, (sqlite3_uint64)iX);
|
|
+ fwrite(sqlite3_column_text(pStmt, k),1,(size_t)iX,out);
|
|
+ break;
|
|
+ case SQLITE_BLOB:
|
|
+ iX = sqlite3_column_bytes(pStmt, k);
|
|
+ putsVarint(out, (sqlite3_uint64)iX);
|
|
+ fwrite(sqlite3_column_blob(pStmt, k),1,(size_t)iX,out);
|
|
+ break;
|
|
+ case SQLITE_NULL:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Generate a CHANGESET for all differences from main.zTab to aux.zTab.
|
|
+*/
|
|
+static void changeset_one_table(const char *zTab, FILE *out){
|
|
+ sqlite3_stmt *pStmt; /* SQL statment */
|
|
+ char *zId = safeId(zTab); /* Escaped name of the table */
|
|
+ char **azCol = 0; /* List of escaped column names */
|
|
+ int nCol = 0; /* Number of columns */
|
|
+ int *aiFlg = 0; /* 0 if column is not part of PK */
|
|
+ int *aiPk = 0; /* Column numbers for each PK column */
|
|
+ int nPk = 0; /* Number of PRIMARY KEY columns */
|
|
+ Str sql; /* SQL for the diff query */
|
|
+ int i, k; /* Loop counters */
|
|
+ const char *zSep; /* List separator */
|
|
+
|
|
+ /* Check that the schemas of the two tables match. Exit early otherwise. */
|
|
+ checkSchemasMatch(zTab);
|
|
+
|
|
+ pStmt = db_prepare("PRAGMA main.table_info=%Q", zTab);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ nCol++;
|
|
+ azCol = sqlite3_realloc(azCol, sizeof(char*)*nCol);
|
|
+ if( azCol==0 ) runtimeError("out of memory");
|
|
+ aiFlg = sqlite3_realloc(aiFlg, sizeof(int)*nCol);
|
|
+ if( aiFlg==0 ) runtimeError("out of memory");
|
|
+ azCol[nCol-1] = safeId((const char*)sqlite3_column_text(pStmt,1));
|
|
+ aiFlg[nCol-1] = i = sqlite3_column_int(pStmt,5);
|
|
+ if( i>0 ){
|
|
+ if( i>nPk ){
|
|
+ nPk = i;
|
|
+ aiPk = sqlite3_realloc(aiPk, sizeof(int)*nPk);
|
|
+ if( aiPk==0 ) runtimeError("out of memory");
|
|
+ }
|
|
+ aiPk[i-1] = nCol-1;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( nPk==0 ) goto end_changeset_one_table;
|
|
+ strInit(&sql);
|
|
+ if( nCol>nPk ){
|
|
+ strPrintf(&sql, "SELECT %d", SQLITE_UPDATE);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ strPrintf(&sql, ",\n A.%s", azCol[i]);
|
|
+ }else{
|
|
+ strPrintf(&sql, ",\n A.%s IS NOT B.%s, A.%s, B.%s",
|
|
+ azCol[i], azCol[i], azCol[i], azCol[i]);
|
|
+ }
|
|
+ }
|
|
+ strPrintf(&sql,"\n FROM main.%s A, aux.%s B\n", zId, zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, azCol[aiPk[i]], azCol[aiPk[i]]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ zSep = "\n AND (";
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ) continue;
|
|
+ strPrintf(&sql, "%sA.%s IS NOT B.%s", zSep, azCol[i], azCol[i]);
|
|
+ zSep = " OR\n ";
|
|
+ }
|
|
+ strPrintf(&sql,")\n UNION ALL\n");
|
|
+ }
|
|
+ strPrintf(&sql, "SELECT %d", SQLITE_DELETE);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ strPrintf(&sql, ",\n A.%s", azCol[i]);
|
|
+ }else{
|
|
+ strPrintf(&sql, ",\n 1, A.%s, NULL", azCol[i]);
|
|
+ }
|
|
+ }
|
|
+ strPrintf(&sql, "\n FROM main.%s A\n", zId);
|
|
+ strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B\n", zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, azCol[aiPk[i]], azCol[aiPk[i]]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n UNION ALL\n");
|
|
+ strPrintf(&sql, "SELECT %d", SQLITE_INSERT);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ strPrintf(&sql, ",\n B.%s", azCol[i]);
|
|
+ }else{
|
|
+ strPrintf(&sql, ",\n 1, NULL, B.%s", azCol[i]);
|
|
+ }
|
|
+ }
|
|
+ strPrintf(&sql, "\n FROM aux.%s B\n", zId);
|
|
+ strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A\n", zId);
|
|
+ zSep = " WHERE";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s A.%s=B.%s", zSep, azCol[aiPk[i]], azCol[aiPk[i]]);
|
|
+ zSep = " AND";
|
|
+ }
|
|
+ strPrintf(&sql, ")\n");
|
|
+ strPrintf(&sql, " ORDER BY");
|
|
+ zSep = " ";
|
|
+ for(i=0; i<nPk; i++){
|
|
+ strPrintf(&sql, "%s %d", zSep, aiPk[i]+2);
|
|
+ zSep = ",";
|
|
+ }
|
|
+ strPrintf(&sql, ";\n");
|
|
+
|
|
+ if( g.fDebug & DEBUG_DIFF_SQL ){
|
|
+ printf("SQL for %s:\n%s\n", zId, sql.z);
|
|
+ goto end_changeset_one_table;
|
|
+ }
|
|
+
|
|
+ putc('T', out);
|
|
+ putsVarint(out, (sqlite3_uint64)nCol);
|
|
+ for(i=0; i<nCol; i++) putc(aiFlg[i], out);
|
|
+ fwrite(zTab, 1, strlen(zTab), out);
|
|
+ putc(0, out);
|
|
+
|
|
+ pStmt = db_prepare("%s", sql.z);
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ int iType = sqlite3_column_int(pStmt,0);
|
|
+ putc(iType, out);
|
|
+ putc(0, out);
|
|
+ switch( sqlite3_column_int(pStmt,0) ){
|
|
+ case SQLITE_UPDATE: {
|
|
+ for(k=1, i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ putValue(out, pStmt, k);
|
|
+ k++;
|
|
+ }else if( sqlite3_column_int(pStmt,k) ){
|
|
+ putValue(out, pStmt, k+1);
|
|
+ k += 3;
|
|
+ }else{
|
|
+ putc(0, out);
|
|
+ k += 3;
|
|
+ }
|
|
+ }
|
|
+ for(k=1, i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ putc(0, out);
|
|
+ k++;
|
|
+ }else if( sqlite3_column_int(pStmt,k) ){
|
|
+ putValue(out, pStmt, k+2);
|
|
+ k += 3;
|
|
+ }else{
|
|
+ putc(0, out);
|
|
+ k += 3;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INSERT: {
|
|
+ for(k=1, i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ putValue(out, pStmt, k);
|
|
+ k++;
|
|
+ }else{
|
|
+ putValue(out, pStmt, k+2);
|
|
+ k += 3;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_DELETE: {
|
|
+ for(k=1, i=0; i<nCol; i++){
|
|
+ if( aiFlg[i] ){
|
|
+ putValue(out, pStmt, k);
|
|
+ k++;
|
|
+ }else{
|
|
+ putValue(out, pStmt, k+1);
|
|
+ k += 3;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+
|
|
+end_changeset_one_table:
|
|
+ while( nCol>0 ) sqlite3_free(azCol[--nCol]);
|
|
+ sqlite3_free(azCol);
|
|
+ sqlite3_free(aiPk);
|
|
+ sqlite3_free(zId);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Extract the next SQL keyword or quoted string from buffer zIn and copy it
|
|
+** (or a prefix of it if it will not fit) into buffer zBuf, size nBuf bytes.
|
|
+** Return a pointer to the character within zIn immediately following
|
|
+** the token or quoted string just extracted.
|
|
+*/
|
|
+const char *gobble_token(const char *zIn, char *zBuf, int nBuf){
|
|
+ const char *p = zIn;
|
|
+ char *pOut = zBuf;
|
|
+ char *pEnd = &pOut[nBuf-1];
|
|
+ char q = 0; /* quote character, if any */
|
|
+
|
|
+ if( p==0 ) return 0;
|
|
+ while( *p==' ' ) p++;
|
|
+ switch( *p ){
|
|
+ case '"': q = '"'; break;
|
|
+ case '\'': q = '\''; break;
|
|
+ case '`': q = '`'; break;
|
|
+ case '[': q = ']'; break;
|
|
+ }
|
|
+
|
|
+ if( q ){
|
|
+ p++;
|
|
+ while( *p && pOut<pEnd ){
|
|
+ if( *p==q ){
|
|
+ p++;
|
|
+ if( *p!=q ) break;
|
|
+ }
|
|
+ if( pOut<pEnd ) *pOut++ = *p;
|
|
+ p++;
|
|
+ }
|
|
+ }else{
|
|
+ while( *p && *p!=' ' && *p!='(' ){
|
|
+ if( pOut<pEnd ) *pOut++ = *p;
|
|
+ p++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *pOut = '\0';
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is the implementation of SQL scalar function "module_name":
|
|
+**
|
|
+** module_name(SQL)
|
|
+**
|
|
+** The only argument should be an SQL statement of the type that may appear
|
|
+** in the sqlite_master table. If the statement is a "CREATE VIRTUAL TABLE"
|
|
+** statement, then the value returned is the name of the module that it
|
|
+** uses. Otherwise, if the statement is not a CVT, NULL is returned.
|
|
+*/
|
|
+static void module_name_func(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nVal, sqlite3_value **apVal
|
|
+){
|
|
+ const char *zSql;
|
|
+ char zToken[32];
|
|
+
|
|
+ assert( nVal==1 );
|
|
+ zSql = (const char*)sqlite3_value_text(apVal[0]);
|
|
+
|
|
+ zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
|
+ if( zSql==0 || sqlite3_stricmp(zToken, "create") ) return;
|
|
+ zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
|
+ if( zSql==0 || sqlite3_stricmp(zToken, "virtual") ) return;
|
|
+ zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
|
+ if( zSql==0 || sqlite3_stricmp(zToken, "table") ) return;
|
|
+ zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
|
+ if( zSql==0 ) return;
|
|
+ zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
|
+ if( zSql==0 || sqlite3_stricmp(zToken, "using") ) return;
|
|
+ zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
|
+
|
|
+ sqlite3_result_text(pCtx, zToken, -1, SQLITE_TRANSIENT);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the text of an SQL statement that itself returns the list of
|
|
+** tables to process within the database.
|
|
+*/
|
|
+const char *all_tables_sql(){
|
|
+ if( g.bHandleVtab ){
|
|
+ int rc;
|
|
+
|
|
+ rc = sqlite3_exec(g.db,
|
|
+ "CREATE TEMP TABLE tblmap(module COLLATE nocase, postfix);"
|
|
+ "INSERT INTO temp.tblmap VALUES"
|
|
+ "('fts3', '_content'), ('fts3', '_segments'), ('fts3', '_segdir'),"
|
|
+
|
|
+ "('fts4', '_content'), ('fts4', '_segments'), ('fts4', '_segdir'),"
|
|
+ "('fts4', '_docsize'), ('fts4', '_stat'),"
|
|
+
|
|
+ "('fts5', '_data'), ('fts5', '_idx'), ('fts5', '_content'),"
|
|
+ "('fts5', '_docsize'), ('fts5', '_config'),"
|
|
+
|
|
+ "('rtree', '_node'), ('rtree', '_rowid'), ('rtree', '_parent');"
|
|
+ , 0, 0, 0
|
|
+ );
|
|
+ assert( rc==SQLITE_OK );
|
|
+
|
|
+ rc = sqlite3_create_function(
|
|
+ g.db, "module_name", 1, SQLITE_UTF8, 0, module_name_func, 0, 0
|
|
+ );
|
|
+ assert( rc==SQLITE_OK );
|
|
+
|
|
+ return
|
|
+ "SELECT name FROM main.sqlite_master\n"
|
|
+ " WHERE type='table' AND (\n"
|
|
+ " module_name(sql) IS NULL OR \n"
|
|
+ " module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
|
|
+ " ) AND name NOT IN (\n"
|
|
+ " SELECT a.name || b.postfix \n"
|
|
+ "FROM main.sqlite_master AS a, temp.tblmap AS b \n"
|
|
+ "WHERE module_name(a.sql) = b.module\n"
|
|
+ " )\n"
|
|
+ "UNION \n"
|
|
+ "SELECT name FROM aux.sqlite_master\n"
|
|
+ " WHERE type='table' AND (\n"
|
|
+ " module_name(sql) IS NULL OR \n"
|
|
+ " module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
|
|
+ " ) AND name NOT IN (\n"
|
|
+ " SELECT a.name || b.postfix \n"
|
|
+ "FROM aux.sqlite_master AS a, temp.tblmap AS b \n"
|
|
+ "WHERE module_name(a.sql) = b.module\n"
|
|
+ " )\n"
|
|
+ " ORDER BY name";
|
|
+ }else{
|
|
+ return
|
|
+ "SELECT name FROM main.sqlite_master\n"
|
|
+ " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
|
|
+ " UNION\n"
|
|
+ "SELECT name FROM aux.sqlite_master\n"
|
|
+ " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
|
|
+ " ORDER BY name";
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Print sketchy documentation for this utility program
|
|
+*/
|
|
+static void showHelp(void){
|
|
+ printf("Usage: %s [options] DB1 DB2\n", g.zArgv0);
|
|
+ printf(
|
|
+"Output SQL text that would transform DB1 into DB2.\n"
|
|
+"Options:\n"
|
|
+" --changeset FILE Write a CHANGESET into FILE\n"
|
|
+" -L|--lib LIBRARY Load an SQLite extension library\n"
|
|
+" --primarykey Use schema-defined PRIMARY KEYs\n"
|
|
+" --rbu Output SQL to create/populate RBU table(s)\n"
|
|
+" --schema Show only differences in the schema\n"
|
|
+" --summary Show only a summary of the differences\n"
|
|
+" --table TAB Show only differences in table TAB\n"
|
|
+" --transaction Show SQL output inside a transaction\n"
|
|
+" --vtab Handle fts3, fts4, fts5 and rtree tables\n"
|
|
+ );
|
|
+}
|
|
+
|
|
+int main(int argc, char **argv){
|
|
+ const char *zDb1 = 0;
|
|
+ const char *zDb2 = 0;
|
|
+ int i;
|
|
+ int rc;
|
|
+ char *zErrMsg = 0;
|
|
+ char *zSql;
|
|
+ sqlite3_stmt *pStmt;
|
|
+ char *zTab = 0;
|
|
+ FILE *out = stdout;
|
|
+ void (*xDiff)(const char*,FILE*) = diff_one_table;
|
|
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
+ int nExt = 0;
|
|
+ char **azExt = 0;
|
|
+#endif
|
|
+ int useTransaction = 0;
|
|
+ int neverUseTransaction = 0;
|
|
+
|
|
+ g.zArgv0 = argv[0];
|
|
+ sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
|
+ for(i=1; i<argc; i++){
|
|
+ const char *z = argv[i];
|
|
+ if( z[0]=='-' ){
|
|
+ z++;
|
|
+ if( z[0]=='-' ) z++;
|
|
+ if( strcmp(z,"changeset")==0 ){
|
|
+ if( i==argc-1 ) cmdlineError("missing argument to %s", argv[i]);
|
|
+ out = fopen(argv[++i], "wb");
|
|
+ if( out==0 ) cmdlineError("cannot open: %s", argv[i]);
|
|
+ xDiff = changeset_one_table;
|
|
+ neverUseTransaction = 1;
|
|
+ }else
|
|
+ if( strcmp(z,"debug")==0 ){
|
|
+ if( i==argc-1 ) cmdlineError("missing argument to %s", argv[i]);
|
|
+ g.fDebug = strtol(argv[++i], 0, 0);
|
|
+ }else
|
|
+ if( strcmp(z,"help")==0 ){
|
|
+ showHelp();
|
|
+ return 0;
|
|
+ }else
|
|
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
+ if( strcmp(z,"lib")==0 || strcmp(z,"L")==0 ){
|
|
+ if( i==argc-1 ) cmdlineError("missing argument to %s", argv[i]);
|
|
+ azExt = realloc(azExt, sizeof(azExt[0])*(nExt+1));
|
|
+ if( azExt==0 ) cmdlineError("out of memory");
|
|
+ azExt[nExt++] = argv[++i];
|
|
+ }else
|
|
+#endif
|
|
+ if( strcmp(z,"primarykey")==0 ){
|
|
+ g.bSchemaPK = 1;
|
|
+ }else
|
|
+ if( strcmp(z,"rbu")==0 ){
|
|
+ xDiff = rbudiff_one_table;
|
|
+ }else
|
|
+ if( strcmp(z,"schema")==0 ){
|
|
+ g.bSchemaOnly = 1;
|
|
+ }else
|
|
+ if( strcmp(z,"summary")==0 ){
|
|
+ xDiff = summarize_one_table;
|
|
+ }else
|
|
+ if( strcmp(z,"table")==0 ){
|
|
+ if( i==argc-1 ) cmdlineError("missing argument to %s", argv[i]);
|
|
+ zTab = argv[++i];
|
|
+ }else
|
|
+ if( strcmp(z,"transaction")==0 ){
|
|
+ useTransaction = 1;
|
|
+ }else
|
|
+ if( strcmp(z,"vtab")==0 ){
|
|
+ g.bHandleVtab = 1;
|
|
+ }else
|
|
+ {
|
|
+ cmdlineError("unknown option: %s", argv[i]);
|
|
+ }
|
|
+ }else if( zDb1==0 ){
|
|
+ zDb1 = argv[i];
|
|
+ }else if( zDb2==0 ){
|
|
+ zDb2 = argv[i];
|
|
+ }else{
|
|
+ cmdlineError("unknown argument: %s", argv[i]);
|
|
+ }
|
|
+ }
|
|
+ if( zDb2==0 ){
|
|
+ cmdlineError("two database arguments required");
|
|
+ }
|
|
+ rc = sqlite3_open(zDb1, &g.db);
|
|
+ if( rc ){
|
|
+ cmdlineError("cannot open database file \"%s\"", zDb1);
|
|
+ }
|
|
+ rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, &zErrMsg);
|
|
+ if( rc || zErrMsg ){
|
|
+ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb1);
|
|
+ }
|
|
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
+ sqlite3_enable_load_extension(g.db, 1);
|
|
+ for(i=0; i<nExt; i++){
|
|
+ rc = sqlite3_load_extension(g.db, azExt[i], 0, &zErrMsg);
|
|
+ if( rc || zErrMsg ){
|
|
+ cmdlineError("error loading %s: %s", azExt[i], zErrMsg);
|
|
+ }
|
|
+ }
|
|
+ free(azExt);
|
|
+#endif
|
|
+ zSql = sqlite3_mprintf("ATTACH %Q as aux;", zDb2);
|
|
+ rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
|
|
+ if( rc || zErrMsg ){
|
|
+ cmdlineError("cannot attach database \"%s\"", zDb2);
|
|
+ }
|
|
+ rc = sqlite3_exec(g.db, "SELECT * FROM aux.sqlite_master", 0, 0, &zErrMsg);
|
|
+ if( rc || zErrMsg ){
|
|
+ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb2);
|
|
+ }
|
|
+
|
|
+ if( neverUseTransaction ) useTransaction = 0;
|
|
+ if( useTransaction ) fprintf(out, "BEGIN TRANSACTION;\n");
|
|
+ if( xDiff==rbudiff_one_table ){
|
|
+ fprintf(out, "CREATE TABLE IF NOT EXISTS rbu_count"
|
|
+ "(tbl TEXT PRIMARY KEY COLLATE NOCASE, cnt INTEGER) "
|
|
+ "WITHOUT ROWID;\n"
|
|
+ );
|
|
+ }
|
|
+ if( zTab ){
|
|
+ xDiff(zTab, out);
|
|
+ }else{
|
|
+ /* Handle tables one by one */
|
|
+ pStmt = db_prepare("%s", all_tables_sql() );
|
|
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ xDiff((const char*)sqlite3_column_text(pStmt,0), out);
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ }
|
|
+ if( useTransaction ) printf("COMMIT;\n");
|
|
+
|
|
+ /* TBD: Handle trigger differences */
|
|
+ /* TBD: Handle view differences */
|
|
+ sqlite3_close(g.db);
|
|
+ return 0;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/sqlite3.1 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/sqlite3.1 2019-03-05 13:14:40.874289900 +0100
|
|
@@ -2,7 +2,7 @@
|
|
.\" First parameter, NAME, should be all caps
|
|
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
|
.\" other parameters are allowed: see man(7), man(1)
|
|
-.TH SQLITE3 1 "Fri Oct 31 10:41:31 EDT 2014"
|
|
+.TH SQLITE3 1 "Fry Jun 30 15:30:00 EDT 2017"
|
|
.\" Please adjust this date whenever revising the manpage.
|
|
.\"
|
|
.\" Some roff macros, for reference:
|
|
@@ -49,7 +49,7 @@ a table named "memos" and insert a coupl
|
|
$
|
|
.B sqlite3 mydata.db
|
|
.br
|
|
-SQLite version 3.8.8
|
|
+SQLite version 3.19.3
|
|
.br
|
|
Enter ".help" for instructions
|
|
.br
|
|
@@ -98,7 +98,7 @@ priority = 100
|
|
The interactive interpreter offers a set of meta-commands that can be
|
|
used to control the output format, examine the currently attached
|
|
database files, or perform administrative operations upon the
|
|
-attached databases (such as rebuilding indices). Meta-commands are
|
|
+attached databases (such as rebuilding indexes). Meta-commands are
|
|
always prefixed with a dot (.).
|
|
|
|
A list of available meta-commands can be viewed at any time by issuing
|
|
@@ -108,39 +108,50 @@ sqlite>
|
|
.B .help
|
|
.nf
|
|
.tr %.
|
|
+%auth ON|OFF Show authorizer callbacks
|
|
%backup ?DB? FILE Backup DB (default "main") to FILE
|
|
%bail on|off Stop after hitting an error. Default OFF
|
|
+%binary on|off Turn binary output on or off. Default OFF
|
|
+%cd DIRECTORY Change the working directory to DIRECTORY
|
|
+%changes on|off Show number of rows changed by SQL
|
|
+%check GLOB Fail if output since .testcase does not match
|
|
%clone NEWDB Clone data into NEWDB from the existing database
|
|
%databases List names and files of attached databases
|
|
+%dbinfo ?DB? Show status information about the database
|
|
%dump ?TABLE? ... Dump the database in an SQL text format
|
|
If TABLE specified, only dump tables matching
|
|
LIKE pattern TABLE.
|
|
%echo on|off Turn command echo on or off
|
|
-%eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN
|
|
+%eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN
|
|
%exit Exit this program
|
|
-%explain ?on|off? Turn output mode suitable for EXPLAIN on or off.
|
|
- With no args, it turns EXPLAIN on.
|
|
-%fullschema Show schema and the content of sqlite_stat tables
|
|
+%fullschema ?--indent? Show schema and the content of sqlite_stat tables
|
|
%headers on|off Turn display of headers on or off
|
|
%help Show this message
|
|
%import FILE TABLE Import data from FILE into TABLE
|
|
-%indices ?TABLE? Show names of all indices
|
|
- If TABLE specified, only show indices for tables
|
|
+%imposter INDEX TABLE Create imposter table TABLE on index INDEX
|
|
+%indexes ?TABLE? Show names of all indexes
|
|
+ If TABLE specified, only show indexes for tables
|
|
matching LIKE pattern TABLE.
|
|
+%limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT
|
|
+%lint OPTIONS Report potential schema issues. Options:
|
|
+ fkey-indexes Find missing foreign key indexes
|
|
%load FILE ?ENTRY? Load an extension library
|
|
%log FILE|off Turn logging on or off. FILE can be stderr/stdout
|
|
%mode MODE ?TABLE? Set output mode where MODE is one of:
|
|
+ ascii Columns/rows delimited by 0x1F and 0x1E
|
|
csv Comma-separated values
|
|
column Left-aligned columns. (See .width)
|
|
html HTML <table> code
|
|
insert SQL insert statements for TABLE
|
|
line One value per line
|
|
- list Values delimited by .separator string
|
|
+ list Values delimited by "|"
|
|
+ quote Escape answers as for SQL
|
|
tabs Tab-separated values
|
|
tcl TCL list elements
|
|
%nullvalue STRING Use STRING in place of NULL values
|
|
%once FILENAME Output for the next SQL command only to FILENAME
|
|
-%open ?FILENAME? Close existing database and reopen FILENAME
|
|
+%open ?OPTIONS? ?FILE? Close existing database and reopen FILE
|
|
+ The --new option starts with an empty file
|
|
%output ?FILENAME? Send output to FILENAME or stdout
|
|
%print STRING... Print literal STRING
|
|
%prompt MAIN CONTINUE Replace the standard prompts
|
|
@@ -148,21 +159,26 @@ sqlite>
|
|
%read FILENAME Execute SQL in FILENAME
|
|
%restore ?DB? FILE Restore content of DB (default "main") from FILE
|
|
%save FILE Write in-memory database into FILE
|
|
-%schema ?TABLE? Show the CREATE statements
|
|
- If TABLE specified, only show tables matching
|
|
- LIKE pattern TABLE.
|
|
-%separator STRING ?NL? Change separator used by output mode and .import
|
|
- NL is the end-of-line mark for CSV
|
|
+%scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off
|
|
+%schema ?PATTERN? Show the CREATE statements matching PATTERN
|
|
+ Add --indent for pretty-printing
|
|
+%separator COL ?ROW? Change the column separator and optionally the row
|
|
+ separator for both the output mode and .import
|
|
+%session CMD ... Create or control sessions
|
|
+%sha3sum ?OPTIONS...? Compute a SHA3 hash of database content
|
|
%shell CMD ARGS... Run CMD ARGS... in a system shell
|
|
%show Show the current values for various settings
|
|
-%stats on|off Turn stats on or off
|
|
+%stats ?on|off? Show stats or turn stats on or off
|
|
%system CMD ARGS... Run CMD ARGS... in a system shell
|
|
%tables ?TABLE? List names of tables
|
|
If TABLE specified, only list tables matching
|
|
LIKE pattern TABLE.
|
|
+%testcase NAME Begin redirecting output to 'testcase-out.txt'
|
|
%timeout MS Try opening locked tables for MS milliseconds
|
|
%timer on|off Turn SQL timer on or off
|
|
%trace FILE|off Output each SQL statement as it is run
|
|
+%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 column widths for "column" mode
|
|
Negative values right-justify
|
|
@@ -173,6 +189,9 @@ sqlite>
|
|
.B sqlite3
|
|
has the following options:
|
|
.TP
|
|
+.B \-ascii
|
|
+Set output mode to ascii.
|
|
+.TP
|
|
.B \-bail
|
|
Stop after hitting an error.
|
|
.TP
|
|
@@ -226,12 +245,25 @@ Set default mmap size to
|
|
.I N
|
|
\.
|
|
.TP
|
|
+.BI \-newline\ separator
|
|
+Set output row separator. Default is the newline character.
|
|
+.TP
|
|
.BI \-nullvalue\ string
|
|
Set string used to represent NULL values. Default is ''
|
|
(empty string).
|
|
.TP
|
|
+.BI \-pagecache\ SIZE N
|
|
+Use
|
|
+.I SIZE
|
|
+entries of
|
|
+.I N
|
|
+bytes each for page cache memory.
|
|
+.TP
|
|
+.B \-scanstats
|
|
+Print scan stats before each finalize.
|
|
+.TP
|
|
.BI \-separator\ separator
|
|
-Set output field separator. Default is '|'.
|
|
+Set output column separator. Default is '|'.
|
|
.TP
|
|
.B \-stats
|
|
Print memory stats before each finalize.
|
|
@@ -243,6 +275,9 @@ Show SQLite version.
|
|
Use
|
|
.I name
|
|
as the default VFS.
|
|
+.TP
|
|
+.B \-vfslog
|
|
+Enable the vfslog extension.
|
|
|
|
|
|
.SH INIT FILE
|
|
--- origsrc/sqlite-autoconf-3280000/sqlite3.c 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/sqlite3.c 2019-05-14 16:22:31.208800700 +0200
|
|
@@ -233,6 +233,9 @@ static const char * const sqlite3azCompi
|
|
#if SQLITE_ENABLE_CURSOR_HINTS
|
|
"ENABLE_CURSOR_HINTS",
|
|
#endif
|
|
+#if SQLITE_ENABLE_DBPAGE_VTAB
|
|
+ "ENABLE_DBPAGE_VTAB",
|
|
+#endif
|
|
#if SQLITE_ENABLE_DBSTAT_VTAB
|
|
"ENABLE_DBSTAT_VTAB",
|
|
#endif
|
|
@@ -275,9 +278,6 @@ static const char * const sqlite3azCompi
|
|
#if SQLITE_ENABLE_JSON1
|
|
"ENABLE_JSON1",
|
|
#endif
|
|
-#if SQLITE_ENABLE_LOAD_EXTENSION
|
|
- "ENABLE_LOAD_EXTENSION",
|
|
-#endif
|
|
#ifdef SQLITE_ENABLE_LOCKING_STYLE
|
|
"ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
|
|
#endif
|
|
@@ -346,7 +346,7 @@ static const char * const sqlite3azCompi
|
|
#if SQLITE_ENABLE_UNLOCK_NOTIFY
|
|
"ENABLE_UNLOCK_NOTIFY",
|
|
#endif
|
|
-#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
|
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
|
|
"ENABLE_UPDATE_DELETE_LIMIT",
|
|
#endif
|
|
#if SQLITE_ENABLE_URI_00_ERROR
|
|
@@ -1198,7 +1198,7 @@ extern "C" {
|
|
**
|
|
** See also: [sqlite_version()] and [sqlite_source_id()].
|
|
*/
|
|
-SQLITE_API const char sqlite3_version[] = SQLITE_VERSION;
|
|
+const char sqlite3_version[] = SQLITE_VERSION;
|
|
SQLITE_API const char *sqlite3_libversion(void);
|
|
SQLITE_API const char *sqlite3_sourceid(void);
|
|
SQLITE_API int sqlite3_libversion_number(void);
|
|
@@ -3066,6 +3066,8 @@ struct sqlite3_mem_methods {
|
|
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
|
|
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
|
|
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
|
|
+#define SQLITE_CONFIG_MALLOC2 98 /* sqlite3_mem_methods2* */
|
|
+#define SQLITE_CONFIG_GETMALLOC2 99 /* sqlite3_mem_methods2* */
|
|
|
|
/*
|
|
** CAPI3REF: Database Connection Configuration Options
|
|
@@ -3406,7 +3408,9 @@ SQLITE_API void sqlite3_set_last_insert_
|
|
** <li> the [data_version pragma]
|
|
** </ul>
|
|
*/
|
|
-SQLITE_API int sqlite3_changes(sqlite3*);
|
|
+SQLITE_API sqlite3_uint64 sqlite3_changes(sqlite3*);
|
|
+#define sqlite3_changes(db) ((int)(sqlite3_changes)(db))
|
|
+#define sqlite3_changes64(db) ((sqlite3_changes)(db))
|
|
|
|
/*
|
|
** CAPI3REF: Total Number Of Rows Modified
|
|
@@ -3443,7 +3447,9 @@ SQLITE_API int sqlite3_changes(sqlite3*)
|
|
** <li> the [SQLITE_FCNTL_DATA_VERSION] [file control]
|
|
** </ul>
|
|
*/
|
|
-SQLITE_API int sqlite3_total_changes(sqlite3*);
|
|
+SQLITE_API sqlite3_uint64 sqlite3_total_changes(sqlite3*);
|
|
+#define sqlite3_total_changes(db) ((int)(sqlite3_total_changes)(db))
|
|
+#define sqlite3_total_changes64(db) ((sqlite3_total_changes)(db))
|
|
|
|
/*
|
|
** CAPI3REF: Interrupt A Long-Running Query
|
|
@@ -5155,7 +5161,7 @@ typedef struct sqlite3_context sqlite3_c
|
|
** See also: [sqlite3_bind_parameter_count()],
|
|
** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
|
|
*/
|
|
-SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
|
|
+SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, unsigned int n, void(*)(void*));
|
|
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,
|
|
void(*)(void*));
|
|
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);
|
|
@@ -5733,8 +5739,8 @@ SQLITE_API sqlite3_int64 sqlite3_column_
|
|
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
|
|
SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
|
|
SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
|
|
-SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
|
|
-SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
|
|
+SQLITE_API unsigned int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
|
|
+SQLITE_API unsigned int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
|
|
SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
|
|
|
|
/*
|
|
@@ -5912,6 +5918,8 @@ SQLITE_API int sqlite3_create_function(
|
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
void (*xFinal)(sqlite3_context*)
|
|
);
|
|
+/* A macro is more efficient than a wrapped call */
|
|
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
|
SQLITE_API int sqlite3_create_function16(
|
|
sqlite3 *db,
|
|
const void *zFunctionName,
|
|
@@ -5984,9 +5992,12 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
|
|
+#define sqlite3_global_recover() SQLITE_OK
|
|
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
|
|
+#define sqlite3_thread_cleanup() /**/
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
|
|
void*,sqlite3_int64);
|
|
+#define sqlite3_memory_alarm(a,b,c) SQLITE_OK
|
|
#endif
|
|
|
|
/*
|
|
@@ -6126,8 +6137,8 @@ SQLITE_API const unsigned char *sqlite3_
|
|
SQLITE_API const void *sqlite3_value_text16(sqlite3_value*);
|
|
SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*);
|
|
SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*);
|
|
-SQLITE_API int sqlite3_value_bytes(sqlite3_value*);
|
|
-SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
|
|
+SQLITE_API unsigned int sqlite3_value_bytes(sqlite3_value*);
|
|
+SQLITE_API unsigned int sqlite3_value_bytes16(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
|
@@ -6439,7 +6450,7 @@ typedef void (*sqlite3_destructor_type)(
|
|
** than the one containing the application-defined function that received
|
|
** the [sqlite3_context] pointer, the results are undefined.
|
|
*/
|
|
-SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
|
|
+SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, unsigned int, void(*)(void*));
|
|
SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*,
|
|
sqlite3_uint64,void(*)(void*));
|
|
SQLITE_API void sqlite3_result_double(sqlite3_context*, double);
|
|
@@ -6464,6 +6475,19 @@ SQLITE_API int sqlite3_result_zeroblob64
|
|
|
|
|
|
/*
|
|
+ * If we are not sure the platform is 32-bit, always use sqlite3_????64()
|
|
+ * in stead of sqlite3_????() for certain functions, in order to prevent overflow.
|
|
+ */
|
|
+#if !defined(i386) && !defined(__i386__) && !defined(_M_IX86) && \
|
|
+ !defined(_M_ARM) && !defined(__arm__) && !defined(__x86) && \
|
|
+ (!defined(__SIZEOF_POINTER__) || (__SIZEOF_POINTER__ != 4))
|
|
+# define sqlite3_bind_blob(pStmt, i, zData, nData, xDel) sqlite3_bind_blob64(pStmt, i, zData, nData, xDel)
|
|
+# define sqlite3_result_blob(pCtx, z, n, xDel) sqlite3_result_blob64(pCtx, z, n, xDel)
|
|
+# define sqlite3_malloc(x) sqlite3_malloc64(x)
|
|
+# define sqlite3_realloc(x,y) sqlite3_realloc64(x,y)
|
|
+#endif
|
|
+
|
|
+/*
|
|
** CAPI3REF: Setting The Subtype Of An SQL Function
|
|
** METHOD: sqlite3_context
|
|
**
|
|
@@ -6564,6 +6588,8 @@ SQLITE_API int sqlite3_create_collation(
|
|
void *pArg,
|
|
int(*xCompare)(void*,int,const void*,int,const void*)
|
|
);
|
|
+/* A macro is more efficient than a wrapped call */
|
|
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
|
SQLITE_API int sqlite3_create_collation_v2(
|
|
sqlite3*,
|
|
const char *zName,
|
|
@@ -6748,7 +6774,7 @@ SQLITE_API int sqlite3_sleep(int);
|
|
** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
|
|
** </pre></blockquote>
|
|
*/
|
|
-SQLITE_API char *sqlite3_temp_directory;
|
|
+char *sqlite3_temp_directory;
|
|
|
|
/*
|
|
** CAPI3REF: Name Of The Folder Holding Database Files
|
|
@@ -6785,7 +6811,7 @@ SQLITE_API char *sqlite3_temp_directory;
|
|
** made NULL or made to point to memory obtained from [sqlite3_malloc]
|
|
** or else the use of the [data_store_directory pragma] should be avoided.
|
|
*/
|
|
-SQLITE_API char *sqlite3_data_directory;
|
|
+char *sqlite3_data_directory;
|
|
|
|
/*
|
|
** CAPI3REF: Win32 Specific Interface
|
|
@@ -7138,6 +7164,7 @@ SQLITE_API sqlite3_int64 sqlite3_soft_he
|
|
*/
|
|
SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
|
|
|
|
+#define sqlite3_soft_heap_limit(N) sqlite3_soft_heap_limit64(N)
|
|
|
|
/*
|
|
** CAPI3REF: Extract Metadata About A Column Of A Table
|
|
@@ -7627,6 +7654,8 @@ SQLITE_API int sqlite3_create_module_v2(
|
|
void *pClientData, /* Client data for xCreate/xConnect */
|
|
void(*xDestroy)(void*) /* Module destructor function */
|
|
);
|
|
+/* A macro is more efficient than a (possible future) wrapped call */
|
|
+#define sqlite3_create_module(a,b,c,d) sqlite3_create_module_v2(a,b,c,d,0);
|
|
|
|
/*
|
|
** CAPI3REF: Virtual Table Instance Object
|
|
@@ -8358,7 +8387,8 @@ SQLITE_API int sqlite3_test_control(int
|
|
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
|
#define SQLITE_TESTCTRL_IMPOSTER 25
|
|
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
|
-#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
|
+#define SQLITE_TESTCTRL_RESULT_INTREAL 27
|
|
+#define SQLITE_TESTCTRL_LAST 27 /* Largest TESTCTRL */
|
|
|
|
/*
|
|
** CAPI3REF: SQL Keyword Checking
|
|
@@ -9621,6 +9651,8 @@ SQLITE_API int sqlite3_wal_autocheckpoin
|
|
** complication) of [sqlite3_wal_checkpoint_v2()].
|
|
*/
|
|
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
|
|
+/* A macro is more efficient than a wrapped call */
|
|
+#define sqlite3_wal_checkpoint(a,b) sqlite3_wal_checkpoint_v2(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0);
|
|
|
|
/*
|
|
** CAPI3REF: Checkpoint a database
|
|
@@ -12757,7 +12789,7 @@ struct fts5_api {
|
|
fts5_api *pApi,
|
|
const char *zName,
|
|
void *pContext,
|
|
- fts5_tokenizer *pTokenizer,
|
|
+ const fts5_tokenizer *pTokenizer,
|
|
void (*xDestroy)(void*)
|
|
);
|
|
|
|
@@ -12828,7 +12860,7 @@ struct fts5_api {
|
|
** to count the size: 2^31-1 or 2147483647.
|
|
*/
|
|
#ifndef SQLITE_MAX_LENGTH
|
|
-# define SQLITE_MAX_LENGTH 1000000000
|
|
+# define SQLITE_MAX_LENGTH 2147483647
|
|
#endif
|
|
|
|
/*
|
|
@@ -12932,7 +12964,7 @@ struct fts5_api {
|
|
** and we have to allow 2 extra counts for the "main" and "temp" databases.
|
|
*/
|
|
#ifndef SQLITE_MAX_ATTACHED
|
|
-# define SQLITE_MAX_ATTACHED 10
|
|
+# define SQLITE_MAX_ATTACHED 62
|
|
#endif
|
|
|
|
|
|
@@ -12979,7 +13011,7 @@ struct fts5_api {
|
|
** SQLite will choose on its own.
|
|
*/
|
|
#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE
|
|
-# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192
|
|
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE 32768
|
|
#endif
|
|
#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
|
|
# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
|
|
@@ -13017,6 +13049,13 @@ struct fts5_api {
|
|
# define SQLITE_MAX_TRIGGER_DEPTH 1000
|
|
#endif
|
|
|
|
+/*
|
|
+** Maximum supported path-length..
|
|
+*/
|
|
+#ifndef SQLITE_MAX_PATH_LENGTH
|
|
+# define SQLITE_MAX_PATH_LENGTH 4096
|
|
+#endif
|
|
+
|
|
/************** End of sqliteLimit.h *****************************************/
|
|
/************** Continuing where we left off in sqliteInt.h ******************/
|
|
|
|
@@ -13388,6 +13427,8 @@ SQLITE_PRIVATE void sqlite3Coverage(in
|
|
#ifndef SQLITE_HASH_H
|
|
#define SQLITE_HASH_H
|
|
|
|
+#include <stddef.h>
|
|
+
|
|
/* Forward declarations of structures. */
|
|
typedef struct Hash Hash;
|
|
typedef struct HashElem HashElem;
|
|
@@ -13414,11 +13455,11 @@ typedef struct HashElem HashElem;
|
|
** the hash table.
|
|
*/
|
|
struct Hash {
|
|
- unsigned int htsize; /* Number of buckets in the hash table */
|
|
- unsigned int count; /* Number of entries in this table */
|
|
+ size_t htsize; /* Number of buckets in the hash table */
|
|
+ size_t count; /* Number of entries in this table */
|
|
HashElem *first; /* The first element of the array */
|
|
struct _ht { /* the hash table */
|
|
- unsigned int count; /* Number of entries with this hash */
|
|
+ size_t count; /* Number of entries with this hash */
|
|
HashElem *chain; /* Pointer to first entry with this hash */
|
|
} *ht;
|
|
};
|
|
@@ -13654,7 +13695,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Has
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
-#include <stddef.h>
|
|
+/* #include <stddef.h> */
|
|
|
|
/*
|
|
** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY.
|
|
@@ -14033,6 +14074,7 @@ typedef INT16_TYPE LogEst;
|
|
#ifndef SQLITE_MAX_MMAP_SIZE
|
|
# if defined(__linux__) \
|
|
|| defined(_WIN32) \
|
|
+ || defined(__CYGWIN__) \
|
|
|| (defined(__APPLE__) && defined(__MACH__)) \
|
|
|| defined(__sun) \
|
|
|| defined(__FreeBSD__) \
|
|
@@ -14392,7 +14434,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuu
|
|
#define BTREE_BLOBKEY 2 /* Table has keys only - no data */
|
|
|
|
SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*);
|
|
-SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*);
|
|
+SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, u64*);
|
|
SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor*);
|
|
SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree*, int, int);
|
|
|
|
@@ -14738,7 +14780,7 @@ struct VdbeOp {
|
|
#endif
|
|
int (*xAdvance)(BtCursor *, int);
|
|
} p4;
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 1
|
|
char *zComment; /* Comment to improve readability */
|
|
#endif
|
|
#ifdef VDBE_PROFILE
|
|
@@ -15165,11 +15207,11 @@ SQLITE_PRIVATE int sqlite3MemCompare(con
|
|
SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*);
|
|
|
|
SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
|
|
-SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
|
|
-SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
|
|
+SQLITE_PRIVATE int sqlite3VdbeRecordCompare(size_t,const void*,UnpackedRecord*);
|
|
+SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(size_t, const void *, UnpackedRecord *, int);
|
|
SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
|
|
|
|
-typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
|
|
+typedef int (*RecordCompare)(size_t,const void*,UnpackedRecord*);
|
|
SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
|
|
|
|
#ifndef SQLITE_OMIT_TRIGGER
|
|
@@ -15185,7 +15227,7 @@ SQLITE_PRIVATE int sqlite3NotPureFunc(sq
|
|
** comments in VDBE programs that show key decision points in the code
|
|
** generator.
|
|
*/
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 0
|
|
SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...);
|
|
# define VdbeComment(X) sqlite3VdbeComment X
|
|
SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
|
|
@@ -15196,9 +15238,13 @@ SQLITE_PRIVATE void sqlite3VdbeNoopCom
|
|
# define VdbeModuleComment(X)
|
|
# endif
|
|
#else
|
|
-# define VdbeComment(X)
|
|
-# define VdbeNoopComment(X)
|
|
-# define VdbeModuleComment(X)
|
|
+# define VdbeComment(X) if (sqlite3GlobalConfig.bVdbeComments) sqlite3VdbeComment X
|
|
+# define VdbeNoopComment(X) if (sqlite3GlobalConfig.bVdbeComments) sqlite3VdbeNoopComment X
|
|
+# ifdef SQLITE_ENABLE_MODULE_COMMENTS
|
|
+# define VdbeModuleComment(X) if (sqlite3GlobalConfig.bVdbeComments) sqlite3VdbeNoopComment X
|
|
+#else
|
|
+# define VdbeModuleComment(X)
|
|
+#endif
|
|
#endif
|
|
|
|
/*
|
|
@@ -15601,7 +15647,7 @@ SQLITE_PRIVATE void sqlite3PcacheShutdow
|
|
/* Page cache buffer management:
|
|
** These routines implement SQLITE_CONFIG_PAGECACHE.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n);
|
|
+SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, size_t sz, int n);
|
|
|
|
/* Create a new pager cache.
|
|
** Under memory stress, invoke xStress to try to make pages clean.
|
|
@@ -15703,7 +15749,7 @@ SQLITE_PRIVATE void sqlite3PcacheShrink(
|
|
|
|
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
/* Try to return memory used by the pcache module to the main memory heap */
|
|
-SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int);
|
|
+SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(size_t);
|
|
#endif
|
|
|
|
#ifdef SQLITE_TEST
|
|
@@ -15796,10 +15842,13 @@ SQLITE_PRIVATE int sqlite3PCacheIsDirty(
|
|
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
|
|
# define SQLITE_OS_OTHER 0
|
|
# ifndef SQLITE_OS_WIN
|
|
-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
|
|
+# if defined(_WIN32) || defined(WIN32) || defined(__MSVCRT__) || \
|
|
defined(__MINGW32__) || defined(__BORLANDC__)
|
|
# define SQLITE_OS_WIN 1
|
|
# define SQLITE_OS_UNIX 0
|
|
+# elif defined(__CYGWIN__)
|
|
+# define SQLITE_OS_WIN 1
|
|
+# define SQLITE_OS_UNIX 1
|
|
# else
|
|
# define SQLITE_OS_WIN 0
|
|
# define SQLITE_OS_UNIX 1
|
|
@@ -16050,7 +16099,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(s
|
|
# define SQLITE_MUTEX_OMIT
|
|
#endif
|
|
#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP)
|
|
-# if SQLITE_OS_UNIX
|
|
+# if SQLITE_OS_UNIX || (defined(__CYGWIN__) && !defined(SQLITE_TEST))
|
|
# define SQLITE_MUTEX_PTHREADS
|
|
# elif SQLITE_OS_WIN
|
|
# define SQLITE_MUTEX_W32
|
|
@@ -16120,7 +16169,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(s
|
|
** databases may be attached.
|
|
*/
|
|
struct Db {
|
|
- char *zDbSName; /* Name of this database. (schema name, not filename) */
|
|
+ const char *zDbSName; /* Name of this database. (schema name, not filename) */
|
|
Btree *pBt; /* The B*Tree structure for this database file */
|
|
u8 safety_level; /* How aggressive at syncing data to disk */
|
|
u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */
|
|
@@ -16323,8 +16372,8 @@ struct sqlite3 {
|
|
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
|
|
int nextPagesize; /* Pagesize after VACUUM if >0 */
|
|
u32 magic; /* Magic number for detect library misuse */
|
|
- int nChange; /* Value returned by sqlite3_changes() */
|
|
- int nTotalChange; /* Value returned by sqlite3_total_changes() */
|
|
+ u64 nChange; /* Value returned by sqlite3_changes() */
|
|
+ u64 nTotalChange; /* Value returned by sqlite3_total_changes() */
|
|
int aLimit[SQLITE_N_LIMIT]; /* Limits */
|
|
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
|
|
struct sqlite3InitInfo { /* Information used during initialization */
|
|
@@ -16401,7 +16450,7 @@ struct sqlite3 {
|
|
int nStatement; /* Number of nested statement-transactions */
|
|
i64 nDeferredCons; /* Net deferred constraints this transaction. */
|
|
i64 nDeferredImmCons; /* Net deferred immediate constraints */
|
|
- int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
|
|
+ size_t *pnBytesFreed; /* If not NULL, increment this in DbFree() */
|
|
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
|
/* The following variables are all protected by the STATIC_MASTER
|
|
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
|
|
@@ -17176,6 +17225,7 @@ struct Index {
|
|
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
|
|
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
|
|
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
|
|
+ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
|
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
int nSample; /* Number of elements in aSample[] */
|
|
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
|
|
@@ -17952,7 +18002,7 @@ struct TriggerPrg {
|
|
/*
|
|
** The yDbMask datatype for the bitmask of all attached databases.
|
|
*/
|
|
-#if SQLITE_MAX_ATTACHED>30
|
|
+#if SQLITE_MAX_ATTACHED>62
|
|
typedef unsigned char yDbMask[(SQLITE_MAX_ATTACHED+9)/8];
|
|
# define DbMaskTest(M,I) (((M)[(I)/8]&(1<<((I)&7)))!=0)
|
|
# define DbMaskZero(M) memset((M),0,sizeof(M))
|
|
@@ -17960,7 +18010,11 @@ struct TriggerPrg {
|
|
# define DbMaskAllZero(M) sqlite3DbMaskAllZero(M)
|
|
# define DbMaskNonZero(M) (sqlite3DbMaskAllZero(M)==0)
|
|
#else
|
|
+#if SQLITE_MAX_ATTACHED>30
|
|
+ typedef u64 yDbMask;
|
|
+#else
|
|
typedef unsigned int yDbMask;
|
|
+#endif
|
|
# define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0)
|
|
# define DbMaskZero(M) (M)=0
|
|
# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I))
|
|
@@ -18004,7 +18058,7 @@ struct Parse {
|
|
int nErr; /* Number of errors seen */
|
|
int nTab; /* Number of previously allocated VDBE cursors */
|
|
int nMem; /* Number of memory cells used so far */
|
|
- int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
|
|
+ size_t szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
|
|
int iSelfTab; /* Table associated with an index on expr, or negative
|
|
** of the base register during check-constraint eval */
|
|
int nLabel; /* The *negative* of the number of labels used */
|
|
@@ -18276,8 +18330,8 @@ struct DbFixer {
|
|
struct sqlite3_str {
|
|
sqlite3 *db; /* Optional database for lookaside. Can be NULL */
|
|
char *zText; /* The string collected so far */
|
|
- u32 nAlloc; /* Amount of space allocated in zText */
|
|
- u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
|
|
+ size_t nAlloc; /* Amount of space allocated in zText */
|
|
+ size_t mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
|
|
u32 nChar; /* Length of the string so far */
|
|
u8 accError; /* SQLITE_NOMEM or SQLITE_TOOBIG */
|
|
u8 printfFlags; /* SQLITE_PRINTF flags below */
|
|
@@ -18303,6 +18357,22 @@ typedef struct {
|
|
} InitData;
|
|
|
|
/*
|
|
+** Moved here from sqlite.h, made private on purpose
|
|
+*/
|
|
+
|
|
+typedef struct sqlite3_mem_methods2 sqlite3_mem_methods2;
|
|
+struct sqlite3_mem_methods2 {
|
|
+ void *(*xMalloc)(size_t); /* Memory allocation function */
|
|
+ void (*xFree)(void*); /* Free a prior allocation */
|
|
+ void *(*xRealloc)(void*,size_t);/* Resize an allocation */
|
|
+ size_t (*xSize)(void*); /* Return the size of an allocation */
|
|
+ size_t (*xRoundup)(size_t); /* Round up request size to allocation size */
|
|
+ int (*xInit)(void*); /* Initialize the memory allocator */
|
|
+ void (*xShutdown)(void*); /* Deinitialize the memory allocator */
|
|
+ void *pAppData; /* Argument to xInit() and xShutdown() */
|
|
+};
|
|
+
|
|
+/*
|
|
** Allowed values for mInitFlags
|
|
*/
|
|
#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
|
|
@@ -18325,6 +18395,7 @@ struct Sqlite3Config {
|
|
int nLookaside; /* Default lookaside buffer count */
|
|
int nStmtSpill; /* Stmt-journal spill-to-disk threshold */
|
|
sqlite3_mem_methods m; /* Low-level memory allocation interface */
|
|
+ sqlite3_mem_methods2 m2; /* Low-level memory allocation interface */
|
|
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
|
|
sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */
|
|
void *pHeap; /* Heap storage space */
|
|
@@ -18349,6 +18420,8 @@ struct Sqlite3Config {
|
|
sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
|
|
void (*xLog)(void*,int,const char*); /* Function for logging */
|
|
void *pLogArg; /* First argument to xLog() */
|
|
+ int bVdbeComments; /* True to enable VDBE comments */
|
|
+ int bEnableUnknownSqlFunction; /* True to enable VDBE comments */
|
|
#ifdef SQLITE_ENABLE_SQLLOG
|
|
void(*xSqllog)(void*,sqlite3*,const char*, int);
|
|
void *pSqllogArg;
|
|
@@ -18365,8 +18438,8 @@ struct Sqlite3Config {
|
|
#endif
|
|
#ifndef SQLITE_UNTESTABLE
|
|
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
|
|
-#endif
|
|
int bLocaltimeFault; /* True to fail localtime() calls */
|
|
+#endif
|
|
int bInternalFunctions; /* Internal SQL functions are visible */
|
|
int iOnceResetThreshold; /* When to reset OP_Once counters */
|
|
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
|
|
@@ -18627,29 +18700,29 @@ SQLITE_PRIVATE int sqlite3IsIdChar(u8);
|
|
** Internal function prototypes
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*);
|
|
-SQLITE_PRIVATE int sqlite3Strlen30(const char*);
|
|
+SQLITE_PRIVATE size_t sqlite3Strlen30(const char*);
|
|
#define sqlite3Strlen30NN(C) (strlen(C)&0x3fffffff)
|
|
-SQLITE_PRIVATE char *sqlite3ColumnType(Column*,char*);
|
|
+SQLITE_PRIVATE const char *sqlite3ColumnType(Column*,const char*);
|
|
#define sqlite3StrNICmp sqlite3_strnicmp
|
|
|
|
SQLITE_PRIVATE int sqlite3MallocInit(void);
|
|
SQLITE_PRIVATE void sqlite3MallocEnd(void);
|
|
-SQLITE_PRIVATE void *sqlite3Malloc(u64);
|
|
-SQLITE_PRIVATE void *sqlite3MallocZero(u64);
|
|
-SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, u64);
|
|
-SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, u64);
|
|
+SQLITE_PRIVATE void *sqlite3Malloc(size_t);
|
|
+SQLITE_PRIVATE void *sqlite3MallocZero(size_t);
|
|
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, size_t);
|
|
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, size_t);
|
|
SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3*, u64);
|
|
SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3*,const char*);
|
|
-SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
|
|
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, size_t);
|
|
SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3*,const char*,const char*);
|
|
-SQLITE_PRIVATE void *sqlite3Realloc(void*, u64);
|
|
-SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
|
|
-SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64);
|
|
+SQLITE_PRIVATE void *sqlite3Realloc(void*, size_t);
|
|
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, size_t);
|
|
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, size_t);
|
|
SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*);
|
|
SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*);
|
|
-SQLITE_PRIVATE int sqlite3MallocSize(void*);
|
|
-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*);
|
|
-SQLITE_PRIVATE void *sqlite3PageMalloc(int);
|
|
+SQLITE_PRIVATE size_t sqlite3MallocSize(void*);
|
|
+SQLITE_PRIVATE size_t sqlite3DbMallocSize(sqlite3*, void*);
|
|
+SQLITE_PRIVATE void *sqlite3PageMalloc(size_t);
|
|
SQLITE_PRIVATE void sqlite3PageFree(void*);
|
|
SQLITE_PRIVATE void sqlite3MemSetDefault(void);
|
|
#ifndef SQLITE_UNTESTABLE
|
|
@@ -18666,6 +18739,9 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull
|
|
** that deal with sqlite3StackAlloc() failures to be unreachable.
|
|
*/
|
|
#ifdef SQLITE_USE_ALLOCA
|
|
+#if !defined(alloca) && defined(_WIN32)
|
|
+# define alloca _alloca
|
|
+#endif
|
|
# define sqlite3StackAllocRaw(D,N) alloca(N)
|
|
# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N)
|
|
# define sqlite3StackFree(D,P)
|
|
@@ -18679,11 +18755,11 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull
|
|
** are, disable MEMSYS3
|
|
*/
|
|
#ifdef SQLITE_ENABLE_MEMSYS5
|
|
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void);
|
|
+SQLITE_PRIVATE const sqlite3_mem_methods2 *sqlite3MemGetMemsys5(void);
|
|
#undef SQLITE_ENABLE_MEMSYS3
|
|
#endif
|
|
#ifdef SQLITE_ENABLE_MEMSYS3
|
|
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void);
|
|
+SQLITE_PRIVATE const sqlite3_mem_methods2 *sqlite3MemGetMemsys3(void);
|
|
#endif
|
|
|
|
|
|
@@ -18700,10 +18776,10 @@ SQLITE_PRIVATE void sqlite3MemoryBarri
|
|
# define sqlite3MemoryBarrier()
|
|
#endif
|
|
|
|
-SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int);
|
|
-SQLITE_PRIVATE void sqlite3StatusUp(int, int);
|
|
-SQLITE_PRIVATE void sqlite3StatusDown(int, int);
|
|
-SQLITE_PRIVATE void sqlite3StatusHighwater(int, int);
|
|
+SQLITE_PRIVATE size_t sqlite3StatusValue(int);
|
|
+SQLITE_PRIVATE void sqlite3StatusUp(int, size_t);
|
|
+SQLITE_PRIVATE void sqlite3StatusDown(int, size_t);
|
|
+SQLITE_PRIVATE void sqlite3StatusHighwater(int, size_t);
|
|
SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3*,int*);
|
|
|
|
/* Access to mutexes used by sqlite3_status() */
|
|
@@ -18747,7 +18823,7 @@ SQLITE_PRIVATE void sqlite3TreeViewBar
|
|
SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
|
|
SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*);
|
|
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
|
|
-SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
|
|
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*);
|
|
#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView*, const Window*, u8);
|
|
SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8);
|
|
@@ -18861,7 +18937,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColum
|
|
# define sqlite3ViewGetColumnNames(A,B) 0
|
|
#endif
|
|
|
|
-#if SQLITE_MAX_ATTACHED>30
|
|
+#if SQLITE_MAX_ATTACHED>62
|
|
SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask);
|
|
#endif
|
|
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
|
|
@@ -18902,7 +18978,7 @@ SQLITE_PRIVATE Table *sqlite3SrcListLook
|
|
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
|
|
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
|
|
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
|
|
-SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
|
|
+SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,const char*);
|
|
#endif
|
|
SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
|
|
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
|
|
@@ -19172,7 +19248,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipColl
|
|
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
|
|
SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*);
|
|
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
|
|
-SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
|
|
+SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, u64);
|
|
SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64);
|
|
SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64);
|
|
SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64);
|
|
@@ -19185,11 +19261,14 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(c
|
|
SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8);
|
|
|
|
SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8);
|
|
-SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8);
|
|
+SQLITE_PRIVATE size_t sqlite3ValueBytes(sqlite3_value*, u8);
|
|
SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
|
|
void(*)(void*));
|
|
SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value*);
|
|
SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*);
|
|
+#ifndef SQLITE_UNTESTABLE
|
|
+SQLITE_PRIVATE void sqlite3ResultIntReal(sqlite3_context*);
|
|
+#endif
|
|
SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *);
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
|
|
@@ -19273,7 +19352,7 @@ SQLITE_PRIVATE void sqlite3OomClear(sqli
|
|
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
|
|
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
|
|
|
|
-SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
|
|
+SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, size_t, size_t);
|
|
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
|
|
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int);
|
|
SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
|
|
@@ -19301,7 +19380,7 @@ SQLITE_PRIVATE char sqlite3IndexColumnAf
|
|
** The interface to the LEMON-generated parser
|
|
*/
|
|
#ifndef SQLITE_AMALGAMATION
|
|
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(u64), Parse*);
|
|
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(size_t), Parse*);
|
|
SQLITE_PRIVATE void sqlite3ParserFree(void*, void(*)(void*));
|
|
#endif
|
|
SQLITE_PRIVATE void sqlite3Parser(void*, int, Token);
|
|
@@ -19811,6 +19890,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3
|
|
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
|
|
SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
|
|
{0,0,0,0,0,0,0,0}, /* m */
|
|
+ {0,0,0,0,0,0,0,0}, /* m2 */
|
|
{0,0,0,0,0,0,0,0,0}, /* mutex */
|
|
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
|
|
(void*)0, /* pHeap */
|
|
@@ -19834,6 +19914,16 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3
|
|
0, /* pInitMutex */
|
|
0, /* xLog */
|
|
0, /* pLogArg */
|
|
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+ 1, /* bVdbeComments */
|
|
+#else
|
|
+ 0, /* bVdbeComments */
|
|
+#endif
|
|
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
|
+ 1, /* bEnableUnknownSqlFunction */
|
|
+#else
|
|
+ 0, /* bEnableUnknownSqlFunction */
|
|
+#endif
|
|
#ifdef SQLITE_ENABLE_SQLLOG
|
|
0, /* xSqllog */
|
|
0, /* pSqllogArg */
|
|
@@ -19847,8 +19937,8 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3
|
|
#endif
|
|
#ifndef SQLITE_UNTESTABLE
|
|
0, /* xTestCallback */
|
|
-#endif
|
|
0, /* bLocaltimeFault */
|
|
+#endif
|
|
0, /* bInternalFunctions */
|
|
0x7ffffffe, /* iOnceResetThreshold */
|
|
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
|
|
@@ -20113,8 +20203,8 @@ struct VdbeFrame {
|
|
int nMem; /* Number of entries in aMem */
|
|
int nChildMem; /* Number of memory cells for child frame */
|
|
int nChildCsr; /* Number of cursors for child frame */
|
|
- int nChange; /* Statement changes (Vdbe.nChange) */
|
|
- int nDbChange; /* Value of db->nChange */
|
|
+ u64 nChange; /* Statement changes (Vdbe.nChange) */
|
|
+ u64 nDbChange; /* Value of db->nChange */
|
|
};
|
|
|
|
/* Magic number for sanity checking on VdbeFrame objects */
|
|
@@ -20135,18 +20225,18 @@ struct sqlite3_value {
|
|
union MemValue {
|
|
double r; /* Real value used when MEM_Real is set in flags */
|
|
i64 i; /* Integer value used when MEM_Int is set in flags */
|
|
- int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */
|
|
+ size_t nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */
|
|
const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */
|
|
FuncDef *pDef; /* Used only when flags==MEM_Agg */
|
|
} u;
|
|
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
|
|
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
|
|
u8 eSubtype; /* Subtype for this value */
|
|
- int n; /* Number of characters in string value, excluding '\0' */
|
|
+ size_t n; /* Number of characters in string value, excluding '\0' */
|
|
char *z; /* String or BLOB value */
|
|
/* ShallowCopy only needs to copy the information above */
|
|
char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
|
|
- int szMalloc; /* Size of the zMalloc allocation */
|
|
+ size_t szMalloc; /* Size of the zMalloc allocation */
|
|
u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
|
|
sqlite3 *db; /* The associated database connection */
|
|
void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
|
|
@@ -20180,12 +20270,12 @@ struct sqlite3_value {
|
|
#define MEM_Int 0x0004 /* Value is an integer */
|
|
#define MEM_Real 0x0008 /* Value is a real number */
|
|
#define MEM_Blob 0x0010 /* Value is a BLOB */
|
|
-#define MEM_AffMask 0x001f /* Mask of affinity bits */
|
|
-#define MEM_FromBind 0x0020 /* Value originates from sqlite3_bind() */
|
|
-/* Available 0x0040 */
|
|
+#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */
|
|
+#define MEM_AffMask 0x003f /* Mask of affinity bits */
|
|
+#define MEM_FromBind 0x0040 /* Value originates from sqlite3_bind() */
|
|
#define MEM_Undefined 0x0080 /* Value is undefined */
|
|
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
|
|
-#define MEM_TypeMask 0xc1df /* Mask of type bits */
|
|
+#define MEM_TypeMask 0xc1bf /* Mask of type bits */
|
|
|
|
|
|
/* Whenever Mem contains a valid string or blob representation, one of
|
|
@@ -20320,7 +20410,7 @@ struct Vdbe {
|
|
u32 cacheCtr; /* VdbeCursor row cache generation counter */
|
|
int pc; /* The program counter */
|
|
int rc; /* Value to return */
|
|
- int nChange; /* Number of db changes made since last reset */
|
|
+ u64 nChange; /* Number of db changes made since last reset */
|
|
int iStatement; /* Statement number (or 0 if has no opened stmt) */
|
|
i64 iCurrentTime; /* Value of julianday('now') for this statement */
|
|
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
|
|
@@ -20419,7 +20509,7 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCurso
|
|
void sqliteVdbePopStack(Vdbe*,int);
|
|
SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
|
|
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
|
|
-SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
|
|
+SQLITE_PRIVATE size_t sqlite3VdbeSerialTypeLen(u32);
|
|
SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
|
|
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
|
|
SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
|
|
@@ -20440,7 +20530,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Me
|
|
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int);
|
|
SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*);
|
|
-SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*));
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, size_t, u8, void(*)(void*));
|
|
SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64);
|
|
#ifdef SQLITE_OMIT_FLOATING_POINT
|
|
# define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64
|
|
@@ -20450,7 +20540,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetD
|
|
SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*));
|
|
SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
|
|
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*);
|
|
-SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int);
|
|
+SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,size_t);
|
|
#ifdef SQLITE_DEBUG
|
|
SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*);
|
|
#endif
|
|
@@ -20474,8 +20564,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemAggValu
|
|
#ifndef SQLITE_OMIT_EXPLAIN
|
|
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
|
|
#endif
|
|
-SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
|
-SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, size_t n, int preserve);
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, size_t n);
|
|
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
|
|
#ifdef SQLITE_DEBUG
|
|
SQLITE_PRIVATE int sqlite3VdbeFrameIsValid(VdbeFrame*);
|
|
@@ -20553,15 +20643,10 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpan
|
|
/*
|
|
** Variables in which to record status information.
|
|
*/
|
|
-#if SQLITE_PTRSIZE>4
|
|
-typedef sqlite3_int64 sqlite3StatValueType;
|
|
-#else
|
|
-typedef u32 sqlite3StatValueType;
|
|
-#endif
|
|
typedef struct sqlite3StatType sqlite3StatType;
|
|
static SQLITE_WSD struct sqlite3StatType {
|
|
- sqlite3StatValueType nowValue[10]; /* Current value */
|
|
- sqlite3StatValueType mxValue[10]; /* Maximum value */
|
|
+ size_t nowValue[10]; /* Current value */
|
|
+ size_t mxValue[10]; /* Maximum value */
|
|
} sqlite3Stat = { {0,}, {0,} };
|
|
|
|
/*
|
|
@@ -20600,7 +20685,7 @@ static const char statMutex[] = {
|
|
** Return the current value of a status parameter. The caller must
|
|
** be holding the appropriate mutex.
|
|
*/
|
|
-SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int op){
|
|
+SQLITE_PRIVATE size_t sqlite3StatusValue(int op){
|
|
wsdStatInit;
|
|
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
|
|
assert( op>=0 && op<ArraySize(statMutex) );
|
|
@@ -20620,7 +20705,7 @@ SQLITE_PRIVATE sqlite3_int64 sqlite3Stat
|
|
** The StatusDown() routine lowers the current value by N. The highwater
|
|
** mark is unchanged. N must be non-negative for StatusDown().
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3StatusUp(int op, int N){
|
|
+SQLITE_PRIVATE void sqlite3StatusUp(int op, size_t N){
|
|
wsdStatInit;
|
|
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
|
|
assert( op>=0 && op<ArraySize(statMutex) );
|
|
@@ -20631,9 +20716,8 @@ SQLITE_PRIVATE void sqlite3StatusUp(int
|
|
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
|
}
|
|
}
|
|
-SQLITE_PRIVATE void sqlite3StatusDown(int op, int N){
|
|
+SQLITE_PRIVATE void sqlite3StatusDown(int op, size_t N){
|
|
wsdStatInit;
|
|
- assert( N>=0 );
|
|
assert( op>=0 && op<ArraySize(statMutex) );
|
|
assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
|
|
: sqlite3MallocMutex()) );
|
|
@@ -20645,11 +20729,8 @@ SQLITE_PRIVATE void sqlite3StatusDown(in
|
|
** Adjust the highwater mark if necessary.
|
|
** The caller must hold the appropriate mutex.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3StatusHighwater(int op, int X){
|
|
- sqlite3StatValueType newValue;
|
|
+SQLITE_PRIVATE void sqlite3StatusHighwater(int op, size_t newValue){
|
|
wsdStatInit;
|
|
- assert( X>=0 );
|
|
- newValue = (sqlite3StatValueType)X;
|
|
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
|
|
assert( op>=0 && op<ArraySize(statMutex) );
|
|
assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
|
|
@@ -20808,7 +20889,7 @@ SQLITE_API int sqlite3_db_status(
|
|
*/
|
|
case SQLITE_DBSTATUS_SCHEMA_USED: {
|
|
int i; /* Used to iterate through schemas */
|
|
- int nByte = 0; /* Used to accumulate return value */
|
|
+ size_t nByte = 0; /* Used to accumulate return value */
|
|
|
|
sqlite3BtreeEnterAll(db);
|
|
db->pnBytesFreed = &nByte;
|
|
@@ -20817,16 +20898,16 @@ SQLITE_API int sqlite3_db_status(
|
|
if( ALWAYS(pSchema!=0) ){
|
|
HashElem *p;
|
|
|
|
- nByte += sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * (
|
|
+ nByte += sqlite3GlobalConfig.m2.xRoundup(sizeof(HashElem)) * (
|
|
pSchema->tblHash.count
|
|
+ pSchema->trigHash.count
|
|
+ pSchema->idxHash.count
|
|
+ pSchema->fkeyHash.count
|
|
);
|
|
- nByte += sqlite3_msize(pSchema->tblHash.ht);
|
|
- nByte += sqlite3_msize(pSchema->trigHash.ht);
|
|
- nByte += sqlite3_msize(pSchema->idxHash.ht);
|
|
- nByte += sqlite3_msize(pSchema->fkeyHash.ht);
|
|
+ nByte += sqlite3MallocSize(pSchema->tblHash.ht);
|
|
+ nByte += sqlite3MallocSize(pSchema->trigHash.ht);
|
|
+ nByte += sqlite3MallocSize(pSchema->idxHash.ht);
|
|
+ nByte += sqlite3MallocSize(pSchema->fkeyHash.ht);
|
|
|
|
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
|
|
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
|
|
@@ -20840,7 +20921,7 @@ SQLITE_API int sqlite3_db_status(
|
|
sqlite3BtreeLeaveAll(db);
|
|
|
|
*pHighwater = 0;
|
|
- *pCurrent = nByte;
|
|
+ *pCurrent = (int)nByte;
|
|
break;
|
|
}
|
|
|
|
@@ -20851,7 +20932,7 @@ SQLITE_API int sqlite3_db_status(
|
|
*/
|
|
case SQLITE_DBSTATUS_STMT_USED: {
|
|
struct Vdbe *pVdbe; /* Used to iterate through VMs */
|
|
- int nByte = 0; /* Used to accumulate return value */
|
|
+ size_t nByte = 0; /* Used to accumulate return value */
|
|
|
|
db->pnBytesFreed = &nByte;
|
|
for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){
|
|
@@ -20861,7 +20942,7 @@ SQLITE_API int sqlite3_db_status(
|
|
db->pnBytesFreed = 0;
|
|
|
|
*pHighwater = 0; /* IMP: R-64479-57858 */
|
|
- *pCurrent = nByte;
|
|
+ *pCurrent = (int)nByte;
|
|
|
|
break;
|
|
}
|
|
@@ -21026,8 +21107,8 @@ struct DateTime {
|
|
*/
|
|
static int getDigits(const char *zDate, const char *zFormat, ...){
|
|
/* The aMx[] array translates the 3rd character of each format
|
|
- ** spec into a max size: a b c d e f */
|
|
- static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 };
|
|
+ ** spec into a max size: a b c d e f g */
|
|
+ static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999, 60 };
|
|
va_list ap;
|
|
int cnt = 0;
|
|
char nextC;
|
|
@@ -21123,7 +21204,7 @@ static int parseHhMmSs(const char *zDate
|
|
zDate += 5;
|
|
if( *zDate==':' ){
|
|
zDate++;
|
|
- if( getDigits(zDate, "20e", &s)!=1 ){
|
|
+ if( getDigits(zDate, "20g", &s)!=1 ){
|
|
return 1;
|
|
}
|
|
zDate += 2;
|
|
@@ -21457,7 +21538,7 @@ static int osLocaltime(time_t *t, struct
|
|
#endif /* SQLITE_OMIT_LOCALTIME */
|
|
|
|
|
|
-#ifndef SQLITE_OMIT_LOCALTIME
|
|
+#if 1
|
|
/*
|
|
** Compute the difference (in milliseconds) between localtime and UTC
|
|
** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs,
|
|
@@ -21471,6 +21552,16 @@ static sqlite3_int64 localtimeOffset(
|
|
sqlite3_context *pCtx, /* Write error here if one occurs */
|
|
int *pRc /* OUT: Error code. SQLITE_OK or ERROR */
|
|
){
|
|
+#ifdef SQLITE_OMIT_LOCALTIME
|
|
+#ifndef SQLITE_UNTESTABLE
|
|
+ if( sqlite3GlobalConfig.bLocaltimeFault ) {
|
|
+ sqlite3_result_error(pCtx, "local time unavailable", -1);
|
|
+ *pRc = SQLITE_ERROR;
|
|
+ } else
|
|
+#endif
|
|
+ *pRc = SQLITE_OK;
|
|
+ return 0;
|
|
+#else
|
|
DateTime x, y;
|
|
time_t t;
|
|
struct tm sLocal;
|
|
@@ -21520,6 +21611,7 @@ static sqlite3_int64 localtimeOffset(
|
|
computeJD(&y);
|
|
*pRc = SQLITE_OK;
|
|
return y.iJD - x.iJD;
|
|
+#endif /* SQLITE_OMIT_LOCALTIME */
|
|
}
|
|
#endif /* SQLITE_OMIT_LOCALTIME */
|
|
|
|
@@ -21534,16 +21626,16 @@ static sqlite3_int64 localtimeOffset(
|
|
static const struct {
|
|
u8 eType; /* Transformation type code */
|
|
u8 nName; /* Length of th name */
|
|
- char *zName; /* Name of the transformation */
|
|
+ const char *zName; /* Name of the transformation */
|
|
double rLimit; /* Maximum NNN value for this transform */
|
|
double rXform; /* Constant used for this transform */
|
|
} aXformType[] = {
|
|
- { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
|
|
- { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
|
|
- { 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
|
|
+ { 0, 6, "second", 464269060800.0, 1000.0 },
|
|
+ { 0, 6, "minute", 7737817680.0, 60000.0 },
|
|
+ { 0, 4, "hour", 128963628.0, 3600000.0 },
|
|
{ 0, 3, "day", 5373485.0, 86400000.0 },
|
|
- { 1, 5, "month", 176546.0, 30.0*86400000.0 },
|
|
- { 2, 4, "year", 14713.0, 365.0*86400000.0 },
|
|
+ { 1, 5, "month", 176546.0, 2592000000.0 },
|
|
+ { 2, 4, "year", 14713.0, 31536000000.0 },
|
|
};
|
|
|
|
/*
|
|
@@ -21579,7 +21671,7 @@ static int parseModifier(
|
|
int rc = 1;
|
|
double r;
|
|
switch(sqlite3UpperToLower[(u8)z[0]] ){
|
|
-#ifndef SQLITE_OMIT_LOCALTIME
|
|
+#if 1
|
|
case 'l': {
|
|
/* localtime
|
|
**
|
|
@@ -21611,7 +21703,7 @@ static int parseModifier(
|
|
rc = 0;
|
|
}
|
|
}
|
|
-#ifndef SQLITE_OMIT_LOCALTIME
|
|
+#if 1
|
|
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
|
|
if( p->tzSet==0 ){
|
|
sqlite3_int64 c1;
|
|
@@ -22488,7 +22580,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(s
|
|
** error in sqlite3_os_init() by the upper layers can be tested.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3OsInit(void){
|
|
- void *p = sqlite3_malloc(10);
|
|
+ void *p = sqlite3Malloc(10);
|
|
if( p==0 ) return SQLITE_NOMEM_BKPT;
|
|
sqlite3_free(p);
|
|
return sqlite3_os_init();
|
|
@@ -22713,11 +22805,11 @@ SQLITE_PRIVATE void sqlite3EndBenignMall
|
|
/*
|
|
** No-op versions of all memory allocation routines
|
|
*/
|
|
-static void *sqlite3MemMalloc(int nByte){ return 0; }
|
|
+static void *sqlite3MemMalloc(size_t nByte){ return 0; }
|
|
static void sqlite3MemFree(void *pPrior){ return; }
|
|
-static void *sqlite3MemRealloc(void *pPrior, int nByte){ return 0; }
|
|
-static int sqlite3MemSize(void *pPrior){ return 0; }
|
|
-static int sqlite3MemRoundup(int n){ return n; }
|
|
+static void *sqlite3MemRealloc(void *pPrior, size_t nByte){ return 0; }
|
|
+static size_t sqlite3MemSize(void *pPrior){ return 0; }
|
|
+static size_t sqlite3MemRoundup(size_t n){ return n; }
|
|
static int sqlite3MemInit(void *NotUsed){ return SQLITE_OK; }
|
|
static void sqlite3MemShutdown(void *NotUsed){ return; }
|
|
|
|
@@ -22728,7 +22820,7 @@ static void sqlite3MemShutdown(void *Not
|
|
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
|
- static const sqlite3_mem_methods defaultMethods = {
|
|
+ static const sqlite3_mem_methods2 defaultMethods = {
|
|
sqlite3MemMalloc,
|
|
sqlite3MemFree,
|
|
sqlite3MemRealloc,
|
|
@@ -22738,7 +22830,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault
|
|
sqlite3MemShutdown,
|
|
0
|
|
};
|
|
- sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
|
+ sqlite3_config(SQLITE_CONFIG_MALLOC2, &defaultMethods);
|
|
}
|
|
|
|
#endif /* SQLITE_ZERO_MALLOC */
|
|
@@ -22837,7 +22929,7 @@ static malloc_zone_t* _sqliteZone_;
|
|
** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
|
|
** the malloc.h header file.
|
|
*/
|
|
-#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
|
|
+#elif (defined(_MSC_VER) || defined(__MSVCRT__)) && !defined(SQLITE_WITHOUT_MSIZE)
|
|
# define SQLITE_USE_MALLOC_H
|
|
# define SQLITE_USE_MSIZE
|
|
#endif
|
|
@@ -22851,6 +22943,9 @@ static malloc_zone_t* _sqliteZone_;
|
|
*/
|
|
#if defined(SQLITE_USE_MALLOC_H)
|
|
# include <malloc.h>
|
|
+#if !defined(alloca) && defined(_WIN32)
|
|
+# define alloca _alloca
|
|
+#endif
|
|
# if defined(SQLITE_USE_MALLOC_USABLE_SIZE)
|
|
# if !defined(SQLITE_MALLOCSIZE)
|
|
# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
|
@@ -22872,14 +22967,14 @@ static malloc_zone_t* _sqliteZone_;
|
|
** cases of nByte<=0 will be intercepted and dealt with by higher level
|
|
** routines.
|
|
*/
|
|
-static void *sqlite3MemMalloc(int nByte){
|
|
+static void *sqlite3MemMalloc(size_t nByte){
|
|
#ifdef SQLITE_MALLOCSIZE
|
|
void *p;
|
|
testcase( ROUND8(nByte)==nByte );
|
|
p = SQLITE_MALLOC( nByte );
|
|
if( p==0 ){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to allocate %llu bytes of memory", (u64)nByte);
|
|
}
|
|
return p;
|
|
#else
|
|
@@ -22892,7 +22987,7 @@ static void *sqlite3MemMalloc(int nByte)
|
|
p++;
|
|
}else{
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to allocate %llu bytes of memory", (u64)nByte);
|
|
}
|
|
return (void *)p;
|
|
#endif
|
|
@@ -22921,16 +23016,16 @@ static void sqlite3MemFree(void *pPrior)
|
|
** Report the allocated size of a prior return from xMalloc()
|
|
** or xRealloc().
|
|
*/
|
|
-static int sqlite3MemSize(void *pPrior){
|
|
+static size_t sqlite3MemSize(void *pPrior){
|
|
#ifdef SQLITE_MALLOCSIZE
|
|
assert( pPrior!=0 );
|
|
- return (int)SQLITE_MALLOCSIZE(pPrior);
|
|
+ return SQLITE_MALLOCSIZE(pPrior);
|
|
#else
|
|
sqlite3_int64 *p;
|
|
assert( pPrior!=0 );
|
|
p = (sqlite3_int64*)pPrior;
|
|
p--;
|
|
- return (int)p[0];
|
|
+ return (size_t)p[0];
|
|
#endif
|
|
}
|
|
|
|
@@ -22944,14 +23039,14 @@ static int sqlite3MemSize(void *pPrior){
|
|
** cases where nByte<=0 will have been intercepted by higher-level
|
|
** routines and redirected to xFree.
|
|
*/
|
|
-static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
|
+static void *sqlite3MemRealloc(void *pPrior, size_t nByte){
|
|
#ifdef SQLITE_MALLOCSIZE
|
|
void *p = SQLITE_REALLOC(pPrior, nByte);
|
|
if( p==0 ){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
sqlite3_log(SQLITE_NOMEM,
|
|
- "failed memory resize %u to %u bytes",
|
|
- SQLITE_MALLOCSIZE(pPrior), nByte);
|
|
+ "failed memory resize %llu to %llu bytes",
|
|
+ (u64)SQLITE_MALLOCSIZE(pPrior), (u64)nByte);
|
|
}
|
|
return p;
|
|
#else
|
|
@@ -22966,8 +23061,8 @@ static void *sqlite3MemRealloc(void *pPr
|
|
}else{
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
sqlite3_log(SQLITE_NOMEM,
|
|
- "failed memory resize %u to %u bytes",
|
|
- sqlite3MemSize(pPrior), nByte);
|
|
+ "failed memory resize %llu to %llu bytes",
|
|
+ (u64)sqlite3MemSize(pPrior), (u64)nByte);
|
|
}
|
|
return (void*)p;
|
|
#endif
|
|
@@ -22976,7 +23071,7 @@ static void *sqlite3MemRealloc(void *pPr
|
|
/*
|
|
** Round up a request size to the next valid allocation size.
|
|
*/
|
|
-static int sqlite3MemRoundup(int n){
|
|
+static size_t sqlite3MemRoundup(size_t n){
|
|
return ROUND8(n);
|
|
}
|
|
|
|
@@ -23022,7 +23117,7 @@ static void sqlite3MemShutdown(void *Not
|
|
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
|
- static const sqlite3_mem_methods defaultMethods = {
|
|
+ static const sqlite3_mem_methods2 defaultMethods = {
|
|
sqlite3MemMalloc,
|
|
sqlite3MemFree,
|
|
sqlite3MemRealloc,
|
|
@@ -23032,7 +23127,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault
|
|
sqlite3MemShutdown,
|
|
0
|
|
};
|
|
- sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
|
+ sqlite3_config(SQLITE_CONFIG_MALLOC2, &defaultMethods);
|
|
}
|
|
|
|
#endif /* SQLITE_SYSTEM_MALLOC */
|
|
@@ -23094,20 +23189,20 @@ SQLITE_PRIVATE void sqlite3MemSetDefault
|
|
** MemBlockHdr.
|
|
*/
|
|
struct MemBlockHdr {
|
|
- i64 iSize; /* Size of this allocation */
|
|
+ size_t iSize; /* Size of this allocation */
|
|
struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */
|
|
char nBacktrace; /* Number of backtraces on this alloc */
|
|
char nBacktraceSlots; /* Available backtrace slots */
|
|
u8 nTitle; /* Bytes of title; includes '\0' */
|
|
u8 eType; /* Allocation type code */
|
|
- int iForeGuard; /* Guard word for sanity */
|
|
+ unsigned int iForeGuard; /* Guard word for sanity */
|
|
};
|
|
|
|
/*
|
|
** Guard words
|
|
*/
|
|
-#define FOREGUARD 0x80F5E153
|
|
-#define REARGUARD 0xE4676B53
|
|
+#define FOREGUARD 0x80F5E153U
|
|
+#define REARGUARD 0xE4676B53U
|
|
|
|
/*
|
|
** Number of malloc size increments to track.
|
|
@@ -23192,13 +23287,16 @@ static void adjustStats(int iSize, int i
|
|
*/
|
|
static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
|
|
struct MemBlockHdr *p;
|
|
+#ifndef NDEBUG
|
|
int *pInt;
|
|
u8 *pU8;
|
|
- int nReserve;
|
|
+ size_t nReserve;
|
|
+#endif /* NDEBUG */
|
|
|
|
p = (struct MemBlockHdr*)pAllocation;
|
|
p--;
|
|
- assert( p->iForeGuard==(int)FOREGUARD );
|
|
+#ifndef NDEBUG
|
|
+ assert( p->iForeGuard==FOREGUARD );
|
|
nReserve = ROUND8(p->iSize);
|
|
pInt = (int*)pAllocation;
|
|
pU8 = (u8*)pAllocation;
|
|
@@ -23208,19 +23306,20 @@ static struct MemBlockHdr *sqlite3Memsys
|
|
** they haven't been overwritten.
|
|
*/
|
|
while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 );
|
|
+#endif /* NDEBUG */
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
** Return the number of bytes currently allocated at address p.
|
|
*/
|
|
-static int sqlite3MemSize(void *p){
|
|
+static size_t sqlite3MemSize(void *p){
|
|
struct MemBlockHdr *pHdr;
|
|
if( !p ){
|
|
return 0;
|
|
}
|
|
pHdr = sqlite3MemsysGetHeader(p);
|
|
- return (int)pHdr->iSize;
|
|
+ return (size_t)pHdr->iSize;
|
|
}
|
|
|
|
/*
|
|
@@ -23248,7 +23347,7 @@ static void sqlite3MemShutdown(void *Not
|
|
/*
|
|
** Round up a request size to the next valid allocation size.
|
|
*/
|
|
-static int sqlite3MemRoundup(int n){
|
|
+static size_t sqlite3MemRoundup(size_t n){
|
|
return ROUND8(n);
|
|
}
|
|
|
|
@@ -23257,7 +23356,7 @@ static int sqlite3MemRoundup(int n){
|
|
** the content of a new memory allocation to unpredictable values and
|
|
** to clear the content of a freed allocation to unpredictable values.
|
|
*/
|
|
-static void randomFill(char *pBuf, int nByte){
|
|
+static void randomFill(char *pBuf, size_t nByte){
|
|
unsigned int x, y, r;
|
|
x = SQLITE_PTR_TO_INT(pBuf);
|
|
y = nByte | 1;
|
|
@@ -23280,14 +23379,14 @@ static void randomFill(char *pBuf, int n
|
|
/*
|
|
** Allocate nByte bytes of memory.
|
|
*/
|
|
-static void *sqlite3MemMalloc(int nByte){
|
|
+static void *sqlite3MemMalloc(size_t nByte){
|
|
struct MemBlockHdr *pHdr;
|
|
void **pBt;
|
|
char *z;
|
|
int *pInt;
|
|
void *p = 0;
|
|
- int totalSize;
|
|
- int nReserve;
|
|
+ size_t totalSize;
|
|
+ size_t nReserve;
|
|
sqlite3_mutex_enter(mem.mutex);
|
|
assert( mem.disallow==0 );
|
|
nReserve = ROUND8(nByte);
|
|
@@ -23381,7 +23480,7 @@ static void sqlite3MemFree(void *pPrior)
|
|
** much more likely to break and we are much more liking to find
|
|
** the error.
|
|
*/
|
|
-static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
|
+static void *sqlite3MemRealloc(void *pPrior, size_t nByte){
|
|
struct MemBlockHdr *pOldHdr;
|
|
void *pNew;
|
|
assert( mem.disallow==0 );
|
|
@@ -23403,7 +23502,7 @@ static void *sqlite3MemRealloc(void *pPr
|
|
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
|
- static const sqlite3_mem_methods defaultMethods = {
|
|
+ static const sqlite3_mem_methods2 defaultMethods = {
|
|
sqlite3MemMalloc,
|
|
sqlite3MemFree,
|
|
sqlite3MemRealloc,
|
|
@@ -23413,14 +23512,14 @@ SQLITE_PRIVATE void sqlite3MemSetDefault
|
|
sqlite3MemShutdown,
|
|
0
|
|
};
|
|
- sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
|
+ sqlite3_config(SQLITE_CONFIG_MALLOC2, &defaultMethods);
|
|
}
|
|
|
|
/*
|
|
** Set the "type" of an allocation.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
|
|
- if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
|
+ if( p && sqlite3GlobalConfig.m2.xMalloc==sqlite3MemMalloc ){
|
|
struct MemBlockHdr *pHdr;
|
|
pHdr = sqlite3MemsysGetHeader(p);
|
|
assert( pHdr->iForeGuard==FOREGUARD );
|
|
@@ -23439,7 +23538,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSetTy
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
|
|
int rc = 1;
|
|
- if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
|
+ if( p && sqlite3GlobalConfig.m2.xMalloc==sqlite3MemMalloc ){
|
|
struct MemBlockHdr *pHdr;
|
|
pHdr = sqlite3MemsysGetHeader(p);
|
|
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
|
|
@@ -23461,7 +23560,7 @@ SQLITE_PRIVATE int sqlite3MemdebugHasTyp
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
|
|
int rc = 1;
|
|
- if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
|
+ if( p && sqlite3GlobalConfig.m2.xMalloc==sqlite3MemMalloc ){
|
|
struct MemBlockHdr *pHdr;
|
|
pHdr = sqlite3MemsysGetHeader(p);
|
|
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
|
|
@@ -23528,8 +23627,8 @@ SQLITE_PRIVATE void sqlite3MemdebugDump(
|
|
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
|
|
char *z = (char*)pHdr;
|
|
z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle;
|
|
- fprintf(out, "**** %lld bytes at %p from %s ****\n",
|
|
- pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???");
|
|
+ fprintf(out, "**** %llu bytes at %p from %s ****\n",
|
|
+ (u64)pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???");
|
|
if( pHdr->nBacktrace ){
|
|
fflush(out);
|
|
pBt = (void**)pHdr;
|
|
@@ -23804,12 +23903,14 @@ static void memsys3Leave(void){
|
|
/*
|
|
** Called when we are unable to satisfy an allocation of nBytes.
|
|
*/
|
|
-static void memsys3OutOfMemory(int nByte){
|
|
+static void memsys3OutOfMemory(size_t nByte){
|
|
if( !mem3.alarmBusy ){
|
|
mem3.alarmBusy = 1;
|
|
assert( sqlite3_mutex_held(mem3.mutex) );
|
|
sqlite3_mutex_leave(mem3.mutex);
|
|
- sqlite3_release_memory(nByte);
|
|
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
+ sqlite3PcacheReleaseMemory(nByte);
|
|
+#endif
|
|
sqlite3_mutex_enter(mem3.mutex);
|
|
mem3.alarmBusy = 0;
|
|
}
|
|
@@ -23923,9 +24024,9 @@ static void memsys3Merge(u32 *pRoot){
|
|
** This function assumes that the necessary mutexes, if any, are
|
|
** already held by the caller. Hence "Unsafe".
|
|
*/
|
|
-static void *memsys3MallocUnsafe(int nByte){
|
|
+static void *memsys3MallocUnsafe(size_t nByte){
|
|
u32 i;
|
|
- u32 nBlock;
|
|
+ size_t nBlock;
|
|
u32 toFree;
|
|
|
|
assert( sqlite3_mutex_held(mem3.mutex) );
|
|
@@ -23949,7 +24050,7 @@ static void *memsys3MallocUnsafe(int nBy
|
|
return memsys3Checkout(i, nBlock);
|
|
}
|
|
}else{
|
|
- int hash = nBlock % N_HASH;
|
|
+ size_t hash = nBlock % N_HASH;
|
|
for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){
|
|
if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){
|
|
memsys3UnlinkFromList(i, &mem3.aiHash[hash]);
|
|
@@ -24046,7 +24147,7 @@ static void memsys3FreeUnsafe(void *pOld
|
|
** size returned omits the 8-byte header overhead. This only
|
|
** works for chunks that are currently checked out.
|
|
*/
|
|
-static int memsys3Size(void *p){
|
|
+static size_t memsys3Size(void *p){
|
|
Mem3Block *pBlock;
|
|
assert( p!=0 );
|
|
pBlock = (Mem3Block*)p;
|
|
@@ -24057,7 +24158,7 @@ static int memsys3Size(void *p){
|
|
/*
|
|
** Round up a request size to the next valid allocation size.
|
|
*/
|
|
-static int memsys3Roundup(int n){
|
|
+static size_t memsys3Roundup(size_t n){
|
|
if( n<=12 ){
|
|
return 12;
|
|
}else{
|
|
@@ -24068,7 +24169,7 @@ static int memsys3Roundup(int n){
|
|
/*
|
|
** Allocate nBytes of memory.
|
|
*/
|
|
-static void *memsys3Malloc(int nBytes){
|
|
+static void *memsys3Malloc(size_t nBytes){
|
|
sqlite3_int64 *p;
|
|
assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */
|
|
memsys3Enter();
|
|
@@ -24090,11 +24191,11 @@ static void memsys3Free(void *pPrior){
|
|
/*
|
|
** Change the size of an existing memory allocation
|
|
*/
|
|
-static void *memsys3Realloc(void *pPrior, int nBytes){
|
|
- int nOld;
|
|
+static void *memsys3Realloc(void *pPrior, size_t nBytes){
|
|
+ size_t nOld;
|
|
void *p;
|
|
if( pPrior==0 ){
|
|
- return sqlite3_malloc(nBytes);
|
|
+ return memsys3Malloc(nBytes);
|
|
}
|
|
if( nBytes<=0 ){
|
|
sqlite3_free(pPrior);
|
|
@@ -24158,8 +24259,8 @@ static void memsys3Shutdown(void *NotUse
|
|
** Open the file indicated and write a log of all unfreed memory
|
|
** allocations into that log.
|
|
*/
|
|
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
|
SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
|
|
-#ifdef SQLITE_DEBUG
|
|
FILE *out;
|
|
u32 i, j;
|
|
u32 size;
|
|
@@ -24226,10 +24327,8 @@ SQLITE_PRIVATE void sqlite3Memsys3Dump(c
|
|
}else{
|
|
fclose(out);
|
|
}
|
|
-#else
|
|
- UNUSED_PARAMETER(zFilename);
|
|
-#endif
|
|
}
|
|
+#endif
|
|
|
|
/*
|
|
** This routine is the only routine in this file with external
|
|
@@ -24242,8 +24341,8 @@ SQLITE_PRIVATE void sqlite3Memsys3Dump(c
|
|
** This routine is only called by sqlite3_config(), and therefore
|
|
** is not required to be threadsafe (it is not).
|
|
*/
|
|
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
|
|
- static const sqlite3_mem_methods mempoolMethods = {
|
|
+SQLITE_PRIVATE const sqlite3_mem_methods2 *sqlite3MemGetMemsys3(void){
|
|
+ static const sqlite3_mem_methods2 mempoolMethods = {
|
|
memsys3Malloc,
|
|
memsys3Free,
|
|
memsys3Realloc,
|
|
@@ -24355,8 +24454,8 @@ static SQLITE_WSD struct Mem5Global {
|
|
/*
|
|
** Memory available for allocation
|
|
*/
|
|
- int szAtom; /* Smallest possible allocation in bytes */
|
|
- int nBlock; /* Number of szAtom sized blocks in zPool */
|
|
+ size_t szAtom; /* Smallest possible allocation in bytes */
|
|
+ size_t nBlock; /* Number of szAtom sized blocks in zPool */
|
|
u8 *zPool; /* Memory available to be allocated */
|
|
|
|
/*
|
|
@@ -24375,7 +24474,7 @@ static SQLITE_WSD struct Mem5Global {
|
|
u32 currentCount; /* Current number of distinct checkouts */
|
|
u32 maxOut; /* Maximum instantaneous currentOut */
|
|
u32 maxCount; /* Maximum instantaneous currentCount */
|
|
- u32 maxRequest; /* Largest allocation (exclusive of internal frag) */
|
|
+ size_t maxRequest; /* Largest allocation (exclusive of internal frag) */
|
|
#endif
|
|
|
|
/*
|
|
@@ -24408,10 +24507,10 @@ static SQLITE_WSD struct Mem5Global {
|
|
** Unlink the chunk at mem5.aPool[i] from list it is currently
|
|
** on. It should be found on mem5.aiFreelist[iLogsize].
|
|
*/
|
|
-static void memsys5Unlink(int i, int iLogsize){
|
|
+static void memsys5Unlink(size_t i, size_t iLogsize){
|
|
int next, prev;
|
|
- assert( i>=0 && i<mem5.nBlock );
|
|
- assert( iLogsize>=0 && iLogsize<=LOGMAX );
|
|
+ assert( i<mem5.nBlock );
|
|
+ assert( iLogsize<=LOGMAX );
|
|
assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
|
|
|
|
next = MEM5LINK(i)->next;
|
|
@@ -24460,8 +24559,9 @@ static void memsys5Leave(void){
|
|
** Return the size of an outstanding allocation, in bytes.
|
|
** This only works for chunks that are currently checked out.
|
|
*/
|
|
-static int memsys5Size(void *p){
|
|
- int iSize, i;
|
|
+static size_t memsys5Size(void *p){
|
|
+ size_t iSize;
|
|
+ int i;
|
|
assert( p!=0 );
|
|
i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
|
|
assert( i>=0 && i<mem5.nBlock );
|
|
@@ -24479,22 +24579,24 @@ static int memsys5Size(void *p){
|
|
** routine so there is never any chance that two or more
|
|
** threads can be in this routine at the same time.
|
|
*/
|
|
-static void *memsys5MallocUnsafe(int nByte){
|
|
+static void *memsys5MallocUnsafe(size_t nByte){
|
|
int i; /* Index of a mem5.aPool[] slot */
|
|
int iBin; /* Index into mem5.aiFreelist[] */
|
|
- int iFullSz; /* Size of allocation rounded up to power of 2 */
|
|
+ size_t iFullSz; /* Size of allocation rounded up to power of 2 */
|
|
int iLogsize; /* Log2 of iFullSz/POW2_MIN */
|
|
|
|
/* nByte must be a positive */
|
|
assert( nByte>0 );
|
|
|
|
+#if 0
|
|
/* No more than 1GiB per allocation */
|
|
if( nByte > 0x40000000 ) return 0;
|
|
+#endif
|
|
|
|
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
|
/* Keep track of the maximum allocation request. Even unfulfilled
|
|
** requests are counted */
|
|
- if( (u32)nByte>mem5.maxRequest ){
|
|
+ if( nByte>mem5.maxRequest ){
|
|
mem5.maxRequest = nByte;
|
|
}
|
|
#endif
|
|
@@ -24516,10 +24618,10 @@ static void *memsys5MallocUnsafe(int nBy
|
|
i = mem5.aiFreelist[iBin];
|
|
memsys5Unlink(i, iBin);
|
|
while( iBin>iLogsize ){
|
|
- int newSize;
|
|
+ size_t newSize;
|
|
|
|
iBin--;
|
|
- newSize = 1 << iBin;
|
|
+ newSize = (size_t)1 << iBin;
|
|
mem5.aCtrl[i+newSize] = CTRL_FREE | iBin;
|
|
memsys5Link(i+newSize, iBin);
|
|
}
|
|
@@ -24550,21 +24652,21 @@ static void *memsys5MallocUnsafe(int nBy
|
|
** Free an outstanding memory allocation.
|
|
*/
|
|
static void memsys5FreeUnsafe(void *pOld){
|
|
- u32 size, iLogsize;
|
|
- int iBlock;
|
|
+ size_t size, iLogsize;
|
|
+ size_t iBlock;
|
|
|
|
/* Set iBlock to the index of the block pointed to by pOld in
|
|
** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
|
|
*/
|
|
- iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
|
|
+ iBlock = (((u8 *)pOld-mem5.zPool)/mem5.szAtom);
|
|
|
|
/* Check that the pointer pOld points to a valid, non-free block. */
|
|
- assert( iBlock>=0 && iBlock<mem5.nBlock );
|
|
+ assert( iBlock<mem5.nBlock );
|
|
assert( ((u8 *)pOld-mem5.zPool)%mem5.szAtom==0 );
|
|
assert( (mem5.aCtrl[iBlock] & CTRL_FREE)==0 );
|
|
|
|
iLogsize = mem5.aCtrl[iBlock] & CTRL_LOGSIZE;
|
|
- size = 1<<iLogsize;
|
|
+ size = (size_t)1<<iLogsize;
|
|
assert( iBlock+size-1<(u32)mem5.nBlock );
|
|
|
|
mem5.aCtrl[iBlock] |= CTRL_FREE;
|
|
@@ -24581,7 +24683,7 @@ static void memsys5FreeUnsafe(void *pOld
|
|
|
|
mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
|
|
while( ALWAYS(iLogsize<LOGMAX) ){
|
|
- int iBuddy;
|
|
+ size_t iBuddy;
|
|
if( (iBlock>>iLogsize) & 1 ){
|
|
iBuddy = iBlock - size;
|
|
assert( iBuddy>=0 );
|
|
@@ -24615,7 +24717,7 @@ static void memsys5FreeUnsafe(void *pOld
|
|
/*
|
|
** Allocate nBytes of memory.
|
|
*/
|
|
-static void *memsys5Malloc(int nBytes){
|
|
+static void *memsys5Malloc(size_t nBytes){
|
|
sqlite3_int64 *p = 0;
|
|
if( nBytes>0 ){
|
|
memsys5Enter();
|
|
@@ -24650,8 +24752,8 @@ static void memsys5Free(void *pPrior){
|
|
** (an allocation larger than 0x40000000) was requested and this
|
|
** routine should return 0 without freeing pPrior.
|
|
*/
|
|
-static void *memsys5Realloc(void *pPrior, int nBytes){
|
|
- int nOld;
|
|
+static void *memsys5Realloc(void *pPrior, size_t nBytes){
|
|
+ unsigned int nOld;
|
|
void *p;
|
|
assert( pPrior!=0 );
|
|
assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */
|
|
@@ -24680,9 +24782,8 @@ static void *memsys5Realloc(void *pPrior
|
|
** 32-bit signed integer. Hence the largest allocation is 0x40000000
|
|
** or 1073741824 bytes.
|
|
*/
|
|
-static int memsys5Roundup(int n){
|
|
- int iFullSz;
|
|
- if( n > 0x40000000 ) return 0;
|
|
+static size_t memsys5Roundup(size_t n){
|
|
+ size_t iFullSz;
|
|
for(iFullSz=mem5.szAtom; iFullSz<n; iFullSz *= 2);
|
|
return iFullSz;
|
|
}
|
|
@@ -24697,9 +24798,9 @@ static int memsys5Roundup(int n){
|
|
** memsys5Log(8) -> 3
|
|
** memsys5Log(9) -> 4
|
|
*/
|
|
-static int memsys5Log(int iValue){
|
|
+static int memsys5Log(size_t iValue){
|
|
int iLog;
|
|
- for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<<iLog)<iValue; iLog++);
|
|
+ for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && ((size_t)1<<iLog)<iValue; iLog++);
|
|
return iLog;
|
|
}
|
|
|
|
@@ -24711,10 +24812,10 @@ static int memsys5Log(int iValue){
|
|
*/
|
|
static int memsys5Init(void *NotUsed){
|
|
int ii; /* Loop counter */
|
|
- int nByte; /* Number of bytes of memory available to this allocator */
|
|
+ size_t nByte; /* Number of bytes of memory available to this allocator */
|
|
u8 *zByte; /* Memory usable by this allocator */
|
|
int nMinLog; /* Log base 2 of minimum allocation size in bytes */
|
|
- int iOffset; /* An offset into mem5.aCtrl[] */
|
|
+ size_t iOffset; /* An offset into mem5.aCtrl[] */
|
|
|
|
UNUSED_PARAMETER(NotUsed);
|
|
|
|
@@ -24732,8 +24833,8 @@ static int memsys5Init(void *NotUsed){
|
|
|
|
/* boundaries on sqlite3GlobalConfig.mnReq are enforced in sqlite3_config() */
|
|
nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq);
|
|
- mem5.szAtom = (1<<nMinLog);
|
|
- while( (int)sizeof(Mem5Link)>mem5.szAtom ){
|
|
+ mem5.szAtom = ((size_t)1<<nMinLog);
|
|
+ while( sizeof(Mem5Link)>mem5.szAtom ){
|
|
mem5.szAtom = mem5.szAtom << 1;
|
|
}
|
|
|
|
@@ -24747,7 +24848,7 @@ static int memsys5Init(void *NotUsed){
|
|
|
|
iOffset = 0;
|
|
for(ii=LOGMAX; ii>=0; ii--){
|
|
- int nAlloc = (1<<ii);
|
|
+ size_t nAlloc = ((size_t)1<<ii);
|
|
if( (iOffset+nAlloc)<=mem5.nBlock ){
|
|
mem5.aCtrl[iOffset] = ii | CTRL_FREE;
|
|
memsys5Link(iOffset, ii);
|
|
@@ -24799,14 +24900,14 @@ SQLITE_PRIVATE void sqlite3Memsys5Dump(c
|
|
for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){}
|
|
fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n);
|
|
}
|
|
- fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc);
|
|
- fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc);
|
|
- fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess);
|
|
+ fprintf(out, "mem5.nAlloc = %llu\n", (u64)mem5.nAlloc);
|
|
+ fprintf(out, "mem5.totalAlloc = %llu\n", (u64)mem5.totalAlloc);
|
|
+ fprintf(out, "mem5.totalExcess = %llu\n", (u64)mem5.totalExcess);
|
|
fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut);
|
|
fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount);
|
|
fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut);
|
|
fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount);
|
|
- fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest);
|
|
+ fprintf(out, "mem5.maxRequest = %llu\n", (u64)mem5.maxRequest);
|
|
memsys5Leave();
|
|
if( out==stdout ){
|
|
fflush(stdout);
|
|
@@ -24821,8 +24922,8 @@ SQLITE_PRIVATE void sqlite3Memsys5Dump(c
|
|
** linkage. It returns a pointer to a static sqlite3_mem_methods
|
|
** struct populated with the memsys5 methods.
|
|
*/
|
|
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
|
|
- static const sqlite3_mem_methods memsys5Methods = {
|
|
+SQLITE_PRIVATE const sqlite3_mem_methods2 *sqlite3MemGetMemsys5(void){
|
|
+ static const sqlite3_mem_methods2 memsys5Methods = {
|
|
memsys5Malloc,
|
|
memsys5Free,
|
|
memsys5Realloc,
|
|
@@ -26063,9 +26164,16 @@ SQLITE_API extern int sqlite3_open_file_
|
|
*/
|
|
#include "windows.h"
|
|
|
|
+#include <errno.h> /* amalgamator: dontcache */
|
|
#ifdef __CYGWIN__
|
|
# include <sys/cygwin.h>
|
|
-# include <errno.h> /* amalgamator: dontcache */
|
|
+# include <sys/stat.h> /* amalgamator: dontcache */
|
|
+# include <unistd.h> /* amalgamator: dontcache */
|
|
+#elif defined(_WIN32)
|
|
+enum {
|
|
+ CCP_POSIX_TO_WIN_W = 1, /* from is char*, to is wchar_t* */
|
|
+ CCP_RELATIVE = 0x100 /* Request to keep path relative. */
|
|
+};
|
|
#endif
|
|
|
|
/*
|
|
@@ -26122,7 +26230,7 @@ SQLITE_API extern int sqlite3_open_file_
|
|
** functions are not available (e.g. those not using MSVC, Cygwin, etc).
|
|
*/
|
|
#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
|
- SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
|
|
+ SQLITE_THREADSAFE>0 && (defined(_MSC_VER) || defined(__MSVCRT__))
|
|
# define SQLITE_OS_WIN_THREADS 1
|
|
#else
|
|
# define SQLITE_OS_WIN_THREADS 0
|
|
@@ -26145,8 +26253,10 @@ SQLITE_API extern int sqlite3_open_file_
|
|
*/
|
|
struct sqlite3_mutex {
|
|
CRITICAL_SECTION mutex; /* Mutex controlling the lock */
|
|
+#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_API_ARMOR)
|
|
int id; /* Mutex type */
|
|
-#ifdef SQLITE_DEBUG
|
|
+#endif
|
|
+#if defined(SQLITE_DEBUG)
|
|
volatile int nRef; /* Number of enterances */
|
|
volatile DWORD owner; /* Thread holding this mutex */
|
|
volatile LONG trace; /* True to trace changes */
|
|
@@ -26163,6 +26273,8 @@ struct sqlite3_mutex {
|
|
#ifdef SQLITE_DEBUG
|
|
#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \
|
|
0L, (DWORD)0, 0 }
|
|
+#elif !defined(SQLITE_ENABLE_API_ARMOR)
|
|
+#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER }
|
|
#else
|
|
#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id }
|
|
#endif
|
|
@@ -26222,7 +26334,6 @@ static sqlite3_mutex winMutex_staticMute
|
|
};
|
|
|
|
static int winMutex_isInit = 0;
|
|
-static int winMutex_isNt = -1; /* <0 means "need to query" */
|
|
|
|
/* As the winMutexInit() and winMutexEnd() functions are called as part
|
|
** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
|
|
@@ -26230,7 +26341,9 @@ static int winMutex_isNt = -1; /* <0 mea
|
|
*/
|
|
static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0;
|
|
|
|
-SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */
|
|
+#if defined(__CYGWIN__) && defined(SQLITE_AMALGAMATION)
|
|
+static
|
|
+#endif
|
|
SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
|
|
|
|
static int winMutexInit(void){
|
|
@@ -26326,9 +26439,9 @@ static sqlite3_mutex *winMutexAlloc(int
|
|
case SQLITE_MUTEX_RECURSIVE: {
|
|
p = sqlite3MallocZero( sizeof(*p) );
|
|
if( p ){
|
|
+#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_API_ARMOR)
|
|
p->id = iType;
|
|
-#ifdef SQLITE_DEBUG
|
|
-#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC
|
|
+#if defined(SQLITE_DEBUG) && defined(SQLITE_WIN32_MUTEX_TRACE_DYNAMIC)
|
|
p->trace = 1;
|
|
#endif
|
|
#endif
|
|
@@ -26348,8 +26461,8 @@ static sqlite3_mutex *winMutexAlloc(int
|
|
}
|
|
#endif
|
|
p = &winMutex_staticMutexes[iType-2];
|
|
-#ifdef SQLITE_DEBUG
|
|
-#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
|
|
+#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_API_ARMOR)
|
|
+#if defined(SQLITE_DEBUG) && defined(SQLITE_WIN32_MUTEX_TRACE_DYNAMIC)
|
|
InterlockedCompareExchange(&p->trace, 1, 0);
|
|
#endif
|
|
#endif
|
|
@@ -26369,14 +26482,19 @@ static sqlite3_mutex *winMutexAlloc(int
|
|
static void winMutexFree(sqlite3_mutex *p){
|
|
assert( p );
|
|
assert( p->nRef==0 && p->owner==0 );
|
|
- if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){
|
|
+#if SQLITE_ENABLE_API_ARMOR
|
|
+ if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
|
|
+#endif
|
|
+ {
|
|
DeleteCriticalSection(&p->mutex);
|
|
sqlite3_free(p);
|
|
- }else{
|
|
+ }
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
+ else{
|
|
(void)SQLITE_MISUSE_BKPT;
|
|
-#endif
|
|
}
|
|
+#endif
|
|
+
|
|
}
|
|
|
|
/*
|
|
@@ -26424,30 +26542,15 @@ static int winMutexTry(sqlite3_mutex *p)
|
|
** The sqlite3_mutex_try() routine is very rarely used, and when it
|
|
** is used it is merely an optimization. So it is OK for it to always
|
|
** fail.
|
|
- **
|
|
- ** The TryEnterCriticalSection() interface is only available on WinNT.
|
|
- ** And some windows compilers complain if you try to use it without
|
|
- ** first doing some #defines that prevent SQLite from building on Win98.
|
|
- ** For that reason, we will omit this optimization for now. See
|
|
- ** ticket #2685.
|
|
*/
|
|
-#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
|
|
assert( winMutex_isInit==1 );
|
|
- assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
|
|
- if( winMutex_isNt<0 ){
|
|
- winMutex_isNt = sqlite3_win32_is_nt();
|
|
- }
|
|
- assert( winMutex_isNt==0 || winMutex_isNt==1 );
|
|
- if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
|
|
+ if( TryEnterCriticalSection(&p->mutex) ){
|
|
#ifdef SQLITE_DEBUG
|
|
p->owner = tid;
|
|
p->nRef++;
|
|
#endif
|
|
rc = SQLITE_OK;
|
|
}
|
|
-#else
|
|
- UNUSED_PARAMETER(p);
|
|
-#endif
|
|
#ifdef SQLITE_DEBUG
|
|
if( p->trace ){
|
|
OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
|
|
@@ -26548,7 +26651,7 @@ SQLITE_API int sqlite3_release_memory(in
|
|
*/
|
|
static SQLITE_WSD struct Mem0Global {
|
|
sqlite3_mutex *mutex; /* Mutex to serialize access */
|
|
- sqlite3_int64 alarmThreshold; /* The soft heap limit */
|
|
+ size_t alarmThreshold; /* The soft heap limit */
|
|
|
|
/*
|
|
** True if heap is nearly "full" where "full" is defined by the
|
|
@@ -26572,6 +26675,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3Mal
|
|
** that was invoked when memory usage grew too large. Now it is a
|
|
** no-op.
|
|
*/
|
|
+#undef sqlite3_memory_alarm
|
|
SQLITE_API int sqlite3_memory_alarm(
|
|
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
|
void *pArg,
|
|
@@ -26589,9 +26693,8 @@ SQLITE_API int sqlite3_memory_alarm(
|
|
** negative value indicates no limit.
|
|
*/
|
|
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
|
- sqlite3_int64 priorLimit;
|
|
- sqlite3_int64 excess;
|
|
- sqlite3_int64 nUsed;
|
|
+ size_t priorLimit;
|
|
+ size_t nUsed;
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
int rc = sqlite3_initialize();
|
|
if( rc ) return -1;
|
|
@@ -26604,23 +26707,27 @@ SQLITE_API sqlite3_int64 sqlite3_soft_he
|
|
}
|
|
mem0.alarmThreshold = n;
|
|
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
- mem0.nearlyFull = (n>0 && n<=nUsed);
|
|
+ mem0.nearlyFull = (n>0 && (size_t)n<=nUsed);
|
|
sqlite3_mutex_leave(mem0.mutex);
|
|
- excess = sqlite3_memory_used() - n;
|
|
- if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
|
|
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
+ if( sqlite3_memory_used() > n ) sqlite3PcacheReleaseMemory(sqlite3_memory_used() - n);
|
|
+#endif
|
|
return priorLimit;
|
|
}
|
|
+#ifndef SQLITE_OMIT_DEPRECATED
|
|
+#undef sqlite3_soft_heap_limit
|
|
SQLITE_API void sqlite3_soft_heap_limit(int n){
|
|
if( n<0 ) n = 0;
|
|
sqlite3_soft_heap_limit64(n);
|
|
}
|
|
+#endif
|
|
|
|
/*
|
|
** Initialize the memory allocation subsystem.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3MallocInit(void){
|
|
int rc;
|
|
- if( sqlite3GlobalConfig.m.xMalloc==0 ){
|
|
+ if( sqlite3GlobalConfig.m2.xMalloc==0 ){
|
|
sqlite3MemSetDefault();
|
|
}
|
|
memset(&mem0, 0, sizeof(mem0));
|
|
@@ -26630,7 +26737,7 @@ SQLITE_PRIVATE int sqlite3MallocInit(voi
|
|
sqlite3GlobalConfig.pPage = 0;
|
|
sqlite3GlobalConfig.szPage = 0;
|
|
}
|
|
- rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
|
|
+ rc = sqlite3GlobalConfig.m2.xInit(sqlite3GlobalConfig.m2.pAppData);
|
|
if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0));
|
|
return rc;
|
|
}
|
|
@@ -26648,8 +26755,8 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull
|
|
** Deinitialize the memory allocation subsystem.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3MallocEnd(void){
|
|
- if( sqlite3GlobalConfig.m.xShutdown ){
|
|
- sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData);
|
|
+ if( sqlite3GlobalConfig.m2.xShutdown ){
|
|
+ sqlite3GlobalConfig.m2.xShutdown(sqlite3GlobalConfig.m2.pAppData);
|
|
}
|
|
memset(&mem0, 0, sizeof(mem0));
|
|
}
|
|
@@ -26677,10 +26784,12 @@ SQLITE_API sqlite3_int64 sqlite3_memory_
|
|
/*
|
|
** Trigger the alarm
|
|
*/
|
|
-static void sqlite3MallocAlarm(int nByte){
|
|
+static void sqlite3MallocAlarm(size_t nByte){
|
|
if( mem0.alarmThreshold<=0 ) return;
|
|
sqlite3_mutex_leave(mem0.mutex);
|
|
- sqlite3_release_memory(nByte);
|
|
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
+ sqlite3PcacheReleaseMemory(nByte);
|
|
+#endif
|
|
sqlite3_mutex_enter(mem0.mutex);
|
|
}
|
|
|
|
@@ -26688,9 +26797,9 @@ static void sqlite3MallocAlarm(int nByte
|
|
** Do a memory allocation with statistics and alarms. Assume the
|
|
** lock is already held.
|
|
*/
|
|
-static void mallocWithAlarm(int n, void **pp){
|
|
+static void mallocWithAlarm(size_t n, void **pp){
|
|
void *p;
|
|
- int nFull;
|
|
+ size_t nFull;
|
|
assert( sqlite3_mutex_held(mem0.mutex) );
|
|
assert( n>0 );
|
|
|
|
@@ -26699,7 +26808,7 @@ static void mallocWithAlarm(int n, void
|
|
** mode and specifically when the DMD "Dark Matter Detector" is enabled
|
|
** or else a crash results. Hence, do not attempt to optimize out the
|
|
** following xRoundup() call. */
|
|
- nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
|
+ nFull = sqlite3GlobalConfig.m2.xRoundup(n);
|
|
|
|
#ifdef SQLITE_MAX_MEMORY
|
|
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nFull>SQLITE_MAX_MEMORY ){
|
|
@@ -26710,19 +26819,19 @@ static void mallocWithAlarm(int n, void
|
|
|
|
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
|
|
if( mem0.alarmThreshold>0 ){
|
|
- sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
- if( nUsed >= mem0.alarmThreshold - nFull ){
|
|
+ size_t nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
+ if( (nUsed + nFull >= mem0.alarmThreshold) || (nUsed + nFull < nUsed) ){
|
|
mem0.nearlyFull = 1;
|
|
sqlite3MallocAlarm(nFull);
|
|
}else{
|
|
mem0.nearlyFull = 0;
|
|
}
|
|
}
|
|
- p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
|
+ p = sqlite3GlobalConfig.m2.xMalloc(nFull);
|
|
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
if( p==0 && mem0.alarmThreshold>0 ){
|
|
sqlite3MallocAlarm(nFull);
|
|
- p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
|
+ p = sqlite3GlobalConfig.m2.xMalloc(nFull);
|
|
}
|
|
#endif
|
|
if( p ){
|
|
@@ -26737,9 +26846,9 @@ static void mallocWithAlarm(int n, void
|
|
** Allocate memory. This routine is like sqlite3_malloc() except that it
|
|
** assumes the memory subsystem has already been initialized.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3Malloc(u64 n){
|
|
+SQLITE_PRIVATE void *sqlite3Malloc(size_t n){
|
|
void *p;
|
|
- if( n==0 || n>=0x7fffff00 ){
|
|
+ if( n==0 ){
|
|
/* A memory allocation of a number of bytes which is near the maximum
|
|
** signed integer value might cause an integer overflow inside of the
|
|
** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
|
|
@@ -26748,10 +26857,10 @@ SQLITE_PRIVATE void *sqlite3Malloc(u64 n
|
|
p = 0;
|
|
}else if( sqlite3GlobalConfig.bMemstat ){
|
|
sqlite3_mutex_enter(mem0.mutex);
|
|
- mallocWithAlarm((int)n, &p);
|
|
+ mallocWithAlarm(n, &p);
|
|
sqlite3_mutex_leave(mem0.mutex);
|
|
}else{
|
|
- p = sqlite3GlobalConfig.m.xMalloc((int)n);
|
|
+ p = sqlite3GlobalConfig.m2.xMalloc(n);
|
|
}
|
|
assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */
|
|
return p;
|
|
@@ -26762,18 +26871,26 @@ SQLITE_PRIVATE void *sqlite3Malloc(u64 n
|
|
** First make sure the memory subsystem is initialized, then do the
|
|
** allocation.
|
|
*/
|
|
+#undef sqlite3_malloc
|
|
+#if !defined(SQLITE_OMIT_DEPRECATED) || SQLITE_PTRSIZE<8
|
|
SQLITE_API void *sqlite3_malloc(int n){
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
if( sqlite3_initialize() ) return 0;
|
|
#endif
|
|
- return n<=0 ? 0 : sqlite3Malloc(n);
|
|
+ return sqlite3Malloc((unsigned int)n);
|
|
}
|
|
+#endif
|
|
+#define sqlite3_malloc(x) sqlite3Malloc(x)
|
|
SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
if( sqlite3_initialize() ) return 0;
|
|
#endif
|
|
+#if SQLITE_PTRSIZE<8
|
|
+ if(n > (sqlite3_uint64)(size_t)-1) return 0;
|
|
+#endif
|
|
return sqlite3Malloc(n);
|
|
}
|
|
+#define sqlite3_malloc64(x) sqlite3Malloc(x)
|
|
|
|
/*
|
|
** TRUE if p is a lookaside memory allocation from db
|
|
@@ -26790,11 +26907,11 @@ static int isLookaside(sqlite3 *db, void
|
|
** Return the size of a memory allocation previously obtained from
|
|
** sqlite3Malloc() or sqlite3_malloc().
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3MallocSize(void *p){
|
|
+SQLITE_PRIVATE size_t sqlite3MallocSize(void *p){
|
|
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
|
- return sqlite3GlobalConfig.m.xSize(p);
|
|
+ return sqlite3GlobalConfig.m2.xSize(p);
|
|
}
|
|
-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
|
+SQLITE_PRIVATE size_t sqlite3DbMallocSize(sqlite3 *db, void *p){
|
|
assert( p!=0 );
|
|
if( db==0 || !isLookaside(db,p) ){
|
|
#ifdef SQLITE_DEBUG
|
|
@@ -26806,7 +26923,7 @@ SQLITE_PRIVATE int sqlite3DbMallocSize(s
|
|
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
|
}
|
|
#endif
|
|
- return sqlite3GlobalConfig.m.xSize(p);
|
|
+ return sqlite3GlobalConfig.m2.xSize(p);
|
|
}else{
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
return db->lookaside.sz;
|
|
@@ -26815,7 +26932,7 @@ SQLITE_PRIVATE int sqlite3DbMallocSize(s
|
|
SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){
|
|
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
|
|
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
|
- return p ? sqlite3GlobalConfig.m.xSize(p) : 0;
|
|
+ return p ? sqlite3GlobalConfig.m2.xSize(p) : 0;
|
|
}
|
|
|
|
/*
|
|
@@ -26829,10 +26946,10 @@ SQLITE_API void sqlite3_free(void *p){
|
|
sqlite3_mutex_enter(mem0.mutex);
|
|
sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p));
|
|
sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
+ sqlite3GlobalConfig.m2.xFree(p);
|
|
sqlite3_mutex_leave(mem0.mutex);
|
|
}else{
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
+ sqlite3GlobalConfig.m2.xFree(p);
|
|
}
|
|
}
|
|
|
|
@@ -26882,8 +26999,8 @@ SQLITE_PRIVATE void sqlite3DbFree(sqlite
|
|
/*
|
|
** Change the size of an existing memory allocation
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
|
|
- int nOld, nNew, nDiff;
|
|
+SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, size_t nBytes){
|
|
+ size_t nOld, nNew;
|
|
void *pNew;
|
|
assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
|
|
assert( sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) );
|
|
@@ -26894,37 +27011,33 @@ SQLITE_PRIVATE void *sqlite3Realloc(void
|
|
sqlite3_free(pOld); /* IMP: R-26507-47431 */
|
|
return 0;
|
|
}
|
|
- if( nBytes>=0x7fffff00 ){
|
|
- /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */
|
|
- return 0;
|
|
- }
|
|
nOld = sqlite3MallocSize(pOld);
|
|
/* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second
|
|
** argument to xRealloc is always a value returned by a prior call to
|
|
** xRoundup. */
|
|
- nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes);
|
|
+ nNew = sqlite3GlobalConfig.m2.xRoundup(nBytes);
|
|
if( nOld==nNew ){
|
|
pNew = pOld;
|
|
}else if( sqlite3GlobalConfig.bMemstat ){
|
|
sqlite3_mutex_enter(mem0.mutex);
|
|
- sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
|
|
- nDiff = nNew - nOld;
|
|
- if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
|
|
- mem0.alarmThreshold-nDiff ){
|
|
- sqlite3MallocAlarm(nDiff);
|
|
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, nBytes);
|
|
+ if( nNew > nOld && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew >=
|
|
+ mem0.alarmThreshold+nOld ){
|
|
+ sqlite3MallocAlarm(nNew - nOld);
|
|
}
|
|
- pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
+ pNew = sqlite3GlobalConfig.m2.xRealloc(pOld, nNew);
|
|
if( pNew==0 && mem0.alarmThreshold>0 ){
|
|
- sqlite3MallocAlarm((int)nBytes);
|
|
- pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
+ sqlite3MallocAlarm(nBytes);
|
|
+ pNew = sqlite3GlobalConfig.m2.xRealloc(pOld, nNew);
|
|
}
|
|
if( pNew ){
|
|
nNew = sqlite3MallocSize(pNew);
|
|
- sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
|
|
+ sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, nOld);
|
|
+ sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew);
|
|
}
|
|
sqlite3_mutex_leave(mem0.mutex);
|
|
}else{
|
|
- pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
+ pNew = sqlite3GlobalConfig.m2.xRealloc(pOld, nNew);
|
|
}
|
|
assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */
|
|
return pNew;
|
|
@@ -26934,28 +27047,35 @@ SQLITE_PRIVATE void *sqlite3Realloc(void
|
|
** The public interface to sqlite3Realloc. Make sure that the memory
|
|
** subsystem is initialized prior to invoking sqliteRealloc.
|
|
*/
|
|
+#undef sqlite3_realloc
|
|
+#if !defined(SQLITE_OMIT_DEPRECATED) || SQLITE_PTRSIZE<8
|
|
SQLITE_API void *sqlite3_realloc(void *pOld, int n){
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
if( sqlite3_initialize() ) return 0;
|
|
#endif
|
|
- if( n<0 ) n = 0; /* IMP: R-26507-47431 */
|
|
- return sqlite3Realloc(pOld, n);
|
|
+ return sqlite3Realloc(pOld, (unsigned int)n);
|
|
}
|
|
+#endif
|
|
+#define sqlite3_realloc(x,y) sqlite3Realloc(x,y)
|
|
SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
if( sqlite3_initialize() ) return 0;
|
|
#endif
|
|
+#if SQLITE_PTRSIZE<8
|
|
+ if(n > (sqlite3_uint64)(size_t)-1) n = 0;
|
|
+#endif
|
|
return sqlite3Realloc(pOld, n);
|
|
}
|
|
+#define sqlite3_realloc64(x,y) sqlite3Realloc(x,y)
|
|
|
|
|
|
/*
|
|
** Allocate and zero memory.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){
|
|
+SQLITE_PRIVATE void *sqlite3MallocZero(size_t n){
|
|
void *p = sqlite3Malloc(n);
|
|
if( p ){
|
|
- memset(p, 0, (size_t)n);
|
|
+ memset(p, 0, n);
|
|
}
|
|
return p;
|
|
}
|
|
@@ -26964,11 +27084,11 @@ SQLITE_PRIVATE void *sqlite3MallocZero(u
|
|
** Allocate and zero memory. If the allocation fails, make
|
|
** the mallocFailed flag in the connection pointer.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
|
|
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, size_t n){
|
|
void *p;
|
|
testcase( db==0 );
|
|
p = sqlite3DbMallocRaw(db, n);
|
|
- if( p ) memset(p, 0, (size_t)n);
|
|
+ if( p ) memset(p, 0, n);
|
|
return p;
|
|
}
|
|
|
|
@@ -26976,7 +27096,7 @@ SQLITE_PRIVATE void *sqlite3DbMallocZero
|
|
/* Finish the work of sqlite3DbMallocRawNN for the unusual and
|
|
** slower case when the allocation cannot be fulfilled using lookaside.
|
|
*/
|
|
-static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
|
|
+static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, size_t n){
|
|
void *p;
|
|
assert( db!=0 );
|
|
p = sqlite3Malloc(n);
|
|
@@ -27008,7 +27128,7 @@ static SQLITE_NOINLINE void *dbMallocRaw
|
|
** The sqlite3MallocRawNN() variant guarantees that the "db" parameter is
|
|
** not a NULL pointer.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
|
|
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, size_t n){
|
|
void *p;
|
|
if( db ) return sqlite3DbMallocRawNN(db, n);
|
|
p = sqlite3Malloc(n);
|
|
@@ -27051,20 +27171,20 @@ SQLITE_PRIVATE void *sqlite3DbMallocRawN
|
|
}
|
|
|
|
/* Forward declaration */
|
|
-static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n);
|
|
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, size_t n);
|
|
|
|
/*
|
|
** Resize the block of memory pointed to by p to n bytes. If the
|
|
** resize fails, set the mallocFailed flag in the connection object.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
|
|
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, size_t n){
|
|
assert( db!=0 );
|
|
if( p==0 ) return sqlite3DbMallocRawNN(db, n);
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
|
|
return dbReallocFinish(db, p, n);
|
|
}
|
|
-static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
|
|
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, size_t n){
|
|
void *pNew = 0;
|
|
assert( db!=0 );
|
|
assert( p!=0 );
|
|
@@ -27094,7 +27214,7 @@ static SQLITE_NOINLINE void *dbReallocFi
|
|
** Attempt to reallocate p. If the reallocation fails, then free p
|
|
** and set the mallocFailed flag in the database connection.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n){
|
|
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, size_t n){
|
|
void *pNew;
|
|
pNew = sqlite3DbRealloc(db, p, n);
|
|
if( !pNew ){
|
|
@@ -27123,7 +27243,7 @@ SQLITE_PRIVATE char *sqlite3DbStrDup(sql
|
|
}
|
|
return zNew;
|
|
}
|
|
-SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
|
|
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, size_t n){
|
|
char *zNew;
|
|
assert( db!=0 );
|
|
if( z==0 ){
|
|
@@ -27132,7 +27252,7 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sq
|
|
assert( (n&0x7fffffff)==n );
|
|
zNew = sqlite3DbMallocRawNN(db, n+1);
|
|
if( zNew ){
|
|
- memcpy(zNew, z, (size_t)n);
|
|
+ memcpy(zNew, z, n);
|
|
zNew[n] = 0;
|
|
}
|
|
return zNew;
|
|
@@ -27734,6 +27854,8 @@ SQLITE_API void sqlite3_str_vappendf(
|
|
case etGENERIC:
|
|
if( bArgList ){
|
|
realvalue = getDoubleArg(pArgList);
|
|
+ }else if( flag_long==2 ){
|
|
+ realvalue = va_arg(ap,long double);
|
|
}else{
|
|
realvalue = va_arg(ap,double);
|
|
}
|
|
@@ -27754,7 +27876,7 @@ SQLITE_API void sqlite3_str_vappendf(
|
|
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
|
exp = 0;
|
|
if( sqlite3IsNaN((double)realvalue) ){
|
|
- bufpt = "NaN";
|
|
+ bufpt = (char *)"NaN";
|
|
length = 3;
|
|
break;
|
|
}
|
|
@@ -27954,7 +28076,7 @@ SQLITE_API void sqlite3_str_vappendf(
|
|
bufpt = va_arg(ap,char*);
|
|
}
|
|
if( bufpt==0 ){
|
|
- bufpt = "";
|
|
+ bufpt = (char *)"";
|
|
}else if( xtype==etDYNSTRING ){
|
|
if( pAccum->nChar==0
|
|
&& pAccum->mxAlloc
|
|
@@ -28004,7 +28126,7 @@ SQLITE_API void sqlite3_str_vappendf(
|
|
int needQuote;
|
|
char ch;
|
|
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
|
|
- char *escarg;
|
|
+ const char *escarg;
|
|
|
|
if( bArgList ){
|
|
escarg = getTextArg(pArgList);
|
|
@@ -28110,7 +28232,7 @@ SQLITE_API void sqlite3_str_vappendf(
|
|
** Return the number of bytes of text that StrAccum is able to accept
|
|
** after the attempted enlargement. The value returned might be zero.
|
|
*/
|
|
-static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
|
+static int sqlite3StrAccumEnlarge(StrAccum *p, size_t N){
|
|
char *zNew;
|
|
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
|
|
if( p->accError ){
|
|
@@ -28123,7 +28245,7 @@ static int sqlite3StrAccumEnlarge(StrAcc
|
|
return p->nAlloc - p->nChar - 1;
|
|
}else{
|
|
char *zOld = isMalloced(p) ? p->zText : 0;
|
|
- i64 szNew = p->nChar;
|
|
+ size_t szNew = p->nChar;
|
|
szNew += N + 1;
|
|
if( szNew+p->nChar<=p->mxAlloc ){
|
|
/* Force exponential buffer size growth as long as it does not overflow,
|
|
@@ -28135,12 +28257,12 @@ static int sqlite3StrAccumEnlarge(StrAcc
|
|
setStrAccumError(p, SQLITE_TOOBIG);
|
|
return 0;
|
|
}else{
|
|
- p->nAlloc = (int)szNew;
|
|
+ p->nAlloc = szNew;
|
|
}
|
|
if( p->db ){
|
|
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
|
}else{
|
|
- zNew = sqlite3_realloc64(zOld, p->nAlloc);
|
|
+ zNew = sqlite3Realloc(zOld, p->nAlloc);
|
|
}
|
|
if( zNew ){
|
|
assert( p->zText!=0 || p->nChar==0 );
|
|
@@ -28162,7 +28284,7 @@ static int sqlite3StrAccumEnlarge(StrAcc
|
|
*/
|
|
SQLITE_API void sqlite3_str_appendchar(sqlite3_str *p, int N, char c){
|
|
testcase( p->nChar + (i64)N > 0x7fffffff );
|
|
- if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
|
|
+ if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
|
|
return;
|
|
}
|
|
while( (N--)>0 ) p->zText[p->nChar++] = c;
|
|
@@ -28176,7 +28298,7 @@ SQLITE_API void sqlite3_str_appendchar(s
|
|
** work (enlarging the buffer) using tail recursion, so that the
|
|
** sqlite3_str_append() routine can use fast calling semantics.
|
|
*/
|
|
-static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
|
+static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, size_t N){
|
|
N = sqlite3StrAccumEnlarge(p, N);
|
|
if( N>0 ){
|
|
memcpy(&p->zText[p->nChar], z, N);
|
|
@@ -28191,7 +28313,6 @@ static void SQLITE_NOINLINE enlargeAndAp
|
|
SQLITE_API void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
|
|
assert( z!=0 || N==0 );
|
|
assert( p->zText!=0 || p->nChar==0 || p->accError );
|
|
- assert( N>=0 );
|
|
assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 );
|
|
if( p->nChar+N >= p->nAlloc ){
|
|
enlargeAndAppend(p,z,N);
|
|
@@ -28305,7 +28426,7 @@ SQLITE_API void sqlite3_str_reset(StrAcc
|
|
** mx: Maximum number of bytes to accumulate. If mx==0 then no memory
|
|
** allocations will ever occur.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
|
|
+SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, size_t n, size_t mx){
|
|
p->zText = zBase;
|
|
p->db = db;
|
|
p->nAlloc = n;
|
|
@@ -28542,14 +28663,14 @@ SQLITE_API void sqlite3_str_appendf(StrA
|
|
*/
|
|
static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
|
|
if( p==0 ){
|
|
- p = sqlite3_malloc64( sizeof(*p) );
|
|
+ p = sqlite3Malloc( sizeof(*p) );
|
|
if( p==0 ) return 0;
|
|
memset(p, 0, sizeof(*p));
|
|
}else{
|
|
p->iLevel++;
|
|
}
|
|
assert( moreToFollow==0 || moreToFollow==1 );
|
|
- if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
|
|
+ if( (unsigned)p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
|
|
return p;
|
|
}
|
|
|
|
@@ -28573,7 +28694,7 @@ static void sqlite3TreeViewLine(TreeView
|
|
char zBuf[500];
|
|
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
|
if( p ){
|
|
- for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
|
|
+ for(i=0; i<p->iLevel && (unsigned)i<sizeof(p->bLine)-1; i++){
|
|
sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4);
|
|
}
|
|
sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
|
@@ -28601,7 +28722,7 @@ static void sqlite3TreeViewItem(TreeView
|
|
/*
|
|
** Generate a human-readable description of a WITH clause.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
|
|
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith){
|
|
int i;
|
|
if( pWith==0 ) return;
|
|
if( pWith->nCte==0 ) return;
|
|
@@ -28687,7 +28808,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelec
|
|
}
|
|
pView = sqlite3TreeViewPush(pView, moreToFollow);
|
|
if( p->pWith ){
|
|
- sqlite3TreeViewWith(pView, p->pWith, 1);
|
|
+ sqlite3TreeViewWith(pView, p->pWith);
|
|
cnt = 1;
|
|
sqlite3TreeViewPush(pView, 1);
|
|
}
|
|
@@ -29416,7 +29537,8 @@ SQLITE_PRIVATE void sqlite3PrngRestoreSt
|
|
#if SQLITE_MAX_WORKER_THREADS>0
|
|
|
|
/********************************* Unix Pthreads ****************************/
|
|
-#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
|
+#if (SQLITE_OS_UNIX || defined(__CYGWIN__)) && \
|
|
+ defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
|
|
|
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
|
/* #include <pthread.h> */
|
|
@@ -29483,7 +29605,8 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQL
|
|
return rc;
|
|
}
|
|
|
|
-#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
|
|
+#endif /* (SQLITE_OS_UNIX || defined(__CYGWIN__)) && \
|
|
+ defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 */
|
|
/******************************** End Unix Pthreads *************************/
|
|
|
|
|
|
@@ -29566,7 +29689,6 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HA
|
|
/* Get the results of the thread */
|
|
SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
|
DWORD rc;
|
|
- BOOL bRc;
|
|
|
|
assert( ppOut!=0 );
|
|
if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
|
|
@@ -29578,8 +29700,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQL
|
|
assert( p->id!=0 && p->id!=GetCurrentThreadId() );
|
|
rc = sqlite3Win32Wait((HANDLE)p->tid);
|
|
assert( rc!=WAIT_IO_COMPLETION );
|
|
- bRc = CloseHandle((HANDLE)p->tid);
|
|
- assert( bRc );
|
|
+ CloseHandle((HANDLE)p->tid);
|
|
}
|
|
if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
|
|
sqlite3_free(p);
|
|
@@ -29860,7 +29981,7 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
|
|
** encoding, or if *pMem does not contain a string value.
|
|
*/
|
|
SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
|
|
- sqlite3_int64 len; /* Maximum length of output string in bytes */
|
|
+ size_t len; /* Maximum length of output string in bytes */
|
|
unsigned char *zOut; /* Output buffer */
|
|
unsigned char *zIn; /* Input iterator */
|
|
unsigned char *zTerm; /* End of input */
|
|
@@ -29871,7 +29992,6 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlit
|
|
assert( pMem->flags&MEM_Str );
|
|
assert( pMem->enc!=desiredEnc );
|
|
assert( pMem->enc!=0 );
|
|
- assert( pMem->n>=0 );
|
|
|
|
#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
|
|
{
|
|
@@ -30008,7 +30128,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemHandleB
|
|
int rc = SQLITE_OK;
|
|
u8 bom = 0;
|
|
|
|
- assert( pMem->n>=0 );
|
|
if( pMem->n>1 ){
|
|
u8 b1 = *(u8 *)pMem->z;
|
|
u8 b2 = *(((u8 *)pMem->z) + 1);
|
|
@@ -30307,9 +30426,14 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x
|
|
** than the actual length of the string. For very long strings (greater
|
|
** than 1GiB) the value returned might be less than the true string length.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
|
|
+SQLITE_PRIVATE size_t sqlite3Strlen30(const char *z){
|
|
+ size_t result;
|
|
if( z==0 ) return 0;
|
|
- return 0x3fffffff & (int)strlen(z);
|
|
+ result = strlen(z);
|
|
+ if( result > SQLITE_MAX_LENGTH ) {
|
|
+ result &= 0x3fffffff;
|
|
+ }
|
|
+ return result;
|
|
}
|
|
|
|
/*
|
|
@@ -30319,7 +30443,7 @@ SQLITE_PRIVATE int sqlite3Strlen30(const
|
|
** The column type is an extra string stored after the zero-terminator on
|
|
** the column name if and only if the COLFLAG_HASTYPE flag is set.
|
|
*/
|
|
-SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
|
|
+SQLITE_PRIVATE const char *sqlite3ColumnType(Column *pCol, const char *zDflt){
|
|
if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt;
|
|
return pCol->zName + strlen(pCol->zName) + 1;
|
|
}
|
|
@@ -31951,7 +32075,7 @@ static void insertElement(
|
|
** if the new size is the same as the prior size.
|
|
** Return TRUE if the resize occurs and false if not.
|
|
*/
|
|
-static int rehash(Hash *pH, unsigned int new_size){
|
|
+static int rehash(Hash *pH, size_t new_size){
|
|
struct _ht *new_ht; /* The new hash table */
|
|
HashElem *elem, *next_elem; /* For looping over existing elements */
|
|
|
|
@@ -32411,7 +32535,7 @@ SQLITE_PRIVATE const char *sqlite3Opcode
|
|
# include <sys/mman.h>
|
|
#endif
|
|
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE
|
|
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__CYGWIN__) || defined(__GNU__) || defined(HAVE_FLOCK)
|
|
/* # include <sys/ioctl.h> */
|
|
# include <sys/file.h>
|
|
# include <sys/param.h>
|
|
@@ -32471,7 +32595,7 @@ SQLITE_PRIVATE const char *sqlite3Opcode
|
|
/*
|
|
** Maximum supported path-length.
|
|
*/
|
|
-#define MAX_PATHNAME 512
|
|
+#define MAX_PATHNAME SQLITE_MAX_PATH_LENGTH
|
|
|
|
/*
|
|
** Maximum supported symbolic links
|
|
@@ -32823,7 +32947,7 @@ SQLITE_API extern int sqlite3_open_file_
|
|
** HAVE_MREMAP defaults to true on Linux and false everywhere else.
|
|
*/
|
|
#if !defined(HAVE_MREMAP)
|
|
-# if defined(__linux__) && defined(_GNU_SOURCE)
|
|
+# if (defined(__linux__) || defined(__ANDROID__)) && defined(_GNU_SOURCE)
|
|
# define HAVE_MREMAP 1
|
|
# else
|
|
# define HAVE_MREMAP 0
|
|
@@ -32874,6 +32998,7 @@ static int unixGetpagesize(void);
|
|
** testing and sandboxing. The following array holds the names and pointers
|
|
** to all overrideable system calls.
|
|
*/
|
|
+#define aSyscall aUnixSyscall
|
|
static struct unix_syscall {
|
|
const char *zName; /* Name of the system call */
|
|
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
|
@@ -33462,7 +33587,7 @@ static struct vxworksFileId *vxworksFind
|
|
|
|
assert( zAbsoluteName[0]=='/' );
|
|
n = (int)strlen(zAbsoluteName);
|
|
- pNew = sqlite3_malloc64( sizeof(*pNew) + (n+1) );
|
|
+ pNew = sqlite3Malloc( sizeof(*pNew) + (n+1) );
|
|
if( pNew==0 ) return 0;
|
|
pNew->zCanonicalName = (char*)&pNew[1];
|
|
memcpy(pNew->zCanonicalName, zAbsoluteName, n+1);
|
|
@@ -33717,7 +33842,7 @@ static int unixLogErrorAtLine(
|
|
const char *zPath, /* File path associated with error */
|
|
int iLine /* Source line number where error occurred */
|
|
){
|
|
- char *zErr; /* Message from strerror() or equivalent */
|
|
+ const char *zErr; /* Message from strerror() or equivalent */
|
|
int iErrno = errno; /* Saved syscall error number */
|
|
|
|
/* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use
|
|
@@ -33913,7 +34038,7 @@ static int findInodeInfo(
|
|
pInode = pInode->pNext;
|
|
}
|
|
if( pInode==0 ){
|
|
- pInode = sqlite3_malloc64( sizeof(*pInode) );
|
|
+ pInode = sqlite3Malloc( sizeof(*pInode) );
|
|
if( pInode==0 ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -34881,7 +35006,7 @@ static int dotlockClose(sqlite3_file *id
|
|
**
|
|
** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off
|
|
*/
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE
|
|
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__CYGWIN__) || defined(__GNU__) || defined(HAVE_FLOCK)
|
|
|
|
/*
|
|
** Retry flock() calls that fail with EINTR
|
|
@@ -36019,9 +36144,13 @@ static int unixWrite(
|
|
** Count the number of fullsyncs and normal syncs. This is used to test
|
|
** that syncs and fullsyncs are occurring at the right times.
|
|
*/
|
|
+#if SQLITE_OS_WIN
|
|
+extern int sqlite3_sync_count, sqlite3_fullsync_count;
|
|
+#else
|
|
SQLITE_API int sqlite3_sync_count = 0;
|
|
SQLITE_API int sqlite3_fullsync_count = 0;
|
|
#endif
|
|
+#endif
|
|
|
|
/*
|
|
** We do not trust systems to provide a working fdatasync(). Some do.
|
|
@@ -36475,7 +36604,7 @@ static int unixFileControl(sqlite3_file
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_FCNTL_TEMPFILENAME: {
|
|
- char *zTFile = sqlite3_malloc64( pFile->pVfs->mxPathname );
|
|
+ char *zTFile = sqlite3Malloc( pFile->pVfs->mxPathname );
|
|
if( zTFile ){
|
|
unixGetTempname(pFile->pVfs->mxPathname, zTFile);
|
|
*(char**)pArg = zTFile;
|
|
@@ -37009,7 +37138,7 @@ static int unixOpenSharedMemory(unixFile
|
|
int nShmFilename; /* Size of the SHM filename in bytes */
|
|
|
|
/* Allocate space for the new unixShm object. */
|
|
- p = sqlite3_malloc64( sizeof(*p) );
|
|
+ p = sqlite3Malloc( sizeof(*p) );
|
|
if( p==0 ) return SQLITE_NOMEM_BKPT;
|
|
memset(p, 0, sizeof(*p));
|
|
assert( pDbFd->pShm==0 );
|
|
@@ -37041,7 +37170,7 @@ static int unixOpenSharedMemory(unixFile
|
|
#else
|
|
nShmFilename = 6 + (int)strlen(zBasePath);
|
|
#endif
|
|
- pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename );
|
|
+ pShmNode = sqlite3Malloc( sizeof(*pShmNode) + nShmFilename );
|
|
if( pShmNode==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto shm_open_err;
|
|
@@ -37249,7 +37378,7 @@ static int unixShmMap(
|
|
goto shmpage_out;
|
|
}
|
|
}else{
|
|
- pMem = sqlite3_malloc64(nMap);
|
|
+ pMem = sqlite3Malloc(nMap);
|
|
if( pMem==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto shmpage_out;
|
|
@@ -37789,7 +37918,7 @@ IOMETHODS(
|
|
0 /* xShmMap method */
|
|
)
|
|
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE
|
|
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__CYGWIN__) || defined(__GNU__) || defined(HAVE_FLOCK)
|
|
IOMETHODS(
|
|
flockIoFinder, /* Finder function name */
|
|
flockIoMethods, /* sqlite3_io_methods object name */
|
|
@@ -37828,6 +37957,19 @@ IOMETHODS(
|
|
)
|
|
#endif
|
|
|
|
+#if defined(__CYGWIN__)
|
|
+IOMETHODS(
|
|
+ cygwinIoFinder, /* Finder function name */
|
|
+ cygwinIoMethods, /* sqlite3_io_methods object name */
|
|
+ 1, /* shared memory is disabled */
|
|
+ flockClose, /* xClose method */
|
|
+ flockLock, /* xLock method */
|
|
+ flockUnlock, /* xUnlock method */
|
|
+ flockCheckReservedLock, /* xCheckReservedLock method */
|
|
+ 0 /* xShmMap method */
|
|
+)
|
|
+#endif
|
|
+
|
|
/*
|
|
** The proxy locking method is a "super-method" in the sense that it
|
|
** opens secondary file descriptors for the conch and lock files and
|
|
@@ -38076,7 +38218,7 @@ static int fillInUnixFile(
|
|
** the afpLockingContext.
|
|
*/
|
|
afpLockingContext *pCtx;
|
|
- pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) );
|
|
+ pNew->lockingContext = pCtx = sqlite3Malloc( sizeof(*pCtx) );
|
|
if( pCtx==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
@@ -38106,7 +38248,7 @@ static int fillInUnixFile(
|
|
int nFilename;
|
|
assert( zFilename!=0 );
|
|
nFilename = (int)strlen(zFilename) + 6;
|
|
- zLockFile = (char *)sqlite3_malloc64(nFilename);
|
|
+ zLockFile = (char *)sqlite3Malloc(nFilename);
|
|
if( zLockFile==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
@@ -38138,6 +38280,15 @@ static int fillInUnixFile(
|
|
unixLeaveMutex();
|
|
}
|
|
#endif
|
|
+
|
|
+#if defined(__CYGWIN__)
|
|
+ else if( pLockingStyle == &cygwinIoMethods ){
|
|
+ if ((osFcntl(h, F_LCK_MANDATORY, 1) != 0) && (errno != EINVAL)) {
|
|
+ /* The API exists but it refused to enable mandatory locking! */
|
|
+ rc = SQLITE_IOERR_ACCESS;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
|
|
storeLastErrno(pNew, 0);
|
|
#if OS_VXWORKS
|
|
@@ -38166,6 +38317,8 @@ static const char *unixTempFileDir(void)
|
|
static const char *azDirs[] = {
|
|
0,
|
|
0,
|
|
+ 0,
|
|
+ 0,
|
|
"/var/tmp",
|
|
"/usr/tmp",
|
|
"/tmp",
|
|
@@ -38177,6 +38330,8 @@ static const char *unixTempFileDir(void)
|
|
|
|
if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
|
|
if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
|
+ if( !azDirs[3] ) azDirs[2] = getenv("TMP");
|
|
+ if( !azDirs[4] ) azDirs[3] = getenv("TEMP");
|
|
while(1){
|
|
if( zDir!=0
|
|
&& osStat(zDir, &buf)==0
|
|
@@ -38497,7 +38652,7 @@ static int unixOpen(
|
|
if( pUnused ){
|
|
fd = pUnused->fd;
|
|
}else{
|
|
- pUnused = sqlite3_malloc64(sizeof(*pUnused));
|
|
+ pUnused = sqlite3Malloc(sizeof(*pUnused));
|
|
if( !pUnused ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -38824,7 +38979,7 @@ static int unixFullPathname(
|
|
|
|
if( bLink ){
|
|
if( zDel==0 ){
|
|
- zDel = sqlite3_malloc(nOut);
|
|
+ zDel = sqlite3Malloc(nOut);
|
|
if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
|
|
}else if( ++nLink>SQLITE_MAX_SYMLINKS ){
|
|
rc = SQLITE_CANTOPEN_BKPT;
|
|
@@ -38875,7 +39030,7 @@ static int unixFullPathname(
|
|
#include <dlfcn.h>
|
|
static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){
|
|
UNUSED_PARAMETER(NotUsed);
|
|
- return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL);
|
|
+ return dlopen(zFilename, RTLD_NOW | RTLD_LOCAL);
|
|
}
|
|
|
|
/*
|
|
@@ -39006,7 +39161,11 @@ static int unixSleep(sqlite3_vfs *NotUse
|
|
** sqlite3OsCurrentTime() during testing.
|
|
*/
|
|
#ifdef SQLITE_TEST
|
|
+# if SQLITE_OS_WIN
|
|
+SQLITE_API extern int sqlite3_current_time; /* Fake system time in seconds since 1970. */
|
|
+# else
|
|
SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
|
+# endif
|
|
#endif
|
|
|
|
/*
|
|
@@ -39362,7 +39521,7 @@ static int proxyCreateUnixFile(
|
|
if( pUnused ){
|
|
fd = pUnused->fd;
|
|
}else{
|
|
- pUnused = sqlite3_malloc64(sizeof(*pUnused));
|
|
+ pUnused = sqlite3Malloc(sizeof(*pUnused));
|
|
if( !pUnused ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -39395,7 +39554,7 @@ static int proxyCreateUnixFile(
|
|
}
|
|
}
|
|
|
|
- pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew));
|
|
+ pNew = (unixFile *)sqlite3Malloc(sizeof(*pNew));
|
|
if( pNew==NULL ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto end_create_proxy;
|
|
@@ -39873,7 +40032,7 @@ static int proxyCreateConchPathname(char
|
|
|
|
/* Allocate space for the conch filename and initialize the name to
|
|
** the name of the original database file. */
|
|
- *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8);
|
|
+ *pConchPath = conchPath = (char *)sqlite3Malloc(len + 8);
|
|
if( conchPath==0 ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -39989,7 +40148,7 @@ static int proxyTransformUnixFile(unixFi
|
|
OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h,
|
|
(lockPath ? lockPath : ":auto:"), osGetpid(0)));
|
|
|
|
- pCtx = sqlite3_malloc64( sizeof(*pCtx) );
|
|
+ pCtx = sqlite3Malloc( sizeof(*pCtx) );
|
|
if( pCtx==0 ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -40274,7 +40433,14 @@ static int proxyClose(sqlite3_file *id)
|
|
** necessarily been initialized when this routine is called, and so they
|
|
** should not be used.
|
|
*/
|
|
+#if SQLITE_OS_WIN
|
|
+#if defined(SQLITE_AMALGAMATION)
|
|
+static
|
|
+#endif
|
|
+SQLITE_API int sqlite3_os_unix_init(void){
|
|
+#else
|
|
SQLITE_API int sqlite3_os_init(void){
|
|
+#endif
|
|
/*
|
|
** The following macro defines an initializer for an sqlite3_vfs object.
|
|
** The name of the VFS is NAME. The pAppData is a pointer to a pointer
|
|
@@ -40330,21 +40496,32 @@ SQLITE_API int sqlite3_os_init(void){
|
|
static sqlite3_vfs aVfs[] = {
|
|
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
|
UNIXVFS("unix", autolockIoFinder ),
|
|
+#elif defined(__CYGWIN__)
|
|
+ UNIXVFS("unix", cygwinIoFinder ),
|
|
#elif OS_VXWORKS
|
|
UNIXVFS("unix", vxworksIoFinder ),
|
|
+#elif defined(__GNU__) || defined(HAVE_FLOCK)
|
|
+ UNIXVFS("unix", flockIoFinder ),
|
|
#else
|
|
UNIXVFS("unix", posixIoFinder ),
|
|
#endif
|
|
+#if !SQLITE_OS_WIN
|
|
+ UNIXVFS("none", nolockIoFinder ),
|
|
+#endif
|
|
UNIXVFS("unix-none", nolockIoFinder ),
|
|
UNIXVFS("unix-dotfile", dotlockIoFinder ),
|
|
+#if defined(__CYGWIN__)
|
|
+ UNIXVFS("unix-excl", cygwinIoFinder ),
|
|
+#else
|
|
UNIXVFS("unix-excl", posixIoFinder ),
|
|
+#endif
|
|
#if OS_VXWORKS
|
|
UNIXVFS("unix-namedsem", semIoFinder ),
|
|
#endif
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
|
|
+#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS || defined(__CYGWIN__) || defined(__GNU__)
|
|
UNIXVFS("unix-posix", posixIoFinder ),
|
|
#endif
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE
|
|
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__CYGWIN__) || defined(__GNU__) || defined(HAVE_FLOCK)
|
|
UNIXVFS("unix-flock", flockIoFinder ),
|
|
#endif
|
|
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
|
@@ -40367,6 +40544,8 @@ SQLITE_API int sqlite3_os_init(void){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
+#undef aSyscall
|
|
+#if !SQLITE_OS_WIN
|
|
/*
|
|
** Shutdown the operating system interface.
|
|
**
|
|
@@ -40378,6 +40557,7 @@ SQLITE_API int sqlite3_os_end(void){
|
|
unixBigLock = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
+#endif /* !SQLITE_OS_WIN */
|
|
|
|
#endif /* SQLITE_OS_UNIX */
|
|
|
|
@@ -40608,6 +40788,13 @@ SQLITE_API extern int sqlite3_open_file_
|
|
*/
|
|
/* #include "os_win.h" */
|
|
|
|
+struct cygwinstat {
|
|
+ i64 st_dev;
|
|
+ i64 st_ino;
|
|
+ i64 st_mode;
|
|
+ char dummy[40 + 8*SQLITE_PTRSIZE];
|
|
+};
|
|
+
|
|
/*
|
|
** Compiling and using WAL mode requires several APIs that are only
|
|
** available in Windows platforms based on the NT kernel.
|
|
@@ -40626,7 +40813,7 @@ SQLITE_API extern int sqlite3_open_file_
|
|
** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
|
|
** based on the sub-platform)?
|
|
*/
|
|
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
|
|
+#if 0
|
|
# define SQLITE_WIN32_HAS_ANSI
|
|
#endif
|
|
|
|
@@ -40724,7 +40911,11 @@ SQLITE_API extern int sqlite3_open_file_
|
|
** 4-bytes-per-character for UTF8.
|
|
*/
|
|
#ifndef SQLITE_WIN32_MAX_PATH_BYTES
|
|
+# if defined(SQLITE_MAX_PATH_LENGTH) && SQLITE_MAX_PATH_LENGTH > (SQLITE_WIN32_MAX_PATH_CHARS*4)
|
|
+# define SQLITE_WIN32_MAX_PATH_BYTES SQLITE_MAX_PATH_LENGTH
|
|
+# else
|
|
# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4)
|
|
+# endif
|
|
#endif
|
|
|
|
/*
|
|
@@ -41038,15 +41229,15 @@ static struct winMemData win_mem_data =
|
|
#define winMemGetHeap() win_mem_data.hHeap
|
|
#define winMemGetOwned() win_mem_data.bOwned
|
|
|
|
-static void *winMemMalloc(int nBytes);
|
|
+static void *winMemMalloc(size_t nBytes);
|
|
static void winMemFree(void *pPrior);
|
|
-static void *winMemRealloc(void *pPrior, int nBytes);
|
|
-static int winMemSize(void *p);
|
|
-static int winMemRoundup(int n);
|
|
+static void *winMemRealloc(void *pPrior, size_t nBytes);
|
|
+static size_t winMemSize(void *p);
|
|
+static size_t winMemRoundup(size_t n);
|
|
static int winMemInit(void *pAppData);
|
|
static void winMemShutdown(void *pAppData);
|
|
|
|
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void);
|
|
+SQLITE_PRIVATE const sqlite3_mem_methods2 *sqlite3MemGetWin32(void);
|
|
#endif /* SQLITE_WIN32_MALLOC */
|
|
|
|
/*
|
|
@@ -41063,8 +41254,6 @@ SQLITE_PRIVATE const sqlite3_mem_methods
|
|
*/
|
|
#ifdef SQLITE_TEST
|
|
SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
|
|
-#else
|
|
-static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
|
|
#endif
|
|
|
|
#ifndef SYSCALL
|
|
@@ -41159,7 +41348,7 @@ static struct win_syscall {
|
|
#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
|
|
DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
|
|
|
|
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
|
+#if SQLITE_OS_WINCE
|
|
{ "CreateMutexW", (SYSCALL)CreateMutexW, 0 },
|
|
#else
|
|
{ "CreateMutexW", (SYSCALL)0, 0 },
|
|
@@ -41309,7 +41498,7 @@ static struct win_syscall {
|
|
|
|
#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
|
|
|
|
-#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) || defined(_WIN32)
|
|
#if SQLITE_OS_WINCE
|
|
/* The GetProcAddressA() routine is only available on Windows CE. */
|
|
{ "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 },
|
|
@@ -41467,7 +41656,7 @@ static struct win_syscall {
|
|
|
|
#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
|
|
|
|
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
|
+#ifdef SQLITE_WIN32_HAS_ANSI
|
|
{ "LockFile", (SYSCALL)LockFile, 0 },
|
|
#else
|
|
{ "LockFile", (SYSCALL)0, 0 },
|
|
@@ -41535,12 +41724,16 @@ static struct win_syscall {
|
|
|
|
#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
|
|
|
|
+#if SQLITE_OS_WINCE
|
|
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
|
|
+#else
|
|
+ { "SystemTimeToFileTime", (SYSCALL)0, 0 },
|
|
+#endif
|
|
|
|
#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
|
|
LPFILETIME))aSyscall[56].pCurrent)
|
|
|
|
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
|
+#ifdef SQLITE_WIN32_HAS_ANSI
|
|
{ "UnlockFile", (SYSCALL)UnlockFile, 0 },
|
|
#else
|
|
{ "UnlockFile", (SYSCALL)0, 0 },
|
|
@@ -41587,7 +41780,7 @@ static struct win_syscall {
|
|
#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
|
|
DWORD,DWORD))aSyscall[62].pCurrent)
|
|
|
|
-#if !SQLITE_OS_WINRT
|
|
+#if SQLITE_OS_WINCE
|
|
{ "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
|
|
#else
|
|
{ "WaitForSingleObject", (SYSCALL)0, 0 },
|
|
@@ -41701,6 +41894,7 @@ static struct win_syscall {
|
|
** is really just a macro that uses a compiler intrinsic (e.g. x64).
|
|
** So do not try to make this is into a redefinable interface.
|
|
*/
|
|
+#if 0
|
|
#if defined(InterlockedCompareExchange)
|
|
{ "InterlockedCompareExchange", (SYSCALL)0, 0 },
|
|
|
|
@@ -41711,6 +41905,15 @@ static struct win_syscall {
|
|
#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \
|
|
SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent)
|
|
#endif /* defined(InterlockedCompareExchange) */
|
|
+#endif /* 0 */
|
|
+
|
|
+#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32)
|
|
+ { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 },
|
|
+#else
|
|
+ { "GetModuleHandleW", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[76].pCurrent)
|
|
|
|
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
|
|
{ "UuidCreate", (SYSCALL)UuidCreate, 0 },
|
|
@@ -41738,6 +41941,68 @@ static struct win_syscall {
|
|
#define osFlushViewOfFile \
|
|
((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)
|
|
|
|
+#if defined(SQLITE_WIN32_HAS_WIDE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
+ { "SetDllDirectoryW", (SYSCALL)SetDllDirectoryW, 0 },
|
|
+#else
|
|
+ { "SetDllDirectoryW", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define osSetDllDirectoryW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[80].pCurrent)
|
|
+
|
|
+#if defined(__CYGWIN__)
|
|
+ { "getenv", (SYSCALL)getenv, 0 },
|
|
+#else
|
|
+ { "getenv", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define osGetenv ((const char *(*)(const char *))aSyscall[81].pCurrent)
|
|
+
|
|
+#if defined(__CYGWIN__)
|
|
+ { "getcwd", (SYSCALL)getcwd, 0 },
|
|
+#else
|
|
+ { "getcwd", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define getcwd ((char*(*)(char*,size_t))aSyscall[82].pCurrent)
|
|
+
|
|
+#if defined(__CYGWIN__)
|
|
+ { "readlink", (SYSCALL)readlink, 0 },
|
|
+#else
|
|
+ { "readlink", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define readlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[83].pCurrent)
|
|
+
|
|
+#if defined(__CYGWIN__)
|
|
+ { "lstat", (SYSCALL)lstat, 0 },
|
|
+#else
|
|
+ { "lstat", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define lstat ((int(*)(const char*,struct cygwinstat*))aSyscall[84].pCurrent)
|
|
+
|
|
+#if defined(__CYGWIN__)
|
|
+ { "__errno", (SYSCALL)__errno, 0 },
|
|
+#else
|
|
+ { "__errno", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define osErrno (*((int*(*)(void))aSyscall[85].pCurrent)())
|
|
+
|
|
+#if defined(__CYGWIN__) && defined(SQLITE_WIN32_HAS_WIDE)
|
|
+ { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 },
|
|
+#else
|
|
+ { "cygwin_conv_path", (SYSCALL)0, 0 },
|
|
+#endif
|
|
+
|
|
+#define cygwin_conv_path ((size_t(*)(unsigned int, \
|
|
+ const void *, void *, size_t))aSyscall[86].pCurrent)
|
|
+
|
|
+ { "cygwin_conv_to_full_win32_path", (SYSCALL)0, 0 },
|
|
+
|
|
+#define cygwin_conv_to_full_win32_path ((void(*)(const char *, \
|
|
+ char *))aSyscall[87].pCurrent)
|
|
+
|
|
}; /* End of the overrideable system calls */
|
|
|
|
/*
|
|
@@ -41851,7 +42116,7 @@ SQLITE_API int sqlite3_win32_compact_hea
|
|
(void*)hHeap);
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
- sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p",
|
|
+ sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%u), heap=%p",
|
|
osGetLastError(), (void*)hHeap);
|
|
rc = SQLITE_ERROR;
|
|
}
|
|
@@ -41911,6 +42176,7 @@ SQLITE_API int sqlite3_win32_reset_heap(
|
|
}
|
|
#endif /* SQLITE_WIN32_MALLOC */
|
|
|
|
+#ifdef _WIN32
|
|
/*
|
|
** This function outputs the specified (ANSI) string to the Win32 debugger
|
|
** (if available).
|
|
@@ -41953,6 +42219,7 @@ SQLITE_API void sqlite3_win32_write_debu
|
|
}
|
|
#endif
|
|
}
|
|
+#endif /* _WIN32 */
|
|
|
|
/*
|
|
** The following routine suspends the current thread for at least ms
|
|
@@ -41962,6 +42229,9 @@ SQLITE_API void sqlite3_win32_write_debu
|
|
static HANDLE sleepObj = NULL;
|
|
#endif
|
|
|
|
+#if defined(__CYGWIN__) && defined(SQLITE_AMALGAMATION)
|
|
+static
|
|
+#endif
|
|
SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
|
|
#if SQLITE_OS_WINRT
|
|
if ( sleepObj==NULL ){
|
|
@@ -41975,8 +42245,7 @@ SQLITE_API void sqlite3_win32_sleep(DWOR
|
|
#endif
|
|
}
|
|
|
|
-#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
|
- SQLITE_THREADSAFE>0
|
|
+#if SQLITE_OS_WIN_THREADS
|
|
SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
|
|
DWORD rc;
|
|
while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
|
|
@@ -42007,6 +42276,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HA
|
|
# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt())
|
|
#endif
|
|
|
|
+#if 0
|
|
/*
|
|
** This function determines if the machine is running a version of Windows
|
|
** based on the NT kernel.
|
|
@@ -42045,12 +42315,13 @@ SQLITE_API int sqlite3_win32_is_nt(void)
|
|
return 1;
|
|
#endif
|
|
}
|
|
+#endif /* 0 */
|
|
|
|
#ifdef SQLITE_WIN32_MALLOC
|
|
/*
|
|
** Allocate nBytes of memory.
|
|
*/
|
|
-static void *winMemMalloc(int nBytes){
|
|
+static void *winMemMalloc(size_t nBytes){
|
|
HANDLE hHeap;
|
|
void *p;
|
|
|
|
@@ -42061,11 +42332,10 @@ static void *winMemMalloc(int nBytes){
|
|
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
|
assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
|
#endif
|
|
- assert( nBytes>=0 );
|
|
p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
|
|
if( !p ){
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p",
|
|
- nBytes, osGetLastError(), (void*)hHeap);
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %llu bytes (%u), heap=%p",
|
|
+ (u64)nBytes, osGetLastError(), (void*)hHeap);
|
|
}
|
|
return p;
|
|
}
|
|
@@ -42085,7 +42355,7 @@ static void winMemFree(void *pPrior){
|
|
#endif
|
|
if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
|
|
if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p",
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%u), heap=%p",
|
|
pPrior, osGetLastError(), (void*)hHeap);
|
|
}
|
|
}
|
|
@@ -42093,7 +42363,7 @@ static void winMemFree(void *pPrior){
|
|
/*
|
|
** Change the size of an existing memory allocation
|
|
*/
|
|
-static void *winMemRealloc(void *pPrior, int nBytes){
|
|
+static void *winMemRealloc(void *pPrior, size_t nBytes){
|
|
HANDLE hHeap;
|
|
void *p;
|
|
|
|
@@ -42104,15 +42374,14 @@ static void *winMemRealloc(void *pPrior,
|
|
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
|
assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
|
|
#endif
|
|
- assert( nBytes>=0 );
|
|
if( !pPrior ){
|
|
p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
|
|
}else{
|
|
p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
|
|
}
|
|
if( !p ){
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p",
|
|
- pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(),
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %llu bytes (%u), heap=%p",
|
|
+ pPrior ? "HeapReAlloc" : "HeapAlloc", (u64)nBytes, osGetLastError(),
|
|
(void*)hHeap);
|
|
}
|
|
return p;
|
|
@@ -42121,7 +42390,7 @@ static void *winMemRealloc(void *pPrior,
|
|
/*
|
|
** Return the size of an outstanding allocation, in bytes.
|
|
*/
|
|
-static int winMemSize(void *p){
|
|
+static size_t winMemSize(void *p){
|
|
HANDLE hHeap;
|
|
SIZE_T n;
|
|
|
|
@@ -42135,17 +42404,17 @@ static int winMemSize(void *p){
|
|
if( !p ) return 0;
|
|
n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
|
|
if( n==(SIZE_T)-1 ){
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p",
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%u), heap=%p",
|
|
p, osGetLastError(), (void*)hHeap);
|
|
return 0;
|
|
}
|
|
- return (int)n;
|
|
+ return (size_t)n;
|
|
}
|
|
|
|
/*
|
|
** Round up a request size to the next valid allocation size.
|
|
*/
|
|
-static int winMemRoundup(int n){
|
|
+static size_t winMemRoundup(size_t n){
|
|
return n;
|
|
}
|
|
|
|
@@ -42172,7 +42441,7 @@ static int winMemInit(void *pAppData){
|
|
dwInitialSize, dwMaximumSize);
|
|
if( !pWinMemData->hHeap ){
|
|
sqlite3_log(SQLITE_NOMEM,
|
|
- "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu",
|
|
+ "failed to HeapCreate (%u), flags=%u, initSize=%u, maxSize=%u",
|
|
osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize,
|
|
dwMaximumSize);
|
|
return SQLITE_NOMEM_BKPT;
|
|
@@ -42184,7 +42453,7 @@ static int winMemInit(void *pAppData){
|
|
pWinMemData->hHeap = osGetProcessHeap();
|
|
if( !pWinMemData->hHeap ){
|
|
sqlite3_log(SQLITE_NOMEM,
|
|
- "failed to GetProcessHeap (%lu)", osGetLastError());
|
|
+ "failed to GetProcessHeap (%u)", osGetLastError());
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
pWinMemData->bOwned = FALSE;
|
|
@@ -42215,7 +42484,7 @@ static void winMemShutdown(void *pAppDat
|
|
#endif
|
|
if( pWinMemData->bOwned ){
|
|
if( !osHeapDestroy(pWinMemData->hHeap) ){
|
|
- sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p",
|
|
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%u), heap=%p",
|
|
osGetLastError(), (void*)pWinMemData->hHeap);
|
|
}
|
|
pWinMemData->bOwned = FALSE;
|
|
@@ -42232,8 +42501,8 @@ static void winMemShutdown(void *pAppDat
|
|
** This routine is only called by sqlite3_config(), and therefore
|
|
** is not required to be threadsafe (it is not).
|
|
*/
|
|
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void){
|
|
- static const sqlite3_mem_methods winMemMethods = {
|
|
+SQLITE_PRIVATE const sqlite3_mem_methods2 *sqlite3MemGetWin32(void){
|
|
+ static const sqlite3_mem_methods2 winMemMethods = {
|
|
winMemMalloc,
|
|
winMemFree,
|
|
winMemRealloc,
|
|
@@ -42247,16 +42516,17 @@ SQLITE_PRIVATE const sqlite3_mem_methods
|
|
}
|
|
|
|
SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
|
- sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
|
|
+ sqlite3_config(SQLITE_CONFIG_MALLOC2, sqlite3MemGetWin32());
|
|
}
|
|
#endif /* SQLITE_WIN32_MALLOC */
|
|
|
|
+#ifdef _WIN32
|
|
/*
|
|
** Convert a UTF-8 string to Microsoft Unicode.
|
|
**
|
|
** Space to hold the returned string is obtained from sqlite3_malloc().
|
|
*/
|
|
-static LPWSTR winUtf8ToUnicode(const char *zText){
|
|
+static LPWSTR winUtf8ToUnicode(const char *zText, WCHAR *buf){
|
|
int nChar;
|
|
LPWSTR zWideText;
|
|
|
|
@@ -42264,9 +42534,13 @@ static LPWSTR winUtf8ToUnicode(const cha
|
|
if( nChar==0 ){
|
|
return 0;
|
|
}
|
|
- zWideText = sqlite3MallocZero( nChar*sizeof(WCHAR) );
|
|
- if( zWideText==0 ){
|
|
- return 0;
|
|
+ if( buf && ((size_t)nChar<=MAX_PATH) ){
|
|
+ zWideText = buf;
|
|
+ }else{
|
|
+ zWideText = sqlite3Malloc( nChar*sizeof(WCHAR) );
|
|
+ if( zWideText==0 ){
|
|
+ return 0;
|
|
+ }
|
|
}
|
|
nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText,
|
|
nChar);
|
|
@@ -42276,6 +42550,7 @@ static LPWSTR winUtf8ToUnicode(const cha
|
|
}
|
|
return zWideText;
|
|
}
|
|
+#endif /* _WIN32 */
|
|
|
|
/*
|
|
** Convert a Microsoft Unicode string to UTF-8.
|
|
@@ -42290,7 +42565,7 @@ static char *winUnicodeToUtf8(LPCWSTR zW
|
|
if( nByte == 0 ){
|
|
return 0;
|
|
}
|
|
- zText = sqlite3MallocZero( nByte );
|
|
+ zText = sqlite3Malloc( nByte );
|
|
if( zText==0 ){
|
|
return 0;
|
|
}
|
|
@@ -42315,11 +42590,11 @@ static LPWSTR winMbcsToUnicode(const cha
|
|
int codepage = useAnsi ? CP_ACP : CP_OEMCP;
|
|
|
|
nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
|
|
- 0)*sizeof(WCHAR);
|
|
+ 0);
|
|
if( nByte==0 ){
|
|
return 0;
|
|
}
|
|
- zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
|
|
+ zMbcsText = sqlite3Malloc( nByte*sizeof(WCHAR) );
|
|
if( zMbcsText==0 ){
|
|
return 0;
|
|
}
|
|
@@ -42332,6 +42607,7 @@ static LPWSTR winMbcsToUnicode(const cha
|
|
return zMbcsText;
|
|
}
|
|
|
|
+#ifdef _WIN32
|
|
/*
|
|
** Convert a Microsoft Unicode string to a multi-byte character string,
|
|
** using the ANSI or OEM code page.
|
|
@@ -42347,7 +42623,7 @@ static char *winUnicodeToMbcs(LPCWSTR zW
|
|
if( nByte == 0 ){
|
|
return 0;
|
|
}
|
|
- zText = sqlite3MallocZero( nByte );
|
|
+ zText = sqlite3Malloc( nByte );
|
|
if( zText==0 ){
|
|
return 0;
|
|
}
|
|
@@ -42359,6 +42635,7 @@ static char *winUnicodeToMbcs(LPCWSTR zW
|
|
}
|
|
return zText;
|
|
}
|
|
+#endif /* _WIN32 */
|
|
|
|
/*
|
|
** Convert a multi-byte character string to UTF-8.
|
|
@@ -42378,6 +42655,7 @@ static char *winMbcsToUtf8(const char *z
|
|
return zTextUtf8;
|
|
}
|
|
|
|
+#ifdef _WIN32
|
|
/*
|
|
** Convert a UTF-8 string to a multi-byte character string.
|
|
**
|
|
@@ -42386,13 +42664,16 @@ static char *winMbcsToUtf8(const char *z
|
|
static char *winUtf8ToMbcs(const char *zText, int useAnsi){
|
|
char *zTextMbcs;
|
|
LPWSTR zTmpWide;
|
|
+ WCHAR buf[MAX_PATH];
|
|
|
|
- zTmpWide = winUtf8ToUnicode(zText);
|
|
+ zTmpWide = winUtf8ToUnicode(zText, buf);
|
|
if( zTmpWide==0 ){
|
|
return 0;
|
|
}
|
|
zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi);
|
|
- sqlite3_free(zTmpWide);
|
|
+ if (zTmpWide != buf){
|
|
+ sqlite3_free(zTmpWide);
|
|
+ }
|
|
return zTextMbcs;
|
|
}
|
|
|
|
@@ -42409,7 +42690,7 @@ SQLITE_API LPWSTR sqlite3_win32_utf8_to_
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
if( sqlite3_initialize() ) return 0;
|
|
#endif
|
|
- return winUtf8ToUnicode(zText);
|
|
+ return winUtf8ToUnicode(zText, 0);
|
|
}
|
|
|
|
/*
|
|
@@ -42427,6 +42708,7 @@ SQLITE_API char *sqlite3_win32_unicode_t
|
|
#endif
|
|
return winUnicodeToUtf8(zWideText);
|
|
}
|
|
+#endif /* _WIN32 */
|
|
|
|
/*
|
|
** This is a public wrapper for the winMbcsToUtf8() function.
|
|
@@ -42444,6 +42726,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_u
|
|
return winMbcsToUtf8(zText, osAreFileApisANSI());
|
|
}
|
|
|
|
+#ifdef _WIN32
|
|
/*
|
|
** This is a public wrapper for the winMbcsToUtf8() function.
|
|
*/
|
|
@@ -42514,7 +42797,7 @@ SQLITE_API int sqlite3_win32_set_directo
|
|
|| type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE
|
|
);
|
|
assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
|
|
- if( ppDirectory ){
|
|
+ if( !osGetenv && ppDirectory ){
|
|
char *zCopy = 0;
|
|
if( zValue && zValue[0] ){
|
|
zCopy = sqlite3_mprintf("%s", zValue);
|
|
@@ -42561,6 +42844,7 @@ SQLITE_API int sqlite3_win32_set_directo
|
|
){
|
|
return sqlite3_win32_set_directory16(type, zValue);
|
|
}
|
|
+#endif /* _WIN32 */
|
|
|
|
/*
|
|
** The return value of winGetLastErrorMsg
|
|
@@ -42632,7 +42916,7 @@ static int winGetLastErrorMsg(DWORD last
|
|
}
|
|
#endif
|
|
if( 0 == dwLen ){
|
|
- sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno);
|
|
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", lastErrno, lastErrno);
|
|
}else{
|
|
/* copy a maximum of nBuf chars to output buffer */
|
|
sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
|
|
@@ -42675,7 +42959,7 @@ static int winLogErrorAtLine(
|
|
for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
|
|
zMsg[i] = 0;
|
|
sqlite3_log(errcode,
|
|
- "os_win.c:%d: (%lu) %s(%s) - %s",
|
|
+ "os_win.c:%d: (%u) %s(%s) - %s",
|
|
iLine, lastErrno, zFunc, zPath, zMsg
|
|
);
|
|
|
|
@@ -42830,8 +43114,9 @@ static int winceCreateLock(const char *z
|
|
DWORD lastErrno;
|
|
BOOL bLogged = FALSE;
|
|
BOOL bInit = TRUE;
|
|
+ WCHAR buf[MAX_PATH];
|
|
|
|
- zName = winUtf8ToUnicode(zFilename);
|
|
+ zName = winUtf8ToUnicode(zFilename, buf);
|
|
if( zName==0 ){
|
|
/* out of memory */
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
@@ -42851,7 +43136,9 @@ static int winceCreateLock(const char *z
|
|
pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
|
|
if (!pFile->hMutex){
|
|
pFile->lastErrno = osGetLastError();
|
|
- sqlite3_free(zName);
|
|
+ if( zName!=buf ){
|
|
+ sqlite3_free(zName);
|
|
+ }
|
|
return winLogError(SQLITE_IOERR, pFile->lastErrno,
|
|
"winceCreateLock1", zFilename);
|
|
}
|
|
@@ -42875,7 +43162,9 @@ static int winceCreateLock(const char *z
|
|
bInit = FALSE;
|
|
}
|
|
|
|
- sqlite3_free(zName);
|
|
+ if( zName!=buf ){
|
|
+ sqlite3_free(zName);
|
|
+ }
|
|
|
|
/* If we succeeded in making the shared memory handle, map it. */
|
|
if( pFile->hShared ){
|
|
@@ -43109,9 +43398,11 @@ static BOOL winLockFile(
|
|
ovlp.Offset = offsetLow;
|
|
ovlp.OffsetHigh = offsetHigh;
|
|
return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
|
|
+#ifdef SQLITE_WIN32_HAS_ANSI
|
|
}else{
|
|
return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
|
numBytesHigh);
|
|
+#endif
|
|
}
|
|
#endif
|
|
}
|
|
@@ -43140,9 +43431,11 @@ static BOOL winUnlockFile(
|
|
ovlp.Offset = offsetLow;
|
|
ovlp.OffsetHigh = offsetHigh;
|
|
return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
|
|
+#ifdef SQLITE_WIN32_HAS_ANSI
|
|
}else{
|
|
return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
|
numBytesHigh);
|
|
+#endif
|
|
}
|
|
#endif
|
|
}
|
|
@@ -43246,7 +43539,7 @@ static int winClose(sqlite3_file *id){
|
|
assert( pFile->pShm==0 );
|
|
#endif
|
|
assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE );
|
|
- OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n",
|
|
+ OSTRACE(("CLOSE pid=%u, pFile=%p, file=%p\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
@@ -43281,7 +43574,7 @@ static int winClose(sqlite3_file *id){
|
|
pFile->h = NULL;
|
|
}
|
|
OpenCounter(-1);
|
|
- OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n",
|
|
+ OSTRACE(("CLOSE pid=%u, pFile=%p, file=%p, rc=%s\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed"));
|
|
return rc ? SQLITE_OK
|
|
: winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
|
|
@@ -43310,7 +43603,7 @@ static int winRead(
|
|
assert( amt>0 );
|
|
assert( offset>=0 );
|
|
SimulateIOError(return SQLITE_IOERR_READ);
|
|
- OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
|
|
+ OSTRACE(("READ pid=%u, pFile=%p, file=%p, buffer=%p, amount=%d, "
|
|
"offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
|
|
pFile->h, pBuf, amt, offset, pFile->locktype));
|
|
|
|
@@ -43320,7 +43613,7 @@ static int winRead(
|
|
if( offset<pFile->mmapSize ){
|
|
if( offset+amt <= pFile->mmapSize ){
|
|
memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
|
|
- OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("READ-MMAP pid=%u, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_OK;
|
|
}else{
|
|
@@ -43335,7 +43628,7 @@ static int winRead(
|
|
|
|
#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
|
if( winSeekFile(pFile, offset) ){
|
|
- OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n",
|
|
+ OSTRACE(("READ pid=%u, pFile=%p, file=%p, rc=SQLITE_FULL\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_FULL;
|
|
}
|
|
@@ -43350,7 +43643,7 @@ static int winRead(
|
|
DWORD lastErrno;
|
|
if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
|
|
pFile->lastErrno = lastErrno;
|
|
- OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n",
|
|
+ OSTRACE(("READ pid=%u, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
|
|
"winRead", pFile->zPath);
|
|
@@ -43359,12 +43652,12 @@ static int winRead(
|
|
if( nRead<(DWORD)amt ){
|
|
/* Unread parts of the buffer must be zero-filled */
|
|
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
|
|
- OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n",
|
|
+ OSTRACE(("READ pid=%u, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
|
|
- OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("READ pid=%u, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -43388,7 +43681,7 @@ static int winWrite(
|
|
SimulateIOError(return SQLITE_IOERR_WRITE);
|
|
SimulateDiskfullError(return SQLITE_FULL);
|
|
|
|
- OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
|
|
+ OSTRACE(("WRITE pid=%u, pFile=%p, file=%p, buffer=%p, amount=%d, "
|
|
"offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
|
|
pFile->h, pBuf, amt, offset, pFile->locktype));
|
|
|
|
@@ -43398,7 +43691,7 @@ static int winWrite(
|
|
if( offset<pFile->mmapSize ){
|
|
if( offset+amt <= pFile->mmapSize ){
|
|
memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
|
|
- OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("WRITE-MMAP pid=%u, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_OK;
|
|
}else{
|
|
@@ -43462,19 +43755,19 @@ static int winWrite(
|
|
if( rc ){
|
|
if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|
|
|| ( pFile->lastErrno==ERROR_DISK_FULL )){
|
|
- OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n",
|
|
+ OSTRACE(("WRITE pid=%u, pFile=%p, file=%p, rc=SQLITE_FULL\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return winLogError(SQLITE_FULL, pFile->lastErrno,
|
|
"winWrite1", pFile->zPath);
|
|
}
|
|
- OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n",
|
|
+ OSTRACE(("WRITE pid=%u, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
|
|
"winWrite2", pFile->zPath);
|
|
}else{
|
|
winLogIoerr(nRetry, __LINE__);
|
|
}
|
|
- OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("WRITE pid=%u, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -43512,7 +43805,7 @@ static int winTruncate(sqlite3_file *id,
|
|
|
|
assert( pFile );
|
|
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
|
- OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n",
|
|
+ OSTRACE(("TRUNCATE pid=%u, pFile=%p, file=%p, size=%lld, lock=%d\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype));
|
|
|
|
/* If the user has configured a chunk-size for this file, truncate the
|
|
@@ -43554,7 +43847,7 @@ static int winTruncate(sqlite3_file *id,
|
|
}
|
|
#endif
|
|
|
|
- OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n",
|
|
+ OSTRACE(("TRUNCATE pid=%u, pFile=%p, file=%p, rc=%s\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h, sqlite3ErrName(rc)));
|
|
return rc;
|
|
}
|
|
@@ -43600,7 +43893,7 @@ static int winSync(sqlite3_file *id, int
|
|
*/
|
|
SimulateDiskfullError( return SQLITE_FULL );
|
|
|
|
- OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n",
|
|
+ OSTRACE(("SYNC pid=%u, pFile=%p, file=%p, flags=%x, lock=%d\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h, flags,
|
|
pFile->locktype));
|
|
|
|
@@ -43617,19 +43910,19 @@ static int winSync(sqlite3_file *id, int
|
|
** no-op
|
|
*/
|
|
#ifdef SQLITE_NO_SYNC
|
|
- OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("SYNC-NOP pid=%u, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_OK;
|
|
#else
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
if( pFile->pMapRegion ){
|
|
if( osFlushViewOfFile(pFile->pMapRegion, 0) ){
|
|
- OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, "
|
|
+ OSTRACE(("SYNC-MMAP pid=%u, pFile=%p, pMapRegion=%p, "
|
|
"rc=SQLITE_OK\n", osGetCurrentProcessId(),
|
|
pFile, pFile->pMapRegion));
|
|
}else{
|
|
pFile->lastErrno = osGetLastError();
|
|
- OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, "
|
|
+ OSTRACE(("SYNC-MMAP pid=%u, pFile=%p, pMapRegion=%p, "
|
|
"rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(),
|
|
pFile, pFile->pMapRegion));
|
|
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
|
|
@@ -43640,12 +43933,12 @@ static int winSync(sqlite3_file *id, int
|
|
rc = osFlushFileBuffers(pFile->h);
|
|
SimulateIOError( rc=FALSE );
|
|
if( rc ){
|
|
- OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("SYNC pid=%u, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return SQLITE_OK;
|
|
}else{
|
|
pFile->lastErrno = osGetLastError();
|
|
- OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n",
|
|
+ OSTRACE(("SYNC pid=%u, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n",
|
|
osGetCurrentProcessId(), pFile, pFile->h));
|
|
return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
|
|
"winSync2", pFile->zPath);
|
|
@@ -44396,7 +44689,7 @@ static int winShmSystemLock(
|
|
rc = SQLITE_BUSY;
|
|
}
|
|
|
|
- OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
|
|
+ OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%u, rc=%s\n",
|
|
pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
|
|
"winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
|
|
|
|
@@ -44417,7 +44710,7 @@ static void winShmPurge(sqlite3_vfs *pVf
|
|
winShmNode **pp;
|
|
winShmNode *p;
|
|
assert( winShmMutexHeld() );
|
|
- OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
|
|
+ OSTRACE(("SHM-PURGE pid=%u, deleteFlag=%d\n",
|
|
osGetCurrentProcessId(), deleteFlag));
|
|
pp = &winShmNodeList;
|
|
while( (p = *pp)!=0 ){
|
|
@@ -44426,11 +44719,11 @@ static void winShmPurge(sqlite3_vfs *pVf
|
|
if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
|
|
for(i=0; i<p->nRegion; i++){
|
|
BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
|
|
- OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
|
|
+ OSTRACE(("SHM-PURGE-UNMAP pid=%u, region=%d, rc=%s\n",
|
|
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
|
|
UNUSED_VARIABLE_VALUE(bRc);
|
|
bRc = osCloseHandle(p->aRegion[i].hMap);
|
|
- OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
|
|
+ OSTRACE(("SHM-PURGE-CLOSE pid=%u, region=%d, rc=%s\n",
|
|
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
|
|
UNUSED_VARIABLE_VALUE(bRc);
|
|
}
|
|
@@ -44740,7 +45033,7 @@ static int winShmLock(
|
|
}
|
|
}
|
|
sqlite3_mutex_leave(pShmNode->mutex);
|
|
- OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
|
|
+ OSTRACE(("SHM-LOCK pid=%u, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
|
|
osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
|
|
sqlite3ErrName(rc)));
|
|
return rc;
|
|
@@ -44875,7 +45168,7 @@ static int winShmMap(
|
|
NULL, protect, 0, nByte, NULL
|
|
);
|
|
#endif
|
|
- OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
|
|
+ OSTRACE(("SHM-MAP-CREATE pid=%u, region=%d, size=%d, rc=%s\n",
|
|
osGetCurrentProcessId(), pShmNode->nRegion, nByte,
|
|
hMap ? "ok" : "failed"));
|
|
if( hMap ){
|
|
@@ -44890,7 +45183,7 @@ static int winShmMap(
|
|
0, iOffset - iOffsetShift, szRegion + iOffsetShift
|
|
);
|
|
#endif
|
|
- OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n",
|
|
+ OSTRACE(("SHM-MAP-MAP pid=%u, region=%d, offset=%d, size=%d, rc=%s\n",
|
|
osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
|
|
szRegion, pMap ? "ok" : "failed"));
|
|
}
|
|
@@ -44935,14 +45228,14 @@ shmpage_out:
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
static int winUnmapfile(winFile *pFile){
|
|
assert( pFile!=0 );
|
|
- OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, "
|
|
+ OSTRACE(("UNMAP-FILE pid=%u, pFile=%p, hMap=%p, pMapRegion=%p, "
|
|
"mmapSize=%lld, mmapSizeMax=%lld\n",
|
|
osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion,
|
|
pFile->mmapSize, pFile->mmapSizeMax));
|
|
if( pFile->pMapRegion ){
|
|
if( !osUnmapViewOfFile(pFile->pMapRegion) ){
|
|
pFile->lastErrno = osGetLastError();
|
|
- OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, "
|
|
+ OSTRACE(("UNMAP-FILE pid=%u, pFile=%p, pMapRegion=%p, "
|
|
"rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile,
|
|
pFile->pMapRegion));
|
|
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
|
|
@@ -44954,14 +45247,14 @@ static int winUnmapfile(winFile *pFile){
|
|
if( pFile->hMap!=NULL ){
|
|
if( !osCloseHandle(pFile->hMap) ){
|
|
pFile->lastErrno = osGetLastError();
|
|
- OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
|
|
+ OSTRACE(("UNMAP-FILE pid=%u, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
|
|
osGetCurrentProcessId(), pFile, pFile->hMap));
|
|
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
|
|
"winUnmapfile2", pFile->zPath);
|
|
}
|
|
pFile->hMap = NULL;
|
|
}
|
|
- OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("UNMAP-FILE pid=%u, pFile=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFile));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -44987,7 +45280,7 @@ static int winMapfile(winFile *pFd, sqli
|
|
int rc;
|
|
|
|
assert( nMap>=0 || pFd->nFetchOut==0 );
|
|
- OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n",
|
|
+ OSTRACE(("MAP-FILE pid=%u, pFile=%p, size=%lld\n",
|
|
osGetCurrentProcessId(), pFd, nByte));
|
|
|
|
if( pFd->nFetchOut>0 ) return SQLITE_OK;
|
|
@@ -44995,7 +45288,7 @@ static int winMapfile(winFile *pFd, sqli
|
|
if( nMap<0 ){
|
|
rc = winFileSize((sqlite3_file*)pFd, &nMap);
|
|
if( rc ){
|
|
- OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n",
|
|
+ OSTRACE(("MAP-FILE pid=%u, pFile=%p, rc=SQLITE_IOERR_FSTAT\n",
|
|
osGetCurrentProcessId(), pFd));
|
|
return SQLITE_IOERR_FSTAT;
|
|
}
|
|
@@ -45036,7 +45329,7 @@ static int winMapfile(winFile *pFd, sqli
|
|
rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
|
|
"winMapfile1", pFd->zPath);
|
|
/* Log the error, but continue normal operation using xRead/xWrite */
|
|
- OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n",
|
|
+ OSTRACE(("MAP-FILE-CREATE pid=%u, pFile=%p, rc=%s\n",
|
|
osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -45054,7 +45347,7 @@ static int winMapfile(winFile *pFd, sqli
|
|
rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
|
|
"winMapfile2", pFd->zPath);
|
|
/* Log the error, but continue normal operation using xRead/xWrite */
|
|
- OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n",
|
|
+ OSTRACE(("MAP-FILE-MAP pid=%u, pFile=%p, rc=%s\n",
|
|
osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -45062,7 +45355,7 @@ static int winMapfile(winFile *pFd, sqli
|
|
pFd->mmapSize = nMap;
|
|
}
|
|
|
|
- OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("MAP-FILE pid=%u, pFile=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), pFd));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -45086,7 +45379,7 @@ static int winFetch(sqlite3_file *fd, i6
|
|
#endif
|
|
*pp = 0;
|
|
|
|
- OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
|
|
+ OSTRACE(("FETCH pid=%u, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
|
|
osGetCurrentProcessId(), fd, iOff, nAmt, pp));
|
|
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
@@ -45094,7 +45387,7 @@ static int winFetch(sqlite3_file *fd, i6
|
|
if( pFd->pMapRegion==0 ){
|
|
int rc = winMapfile(pFd, -1);
|
|
if( rc!=SQLITE_OK ){
|
|
- OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
|
|
+ OSTRACE(("FETCH pid=%u, pFile=%p, rc=%s\n",
|
|
osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
|
|
return rc;
|
|
}
|
|
@@ -45106,7 +45399,7 @@ static int winFetch(sqlite3_file *fd, i6
|
|
}
|
|
#endif
|
|
|
|
- OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n",
|
|
+ OSTRACE(("FETCH pid=%u, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), fd, pp, *pp));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -45133,7 +45426,7 @@ static int winUnfetch(sqlite3_file *fd,
|
|
/* If p!=0, it must match the iOff value. */
|
|
assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
|
|
|
|
- OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n",
|
|
+ OSTRACE(("UNFETCH pid=%u, pFile=%p, offset=%lld, p=%p\n",
|
|
osGetCurrentProcessId(), pFd, iOff, p));
|
|
|
|
if( p ){
|
|
@@ -45149,7 +45442,8 @@ static int winUnfetch(sqlite3_file *fd,
|
|
assert( pFd->nFetchOut>=0 );
|
|
#endif
|
|
|
|
- OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
|
+ UNUSED_PARAMETER(iOff);
|
|
+ OSTRACE(("UNFETCH pid=%u, pFile=%p, rc=SQLITE_OK\n",
|
|
osGetCurrentProcessId(), fd));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -45231,7 +45525,7 @@ static winVfsAppData winNolockAppData =
|
|
** sqlite3_vfs object.
|
|
*/
|
|
|
|
-#if defined(__CYGWIN__)
|
|
+#if 0
|
|
/*
|
|
** Convert a filename from whatever the underlying operating system
|
|
** supports for filenames into UTF-8. Space to hold the result is
|
|
@@ -45256,12 +45550,106 @@ static char *winConvertToUtf8Filename(co
|
|
** Convert a UTF-8 filename into whatever form the underlying
|
|
** operating system wants filenames in. Space to hold the result
|
|
** is obtained from malloc and must be freed by the calling
|
|
-** function.
|
|
+** function, unless buf is not NULL and the needed size is less
|
|
+** than MAX_PATH bytes.
|
|
+**
|
|
+** On Cygwin 1.7 and higher, 3 possible input forms are accepted:
|
|
+** - If the filename starts with "<drive>:/" or "<drive>:\",
|
|
+** it is converted to UTF-16 as-is.
|
|
+** - If the filename contains '/', it is assumed to be a
|
|
+** Cygwin absolute path, it is converted to a win32
|
|
+** absolute path in UTF-16.
|
|
+** - Otherwise it must be a filename only, the win32 filename
|
|
+** is returned in UTF-16.
|
|
+** Note: The function cygwin_conv_path does not exist in
|
|
+** Cygwin 1.5. Cygwin 1.7 does not run in Windows 95/98/ME.
|
|
+** Therefore the !osIsNT() case does not need special handling.
|
|
+** Note 2: If the function cygwin_conv_path() fails, only
|
|
+** UTF-8 -> UTF-16 conversion will be done. This can only
|
|
+** happen when the file path >32k, in which case winUtf8ToUnicode()
|
|
+** will fail too.
|
|
*/
|
|
-static void *winConvertFromUtf8Filename(const char *zFilename){
|
|
+static void *winConvertFromUtf8Filename(const char *zFilename, WCHAR *buf){
|
|
void *zConverted = 0;
|
|
if( osIsNT() ){
|
|
- zConverted = winUtf8ToUnicode(zFilename);
|
|
+ int nChar;
|
|
+ LPWSTR zWideFilename;
|
|
+
|
|
+ if( cygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
|
|
+ && winIsDirSep(zFilename[2])) ){
|
|
+ int nByte;
|
|
+ int convertflag = CCP_POSIX_TO_WIN_W;
|
|
+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
|
|
+ nByte = (int) cygwin_conv_path(convertflag,
|
|
+ zFilename, 0, 0);
|
|
+ if( nByte>0 ){
|
|
+ if( buf && (nByte<(MAX_PATH-12)) ){
|
|
+ zConverted = buf;
|
|
+ }else{
|
|
+ zConverted = sqlite3Malloc(nByte+12);
|
|
+ if ( zConverted==0 ){
|
|
+ return zConverted;
|
|
+ }
|
|
+ }
|
|
+ zWideFilename = zConverted;
|
|
+ /* Filenames should be prefixed, except when converted
|
|
+ * full path already starts with "\\?\". */
|
|
+ if( cygwin_conv_path(convertflag, zFilename,
|
|
+ zWideFilename+4, nByte)==0 ){
|
|
+ if( (convertflag&CCP_RELATIVE) ){
|
|
+ memmove(zWideFilename, zWideFilename+4, nByte);
|
|
+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
|
|
+ memcpy(zWideFilename, L"\\\\?\\", 8);
|
|
+ }else if( zWideFilename[6]!='?' ){
|
|
+ memmove(zWideFilename+6, zWideFilename+4, nByte);
|
|
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
|
|
+ }else{
|
|
+ memmove(zWideFilename, zWideFilename+4, nByte);
|
|
+ }
|
|
+ return zConverted;
|
|
+ }
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
+ }
|
|
+#ifdef _WIN32
|
|
+ }else if( (cygwin_conv_to_full_win32_path!=NULL) &&
|
|
+ !(winIsDriveLetterAndColon(zFilename) && winIsDirSep(zFilename[2]))){
|
|
+ char buf1[MAX_PATH];
|
|
+ cygwin_conv_to_full_win32_path(zFilename, buf1);
|
|
+ return winMbcsToUnicode(buf1, osAreFileApisANSI());
|
|
+#endif
|
|
+ }
|
|
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
|
|
+ if( nChar==0 ){
|
|
+ return 0;
|
|
+ }
|
|
+ if( buf && ((size_t)nChar<=MAX_PATH) ){
|
|
+ zWideFilename = buf;
|
|
+ }else{
|
|
+ zWideFilename = sqlite3Malloc( nChar*sizeof(WCHAR)+12 );
|
|
+ if( zWideFilename==0 ){
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
|
|
+ zWideFilename, nChar);
|
|
+ if( nChar==0 ){
|
|
+ sqlite3_free(zWideFilename);
|
|
+ zWideFilename = 0;
|
|
+ }else if( nChar>MAX_PATH
|
|
+ && winIsDriveLetterAndColon(zFilename)
|
|
+ && winIsDirSep(zFilename[2]) ){
|
|
+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
|
|
+ zWideFilename[2] = '\\';
|
|
+ memcpy(zWideFilename, L"\\\\?\\", 8);
|
|
+ }else if( nChar>MAX_PATH
|
|
+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
|
|
+ && zFilename[2] != '?' ){
|
|
+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
|
|
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
|
|
+ }
|
|
+ zConverted = zWideFilename;
|
|
}
|
|
#ifdef SQLITE_WIN32_HAS_ANSI
|
|
else{
|
|
@@ -45284,7 +45672,14 @@ static int winMakeEndInDirSep(int nBuf,
|
|
if( winIsDirSep(zBuf[nLen-1]) ){
|
|
return 1;
|
|
}else if( nLen+1<nBuf ){
|
|
- zBuf[nLen] = winGetDirSep();
|
|
+ if( !osGetenv ){
|
|
+ zBuf[nLen] = '\\';
|
|
+ }else if( winIsDriveLetterAndColon(zBuf) && winIsDirSep(zBuf[2]) ){
|
|
+ zBuf[nLen] = '\\';
|
|
+ zBuf[2]='\\';
|
|
+ }else{
|
|
+ zBuf[nLen] = '/';
|
|
+ }
|
|
zBuf[nLen+1] = '\0';
|
|
return 1;
|
|
}
|
|
@@ -45298,7 +45693,7 @@ static int winMakeEndInDirSep(int nBuf,
|
|
** The pointer returned in pzBuf must be freed via sqlite3_free().
|
|
*/
|
|
static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
|
|
- static char zChars[] =
|
|
+ static const char zChars[] =
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"0123456789";
|
|
@@ -45343,8 +45738,8 @@ static int winGetTempname(sqlite3_vfs *p
|
|
sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
|
|
}
|
|
}
|
|
-#if defined(__CYGWIN__)
|
|
- else{
|
|
+#if SQLITE_OS_WINNT
|
|
+ else if( osGetenv!=NULL ){
|
|
static const char *azDirs[] = {
|
|
0, /* getenv("SQLITE_TMPDIR") */
|
|
0, /* getenv("TMPDIR") */
|
|
@@ -45360,21 +45755,22 @@ static int winGetTempname(sqlite3_vfs *p
|
|
unsigned int i;
|
|
const char *zDir = 0;
|
|
|
|
- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
|
|
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
|
- if( !azDirs[2] ) azDirs[2] = getenv("TMP");
|
|
- if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
|
|
- if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
|
|
+ if( !azDirs[0] ) azDirs[0] = osGetenv("SQLITE_TMPDIR");
|
|
+ if( !azDirs[1] ) azDirs[1] = osGetenv("TMPDIR");
|
|
+ if( !azDirs[2] ) azDirs[2] = osGetenv("TMP");
|
|
+ if( !azDirs[3] ) azDirs[3] = osGetenv("TEMP");
|
|
+ if( !azDirs[4] ) azDirs[4] = osGetenv("USERPROFILE");
|
|
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
|
void *zConverted;
|
|
+ WCHAR buf[MAX_PATH];
|
|
if( zDir==0 ) continue;
|
|
/* If the path starts with a drive letter followed by the colon
|
|
** character, assume it is already a native Win32 path; otherwise,
|
|
** it must be converted to a native Win32 path via the Cygwin API
|
|
** prior to using it.
|
|
*/
|
|
- if( winIsDriveLetterAndColon(zDir) ){
|
|
- zConverted = winConvertFromUtf8Filename(zDir);
|
|
+ {
|
|
+ zConverted = winConvertFromUtf8Filename(zDir, buf);
|
|
if( !zConverted ){
|
|
sqlite3_free(zBuf);
|
|
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
@@ -45382,10 +45778,15 @@ static int winGetTempname(sqlite3_vfs *p
|
|
}
|
|
if( winIsDir(zConverted) ){
|
|
sqlite3_snprintf(nMax, zBuf, "%s", zDir);
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
break;
|
|
}
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
+#if 0 /* No longer necessary */
|
|
}else{
|
|
zConverted = sqlite3MallocZero( nMax+1 );
|
|
if( !zConverted ){
|
|
@@ -45420,13 +45821,16 @@ static int winGetTempname(sqlite3_vfs *p
|
|
break;
|
|
}
|
|
sqlite3_free(zConverted);
|
|
+#endif /* No longer necessary */
|
|
}
|
|
}
|
|
}
|
|
-#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
|
+#endif
|
|
+
|
|
+#if !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
|
else if( osIsNT() ){
|
|
char *zMulti;
|
|
- LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
|
|
+ LPWSTR zWidePath = sqlite3Malloc( nMax*sizeof(WCHAR) );
|
|
if( !zWidePath ){
|
|
sqlite3_free(zBuf);
|
|
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
@@ -45576,9 +45980,7 @@ static int winOpen(
|
|
DWORD dwShareMode;
|
|
DWORD dwCreationDisposition;
|
|
DWORD dwFlagsAndAttributes = 0;
|
|
-#if SQLITE_OS_WINCE
|
|
- int isTemp = 0;
|
|
-#endif
|
|
+ WCHAR buf[MAX_PATH];
|
|
winVfsAppData *pAppData;
|
|
winFile *pFile = (winFile*)id;
|
|
void *zConverted; /* Filename in OS encoding */
|
|
@@ -45670,7 +46072,11 @@ static int winOpen(
|
|
zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
|
|
|
|
/* Convert the filename to the system encoding. */
|
|
- zConverted = winConvertFromUtf8Filename(zUtf8Name);
|
|
+#if SQLITE_OS_WINCE
|
|
+ zConverted = winConvertFromUtf8Filename(zUtf8Name, isDelete ? 0 : buf );
|
|
+#else
|
|
+ zConverted = winConvertFromUtf8Filename(zUtf8Name, buf );
|
|
+#endif
|
|
if( zConverted==0 ){
|
|
sqlite3_free(zTmpname);
|
|
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
|
|
@@ -45678,7 +46084,9 @@ static int winOpen(
|
|
}
|
|
|
|
if( winIsDir(zConverted) ){
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
sqlite3_free(zTmpname);
|
|
OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
|
|
return SQLITE_CANTOPEN_ISDIR;
|
|
@@ -45711,7 +46119,6 @@ static int winOpen(
|
|
if( isDelete ){
|
|
#if SQLITE_OS_WINCE
|
|
dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
|
|
- isTemp = 1;
|
|
#else
|
|
dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
|
|
| FILE_ATTRIBUTE_HIDDEN
|
|
@@ -45796,7 +46203,9 @@ static int winOpen(
|
|
dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
|
|
|
|
if( h==INVALID_HANDLE_VALUE ){
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
sqlite3_free(zTmpname);
|
|
if( isReadWrite && !isExclusive ){
|
|
return winOpen(pVfs, zName, id,
|
|
@@ -45831,18 +46240,22 @@ static int winOpen(
|
|
&& (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
|
|
){
|
|
osCloseHandle(h);
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
sqlite3_free(zTmpname);
|
|
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
|
|
return rc;
|
|
}
|
|
}
|
|
- if( isTemp ){
|
|
+ if( isDelete ){
|
|
pFile->zDeleteOnClose = zConverted;
|
|
}else
|
|
#endif
|
|
{
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
}
|
|
|
|
sqlite3_free(zTmpname);
|
|
@@ -45890,13 +46303,14 @@ static int winDelete(
|
|
DWORD attr;
|
|
DWORD lastErrno = 0;
|
|
void *zConverted;
|
|
+ WCHAR buf[MAX_PATH];
|
|
UNUSED_PARAMETER(pVfs);
|
|
UNUSED_PARAMETER(syncDir);
|
|
|
|
SimulateIOError(return SQLITE_IOERR_DELETE);
|
|
OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
|
|
|
|
- zConverted = winConvertFromUtf8Filename(zFilename);
|
|
+ zConverted = winConvertFromUtf8Filename(zFilename, buf);
|
|
if( zConverted==0 ){
|
|
OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
@@ -45980,7 +46394,9 @@ static int winDelete(
|
|
}else{
|
|
winLogIoerr(cnt, __LINE__);
|
|
}
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted != buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
|
|
return rc;
|
|
}
|
|
@@ -45998,13 +46414,14 @@ static int winAccess(
|
|
int rc = 0;
|
|
DWORD lastErrno = 0;
|
|
void *zConverted;
|
|
+ WCHAR buf[MAX_PATH];
|
|
UNUSED_PARAMETER(pVfs);
|
|
|
|
SimulateIOError( return SQLITE_IOERR_ACCESS; );
|
|
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
|
|
zFilename, flags, pResOut));
|
|
|
|
- zConverted = winConvertFromUtf8Filename(zFilename);
|
|
+ zConverted = winConvertFromUtf8Filename(zFilename, buf);
|
|
if( zConverted==0 ){
|
|
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
@@ -46030,7 +46447,9 @@ static int winAccess(
|
|
}else{
|
|
winLogIoerr(cnt, __LINE__);
|
|
if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
|
|
zFilename);
|
|
}else{
|
|
@@ -46043,7 +46462,9 @@ static int winAccess(
|
|
attr = osGetFileAttributesA((char*)zConverted);
|
|
}
|
|
#endif
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
switch( flags ){
|
|
case SQLITE_ACCESS_READ:
|
|
case SQLITE_ACCESS_EXISTS:
|
|
@@ -46072,6 +46493,7 @@ static BOOL winIsDriveLetterAndColon(
|
|
return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
|
|
}
|
|
|
|
+#ifdef _WIN32
|
|
/*
|
|
** Returns non-zero if the specified path name should be used verbatim. If
|
|
** non-zero is returned from this function, the calling function must simply
|
|
@@ -46108,6 +46530,71 @@ static BOOL winIsVerbatimPathname(
|
|
*/
|
|
return FALSE;
|
|
}
|
|
+#endif /* _WIN32 */
|
|
+
|
|
+/*
|
|
+** Simplify a filename into its canonical form
|
|
+** by making the following changes:
|
|
+**
|
|
+** * convert any '/' to '\' (win32) or reverse (Cygwin)
|
|
+** * removing any trailing and duplicate / (except for UNC paths)
|
|
+** * convert /./ into just /
|
|
+**
|
|
+** Changes are made in-place. Return the new name length.
|
|
+**
|
|
+** The original filename is in z[0..]. If the path is shortened,
|
|
+** no-longer used bytes will be written by '\0'.
|
|
+*/
|
|
+static void winSimplifyName(char *z){
|
|
+ int i, j;
|
|
+ for(i=j=0; z[i]; ++i){
|
|
+ if( winIsDirSep(z[i]) ){
|
|
+#if !defined(SQLITE_TEST)
|
|
+ /* Some test-cases assume that "./foo" and "foo" are different */
|
|
+ if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){
|
|
+ ++i;
|
|
+ continue;
|
|
+ }
|
|
+#endif
|
|
+ if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){
|
|
+ continue;
|
|
+ }
|
|
+ z[j++] = osGetenv?'/':'\\';
|
|
+ }else{
|
|
+ z[j++] = z[i];
|
|
+ }
|
|
+ }
|
|
+ while(j<i) z[j++] = '\0';
|
|
+}
|
|
+
|
|
+#if !SQLITE_OS_UNIX
|
|
+
|
|
+#define SQLITE_MAX_SYMLINKS 100
|
|
+
|
|
+static int mkFullPathname(
|
|
+ const char *zPath, /* Input path */
|
|
+ char *zOut, /* Output buffer */
|
|
+ int nOut /* Allocated size of buffer zOut */
|
|
+){
|
|
+ int nPath = sqlite3Strlen30(zPath);
|
|
+ int iOff = 0;
|
|
+ if( zPath[0]!='/' ){
|
|
+ if( getcwd(zOut, nOut-2)==0 ){
|
|
+ return winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "getcwd", zPath);
|
|
+ }
|
|
+ iOff = sqlite3Strlen30(zOut);
|
|
+ zOut[iOff++] = '/';
|
|
+ }
|
|
+ if( (iOff+nPath+1)>nOut ){
|
|
+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer
|
|
+ ** even if it returns an error. */
|
|
+ zOut[iOff] = '\0';
|
|
+ return SQLITE_CANTOPEN_BKPT;
|
|
+ }
|
|
+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#endif
|
|
|
|
/*
|
|
** Turn a relative pathname into a full pathname. Write the full
|
|
@@ -46120,8 +46607,9 @@ static int winFullPathname(
|
|
int nFull, /* Size of output buffer in bytes */
|
|
char *zFull /* Output buffer */
|
|
){
|
|
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
|
- DWORD nByte;
|
|
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
|
+ WCHAR buf[MAX_PATH];
|
|
+ int nByte;
|
|
void *zConverted;
|
|
char *zOut;
|
|
#endif
|
|
@@ -46133,7 +46621,85 @@ static int winFullPathname(
|
|
zRelative++;
|
|
}
|
|
|
|
-#if defined(__CYGWIN__)
|
|
+ SimulateIOError( return SQLITE_ERROR );
|
|
+
|
|
+ if( getcwd ){
|
|
+ zFull[nFull-1] = '\0';
|
|
+ if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){
|
|
+ int rc = SQLITE_OK;
|
|
+ int nByte;
|
|
+ int nLink = 1; /* Number of symbolic links followed so far */
|
|
+ const char *zIn = zRelative; /* Input path for each iteration of loop */
|
|
+ char *zDel = 0;
|
|
+ struct cygwinstat buf;
|
|
+
|
|
+#ifdef __CYGWIN__ /* Make sure that "struct cygwinstat" corresponds with real struct */
|
|
+ assert( sizeof(buf)==sizeof(struct stat));
|
|
+#endif
|
|
+ UNUSED_PARAMETER(pVfs);
|
|
+
|
|
+ do {
|
|
+ /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic
|
|
+ ** link, or false otherwise. */
|
|
+ int bLink = 0;
|
|
+ if( lstat && readlink ) {
|
|
+ if( lstat(zIn, &buf)!=0 ){
|
|
+ int myErrno = osErrno;
|
|
+ if( myErrno!=ENOENT ){
|
|
+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn);
|
|
+ }
|
|
+ }else{
|
|
+ bLink = ((buf.st_mode & 0170000) == 0120000);
|
|
+ }
|
|
+
|
|
+ if( bLink ){
|
|
+ if( zDel==0 ){
|
|
+ zDel = sqlite3Malloc(nFull);
|
|
+ if( zDel==0 ) rc = SQLITE_NOMEM;
|
|
+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
|
|
+ rc = SQLITE_CANTOPEN_BKPT;
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ nByte = readlink(zIn, zDel, nFull-1);
|
|
+ if( nByte<0 ){
|
|
+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn);
|
|
+ }else{
|
|
+ if( zDel[0]!='/' ){
|
|
+ int n;
|
|
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
|
|
+ if( nByte+n+1>nFull ){
|
|
+ rc = SQLITE_CANTOPEN_BKPT;
|
|
+ }else{
|
|
+ memmove(&zDel[n], zDel, nByte+1);
|
|
+ memcpy(zDel, zIn, n);
|
|
+ nByte += n;
|
|
+ }
|
|
+ }
|
|
+ zDel[nByte] = '\0';
|
|
+ }
|
|
+ }
|
|
+
|
|
+ zIn = zDel;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ assert( rc!=SQLITE_OK || zIn!=zFull || zIn[0]=='/' );
|
|
+ if( rc==SQLITE_OK && zIn!=zFull ){
|
|
+ rc = mkFullPathname(zIn, zFull, nFull);
|
|
+ }
|
|
+ if( bLink==0 ) break;
|
|
+ zIn = zFull;
|
|
+ }while( rc==SQLITE_OK );
|
|
+
|
|
+ sqlite3_free(zDel);
|
|
+ winSimplifyName(zFull);
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+#if 0 /* This doesn't work correctly at all! See:
|
|
+ <http://sqlite.1065341.n5.nabble.com/Wrong-filename-handling-in-sqlite3-load-extension-for-Cygwin-td74049.html>
|
|
+*/
|
|
SimulateIOError( return SQLITE_ERROR );
|
|
UNUSED_PARAMETER(nFull);
|
|
assert( nFull>=pVfs->mxPathname );
|
|
@@ -46144,7 +46710,7 @@ static int winFullPathname(
|
|
** for converting the relative path name to an absolute
|
|
** one by prepending the data directory and a slash.
|
|
*/
|
|
- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
|
|
+ char *zOut = sqlite3Malloc( pVfs->mxPathname+1 );
|
|
if( !zOut ){
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
}
|
|
@@ -46166,7 +46732,7 @@ static int winFullPathname(
|
|
sqlite3_free(zOut);
|
|
}
|
|
}else{
|
|
- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
|
|
+ char *zOut = sqlite3Malloc( pVfs->mxPathname+1 );
|
|
if( !zOut ){
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
}
|
|
@@ -46191,7 +46757,6 @@ static int winFullPathname(
|
|
#endif
|
|
|
|
#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
|
|
- SimulateIOError( return SQLITE_ERROR );
|
|
/* WinCE has no concept of a relative pathname, or so I am told. */
|
|
/* WinRT has no way to convert a relative path to an absolute one. */
|
|
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
|
@@ -46209,13 +46774,8 @@ static int winFullPathname(
|
|
return SQLITE_OK;
|
|
#endif
|
|
|
|
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
|
- /* It's odd to simulate an io-error here, but really this is just
|
|
- ** using the io-error infrastructure to test that SQLite handles this
|
|
- ** function failing. This function could fail if, for example, the
|
|
- ** current working directory has been unlinked.
|
|
- */
|
|
- SimulateIOError( return SQLITE_ERROR );
|
|
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
|
+#if defined(_WIN32)
|
|
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
|
/*
|
|
** NOTE: We are dealing with a relative path name and the data
|
|
@@ -46227,7 +46787,8 @@ static int winFullPathname(
|
|
sqlite3_data_directory, winGetDirSep(), zRelative);
|
|
return SQLITE_OK;
|
|
}
|
|
- zConverted = winConvertFromUtf8Filename(zRelative);
|
|
+#endif
|
|
+ zConverted = winConvertFromUtf8Filename(zRelative, buf);
|
|
if( zConverted==0 ){
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
}
|
|
@@ -46235,24 +46796,32 @@ static int winFullPathname(
|
|
LPWSTR zTemp;
|
|
nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
|
|
if( nByte==0 ){
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted != buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
|
"winFullPathname1", zRelative);
|
|
}
|
|
nByte += 3;
|
|
- zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
|
|
+ zTemp = sqlite3Malloc( nByte*sizeof(zTemp[0]) );
|
|
if( zTemp==0 ){
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted != buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
}
|
|
nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
|
|
if( nByte==0 ){
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted != buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
sqlite3_free(zTemp);
|
|
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
|
"winFullPathname2", zRelative);
|
|
}
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted != buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
zOut = winUnicodeToUtf8(zTemp);
|
|
sqlite3_free(zTemp);
|
|
}
|
|
@@ -46284,7 +46853,22 @@ static int winFullPathname(
|
|
}
|
|
#endif
|
|
if( zOut ){
|
|
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
|
|
+ if( memcmp(zOut, "\\\\?\\", 4) ){
|
|
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
|
|
+ }else if( memcmp(zOut+4, "UNC\\", 4) ){
|
|
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4);
|
|
+ }else{
|
|
+ char *p = zOut+6;
|
|
+ *p = '\\';
|
|
+ if( getcwd ){
|
|
+ /* On Cygwin, UNC paths use forward slashes */
|
|
+ while( *p ){
|
|
+ if( *p=='\\' ) *p = '/';
|
|
+ ++p;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6);
|
|
+ }
|
|
sqlite3_free(zOut);
|
|
return SQLITE_OK;
|
|
}else{
|
|
@@ -46300,9 +46884,11 @@ static int winFullPathname(
|
|
*/
|
|
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
|
|
HANDLE h;
|
|
-#if defined(__CYGWIN__)
|
|
+#if 0 /* This doesn't work correctly at all! See:
|
|
+ <http://sqlite.1065341.n5.nabble.com/Wrong-filename-handling-in-sqlite3-load-extension-for-Cygwin-td74049.html>
|
|
+*/
|
|
int nFull = pVfs->mxPathname+1;
|
|
- char *zFull = sqlite3MallocZero( nFull );
|
|
+ char *zFull = sqlite3Malloc( nFull );
|
|
void *zConverted = 0;
|
|
if( zFull==0 ){
|
|
OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
|
|
@@ -46316,7 +46902,8 @@ static void *winDlOpen(sqlite3_vfs *pVfs
|
|
zConverted = winConvertFromUtf8Filename(zFull);
|
|
sqlite3_free(zFull);
|
|
#else
|
|
- void *zConverted = winConvertFromUtf8Filename(zFilename);
|
|
+ WCHAR buf[MAX_PATH];
|
|
+ void *zConverted = winConvertFromUtf8Filename(zFilename, buf);
|
|
UNUSED_PARAMETER(pVfs);
|
|
#endif
|
|
if( zConverted==0 ){
|
|
@@ -46336,7 +46923,9 @@ static void *winDlOpen(sqlite3_vfs *pVfs
|
|
}
|
|
#endif
|
|
OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h));
|
|
- sqlite3_free(zConverted);
|
|
+ if( zConverted!=buf ){
|
|
+ sqlite3_free(zConverted);
|
|
+ }
|
|
return (void*)h;
|
|
}
|
|
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
|
|
@@ -46426,15 +47015,31 @@ static int winRandomness(sqlite3_vfs *pV
|
|
osQueryPerformanceCounter(&i);
|
|
xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER));
|
|
}
|
|
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
|
|
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
|
{
|
|
UUID id;
|
|
- memset(&id, 0, sizeof(UUID));
|
|
- osUuidCreate(&id);
|
|
- xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
|
|
- memset(&id, 0, sizeof(UUID));
|
|
- osUuidCreateSequential(&id);
|
|
- xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
|
|
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_WIN32_USE_UUID)
|
|
+ static HMODULE module = 0;
|
|
+ if( !module && module != (HMODULE)-1 ){
|
|
+ module = osLoadLibraryW(L"RPCRT4.DLL");
|
|
+ if( !module ){
|
|
+ module = (HMODULE)-1;
|
|
+ }else{
|
|
+ aSyscall[77].pCurrent = (SYSCALL) osGetProcAddressA(module, "UuidCreate");
|
|
+ aSyscall[78].pCurrent = (SYSCALL) osGetProcAddressA(module, "UuidCreateSequential");
|
|
+ }
|
|
+ }
|
|
+#endif /* !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
|
+ if( osUuidCreate ){
|
|
+ memset(&id, 0, sizeof(UUID));
|
|
+ osUuidCreate(&id);
|
|
+ xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
|
|
+ }
|
|
+ if( osUuidCreateSequential ){
|
|
+ memset(&id, 0, sizeof(UUID));
|
|
+ osUuidCreateSequential(&id);
|
|
+ xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
|
|
+ }
|
|
}
|
|
#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */
|
|
return e.nXor>nBuf ? nBuf : e.nXor;
|
|
@@ -46560,6 +47165,10 @@ static int winGetLastError(sqlite3_vfs *
|
|
return e;
|
|
}
|
|
|
|
+#if SQLITE_OS_UNIX && !defined(SQLITE_AMALGAMATION)
|
|
+SQLITE_API int sqlite3_os_unix_init(void);
|
|
+#endif
|
|
+
|
|
/*
|
|
** Initialize and deinitialize the operating system interface.
|
|
*/
|
|
@@ -46638,6 +47247,30 @@ SQLITE_API int sqlite3_os_init(void){
|
|
winGetSystemCall, /* xGetSystemCall */
|
|
winNextSystemCall, /* xNextSystemCall */
|
|
};
|
|
+ static sqlite3_vfs winNolockVfs1 = {
|
|
+ 3, /* iVersion */
|
|
+ sizeof(winFile), /* szOsFile */
|
|
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ "none", /* zName */
|
|
+ &winNolockAppData, /* pAppData */
|
|
+ winOpen, /* xOpen */
|
|
+ winDelete, /* xDelete */
|
|
+ winAccess, /* xAccess */
|
|
+ winFullPathname, /* xFullPathname */
|
|
+ winDlOpen, /* xDlOpen */
|
|
+ winDlError, /* xDlError */
|
|
+ winDlSym, /* xDlSym */
|
|
+ winDlClose, /* xDlClose */
|
|
+ winRandomness, /* xRandomness */
|
|
+ winSleep, /* xSleep */
|
|
+ winCurrentTime, /* xCurrentTime */
|
|
+ winGetLastError, /* xGetLastError */
|
|
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
|
+ winSetSystemCall, /* xSetSystemCall */
|
|
+ winGetSystemCall, /* xGetSystemCall */
|
|
+ winNextSystemCall, /* xNextSystemCall */
|
|
+ };
|
|
#if defined(SQLITE_WIN32_HAS_WIDE)
|
|
static sqlite3_vfs winLongPathNolockVfs = {
|
|
3, /* iVersion */
|
|
@@ -46663,11 +47296,15 @@ SQLITE_API int sqlite3_os_init(void){
|
|
winGetSystemCall, /* xGetSystemCall */
|
|
winNextSystemCall, /* xNextSystemCall */
|
|
};
|
|
+#ifdef _WIN32
|
|
+ int i;
|
|
+ HMODULE module;
|
|
+#endif
|
|
#endif
|
|
|
|
/* Double-check that the aSyscall[] array has been constructed
|
|
** correctly. See ticket [bb3a86e890c8e96ab] */
|
|
- assert( ArraySize(aSyscall)==80 );
|
|
+ assert( ArraySize(aSyscall)==88 );
|
|
|
|
/* get memory map allocation granularity */
|
|
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
|
|
@@ -46679,13 +47316,52 @@ SQLITE_API int sqlite3_os_init(void){
|
|
assert( winSysInfo.dwAllocationGranularity>0 );
|
|
assert( winSysInfo.dwPageSize>0 );
|
|
|
|
+#ifdef _WIN32
|
|
+ module = osGetModuleHandleW(L"CYGWIN1.DLL");
|
|
+ if( !module ){
|
|
+ module = osGetModuleHandleW(L"MSYS-2.0.DLL");
|
|
+ }
|
|
+ if( !module ){
|
|
+ module = osGetModuleHandleW(L"MSYS-1.0.DLL");
|
|
+ }
|
|
+ if( module ){
|
|
+ for( i=81; i<ArraySize(aSyscall); ++i ){
|
|
+ aSyscall[i].pCurrent = (SYSCALL) osGetProcAddressA(module,
|
|
+ aSyscall[i].zName);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+#if SQLITE_OS_UNIX
|
|
+ sqlite3_os_unix_init();
|
|
+#endif
|
|
+
|
|
sqlite3_vfs_register(&winVfs, 1);
|
|
|
|
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
+ if( cygwin_conv_path ){
|
|
+ WCHAR buf[MAX_PATH];
|
|
+ cygwin_conv_path(CCP_POSIX_TO_WIN_W, "/usr/bin",
|
|
+ buf, MAX_PATH*sizeof(WCHAR));
|
|
+ osSetDllDirectoryW(buf);
|
|
+#ifdef _WIN32
|
|
+ }else if( cygwin_conv_to_full_win32_path ){
|
|
+ WCHAR buf[MAX_PATH];
|
|
+ char *buf1 = (char *)buf;
|
|
+ int i = MAX_PATH;
|
|
+ cygwin_conv_to_full_win32_path("/usr/bin", buf1);
|
|
+ while(--i>=0) buf[i] = buf1[i];
|
|
+ osSetDllDirectoryW(buf);
|
|
+#endif
|
|
+ }
|
|
+#endif
|
|
+
|
|
#if defined(SQLITE_WIN32_HAS_WIDE)
|
|
sqlite3_vfs_register(&winLongPathVfs, 0);
|
|
#endif
|
|
|
|
sqlite3_vfs_register(&winNolockVfs, 0);
|
|
+ sqlite3_vfs_register(&winNolockVfs1, 0);
|
|
|
|
#if defined(SQLITE_WIN32_HAS_WIDE)
|
|
sqlite3_vfs_register(&winLongPathNolockVfs, 0);
|
|
@@ -46699,6 +47375,19 @@ SQLITE_API int sqlite3_os_init(void){
|
|
}
|
|
|
|
SQLITE_API int sqlite3_os_end(void){
|
|
+#ifdef _WIN32
|
|
+ int i;
|
|
+#endif
|
|
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
+ if( cygwin_conv_path || cygwin_conv_to_full_win32_path){
|
|
+ osSetDllDirectoryW(0);
|
|
+ }
|
|
+#endif
|
|
+#ifdef _WIN32
|
|
+ for( i=81; i<ArraySize(aSyscall); ++i ){
|
|
+ aSyscall[i].pCurrent = 0;
|
|
+ }
|
|
+#endif
|
|
#if SQLITE_OS_WINRT
|
|
if( sleepObj!=NULL ){
|
|
osCloseHandle(sleepObj);
|
|
@@ -47690,7 +48379,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinT
|
|
** bits to act as the reference */
|
|
pBitvec = sqlite3BitvecCreate( sz );
|
|
pV = sqlite3MallocZero( (sz+7)/8 + 1 );
|
|
- pTmpSpace = sqlite3_malloc64(BITVEC_SZ);
|
|
+ pTmpSpace = sqlite3Malloc(BITVEC_SZ);
|
|
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
|
|
|
|
/* NULL pBitvec tests */
|
|
@@ -48812,8 +49501,8 @@ struct PCache1 {
|
|
*/
|
|
PGroup *pGroup; /* PGroup this cache belongs to */
|
|
unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */
|
|
- int szPage; /* Size of database content section */
|
|
- int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
|
|
+ size_t szPage; /* Size of database content section */
|
|
+ size_t szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
|
|
int szAlloc; /* Total size of one pcache line */
|
|
int bPurgeable; /* True if cache is purgeable */
|
|
unsigned int nMin; /* Minimum number of pages reserved */
|
|
@@ -48855,7 +49544,7 @@ static SQLITE_WSD struct PCacheGlobal {
|
|
int isInit; /* True if initialized */
|
|
int separateCache; /* Use a new PGroup for each PCache */
|
|
int nInitPage; /* Initial bulk allocation size */
|
|
- int szSlot; /* Size of each free slot */
|
|
+ size_t szSlot; /* Size of each free slot */
|
|
int nSlot; /* The number of pcache slots */
|
|
int nReserve; /* Try to keep nFreeSlot above this */
|
|
void *pStart, *pEnd; /* Bounds of global page cache memory */
|
|
@@ -48903,7 +49592,7 @@ static SQLITE_WSD struct PCacheGlobal {
|
|
** This routine is called from sqlite3_initialize() and so it is guaranteed
|
|
** to be serialized already. There is no need for further mutexing.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
|
+SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, size_t sz, int n){
|
|
if( pcache1.isInit ){
|
|
PgFreeslot *p;
|
|
if( pBuf==0 ) sz = n = 0;
|
|
@@ -48972,7 +49661,7 @@ static int pcache1InitBulk(PCache1 *pCac
|
|
** Multiple threads can run this routine at the same time. Global variables
|
|
** in pcache1 need to be protected via mutex.
|
|
*/
|
|
-static void *pcache1Alloc(int nByte){
|
|
+static void *pcache1Alloc(size_t nByte){
|
|
void *p = 0;
|
|
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
|
|
if( nByte<=pcache1.szSlot ){
|
|
@@ -48995,7 +49684,7 @@ static void *pcache1Alloc(int nByte){
|
|
p = sqlite3Malloc(nByte);
|
|
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
|
|
if( p ){
|
|
- int sz = sqlite3MallocSize(p);
|
|
+ size_t sz = sqlite3MallocSize(p);
|
|
sqlite3_mutex_enter(pcache1.mutex);
|
|
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
|
|
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
|
|
@@ -49043,11 +49732,11 @@ static void pcache1Free(void *p){
|
|
/*
|
|
** Return the size of a pcache allocation
|
|
*/
|
|
-static int pcache1MemSize(void *p){
|
|
+static size_t pcache1MemSize(void *p){
|
|
if( p>=pcache1.pStart && p<pcache1.pEnd ){
|
|
return pcache1.szSlot;
|
|
}else{
|
|
- int iSize;
|
|
+ size_t iSize;
|
|
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
|
|
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
iSize = sqlite3MallocSize(p);
|
|
@@ -49130,7 +49819,7 @@ static void pcache1FreePage(PgHdr1 *p){
|
|
** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
|
|
** exists, this function falls back to sqlite3Malloc().
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){
|
|
+SQLITE_PRIVATE void *sqlite3PageMalloc(size_t sz){
|
|
assert( sz<=65536+8 ); /* These allocations are never very large */
|
|
return pcache1Alloc(sz);
|
|
}
|
|
@@ -49866,14 +50555,14 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3Pca
|
|
** been released, the function returns. The return value is the total number
|
|
** of bytes of memory released.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
|
|
- int nFree = 0;
|
|
+SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(size_t nReq){
|
|
+ size_t nFree = 0;
|
|
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
|
|
assert( sqlite3_mutex_notheld(pcache1.mutex) );
|
|
if( sqlite3GlobalConfig.pPage==0 ){
|
|
PgHdr1 *p;
|
|
pcache1EnterMutex(&pcache1.grp);
|
|
- while( (nReq<0 || nFree<nReq)
|
|
+ while( (nFree<nReq)
|
|
&& (p=pcache1.grp.lru.pLruPrev)!=0
|
|
&& p->isAnchor==0
|
|
){
|
|
@@ -58889,9 +59578,9 @@ static SQLITE_NOINLINE int walIndexPageR
|
|
|
|
/* Enlarge the pWal->apWiData[] array if required */
|
|
if( pWal->nWiData<=iPage ){
|
|
- sqlite3_int64 nByte = sizeof(u32*)*(iPage+1);
|
|
+ size_t nByte = sizeof(u32*)*(iPage+1);
|
|
volatile u32 **apNew;
|
|
- apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte);
|
|
+ apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte);
|
|
if( !apNew ){
|
|
*ppPage = 0;
|
|
return SQLITE_NOMEM_BKPT;
|
|
@@ -59542,7 +60231,7 @@ static int walIndexRecover(Wal *pWal){
|
|
|
|
/* Malloc a buffer to read frames into. */
|
|
szFrame = szPage + WAL_FRAME_HDRSIZE;
|
|
- aFrame = (u8 *)sqlite3_malloc64(szFrame);
|
|
+ aFrame = (u8 *)sqlite3Malloc(szFrame);
|
|
if( !aFrame ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto recovery_error;
|
|
@@ -59947,7 +60636,7 @@ static int walIteratorInit(Wal *pWal, u3
|
|
nByte = sizeof(WalIterator)
|
|
+ (nSegment-1)*sizeof(struct WalSegment)
|
|
+ iLast*sizeof(ht_slot);
|
|
- p = (WalIterator *)sqlite3_malloc64(nByte);
|
|
+ p = (WalIterator *)sqlite3Malloc(nByte);
|
|
if( !p ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -59957,7 +60646,7 @@ static int walIteratorInit(Wal *pWal, u3
|
|
/* Allocate temporary space used by the merge-sort routine. This block
|
|
** of memory will be freed before this function returns.
|
|
*/
|
|
- aTmp = (ht_slot *)sqlite3_malloc64(
|
|
+ aTmp = (ht_slot *)sqlite3Malloc(
|
|
sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
|
|
);
|
|
if( !aTmp ){
|
|
@@ -61580,7 +62269,7 @@ static int walRewriteChecksums(Wal *pWal
|
|
u32 iRead; /* Next frame to read from wal file */
|
|
i64 iCksumOff;
|
|
|
|
- aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE);
|
|
+ aBuf = sqlite3Malloc(szPage + WAL_FRAME_HDRSIZE);
|
|
if( aBuf==0 ) return SQLITE_NOMEM_BKPT;
|
|
|
|
/* Find the checksum values to use as input for the recalculating the
|
|
@@ -62066,7 +62755,7 @@ SQLITE_PRIVATE int sqlite3WalSnapshotGet
|
|
*ppSnapshot = 0;
|
|
return SQLITE_ERROR;
|
|
}
|
|
- pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr));
|
|
+ pRet = (WalIndexHdr*)sqlite3Malloc(sizeof(WalIndexHdr));
|
|
if( pRet==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
@@ -67975,7 +68664,7 @@ static int accessPayload(
|
|
if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){
|
|
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
|
|
if( pCur->aOverflow==0
|
|
- || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
|
|
+ || nOvfl*sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
|
|
){
|
|
Pgno *aNew = (Pgno*)sqlite3Realloc(
|
|
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
|
@@ -68483,6 +69172,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCu
|
|
assert( pCur->ix==pCur->pPage->nCell-1 );
|
|
assert( pCur->pPage->leaf );
|
|
#endif
|
|
+ *pRes = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -72330,7 +73020,7 @@ static int clearDatabasePage(
|
|
BtShared *pBt, /* The BTree that contains the table */
|
|
Pgno pgno, /* Page number to clear */
|
|
int freePageFlag, /* Deallocate page if true */
|
|
- int *pnChange /* Add number of Cells freed to this counter */
|
|
+ u64 *pnChange /* Add number of Cells freed to this counter */
|
|
){
|
|
MemPage *pPage;
|
|
int rc;
|
|
@@ -72393,7 +73083,7 @@ cleardatabasepage_out:
|
|
** integer value pointed to by pnChange is incremented by the number of
|
|
** entries in the table.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
|
|
+SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, u64 *pnChange){
|
|
int rc;
|
|
BtShared *pBt = p->pBt;
|
|
sqlite3BtreeEnter(p);
|
|
@@ -74439,6 +75129,11 @@ copy_finished:
|
|
/* #include "sqliteInt.h" */
|
|
/* #include "vdbeInt.h" */
|
|
|
|
+/* True if X is a power of two. 0 is considered a power of two here.
|
|
+** In other words, return true if X has at most one bit set.
|
|
+*/
|
|
+#define ISPOWEROF2(X) (((X)&((X)-1))==0)
|
|
+
|
|
#ifdef SQLITE_DEBUG
|
|
/*
|
|
** Check invariants on a Mem object.
|
|
@@ -74458,8 +75153,8 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemIn
|
|
** That saves a few cycles in inner loops. */
|
|
assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 );
|
|
|
|
- /* Cannot be both MEM_Int and MEM_Real at the same time */
|
|
- assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) );
|
|
+ /* Cannot have more than one of MEM_Int, MEM_Real, or MEM_IntReal */
|
|
+ assert( ISPOWEROF2(p->flags & (MEM_Int|MEM_Real|MEM_IntReal)) );
|
|
|
|
if( p->flags & MEM_Null ){
|
|
/* Cannot be both MEM_Null and some other type */
|
|
@@ -74513,6 +75208,25 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemIn
|
|
}
|
|
#endif
|
|
|
|
+/*
|
|
+** Render a Mem object which is one of MEM_Int, MEM_Real, or MEM_IntReal
|
|
+** into a buffer.
|
|
+*/
|
|
+static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
|
|
+ StrAccum acc;
|
|
+ assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) );
|
|
+ sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0);
|
|
+ if( p->flags & MEM_Int ){
|
|
+ sqlite3_str_appendf(&acc, "%lld", p->u.i);
|
|
+ }else if( p->flags & MEM_IntReal ){
|
|
+ sqlite3_str_appendf(&acc, "%!.15g", (double)p->u.i);
|
|
+ }else{
|
|
+ sqlite3_str_appendf(&acc, "%!.15g", p->u.r);
|
|
+ }
|
|
+ assert( acc.zText==zBuf && acc.mxAlloc<=0 );
|
|
+ zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */
|
|
+}
|
|
+
|
|
#ifdef SQLITE_DEBUG
|
|
/*
|
|
** Check that string value of pMem agrees with its integer or real value.
|
|
@@ -74538,12 +75252,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemConsist
|
|
char *z;
|
|
int i, j, incr;
|
|
if( (p->flags & MEM_Str)==0 ) return 1;
|
|
- if( (p->flags & (MEM_Int|MEM_Real))==0 ) return 1;
|
|
- if( p->flags & MEM_Int ){
|
|
- sqlite3_snprintf(sizeof(zBuf),zBuf,"%lld",p->u.i);
|
|
- }else{
|
|
- sqlite3_snprintf(sizeof(zBuf),zBuf,"%!.15g",p->u.r);
|
|
- }
|
|
+ if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1;
|
|
+ vdbeMemRenderNum(sizeof(zBuf), zBuf, p);
|
|
z = p->z;
|
|
i = j = 0;
|
|
incr = 1;
|
|
@@ -74606,7 +75316,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEnco
|
|
** blob if bPreserve is true. If bPreserve is false, any prior content
|
|
** in pMem->z is discarded.
|
|
*/
|
|
-SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
|
|
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, size_t n, int bPreserve){
|
|
assert( sqlite3VdbeCheckMemInvariants(pMem) );
|
|
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
testcase( pMem->db==0 );
|
|
@@ -74655,13 +75365,13 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlit
|
|
**
|
|
** Any prior string or blob content in the pMem object may be discarded.
|
|
** The pMem->xDel destructor is called, if it exists. Though MEM_Str
|
|
-** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, and MEM_Null
|
|
-** values are preserved.
|
|
+** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, MEM_IntReal,
|
|
+** and MEM_Null values are preserved.
|
|
**
|
|
** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM)
|
|
** if unable to complete the resizing.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, size_t szNew){
|
|
assert( CORRUPT_DB || szNew>0 );
|
|
assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 );
|
|
if( pMem->szMalloc<szNew ){
|
|
@@ -74669,7 +75379,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAn
|
|
}
|
|
assert( (pMem->flags & MEM_Dyn)==0 );
|
|
pMem->z = pMem->zMalloc;
|
|
- pMem->flags &= (MEM_Null|MEM_Int|MEM_Real);
|
|
+ pMem->flags &= (MEM_Null|MEM_Int|MEM_Real|MEM_IntReal);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -74760,8 +75470,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerm
|
|
** are converted using sqlite3_snprintf(). Converting a BLOB to a string
|
|
** is a no-op.
|
|
**
|
|
-** Existing representations MEM_Int and MEM_Real are invalidated if
|
|
-** bForce is true but are retained if bForce is false.
|
|
+** Existing representations MEM_Int, MEM_Real, or MEM_IntReal are invalidated
|
|
+** if bForce is true but are retained if bForce is false.
|
|
**
|
|
** A MEM_Null value will never be passed to this function. This function is
|
|
** used for converting values to text for returning to the user (i.e. via
|
|
@@ -74770,13 +75480,12 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerm
|
|
** user and the latter is an internal programming error.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
|
|
- int fg = pMem->flags;
|
|
const int nByte = 32;
|
|
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
- assert( !(fg&MEM_Zero) );
|
|
- assert( !(fg&(MEM_Str|MEM_Blob)) );
|
|
- assert( fg&(MEM_Int|MEM_Real) );
|
|
+ assert( !(pMem->flags&MEM_Zero) );
|
|
+ assert( !(pMem->flags&(MEM_Str|MEM_Blob)) );
|
|
+ assert( pMem->flags&(MEM_Int|MEM_Real|MEM_IntReal) );
|
|
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
|
|
|
@@ -74786,23 +75495,12 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringi
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
|
|
- /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
|
|
- ** string representation of the value. Then, if the required encoding
|
|
- ** is UTF-16le or UTF-16be do a translation.
|
|
- **
|
|
- ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16.
|
|
- */
|
|
- if( fg & MEM_Int ){
|
|
- sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
|
|
- }else{
|
|
- assert( fg & MEM_Real );
|
|
- sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
|
|
- }
|
|
+ vdbeMemRenderNum(nByte, pMem->z, pMem);
|
|
assert( pMem->z!=0 );
|
|
pMem->n = sqlite3Strlen30NN(pMem->z);
|
|
pMem->enc = SQLITE_UTF8;
|
|
pMem->flags |= MEM_Str|MEM_Term;
|
|
- if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
|
|
+ if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
|
|
sqlite3VdbeChangeEncoding(pMem, enc);
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -74976,7 +75674,8 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(M
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
|
flags = pMem->flags;
|
|
- if( flags & MEM_Int ){
|
|
+ if( flags & (MEM_Int|MEM_IntReal) ){
|
|
+ testcase( flags & MEM_IntReal );
|
|
return pMem->u.i;
|
|
}else if( flags & MEM_Real ){
|
|
return doubleToInt64(pMem->u.r);
|
|
@@ -75005,7 +75704,8 @@ SQLITE_PRIVATE double sqlite3VdbeRealVal
|
|
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
|
if( pMem->flags & MEM_Real ){
|
|
return pMem->u.r;
|
|
- }else if( pMem->flags & MEM_Int ){
|
|
+ }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){
|
|
+ testcase( pMem->flags & MEM_IntReal );
|
|
return (double)pMem->u.i;
|
|
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
|
|
return memRealValue(pMem);
|
|
@@ -75020,7 +75720,8 @@ SQLITE_PRIVATE double sqlite3VdbeRealVal
|
|
** Return the value ifNull if pMem is NULL.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){
|
|
- if( pMem->flags & MEM_Int ) return pMem->u.i!=0;
|
|
+ testcase( pMem->flags & MEM_IntReal );
|
|
+ if( pMem->flags & (MEM_Int|MEM_IntReal) ) return pMem->u.i!=0;
|
|
if( pMem->flags & MEM_Null ) return ifNull;
|
|
return sqlite3VdbeRealValue(pMem)!=0.0;
|
|
}
|
|
@@ -75093,7 +75794,7 @@ static int sqlite3RealSameAsInt(double r
|
|
}
|
|
|
|
/*
|
|
-** Convert pMem so that it has types MEM_Real or MEM_Int or both.
|
|
+** Convert pMem so that it has type MEM_Real or MEM_Int.
|
|
** Invalidate any prior representations.
|
|
**
|
|
** Every effort is made to force the conversion, even if the input
|
|
@@ -75101,7 +75802,11 @@ static int sqlite3RealSameAsInt(double r
|
|
** as much of the string as we can and ignore the rest.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
|
|
- if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
|
|
+ testcase( pMem->flags & MEM_Int );
|
|
+ testcase( pMem->flags & MEM_Real );
|
|
+ testcase( pMem->flags & MEM_IntReal );
|
|
+ testcase( pMem->flags & MEM_Null );
|
|
+ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))==0 ){
|
|
int rc;
|
|
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
@@ -75119,7 +75824,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerif
|
|
}
|
|
}
|
|
}
|
|
- assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
|
|
+ assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))!=0 );
|
|
pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero);
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -75162,7 +75867,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemCast(M
|
|
pMem->flags |= (pMem->flags&MEM_Blob)>>3;
|
|
sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
|
|
assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
|
|
- pMem->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero);
|
|
+ pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero);
|
|
break;
|
|
}
|
|
}
|
|
@@ -75208,11 +75913,11 @@ SQLITE_PRIVATE void sqlite3ValueSetNull(
|
|
** Delete any previous value and set the value to be a BLOB of length
|
|
** n containing all zeros.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
|
|
+SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, size_t n){
|
|
sqlite3VdbeMemRelease(pMem);
|
|
pMem->flags = MEM_Blob|MEM_Zero;
|
|
pMem->n = 0;
|
|
- if( n<0 ) n = 0;
|
|
+ if( (int)n<0 ) n = 0;
|
|
pMem->u.nZero = n;
|
|
pMem->enc = SQLITE_UTF8;
|
|
pMem->z = 0;
|
|
@@ -75346,7 +76051,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutT
|
|
** dual type, are allowed, as long as the underlying value is the
|
|
** same. */
|
|
u16 mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
|
|
- assert( (mFlags&MEM_Int)==0 || pMem->u.i==pX->u.i );
|
|
+ assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i );
|
|
assert( (mFlags&MEM_Real)==0 || pMem->u.r==pX->u.r );
|
|
assert( (mFlags&MEM_Str)==0 || (pMem->n==pX->n && pMem->z==pX->z) );
|
|
assert( (mFlags&MEM_Blob)==0 || sqlite3BlobCompare(pMem,pX)==0 );
|
|
@@ -75441,7 +76146,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(M
|
|
SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
|
|
Mem *pMem, /* Memory cell to set to string value */
|
|
const char *z, /* String pointer */
|
|
- int n, /* Bytes in string, or negative */
|
|
+ size_t n, /* Bytes in string, or negative */
|
|
u8 enc, /* Encoding of z. 0 for BLOBs */
|
|
void (*xDel)(void*) /* Destructor function */
|
|
){
|
|
@@ -75909,7 +76614,12 @@ static int valueFromExpr(
|
|
}else{
|
|
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
|
|
}
|
|
- if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
|
|
+ assert( (pVal->flags & MEM_IntReal)==0 );
|
|
+ if( pVal->flags & (MEM_Int|MEM_IntReal|MEM_Real) ){
|
|
+ testcase( pVal->flags & MEM_Int );
|
|
+ testcase( pVal->flags & MEM_Real );
|
|
+ pVal->flags &= ~MEM_Str;
|
|
+ }
|
|
if( enc!=SQLITE_UTF8 ){
|
|
rc = sqlite3VdbeChangeEncoding(pVal, enc);
|
|
}
|
|
@@ -75932,7 +76642,7 @@ static int valueFromExpr(
|
|
}else if( op==TK_NULL ){
|
|
pVal = valueNew(db, pCtx);
|
|
if( pVal==0 ) goto no_mem;
|
|
- sqlite3VdbeMemNumerify(pVal);
|
|
+ sqlite3VdbeMemSetNull(pVal);
|
|
}
|
|
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
|
else if( op==TK_BLOB ){
|
|
@@ -76291,10 +77001,10 @@ SQLITE_PRIVATE void sqlite3ValueFree(sql
|
|
** sqlite3_value object assuming that it uses the encoding "enc".
|
|
** The valueBytes() routine is a helper function.
|
|
*/
|
|
-static SQLITE_NOINLINE int valueBytes(sqlite3_value *pVal, u8 enc){
|
|
+static SQLITE_NOINLINE size_t valueBytes(sqlite3_value *pVal, u8 enc){
|
|
return valueToText(pVal, enc)!=0 ? pVal->n : 0;
|
|
}
|
|
-SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
|
|
+SQLITE_PRIVATE size_t sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
|
|
Mem *p = (Mem*)pVal;
|
|
assert( (p->flags & MEM_Null)==0 || (p->flags & (MEM_Str|MEM_Blob))==0 );
|
|
if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){
|
|
@@ -76484,7 +77194,7 @@ static int growOpArray(Vdbe *v, int nOp)
|
|
return SQLITE_NOMEM;
|
|
}
|
|
|
|
- assert( nOp<=(1024/sizeof(Op)) );
|
|
+ assert( (unsigned)nOp<=(1024/sizeof(Op)) );
|
|
assert( nNew>=(v->nOpAlloc+nOp) );
|
|
pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
|
|
if( pNew ){
|
|
@@ -76547,7 +77257,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdb
|
|
pOp->p3 = p3;
|
|
pOp->p4.p = 0;
|
|
pOp->p4type = P4_NOTUSED;
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 1
|
|
pOp->zComment = 0;
|
|
#endif
|
|
#ifdef SQLITE_DEBUG
|
|
@@ -77232,7 +77942,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpL
|
|
pOut->p4type = P4_NOTUSED;
|
|
pOut->p4.p = 0;
|
|
pOut->p5 = 0;
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 1
|
|
pOut->zComment = 0;
|
|
#endif
|
|
#ifdef SQLITE_VDBE_COVERAGE
|
|
@@ -77385,7 +78095,7 @@ static void vdbeFreeOpArray(sqlite3 *db,
|
|
Op *pOp;
|
|
for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){
|
|
if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p);
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 1
|
|
sqlite3DbFree(db, pOp->zComment);
|
|
#endif
|
|
}
|
|
@@ -77538,7 +78248,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetP4KeyI
|
|
if( pKeyInfo ) sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
|
|
}
|
|
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 1
|
|
/*
|
|
** Change the comment on the most recently coded instruction. Or
|
|
** insert a No-op and add the comment to that new instruction. This
|
|
@@ -77611,7 +78321,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(
|
|
}
|
|
}
|
|
|
|
-#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
|
|
+#if 1
|
|
/*
|
|
** Return an integer value for one of the parameters to the opcode pOp
|
|
** determined by character c.
|
|
@@ -77789,8 +78499,8 @@ static void displayP4Expr(StrAccum *p, E
|
|
** Compute a string that describes the P4 parameter for an opcode.
|
|
** Use zTemp for any required temporary buffer space.
|
|
*/
|
|
-static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
|
- char *zP4 = zTemp;
|
|
+static const char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
|
+ const char *zP4 = zTemp;
|
|
StrAccum x;
|
|
assert( nTemp>=20 );
|
|
sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
|
|
@@ -77849,7 +78559,7 @@ static char *displayP4(Op *pOp, char *zT
|
|
Mem *pMem = pOp->p4.pMem;
|
|
if( pMem->flags & MEM_Str ){
|
|
zP4 = pMem->z;
|
|
- }else if( pMem->flags & MEM_Int ){
|
|
+ }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){
|
|
sqlite3_str_appendf(&x, "%lld", pMem->u.i);
|
|
}else if( pMem->flags & MEM_Real ){
|
|
sqlite3_str_appendf(&x, "%.16g", pMem->u.r);
|
|
@@ -77992,14 +78702,18 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdb
|
|
** Print a single opcode. This routine is used for debugging only.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
|
|
- char *zP4;
|
|
+ const char *zP4;
|
|
char zPtr[50];
|
|
char zCom[100];
|
|
static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
|
|
if( pOut==0 ) pOut = stdout;
|
|
zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
- displayComment(pOp, zP4, zCom, sizeof(zCom));
|
|
+#if 1
|
|
+ if( sqlite3GlobalConfig.bVdbeComments ){
|
|
+ displayComment(pOp, zP4, zCom, sizeof(zCom));
|
|
+ }else{
|
|
+ zCom[0] = 0;
|
|
+ }
|
|
#else
|
|
zCom[0] = 0;
|
|
#endif
|
|
@@ -78253,7 +78967,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
|
|
rc = SQLITE_ERROR;
|
|
sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
|
|
}else{
|
|
- char *zP4;
|
|
+ const char *zP4;
|
|
if( p->explain==1 ){
|
|
pMem->flags = MEM_Int;
|
|
pMem->u.i = i; /* Program counter */
|
|
@@ -78306,7 +79020,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
|
|
pMem->enc = SQLITE_UTF8;
|
|
pMem++;
|
|
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+ if( sqlite3GlobalConfig.bVdbeComments ){
|
|
if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
|
|
assert( p->db->mallocFailed );
|
|
return SQLITE_ERROR;
|
|
@@ -78314,9 +79028,9 @@ SQLITE_PRIVATE int sqlite3VdbeList(
|
|
pMem->flags = MEM_Str|MEM_Term;
|
|
pMem->n = displayComment(pOp, zP4, pMem->z, 500);
|
|
pMem->enc = SQLITE_UTF8;
|
|
-#else
|
|
+ } else {
|
|
pMem->flags = MEM_Null; /* Comment */
|
|
-#endif
|
|
+ }
|
|
}
|
|
|
|
p->nResColumn = 8 - 4*(p->explain-1);
|
|
@@ -79211,7 +79925,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe
|
|
}
|
|
|
|
/* Check for immediate foreign key violations. */
|
|
- if( p->rc==SQLITE_OK ){
|
|
+ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
|
|
sqlite3VdbeCheckFk(p, 0);
|
|
}
|
|
|
|
@@ -79737,6 +80451,8 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMove
|
|
|
|
/*
|
|
** Return the serial-type for the value stored in pMem.
|
|
+**
|
|
+** This routine might convert a large MEM_IntReal value into MEM_Real.
|
|
*/
|
|
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
|
|
int flags = pMem->flags;
|
|
@@ -79747,11 +80463,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType
|
|
*pLen = 0;
|
|
return 0;
|
|
}
|
|
- if( flags&MEM_Int ){
|
|
+ if( flags&(MEM_Int|MEM_IntReal) ){
|
|
/* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
|
|
# define MAX_6BYTE ((((i64)0x00008000)<<32)-1)
|
|
i64 i = pMem->u.i;
|
|
u64 u;
|
|
+ testcase( flags & MEM_Int );
|
|
+ testcase( flags & MEM_IntReal );
|
|
if( i<0 ){
|
|
u = ~i;
|
|
}else{
|
|
@@ -79771,6 +80489,15 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType
|
|
if( u<=2147483647 ){ *pLen = 4; return 4; }
|
|
if( u<=MAX_6BYTE ){ *pLen = 6; return 5; }
|
|
*pLen = 8;
|
|
+ if( flags&MEM_IntReal ){
|
|
+ /* If the value is IntReal and is going to take up 8 bytes to store
|
|
+ ** as an integer, then we might as well make it an 8-byte floating
|
|
+ ** point value */
|
|
+ pMem->u.r = (double)pMem->u.i;
|
|
+ pMem->flags &= ~MEM_IntReal;
|
|
+ pMem->flags |= MEM_Real;
|
|
+ return 7;
|
|
+ }
|
|
return 6;
|
|
}
|
|
if( flags&MEM_Real ){
|
|
@@ -79778,7 +80505,6 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType
|
|
return 7;
|
|
}
|
|
assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) );
|
|
- assert( pMem->n>=0 );
|
|
n = (u32)pMem->n;
|
|
if( flags & MEM_Zero ){
|
|
n += pMem->u.nZero;
|
|
@@ -79810,7 +80536,7 @@ static const u8 sqlite3SmallTypeSizes[]
|
|
/*
|
|
** Return the length of the data corresponding to the supplied serial-type.
|
|
*/
|
|
-SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32 serial_type){
|
|
+SQLITE_PRIVATE size_t sqlite3VdbeSerialTypeLen(u32 serial_type){
|
|
if( serial_type>=128 ){
|
|
return (serial_type-12)/2;
|
|
}else{
|
|
@@ -79916,7 +80642,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(
|
|
/* String or blob */
|
|
if( serial_type>=12 ){
|
|
assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0)
|
|
- == (int)sqlite3VdbeSerialTypeLen(serial_type) );
|
|
+ == sqlite3VdbeSerialTypeLen(serial_type) );
|
|
len = pMem->n;
|
|
if( len>0 ) memcpy(buf, pMem->z, len);
|
|
return len;
|
|
@@ -80266,7 +80992,7 @@ debugCompareEnd:
|
|
** incorrectly.
|
|
*/
|
|
static void vdbeAssertFieldCountWithinLimits(
|
|
- int nKey, const void *pKey, /* The record to verify */
|
|
+ size_t nKey, const void *pKey,/* The record to verify */
|
|
const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */
|
|
){
|
|
int nField = 0;
|
|
@@ -80277,8 +81003,7 @@ static void vdbeAssertFieldCountWithinLi
|
|
|
|
if( CORRUPT_DB ) return;
|
|
idx = getVarint32(aKey, szHdr);
|
|
- assert( nKey>=0 );
|
|
- assert( szHdr<=(u32)nKey );
|
|
+ assert( szHdr<=nKey );
|
|
while( idx<szHdr ){
|
|
idx += getVarint32(aKey+idx, notUsed);
|
|
nField++;
|
|
@@ -80426,8 +81151,13 @@ SQLITE_PRIVATE int sqlite3MemCompare(con
|
|
|
|
/* At least one of the two values is a number
|
|
*/
|
|
- if( combined_flags&(MEM_Int|MEM_Real) ){
|
|
- if( (f1 & f2 & MEM_Int)!=0 ){
|
|
+ if( combined_flags&(MEM_Int|MEM_Real|MEM_IntReal) ){
|
|
+ testcase( combined_flags & MEM_Int );
|
|
+ testcase( combined_flags & MEM_Real );
|
|
+ testcase( combined_flags & MEM_IntReal );
|
|
+ if( (f1 & f2 & (MEM_Int|MEM_IntReal))!=0 ){
|
|
+ testcase( f1 & f2 & MEM_Int );
|
|
+ testcase( f1 & f2 & MEM_IntReal );
|
|
if( pMem1->u.i < pMem2->u.i ) return -1;
|
|
if( pMem1->u.i > pMem2->u.i ) return +1;
|
|
return 0;
|
|
@@ -80437,15 +81167,23 @@ SQLITE_PRIVATE int sqlite3MemCompare(con
|
|
if( pMem1->u.r > pMem2->u.r ) return +1;
|
|
return 0;
|
|
}
|
|
- if( (f1&MEM_Int)!=0 ){
|
|
+ if( (f1&(MEM_Int|MEM_IntReal))!=0 ){
|
|
+ testcase( f1 & MEM_Int );
|
|
+ testcase( f1 & MEM_IntReal );
|
|
if( (f2&MEM_Real)!=0 ){
|
|
return sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r);
|
|
+ }else if( (f2&(MEM_Int|MEM_IntReal))!=0 ){
|
|
+ if( pMem1->u.i < pMem2->u.i ) return -1;
|
|
+ if( pMem1->u.i > pMem2->u.i ) return +1;
|
|
+ return 0;
|
|
}else{
|
|
return -1;
|
|
}
|
|
}
|
|
if( (f1&MEM_Real)!=0 ){
|
|
- if( (f2&MEM_Int)!=0 ){
|
|
+ if( (f2&(MEM_Int|MEM_IntReal))!=0 ){
|
|
+ testcase( f2 & MEM_Int );
|
|
+ testcase( f2 & MEM_IntReal );
|
|
return -sqlite3IntFloatCompare(pMem2->u.i, pMem1->u.r);
|
|
}else{
|
|
return -1;
|
|
@@ -80550,7 +81288,7 @@ static i64 vdbeRecordDecodeInt(u32 seria
|
|
** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
|
|
- int nKey1, const void *pKey1, /* Left key */
|
|
+ size_t nKey1, const void *pKey1,/* Left key */
|
|
UnpackedRecord *pPKey2, /* Right key */
|
|
int bSkip /* If true, skip the first field */
|
|
){
|
|
@@ -80594,7 +81332,9 @@ SQLITE_PRIVATE int sqlite3VdbeRecordComp
|
|
u32 serial_type;
|
|
|
|
/* RHS is an integer */
|
|
- if( pRhs->flags & MEM_Int ){
|
|
+ if( pRhs->flags & (MEM_Int|MEM_IntReal) ){
|
|
+ testcase( pRhs->flags & MEM_Int );
|
|
+ testcase( pRhs->flags & MEM_IntReal );
|
|
serial_type = aKey1[idx1];
|
|
testcase( serial_type==12 );
|
|
if( serial_type>=10 ){
|
|
@@ -80681,7 +81421,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordComp
|
|
if( serial_type<12 || (serial_type & 0x01) ){
|
|
rc = -1;
|
|
}else{
|
|
- int nStr = (serial_type - 12) / 2;
|
|
+ size_t nStr = (serial_type - 12) / 2;
|
|
testcase( (d1+nStr)==(unsigned)nKey1 );
|
|
testcase( (d1+nStr+1)==(unsigned)nKey1 );
|
|
if( (d1+nStr) > (unsigned)nKey1 ){
|
|
@@ -80739,7 +81479,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordComp
|
|
return pPKey2->default_rc;
|
|
}
|
|
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
|
|
- int nKey1, const void *pKey1, /* Left key */
|
|
+ size_t nKey1, const void *pKey1,/* Left key */
|
|
UnpackedRecord *pPKey2 /* Right key */
|
|
){
|
|
return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
|
|
@@ -80756,7 +81496,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordComp
|
|
** on schemas where the maximum valid header size is 63 bytes or less.
|
|
*/
|
|
static int vdbeRecordCompareInt(
|
|
- int nKey1, const void *pKey1, /* Left key */
|
|
+ size_t nKey1, const void *pKey1, /* Left key */
|
|
UnpackedRecord *pPKey2 /* Right key */
|
|
){
|
|
const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F];
|
|
@@ -80850,7 +81590,7 @@ static int vdbeRecordCompareInt(
|
|
** at the start of (pKey1/nKey1) fits in a single byte.
|
|
*/
|
|
static int vdbeRecordCompareString(
|
|
- int nKey1, const void *pKey1, /* Left key */
|
|
+ size_t nKey1, const void *pKey1, /* Left key */
|
|
UnpackedRecord *pPKey2 /* Right key */
|
|
){
|
|
const u8 *aKey1 = (const u8*)pKey1;
|
|
@@ -80865,8 +81605,8 @@ static int vdbeRecordCompareString(
|
|
}else if( !(serial_type & 0x01) ){
|
|
res = pPKey2->r2; /* (pKey1/nKey1) is a blob */
|
|
}else{
|
|
- int nCmp;
|
|
- int nStr;
|
|
+ size_t nCmp;
|
|
+ size_t nStr;
|
|
int szHdr = aKey1[0];
|
|
|
|
nStr = (serial_type-12) / 2;
|
|
@@ -80939,7 +81679,9 @@ SQLITE_PRIVATE RecordCompare sqlite3Vdbe
|
|
testcase( flags & MEM_Real );
|
|
testcase( flags & MEM_Null );
|
|
testcase( flags & MEM_Blob );
|
|
- if( (flags & (MEM_Real|MEM_Null|MEM_Blob))==0 && p->pKeyInfo->aColl[0]==0 ){
|
|
+ if( (flags & (MEM_Real|MEM_IntReal|MEM_Null|MEM_Blob))==0
|
|
+ && p->pKeyInfo->aColl[0]==0
|
|
+ ){
|
|
assert( flags & MEM_Str );
|
|
return vdbeRecordCompareString;
|
|
}
|
|
@@ -80986,7 +81728,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(s
|
|
testcase( szHdr==m.n );
|
|
testcase( szHdr>0x7fffffff );
|
|
assert( m.n>=0 );
|
|
- if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){
|
|
+ if( unlikely(szHdr<3 || szHdr>m.n) ){
|
|
goto idx_rowid_corruption;
|
|
}
|
|
|
|
@@ -81050,12 +81792,13 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyComp
|
|
pCur = pC->uc.pCursor;
|
|
assert( sqlite3BtreeCursorIsValid(pCur) );
|
|
nCellKey = sqlite3BtreePayloadSize(pCur);
|
|
- /* nCellKey will always be between 0 and 0xffffffff because of the way
|
|
+ /* nCellKey will always be between 0 and 0x7fffffff because of the way
|
|
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
|
|
- if( nCellKey<=0 || nCellKey>0x7fffffff ){
|
|
+ if( (nCellKey & ~(i64)0x7fffffff)!=0 ){
|
|
*res = 0;
|
|
return SQLITE_CORRUPT_BKPT;
|
|
}
|
|
+ assert( nCellKey>=0 && nCellKey<=0x7fffffff );
|
|
sqlite3VdbeMemInit(&m, db, 0);
|
|
rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m);
|
|
if( rc ){
|
|
@@ -81070,7 +81813,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyComp
|
|
** This routine sets the value to be returned by subsequent calls to
|
|
** sqlite3_changes() on the database handle 'db'.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){
|
|
+SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, u64 nChange){
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
db->nChange = nChange;
|
|
db->nTotalChange += nChange;
|
|
@@ -81477,10 +82220,10 @@ SQLITE_API const void *sqlite3_value_blo
|
|
return sqlite3_value_text(pVal);
|
|
}
|
|
}
|
|
-SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){
|
|
+SQLITE_API unsigned int sqlite3_value_bytes(sqlite3_value *pVal){
|
|
return sqlite3ValueBytes(pVal, SQLITE_UTF8);
|
|
}
|
|
-SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){
|
|
+SQLITE_API unsigned int sqlite3_value_bytes16(sqlite3_value *pVal){
|
|
return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE);
|
|
}
|
|
SQLITE_API double sqlite3_value_double(sqlite3_value *pVal){
|
|
@@ -81529,39 +82272,86 @@ SQLITE_API const void *sqlite3_value_tex
|
|
*/
|
|
SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){
|
|
static const u8 aType[] = {
|
|
- SQLITE_BLOB, /* 0x00 */
|
|
- SQLITE_NULL, /* 0x01 */
|
|
- SQLITE_TEXT, /* 0x02 */
|
|
- SQLITE_NULL, /* 0x03 */
|
|
- SQLITE_INTEGER, /* 0x04 */
|
|
- SQLITE_NULL, /* 0x05 */
|
|
- SQLITE_INTEGER, /* 0x06 */
|
|
- SQLITE_NULL, /* 0x07 */
|
|
- SQLITE_FLOAT, /* 0x08 */
|
|
- SQLITE_NULL, /* 0x09 */
|
|
- SQLITE_FLOAT, /* 0x0a */
|
|
- SQLITE_NULL, /* 0x0b */
|
|
- SQLITE_INTEGER, /* 0x0c */
|
|
- SQLITE_NULL, /* 0x0d */
|
|
- SQLITE_INTEGER, /* 0x0e */
|
|
- SQLITE_NULL, /* 0x0f */
|
|
- SQLITE_BLOB, /* 0x10 */
|
|
- SQLITE_NULL, /* 0x11 */
|
|
- SQLITE_TEXT, /* 0x12 */
|
|
- SQLITE_NULL, /* 0x13 */
|
|
- SQLITE_INTEGER, /* 0x14 */
|
|
- SQLITE_NULL, /* 0x15 */
|
|
- SQLITE_INTEGER, /* 0x16 */
|
|
- SQLITE_NULL, /* 0x17 */
|
|
- SQLITE_FLOAT, /* 0x18 */
|
|
- SQLITE_NULL, /* 0x19 */
|
|
- SQLITE_FLOAT, /* 0x1a */
|
|
- SQLITE_NULL, /* 0x1b */
|
|
- SQLITE_INTEGER, /* 0x1c */
|
|
- SQLITE_NULL, /* 0x1d */
|
|
- SQLITE_INTEGER, /* 0x1e */
|
|
- SQLITE_NULL, /* 0x1f */
|
|
+ SQLITE_BLOB, /* 0x00 (not possible) */
|
|
+ SQLITE_NULL, /* 0x01 NULL */
|
|
+ SQLITE_TEXT, /* 0x02 TEXT */
|
|
+ SQLITE_NULL, /* 0x03 (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x04 INTEGER */
|
|
+ SQLITE_NULL, /* 0x05 (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x06 INTEGER + TEXT */
|
|
+ SQLITE_NULL, /* 0x07 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x08 FLOAT */
|
|
+ SQLITE_NULL, /* 0x09 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x0a FLOAT + TEXT */
|
|
+ SQLITE_NULL, /* 0x0b (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x0c (not possible) */
|
|
+ SQLITE_NULL, /* 0x0d (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x0e (not possible) */
|
|
+ SQLITE_NULL, /* 0x0f (not possible) */
|
|
+ SQLITE_BLOB, /* 0x10 BLOB */
|
|
+ SQLITE_NULL, /* 0x11 (not possible) */
|
|
+ SQLITE_TEXT, /* 0x12 (not possible) */
|
|
+ SQLITE_NULL, /* 0x13 (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x14 INTEGER + BLOB */
|
|
+ SQLITE_NULL, /* 0x15 (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x16 (not possible) */
|
|
+ SQLITE_NULL, /* 0x17 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x18 FLOAT + BLOB */
|
|
+ SQLITE_NULL, /* 0x19 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x1a (not possible) */
|
|
+ SQLITE_NULL, /* 0x1b (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x1c (not possible) */
|
|
+ SQLITE_NULL, /* 0x1d (not possible) */
|
|
+ SQLITE_INTEGER, /* 0x1e (not possible) */
|
|
+ SQLITE_NULL, /* 0x1f (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x20 INTREAL */
|
|
+ SQLITE_NULL, /* 0x21 (not possible) */
|
|
+ SQLITE_TEXT, /* 0x22 INTREAL + TEXT */
|
|
+ SQLITE_NULL, /* 0x23 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x24 (not possible) */
|
|
+ SQLITE_NULL, /* 0x25 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x26 (not possible) */
|
|
+ SQLITE_NULL, /* 0x27 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x28 (not possible) */
|
|
+ SQLITE_NULL, /* 0x29 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x2a (not possible) */
|
|
+ SQLITE_NULL, /* 0x2b (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x2c (not possible) */
|
|
+ SQLITE_NULL, /* 0x2d (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x2e (not possible) */
|
|
+ SQLITE_NULL, /* 0x2f (not possible) */
|
|
+ SQLITE_BLOB, /* 0x30 (not possible) */
|
|
+ SQLITE_NULL, /* 0x31 (not possible) */
|
|
+ SQLITE_TEXT, /* 0x32 (not possible) */
|
|
+ SQLITE_NULL, /* 0x33 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x34 (not possible) */
|
|
+ SQLITE_NULL, /* 0x35 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x36 (not possible) */
|
|
+ SQLITE_NULL, /* 0x37 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x38 (not possible) */
|
|
+ SQLITE_NULL, /* 0x39 (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x3a (not possible) */
|
|
+ SQLITE_NULL, /* 0x3b (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x3c (not possible) */
|
|
+ SQLITE_NULL, /* 0x3d (not possible) */
|
|
+ SQLITE_FLOAT, /* 0x3e (not possible) */
|
|
+ SQLITE_NULL, /* 0x3f (not possible) */
|
|
};
|
|
+#ifdef SQLITE_DEBUG
|
|
+ {
|
|
+ int eType = SQLITE_BLOB;
|
|
+ if( pVal->flags & MEM_Null ){
|
|
+ eType = SQLITE_NULL;
|
|
+ }else if( pVal->flags & (MEM_Real|MEM_IntReal) ){
|
|
+ eType = SQLITE_FLOAT;
|
|
+ }else if( pVal->flags & MEM_Int ){
|
|
+ eType = SQLITE_INTEGER;
|
|
+ }else if( pVal->flags & MEM_Str ){
|
|
+ eType = SQLITE_TEXT;
|
|
+ }
|
|
+ assert( eType == aType[pVal->flags&MEM_AffMask] );
|
|
+ }
|
|
+#endif
|
|
return aType[pVal->flags&MEM_AffMask];
|
|
}
|
|
|
|
@@ -81580,7 +82370,7 @@ SQLITE_API int sqlite3_value_frombind(sq
|
|
SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
|
|
sqlite3_value *pNew;
|
|
if( pOrig==0 ) return 0;
|
|
- pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ pNew = sqlite3Malloc( sizeof(*pNew) );
|
|
if( pNew==0 ) return 0;
|
|
memset(pNew, 0, sizeof(*pNew));
|
|
memcpy(pNew, pOrig, MEMCELLSIZE);
|
|
@@ -81619,7 +82409,7 @@ SQLITE_API void sqlite3_value_free(sqlit
|
|
static void setResultStrOrError(
|
|
sqlite3_context *pCtx, /* Function context */
|
|
const char *z, /* String pointer */
|
|
- int n, /* Bytes in string, or negative */
|
|
+ size_t n, /* Bytes in string, or negative */
|
|
u8 enc, /* Encoding of z. 0 for BLOBs */
|
|
void (*xDel)(void*) /* Destructor function */
|
|
){
|
|
@@ -81643,15 +82433,15 @@ static int invokeValueDestructor(
|
|
if( pCtx ) sqlite3_result_error_toobig(pCtx);
|
|
return SQLITE_TOOBIG;
|
|
}
|
|
+#undef sqlite3_result_blob
|
|
SQLITE_API void sqlite3_result_blob(
|
|
sqlite3_context *pCtx,
|
|
const void *z,
|
|
- int n,
|
|
+ unsigned int n,
|
|
void (*xDel)(void *)
|
|
){
|
|
- assert( n>=0 );
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
- setResultStrOrError(pCtx, z, n, 0, xDel);
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, 0, xDel);
|
|
}
|
|
SQLITE_API void sqlite3_result_blob64(
|
|
sqlite3_context *pCtx,
|
|
@@ -81661,11 +82451,12 @@ SQLITE_API void sqlite3_result_blob64(
|
|
){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
assert( xDel!=SQLITE_DYNAMIC );
|
|
- if( n>0x7fffffff ){
|
|
- (void)invokeValueDestructor(z, xDel, pCtx);
|
|
- }else{
|
|
- setResultStrOrError(pCtx, z, (int)n, 0, xDel);
|
|
+
|
|
+ if( n>SQLITE_MAX_LENGTH ){
|
|
+ (void)invokeValueDestructor(z, xDel, 0);
|
|
+ return;
|
|
}
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, 0, xDel);
|
|
}
|
|
SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
@@ -81713,6 +82504,7 @@ SQLITE_API void sqlite3_result_subtype(s
|
|
pOut->eSubtype = eSubtype & 0xff;
|
|
pOut->flags |= MEM_Subtype;
|
|
}
|
|
+#undef sqlite3_result_text
|
|
SQLITE_API void sqlite3_result_text(
|
|
sqlite3_context *pCtx,
|
|
const char *z,
|
|
@@ -81720,7 +82512,7 @@ SQLITE_API void sqlite3_result_text(
|
|
void (*xDel)(void *)
|
|
){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
- setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, SQLITE_UTF8, xDel);
|
|
}
|
|
SQLITE_API void sqlite3_result_text64(
|
|
sqlite3_context *pCtx,
|
|
@@ -81731,14 +82523,20 @@ SQLITE_API void sqlite3_result_text64(
|
|
){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
assert( xDel!=SQLITE_DYNAMIC );
|
|
- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
|
|
if( n>0x7fffffff ){
|
|
- (void)invokeValueDestructor(z, xDel, pCtx);
|
|
- }else{
|
|
- setResultStrOrError(pCtx, z, (int)n, enc, xDel);
|
|
+ if( (sqlite3_int64)n >= 0 ){
|
|
+ (void)invokeValueDestructor(z, xDel, pCtx);
|
|
+ return;
|
|
+ }
|
|
+ n = (sqlite3_uint64)-1;
|
|
}
|
|
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, enc, xDel);
|
|
}
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
+#undef sqlite3_result_text16
|
|
+#undef sqlite3_result_text16be
|
|
+#undef sqlite3_result_text16le
|
|
SQLITE_API void sqlite3_result_text16(
|
|
sqlite3_context *pCtx,
|
|
const void *z,
|
|
@@ -81746,7 +82544,7 @@ SQLITE_API void sqlite3_result_text16(
|
|
void (*xDel)(void *)
|
|
){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel);
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, SQLITE_UTF16NATIVE, xDel);
|
|
}
|
|
SQLITE_API void sqlite3_result_text16be(
|
|
sqlite3_context *pCtx,
|
|
@@ -81755,7 +82553,7 @@ SQLITE_API void sqlite3_result_text16be(
|
|
void (*xDel)(void *)
|
|
){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel);
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, SQLITE_UTF16BE, xDel);
|
|
}
|
|
SQLITE_API void sqlite3_result_text16le(
|
|
sqlite3_context *pCtx,
|
|
@@ -81764,16 +82562,17 @@ SQLITE_API void sqlite3_result_text16le(
|
|
void (*xDel)(void *)
|
|
){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel);
|
|
+ setResultStrOrError(pCtx, z, (size_t)n, SQLITE_UTF16LE, xDel);
|
|
}
|
|
#endif /* SQLITE_OMIT_UTF16 */
|
|
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
sqlite3VdbeMemCopy(pCtx->pOut, pValue);
|
|
}
|
|
+#undef sqlite3_result_zeroblob
|
|
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
- sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n);
|
|
+ sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (size_t)n);
|
|
}
|
|
SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
|
|
Mem *pOut = pCtx->pOut;
|
|
@@ -81781,7 +82580,7 @@ SQLITE_API int sqlite3_result_zeroblob64
|
|
if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
return SQLITE_TOOBIG;
|
|
}
|
|
- sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n);
|
|
+ sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (size_t)n);
|
|
return SQLITE_OK;
|
|
}
|
|
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
|
|
@@ -81811,6 +82610,21 @@ SQLITE_API void sqlite3_result_error_nom
|
|
sqlite3OomFault(pCtx->pOut->db);
|
|
}
|
|
|
|
+#ifndef SQLITE_UNTESTABLE
|
|
+/* Force the INT64 value currently stored as the result to be
|
|
+** a MEM_IntReal value. See the SQLITE_TESTCTRL_RESULT_INTREAL
|
|
+** test-control.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3ResultIntReal(sqlite3_context *pCtx){
|
|
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
+ if( pCtx->pOut->flags & MEM_Int ){
|
|
+ pCtx->pOut->flags &= ~MEM_Int;
|
|
+ pCtx->pOut->flags |= MEM_IntReal;
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
/*
|
|
** This function is called after a transaction has been committed. It
|
|
** invokes callbacks registered with sqlite3_wal_hook() as required.
|
|
@@ -82351,13 +83165,13 @@ SQLITE_API const void *sqlite3_column_bl
|
|
columnMallocFailure(pStmt);
|
|
return val;
|
|
}
|
|
-SQLITE_API int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
|
|
- int val = sqlite3_value_bytes( columnMem(pStmt,i) );
|
|
+SQLITE_API unsigned int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
|
|
+ unsigned int val = sqlite3ValueBytes( columnMem(pStmt,i), SQLITE_UTF8 );
|
|
columnMallocFailure(pStmt);
|
|
return val;
|
|
}
|
|
-SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){
|
|
- int val = sqlite3_value_bytes16( columnMem(pStmt,i) );
|
|
+SQLITE_API unsigned int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){
|
|
+ unsigned int val = sqlite3ValueBytes( columnMem(pStmt,i), SQLITE_UTF16NATIVE );
|
|
columnMallocFailure(pStmt);
|
|
return val;
|
|
}
|
|
@@ -82608,7 +83422,7 @@ static int bindText(
|
|
sqlite3_stmt *pStmt, /* The statement to bind against */
|
|
int i, /* Index of the parameter to bind */
|
|
const void *zData, /* Pointer to the data to be bound */
|
|
- int nData, /* Number of bytes of data to be bound */
|
|
+ size_t nData, /* Number of bytes of data to be bound */
|
|
void (*xDel)(void*), /* Destructor for the data */
|
|
u8 encoding /* Encoding for the data */
|
|
){
|
|
@@ -82640,11 +83454,12 @@ static int bindText(
|
|
/*
|
|
** Bind a blob value to an SQL statement variable.
|
|
*/
|
|
+#undef sqlite3_bind_blob
|
|
SQLITE_API int sqlite3_bind_blob(
|
|
sqlite3_stmt *pStmt,
|
|
int i,
|
|
const void *zData,
|
|
- int nData,
|
|
+ unsigned int nData,
|
|
void (*xDel)(void*)
|
|
){
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
@@ -82660,11 +83475,11 @@ SQLITE_API int sqlite3_bind_blob64(
|
|
void (*xDel)(void*)
|
|
){
|
|
assert( xDel!=SQLITE_DYNAMIC );
|
|
- if( nData>0x7fffffff ){
|
|
+
|
|
+ if( nData>SQLITE_MAX_LENGTH ){
|
|
return invokeValueDestructor(zData, xDel, 0);
|
|
- }else{
|
|
- return bindText(pStmt, i, zData, (int)nData, xDel, 0);
|
|
}
|
|
+ return bindText(pStmt, i, zData, (size_t)nData, xDel, 0);
|
|
}
|
|
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
|
|
int rc;
|
|
@@ -82716,6 +83531,7 @@ SQLITE_API int sqlite3_bind_pointer(
|
|
}
|
|
return rc;
|
|
}
|
|
+#undef sqlite3_bind_text
|
|
SQLITE_API int sqlite3_bind_text(
|
|
sqlite3_stmt *pStmt,
|
|
int i,
|
|
@@ -82735,13 +83551,16 @@ SQLITE_API int sqlite3_bind_text64(
|
|
){
|
|
assert( xDel!=SQLITE_DYNAMIC );
|
|
if( nData>0x7fffffff ){
|
|
- return invokeValueDestructor(zData, xDel, 0);
|
|
- }else{
|
|
- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
|
|
- return bindText(pStmt, i, zData, (int)nData, xDel, enc);
|
|
+ if( (sqlite3_int64)nData >= 0 ){
|
|
+ return invokeValueDestructor(zData, xDel, 0);
|
|
+ }
|
|
+ nData = (sqlite3_uint64)-1;
|
|
}
|
|
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
|
|
+ return bindText(pStmt, i, zData, nData, xDel, enc);
|
|
}
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
+#undef sqlite3_bind_text16
|
|
SQLITE_API int sqlite3_bind_text16(
|
|
sqlite3_stmt *pStmt,
|
|
int i,
|
|
@@ -82783,12 +83602,13 @@ SQLITE_API int sqlite3_bind_value(sqlite
|
|
}
|
|
return rc;
|
|
}
|
|
+#undef sqlite3_bind_zeroblob
|
|
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
|
|
int rc;
|
|
Vdbe *p = (Vdbe *)pStmt;
|
|
rc = vdbeUnbind(p, i);
|
|
if( rc==SQLITE_OK ){
|
|
- sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
|
|
+ sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], (size_t)n);
|
|
sqlite3_mutex_leave(p->db->mutex);
|
|
}
|
|
return rc;
|
|
@@ -82800,7 +83620,7 @@ SQLITE_API int sqlite3_bind_zeroblob64(s
|
|
if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
rc = SQLITE_TOOBIG;
|
|
}else{
|
|
- assert( (n & 0x7FFFFFFF)==n );
|
|
+ assert( n<=SQLITE_MAX_LENGTH );
|
|
rc = sqlite3_bind_zeroblob(pStmt, i, n);
|
|
}
|
|
rc = sqlite3ApiExit(p->db, rc);
|
|
@@ -82953,7 +83773,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_st
|
|
*/
|
|
SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
|
|
Vdbe *pVdbe = (Vdbe*)pStmt;
|
|
- u32 v;
|
|
+ size_t v;
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !pStmt
|
|
|| (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter)))
|
|
@@ -82966,7 +83786,7 @@ SQLITE_API int sqlite3_stmt_status(sqlit
|
|
sqlite3 *db = pVdbe->db;
|
|
sqlite3_mutex_enter(db->mutex);
|
|
v = 0;
|
|
- db->pnBytesFreed = (int*)&v;
|
|
+ db->pnBytesFreed = &v;
|
|
sqlite3VdbeClearObject(db, pVdbe);
|
|
sqlite3DbFree(db, pVdbe);
|
|
db->pnBytesFreed = 0;
|
|
@@ -83097,7 +83917,9 @@ SQLITE_API int sqlite3_preupdate_old(sql
|
|
}else if( iIdx>=p->pUnpacked->nField ){
|
|
*ppValue = (sqlite3_value *)columnNullValue();
|
|
}else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
|
|
- if( pMem->flags & MEM_Int ){
|
|
+ if( pMem->flags & (MEM_Int|MEM_IntReal) ){
|
|
+ testcase( pMem->flags & MEM_Int );
|
|
+ testcase( pMem->flags & MEM_IntReal );
|
|
sqlite3VdbeMemRealify(pMem);
|
|
}
|
|
}
|
|
@@ -83416,7 +84238,7 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSq
|
|
pVar = &p->aVar[idx-1];
|
|
if( pVar->flags & MEM_Null ){
|
|
sqlite3_str_append(&out, "NULL", 4);
|
|
- }else if( pVar->flags & MEM_Int ){
|
|
+ }else if( pVar->flags & (MEM_Int|MEM_IntReal) ){
|
|
sqlite3_str_appendf(&out, "%lld", pVar->u.i);
|
|
}else if( pVar->flags & MEM_Real ){
|
|
sqlite3_str_appendf(&out, "%!.15g", pVar->u.r);
|
|
@@ -83679,14 +84501,6 @@ SQLITE_API int sqlite3_found_count = 0;
|
|
#endif
|
|
|
|
/*
|
|
-** Convert the given register into a string if it isn't one
|
|
-** already. Return non-zero if a malloc() fails.
|
|
-*/
|
|
-#define Stringify(P, enc) \
|
|
- if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc,0)) \
|
|
- { goto no_mem; }
|
|
-
|
|
-/*
|
|
** An ephemeral string value (signified by the MEM_Ephem flag) contains
|
|
** a pointer to a dynamically allocated string where some other entity
|
|
** is responsible for deallocating that string. Because the register
|
|
@@ -83786,7 +84600,7 @@ static void applyNumericAffinity(Mem *pR
|
|
double rValue;
|
|
i64 iValue;
|
|
u8 enc = pRec->enc;
|
|
- assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real))==MEM_Str );
|
|
+ assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_IntReal))==MEM_Str );
|
|
if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
|
|
if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
|
|
pRec->u.i = iValue;
|
|
@@ -83843,11 +84657,14 @@ static void applyAffinity(
|
|
** there is already a string rep, but it is pointless to waste those
|
|
** CPU cycles. */
|
|
if( 0==(pRec->flags&MEM_Str) ){ /*OPTIMIZATION-IF-FALSE*/
|
|
- if( (pRec->flags&(MEM_Real|MEM_Int)) ){
|
|
+ if( (pRec->flags&(MEM_Real|MEM_Int|MEM_IntReal)) ){
|
|
+ testcase( pRec->flags & MEM_Int );
|
|
+ testcase( pRec->flags & MEM_Real );
|
|
+ testcase( pRec->flags & MEM_IntReal );
|
|
sqlite3VdbeMemStringify(pRec, enc, 1);
|
|
}
|
|
}
|
|
- pRec->flags &= ~(MEM_Real|MEM_Int);
|
|
+ pRec->flags &= ~(MEM_Real|MEM_Int|MEM_IntReal);
|
|
}
|
|
}
|
|
|
|
@@ -83886,7 +84703,7 @@ SQLITE_PRIVATE void sqlite3ValueApplyAff
|
|
** accordingly.
|
|
*/
|
|
static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){
|
|
- assert( (pMem->flags & (MEM_Int|MEM_Real))==0 );
|
|
+ assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 );
|
|
assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 );
|
|
ExpandBlob(pMem);
|
|
if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){
|
|
@@ -83906,10 +84723,15 @@ static u16 SQLITE_NOINLINE computeNumeri
|
|
** But it does set pMem->u.r and pMem->u.i appropriately.
|
|
*/
|
|
static u16 numericType(Mem *pMem){
|
|
- if( pMem->flags & (MEM_Int|MEM_Real) ){
|
|
- return pMem->flags & (MEM_Int|MEM_Real);
|
|
+ if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){
|
|
+ testcase( pMem->flags & MEM_Int );
|
|
+ testcase( pMem->flags & MEM_Real );
|
|
+ testcase( pMem->flags & MEM_IntReal );
|
|
+ return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal);
|
|
}
|
|
if( pMem->flags & (MEM_Str|MEM_Blob) ){
|
|
+ testcase( pMem->flags & MEM_Str );
|
|
+ testcase( pMem->flags & MEM_Blob );
|
|
return computeNumericType(pMem);
|
|
}
|
|
return 0;
|
|
@@ -83942,20 +84764,20 @@ SQLITE_PRIVATE void sqlite3VdbeMemPretty
|
|
c = 's';
|
|
}
|
|
*(zCsr++) = c;
|
|
- sqlite3_snprintf(100, zCsr, "%d[", pMem->n);
|
|
+ sqlite3_snprintf(100, zCsr, "%lld[", (u64)pMem->n);
|
|
zCsr += sqlite3Strlen30(zCsr);
|
|
- for(i=0; i<16 && i<pMem->n; i++){
|
|
+ for(i=0; i<16 && i<(int)pMem->n; i++){
|
|
sqlite3_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF));
|
|
zCsr += sqlite3Strlen30(zCsr);
|
|
}
|
|
- for(i=0; i<16 && i<pMem->n; i++){
|
|
+ for(i=0; i<16 && i<(int)pMem->n; i++){
|
|
char z = pMem->z[i];
|
|
if( z<32 || z>126 ) *zCsr++ = '.';
|
|
else *zCsr++ = z;
|
|
}
|
|
*(zCsr++) = ']';
|
|
if( f & MEM_Zero ){
|
|
- sqlite3_snprintf(100, zCsr,"+%dz",pMem->u.nZero);
|
|
+ sqlite3_snprintf(100, zCsr,"+%lldz",(u64)pMem->u.nZero);
|
|
zCsr += sqlite3Strlen30(zCsr);
|
|
}
|
|
*zCsr = '\0';
|
|
@@ -83975,10 +84797,10 @@ SQLITE_PRIVATE void sqlite3VdbeMemPretty
|
|
zBuf[1] = 's';
|
|
}
|
|
k = 2;
|
|
- sqlite3_snprintf(100, &zBuf[k], "%d", pMem->n);
|
|
+ sqlite3_snprintf(100, &zBuf[k], "%lld", (u64)pMem->n);
|
|
k += sqlite3Strlen30(&zBuf[k]);
|
|
zBuf[k++] = '[';
|
|
- for(j=0; j<15 && j<pMem->n; j++){
|
|
+ for(j=0; j<15 && j<(int)pMem->n; j++){
|
|
u8 c = pMem->z[j];
|
|
if( c>=0x20 && c<0x7f ){
|
|
zBuf[k++] = c;
|
|
@@ -84004,9 +84826,19 @@ static void memTracePrint(Mem *p){
|
|
}else if( p->flags & MEM_Null ){
|
|
printf(p->flags & MEM_Zero ? " NULL-nochng" : " NULL");
|
|
}else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
|
|
+#ifdef _WIN32
|
|
+ printf(" si:%I64d", p->u.i);
|
|
+ }else if( (p->flags & (MEM_Int|MEM_IntReal))==(MEM_Int|MEM_IntReal) ){
|
|
+ printf(" ir:%I64d", p->u.i);
|
|
+ }else if( p->flags & MEM_Int ){
|
|
+ printf(" i:%I64d", p->u.i);
|
|
+#else
|
|
printf(" si:%lld", p->u.i);
|
|
+ }else if( (p->flags & (MEM_IntReal))!=0 ){
|
|
+ printf(" ir:%lld", p->u.i);
|
|
}else if( p->flags & MEM_Int ){
|
|
printf(" i:%lld", p->u.i);
|
|
+#endif
|
|
#ifndef SQLITE_OMIT_FLOATING_POINT
|
|
}else if( p->flags & MEM_Real ){
|
|
printf(" r:%g", p->u.r);
|
|
@@ -85035,19 +85867,38 @@ case OP_ResultRow: {
|
|
** to avoid a memcpy().
|
|
*/
|
|
case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
|
|
- i64 nByte;
|
|
+ i64 nByte; /* Total size of the output string or blob */
|
|
+ u16 flags1; /* Initial flags for P1 */
|
|
+ u16 flags2; /* Initial flags for P2 */
|
|
|
|
pIn1 = &aMem[pOp->p1];
|
|
pIn2 = &aMem[pOp->p2];
|
|
pOut = &aMem[pOp->p3];
|
|
+ testcase( pIn1==pIn2 );
|
|
+ testcase( pOut==pIn2 );
|
|
assert( pIn1!=pOut );
|
|
- if( (pIn1->flags | pIn2->flags) & MEM_Null ){
|
|
+ flags1 = pIn1->flags;
|
|
+ testcase( flags1 & MEM_Null );
|
|
+ testcase( pIn2->flags & MEM_Null );
|
|
+ if( (flags1 | pIn2->flags) & MEM_Null ){
|
|
sqlite3VdbeMemSetNull(pOut);
|
|
break;
|
|
}
|
|
- if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem;
|
|
- Stringify(pIn1, encoding);
|
|
- Stringify(pIn2, encoding);
|
|
+ if( (flags1 & (MEM_Str|MEM_Blob))==0 ){
|
|
+ if( sqlite3VdbeMemStringify(pIn1,encoding,0) ) goto no_mem;
|
|
+ flags1 = pIn1->flags & ~MEM_Str;
|
|
+ }else if( (flags1 & MEM_Zero)!=0 ){
|
|
+ if( sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem;
|
|
+ flags1 = pIn1->flags & ~MEM_Str;
|
|
+ }
|
|
+ flags2 = pIn2->flags;
|
|
+ if( (flags2 & (MEM_Str|MEM_Blob))==0 ){
|
|
+ if( sqlite3VdbeMemStringify(pIn2,encoding,0) ) goto no_mem;
|
|
+ flags2 = pIn2->flags & ~MEM_Str;
|
|
+ }else if( (flags2 & MEM_Zero)!=0 ){
|
|
+ if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
|
|
+ flags2 = pIn2->flags & ~MEM_Str;
|
|
+ }
|
|
nByte = pIn1->n + pIn2->n;
|
|
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
goto too_big;
|
|
@@ -85058,8 +85909,12 @@ case OP_Concat: { /* same as T
|
|
MemSetTypeFlag(pOut, MEM_Str);
|
|
if( pOut!=pIn2 ){
|
|
memcpy(pOut->z, pIn2->z, pIn2->n);
|
|
+ assert( (pIn2->flags & MEM_Dyn) == (flags2 & MEM_Dyn) );
|
|
+ pIn2->flags = flags2;
|
|
}
|
|
memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n);
|
|
+ assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
|
|
+ pIn1->flags = flags1;
|
|
pOut->z[nByte]=0;
|
|
pOut->z[nByte+1] = 0;
|
|
pOut->flags |= MEM_Term;
|
|
@@ -85185,7 +86040,7 @@ fp_math:
|
|
}
|
|
pOut->u.r = rB;
|
|
MemSetTypeFlag(pOut, MEM_Real);
|
|
- if( ((type1|type2)&MEM_Real)==0 && !bIntint ){
|
|
+ if( ((type1|type2)&(MEM_Real|MEM_IntReal))==0 && !bIntint ){
|
|
sqlite3VdbeIntegerAffinity(pOut);
|
|
}
|
|
#endif
|
|
@@ -85356,7 +86211,9 @@ case OP_MustBeInt: { /* jump,
|
|
*/
|
|
case OP_RealAffinity: { /* in1 */
|
|
pIn1 = &aMem[pOp->p1];
|
|
- if( pIn1->flags & MEM_Int ){
|
|
+ if( pIn1->flags & (MEM_Int|MEM_IntReal) ){
|
|
+ testcase( pIn1->flags & MEM_Int );
|
|
+ testcase( pIn1->flags & MEM_IntReal );
|
|
sqlite3VdbeMemRealify(pIn1);
|
|
}
|
|
break;
|
|
@@ -85548,7 +86405,7 @@ case OP_Ge: { /* same as TK_
|
|
affinity = pOp->p5 & SQLITE_AFF_MASK;
|
|
if( affinity>=SQLITE_AFF_NUMERIC ){
|
|
if( (flags1 | flags3)&MEM_Str ){
|
|
- if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
|
|
+ if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
|
|
applyNumericAffinity(pIn1,0);
|
|
assert( flags3==pIn3->flags );
|
|
/* testcase( flags3!=pIn3->flags );
|
|
@@ -85558,7 +86415,7 @@ case OP_Ge: { /* same as TK_
|
|
** in case our analysis is incorrect, so it is left in. */
|
|
flags3 = pIn3->flags;
|
|
}
|
|
- if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
|
|
+ if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
|
|
applyNumericAffinity(pIn3,0);
|
|
}
|
|
}
|
|
@@ -85571,17 +86428,19 @@ case OP_Ge: { /* same as TK_
|
|
goto compare_op;
|
|
}
|
|
}else if( affinity==SQLITE_AFF_TEXT ){
|
|
- if( (flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0 ){
|
|
+ if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
|
|
testcase( pIn1->flags & MEM_Int );
|
|
testcase( pIn1->flags & MEM_Real );
|
|
+ testcase( pIn1->flags & MEM_IntReal );
|
|
sqlite3VdbeMemStringify(pIn1, encoding, 1);
|
|
testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
|
|
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
|
|
assert( pIn1!=pIn3 );
|
|
}
|
|
- if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){
|
|
+ if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
|
|
testcase( pIn3->flags & MEM_Int );
|
|
testcase( pIn3->flags & MEM_Real );
|
|
+ testcase( pIn3->flags & MEM_IntReal );
|
|
sqlite3VdbeMemStringify(pIn3, encoding, 1);
|
|
testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) );
|
|
flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask);
|
|
@@ -86059,7 +86918,7 @@ case OP_Column: {
|
|
VdbeCursor *pC; /* The VDBE cursor */
|
|
BtCursor *pCrsr; /* The BTree cursor */
|
|
u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
|
|
- int len; /* The length of the serialized data for the column */
|
|
+ size_t len; /* The length of the serialized data for the column */
|
|
int i; /* Loop counter */
|
|
Mem *pDest; /* Where to write the extracted value */
|
|
Mem sMem; /* For storing the record being decoded */
|
|
@@ -86337,12 +87196,20 @@ case OP_Affinity: {
|
|
assert( pOp->p2>0 );
|
|
assert( zAffinity[pOp->p2]==0 );
|
|
pIn1 = &aMem[pOp->p1];
|
|
- do{
|
|
+ while( 1 /*edit-by-break*/ ){
|
|
assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] );
|
|
assert( memIsValid(pIn1) );
|
|
- applyAffinity(pIn1, *(zAffinity++), encoding);
|
|
+ applyAffinity(pIn1, zAffinity[0], encoding);
|
|
+ if( zAffinity[0]==SQLITE_AFF_REAL && (pIn1->flags & MEM_Int)!=0 ){
|
|
+ /* When applying REAL affinity, if the result is still MEM_Int,
|
|
+ ** indicate that REAL is actually desired */
|
|
+ pIn1->flags |= MEM_IntReal;
|
|
+ pIn1->flags &= ~MEM_Int;
|
|
+ }
|
|
+ zAffinity++;
|
|
+ if( zAffinity[0]==0 ) break;
|
|
pIn1++;
|
|
- }while( zAffinity[0] );
|
|
+ }
|
|
break;
|
|
}
|
|
|
|
@@ -87535,20 +88402,24 @@ case OP_SeekGT: { /* jump, in3, gr
|
|
** blob, or NULL. But it needs to be an integer before we can do
|
|
** the seek, so convert it. */
|
|
pIn3 = &aMem[pOp->p3];
|
|
- if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
|
|
+ if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){
|
|
applyNumericAffinity(pIn3, 0);
|
|
}
|
|
iKey = sqlite3VdbeIntValue(pIn3);
|
|
|
|
/* If the P3 value could not be converted into an integer without
|
|
** loss of information, then special processing is required... */
|
|
- if( (pIn3->flags & MEM_Int)==0 ){
|
|
+ if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
|
|
if( (pIn3->flags & MEM_Real)==0 ){
|
|
- /* If the P3 value cannot be converted into any kind of a number,
|
|
- ** then the seek is not possible, so jump to P2 */
|
|
- VdbeBranchTaken(1,2); goto jump_to_p2;
|
|
- break;
|
|
- }
|
|
+ if( (pIn3->flags & MEM_Null) || oc>=OP_SeekGE ){
|
|
+ VdbeBranchTaken(1,2); goto jump_to_p2;
|
|
+ break;
|
|
+ }else{
|
|
+ rc = sqlite3BtreeLast(pC->uc.pCursor, &res);
|
|
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
|
+ goto seek_not_found;
|
|
+ }
|
|
+ }else
|
|
|
|
/* If the approximation iKey is larger than the actual real search
|
|
** term, substitute >= for > and < for <=. e.g. if the search term
|
|
@@ -87572,7 +88443,7 @@ case OP_SeekGT: { /* jump, in3, gr
|
|
assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) );
|
|
if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++;
|
|
}
|
|
- }
|
|
+ }
|
|
rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res);
|
|
pC->movetoTarget = iKey; /* Used by OP_Delete */
|
|
if( rc!=SQLITE_OK ){
|
|
@@ -87927,7 +88798,9 @@ case OP_SeekRowid: { /* jump, in3
|
|
u64 iKey;
|
|
|
|
pIn3 = &aMem[pOp->p3];
|
|
- if( (pIn3->flags & MEM_Int)==0 ){
|
|
+ testcase( pIn3->flags & MEM_Int );
|
|
+ testcase( pIn3->flags & MEM_IntReal );
|
|
+ if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
|
|
/* Make sure pIn3->u.i contains a valid integer representation of
|
|
** the key value, but do not change the datatype of the register, as
|
|
** other parts of the perpared statement might be depending on the
|
|
@@ -89227,7 +90100,7 @@ case OP_Destroy: { /* out2 */
|
|
** See also: Destroy
|
|
*/
|
|
case OP_Clear: {
|
|
- int nChange;
|
|
+ u64 nChange;
|
|
|
|
sqlite3VdbeIncrWriteCounter(p, 0);
|
|
nChange = 0;
|
|
@@ -91394,7 +92267,7 @@ SQLITE_API int sqlite3_blob_open(
|
|
goto blob_open_out;
|
|
}
|
|
pBlob->pTab = pTab;
|
|
- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
|
|
+ pBlob->zDb = (char *) db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
|
|
|
|
/* Now search pTab for the exact column. */
|
|
for(iCol=0; iCol<pTab->nCol; iCol++) {
|
|
@@ -92335,7 +93208,7 @@ static int vdbeSorterMapFile(SortSubtask
|
|
int rc = SQLITE_OK;
|
|
if( pFile->iEof<=(i64)(pTask->pSorter->db->nMaxSorterMmap) ){
|
|
sqlite3_file *pFd = pFile->pFd;
|
|
- if( pFd->pMethods->iVersion>=3 ){
|
|
+ if( pFd->pMethods->iVersion>=3 && pFd->pMethods->xFetch ){
|
|
rc = sqlite3OsFetch(pFd, 0, (int)pFile->iEof, (void**)pp);
|
|
testcase( rc!=SQLITE_OK );
|
|
}
|
|
@@ -92527,10 +93400,10 @@ static int vdbeSorterCompareText(
|
|
const void *pKey1, int nKey1, /* Left side of comparison */
|
|
const void *pKey2, int nKey2 /* Right side of comparison */
|
|
){
|
|
- const u8 * const p1 = (const u8 * const)pKey1;
|
|
- const u8 * const p2 = (const u8 * const)pKey2;
|
|
- const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */
|
|
- const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */
|
|
+ const u8 *p1 = (const u8 *)pKey1;
|
|
+ const u8 *p2 = (const u8 *)pKey2;
|
|
+ const u8 *v1 = &p1[ p1[0] ]; /* Pointer to value 1 */
|
|
+ const u8 *v2 = &p2[ p2[0] ]; /* Pointer to value 2 */
|
|
|
|
int n1;
|
|
int n2;
|
|
@@ -93135,7 +94008,7 @@ static int vdbeSorterSort(SortSubtask *p
|
|
if( (u8*)p==pList->aMemory ){
|
|
pNext = 0;
|
|
}else{
|
|
- assert( p->u.iNext<sqlite3MallocSize(pList->aMemory) );
|
|
+ assert( (unsigned)p->u.iNext<sqlite3MallocSize(pList->aMemory) );
|
|
pNext = (SorterRecord*)&pList->aMemory[p->u.iNext];
|
|
}
|
|
}else{
|
|
@@ -94716,7 +95589,7 @@ static int memjrnlWrite(
|
|
|
|
if( iChunkOffset==0 ){
|
|
/* New chunk is required to extend the file. */
|
|
- FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
|
|
+ FileChunk *pNew = sqlite3Malloc(fileChunkSize(p->nChunkSize));
|
|
if( !pNew ){
|
|
return SQLITE_IOERR_NOMEM_BKPT;
|
|
}
|
|
@@ -95952,8 +96825,8 @@ static int resolveExprStep(Walker *pWalk
|
|
}
|
|
#endif
|
|
else if( no_such_func && pParse->db->init.busy==0
|
|
-#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
|
- && pParse->explain==0
|
|
+#if 1
|
|
+ && (pParse->explain==0 || !sqlite3GlobalConfig.bEnableUnknownSqlFunction)
|
|
#endif
|
|
){
|
|
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
|
|
@@ -100623,8 +101496,8 @@ expr_code_doover:
|
|
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
|
zId = pExpr->u.zToken;
|
|
pDef = sqlite3FindFunction(db, zId, nFarg, enc, 0);
|
|
-#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
|
- if( pDef==0 && pParse->explain ){
|
|
+#if 1
|
|
+ if( pDef==0 && pParse->explain && sqlite3GlobalConfig.bEnableUnknownSqlFunction){
|
|
pDef = sqlite3FindFunction(db, "unknown", nFarg, enc, 0);
|
|
}
|
|
#endif
|
|
@@ -100664,7 +101537,7 @@ expr_code_doover:
|
|
** the SQLite type logic.
|
|
*/
|
|
if( pDef->funcFlags & SQLITE_FUNC_AFFINITY ){
|
|
- const char *azAff[] = { "blob", "text", "numeric", "integer", "real" };
|
|
+ const char *const azAff[] = { "blob", "text", "numeric", "integer", "real" };
|
|
char aff;
|
|
assert( nFarg==1 );
|
|
aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
|
|
@@ -101824,7 +102697,13 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExp
|
|
){
|
|
return 1;
|
|
}
|
|
- if( pE2->op==TK_NOTNULL && pE1->op!=TK_ISNULL && pE1->op!=TK_IS ){
|
|
+ if( pE2->op==TK_NOTNULL
|
|
+ && pE1->op!=TK_ISNULL
|
|
+ && pE1->op!=TK_IS
|
|
+ && pE1->op!=TK_ISNOT
|
|
+ && pE1->op!=TK_OR
|
|
+ && pE1->op!=TK_CASE
|
|
+ ){
|
|
Expr *pX = sqlite3ExprSkipCollate(pE1->pLeft);
|
|
testcase( pX!=pE1->pLeft );
|
|
if( sqlite3ExprCompare(pParse, pX, pE2->pLeft, iTab)==0 ) return 1;
|
|
@@ -102401,7 +103280,7 @@ static void renameTestSchema(Parse *pPar
|
|
sqlite3NestedParse(pParse,
|
|
"SELECT 1 "
|
|
"FROM \"%w\".%s "
|
|
- "WHERE name NOT LIKE 'sqlite_%%'"
|
|
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
|
" AND sql NOT LIKE 'create virtual%%'"
|
|
" AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
|
|
zDb, MASTER_NAME,
|
|
@@ -102412,7 +103291,7 @@ static void renameTestSchema(Parse *pPar
|
|
sqlite3NestedParse(pParse,
|
|
"SELECT 1 "
|
|
"FROM temp.%s "
|
|
- "WHERE name NOT LIKE 'sqlite_%%'"
|
|
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
|
" AND sql NOT LIKE 'create virtual%%'"
|
|
" AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
|
|
MASTER_NAME, zDb
|
|
@@ -102443,7 +103322,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTa
|
|
Token *pName /* The new table name. */
|
|
){
|
|
int iDb; /* Database that contains the table */
|
|
- char *zDb; /* Name of database iDb */
|
|
+ const char *zDb; /* Name of database iDb */
|
|
Table *pTab; /* Table being renamed */
|
|
char *zName = 0; /* NULL-terminated version of pName */
|
|
sqlite3 *db = pParse->db; /* Database connection */
|
|
@@ -102533,7 +103412,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTa
|
|
"UPDATE \"%w\".%s SET "
|
|
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
|
|
"WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
|
|
- "AND name NOT LIKE 'sqlite_%%'"
|
|
+ "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
|
, zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
|
|
);
|
|
|
|
@@ -102544,7 +103423,8 @@ SQLITE_PRIVATE void sqlite3AlterRenameTa
|
|
"tbl_name = %Q, "
|
|
"name = CASE "
|
|
"WHEN type='table' THEN %Q "
|
|
- "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
|
|
+ "WHEN name LIKE 'sqliteX_autoindex%%' ESCAPE 'X' "
|
|
+ " AND type='index' THEN "
|
|
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
|
|
"ELSE name END "
|
|
"WHERE tbl_name=%Q COLLATE nocase AND "
|
|
@@ -102918,7 +103798,8 @@ SQLITE_PRIVATE void sqlite3AlterRenameCo
|
|
sqlite3NestedParse(pParse,
|
|
"UPDATE \"%w\".%s SET "
|
|
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
|
|
- "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)"
|
|
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
|
|
+ " AND (type != 'index' OR tbl_name = %Q)"
|
|
" AND sql NOT LIKE 'create virtual%%'",
|
|
zDb, MASTER_NAME,
|
|
zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
|
|
@@ -104269,7 +105150,7 @@ struct Stat4Sample {
|
|
i64 iRowid; /* Rowid in main table of the key */
|
|
u8 *aRowid; /* Key for WITHOUT ROWID tables */
|
|
} u;
|
|
- u32 nRowid; /* Sizeof aRowid[] */
|
|
+ size_t nRowid; /* Sizeof aRowid[] */
|
|
u8 isPSample; /* True if a periodic sample */
|
|
int iCol; /* If !isPSample, the reason for inclusion */
|
|
u32 iHash; /* Tiebreaker hash */
|
|
@@ -104307,7 +105188,7 @@ static void sampleClear(sqlite3 *db, Sta
|
|
/* Initialize the BLOB value of a ROWID
|
|
*/
|
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
-static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
|
|
+static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, size_t n, const u8 *pData){
|
|
assert( db!=0 );
|
|
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
|
p->u.aRowid = sqlite3DbMallocRawNN(db, n);
|
|
@@ -105385,7 +106266,8 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse
|
|
sqlite3 *db = pParse->db;
|
|
int iDb;
|
|
int i;
|
|
- char *z, *zDb;
|
|
+ char *z;
|
|
+ const char *zDb;
|
|
Table *pTab;
|
|
Index *pIdx;
|
|
Token *pTableName;
|
|
@@ -105715,14 +106597,14 @@ static int loadStatTbl(
|
|
|
|
char *zIndex; /* Index name */
|
|
Index *pIdx; /* Pointer to the index object */
|
|
- int nSample; /* Number of samples */
|
|
+ size_t nSample; /* Number of samples */
|
|
int nByte; /* Bytes of space required */
|
|
int i; /* Bytes of space required */
|
|
tRowcnt *pSpace;
|
|
|
|
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
|
if( zIndex==0 ) continue;
|
|
- nSample = sqlite3_column_int(pStmt, 1);
|
|
+ nSample = sqlite3_column_int64(pStmt, 1);
|
|
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
|
assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
|
|
/* Index.nSample is non-zero at this point if data has already been
|
|
@@ -106048,7 +106930,7 @@ static void attachFunc(
|
|
goto attach_error;
|
|
}
|
|
for(i=0; i<db->nDb; i++){
|
|
- char *z = db->aDb[i].zDbSName;
|
|
+ const char *z = db->aDb[i].zDbSName;
|
|
assert( z && zName );
|
|
if( sqlite3StrICmp(z, zName)==0 ){
|
|
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
|
@@ -106673,7 +107555,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(
|
|
int iDb /* Index of containing database. */
|
|
){
|
|
sqlite3 *db = pParse->db; /* Database handle */
|
|
- char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
|
|
+ const char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
|
|
int rc; /* Auth callback return code */
|
|
|
|
if( db->init.busy ) return SQLITE_OK;
|
|
@@ -106950,9 +107832,9 @@ static void codeTableLocks(Parse *pParse
|
|
/*
|
|
** Return TRUE if the given yDbMask object is empty - if it contains no
|
|
** 1 bits. This routine is used by the DbMaskAllZero() and DbMaskNotZero()
|
|
-** macros when SQLITE_MAX_ATTACHED is greater than 30.
|
|
+** macros when SQLITE_MAX_ATTACHED is greater than 62.
|
|
*/
|
|
-#if SQLITE_MAX_ATTACHED>30
|
|
+#if SQLITE_MAX_ATTACHED>62
|
|
SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask m){
|
|
int i;
|
|
for(i=0; i<sizeof(yDbMask); i++) if( m[i] ) return 0;
|
|
@@ -107347,7 +108229,7 @@ SQLITE_PRIVATE void sqlite3CollapseDatab
|
|
for(i=j=2; i<db->nDb; i++){
|
|
struct Db *pDb = &db->aDb[i];
|
|
if( pDb->pBt==0 ){
|
|
- sqlite3DbFree(db, pDb->zDbSName);
|
|
+ sqlite3DbFree(db, (char *)pDb->zDbSName);
|
|
pDb->zDbSName = 0;
|
|
continue;
|
|
}
|
|
@@ -107767,7 +108649,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
|
|
SQLITE_CREATE_VIEW,
|
|
SQLITE_CREATE_TEMP_VIEW
|
|
};
|
|
- char *zDb = db->aDb[iDb].zDbSName;
|
|
+ const char *zDb = db->aDb[iDb].zDbSName;
|
|
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
|
|
goto begin_table_error;
|
|
}
|
|
@@ -107786,7 +108668,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
|
|
** collisions.
|
|
*/
|
|
if( !IN_SPECIAL_PARSE ){
|
|
- char *zDb = db->aDb[iDb].zDbSName;
|
|
+ const char *zDb = db->aDb[iDb].zDbSName;
|
|
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
|
goto begin_table_error;
|
|
}
|
|
@@ -108452,7 +109334,7 @@ static void identPut(char *z, int *pIdx,
|
|
static char *createTableStmt(sqlite3 *db, Table *p){
|
|
int i, k, n;
|
|
char *zStmt;
|
|
- char *zSep, *zSep2, *zEnd;
|
|
+ const char *zSep, *zSep2, *zEnd;
|
|
Column *pCol;
|
|
n = 0;
|
|
for(pCol = p->aCol, i=0; i<p->nCol; i++, pCol++){
|
|
@@ -108659,13 +109541,16 @@ static void convertToWithoutRowidTable(P
|
|
pList = sqlite3ExprListAppend(pParse, 0,
|
|
sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0));
|
|
if( pList==0 ) return;
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey);
|
|
+ }
|
|
pList->a[0].sortOrder = pParse->iPkSortOrder;
|
|
assert( pParse->pNewTable==pTab );
|
|
+ pTab->iPKey = -1;
|
|
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
|
|
SQLITE_IDXTYPE_PRIMARYKEY);
|
|
if( db->mallocFailed || pParse->nErr ) return;
|
|
pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
- pTab->iPKey = -1;
|
|
}else{
|
|
pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
assert( pPk!=0 );
|
|
@@ -108720,6 +109605,10 @@ static void convertToWithoutRowidTable(P
|
|
if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){
|
|
pIdx->aiColumn[j] = pPk->aiColumn[i];
|
|
pIdx->azColl[j] = pPk->azColl[i];
|
|
+ if( pPk->aSortOrder[i] ){
|
|
+ /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */
|
|
+ pIdx->bAscKeyBug = 1;
|
|
+ }
|
|
j++;
|
|
}
|
|
}
|
|
@@ -108883,8 +109772,8 @@ SQLITE_PRIVATE void sqlite3EndTable(
|
|
if( !db->init.busy ){
|
|
int n;
|
|
Vdbe *v;
|
|
- char *zType; /* "view" or "table" */
|
|
- char *zType2; /* "VIEW" or "TABLE" */
|
|
+ const char *zType; /* "view" or "table" */
|
|
+ const char *zType2; /* "VIEW" or "TABLE" */
|
|
char *zStmt; /* Text of the CREATE TABLE or CREATE VIEW statement */
|
|
|
|
v = sqlite3GetVdbe(pParse);
|
|
@@ -109841,7 +110730,16 @@ static void sqlite3RefillIndex(Parse *pP
|
|
addr2 = sqlite3VdbeCurrentAddr(v);
|
|
}
|
|
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
|
|
- sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
|
|
+ if( !pIndex->bAscKeyBug ){
|
|
+ /* This OP_SeekEnd opcode makes index insert for a REINDEX go much
|
|
+ ** faster by avoiding unnecessary seeks. But the optimization does
|
|
+ ** not work for UNIQUE constraint indexes on WITHOUT ROWID tables
|
|
+ ** with DESC primary keys, since those indexes have there keys in
|
|
+ ** a different order from the main table.
|
|
+ ** See ticket: https://www.sqlite.org/src/info/bba7b69f9849b5bf
|
|
+ */
|
|
+ sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
|
|
+ }
|
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
|
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
|
sqlite3ReleaseTempReg(pParse, regRecord);
|
|
@@ -112171,7 +113069,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
|
|
Expr *pWhere, /* The WHERE clause. May be null */
|
|
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
|
|
Expr *pLimit, /* The LIMIT clause. May be null */
|
|
- char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
|
+ const char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
|
){
|
|
sqlite3 *db = pParse->db;
|
|
Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */
|
|
@@ -112341,6 +113239,11 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
|
|
pOrderBy = 0;
|
|
pLimit = 0;
|
|
}
|
|
+#else
|
|
+ if( pOrderBy || pLimit ){
|
|
+ sqlite3ErrorMsg(pParse, "%s on DELETE not supported", pOrderBy?"ORDER BY":"LIMIT");
|
|
+ goto delete_from_cleanup;
|
|
+ }
|
|
#endif
|
|
|
|
/* If pTab is really a view, make sure it has been initialized.
|
|
@@ -112645,10 +113548,8 @@ delete_from_cleanup:
|
|
sqlite3AuthContextPop(&sContext);
|
|
sqlite3SrcListDelete(db, pTabList);
|
|
sqlite3ExprDelete(db, pWhere);
|
|
-#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
|
sqlite3ExprListDelete(db, pOrderBy);
|
|
sqlite3ExprDelete(db, pLimit);
|
|
-#endif
|
|
sqlite3DbFree(db, aToOpen);
|
|
return;
|
|
}
|
|
@@ -113068,7 +113969,7 @@ static void typeofFunc(
|
|
int NotUsed,
|
|
sqlite3_value **argv
|
|
){
|
|
- static const char *azType[] = { "integer", "real", "text", "blob", "null" };
|
|
+ static const char *const azType[] = { "integer", "real", "text", "blob", "null" };
|
|
int i = sqlite3_value_type(argv[0]) - 1;
|
|
UNUSED_PARAMETER(NotUsed);
|
|
assert( i>=0 && i<ArraySize(azType) );
|
|
@@ -113271,7 +114172,7 @@ static void substrFunc(
|
|
){
|
|
const unsigned char *z;
|
|
const unsigned char *z2;
|
|
- int len;
|
|
+ size_t len;
|
|
int p0type;
|
|
i64 p1, p2;
|
|
int negP2 = 0;
|
|
@@ -113347,7 +114248,7 @@ static void substrFunc(
|
|
sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
|
|
SQLITE_UTF8);
|
|
}else{
|
|
- if( p1+p2>len ){
|
|
+ if( p1+p2>(i64)len ){
|
|
p2 = len-p1;
|
|
if( p2<0 ) p2 = 0;
|
|
}
|
|
@@ -113550,7 +114451,7 @@ static void changes(
|
|
){
|
|
sqlite3 *db = sqlite3_context_db_handle(context);
|
|
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
- sqlite3_result_int(context, sqlite3_changes(db));
|
|
+ sqlite3_result_int64(context, sqlite3_changes(db));
|
|
}
|
|
|
|
/*
|
|
@@ -113566,7 +114467,7 @@ static void total_changes(
|
|
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
/* IMP: R-52756-41993 This function is a wrapper around the
|
|
** sqlite3_total_changes() C/C++ interface. */
|
|
- sqlite3_result_int(context, sqlite3_total_changes(db));
|
|
+ sqlite3_result_int64(context, sqlite3_total_changes(db));
|
|
}
|
|
|
|
/*
|
|
@@ -114093,7 +114994,7 @@ static void charFunc(
|
|
){
|
|
unsigned char *z, *zOut;
|
|
int i;
|
|
- zOut = z = sqlite3_malloc64( argc*4+1 );
|
|
+ zOut = z = sqlite3Malloc( argc*4+1 );
|
|
if( z==0 ){
|
|
sqlite3_result_error_nomem(context);
|
|
return;
|
|
@@ -114187,10 +115088,10 @@ static void replaceFunc(
|
|
const unsigned char *zPattern; /* The pattern string B */
|
|
const unsigned char *zRep; /* The replacement string C */
|
|
unsigned char *zOut; /* The output */
|
|
- int nStr; /* Size of zStr */
|
|
+ size_t nStr; /* Size of zStr */
|
|
int nPattern; /* Size of zPattern */
|
|
- int nRep; /* Size of zRep */
|
|
- i64 nOut; /* Maximum size of zOut */
|
|
+ size_t nRep; /* Size of zRep */
|
|
+ int nOut; /* Maximum size of zOut */
|
|
int loopLimit; /* Last zStr[] that might match zPattern[] */
|
|
int i, j; /* Loop counters */
|
|
unsigned cntExpand; /* Number zOut expansions */
|
|
@@ -114246,7 +115147,7 @@ static void replaceFunc(
|
|
** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */
|
|
u8 *zOld;
|
|
zOld = zOut;
|
|
- zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1));
|
|
+ zOut = sqlite3Realloc(zOut, (int)nOut + (nOut - nStr - 1));
|
|
if( zOut==0 ){
|
|
sqlite3_result_error_nomem(context);
|
|
sqlite3_free(zOld);
|
|
@@ -114259,7 +115160,7 @@ static void replaceFunc(
|
|
i += nPattern-1;
|
|
}
|
|
}
|
|
- assert( j+nStr-i+1<=nOut );
|
|
+ assert( j+nStr-i+1<=(unsigned)nOut );
|
|
memcpy(&zOut[j], &zStr[i], nStr-i);
|
|
j += nStr - i;
|
|
assert( j<=nOut );
|
|
@@ -114352,7 +115253,7 @@ static void trimFunc(
|
|
}
|
|
|
|
|
|
-#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
|
+#if 1 /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
|
|
/*
|
|
** The "unknown" function is automatically substituted in place of
|
|
** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN
|
|
@@ -114981,7 +115882,7 @@ SQLITE_PRIVATE void sqlite3RegisterBuilt
|
|
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
|
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
|
#endif
|
|
-#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
|
+#if 1 /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
|
|
FUNCTION(unknown, -1, 0, 0, unknownFunc ),
|
|
#endif
|
|
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
|
@@ -117267,7 +118168,7 @@ SQLITE_PRIVATE void sqlite3Insert(
|
|
int nIdx;
|
|
nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
|
|
&iDataCur, &iIdxCur);
|
|
- aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1));
|
|
+ aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+2));
|
|
if( aRegIdx==0 ){
|
|
goto insert_cleanup;
|
|
}
|
|
@@ -117276,6 +118177,7 @@ SQLITE_PRIVATE void sqlite3Insert(
|
|
aRegIdx[i] = ++pParse->nMem;
|
|
pParse->nMem += pIdx->nColumn;
|
|
}
|
|
+ aRegIdx[i] = ++pParse->nMem; /* Register to store the table record */
|
|
}
|
|
#ifndef SQLITE_OMIT_UPSERT
|
|
if( pUpsert ){
|
|
@@ -117679,6 +118581,14 @@ SQLITE_PRIVATE int sqlite3ExprReferences
|
|
** the same as the order of indices on the linked list of indices
|
|
** at pTab->pIndex.
|
|
**
|
|
+** (2019-05-07) The generated code also creates a new record for the
|
|
+** main table, if pTab is a rowid table, and stores that record in the
|
|
+** register identified by aRegIdx[nIdx] - in other words in the first
|
|
+** entry of aRegIdx[] past the last index. It is important that the
|
|
+** record be generated during constraint checks to avoid affinity changes
|
|
+** to the register content that occur after constraint checks but before
|
|
+** the new record is inserted.
|
|
+**
|
|
** The caller must have already opened writeable cursors on the main
|
|
** table and all applicable indices (that is to say, all indices for which
|
|
** aRegIdx[] is not zero). iDataCur is the cursor for the main table when
|
|
@@ -118298,6 +119208,16 @@ SQLITE_PRIVATE void sqlite3GenerateConst
|
|
sqlite3VdbeJumpHere(v, ipkBottom);
|
|
}
|
|
|
|
+ /* Generate the table record */
|
|
+ if( HasRowid(pTab) ){
|
|
+ int regRec = aRegIdx[ix];
|
|
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData+1, pTab->nCol, regRec);
|
|
+ sqlite3SetMakeRecordP5(v, pTab);
|
|
+ if( !bAffinityDone ){
|
|
+ sqlite3TableAffinity(v, pTab, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
*pbMayReplace = seenReplace;
|
|
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
|
|
}
|
|
@@ -118347,10 +119267,7 @@ SQLITE_PRIVATE void sqlite3CompleteInser
|
|
Vdbe *v; /* Prepared statements under construction */
|
|
Index *pIdx; /* An index being inserted or updated */
|
|
u8 pik_flags; /* flag values passed to the btree insert */
|
|
- int regData; /* Content registers (after the rowid) */
|
|
- int regRec; /* Register holding assembled record for the table */
|
|
int i; /* Loop counter */
|
|
- u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
|
|
|
|
assert( update_flags==0
|
|
|| update_flags==OPFLAG_ISUPDATE
|
|
@@ -118362,7 +119279,6 @@ SQLITE_PRIVATE void sqlite3CompleteInser
|
|
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
|
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
|
if( aRegIdx[i]==0 ) continue;
|
|
- bAffinityDone = 1;
|
|
if( pIdx->pPartIdxWhere ){
|
|
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
|
|
VdbeCoverage(v);
|
|
@@ -118390,13 +119306,6 @@ SQLITE_PRIVATE void sqlite3CompleteInser
|
|
sqlite3VdbeChangeP5(v, pik_flags);
|
|
}
|
|
if( !HasRowid(pTab) ) return;
|
|
- regData = regNewData + 1;
|
|
- regRec = sqlite3GetTempReg(pParse);
|
|
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
|
|
- sqlite3SetMakeRecordP5(v, pTab);
|
|
- if( !bAffinityDone ){
|
|
- sqlite3TableAffinity(v, pTab, 0);
|
|
- }
|
|
if( pParse->nested ){
|
|
pik_flags = 0;
|
|
}else{
|
|
@@ -118409,7 +119318,7 @@ SQLITE_PRIVATE void sqlite3CompleteInser
|
|
if( useSeekResult ){
|
|
pik_flags |= OPFLAG_USESEEKRESULT;
|
|
}
|
|
- sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
|
|
+ sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, aRegIdx[i], regNewData);
|
|
if( !pParse->nested ){
|
|
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
|
}
|
|
@@ -119100,7 +120009,7 @@ exec_out:
|
|
struct sqlite3_api_routines {
|
|
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
|
int (*aggregate_count)(sqlite3_context*);
|
|
- int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
|
+ int (*bind_blob)(sqlite3_stmt*,int,const void*,unsigned int,void(*)(void*));
|
|
int (*bind_double)(sqlite3_stmt*,int,double);
|
|
int (*bind_int)(sqlite3_stmt*,int,int);
|
|
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
|
@@ -119108,20 +120017,20 @@ struct sqlite3_api_routines {
|
|
int (*bind_parameter_count)(sqlite3_stmt*);
|
|
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
|
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
|
- int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
|
+ int (*bind_text)(sqlite3_stmt*,int,const char*,int,void(*)(void*));
|
|
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
|
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
|
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
|
int (*busy_timeout)(sqlite3*,int ms);
|
|
- int (*changes)(sqlite3*);
|
|
+ sqlite_uint64 (*changes)(sqlite3*);
|
|
int (*close)(sqlite3*);
|
|
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
int eTextRep,const char*));
|
|
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
int eTextRep,const void*));
|
|
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
|
- int (*column_bytes)(sqlite3_stmt*,int iCol);
|
|
- int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
|
+ unsigned int (*column_bytes)(sqlite3_stmt*,int iCol);
|
|
+ unsigned int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
|
int (*column_count)(sqlite3_stmt*pStmt);
|
|
const char * (*column_database_name)(sqlite3_stmt*,int);
|
|
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
|
@@ -119186,7 +120095,7 @@ struct sqlite3_api_routines {
|
|
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
|
void *(*realloc)(void*,int);
|
|
int (*reset)(sqlite3_stmt*pStmt);
|
|
- void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
+ void (*result_blob)(sqlite3_context*,const void*,unsigned int,void(*)(void*));
|
|
void (*result_double)(sqlite3_context*,double);
|
|
void (*result_error)(sqlite3_context*,const char*,int);
|
|
void (*result_error16)(sqlite3_context*,const void*,int);
|
|
@@ -119207,15 +120116,15 @@ struct sqlite3_api_routines {
|
|
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
|
char const**,char const**,int*,int*,int*);
|
|
void (*thread_cleanup)(void);
|
|
- int (*total_changes)(sqlite3*);
|
|
+ sqlite_uint64 (*total_changes)(sqlite3*);
|
|
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
|
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
|
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
|
sqlite_int64),void*);
|
|
void * (*user_data)(sqlite3_context*);
|
|
const void * (*value_blob)(sqlite3_value*);
|
|
- int (*value_bytes)(sqlite3_value*);
|
|
- int (*value_bytes16)(sqlite3_value*);
|
|
+ unsigned int (*value_bytes)(sqlite3_value*);
|
|
+ unsigned int (*value_bytes16)(sqlite3_value*);
|
|
double (*value_double)(sqlite3_value*);
|
|
int (*value_int)(sqlite3_value*);
|
|
sqlite_int64 (*value_int64)(sqlite3_value*);
|
|
@@ -119415,10 +120324,9 @@ typedef int (*sqlite3_loadext_entry)(
|
|
*/
|
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
|
-#endif
|
|
+#ifndef sqlite3_bind_blob
|
|
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
|
+#endif
|
|
#define sqlite3_bind_double sqlite3_api->bind_double
|
|
#define sqlite3_bind_int sqlite3_api->bind_int
|
|
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
|
@@ -119426,12 +120334,19 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
|
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
|
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
|
+#ifndef sqlite3_bind_text
|
|
#define sqlite3_bind_text sqlite3_api->bind_text
|
|
+#endif
|
|
+#ifndef sqlite3_bind_text16
|
|
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
|
+#endif
|
|
#define sqlite3_bind_value sqlite3_api->bind_value
|
|
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
|
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
|
-#define sqlite3_changes sqlite3_api->changes
|
|
+#undef sqlite3_changes
|
|
+#define sqlite3_changes (int)sqlite3_api->changes
|
|
+#undef sqlite3_changes64
|
|
+#define sqlite3_changes64 sqlite3_api->changes
|
|
#define sqlite3_close sqlite3_api->close
|
|
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
|
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
|
@@ -119459,11 +120374,14 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
|
#define sqlite3_complete sqlite3_api->complete
|
|
#define sqlite3_complete16 sqlite3_api->complete16
|
|
-#define sqlite3_create_collation sqlite3_api->create_collation
|
|
+#undef sqlite3_create_collation
|
|
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
|
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
|
-#define sqlite3_create_function sqlite3_api->create_function
|
|
+#undef sqlite3_create_function
|
|
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
|
#define sqlite3_create_function16 sqlite3_api->create_function16
|
|
-#define sqlite3_create_module sqlite3_api->create_module
|
|
+#undef sqlite3_create_module
|
|
+#define sqlite3_create_module(a,b,c,d) sqlite3_create_module_v2(a,b,c,d,0)
|
|
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
|
#define sqlite3_data_count sqlite3_api->data_count
|
|
#define sqlite3_db_handle sqlite3_api->db_handle
|
|
@@ -119473,23 +120391,19 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_errmsg sqlite3_api->errmsg
|
|
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
|
#define sqlite3_exec sqlite3_api->exec
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_expired sqlite3_api->expired
|
|
-#endif
|
|
#define sqlite3_finalize sqlite3_api->finalize
|
|
#define sqlite3_free sqlite3_api->free
|
|
#define sqlite3_free_table sqlite3_api->free_table
|
|
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
|
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
|
#define sqlite3_get_table sqlite3_api->get_table
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_global_recover sqlite3_api->global_recover
|
|
-#endif
|
|
#define sqlite3_interrupt sqlite3_api->interruptx
|
|
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
|
#define sqlite3_libversion sqlite3_api->libversion
|
|
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
|
+#ifndef sqlite3_malloc
|
|
#define sqlite3_malloc sqlite3_api->malloc
|
|
+#endif
|
|
#define sqlite3_mprintf sqlite3_api->mprintf
|
|
#define sqlite3_open sqlite3_api->open
|
|
#define sqlite3_open16 sqlite3_api->open16
|
|
@@ -119499,19 +120413,31 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
#define sqlite3_profile sqlite3_api->profile
|
|
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
|
+#ifndef sqlite3_realloc
|
|
#define sqlite3_realloc sqlite3_api->realloc
|
|
+#endif
|
|
#define sqlite3_reset sqlite3_api->reset
|
|
+#ifndef sqlite3_result_blob
|
|
#define sqlite3_result_blob sqlite3_api->result_blob
|
|
+#endif
|
|
#define sqlite3_result_double sqlite3_api->result_double
|
|
#define sqlite3_result_error sqlite3_api->result_error
|
|
#define sqlite3_result_error16 sqlite3_api->result_error16
|
|
#define sqlite3_result_int sqlite3_api->result_int
|
|
#define sqlite3_result_int64 sqlite3_api->result_int64
|
|
#define sqlite3_result_null sqlite3_api->result_null
|
|
+#ifndef sqlite3_result_text
|
|
#define sqlite3_result_text sqlite3_api->result_text
|
|
+#endif
|
|
+#ifndef sqlite3_result_text16
|
|
#define sqlite3_result_text16 sqlite3_api->result_text16
|
|
+#endif
|
|
+#ifndef sqlite3_result_text16be
|
|
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
|
+#endif
|
|
+#ifndef sqlite3_result_text16le
|
|
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
|
+#endif
|
|
#define sqlite3_result_value sqlite3_api->result_value
|
|
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
|
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
|
@@ -119519,12 +120445,11 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
|
#define sqlite3_step sqlite3_api->step
|
|
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
|
-#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
|
-#define sqlite3_total_changes sqlite3_api->total_changes
|
|
+#undef sqlite3_total_changes
|
|
+#define sqlite3_total_changes (int)sqlite3_api->total_changes
|
|
+#undef sqlite3_total_changes64
|
|
+#define sqlite3_total_changes64 sqlite3_api->total_changes
|
|
#define sqlite3_trace sqlite3_api->trace
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
|
-#endif
|
|
#define sqlite3_update_hook sqlite3_api->update_hook
|
|
#define sqlite3_user_data sqlite3_api->user_data
|
|
#define sqlite3_value_blob sqlite3_api->value_blob
|
|
@@ -119565,12 +120490,13 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
|
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
|
#define sqlite3_sleep sqlite3_api->sleep
|
|
-#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
|
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
|
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
|
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
|
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
|
+#ifndef sqlite3_result_zeroblob
|
|
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
|
+#endif
|
|
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
|
#define sqlite3_test_control sqlite3_api->test_control
|
|
#define sqlite3_randomness sqlite3_api->randomness
|
|
@@ -119599,7 +120525,8 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
|
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
|
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
|
-#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
|
+#undef sqlite3_wal_checkpoint
|
|
+#define sqlite3_wal_checkpoint(a,b) sqlite3_wal_checkpoint_v2(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0)
|
|
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
|
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
|
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
|
@@ -119797,6 +120724,11 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_blob_reopen 0
|
|
#endif
|
|
|
|
+#ifdef SQLITE_OMIT_DEPRECATED
|
|
+# undef sqlite3_soft_heap_limit
|
|
+# define sqlite3_soft_heap_limit 0
|
|
+#endif
|
|
+
|
|
#if defined(SQLITE_OMIT_TRACE)
|
|
# define sqlite3_trace_v2 0
|
|
#endif
|
|
@@ -119816,7 +120748,8 @@ typedef int (*sqlite3_loadext_entry)(
|
|
** also check to make sure that the pointer to the function is
|
|
** not NULL before calling it.
|
|
*/
|
|
-static const sqlite3_api_routines sqlite3Apis = {
|
|
+#define sqlite3Apis apis_.a
|
|
+static const struct {sqlite3_api_routines a;void *b[10];} apis_ = {{
|
|
sqlite3_aggregate_context,
|
|
#ifndef SQLITE_OMIT_DEPRECATED
|
|
sqlite3_aggregate_count,
|
|
@@ -119893,7 +120826,11 @@ static const sqlite3_api_routines sqlite
|
|
sqlite3_last_insert_rowid,
|
|
sqlite3_libversion,
|
|
sqlite3_libversion_number,
|
|
+#if !defined(SQLITE_OMIT_DEPRECATED) || SQLITE_PTRSIZE<8
|
|
sqlite3_malloc,
|
|
+#else
|
|
+ 0,
|
|
+#endif
|
|
sqlite3_mprintf,
|
|
sqlite3_open,
|
|
sqlite3_open16,
|
|
@@ -119901,7 +120838,11 @@ static const sqlite3_api_routines sqlite
|
|
sqlite3_prepare16,
|
|
sqlite3_profile,
|
|
sqlite3_progress_handler,
|
|
+#if !defined(SQLITE_OMIT_DEPRECATED) || SQLITE_PTRSIZE<8
|
|
sqlite3_realloc,
|
|
+#else
|
|
+ 0,
|
|
+#endif
|
|
sqlite3_reset,
|
|
sqlite3_result_blob,
|
|
sqlite3_result_double,
|
|
@@ -120148,6 +121089,7 @@ static const sqlite3_api_routines sqlite
|
|
/* Version 3.28.0 and later */
|
|
sqlite3_stmt_isexplain,
|
|
sqlite3_value_frombind
|
|
+},{0,0,0,0,0,0,0,0,0,0}
|
|
};
|
|
|
|
/*
|
|
@@ -120176,19 +121118,18 @@ static int sqlite3LoadExtension(
|
|
char *zAltEntry = 0;
|
|
void **aHandle;
|
|
u64 nMsg = 300 + sqlite3Strlen30(zFile);
|
|
- int ii;
|
|
int rc;
|
|
|
|
/* Shared library endings to try if zFile cannot be loaded as written */
|
|
- static const char *azEndings[] = {
|
|
-#if SQLITE_OS_WIN
|
|
+ static const char azEnding[] =
|
|
+#if SQLITE_OS_WIN || defined(__CYGWIN__)
|
|
"dll"
|
|
#elif defined(__APPLE__)
|
|
"dylib"
|
|
#else
|
|
"so"
|
|
#endif
|
|
- };
|
|
+ ;
|
|
|
|
|
|
if( pzErrMsg ) *pzErrMsg = 0;
|
|
@@ -120210,17 +121151,27 @@ static int sqlite3LoadExtension(
|
|
zEntry = zProc ? zProc : "sqlite3_extension_init";
|
|
|
|
handle = sqlite3OsDlOpen(pVfs, zFile);
|
|
-#if SQLITE_OS_UNIX || SQLITE_OS_WIN
|
|
- for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
|
|
- char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
|
|
+ if( handle==0 ){
|
|
+ char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEnding);
|
|
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
|
|
handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
|
sqlite3_free(zAltFile);
|
|
- }
|
|
+#if defined(_WIN32) || defined(__CYGWIN__)
|
|
+ if( handle==0 ){
|
|
+# if defined(__CYGWIN__)
|
|
+ zAltFile = sqlite3_mprintf("cygsqlite3%s-0.%s", zFile, azEnding);
|
|
+# else /* how about Msys/Msys-2 ??? */
|
|
+ zAltFile = sqlite3_mprintf("sqlite3%s.%s", zFile, azEnding);
|
|
+# endif
|
|
+ if( zAltFile==0 ) return SQLITE_NOMEM;
|
|
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
|
+ sqlite3_free(zAltFile);
|
|
+ }
|
|
#endif
|
|
+ }
|
|
if( handle==0 ){
|
|
if( pzErrMsg ){
|
|
- *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
|
|
+ *pzErrMsg = zErrmsg = sqlite3Malloc(nMsg);
|
|
if( zErrmsg ){
|
|
sqlite3_snprintf(nMsg, zErrmsg,
|
|
"unable to open shared library [%s]", zFile);
|
|
@@ -120245,7 +121196,7 @@ static int sqlite3LoadExtension(
|
|
if( xInit==0 && zProc==0 ){
|
|
int iFile, iEntry, c;
|
|
int ncFile = sqlite3Strlen30(zFile);
|
|
- zAltEntry = sqlite3_malloc64(ncFile+30);
|
|
+ zAltEntry = sqlite3Malloc(ncFile+30);
|
|
if( zAltEntry==0 ){
|
|
sqlite3OsDlClose(pVfs, handle);
|
|
return SQLITE_NOMEM_BKPT;
|
|
@@ -120266,7 +121217,7 @@ static int sqlite3LoadExtension(
|
|
if( xInit==0 ){
|
|
if( pzErrMsg ){
|
|
nMsg += sqlite3Strlen30(zEntry);
|
|
- *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
|
|
+ *pzErrMsg = zErrmsg = sqlite3Malloc(nMsg);
|
|
if( zErrmsg ){
|
|
sqlite3_snprintf(nMsg, zErrmsg,
|
|
"no entry point [%s] in shared library [%s]", zEntry, zFile);
|
|
@@ -120401,9 +121352,9 @@ SQLITE_API int sqlite3_auto_extension(
|
|
if( wsdAutoext.aExt[i]==xInit ) break;
|
|
}
|
|
if( i==wsdAutoext.nExt ){
|
|
- u64 nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]);
|
|
+ size_t nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]);
|
|
void (**aNew)(void);
|
|
- aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte);
|
|
+ aNew = sqlite3Realloc(wsdAutoext.aExt, nByte);
|
|
if( aNew==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
@@ -120765,7 +121716,7 @@ static const PragmaName aPragmaName[] =
|
|
/* ColNames: */ 0, 0,
|
|
/* iArg: */ SQLITE_CountRows },
|
|
#endif
|
|
-#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
|
|
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && defined(_WIN32)
|
|
{/* zName: */ "data_store_directory",
|
|
/* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY,
|
|
/* ePragFlg: */ PragFlg_NoColumns1,
|
|
@@ -121216,7 +122167,7 @@ static const PragmaName aPragmaName[] =
|
|
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
|
|
#endif
|
|
};
|
|
-/* Number of pragmas: 62 on by default, 81 total. */
|
|
+/* Number of pragmas: 61 on by default, 81 total. */
|
|
|
|
/************** End of pragma.h **********************************************/
|
|
/************** Continuing where we left off in pragma.c *********************/
|
|
@@ -121448,7 +122399,7 @@ static const char *actionName(u8 action)
|
|
** journal-mode name.
|
|
*/
|
|
SQLITE_PRIVATE const char *sqlite3JournalModename(int eMode){
|
|
- static char * const azModeName[] = {
|
|
+ static const char * const azModeName[] = {
|
|
"delete", "persist", "off", "truncate", "memory"
|
|
#ifndef SQLITE_OMIT_WAL
|
|
, "wal"
|
|
@@ -122118,7 +123069,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|
break;
|
|
}
|
|
|
|
-#if SQLITE_OS_WIN
|
|
+#if defined(_WIN32)
|
|
/*
|
|
** PRAGMA data_store_directory
|
|
** PRAGMA data_store_directory = ""|"directory_name"
|
|
@@ -122171,7 +123122,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
|
char *proxy_file_path = NULL;
|
|
sqlite3_file *pFile = sqlite3PagerFile(pPager);
|
|
- sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE,
|
|
+ sqlite3OsFileControlHint(pFile, SQLITE_FCNTL_GET_LOCKPROXYFILE,
|
|
&proxy_file_path);
|
|
returnSingleText(v, proxy_file_path);
|
|
}else{
|
|
@@ -122179,10 +123130,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|
sqlite3_file *pFile = sqlite3PagerFile(pPager);
|
|
int res;
|
|
if( zRight[0] ){
|
|
- res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
|
|
+ res=sqlite3OsFileControl(pFile, SQLITE_FCNTL_SET_LOCKPROXYFILE,
|
|
zRight);
|
|
} else {
|
|
- res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
|
|
+ res=sqlite3OsFileControl(pFile, SQLITE_FCNTL_SET_LOCKPROXYFILE,
|
|
NULL);
|
|
}
|
|
if( res!=SQLITE_OK ){
|
|
@@ -122385,7 +123336,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|
pParse->nMem = 5;
|
|
sqlite3CodeVerifySchema(pParse, iTabDb);
|
|
for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){
|
|
- const char *azOrigin[] = { "c", "u", "pk" };
|
|
+ const char azOrigin[][4] = { "c", "u", "pk" };
|
|
sqlite3VdbeMultiLoad(v, 1, "isisi",
|
|
i,
|
|
pIdx->zName,
|
|
@@ -122878,7 +123829,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|
if( aOp ){
|
|
aOp[0].p2 = 1-mxErr;
|
|
aOp[2].p4type = P4_STATIC;
|
|
- aOp[2].p4.z = "ok";
|
|
+ aOp[2].p4.z = (char *)"ok";
|
|
aOp[5].p4type = P4_STATIC;
|
|
aOp[5].p4.z = (char*)sqlite3ErrStr(SQLITE_CORRUPT);
|
|
}
|
|
@@ -122913,7 +123864,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|
*/
|
|
case PragTyp_ENCODING: {
|
|
static const struct EncName {
|
|
- char *zName;
|
|
+ const char *zName;
|
|
u8 enc;
|
|
} encnames[] = {
|
|
{ "UTF8", SQLITE_UTF8 },
|
|
@@ -125982,7 +126933,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFr
|
|
** Name of the connection operator, used for error messages.
|
|
*/
|
|
static const char *selectOpName(int id){
|
|
- char *z;
|
|
+ const char *z;
|
|
switch( id ){
|
|
case TK_ALL: z = "UNION ALL"; break;
|
|
case TK_INTERSECT: z = "INTERSECT"; break;
|
|
@@ -126498,7 +127449,7 @@ static void generateColumnNames(
|
|
char *zName = pEList->a[i].zName;
|
|
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT);
|
|
}else if( srcName && p->op==TK_COLUMN ){
|
|
- char *zCol;
|
|
+ const char *zCol;
|
|
int iCol = p->iColumn;
|
|
pTab = p->y.pTab;
|
|
assert( pTab!=0 );
|
|
@@ -126594,7 +127545,7 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExp
|
|
Table *pTab = pColExpr->y.pTab;
|
|
assert( pTab!=0 );
|
|
if( iCol<0 ) iCol = pTab->iPKey;
|
|
- zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
|
|
+ zName = iCol>=0 ? pTab->aCol[iCol].zName : (char *)"rowid";
|
|
}else if( pColExpr->op==TK_ID ){
|
|
assert( !ExprHasProperty(pColExpr, EP_IntValue) );
|
|
zName = pColExpr->u.zToken;
|
|
@@ -131299,7 +132250,7 @@ static int sqlite3_get_table_cb(void *pA
|
|
if( p->nData + need > p->nAlloc ){
|
|
char **azNew;
|
|
p->nAlloc = p->nAlloc*2 + need;
|
|
- azNew = sqlite3_realloc64( p->azResult, sizeof(char*)*p->nAlloc );
|
|
+ azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc );
|
|
if( azNew==0 ) goto malloc_failed;
|
|
p->azResult = azNew;
|
|
}
|
|
@@ -131331,7 +132282,7 @@ static int sqlite3_get_table_cb(void *pA
|
|
z = 0;
|
|
}else{
|
|
int n = sqlite3Strlen30(argv[i])+1;
|
|
- z = sqlite3_malloc64( n );
|
|
+ z = sqlite3Malloc( n );
|
|
if( z==0 ) goto malloc_failed;
|
|
memcpy(z, argv[i], n);
|
|
}
|
|
@@ -131380,7 +132331,7 @@ SQLITE_API int sqlite3_get_table(
|
|
res.nData = 1;
|
|
res.nAlloc = 20;
|
|
res.rc = SQLITE_OK;
|
|
- res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc );
|
|
+ res.azResult = sqlite3Malloc(sizeof(char*)*res.nAlloc );
|
|
if( res.azResult==0 ){
|
|
db->errCode = SQLITE_NOMEM;
|
|
return SQLITE_NOMEM_BKPT;
|
|
@@ -131408,7 +132359,7 @@ SQLITE_API int sqlite3_get_table(
|
|
}
|
|
if( res.nAlloc>res.nData ){
|
|
char **azNew;
|
|
- azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData );
|
|
+ azNew = sqlite3Realloc( res.azResult, sizeof(char*)*res.nData );
|
|
if( azNew==0 ){
|
|
sqlite3_free_table(&res.azResult[1]);
|
|
db->errCode = SQLITE_NOMEM;
|
|
@@ -132275,7 +133226,7 @@ static int codeTriggerProgram(
|
|
return 0;
|
|
}
|
|
|
|
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+#if 1
|
|
/*
|
|
** This function is used to add VdbeComment() annotations to a VDBE
|
|
** program. It is not used in production code, only for debugging.
|
|
@@ -132792,7 +133743,7 @@ SQLITE_PRIVATE void sqlite3Update(
|
|
int iDataCur; /* Cursor for the canonical data btree */
|
|
int iIdxCur; /* Cursor for the first index */
|
|
sqlite3 *db; /* The database structure */
|
|
- int *aRegIdx = 0; /* First register in array assigned to each index */
|
|
+ int *aRegIdx = 0; /* Registers for to each index and the main table */
|
|
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
|
** an expression for the i-th column of the table.
|
|
** aXRef[i]==-1 if the i-th column is not changed. */
|
|
@@ -132871,6 +133822,11 @@ SQLITE_PRIVATE void sqlite3Update(
|
|
pOrderBy = 0;
|
|
pLimit = 0;
|
|
}
|
|
+#else
|
|
+ if( pOrderBy || pLimit ){
|
|
+ sqlite3ErrorMsg(pParse, "%s on UPDATE not supported", pOrderBy?"ORDER BY":"LIMIT");
|
|
+ goto update_cleanup;
|
|
+ }
|
|
#endif
|
|
|
|
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
|
@@ -132906,10 +133862,10 @@ SQLITE_PRIVATE void sqlite3Update(
|
|
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
|
|
** Initialize aXRef[] and aToOpen[] to their default values.
|
|
*/
|
|
- aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
|
|
+ aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx+1) + nIdx+2 );
|
|
if( aXRef==0 ) goto update_cleanup;
|
|
aRegIdx = aXRef+pTab->nCol;
|
|
- aToOpen = (u8*)(aRegIdx+nIdx);
|
|
+ aToOpen = (u8*)(aRegIdx+nIdx+1);
|
|
memset(aToOpen, 1, nIdx+1);
|
|
aToOpen[nIdx+1] = 0;
|
|
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
|
@@ -133011,6 +133967,7 @@ SQLITE_PRIVATE void sqlite3Update(
|
|
if( reg==0 ) aToOpen[j+1] = 0;
|
|
aRegIdx[j] = reg;
|
|
}
|
|
+ aRegIdx[j] = ++pParse->nMem; /* Register storing the table record */
|
|
if( bReplace ){
|
|
/* If REPLACE conflict resolution might be invoked, open cursors on all
|
|
** indexes in case they are needed to delete records. */
|
|
@@ -133461,10 +134418,8 @@ update_cleanup:
|
|
sqlite3SrcListDelete(db, pTabList);
|
|
sqlite3ExprListDelete(db, pChanges);
|
|
sqlite3ExprDelete(db, pWhere);
|
|
-#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
|
sqlite3ExprListDelete(db, pOrderBy);
|
|
sqlite3ExprDelete(db, pLimit);
|
|
-#endif
|
|
return;
|
|
}
|
|
/* Make sure "isView" and other macros defined above are undefined. Otherwise
|
|
@@ -134030,8 +134985,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlit
|
|
Btree *pTemp; /* The temporary database we vacuum into */
|
|
u32 saved_mDbFlags; /* Saved value of db->mDbFlags */
|
|
u64 saved_flags; /* Saved value of db->flags */
|
|
- int saved_nChange; /* Saved value of db->nChange */
|
|
- int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
|
+ u64 saved_nChange; /* Saved value of db->nChange */
|
|
+ u64 saved_nTotalChange; /* Saved value of db->nTotalChange */
|
|
u32 saved_openFlags; /* Saved value of db->openFlags */
|
|
u8 saved_mTrace; /* Saved trace settings */
|
|
Db *pDb = 0; /* Database to detach at end of vacuum */
|
|
@@ -134392,6 +135347,7 @@ static int createModule(
|
|
/*
|
|
** External API function used to create a new virtual-table module.
|
|
*/
|
|
+#undef sqlite3_create_module
|
|
SQLITE_API int sqlite3_create_module(
|
|
sqlite3 *db, /* Database in which module is registered */
|
|
const char *zName, /* Name assigned to this module */
|
|
@@ -134835,7 +135791,7 @@ static int vtabCallConstructor(
|
|
pVTable->pMod = pMod;
|
|
|
|
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
- pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;
|
|
+ pTab->azModuleArg[1] = (char *)db->aDb[iDb].zDbSName;
|
|
|
|
/* Invoke the virtual table constructor */
|
|
assert( &db->pVtabCtx );
|
|
@@ -134881,7 +135837,7 @@ static int vtabCallConstructor(
|
|
pTab->pVTable = pVTable;
|
|
|
|
for(iCol=0; iCol<pTab->nCol; iCol++){
|
|
- char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
|
|
+ char *zType = (char *) sqlite3ColumnType(&pTab->aCol[iCol], 0);
|
|
int nType;
|
|
int i = 0;
|
|
nType = sqlite3Strlen30(zType);
|
|
@@ -135418,7 +136374,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWrita
|
|
if( pTab==pToplevel->apVtabLock[i] ) return;
|
|
}
|
|
n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]);
|
|
- apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n);
|
|
+ apVtabLock = sqlite3Realloc(pToplevel->apVtabLock, n);
|
|
if( apVtabLock ){
|
|
pToplevel->apVtabLock = apVtabLock;
|
|
pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
|
|
@@ -135708,7 +136664,7 @@ struct WhereLoop {
|
|
u8 needFree; /* True if sqlite3_free(idxStr) is needed */
|
|
i8 isOrdered; /* True if satisfies ORDER BY */
|
|
u16 omitMask; /* Terms that may be omitted */
|
|
- char *idxStr; /* Index identifier string */
|
|
+ const char *idxStr; /* Index identifier string */
|
|
} vtab;
|
|
} u;
|
|
u32 wsFlags; /* WHERE_* flags describing the plan */
|
|
@@ -138691,11 +139647,11 @@ static int isLikeOrGlob(
|
|
}
|
|
zNew[iTo] = 0;
|
|
|
|
- /* If the RHS begins with a digit or a minus sign, then the LHS must be
|
|
+ /* If the RHS begins with a digit or a +/- sign, then the LHS must be
|
|
** an ordinary column (not a virtual table column) with TEXT affinity.
|
|
** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
|
|
** even though "lhs LIKE rhs" is true. But if the RHS does not start
|
|
- ** with a digit or '-', then "lhs LIKE rhs" will always be false if
|
|
+ ** with a digit or +/-, then "lhs LIKE rhs" will always be false if
|
|
** the LHS is numeric and so the optimization still works.
|
|
**
|
|
** 2018-09-10 ticket c94369cae9b561b1f996d0054bfab11389f9d033
|
|
@@ -138705,6 +139661,7 @@ static int isLikeOrGlob(
|
|
*/
|
|
if( sqlite3Isdigit(zNew[0])
|
|
|| zNew[0]=='-'
|
|
+ || zNew[0]=='+'
|
|
|| (zNew[0]+1=='0' && iTo==1)
|
|
){
|
|
if( pLeft->op!=TK_COLUMN
|
|
@@ -140823,7 +141780,7 @@ static void constructAutomaticIndex(
|
|
pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed);
|
|
if( pIdx==0 ) goto end_auto_index_create;
|
|
pLoop->u.btree.pIndex = pIdx;
|
|
- pIdx->zName = "auto-index";
|
|
+ pIdx->zName = (char *)"auto-index";
|
|
pIdx->pTable = pTable;
|
|
n = 0;
|
|
idxCols = 0;
|
|
@@ -141839,7 +142796,7 @@ static void whereLoopPrint(WhereLoop *p,
|
|
const char *zName;
|
|
if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
|
|
if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
|
|
- int i = sqlite3Strlen30(zName) - 1;
|
|
+ size_t i = sqlite3Strlen30(zName) - 1;
|
|
while( zName[i]!='_' ) i--;
|
|
zName += i;
|
|
}
|
|
@@ -141890,7 +142847,7 @@ static void whereLoopInit(WhereLoop *p){
|
|
static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
|
|
if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){
|
|
if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){
|
|
- sqlite3_free(p->u.vtab.idxStr);
|
|
+ sqlite3_free((char *)p->u.vtab.idxStr);
|
|
p->u.vtab.needFree = 0;
|
|
p->u.vtab.idxStr = 0;
|
|
}else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
|
|
@@ -143270,7 +144227,7 @@ static int whereLoopAddVirtualOne(
|
|
}
|
|
rc = whereLoopInsert(pBuilder, pNew);
|
|
if( pNew->u.vtab.needFree ){
|
|
- sqlite3_free(pNew->u.vtab.idxStr);
|
|
+ sqlite3_free((char *)pNew->u.vtab.idxStr);
|
|
pNew->u.vtab.needFree = 0;
|
|
}
|
|
WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
|
|
@@ -143439,7 +144396,7 @@ static int whereLoopAddVirtual(
|
|
}
|
|
}
|
|
|
|
- if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
|
|
+ if( p->needToFreeIdxStr ) sqlite3_free((char *)p->idxStr);
|
|
sqlite3DbFreeNN(pParse->db, p);
|
|
WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc));
|
|
return rc;
|
|
@@ -148181,7 +149138,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeSte
|
|
** Alternative datatype for the argument to the malloc() routine passed
|
|
** into sqlite3ParserAlloc(). The default is size_t.
|
|
*/
|
|
-#define YYMALLOCARGTYPE u64
|
|
+#define YYMALLOCARGTYPE size_t
|
|
|
|
/*
|
|
** An instance of the following structure describes the event of a
|
|
@@ -148402,17 +149359,17 @@ typedef union {
|
|
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
|
|
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
|
|
#define YYFALLBACK 1
|
|
-#define YYNSTATE 541
|
|
+#define YYNSTATE 545
|
|
#define YYNRULE 375
|
|
#define YYNTOKEN 176
|
|
-#define YY_MAX_SHIFT 540
|
|
-#define YY_MIN_SHIFTREDUCE 784
|
|
-#define YY_MAX_SHIFTREDUCE 1158
|
|
-#define YY_ERROR_ACTION 1159
|
|
-#define YY_ACCEPT_ACTION 1160
|
|
-#define YY_NO_ACTION 1161
|
|
-#define YY_MIN_REDUCE 1162
|
|
-#define YY_MAX_REDUCE 1536
|
|
+#define YY_MAX_SHIFT 544
|
|
+#define YY_MIN_SHIFTREDUCE 788
|
|
+#define YY_MAX_SHIFTREDUCE 1162
|
|
+#define YY_ERROR_ACTION 1163
|
|
+#define YY_ACCEPT_ACTION 1164
|
|
+#define YY_NO_ACTION 1165
|
|
+#define YY_MIN_REDUCE 1166
|
|
+#define YY_MAX_REDUCE 1540
|
|
/************* End control #defines *******************************************/
|
|
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
|
|
|
|
@@ -148481,221 +149438,221 @@ typedef union {
|
|
*********** Begin parsing tables **********************************************/
|
|
#define YY_ACTTAB_COUNT (2142)
|
|
static const YYACTIONTYPE yy_action[] = {
|
|
- /* 0 */ 535, 1323, 112, 109, 209, 112, 109, 209, 1160, 1,
|
|
- /* 10 */ 1, 540, 2, 1164, 535, 1292, 1228, 1207, 289, 384,
|
|
- /* 20 */ 134, 42, 42, 1427, 382, 1228, 9, 1241, 242, 492,
|
|
- /* 30 */ 1291, 915, 373, 379, 1026, 70, 70, 427, 1026, 916,
|
|
- /* 40 */ 529, 529, 529, 119, 120, 110, 1136, 1136, 981, 984,
|
|
- /* 50 */ 974, 974, 117, 117, 118, 118, 118, 118, 380, 264,
|
|
- /* 60 */ 264, 264, 264, 1134, 264, 264, 112, 109, 209, 397,
|
|
- /* 70 */ 454, 517, 532, 491, 532, 1233, 1233, 532, 239, 206,
|
|
- /* 80 */ 493, 112, 109, 209, 464, 219, 118, 118, 118, 118,
|
|
- /* 90 */ 111, 393, 440, 444, 16, 16, 116, 116, 116, 116,
|
|
- /* 100 */ 115, 115, 114, 114, 114, 113, 415, 971, 971, 982,
|
|
- /* 110 */ 985, 235, 1463, 351, 1134, 419, 384, 116, 116, 116,
|
|
- /* 120 */ 116, 115, 115, 114, 114, 114, 113, 415, 116, 116,
|
|
- /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 961,
|
|
- /* 140 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
|
|
- /* 150 */ 117, 118, 118, 118, 118, 952, 415, 941, 298, 951,
|
|
- /* 160 */ 941, 1480, 540, 2, 1164, 1115, 535, 1458, 160, 289,
|
|
- /* 170 */ 6, 134, 1504, 389, 406, 975, 338, 1024, 1241, 337,
|
|
- /* 180 */ 1089, 1476, 1089, 118, 118, 118, 118, 42, 42, 329,
|
|
- /* 190 */ 951, 951, 953, 116, 116, 116, 116, 115, 115, 114,
|
|
- /* 200 */ 114, 114, 113, 415, 311, 430, 299, 311, 881, 160,
|
|
- /* 210 */ 264, 264, 401, 384, 324, 1115, 1116, 1117, 288, 526,
|
|
- /* 220 */ 96, 159, 1441, 532, 141, 116, 116, 116, 116, 115,
|
|
- /* 230 */ 115, 114, 114, 114, 113, 415, 219, 119, 120, 110,
|
|
- /* 240 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
|
|
- /* 250 */ 118, 118, 115, 115, 114, 114, 114, 113, 415, 288,
|
|
- /* 260 */ 526, 403, 533, 121, 870, 870, 419, 250, 267, 336,
|
|
- /* 270 */ 475, 331, 474, 236, 160, 319, 1084, 322, 1465, 329,
|
|
- /* 280 */ 350, 12, 535, 384, 502, 1115, 1084, 435, 312, 1084,
|
|
+ /* 0 */ 539, 1327, 112, 109, 209, 112, 109, 209, 1164, 1,
|
|
+ /* 10 */ 1, 544, 2, 1168, 539, 1296, 1232, 1211, 289, 388,
|
|
+ /* 20 */ 134, 42, 42, 1431, 386, 1232, 9, 1245, 242, 496,
|
|
+ /* 30 */ 1295, 919, 377, 383, 1030, 70, 70, 431, 1030, 920,
|
|
+ /* 40 */ 533, 533, 533, 119, 120, 110, 1140, 1140, 985, 988,
|
|
+ /* 50 */ 978, 978, 117, 117, 118, 118, 118, 118, 384, 264,
|
|
+ /* 60 */ 264, 264, 264, 1138, 264, 264, 112, 109, 209, 401,
|
|
+ /* 70 */ 458, 521, 536, 495, 536, 1237, 1237, 536, 239, 206,
|
|
+ /* 80 */ 497, 112, 109, 209, 468, 219, 118, 118, 118, 118,
|
|
+ /* 90 */ 111, 397, 444, 448, 16, 16, 116, 116, 116, 116,
|
|
+ /* 100 */ 115, 115, 114, 114, 114, 113, 419, 975, 975, 986,
|
|
+ /* 110 */ 989, 235, 1467, 355, 1138, 423, 388, 116, 116, 116,
|
|
+ /* 120 */ 116, 115, 115, 114, 114, 114, 113, 419, 116, 116,
|
|
+ /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 419, 965,
|
|
+ /* 140 */ 119, 120, 110, 1140, 1140, 985, 988, 978, 978, 117,
|
|
+ /* 150 */ 117, 118, 118, 118, 118, 956, 419, 945, 298, 955,
|
|
+ /* 160 */ 945, 1484, 544, 2, 1168, 1119, 539, 1462, 160, 289,
|
|
+ /* 170 */ 6, 134, 1508, 393, 410, 979, 342, 1028, 1245, 341,
|
|
+ /* 180 */ 1093, 1480, 1093, 118, 118, 118, 118, 42, 42, 333,
|
|
+ /* 190 */ 955, 955, 957, 116, 116, 116, 116, 115, 115, 114,
|
|
+ /* 200 */ 114, 114, 113, 419, 315, 434, 301, 315, 885, 160,
|
|
+ /* 210 */ 264, 264, 405, 388, 328, 1119, 1120, 1121, 288, 530,
|
|
+ /* 220 */ 96, 159, 1445, 536, 141, 116, 116, 116, 116, 115,
|
|
+ /* 230 */ 115, 114, 114, 114, 113, 419, 219, 119, 120, 110,
|
|
+ /* 240 */ 1140, 1140, 985, 988, 978, 978, 117, 117, 118, 118,
|
|
+ /* 250 */ 118, 118, 115, 115, 114, 114, 114, 113, 419, 288,
|
|
+ /* 260 */ 530, 407, 537, 121, 874, 874, 423, 250, 267, 340,
|
|
+ /* 270 */ 479, 335, 478, 236, 160, 323, 1088, 326, 1469, 333,
|
|
+ /* 280 */ 354, 12, 539, 388, 506, 1119, 1088, 439, 316, 1088,
|
|
/* 290 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
|
|
- /* 300 */ 415, 535, 836, 42, 42, 138, 426, 119, 120, 110,
|
|
- /* 310 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
|
|
- /* 320 */ 118, 118, 70, 70, 288, 526, 412, 411, 480, 1457,
|
|
- /* 330 */ 335, 79, 6, 473, 1140, 1115, 1116, 1117, 501, 1142,
|
|
- /* 340 */ 334, 837, 811, 1484, 512, 1164, 534, 1141, 123, 187,
|
|
- /* 350 */ 289, 384, 134, 448, 434, 1115, 80, 349, 498, 1241,
|
|
+ /* 300 */ 419, 539, 840, 42, 42, 138, 430, 119, 120, 110,
|
|
+ /* 310 */ 1140, 1140, 985, 988, 978, 978, 117, 117, 118, 118,
|
|
+ /* 320 */ 118, 118, 70, 70, 288, 530, 416, 415, 484, 1461,
|
|
+ /* 330 */ 339, 79, 6, 477, 1144, 1119, 1120, 1121, 505, 1146,
|
|
+ /* 340 */ 338, 841, 815, 1488, 516, 1168, 538, 1145, 123, 187,
|
|
+ /* 350 */ 289, 388, 134, 452, 438, 1119, 80, 353, 502, 1245,
|
|
/* 360 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
|
|
- /* 370 */ 415, 1143, 1115, 1143, 459, 119, 120, 110, 1136, 1136,
|
|
- /* 380 */ 981, 984, 974, 974, 117, 117, 118, 118, 118, 118,
|
|
- /* 390 */ 404, 264, 264, 811, 1463, 506, 368, 1156, 535, 114,
|
|
- /* 400 */ 114, 114, 113, 415, 532, 1115, 1116, 1117, 231, 518,
|
|
- /* 410 */ 1500, 472, 469, 468, 175, 497, 422, 219, 1202, 70,
|
|
- /* 420 */ 70, 467, 1115, 1116, 1117, 176, 201, 200, 116, 116,
|
|
- /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 535,
|
|
- /* 440 */ 1115, 264, 264, 435, 312, 1115, 273, 419, 384, 513,
|
|
- /* 450 */ 1450, 1115, 326, 1084, 532, 517, 82, 1084, 167, 388,
|
|
- /* 460 */ 69, 69, 1115, 1084, 519, 509, 1084, 1084, 12, 1157,
|
|
- /* 470 */ 1084, 420, 119, 120, 110, 1136, 1136, 981, 984, 974,
|
|
- /* 480 */ 974, 117, 117, 118, 118, 118, 118, 258, 258, 535,
|
|
- /* 490 */ 1115, 1116, 1117, 1045, 535, 1115, 1116, 1117, 1323, 535,
|
|
- /* 500 */ 532, 1115, 1116, 1117, 296, 483, 1211, 818, 1046, 448,
|
|
- /* 510 */ 70, 70, 1115, 1116, 1117, 50, 50, 448, 356, 500,
|
|
- /* 520 */ 70, 70, 207, 1047, 32, 116, 116, 116, 116, 115,
|
|
- /* 530 */ 115, 114, 114, 114, 113, 415, 453, 264, 264, 1115,
|
|
- /* 540 */ 450, 449, 961, 508, 856, 384, 517, 5, 900, 822,
|
|
- /* 550 */ 532, 484, 181, 1115, 857, 516, 517, 818, 952, 507,
|
|
- /* 560 */ 3, 1115, 951, 1231, 1231, 482, 398, 1115, 1095, 119,
|
|
- /* 570 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
|
|
- /* 580 */ 118, 118, 118, 118, 1115, 535, 238, 1115, 1391, 1115,
|
|
- /* 590 */ 1116, 1117, 159, 951, 951, 953, 231, 1115, 259, 472,
|
|
- /* 600 */ 469, 468, 310, 1115, 1116, 1117, 13, 13, 297, 467,
|
|
- /* 610 */ 276, 1115, 1116, 1117, 412, 411, 1095, 1115, 1116, 1117,
|
|
- /* 620 */ 395, 355, 116, 116, 116, 116, 115, 115, 114, 114,
|
|
- /* 630 */ 114, 113, 415, 208, 1115, 1116, 1117, 1115, 1116, 1117,
|
|
- /* 640 */ 264, 264, 384, 337, 902, 393, 815, 1115, 1116, 1117,
|
|
- /* 650 */ 413, 413, 413, 532, 112, 109, 209, 309, 900, 1143,
|
|
- /* 660 */ 535, 1143, 535, 393, 901, 1210, 119, 120, 110, 1136,
|
|
- /* 670 */ 1136, 981, 984, 974, 974, 117, 117, 118, 118, 118,
|
|
- /* 680 */ 118, 13, 13, 13, 13, 265, 265, 535, 143, 264,
|
|
- /* 690 */ 264, 288, 526, 535, 1119, 400, 535, 402, 532, 510,
|
|
- /* 700 */ 1457, 512, 532, 6, 113, 415, 1067, 1530, 70, 70,
|
|
- /* 710 */ 1530, 535, 271, 535, 70, 70, 535, 13, 13, 116,
|
|
- /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 415,
|
|
- /* 730 */ 272, 277, 13, 13, 13, 13, 535, 13, 13, 384,
|
|
- /* 740 */ 535, 304, 425, 1100, 284, 1119, 184, 801, 185, 338,
|
|
- /* 750 */ 285, 514, 1532, 369, 1239, 1438, 1182, 70, 70, 425,
|
|
- /* 760 */ 424, 70, 70, 119, 120, 110, 1136, 1136, 981, 984,
|
|
- /* 770 */ 974, 974, 117, 117, 118, 118, 118, 118, 190, 1065,
|
|
- /* 780 */ 1067, 1531, 442, 107, 1531, 408, 264, 264, 264, 264,
|
|
- /* 790 */ 383, 1396, 261, 410, 95, 900, 485, 414, 421, 532,
|
|
- /* 800 */ 1045, 532, 301, 1133, 303, 488, 433, 1451, 1396, 1398,
|
|
- /* 810 */ 278, 535, 278, 520, 1435, 1046, 116, 116, 116, 116,
|
|
- /* 820 */ 115, 115, 114, 114, 114, 113, 415, 425, 264, 264,
|
|
- /* 830 */ 1047, 190, 54, 54, 535, 291, 384, 264, 264, 362,
|
|
- /* 840 */ 962, 532, 1004, 376, 1084, 264, 264, 1029, 1029, 456,
|
|
- /* 850 */ 532, 523, 270, 1065, 1084, 55, 55, 1084, 532, 442,
|
|
- /* 860 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
|
|
- /* 870 */ 117, 118, 118, 118, 118, 535, 1396, 190, 302, 1383,
|
|
- /* 880 */ 208, 535, 789, 790, 791, 535, 515, 535, 1323, 371,
|
|
- /* 890 */ 337, 234, 233, 232, 459, 515, 15, 15, 459, 477,
|
|
- /* 900 */ 459, 459, 44, 44, 136, 900, 56, 56, 57, 57,
|
|
- /* 910 */ 1185, 390, 197, 116, 116, 116, 116, 115, 115, 114,
|
|
- /* 920 */ 114, 114, 113, 415, 535, 876, 535, 442, 535, 274,
|
|
- /* 930 */ 875, 1323, 357, 384, 353, 140, 1426, 946, 1455, 1323,
|
|
- /* 940 */ 1390, 6, 1240, 1236, 292, 58, 58, 59, 59, 60,
|
|
- /* 950 */ 60, 535, 1456, 384, 535, 6, 399, 119, 120, 110,
|
|
- /* 960 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
|
|
- /* 970 */ 118, 118, 61, 61, 535, 45, 45, 119, 120, 110,
|
|
- /* 980 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
|
|
- /* 990 */ 118, 118, 1477, 479, 202, 46, 46, 275, 95, 455,
|
|
- /* 1000 */ 535, 212, 535, 337, 535, 1454, 535, 409, 6, 242,
|
|
+ /* 370 */ 419, 1147, 1119, 1147, 463, 119, 120, 110, 1140, 1140,
|
|
+ /* 380 */ 985, 988, 978, 978, 117, 117, 118, 118, 118, 118,
|
|
+ /* 390 */ 408, 264, 264, 815, 1467, 510, 372, 1160, 539, 114,
|
|
+ /* 400 */ 114, 114, 113, 419, 536, 1119, 1120, 1121, 231, 522,
|
|
+ /* 410 */ 1504, 476, 473, 472, 175, 501, 426, 219, 1206, 70,
|
|
+ /* 420 */ 70, 471, 1119, 1120, 1121, 176, 201, 200, 116, 116,
|
|
+ /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 419, 539,
|
|
+ /* 440 */ 1119, 264, 264, 439, 316, 1119, 273, 423, 388, 517,
|
|
+ /* 450 */ 1454, 1119, 330, 1088, 536, 521, 82, 1088, 167, 392,
|
|
+ /* 460 */ 69, 69, 1119, 1088, 523, 513, 1088, 1088, 12, 1161,
|
|
+ /* 470 */ 1088, 424, 119, 120, 110, 1140, 1140, 985, 988, 978,
|
|
+ /* 480 */ 978, 117, 117, 118, 118, 118, 118, 258, 258, 539,
|
|
+ /* 490 */ 1119, 1120, 1121, 1049, 539, 1119, 1120, 1121, 1327, 539,
|
|
+ /* 500 */ 536, 1119, 1120, 1121, 296, 487, 1215, 822, 1050, 452,
|
|
+ /* 510 */ 70, 70, 1119, 1120, 1121, 50, 50, 452, 360, 504,
|
|
+ /* 520 */ 70, 70, 207, 1051, 32, 116, 116, 116, 116, 115,
|
|
+ /* 530 */ 115, 114, 114, 114, 113, 419, 457, 264, 264, 1119,
|
|
+ /* 540 */ 454, 453, 965, 512, 860, 388, 521, 5, 904, 826,
|
|
+ /* 550 */ 536, 488, 181, 1119, 861, 520, 521, 822, 956, 511,
|
|
+ /* 560 */ 3, 1119, 955, 1235, 1235, 486, 402, 1119, 1099, 119,
|
|
+ /* 570 */ 120, 110, 1140, 1140, 985, 988, 978, 978, 117, 117,
|
|
+ /* 580 */ 118, 118, 118, 118, 1119, 539, 238, 1119, 1395, 1119,
|
|
+ /* 590 */ 1120, 1121, 159, 955, 955, 957, 231, 1119, 259, 476,
|
|
+ /* 600 */ 473, 472, 314, 1119, 1120, 1121, 13, 13, 297, 471,
|
|
+ /* 610 */ 276, 1119, 1120, 1121, 416, 415, 1099, 1119, 1120, 1121,
|
|
+ /* 620 */ 399, 359, 116, 116, 116, 116, 115, 115, 114, 114,
|
|
+ /* 630 */ 114, 113, 419, 208, 1119, 1120, 1121, 1119, 1120, 1121,
|
|
+ /* 640 */ 264, 264, 388, 341, 906, 397, 819, 1119, 1120, 1121,
|
|
+ /* 650 */ 417, 417, 417, 536, 112, 109, 209, 313, 904, 1147,
|
|
+ /* 660 */ 539, 1147, 539, 397, 905, 1214, 119, 120, 110, 1140,
|
|
+ /* 670 */ 1140, 985, 988, 978, 978, 117, 117, 118, 118, 118,
|
|
+ /* 680 */ 118, 13, 13, 13, 13, 265, 265, 539, 143, 264,
|
|
+ /* 690 */ 264, 288, 530, 539, 1123, 404, 539, 406, 536, 514,
|
|
+ /* 700 */ 1461, 516, 536, 6, 113, 419, 1071, 1534, 70, 70,
|
|
+ /* 710 */ 1534, 539, 271, 539, 70, 70, 539, 13, 13, 116,
|
|
+ /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 419,
|
|
+ /* 730 */ 272, 277, 13, 13, 13, 13, 539, 13, 13, 388,
|
|
+ /* 740 */ 539, 308, 429, 1104, 284, 1123, 184, 805, 185, 342,
|
|
+ /* 750 */ 285, 518, 1536, 373, 1243, 1442, 1186, 70, 70, 429,
|
|
+ /* 760 */ 428, 70, 70, 119, 120, 110, 1140, 1140, 985, 988,
|
|
+ /* 770 */ 978, 978, 117, 117, 118, 118, 118, 118, 190, 1069,
|
|
+ /* 780 */ 1071, 1535, 446, 107, 1535, 412, 264, 264, 264, 264,
|
|
+ /* 790 */ 387, 1400, 261, 414, 95, 904, 489, 418, 425, 536,
|
|
+ /* 800 */ 1049, 536, 305, 1137, 307, 492, 437, 1455, 1400, 1402,
|
|
+ /* 810 */ 278, 539, 278, 524, 1439, 1050, 116, 116, 116, 116,
|
|
+ /* 820 */ 115, 115, 114, 114, 114, 113, 419, 429, 264, 264,
|
|
+ /* 830 */ 1051, 190, 54, 54, 539, 291, 388, 264, 264, 366,
|
|
+ /* 840 */ 966, 536, 1008, 380, 1088, 264, 264, 1033, 1033, 460,
|
|
+ /* 850 */ 536, 527, 270, 1069, 1088, 55, 55, 1088, 536, 446,
|
|
+ /* 860 */ 119, 120, 110, 1140, 1140, 985, 988, 978, 978, 117,
|
|
+ /* 870 */ 117, 118, 118, 118, 118, 539, 1400, 190, 306, 1387,
|
|
+ /* 880 */ 208, 539, 793, 794, 795, 539, 519, 539, 1327, 375,
|
|
+ /* 890 */ 341, 234, 233, 232, 463, 519, 15, 15, 463, 481,
|
|
+ /* 900 */ 463, 463, 44, 44, 136, 904, 56, 56, 57, 57,
|
|
+ /* 910 */ 1189, 394, 197, 116, 116, 116, 116, 115, 115, 114,
|
|
+ /* 920 */ 114, 114, 113, 419, 539, 880, 539, 446, 539, 274,
|
|
+ /* 930 */ 879, 1327, 361, 388, 357, 140, 1430, 950, 1459, 1327,
|
|
+ /* 940 */ 1394, 6, 1244, 1240, 292, 58, 58, 59, 59, 60,
|
|
+ /* 950 */ 60, 539, 1460, 388, 539, 6, 403, 119, 120, 110,
|
|
+ /* 960 */ 1140, 1140, 985, 988, 978, 978, 117, 117, 118, 118,
|
|
+ /* 970 */ 118, 118, 61, 61, 539, 45, 45, 119, 120, 110,
|
|
+ /* 980 */ 1140, 1140, 985, 988, 978, 978, 117, 117, 118, 118,
|
|
+ /* 990 */ 118, 118, 1481, 483, 202, 46, 46, 275, 95, 459,
|
|
+ /* 1000 */ 539, 212, 539, 341, 539, 1458, 539, 413, 6, 242,
|
|
/* 1010 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
|
|
- /* 1020 */ 415, 48, 48, 49, 49, 62, 62, 63, 63, 535,
|
|
+ /* 1020 */ 419, 48, 48, 49, 49, 62, 62, 63, 63, 539,
|
|
/* 1030 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
|
|
- /* 1040 */ 415, 535, 459, 535, 1134, 535, 1151, 535, 142, 535,
|
|
- /* 1050 */ 64, 64, 535, 1338, 535, 494, 535, 446, 535, 1264,
|
|
- /* 1060 */ 535, 1337, 14, 14, 65, 65, 125, 125, 66, 66,
|
|
- /* 1070 */ 51, 51, 535, 67, 67, 68, 68, 52, 52, 147,
|
|
- /* 1080 */ 147, 148, 148, 1453, 317, 98, 6, 535, 1245, 481,
|
|
- /* 1090 */ 535, 827, 535, 75, 75, 1134, 102, 481, 100, 535,
|
|
- /* 1100 */ 532, 535, 368, 1066, 1503, 384, 535, 845, 53, 53,
|
|
- /* 1110 */ 93, 71, 71, 126, 126, 295, 528, 390, 288, 526,
|
|
- /* 1120 */ 72, 72, 127, 127, 139, 384, 38, 128, 128, 119,
|
|
- /* 1130 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
|
|
- /* 1140 */ 118, 118, 118, 118, 535, 495, 535, 447, 535, 119,
|
|
- /* 1150 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
|
|
+ /* 1040 */ 419, 539, 463, 539, 1138, 539, 1155, 539, 142, 539,
|
|
+ /* 1050 */ 64, 64, 539, 1342, 539, 498, 539, 450, 539, 1268,
|
|
+ /* 1060 */ 539, 1341, 14, 14, 65, 65, 125, 125, 66, 66,
|
|
+ /* 1070 */ 51, 51, 539, 67, 67, 68, 68, 52, 52, 147,
|
|
+ /* 1080 */ 147, 148, 148, 1457, 321, 98, 6, 539, 1249, 485,
|
|
+ /* 1090 */ 539, 831, 539, 75, 75, 1138, 102, 485, 100, 539,
|
|
+ /* 1100 */ 536, 539, 372, 1070, 1507, 388, 539, 849, 53, 53,
|
|
+ /* 1110 */ 93, 71, 71, 126, 126, 295, 532, 394, 288, 530,
|
|
+ /* 1120 */ 72, 72, 127, 127, 139, 388, 38, 128, 128, 119,
|
|
+ /* 1130 */ 120, 110, 1140, 1140, 985, 988, 978, 978, 117, 117,
|
|
+ /* 1140 */ 118, 118, 118, 118, 539, 499, 539, 451, 539, 119,
|
|
+ /* 1150 */ 120, 110, 1140, 1140, 985, 988, 978, 978, 117, 117,
|
|
/* 1160 */ 118, 118, 118, 118, 235, 124, 124, 146, 146, 145,
|
|
- /* 1170 */ 145, 287, 535, 1277, 535, 1157, 535, 391, 161, 263,
|
|
- /* 1180 */ 206, 381, 116, 116, 116, 116, 115, 115, 114, 114,
|
|
- /* 1190 */ 114, 113, 415, 132, 132, 131, 131, 129, 129, 535,
|
|
- /* 1200 */ 30, 535, 116, 116, 116, 116, 115, 115, 114, 114,
|
|
- /* 1210 */ 114, 113, 415, 535, 216, 1062, 1276, 535, 370, 535,
|
|
- /* 1220 */ 130, 130, 74, 74, 535, 915, 389, 876, 17, 437,
|
|
- /* 1230 */ 429, 31, 875, 916, 76, 76, 266, 101, 73, 73,
|
|
- /* 1240 */ 43, 43, 835, 834, 308, 47, 47, 95, 825, 943,
|
|
- /* 1250 */ 441, 938, 241, 241, 305, 443, 313, 384, 241, 95,
|
|
- /* 1260 */ 842, 843, 193, 465, 1209, 327, 237, 436, 95, 1011,
|
|
- /* 1270 */ 1007, 909, 873, 237, 241, 107, 1023, 384, 1023, 955,
|
|
- /* 1280 */ 1415, 119, 120, 110, 1136, 1136, 981, 984, 974, 974,
|
|
- /* 1290 */ 117, 117, 118, 118, 118, 118, 1022, 809, 1022, 825,
|
|
- /* 1300 */ 137, 119, 108, 110, 1136, 1136, 981, 984, 974, 974,
|
|
- /* 1310 */ 117, 117, 118, 118, 118, 118, 874, 1414, 451, 107,
|
|
- /* 1320 */ 1011, 314, 1273, 318, 218, 321, 323, 325, 1224, 1208,
|
|
- /* 1330 */ 955, 330, 339, 340, 116, 116, 116, 116, 115, 115,
|
|
- /* 1340 */ 114, 114, 114, 113, 415, 1285, 1322, 1260, 1493, 1470,
|
|
- /* 1350 */ 1271, 283, 521, 1328, 116, 116, 116, 116, 115, 115,
|
|
- /* 1360 */ 114, 114, 114, 113, 415, 1191, 1184, 1173, 1172, 1174,
|
|
- /* 1370 */ 522, 1487, 211, 460, 384, 256, 199, 367, 1257, 342,
|
|
- /* 1380 */ 195, 470, 307, 344, 11, 333, 525, 445, 1307, 1315,
|
|
- /* 1390 */ 375, 203, 1207, 1151, 384, 346, 1387, 188, 360, 120,
|
|
- /* 1400 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
|
|
- /* 1410 */ 118, 118, 118, 1386, 428, 1490, 245, 300, 348, 1148,
|
|
- /* 1420 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
|
|
- /* 1430 */ 118, 118, 118, 189, 198, 1434, 1432, 78, 81, 163,
|
|
- /* 1440 */ 82, 392, 439, 1392, 173, 105, 527, 35, 4, 157,
|
|
- /* 1450 */ 1312, 116, 116, 116, 116, 115, 115, 114, 114, 114,
|
|
- /* 1460 */ 113, 415, 530, 165, 93, 1304, 431, 432, 168, 463,
|
|
- /* 1470 */ 221, 116, 116, 116, 116, 115, 115, 114, 114, 114,
|
|
- /* 1480 */ 113, 415, 169, 452, 170, 416, 171, 374, 372, 438,
|
|
- /* 1490 */ 36, 1318, 177, 225, 1381, 87, 458, 524, 1403, 316,
|
|
- /* 1500 */ 257, 105, 527, 227, 4, 182, 461, 160, 320, 228,
|
|
- /* 1510 */ 377, 1175, 476, 229, 1227, 1226, 405, 1225, 530, 1218,
|
|
- /* 1520 */ 961, 378, 1199, 1198, 827, 332, 103, 103, 1197, 407,
|
|
- /* 1530 */ 8, 1217, 1502, 104, 487, 416, 537, 536, 281, 282,
|
|
- /* 1540 */ 951, 416, 490, 1268, 496, 92, 341, 243, 1269, 343,
|
|
- /* 1550 */ 244, 1267, 122, 524, 345, 1461, 515, 288, 526, 10,
|
|
- /* 1560 */ 354, 1266, 1460, 352, 504, 1250, 99, 1367, 94, 503,
|
|
- /* 1570 */ 499, 951, 951, 953, 954, 27, 961, 347, 1249, 194,
|
|
- /* 1580 */ 251, 358, 103, 103, 359, 1181, 34, 538, 1110, 104,
|
|
- /* 1590 */ 255, 416, 537, 536, 286, 252, 951, 254, 539, 149,
|
|
- /* 1600 */ 1170, 1419, 1165, 1420, 1418, 150, 1417, 135, 279, 785,
|
|
- /* 1610 */ 151, 417, 1195, 196, 290, 210, 386, 1194, 269, 387,
|
|
- /* 1620 */ 162, 1021, 133, 77, 1192, 1019, 935, 951, 951, 953,
|
|
- /* 1630 */ 954, 27, 1479, 1104, 418, 164, 153, 268, 217, 166,
|
|
- /* 1640 */ 859, 306, 366, 366, 365, 253, 363, 220, 1035, 798,
|
|
- /* 1650 */ 172, 939, 105, 527, 155, 4, 394, 174, 396, 156,
|
|
- /* 1660 */ 83, 1038, 213, 84, 294, 85, 86, 223, 222, 530,
|
|
- /* 1670 */ 1034, 144, 293, 18, 224, 315, 241, 1027, 1145, 178,
|
|
- /* 1680 */ 457, 226, 179, 37, 800, 334, 462, 230, 328, 466,
|
|
- /* 1690 */ 180, 471, 416, 88, 19, 20, 89, 280, 838, 158,
|
|
- /* 1700 */ 191, 90, 215, 478, 524, 1097, 204, 192, 987, 91,
|
|
- /* 1710 */ 152, 1070, 39, 154, 1071, 504, 486, 40, 489, 205,
|
|
- /* 1720 */ 505, 260, 105, 527, 214, 4, 908, 961, 262, 183,
|
|
- /* 1730 */ 240, 21, 903, 103, 103, 107, 22, 1086, 23, 530,
|
|
- /* 1740 */ 104, 1088, 416, 537, 536, 24, 1093, 951, 25, 1074,
|
|
- /* 1750 */ 1090, 1094, 7, 33, 511, 186, 26, 1002, 385, 95,
|
|
- /* 1760 */ 988, 986, 416, 288, 526, 990, 1044, 246, 1043, 247,
|
|
- /* 1770 */ 991, 28, 41, 106, 524, 956, 810, 29, 951, 951,
|
|
- /* 1780 */ 953, 954, 27, 531, 361, 504, 423, 248, 869, 249,
|
|
- /* 1790 */ 503, 1495, 364, 1105, 1161, 1494, 1161, 961, 1161, 1161,
|
|
- /* 1800 */ 1161, 1161, 1161, 103, 103, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 1810 */ 104, 1161, 416, 537, 536, 1104, 418, 951, 1161, 268,
|
|
- /* 1820 */ 1161, 1161, 1161, 1161, 366, 366, 365, 253, 363, 1161,
|
|
- /* 1830 */ 1161, 798, 1161, 1161, 1161, 1161, 105, 527, 1161, 4,
|
|
- /* 1840 */ 1161, 1161, 1161, 1161, 213, 1161, 294, 1161, 951, 951,
|
|
- /* 1850 */ 953, 954, 27, 530, 293, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 1860 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 1870 */ 1161, 1161, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161,
|
|
- /* 1880 */ 1161, 1161, 1161, 1161, 215, 1161, 1161, 1161, 524, 1161,
|
|
- /* 1890 */ 1161, 1161, 152, 1161, 1161, 154, 105, 527, 1161, 4,
|
|
- /* 1900 */ 1161, 1161, 1161, 1161, 1161, 1161, 214, 1161, 1161, 1161,
|
|
- /* 1910 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 880,
|
|
- /* 1920 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161,
|
|
- /* 1930 */ 1161, 951, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161,
|
|
- /* 1940 */ 385, 1161, 1161, 1161, 1161, 288, 526, 1161, 524, 1161,
|
|
- /* 1950 */ 1161, 1161, 1161, 1161, 1161, 1161, 97, 527, 1161, 4,
|
|
- /* 1960 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 423, 1161,
|
|
- /* 1970 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 1161,
|
|
- /* 1980 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161,
|
|
- /* 1990 */ 1161, 951, 268, 1161, 1161, 1161, 416, 366, 366, 365,
|
|
- /* 2000 */ 253, 363, 1161, 1161, 798, 1161, 1161, 1161, 524, 1161,
|
|
- /* 2010 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 213, 1161, 294,
|
|
- /* 2020 */ 1161, 1161, 951, 951, 953, 954, 27, 293, 1161, 1161,
|
|
- /* 2030 */ 1161, 961, 1161, 1161, 1161, 1161, 1161, 103, 103, 1161,
|
|
- /* 2040 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161,
|
|
- /* 2050 */ 1161, 951, 1161, 1161, 1161, 1161, 1161, 215, 1161, 1161,
|
|
- /* 2060 */ 1161, 1161, 1161, 1161, 1161, 152, 1161, 1161, 154, 1161,
|
|
- /* 2070 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 214,
|
|
- /* 2080 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 1161, 1161,
|
|
- /* 2090 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 2100 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 2110 */ 1161, 1161, 1161, 385, 1161, 1161, 1161, 1161, 288, 526,
|
|
- /* 2120 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 2130 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
|
|
- /* 2140 */ 1161, 423,
|
|
+ /* 1170 */ 145, 287, 539, 1281, 539, 1161, 539, 395, 161, 263,
|
|
+ /* 1180 */ 206, 385, 116, 116, 116, 116, 115, 115, 114, 114,
|
|
+ /* 1190 */ 114, 113, 419, 132, 132, 131, 131, 129, 129, 539,
|
|
+ /* 1200 */ 30, 539, 116, 116, 116, 116, 115, 115, 114, 114,
|
|
+ /* 1210 */ 114, 113, 419, 539, 216, 1066, 1280, 539, 374, 539,
|
|
+ /* 1220 */ 130, 130, 74, 74, 539, 919, 393, 880, 17, 441,
|
|
+ /* 1230 */ 433, 31, 879, 920, 76, 76, 266, 101, 73, 73,
|
|
+ /* 1240 */ 43, 43, 839, 838, 312, 47, 47, 95, 829, 947,
|
|
+ /* 1250 */ 445, 942, 241, 241, 309, 447, 317, 388, 241, 95,
|
|
+ /* 1260 */ 846, 847, 193, 469, 1213, 331, 237, 440, 95, 1015,
|
|
+ /* 1270 */ 1011, 913, 877, 237, 241, 107, 1027, 388, 1027, 959,
|
|
+ /* 1280 */ 1419, 119, 120, 110, 1140, 1140, 985, 988, 978, 978,
|
|
+ /* 1290 */ 117, 117, 118, 118, 118, 118, 1026, 813, 1026, 829,
|
|
+ /* 1300 */ 137, 119, 108, 110, 1140, 1140, 985, 988, 978, 978,
|
|
+ /* 1310 */ 117, 117, 118, 118, 118, 118, 878, 1418, 455, 107,
|
|
+ /* 1320 */ 1015, 318, 1277, 322, 218, 325, 327, 329, 1228, 1212,
|
|
+ /* 1330 */ 959, 334, 343, 344, 116, 116, 116, 116, 115, 115,
|
|
+ /* 1340 */ 114, 114, 114, 113, 419, 1289, 1326, 1264, 1497, 1474,
|
|
+ /* 1350 */ 1275, 283, 525, 1332, 116, 116, 116, 116, 115, 115,
|
|
+ /* 1360 */ 114, 114, 114, 113, 419, 1195, 1188, 1177, 1176, 1178,
|
|
+ /* 1370 */ 526, 1491, 211, 464, 388, 256, 199, 371, 1261, 346,
|
|
+ /* 1380 */ 195, 474, 311, 348, 11, 337, 529, 449, 300, 1319,
|
|
+ /* 1390 */ 379, 203, 1211, 1155, 388, 350, 1391, 188, 364, 120,
|
|
+ /* 1400 */ 110, 1140, 1140, 985, 988, 978, 978, 117, 117, 118,
|
|
+ /* 1410 */ 118, 118, 118, 1390, 432, 1494, 245, 304, 352, 1152,
|
|
+ /* 1420 */ 110, 1140, 1140, 985, 988, 978, 978, 117, 117, 118,
|
|
+ /* 1430 */ 118, 118, 118, 189, 198, 1438, 1436, 78, 81, 163,
|
|
+ /* 1440 */ 82, 396, 443, 1396, 173, 105, 531, 35, 4, 122,
|
|
+ /* 1450 */ 1316, 116, 116, 116, 116, 115, 115, 114, 114, 114,
|
|
+ /* 1460 */ 113, 419, 534, 1311, 519, 165, 299, 157, 1308, 93,
|
|
+ /* 1470 */ 303, 116, 116, 116, 116, 115, 115, 114, 114, 114,
|
|
+ /* 1480 */ 113, 419, 435, 302, 168, 420, 169, 170, 171, 436,
|
|
+ /* 1490 */ 467, 221, 378, 36, 376, 1322, 456, 528, 442, 177,
|
|
+ /* 1500 */ 1385, 105, 531, 225, 4, 462, 87, 160, 257, 1407,
|
|
+ /* 1510 */ 227, 182, 465, 320, 324, 228, 381, 480, 534, 1179,
|
|
+ /* 1520 */ 965, 229, 1231, 1230, 409, 1229, 103, 103, 1202, 1222,
|
|
+ /* 1530 */ 8, 382, 831, 104, 491, 420, 541, 540, 411, 1203,
|
|
+ /* 1540 */ 955, 420, 336, 1201, 1221, 1506, 281, 282, 500, 494,
|
|
+ /* 1550 */ 92, 243, 1272, 528, 244, 345, 1465, 288, 530, 1273,
|
|
+ /* 1560 */ 347, 10, 1371, 358, 508, 99, 1464, 94, 286, 507,
|
|
+ /* 1570 */ 349, 955, 955, 957, 958, 27, 965, 503, 356, 1271,
|
|
+ /* 1580 */ 1254, 1253, 103, 103, 194, 362, 1270, 363, 351, 104,
|
|
+ /* 1590 */ 251, 420, 541, 540, 1185, 34, 955, 542, 1114, 252,
|
|
+ /* 1600 */ 255, 254, 543, 1174, 1169, 149, 1423, 150, 1424, 1422,
|
|
+ /* 1610 */ 1421, 789, 135, 279, 421, 1199, 290, 196, 210, 269,
|
|
+ /* 1620 */ 1198, 77, 151, 1025, 133, 1023, 1196, 955, 955, 957,
|
|
+ /* 1630 */ 958, 27, 1483, 1108, 422, 390, 939, 268, 164, 391,
|
|
+ /* 1640 */ 153, 217, 370, 370, 369, 253, 367, 162, 166, 802,
|
|
+ /* 1650 */ 863, 220, 105, 531, 310, 4, 1039, 172, 155, 943,
|
|
+ /* 1660 */ 398, 400, 213, 174, 294, 83, 84, 85, 86, 534,
|
|
+ /* 1670 */ 156, 1042, 293, 222, 223, 1038, 144, 319, 18, 224,
|
|
+ /* 1680 */ 241, 1149, 461, 226, 1031, 179, 178, 37, 804, 466,
|
|
+ /* 1690 */ 338, 230, 420, 470, 180, 88, 475, 19, 280, 332,
|
|
+ /* 1700 */ 20, 89, 215, 158, 528, 842, 191, 482, 204, 90,
|
|
+ /* 1710 */ 152, 1101, 192, 154, 991, 508, 91, 1074, 39, 490,
|
|
+ /* 1720 */ 509, 1075, 105, 531, 214, 4, 40, 965, 912, 493,
|
|
+ /* 1730 */ 205, 260, 262, 103, 103, 240, 183, 907, 107, 534,
|
|
+ /* 1740 */ 104, 21, 420, 541, 540, 22, 1090, 955, 1092, 23,
|
|
+ /* 1750 */ 24, 25, 1097, 1078, 7, 1094, 1098, 33, 389, 186,
|
|
+ /* 1760 */ 515, 26, 420, 288, 530, 95, 1006, 992, 990, 994,
|
|
+ /* 1770 */ 1048, 1047, 247, 995, 528, 246, 28, 41, 955, 955,
|
|
+ /* 1780 */ 957, 958, 27, 960, 814, 508, 427, 106, 29, 535,
|
|
+ /* 1790 */ 507, 248, 873, 249, 1499, 365, 368, 965, 1109, 1165,
|
|
+ /* 1800 */ 1165, 1498, 1165, 103, 103, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 1810 */ 104, 1165, 420, 541, 540, 1108, 422, 955, 1165, 268,
|
|
+ /* 1820 */ 1165, 1165, 1165, 1165, 370, 370, 369, 253, 367, 1165,
|
|
+ /* 1830 */ 1165, 802, 1165, 1165, 1165, 1165, 105, 531, 1165, 4,
|
|
+ /* 1840 */ 1165, 1165, 1165, 1165, 213, 1165, 294, 1165, 955, 955,
|
|
+ /* 1850 */ 957, 958, 27, 534, 293, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 1860 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 1870 */ 1165, 1165, 1165, 1165, 1165, 1165, 420, 1165, 1165, 1165,
|
|
+ /* 1880 */ 1165, 1165, 1165, 1165, 215, 1165, 1165, 1165, 528, 1165,
|
|
+ /* 1890 */ 1165, 1165, 152, 1165, 1165, 154, 105, 531, 1165, 4,
|
|
+ /* 1900 */ 1165, 1165, 1165, 1165, 1165, 1165, 214, 1165, 1165, 1165,
|
|
+ /* 1910 */ 1165, 965, 1165, 534, 1165, 1165, 1165, 103, 103, 884,
|
|
+ /* 1920 */ 1165, 1165, 1165, 1165, 104, 1165, 420, 541, 540, 1165,
|
|
+ /* 1930 */ 1165, 955, 1165, 1165, 1165, 1165, 420, 1165, 1165, 1165,
|
|
+ /* 1940 */ 389, 1165, 1165, 1165, 1165, 288, 530, 1165, 528, 1165,
|
|
+ /* 1950 */ 1165, 1165, 1165, 1165, 1165, 1165, 97, 531, 1165, 4,
|
|
+ /* 1960 */ 1165, 1165, 955, 955, 957, 958, 27, 1165, 427, 1165,
|
|
+ /* 1970 */ 1165, 965, 1165, 534, 1165, 1165, 1165, 103, 103, 1165,
|
|
+ /* 1980 */ 1165, 1165, 1165, 1165, 104, 1165, 420, 541, 540, 1165,
|
|
+ /* 1990 */ 1165, 955, 268, 1165, 1165, 1165, 420, 370, 370, 369,
|
|
+ /* 2000 */ 253, 367, 1165, 1165, 802, 1165, 1165, 1165, 528, 1165,
|
|
+ /* 2010 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 213, 1165, 294,
|
|
+ /* 2020 */ 1165, 1165, 955, 955, 957, 958, 27, 293, 1165, 1165,
|
|
+ /* 2030 */ 1165, 965, 1165, 1165, 1165, 1165, 1165, 103, 103, 1165,
|
|
+ /* 2040 */ 1165, 1165, 1165, 1165, 104, 1165, 420, 541, 540, 1165,
|
|
+ /* 2050 */ 1165, 955, 1165, 1165, 1165, 1165, 1165, 215, 1165, 1165,
|
|
+ /* 2060 */ 1165, 1165, 1165, 1165, 1165, 152, 1165, 1165, 154, 1165,
|
|
+ /* 2070 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 214,
|
|
+ /* 2080 */ 1165, 1165, 955, 955, 957, 958, 27, 1165, 1165, 1165,
|
|
+ /* 2090 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 2100 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 2110 */ 1165, 1165, 1165, 389, 1165, 1165, 1165, 1165, 288, 530,
|
|
+ /* 2120 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 2130 */ 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165,
|
|
+ /* 2140 */ 1165, 427,
|
|
};
|
|
static const YYCODETYPE yy_lookahead[] = {
|
|
/* 0 */ 184, 184, 259, 260, 261, 259, 260, 261, 176, 177,
|
|
@@ -148842,43 +149799,43 @@ static const YYCODETYPE yy_lookahead[] =
|
|
/* 1410 */ 55, 56, 57, 208, 247, 187, 134, 247, 247, 38,
|
|
/* 1420 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
|
/* 1430 */ 55, 56, 57, 237, 231, 191, 191, 279, 279, 282,
|
|
- /* 1440 */ 143, 191, 108, 268, 22, 19, 20, 256, 22, 43,
|
|
+ /* 1440 */ 143, 191, 108, 268, 22, 19, 20, 256, 22, 141,
|
|
/* 1450 */ 257, 96, 97, 98, 99, 100, 101, 102, 103, 104,
|
|
- /* 1460 */ 105, 106, 36, 222, 142, 234, 18, 191, 225, 18,
|
|
- /* 1470 */ 190, 96, 97, 98, 99, 100, 101, 102, 103, 104,
|
|
- /* 1480 */ 105, 106, 225, 191, 225, 59, 225, 257, 234, 234,
|
|
- /* 1490 */ 256, 222, 222, 190, 234, 150, 62, 71, 275, 274,
|
|
- /* 1500 */ 191, 19, 20, 190, 22, 22, 210, 81, 191, 190,
|
|
- /* 1510 */ 210, 191, 108, 190, 207, 207, 64, 207, 36, 215,
|
|
- /* 1520 */ 94, 210, 207, 209, 119, 207, 100, 101, 207, 106,
|
|
- /* 1530 */ 48, 215, 207, 107, 210, 109, 110, 111, 267, 267,
|
|
- /* 1540 */ 114, 59, 210, 249, 137, 108, 248, 191, 249, 248,
|
|
- /* 1550 */ 88, 249, 141, 71, 248, 299, 138, 131, 132, 22,
|
|
- /* 1560 */ 191, 249, 299, 237, 82, 238, 150, 262, 140, 87,
|
|
- /* 1570 */ 139, 145, 146, 147, 148, 149, 94, 248, 238, 236,
|
|
- /* 1580 */ 25, 235, 100, 101, 234, 194, 26, 193, 13, 107,
|
|
- /* 1590 */ 6, 109, 110, 111, 264, 185, 114, 185, 183, 197,
|
|
- /* 1600 */ 183, 203, 183, 203, 203, 197, 203, 211, 211, 4,
|
|
- /* 1610 */ 197, 3, 203, 22, 155, 15, 288, 203, 93, 288,
|
|
- /* 1620 */ 285, 23, 16, 203, 203, 23, 132, 145, 146, 147,
|
|
- /* 1630 */ 148, 149, 0, 1, 2, 143, 123, 5, 24, 135,
|
|
- /* 1640 */ 20, 16, 10, 11, 12, 13, 14, 137, 1, 17,
|
|
- /* 1650 */ 135, 144, 19, 20, 123, 22, 61, 143, 37, 123,
|
|
- /* 1660 */ 53, 109, 30, 53, 32, 53, 53, 134, 34, 36,
|
|
- /* 1670 */ 1, 5, 40, 22, 108, 153, 26, 68, 75, 68,
|
|
- /* 1680 */ 41, 134, 108, 24, 20, 124, 19, 118, 23, 67,
|
|
- /* 1690 */ 22, 67, 59, 22, 22, 22, 22, 67, 28, 37,
|
|
- /* 1700 */ 23, 142, 70, 22, 71, 23, 157, 23, 23, 26,
|
|
- /* 1710 */ 78, 23, 22, 81, 23, 82, 24, 22, 24, 134,
|
|
- /* 1720 */ 87, 23, 19, 20, 92, 22, 109, 94, 23, 22,
|
|
- /* 1730 */ 34, 34, 136, 100, 101, 26, 34, 85, 34, 36,
|
|
- /* 1740 */ 107, 83, 109, 110, 111, 34, 90, 114, 34, 23,
|
|
- /* 1750 */ 75, 75, 44, 22, 24, 26, 34, 23, 126, 26,
|
|
- /* 1760 */ 23, 23, 59, 131, 132, 23, 23, 26, 23, 22,
|
|
- /* 1770 */ 11, 22, 22, 22, 71, 23, 23, 22, 145, 146,
|
|
- /* 1780 */ 147, 148, 149, 26, 23, 82, 154, 134, 128, 134,
|
|
- /* 1790 */ 87, 134, 15, 1, 301, 134, 301, 94, 301, 301,
|
|
- /* 1800 */ 301, 301, 301, 100, 101, 301, 301, 301, 301, 301,
|
|
+ /* 1460 */ 105, 106, 36, 238, 138, 222, 237, 43, 238, 142,
|
|
+ /* 1470 */ 234, 96, 97, 98, 99, 100, 101, 102, 103, 104,
|
|
+ /* 1480 */ 105, 106, 18, 237, 225, 59, 225, 225, 225, 191,
|
|
+ /* 1490 */ 18, 190, 257, 256, 234, 222, 191, 71, 234, 222,
|
|
+ /* 1500 */ 234, 19, 20, 190, 22, 62, 150, 81, 191, 275,
|
|
+ /* 1510 */ 190, 22, 210, 274, 191, 190, 210, 108, 36, 191,
|
|
+ /* 1520 */ 94, 190, 207, 207, 64, 207, 100, 101, 209, 215,
|
|
+ /* 1530 */ 48, 210, 119, 107, 210, 109, 110, 111, 106, 207,
|
|
+ /* 1540 */ 114, 59, 207, 207, 215, 207, 267, 267, 137, 210,
|
|
+ /* 1550 */ 108, 191, 249, 71, 88, 248, 299, 131, 132, 249,
|
|
+ /* 1560 */ 248, 22, 262, 191, 82, 150, 299, 140, 264, 87,
|
|
+ /* 1570 */ 248, 145, 146, 147, 148, 149, 94, 139, 237, 249,
|
|
+ /* 1580 */ 238, 238, 100, 101, 236, 235, 249, 234, 248, 107,
|
|
+ /* 1590 */ 25, 109, 110, 111, 194, 26, 114, 193, 13, 185,
|
|
+ /* 1600 */ 6, 185, 183, 183, 183, 197, 203, 197, 203, 203,
|
|
+ /* 1610 */ 203, 4, 211, 211, 3, 203, 155, 22, 15, 93,
|
|
+ /* 1620 */ 203, 203, 197, 23, 16, 23, 203, 145, 146, 147,
|
|
+ /* 1630 */ 148, 149, 0, 1, 2, 288, 132, 5, 143, 288,
|
|
+ /* 1640 */ 123, 24, 10, 11, 12, 13, 14, 285, 135, 17,
|
|
+ /* 1650 */ 20, 137, 19, 20, 16, 22, 1, 135, 123, 144,
|
|
+ /* 1660 */ 61, 37, 30, 143, 32, 53, 53, 53, 53, 36,
|
|
+ /* 1670 */ 123, 109, 40, 34, 134, 1, 5, 153, 22, 108,
|
|
+ /* 1680 */ 26, 75, 41, 134, 68, 108, 68, 24, 20, 19,
|
|
+ /* 1690 */ 124, 118, 59, 67, 22, 22, 67, 22, 67, 23,
|
|
+ /* 1700 */ 22, 22, 70, 37, 71, 28, 23, 22, 157, 142,
|
|
+ /* 1710 */ 78, 23, 23, 81, 23, 82, 26, 23, 22, 24,
|
|
+ /* 1720 */ 87, 23, 19, 20, 92, 22, 22, 94, 109, 24,
|
|
+ /* 1730 */ 134, 23, 23, 100, 101, 34, 22, 136, 26, 36,
|
|
+ /* 1740 */ 107, 34, 109, 110, 111, 34, 85, 114, 83, 34,
|
|
+ /* 1750 */ 34, 34, 90, 23, 44, 75, 75, 22, 126, 26,
|
|
+ /* 1760 */ 24, 34, 59, 131, 132, 26, 23, 23, 23, 23,
|
|
+ /* 1770 */ 23, 23, 22, 11, 71, 26, 22, 22, 145, 146,
|
|
+ /* 1780 */ 147, 148, 149, 23, 23, 82, 154, 22, 22, 26,
|
|
+ /* 1790 */ 87, 134, 128, 134, 134, 23, 15, 94, 1, 301,
|
|
+ /* 1800 */ 301, 134, 301, 100, 101, 301, 301, 301, 301, 301,
|
|
/* 1810 */ 107, 301, 109, 110, 111, 1, 2, 114, 301, 5,
|
|
/* 1820 */ 301, 301, 301, 301, 10, 11, 12, 13, 14, 301,
|
|
/* 1830 */ 301, 17, 301, 301, 301, 301, 19, 20, 301, 22,
|
|
@@ -148916,7 +149873,7 @@ static const YYCODETYPE yy_lookahead[] =
|
|
/* 2150 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
|
|
/* 2160 */ 301, 301, 301, 301, 301, 301, 301, 301, 301,
|
|
};
|
|
-#define YY_SHIFT_COUNT (540)
|
|
+#define YY_SHIFT_COUNT (544)
|
|
#define YY_SHIFT_MIN (0)
|
|
#define YY_SHIFT_MAX (1987)
|
|
static const unsigned short int yy_shift_ofst[] = {
|
|
@@ -148949,36 +149906,36 @@ static const unsigned short int yy_shift
|
|
/* 260 */ 985, 985, 985, 522, 135, 135, 797, 326, 875, 986,
|
|
/* 270 */ 968, 1036, 1036, 1038, 968, 968, 1038, 972, 1081, 1118,
|
|
/* 280 */ 1194, 1194, 1194, 1036, 757, 757, 946, 777, 1099, 1102,
|
|
- /* 290 */ 1333, 1282, 1282, 1381, 1381, 1282, 1297, 1334, 1422, 1406,
|
|
- /* 300 */ 1322, 1448, 1448, 1448, 1448, 1282, 1451, 1322, 1322, 1334,
|
|
- /* 310 */ 1422, 1406, 1406, 1322, 1282, 1451, 1345, 1434, 1282, 1451,
|
|
- /* 320 */ 1483, 1282, 1451, 1282, 1451, 1483, 1404, 1404, 1404, 1452,
|
|
- /* 330 */ 1483, 1404, 1405, 1404, 1452, 1404, 1404, 1483, 1423, 1423,
|
|
- /* 340 */ 1483, 1407, 1437, 1407, 1437, 1407, 1437, 1407, 1437, 1282,
|
|
- /* 350 */ 1462, 1462, 1411, 1418, 1537, 1282, 1416, 1411, 1428, 1431,
|
|
- /* 360 */ 1322, 1555, 1560, 1575, 1575, 1584, 1584, 1584, 2142, 2142,
|
|
- /* 370 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
|
|
- /* 380 */ 2142, 2142, 2142, 2142, 61, 725, 374, 1080, 198, 771,
|
|
- /* 390 */ 283, 1192, 1178, 1190, 1107, 1221, 1206, 1226, 1227, 1232,
|
|
- /* 400 */ 1233, 1240, 1242, 1189, 1129, 1253, 216, 1210, 1247, 1248,
|
|
- /* 410 */ 1249, 1131, 1151, 1274, 1293, 1220, 1214, 1605, 1608, 1591,
|
|
- /* 420 */ 1459, 1600, 1525, 1606, 1598, 1602, 1494, 1492, 1513, 1614,
|
|
- /* 430 */ 1504, 1620, 1510, 1625, 1647, 1515, 1507, 1531, 1595, 1621,
|
|
- /* 440 */ 1514, 1607, 1610, 1612, 1613, 1536, 1552, 1634, 1533, 1669,
|
|
- /* 450 */ 1666, 1651, 1566, 1522, 1609, 1650, 1611, 1603, 1639, 1547,
|
|
- /* 460 */ 1574, 1659, 1664, 1667, 1561, 1569, 1668, 1622, 1671, 1672,
|
|
- /* 470 */ 1665, 1673, 1624, 1670, 1674, 1630, 1662, 1677, 1559, 1681,
|
|
- /* 480 */ 1682, 1549, 1684, 1685, 1683, 1688, 1690, 1692, 1691, 1695,
|
|
- /* 490 */ 1694, 1585, 1698, 1705, 1617, 1696, 1707, 1596, 1709, 1697,
|
|
- /* 500 */ 1702, 1704, 1711, 1652, 1675, 1658, 1708, 1676, 1656, 1714,
|
|
- /* 510 */ 1726, 1731, 1730, 1729, 1733, 1722, 1734, 1709, 1737, 1738,
|
|
- /* 520 */ 1742, 1743, 1741, 1745, 1747, 1759, 1749, 1750, 1752, 1753,
|
|
- /* 530 */ 1751, 1755, 1757, 1660, 1653, 1655, 1657, 1661, 1761, 1777,
|
|
- /* 540 */ 1792,
|
|
+ /* 290 */ 1333, 1282, 1282, 1381, 1381, 1282, 1297, 1334, 1422, 1308,
|
|
+ /* 300 */ 1326, 1424, 1308, 1326, 1327, 1464, 1464, 1464, 1464, 1282,
|
|
+ /* 310 */ 1472, 1327, 1327, 1334, 1422, 1424, 1424, 1327, 1282, 1472,
|
|
+ /* 320 */ 1356, 1443, 1282, 1472, 1489, 1282, 1472, 1282, 1472, 1489,
|
|
+ /* 330 */ 1409, 1409, 1409, 1460, 1489, 1409, 1413, 1409, 1460, 1409,
|
|
+ /* 340 */ 1409, 1489, 1432, 1432, 1489, 1411, 1442, 1411, 1442, 1411,
|
|
+ /* 350 */ 1442, 1411, 1442, 1282, 1466, 1466, 1308, 1326, 1539, 1282,
|
|
+ /* 360 */ 1415, 1308, 1427, 1438, 1327, 1565, 1569, 1585, 1585, 1594,
|
|
+ /* 370 */ 1594, 1594, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
|
|
+ /* 380 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 61, 725,
|
|
+ /* 390 */ 374, 1080, 198, 771, 283, 1192, 1178, 1190, 1107, 1221,
|
|
+ /* 400 */ 1206, 1226, 1227, 1232, 1233, 1240, 1242, 1189, 1129, 1253,
|
|
+ /* 410 */ 216, 1210, 1247, 1248, 1249, 1131, 1151, 1274, 1293, 1220,
|
|
+ /* 420 */ 1214, 1607, 1611, 1595, 1461, 1603, 1526, 1608, 1600, 1602,
|
|
+ /* 430 */ 1504, 1495, 1517, 1617, 1513, 1630, 1514, 1638, 1655, 1522,
|
|
+ /* 440 */ 1515, 1535, 1599, 1624, 1520, 1612, 1613, 1614, 1615, 1547,
|
|
+ /* 450 */ 1562, 1639, 1540, 1674, 1671, 1656, 1571, 1524, 1616, 1654,
|
|
+ /* 460 */ 1618, 1606, 1641, 1549, 1577, 1663, 1668, 1670, 1566, 1573,
|
|
+ /* 470 */ 1672, 1626, 1673, 1675, 1676, 1678, 1629, 1677, 1679, 1631,
|
|
+ /* 480 */ 1666, 1683, 1567, 1685, 1688, 1551, 1689, 1691, 1690, 1694,
|
|
+ /* 490 */ 1696, 1695, 1698, 1704, 1705, 1596, 1708, 1709, 1619, 1701,
|
|
+ /* 500 */ 1714, 1601, 1712, 1707, 1711, 1715, 1716, 1661, 1680, 1665,
|
|
+ /* 510 */ 1710, 1681, 1662, 1717, 1730, 1735, 1736, 1733, 1739, 1727,
|
|
+ /* 520 */ 1743, 1712, 1744, 1745, 1746, 1747, 1749, 1748, 1750, 1762,
|
|
+ /* 530 */ 1754, 1755, 1760, 1761, 1765, 1766, 1763, 1664, 1657, 1659,
|
|
+ /* 540 */ 1660, 1667, 1772, 1781, 1797,
|
|
};
|
|
-#define YY_REDUCE_COUNT (383)
|
|
+#define YY_REDUCE_COUNT (387)
|
|
#define YY_REDUCE_MIN (-257)
|
|
-#define YY_REDUCE_MAX (1421)
|
|
+#define YY_REDUCE_MAX (1425)
|
|
static const short yy_reduce_ofst[] = {
|
|
/* 0 */ -168, -17, 164, 214, 310, -166, -184, -18, 98, -170,
|
|
/* 10 */ 305, 315, -163, -193, -178, -257, 395, 401, 476, 478,
|
|
@@ -149009,73 +149966,73 @@ static const short yy_reduce_ofst[] = {
|
|
/* 260 */ 1136, 1140, 1152, 813, 1150, 1150, 1153, 1173, 1195, 1090,
|
|
/* 270 */ 1154, 1167, 1170, 1104, 1155, 1156, 1109, 1172, 1174, 1179,
|
|
/* 280 */ 1177, 1188, 1205, 1171, 1160, 1196, 1121, 1165, 1203, 1228,
|
|
- /* 290 */ 1157, 1244, 1245, 1158, 1159, 1250, 1175, 1193, 1191, 1241,
|
|
- /* 300 */ 1231, 1243, 1257, 1259, 1261, 1276, 1280, 1254, 1255, 1230,
|
|
- /* 310 */ 1234, 1269, 1270, 1260, 1292, 1303, 1223, 1225, 1309, 1313,
|
|
- /* 320 */ 1296, 1317, 1319, 1320, 1323, 1300, 1307, 1308, 1310, 1304,
|
|
- /* 330 */ 1311, 1315, 1314, 1318, 1316, 1321, 1325, 1324, 1271, 1272,
|
|
- /* 340 */ 1332, 1294, 1298, 1299, 1301, 1302, 1306, 1312, 1329, 1356,
|
|
- /* 350 */ 1256, 1263, 1327, 1326, 1305, 1369, 1330, 1340, 1343, 1346,
|
|
- /* 360 */ 1350, 1391, 1394, 1410, 1412, 1415, 1417, 1419, 1328, 1331,
|
|
- /* 370 */ 1335, 1402, 1398, 1400, 1401, 1403, 1408, 1396, 1397, 1409,
|
|
- /* 380 */ 1414, 1420, 1421, 1413,
|
|
+ /* 290 */ 1157, 1244, 1245, 1158, 1159, 1250, 1175, 1193, 1191, 1225,
|
|
+ /* 300 */ 1229, 1243, 1230, 1246, 1236, 1259, 1261, 1262, 1263, 1298,
|
|
+ /* 310 */ 1301, 1260, 1264, 1235, 1237, 1273, 1277, 1266, 1305, 1313,
|
|
+ /* 320 */ 1234, 1239, 1317, 1320, 1302, 1323, 1325, 1328, 1331, 1306,
|
|
+ /* 330 */ 1315, 1316, 1318, 1314, 1321, 1332, 1319, 1335, 1329, 1336,
|
|
+ /* 340 */ 1338, 1324, 1279, 1280, 1339, 1303, 1307, 1310, 1312, 1330,
|
|
+ /* 350 */ 1322, 1337, 1340, 1360, 1257, 1267, 1342, 1341, 1300, 1372,
|
|
+ /* 360 */ 1304, 1343, 1348, 1350, 1353, 1400, 1404, 1414, 1416, 1419,
|
|
+ /* 370 */ 1420, 1421, 1347, 1351, 1362, 1408, 1403, 1405, 1406, 1407,
|
|
+ /* 380 */ 1410, 1401, 1402, 1412, 1417, 1418, 1423, 1425,
|
|
};
|
|
static const YYACTIONTYPE yy_default[] = {
|
|
- /* 0 */ 1536, 1536, 1536, 1376, 1159, 1265, 1159, 1159, 1159, 1376,
|
|
- /* 10 */ 1376, 1376, 1159, 1295, 1295, 1429, 1190, 1159, 1159, 1159,
|
|
- /* 20 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1375, 1159, 1159,
|
|
- /* 30 */ 1159, 1159, 1459, 1459, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 40 */ 1159, 1159, 1159, 1301, 1159, 1159, 1159, 1159, 1159, 1377,
|
|
- /* 50 */ 1378, 1159, 1159, 1159, 1428, 1430, 1393, 1311, 1310, 1309,
|
|
- /* 60 */ 1308, 1411, 1282, 1306, 1299, 1303, 1371, 1372, 1370, 1374,
|
|
- /* 70 */ 1378, 1377, 1159, 1302, 1342, 1356, 1341, 1159, 1159, 1159,
|
|
- /* 80 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 90 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 100 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 110 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 120 */ 1159, 1159, 1159, 1159, 1350, 1355, 1361, 1354, 1351, 1344,
|
|
- /* 130 */ 1343, 1345, 1346, 1159, 1180, 1229, 1159, 1159, 1159, 1159,
|
|
- /* 140 */ 1447, 1446, 1159, 1159, 1190, 1347, 1348, 1358, 1357, 1436,
|
|
- /* 150 */ 1492, 1491, 1394, 1159, 1159, 1159, 1159, 1159, 1159, 1459,
|
|
- /* 160 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 170 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 180 */ 1159, 1159, 1159, 1159, 1459, 1459, 1159, 1190, 1459, 1459,
|
|
- /* 190 */ 1186, 1336, 1335, 1186, 1289, 1159, 1442, 1265, 1256, 1159,
|
|
- /* 200 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 210 */ 1159, 1159, 1159, 1433, 1431, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 220 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 230 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 240 */ 1159, 1159, 1159, 1261, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 250 */ 1159, 1159, 1159, 1159, 1159, 1486, 1159, 1406, 1243, 1261,
|
|
- /* 260 */ 1261, 1261, 1261, 1263, 1244, 1242, 1255, 1190, 1166, 1528,
|
|
- /* 270 */ 1305, 1284, 1284, 1525, 1305, 1305, 1525, 1204, 1506, 1201,
|
|
- /* 280 */ 1295, 1295, 1295, 1284, 1289, 1289, 1373, 1262, 1255, 1159,
|
|
- /* 290 */ 1528, 1270, 1270, 1527, 1527, 1270, 1394, 1314, 1320, 1232,
|
|
- /* 300 */ 1305, 1238, 1238, 1238, 1238, 1270, 1177, 1305, 1305, 1314,
|
|
- /* 310 */ 1320, 1232, 1232, 1305, 1270, 1177, 1410, 1522, 1270, 1177,
|
|
- /* 320 */ 1384, 1270, 1177, 1270, 1177, 1384, 1230, 1230, 1230, 1219,
|
|
- /* 330 */ 1384, 1230, 1204, 1230, 1219, 1230, 1230, 1384, 1388, 1388,
|
|
- /* 340 */ 1384, 1288, 1283, 1288, 1283, 1288, 1283, 1288, 1283, 1270,
|
|
- /* 350 */ 1469, 1469, 1300, 1289, 1379, 1270, 1159, 1300, 1298, 1296,
|
|
- /* 360 */ 1305, 1183, 1222, 1489, 1489, 1485, 1485, 1485, 1533, 1533,
|
|
- /* 370 */ 1442, 1501, 1190, 1190, 1190, 1190, 1501, 1206, 1206, 1190,
|
|
- /* 380 */ 1190, 1190, 1190, 1501, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 390 */ 1496, 1159, 1395, 1274, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 400 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 410 */ 1159, 1159, 1159, 1159, 1159, 1159, 1325, 1159, 1162, 1439,
|
|
- /* 420 */ 1159, 1159, 1437, 1159, 1159, 1159, 1159, 1159, 1159, 1275,
|
|
- /* 430 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 440 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1524, 1159,
|
|
- /* 450 */ 1159, 1159, 1159, 1159, 1159, 1409, 1408, 1159, 1159, 1272,
|
|
- /* 460 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 470 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 480 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 490 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1297, 1159,
|
|
- /* 500 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 510 */ 1159, 1159, 1159, 1474, 1290, 1159, 1159, 1515, 1159, 1159,
|
|
- /* 520 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
|
|
- /* 530 */ 1159, 1159, 1510, 1246, 1327, 1159, 1326, 1330, 1159, 1171,
|
|
- /* 540 */ 1159,
|
|
+ /* 0 */ 1540, 1540, 1540, 1380, 1163, 1269, 1163, 1163, 1163, 1380,
|
|
+ /* 10 */ 1380, 1380, 1163, 1299, 1299, 1433, 1194, 1163, 1163, 1163,
|
|
+ /* 20 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1379, 1163, 1163,
|
|
+ /* 30 */ 1163, 1163, 1463, 1463, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 40 */ 1163, 1163, 1163, 1305, 1163, 1163, 1163, 1163, 1163, 1381,
|
|
+ /* 50 */ 1382, 1163, 1163, 1163, 1432, 1434, 1397, 1315, 1314, 1313,
|
|
+ /* 60 */ 1312, 1415, 1286, 1310, 1303, 1307, 1375, 1376, 1374, 1378,
|
|
+ /* 70 */ 1382, 1381, 1163, 1306, 1346, 1360, 1345, 1163, 1163, 1163,
|
|
+ /* 80 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 90 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 100 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 110 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 120 */ 1163, 1163, 1163, 1163, 1354, 1359, 1365, 1358, 1355, 1348,
|
|
+ /* 130 */ 1347, 1349, 1350, 1163, 1184, 1233, 1163, 1163, 1163, 1163,
|
|
+ /* 140 */ 1451, 1450, 1163, 1163, 1194, 1351, 1352, 1362, 1361, 1440,
|
|
+ /* 150 */ 1496, 1495, 1398, 1163, 1163, 1163, 1163, 1163, 1163, 1463,
|
|
+ /* 160 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 170 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 180 */ 1163, 1163, 1163, 1163, 1463, 1463, 1163, 1194, 1463, 1463,
|
|
+ /* 190 */ 1190, 1340, 1339, 1190, 1293, 1163, 1446, 1269, 1260, 1163,
|
|
+ /* 200 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 210 */ 1163, 1163, 1163, 1437, 1435, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 220 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 230 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 240 */ 1163, 1163, 1163, 1265, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 250 */ 1163, 1163, 1163, 1163, 1163, 1490, 1163, 1410, 1247, 1265,
|
|
+ /* 260 */ 1265, 1265, 1265, 1267, 1248, 1246, 1259, 1194, 1170, 1532,
|
|
+ /* 270 */ 1309, 1288, 1288, 1529, 1309, 1309, 1529, 1208, 1510, 1205,
|
|
+ /* 280 */ 1299, 1299, 1299, 1288, 1293, 1293, 1377, 1266, 1259, 1163,
|
|
+ /* 290 */ 1532, 1274, 1274, 1531, 1531, 1274, 1398, 1318, 1324, 1304,
|
|
+ /* 300 */ 1293, 1236, 1304, 1293, 1309, 1242, 1242, 1242, 1242, 1274,
|
|
+ /* 310 */ 1181, 1309, 1309, 1318, 1324, 1236, 1236, 1309, 1274, 1181,
|
|
+ /* 320 */ 1414, 1526, 1274, 1181, 1388, 1274, 1181, 1274, 1181, 1388,
|
|
+ /* 330 */ 1234, 1234, 1234, 1223, 1388, 1234, 1208, 1234, 1223, 1234,
|
|
+ /* 340 */ 1234, 1388, 1392, 1392, 1388, 1292, 1287, 1292, 1287, 1292,
|
|
+ /* 350 */ 1287, 1292, 1287, 1274, 1473, 1473, 1304, 1293, 1383, 1274,
|
|
+ /* 360 */ 1163, 1304, 1302, 1300, 1309, 1187, 1226, 1493, 1493, 1489,
|
|
+ /* 370 */ 1489, 1489, 1537, 1537, 1446, 1505, 1194, 1194, 1194, 1194,
|
|
+ /* 380 */ 1505, 1210, 1210, 1194, 1194, 1194, 1194, 1505, 1163, 1163,
|
|
+ /* 390 */ 1163, 1163, 1163, 1163, 1500, 1163, 1399, 1278, 1163, 1163,
|
|
+ /* 400 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 410 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 420 */ 1329, 1163, 1166, 1443, 1163, 1163, 1441, 1163, 1163, 1163,
|
|
+ /* 430 */ 1163, 1163, 1163, 1279, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 440 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 450 */ 1163, 1163, 1528, 1163, 1163, 1163, 1163, 1163, 1163, 1413,
|
|
+ /* 460 */ 1412, 1163, 1163, 1276, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 470 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 480 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 490 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 500 */ 1163, 1163, 1301, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 510 */ 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1478, 1294, 1163,
|
|
+ /* 520 */ 1163, 1519, 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163,
|
|
+ /* 530 */ 1163, 1163, 1163, 1163, 1163, 1163, 1514, 1250, 1331, 1163,
|
|
+ /* 540 */ 1330, 1334, 1163, 1175, 1163,
|
|
};
|
|
/********** End of lemon-generated parsing tables *****************************/
|
|
|
|
@@ -149727,10 +150684,10 @@ static const char *const yyRuleName[] =
|
|
/* 139 */ "limit_opt ::= LIMIT expr",
|
|
/* 140 */ "limit_opt ::= LIMIT expr OFFSET expr",
|
|
/* 141 */ "limit_opt ::= LIMIT expr COMMA expr",
|
|
- /* 142 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
|
|
+ /* 142 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt",
|
|
/* 143 */ "where_opt ::=",
|
|
/* 144 */ "where_opt ::= WHERE expr",
|
|
- /* 145 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
|
|
+ /* 145 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt orderby_opt limit_opt",
|
|
/* 146 */ "setlist ::= setlist COMMA nm EQ expr",
|
|
/* 147 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
|
|
/* 148 */ "setlist ::= nm EQ expr",
|
|
@@ -150612,10 +151569,10 @@ static const YYCODETYPE yyRuleInfoLhs[]
|
|
238, /* (139) limit_opt ::= LIMIT expr */
|
|
238, /* (140) limit_opt ::= LIMIT expr OFFSET expr */
|
|
238, /* (141) limit_opt ::= LIMIT expr COMMA expr */
|
|
- 181, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
|
|
+ 181, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt */
|
|
234, /* (143) where_opt ::= */
|
|
234, /* (144) where_opt ::= WHERE expr */
|
|
- 181, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
|
|
+ 181, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt orderby_opt limit_opt */
|
|
254, /* (146) setlist ::= setlist COMMA nm EQ expr */
|
|
254, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */
|
|
254, /* (148) setlist ::= nm EQ expr */
|
|
@@ -150992,10 +151949,10 @@ static const signed char yyRuleInfoNRhs[
|
|
-2, /* (139) limit_opt ::= LIMIT expr */
|
|
-4, /* (140) limit_opt ::= LIMIT expr OFFSET expr */
|
|
-4, /* (141) limit_opt ::= LIMIT expr COMMA expr */
|
|
- -6, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
|
|
+ -8, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt */
|
|
0, /* (143) where_opt ::= */
|
|
-2, /* (144) where_opt ::= WHERE expr */
|
|
- -8, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
|
|
+ -10, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt orderby_opt limit_opt */
|
|
-5, /* (146) setlist ::= setlist COMMA nm EQ expr */
|
|
-7, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */
|
|
-3, /* (148) setlist ::= nm EQ expr */
|
|
@@ -151896,17 +152853,21 @@ static YYACTIONTYPE yy_reduce(
|
|
case 141: /* limit_opt ::= LIMIT expr COMMA expr */
|
|
{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,yymsp[-2].minor.yy524);}
|
|
break;
|
|
- case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
|
|
+ case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt */
|
|
{
|
|
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy483, &yymsp[-1].minor.yy0);
|
|
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy483,yymsp[0].minor.yy524,0,0);
|
|
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy483, &yymsp[-3].minor.yy0);
|
|
+#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
|
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434); yymsp[-1].minor.yy434 = 0;
|
|
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy524); yymsp[0].minor.yy524 = 0;
|
|
+#endif
|
|
+ sqlite3DeleteFrom(pParse,yymsp[-4].minor.yy483,yymsp[-2].minor.yy524,yymsp[-1].minor.yy434,yymsp[0].minor.yy524);
|
|
}
|
|
break;
|
|
- case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
|
|
+ case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt orderby_opt limit_opt */
|
|
{
|
|
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy483, &yymsp[-3].minor.yy0);
|
|
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy434,"set list");
|
|
- sqlite3Update(pParse,yymsp[-4].minor.yy483,yymsp[-1].minor.yy434,yymsp[0].minor.yy524,yymsp[-5].minor.yy494,0,0,0);
|
|
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy483, &yymsp[-5].minor.yy0);
|
|
+ sqlite3ExprListCheckLength(pParse,yymsp[-3].minor.yy434,"set list");
|
|
+ sqlite3Update(pParse,yymsp[-6].minor.yy483,yymsp[-3].minor.yy434,yymsp[-2].minor.yy524,yymsp[-7].minor.yy494,yymsp[-1].minor.yy434,yymsp[0].minor.yy524,0);
|
|
}
|
|
break;
|
|
case 146: /* setlist ::= setlist COMMA nm EQ expr */
|
|
@@ -152722,7 +153683,7 @@ static YYACTIONTYPE yy_reduce(
|
|
break;
|
|
/********** End reduce actions ************************************************/
|
|
};
|
|
- assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
|
|
+ assert( (size_t)yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
|
|
yygoto = yyRuleInfoLhs[yyruleno];
|
|
yysize = yyRuleInfoNRhs[yyruleno];
|
|
yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
|
|
@@ -154654,7 +155615,7 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlit
|
|
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
|
|
** contains the text of SQLITE_VERSION macro.
|
|
*/
|
|
-SQLITE_API const char sqlite3_version[] = SQLITE_VERSION;
|
|
+const char sqlite3_version[] = SQLITE_VERSION;
|
|
#endif
|
|
|
|
/* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns
|
|
@@ -154700,7 +155661,7 @@ SQLITE_API int sqlite3_threadsafe(void){
|
|
** I/O active are written using this function. These messages
|
|
** are intended for debugging activity only.
|
|
*/
|
|
-SQLITE_API void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...) = 0;
|
|
+void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...) = 0;
|
|
#endif
|
|
|
|
/*
|
|
@@ -154710,7 +155671,7 @@ SQLITE_API void (SQLITE_CDECL *sqlite3Io
|
|
**
|
|
** See also the "PRAGMA temp_store_directory" SQL command.
|
|
*/
|
|
-SQLITE_API char *sqlite3_temp_directory = 0;
|
|
+char *sqlite3_temp_directory = 0;
|
|
|
|
/*
|
|
** If the following global variable points to a string which is the
|
|
@@ -154719,7 +155680,7 @@ SQLITE_API char *sqlite3_temp_directory
|
|
**
|
|
** See also the "PRAGMA data_store_directory" SQL command.
|
|
*/
|
|
-SQLITE_API char *sqlite3_data_directory = 0;
|
|
+char *sqlite3_data_directory = 0;
|
|
|
|
/*
|
|
** Initialize SQLite.
|
|
@@ -155034,7 +155995,7 @@ SQLITE_API int sqlite3_config(int op, ..
|
|
#endif
|
|
|
|
case SQLITE_CONFIG_MALLOC: {
|
|
- /* EVIDENCE-OF: R-55594-21030 The SQLITE_CONFIG_MALLOC option takes a
|
|
+ /* EVIDENCE-OF: R-55594-21030 The SQLITE_CONFIG_MALLOC2 option takes a
|
|
** single argument which is a pointer to an instance of the
|
|
** sqlite3_mem_methods structure. The argument specifies alternative
|
|
** low-level memory allocation routines to be used in place of the memory
|
|
@@ -155042,15 +156003,32 @@ SQLITE_API int sqlite3_config(int op, ..
|
|
sqlite3GlobalConfig.m = *va_arg(ap, sqlite3_mem_methods*);
|
|
break;
|
|
}
|
|
+ case SQLITE_CONFIG_MALLOC2: {
|
|
+ /* EVIDENCE-OF: R-55594-21030 The SQLITE_CONFIG_MALLOC2 option takes a
|
|
+ ** single argument which is a pointer to an instance of the
|
|
+ ** sqlite3_mem_methods2 structure. The argument specifies alternative
|
|
+ ** low-level memory allocation routines to be used in place of the memory
|
|
+ ** allocation routines built into SQLite. */
|
|
+ sqlite3GlobalConfig.m2 = *va_arg(ap, sqlite3_mem_methods2*);
|
|
+ break;
|
|
+ }
|
|
case SQLITE_CONFIG_GETMALLOC: {
|
|
- /* EVIDENCE-OF: R-51213-46414 The SQLITE_CONFIG_GETMALLOC option takes a
|
|
+ /* EVIDENCE-OF: R-51213-46414 The SQLITE_CONFIG_GETMALLOC2 option takes a
|
|
** single argument which is a pointer to an instance of the
|
|
** sqlite3_mem_methods structure. The sqlite3_mem_methods structure is
|
|
** filled with the currently defined memory allocation routines. */
|
|
- if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault();
|
|
*va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m;
|
|
break;
|
|
}
|
|
+ case SQLITE_CONFIG_GETMALLOC2: {
|
|
+ /* EVIDENCE-OF: R-51213-46414 The SQLITE_CONFIG_GETMALLOC2 option takes a
|
|
+ ** single argument which is a pointer to an instance of the
|
|
+ ** sqlite3_mem_methods structure. The sqlite3_mem_methods structure is
|
|
+ ** filled with the currently defined memory allocation routines. */
|
|
+ if( sqlite3GlobalConfig.m2.xMalloc==0 ) sqlite3MemSetDefault();
|
|
+ *va_arg(ap, sqlite3_mem_methods2*) = sqlite3GlobalConfig.m2;
|
|
+ break;
|
|
+ }
|
|
case SQLITE_CONFIG_MEMSTATUS: {
|
|
/* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes
|
|
** single argument of type int, interpreted as a boolean, which enables
|
|
@@ -155143,16 +156121,16 @@ SQLITE_API int sqlite3_config(int op, ..
|
|
** Setting sqlite3GlobalConfig.m to all zeros will cause malloc to
|
|
** revert to its default implementation when sqlite3_initialize() is run
|
|
*/
|
|
- memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m));
|
|
+ memset(&sqlite3GlobalConfig.m2, 0, sizeof(sqlite3GlobalConfig.m2));
|
|
}else{
|
|
/* EVIDENCE-OF: R-61006-08918 If the memory pointer is not NULL then the
|
|
** alternative memory allocator is engaged to handle all of SQLites
|
|
** memory allocation needs. */
|
|
#ifdef SQLITE_ENABLE_MEMSYS3
|
|
- sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3();
|
|
+ sqlite3GlobalConfig.m2 = *sqlite3MemGetMemsys3();
|
|
#endif
|
|
#ifdef SQLITE_ENABLE_MEMSYS5
|
|
- sqlite3GlobalConfig.m = *sqlite3MemGetMemsys5();
|
|
+ sqlite3GlobalConfig.m2 = *sqlite3MemGetMemsys5();
|
|
#endif
|
|
}
|
|
break;
|
|
@@ -155252,6 +156230,19 @@ SQLITE_API int sqlite3_config(int op, ..
|
|
break;
|
|
}
|
|
|
|
+ case 64: /* SQLITE_CONFIG_EXPLAIN_COMMENTS */ {
|
|
+ /* Enable VDBE commenting (cannot be switched off) */
|
|
+ sqlite3GlobalConfig.bVdbeComments = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case 65: /* SQLITE_CONFIG_ENABLE_UNKOWN_SQL_FUNCTION */ {
|
|
+ /* Enable unknown sql function (cannot be switched off) */
|
|
+ sqlite3GlobalConfig.bEnableUnknownSqlFunction = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+
|
|
case SQLITE_CONFIG_STMTJRNL_SPILL: {
|
|
sqlite3GlobalConfig.nStmtSpill = va_arg(ap, int);
|
|
break;
|
|
@@ -155433,7 +156424,7 @@ SQLITE_API int sqlite3_db_config(sqlite3
|
|
case SQLITE_DBCONFIG_MAINDBNAME: {
|
|
/* IMP: R-06824-28531 */
|
|
/* IMP: R-36257-52125 */
|
|
- db->aDb[0].zDbSName = va_arg(ap,char*);
|
|
+ db->aDb[0].zDbSName = va_arg(ap,const char*);
|
|
rc = SQLITE_OK;
|
|
break;
|
|
}
|
|
@@ -155598,7 +156589,8 @@ SQLITE_API void sqlite3_set_last_insert_
|
|
/*
|
|
** Return the number of changes in the most recent call to sqlite3_exec().
|
|
*/
|
|
-SQLITE_API int sqlite3_changes(sqlite3 *db){
|
|
+#undef sqlite3_changes
|
|
+SQLITE_API u64 sqlite3_changes(sqlite3 *db){
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !sqlite3SafetyCheckOk(db) ){
|
|
(void)SQLITE_MISUSE_BKPT;
|
|
@@ -155611,7 +156603,8 @@ SQLITE_API int sqlite3_changes(sqlite3 *
|
|
/*
|
|
** Return the number of changes since the database handle was opened.
|
|
*/
|
|
-SQLITE_API int sqlite3_total_changes(sqlite3 *db){
|
|
+#undef sqlite3_total_changes
|
|
+SQLITE_API u64 sqlite3_total_changes(sqlite3 *db){
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !sqlite3SafetyCheckOk(db) ){
|
|
(void)SQLITE_MISUSE_BKPT;
|
|
@@ -156469,6 +157462,7 @@ static int createFunctionApi(
|
|
/*
|
|
** Create new user functions.
|
|
*/
|
|
+#undef sqlite3_create_function
|
|
SQLITE_API int sqlite3_create_function(
|
|
sqlite3 *db,
|
|
const char *zFunc,
|
|
@@ -156796,7 +157790,7 @@ SQLITE_PRIVATE int sqlite3WalDefaultHook
|
|
){
|
|
if( nFrame>=SQLITE_PTR_TO_INT(pClientData) ){
|
|
sqlite3BeginBenignMalloc();
|
|
- sqlite3_wal_checkpoint(db, zDb);
|
|
+ sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0);
|
|
sqlite3EndBenignMalloc();
|
|
}
|
|
return SQLITE_OK;
|
|
@@ -156924,6 +157918,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2
|
|
** to contains a zero-length string, all attached databases are
|
|
** checkpointed.
|
|
*/
|
|
+#undef sqlite3_wal_checkpoint
|
|
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
|
|
/* EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is equivalent to
|
|
** sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). */
|
|
@@ -157354,14 +158349,14 @@ SQLITE_PRIVATE int sqlite3ParseUri(
|
|
int eState; /* Parser state when parsing URI */
|
|
int iIn; /* Input character index */
|
|
int iOut = 0; /* Output character index */
|
|
- u64 nByte = nUri+2; /* Bytes of space to allocate */
|
|
+ size_t nByte = nUri+2; /* Bytes of space to allocate */
|
|
|
|
/* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
|
|
** method that there may be extra parameters following the file-name. */
|
|
flags |= SQLITE_OPEN_URI;
|
|
|
|
for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');
|
|
- zFile = sqlite3_malloc64(nByte);
|
|
+ zFile = sqlite3Malloc(nByte);
|
|
if( !zFile ) return SQLITE_NOMEM_BKPT;
|
|
|
|
iIn = 5;
|
|
@@ -157464,21 +158459,21 @@ SQLITE_PRIVATE int sqlite3ParseUri(
|
|
while( zOpt[0] ){
|
|
int nOpt = sqlite3Strlen30(zOpt);
|
|
char *zVal = &zOpt[nOpt+1];
|
|
- int nVal = sqlite3Strlen30(zVal);
|
|
+ size_t nVal = sqlite3Strlen30(zVal);
|
|
|
|
if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){
|
|
zVfs = zVal;
|
|
}else{
|
|
- struct OpenMode {
|
|
+ const struct OpenMode {
|
|
const char *z;
|
|
int mode;
|
|
} *aMode = 0;
|
|
- char *zModeType = 0;
|
|
+ const char *zModeType = 0;
|
|
int mask = 0;
|
|
int limit = 0;
|
|
|
|
if( nOpt==5 && memcmp("cache", zOpt, 5)==0 ){
|
|
- static struct OpenMode aCacheMode[] = {
|
|
+ static const struct OpenMode aCacheMode[] = {
|
|
{ "shared", SQLITE_OPEN_SHAREDCACHE },
|
|
{ "private", SQLITE_OPEN_PRIVATECACHE },
|
|
{ 0, 0 }
|
|
@@ -157490,7 +158485,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
|
|
zModeType = "cache";
|
|
}
|
|
if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){
|
|
- static struct OpenMode aOpenMode[] = {
|
|
+ static const struct OpenMode aOpenMode[] = {
|
|
{ "ro", SQLITE_OPEN_READONLY },
|
|
{ "rw", SQLITE_OPEN_READWRITE },
|
|
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
|
|
@@ -157534,7 +158529,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
|
|
}
|
|
|
|
}else{
|
|
- zFile = sqlite3_malloc64(nUri+2);
|
|
+ zFile = sqlite3Malloc(nUri+2);
|
|
if( !zFile ) return SQLITE_NOMEM_BKPT;
|
|
if( nUri ){
|
|
memcpy(zFile, zUri, nUri);
|
|
@@ -157702,9 +158697,6 @@ static int openDatabase(
|
|
#if SQLITE_DEFAULT_FILE_FORMAT<4
|
|
| SQLITE_LegacyFileFmt
|
|
#endif
|
|
-#ifdef SQLITE_ENABLE_LOAD_EXTENSION
|
|
- | SQLITE_LoadExtension
|
|
-#endif
|
|
#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS
|
|
| SQLITE_RecTriggers
|
|
#endif
|
|
@@ -158007,6 +158999,7 @@ SQLITE_API int sqlite3_open16(
|
|
/*
|
|
** Register a new collation sequence with the database handle db.
|
|
*/
|
|
+#undef sqlite3_create_collation
|
|
SQLITE_API int sqlite3_create_collation(
|
|
sqlite3* db,
|
|
const char *zName,
|
|
@@ -158118,6 +159111,7 @@ SQLITE_API int sqlite3_collation_needed1
|
|
** This function is now an anachronism. It used to be used to recover from a
|
|
** malloc() failure, but SQLite now does this automatically.
|
|
*/
|
|
+#undef sqlite3_global_recover
|
|
SQLITE_API int sqlite3_global_recover(void){
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -158192,6 +159186,7 @@ SQLITE_PRIVATE int sqlite3IoerrnomemErro
|
|
** SQLite no longer uses thread-specific data so this routine is now a
|
|
** no-op. It is retained for historical compatibility.
|
|
*/
|
|
+#undef sqlite3_thread_cleanup
|
|
SQLITE_API void sqlite3_thread_cleanup(void){
|
|
}
|
|
#endif
|
|
@@ -158598,6 +159593,7 @@ SQLITE_API int sqlite3_test_control(int
|
|
break;
|
|
}
|
|
|
|
+#ifndef SQLITE_UNTESTABLE
|
|
/* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
|
|
**
|
|
** If parameter onoff is non-zero, subsequent calls to localtime()
|
|
@@ -158607,6 +159603,7 @@ SQLITE_API int sqlite3_test_control(int
|
|
sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
|
|
break;
|
|
}
|
|
+#endif
|
|
|
|
/* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff);
|
|
**
|
|
@@ -158719,6 +159716,22 @@ SQLITE_API int sqlite3_test_control(int
|
|
break;
|
|
}
|
|
#endif /* defined(YYCOVERAGE) */
|
|
+
|
|
+ /* sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, sqlite3_context*);
|
|
+ **
|
|
+ ** This test-control causes the most recent sqlite3_result_int64() value
|
|
+ ** to be interpreted as a MEM_IntReal instead of as an MEM_Int. Normally,
|
|
+ ** MEM_IntReal values only arise during an INSERT operation of integer
|
|
+ ** values into a REAL column, so they can be challenging to test. This
|
|
+ ** test-control enables us to write an intreal() SQL function that can
|
|
+ ** inject an intreal() value at arbitrary places in an SQL statement,
|
|
+ ** for testing purposes.
|
|
+ */
|
|
+ case SQLITE_TESTCTRL_RESULT_INTREAL: {
|
|
+ sqlite3_context *pCtx = va_arg(ap, sqlite3_context*);
|
|
+ sqlite3ResultIntReal(pCtx);
|
|
+ break;
|
|
+ }
|
|
}
|
|
va_end(ap);
|
|
#endif /* SQLITE_UNTESTABLE */
|
|
@@ -159993,10 +161006,10 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3
|
|
|
|
|
|
#ifndef MIN
|
|
-# define MIN(x,y) ((x)<(y)?(x):(y))
|
|
+# define MIN(A,B) ((A)<(B)?(A):(B))
|
|
#endif
|
|
#ifndef MAX
|
|
-# define MAX(x,y) ((x)>(y)?(x):(y))
|
|
+# define MAX(A,B) ((A)>(B)?(A):(B))
|
|
#endif
|
|
|
|
/*
|
|
@@ -160538,6 +161551,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
|
SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
|
|
SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
|
|
#endif
|
|
+#define sqlite3Fts5UnicodeIsdiacritic sqlite3FtsUnicodeIsdiacritic
|
|
+#define sqlite3Fts5UnicodeFold sqlite3FtsUnicodeFold
|
|
|
|
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
|
|
#endif /* _FTSINT_H */
|
|
@@ -161067,10 +162082,10 @@ static void fts3Appendf(
|
|
** memory.
|
|
*/
|
|
static char *fts3QuoteId(char const *zInput){
|
|
- sqlite3_int64 nRet;
|
|
+ size_t nRet;
|
|
char *zRet;
|
|
- nRet = 2 + (int)strlen(zInput)*2 + 1;
|
|
- zRet = sqlite3_malloc64(nRet);
|
|
+ nRet = 2 + strlen(zInput)*2 + 1;
|
|
+ zRet = sqlite3_malloc(nRet);
|
|
if( zRet ){
|
|
int i;
|
|
char *z = zRet;
|
|
@@ -161111,14 +162126,14 @@ static char *fts3QuoteId(char const *zIn
|
|
static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
|
|
char *zRet = 0;
|
|
char *zFree = 0;
|
|
- char *zFunction;
|
|
+ const char *zFunction;
|
|
int i;
|
|
|
|
if( p->zContentTbl==0 ){
|
|
if( !zFunc ){
|
|
zFunction = "";
|
|
}else{
|
|
- zFree = zFunction = fts3QuoteId(zFunc);
|
|
+ zFunction = zFree = fts3QuoteId(zFunc);
|
|
}
|
|
fts3Appendf(pRc, &zRet, "docid");
|
|
for(i=0; i<p->nColumn; i++){
|
|
@@ -161168,13 +162183,13 @@ static char *fts3ReadExprList(Fts3Table
|
|
static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
|
|
char *zRet = 0;
|
|
char *zFree = 0;
|
|
- char *zFunction;
|
|
+ const char *zFunction;
|
|
int i;
|
|
|
|
if( !zFunc ){
|
|
zFunction = "";
|
|
}else{
|
|
- zFree = zFunction = fts3QuoteId(zFunc);
|
|
+ zFunction = zFree = fts3QuoteId(zFunc);
|
|
}
|
|
fts3Appendf(pRc, &zRet, "?");
|
|
for(i=0; i<p->nColumn; i++){
|
|
@@ -161251,7 +162266,7 @@ static int fts3PrefixParameter(
|
|
}
|
|
}
|
|
|
|
- aIndex = sqlite3_malloc64(sizeof(struct Fts3Index) * nIndex);
|
|
+ aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
|
|
*apIndex = aIndex;
|
|
if( !aIndex ){
|
|
return SQLITE_NOMEM;
|
|
@@ -161344,7 +162359,7 @@ static int fts3ContentColumns(
|
|
}
|
|
|
|
/* Allocate and populate the array to return. */
|
|
- azCol = (const char **)sqlite3_malloc64(sizeof(char *) * nCol + nStr);
|
|
+ azCol = (const char **)sqlite3_malloc(sizeof(char *) * nCol + nStr);
|
|
if( azCol==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -161392,7 +162407,7 @@ static int fts3InitVtab(
|
|
Fts3Table *p = 0; /* Pointer to allocated vtab */
|
|
int rc = SQLITE_OK; /* Return code */
|
|
int i; /* Iterator variable */
|
|
- sqlite3_int64 nByte; /* Size of allocation used for *p */
|
|
+ size_t nByte; /* Size of allocation used for *p */
|
|
int iCol; /* Column index */
|
|
int nString = 0; /* Bytes required to hold all column names */
|
|
int nCol = 0; /* Number of columns in the FTS table */
|
|
@@ -161426,10 +162441,10 @@ static int fts3InitVtab(
|
|
nName = (int)strlen(argv[2]) + 1;
|
|
|
|
nByte = sizeof(const char *) * (argc-2);
|
|
- aCol = (const char **)sqlite3_malloc64(nByte);
|
|
+ aCol = (const char **)sqlite3_malloc(nByte);
|
|
if( aCol ){
|
|
memset((void*)aCol, 0, nByte);
|
|
- azNotindexed = (char **)sqlite3_malloc64(nByte);
|
|
+ azNotindexed = (char **)sqlite3_malloc(nByte);
|
|
}
|
|
if( azNotindexed ){
|
|
memset(azNotindexed, 0, nByte);
|
|
@@ -161624,7 +162639,7 @@ static int fts3InitVtab(
|
|
nName + /* zName */
|
|
nDb + /* zDb */
|
|
nString; /* Space for azColumn strings */
|
|
- p = (Fts3Table*)sqlite3_malloc64(nByte);
|
|
+ p = (Fts3Table*)sqlite3_malloc(nByte);
|
|
if( p==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
goto fts3_init_out;
|
|
@@ -162811,7 +163826,7 @@ static int fts3DoclistOrMerge(
|
|
** A symetric argument may be made if the doclists are in descending
|
|
** order.
|
|
*/
|
|
- aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
|
|
+ aOut = sqlite3_malloc((size_t)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
|
|
if( !aOut ) return SQLITE_NOMEM;
|
|
|
|
p = aOut;
|
|
@@ -162881,7 +163896,7 @@ static int fts3DoclistPhraseMerge(
|
|
|
|
assert( nDist>0 );
|
|
if( bDescDoclist ){
|
|
- aOut = sqlite3_malloc64((sqlite3_int64)*pnRight + FTS3_VARINT_MAX);
|
|
+ aOut = sqlite3_malloc((size_t)*pnRight + FTS3_VARINT_MAX);
|
|
if( aOut==0 ) return SQLITE_NOMEM;
|
|
}else{
|
|
aOut = aRight;
|
|
@@ -164251,6 +165266,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlit
|
|
|
|
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
|
|| sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
|
|
+ || sqlite3Fts3HashInsert(pHash, "unicode", 8, (void *)pUnicode)
|
|
#endif
|
|
#ifdef SQLITE_ENABLE_ICU
|
|
|| (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
|
|
@@ -165292,7 +166308,7 @@ static int fts3EvalStart(Fts3Cursor *pCs
|
|
if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){
|
|
Fts3TokenAndCost *aTC;
|
|
Fts3Expr **apOr;
|
|
- aTC = (Fts3TokenAndCost *)sqlite3_malloc64(
|
|
+ aTC = (Fts3TokenAndCost *)sqlite3_malloc(
|
|
sizeof(Fts3TokenAndCost) * nToken
|
|
+ sizeof(Fts3Expr *) * nOr * 2
|
|
);
|
|
@@ -165603,7 +166619,7 @@ static int fts3EvalNearTest(Fts3Expr *pE
|
|
&& (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
|
|
){
|
|
Fts3Expr *p;
|
|
- sqlite3_int64 nTmp = 0; /* Bytes of temp space */
|
|
+ size_t nTmp = 0; /* Bytes of temp space */
|
|
char *aTmp; /* Temp space for PoslistNearMerge() */
|
|
|
|
/* Allocate temporary working space. */
|
|
@@ -165612,7 +166628,7 @@ static int fts3EvalNearTest(Fts3Expr *pE
|
|
nTmp += p->pRight->pPhrase->doclist.nList;
|
|
}
|
|
nTmp += p->pPhrase->doclist.nList;
|
|
- aTmp = sqlite3_malloc64(nTmp*2);
|
|
+ aTmp = sqlite3_malloc(nTmp*2);
|
|
if( !aTmp ){
|
|
*pRc = SQLITE_NOMEM;
|
|
res = 0;
|
|
@@ -165953,7 +166969,7 @@ static int fts3EvalGatherStats(
|
|
for(p=pRoot; p; p=p->pLeft){
|
|
Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight);
|
|
assert( pE->aMI==0 );
|
|
- pE->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32));
|
|
+ pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32));
|
|
if( !pE->aMI ) return SQLITE_NOMEM;
|
|
memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
|
|
}
|
|
@@ -166329,7 +167345,7 @@ static int fts3auxConnectMethod(
|
|
char const *zFts3; /* Name of fts3 table */
|
|
int nDb; /* Result of strlen(zDb) */
|
|
int nFts3; /* Result of strlen(zFts3) */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate here */
|
|
+ size_t nByte; /* Bytes of space to allocate here */
|
|
int rc; /* value returned by declare_vtab() */
|
|
Fts3auxTable *p; /* Virtual table object to return */
|
|
|
|
@@ -166361,7 +167377,7 @@ static int fts3auxConnectMethod(
|
|
if( rc!=SQLITE_OK ) return rc;
|
|
|
|
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
|
|
- p = (Fts3auxTable *)sqlite3_malloc64(nByte);
|
|
+ p = (Fts3auxTable *)sqlite3_malloc(nByte);
|
|
if( !p ) return SQLITE_NOMEM;
|
|
memset(p, 0, nByte);
|
|
|
|
@@ -166807,7 +167823,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sq
|
|
};
|
|
int rc; /* Return code */
|
|
|
|
- rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0);
|
|
+ rc = sqlite3_create_module_v2(db, "fts4aux", &fts3aux_module, 0, 0);
|
|
return rc;
|
|
}
|
|
|
|
@@ -166939,8 +167955,8 @@ static int fts3isspace(char c){
|
|
** zero the memory before returning a pointer to it. If unsuccessful,
|
|
** return NULL.
|
|
*/
|
|
-static void *fts3MallocZero(sqlite3_int64 nByte){
|
|
- void *pRet = sqlite3_malloc64(nByte);
|
|
+static void *fts3MallocZero(size_t nByte){
|
|
+ void *pRet = sqlite3_malloc(nByte);
|
|
if( pRet ) memset(pRet, 0, nByte);
|
|
return pRet;
|
|
}
|
|
@@ -167214,7 +168230,7 @@ static int getNextNode(
|
|
int *pnConsumed /* OUT: Number of bytes consumed */
|
|
){
|
|
static const struct Fts3Keyword {
|
|
- char *z; /* Keyword text */
|
|
+ const char *z; /* Keyword text */
|
|
unsigned char n; /* Length of the keyword */
|
|
unsigned char parenOnly; /* Only valid in paren mode */
|
|
unsigned char eType; /* Keyword code */
|
|
@@ -167612,7 +168628,7 @@ static int fts3ExprBalance(Fts3Expr **pp
|
|
if( rc==SQLITE_OK ){
|
|
if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
|
|
Fts3Expr **apLeaf;
|
|
- apLeaf = (Fts3Expr **)sqlite3_malloc64(sizeof(Fts3Expr *) * nMaxDepth);
|
|
+ apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
|
|
if( 0==apLeaf ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -168032,7 +169048,7 @@ static void fts3ExprTestCommon(
|
|
zExpr = (const char *)sqlite3_value_text(argv[1]);
|
|
nExpr = sqlite3_value_bytes(argv[1]);
|
|
nCol = argc-2;
|
|
- azCol = (char **)sqlite3_malloc64(nCol*sizeof(char *));
|
|
+ azCol = (char **)sqlite3_malloc(nCol*sizeof(char *));
|
|
if( !azCol ){
|
|
sqlite3_result_error_nomem(context);
|
|
goto exprtest_out;
|
|
@@ -168146,8 +169162,8 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTe
|
|
/*
|
|
** Malloc and Free functions
|
|
*/
|
|
-static void *fts3HashMalloc(sqlite3_int64 n){
|
|
- void *p = sqlite3_malloc64(n);
|
|
+static void *fts3HashMalloc(size_t n){
|
|
+ void *p = sqlite3_malloc(n);
|
|
if( p ){
|
|
memset(p, 0, n);
|
|
}
|
|
@@ -169486,7 +170502,7 @@ static void testFunc(
|
|
}
|
|
|
|
while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){
|
|
- Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos));
|
|
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(iPos));
|
|
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken));
|
|
zToken = &zInput[iStart];
|
|
nToken = iEnd-iStart;
|
|
@@ -170035,14 +171051,14 @@ static int fts3tokDequoteArray(
|
|
*pazDequote = 0;
|
|
}else{
|
|
int i;
|
|
- int nByte = 0;
|
|
+ size_t nByte = 0;
|
|
char **azDequote;
|
|
|
|
for(i=0; i<argc; i++){
|
|
- nByte += (int)(strlen(argv[i]) + 1);
|
|
+ nByte += strlen(argv[i]) + 1;
|
|
}
|
|
|
|
- *pazDequote = azDequote = sqlite3_malloc64(sizeof(char *)*argc + nByte);
|
|
+ *pazDequote = azDequote = sqlite3_malloc(sizeof(char *)*argc + nByte);
|
|
if( azDequote==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -170368,7 +171384,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sq
|
|
};
|
|
int rc; /* Return code */
|
|
|
|
- rc = sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash);
|
|
+ rc = sqlite3_create_module_v2(db, "fts3tokenize", &fts3tok_module, (void*)pHash, 0);
|
|
return rc;
|
|
}
|
|
|
|
@@ -170668,7 +171684,7 @@ static int fts3SqlStmt(
|
|
sqlite3_stmt **pp, /* OUT: Statement handle */
|
|
sqlite3_value **apVal /* Values to bind to statement */
|
|
){
|
|
- const char *azSql[] = {
|
|
+ const char *const azSql[] = {
|
|
/* 0 */ "DELETE FROM %Q.'%q_content' WHERE rowid = ?",
|
|
/* 1 */ "SELECT NOT EXISTS(SELECT docid FROM %Q.'%q_content' WHERE rowid!=?)",
|
|
/* 2 */ "DELETE FROM %Q.'%q_content'",
|
|
@@ -176275,7 +177291,7 @@ static void (*fts3MIBufferAlloc(Matchinf
|
|
aOut = &p->aMatchinfo[p->nElem+2];
|
|
xRet = fts3MIBufferFree;
|
|
}else{
|
|
- aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
|
|
+ aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32));
|
|
if( aOut ){
|
|
xRet = sqlite3_free;
|
|
if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
|
|
@@ -176635,7 +177651,7 @@ static int fts3BestSnippet(
|
|
** the required space using malloc().
|
|
*/
|
|
nByte = sizeof(SnippetPhrase) * nList;
|
|
- sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc64(nByte);
|
|
+ sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc(nByte);
|
|
if( !sIter.aPhrase ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -177228,7 +178244,7 @@ static int fts3MatchinfoLcs(Fts3Cursor *
|
|
/* Allocate and populate the array of LcsIterator objects. The array
|
|
** contains one element for each matchable phrase in the query.
|
|
**/
|
|
- aIter = sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase);
|
|
+ aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase);
|
|
if( !aIter ) return SQLITE_NOMEM;
|
|
memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
|
|
(void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
|
|
@@ -177681,7 +178697,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
|
|
if( rc!=SQLITE_OK ) goto offsets_out;
|
|
|
|
/* Allocate the array of TermOffset iterators. */
|
|
- sCtx.aTerm = (TermOffset *)sqlite3_malloc64(sizeof(TermOffset)*nToken);
|
|
+ sCtx.aTerm = (TermOffset *)sqlite3_malloc(sizeof(TermOffset)*nToken);
|
|
if( 0==sCtx.aTerm ){
|
|
rc = SQLITE_NOMEM;
|
|
goto offsets_out;
|
|
@@ -178267,84 +179283,105 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsal
|
|
0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
|
|
0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
|
|
0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
|
|
- 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
|
|
- 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
|
|
- 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
|
|
- 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
|
|
- 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
|
|
+ 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163403,
|
|
+ 0x00164437, 0x0017CC02, 0x0018001D, 0x00187802, 0x00192C15,
|
|
+ 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, 0x001B9C07,
|
|
+ 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, 0x001CC01B,
|
|
+ 0x001E980B, 0x001FAC09, 0x001FD804, 0x001FF403, 0x00205804,
|
|
0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
|
|
- 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
|
|
- 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
|
|
- 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
|
|
- 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
|
|
- 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
|
|
+ 0x00217801, 0x00234C31, 0x0024E803, 0x0024F812, 0x00254407,
|
|
+ 0x00258804, 0x0025C001, 0x00260403, 0x0026F001, 0x0026F807,
|
|
+ 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802, 0x0027C802,
|
|
+ 0x0027E802, 0x0027F402, 0x00280403, 0x0028F001, 0x0028F805,
|
|
+ 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D402,
|
|
0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
|
|
- 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
|
|
- 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
|
|
- 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
|
|
- 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
|
|
- 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
|
|
- 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
|
|
- 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
|
|
- 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
|
|
- 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
|
|
- 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
|
|
- 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
|
|
- 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
|
|
- 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
|
|
- 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
|
|
- 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
|
|
- 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
|
|
- 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
|
|
- 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
|
|
- 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
|
|
- 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
|
|
- 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
|
|
- 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
|
|
- 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
|
|
- 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
|
|
- 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
|
|
- 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
|
|
- 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
|
|
- 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
|
|
- 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
|
|
- 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
|
|
- 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
|
|
- 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
|
|
- 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
|
|
- 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
|
|
- 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
|
|
- 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
|
|
- 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
|
|
- 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
|
|
- 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
|
|
- 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
|
|
- 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
|
|
- 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
|
|
- 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
|
|
- 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
|
|
- 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
|
|
- 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
|
|
- 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
|
|
- 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
|
|
- 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
|
|
- 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
|
|
- 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
|
|
- 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
|
|
- 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
|
|
- 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
|
|
- 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
|
|
- 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
|
|
- 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
|
|
- 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
|
|
- 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
|
|
- 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
|
|
- 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
|
|
- 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
|
|
- 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
|
|
- 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
|
|
- 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
|
|
- 0x380400F0,
|
|
+ 0x002B8802, 0x002BC002, 0x002BE806, 0x002C0403, 0x002CF001,
|
|
+ 0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802,
|
|
+ 0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804,
|
|
+ 0x002F5C01, 0x002FCC08, 0x00300005, 0x0030F807, 0x00311803,
|
|
+ 0x00312804, 0x00315402, 0x00318802, 0x0031DC01, 0x0031FC01,
|
|
+ 0x00320404, 0x0032F001, 0x0032F807, 0x00331803, 0x00332804,
|
|
+ 0x00335402, 0x00338802, 0x00340004, 0x0034EC02, 0x0034F807,
|
|
+ 0x00351803, 0x00352804, 0x00353C01, 0x00355C01, 0x00358802,
|
|
+ 0x0035E401, 0x00360802, 0x00372801, 0x00373C06, 0x00375801,
|
|
+ 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01,
|
|
+ 0x00391C09, 0x00396802, 0x003AC401, 0x003AD009, 0x003B2006,
|
|
+ 0x003C041F, 0x003CD00C, 0x003DC417, 0x003E340B, 0x003E6424,
|
|
+ 0x003EF80F, 0x003F380D, 0x0040AC14, 0x00412806, 0x00415804,
|
|
+ 0x00417803, 0x00418803, 0x00419C07, 0x0041C404, 0x0042080C,
|
|
+ 0x00423C01, 0x00426806, 0x0043EC01, 0x004D740C, 0x004E400A,
|
|
+ 0x00500001, 0x0059B402, 0x005A0001, 0x005A6C02, 0x005BAC03,
|
|
+ 0x005C4803, 0x005CC805, 0x005D4802, 0x005DC802, 0x005ED023,
|
|
+ 0x005F6004, 0x005F7401, 0x0060000F, 0x00621402, 0x0062A401,
|
|
+ 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, 0x00677822,
|
|
+ 0x00685C05, 0x00687802, 0x0069540A, 0x0069801D, 0x0069FC01,
|
|
+ 0x006A8007, 0x006AA006, 0x006AC00F, 0x006C0005, 0x006CD011,
|
|
+ 0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E, 0x006FF004,
|
|
+ 0x00709014, 0x0070EC05, 0x0071F802, 0x00730008, 0x00734019,
|
|
+ 0x0073B401, 0x0073D001, 0x0073DC03, 0x0077003A, 0x0077EC05,
|
|
+ 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, 0x007FB403,
|
|
+ 0x007FF402, 0x00800065, 0x0081980A, 0x0081E805, 0x00822805,
|
|
+ 0x00828020, 0x00834021, 0x00840002, 0x00840C04, 0x00842002,
|
|
+ 0x00845001, 0x00845803, 0x00847806, 0x00849401, 0x00849C01,
|
|
+ 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, 0x00852804,
|
|
+ 0x00853C01, 0x00862802, 0x00864297, 0x0091000B, 0x0092704E,
|
|
+ 0x00940276, 0x009E53E0, 0x00ADD820, 0x00AE6068, 0x00B39406,
|
|
+ 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, 0x00B5FC01,
|
|
+ 0x00B7804F, 0x00B8C020, 0x00BA001A, 0x00BA6C59, 0x00BC00D6,
|
|
+ 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, 0x00C0D802,
|
|
+ 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, 0x00C64002,
|
|
+ 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, 0x00C94001,
|
|
+ 0x00C98020, 0x00CA2827, 0x00CB0140, 0x01370040, 0x02924037,
|
|
+ 0x0293F802, 0x02983403, 0x0299BC10, 0x029A7802, 0x029BC008,
|
|
+ 0x029C0017, 0x029C8002, 0x029E2402, 0x02A00801, 0x02A01801,
|
|
+ 0x02A02C01, 0x02A08C09, 0x02A0D804, 0x02A1D004, 0x02A20002,
|
|
+ 0x02A2D012, 0x02A33802, 0x02A38012, 0x02A3E003, 0x02A3F001,
|
|
+ 0x02A3FC01, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
|
|
+ 0x02A6CC1B, 0x02A77802, 0x02A79401, 0x02A8A40E, 0x02A90C01,
|
|
+ 0x02A93002, 0x02A97004, 0x02A9DC03, 0x02A9EC03, 0x02AAC001,
|
|
+ 0x02AAC803, 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802,
|
|
+ 0x02ABAC07, 0x02ABD402, 0x02AD6C01, 0x02AF8C0B, 0x03600001,
|
|
+ 0x036DFC02, 0x036FFC02, 0x037FFC01, 0x03EC7801, 0x03ECA401,
|
|
+ 0x03EEC810, 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88033,
|
|
+ 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
|
|
+ 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
|
|
+ 0x04040003, 0x0404DC09, 0x0405E411, 0x04063003, 0x0406400C,
|
|
+ 0x04068001, 0x0407402E, 0x040B8001, 0x040DD805, 0x040E7C01,
|
|
+ 0x040F4001, 0x0415BC01, 0x04215C01, 0x0421DC02, 0x04247C01,
|
|
+ 0x0424FC01, 0x04280403, 0x04281402, 0x04283004, 0x0428E003,
|
|
+ 0x0428FC01, 0x04294009, 0x0429FC01, 0x042B2001, 0x042B9402,
|
|
+ 0x042BC007, 0x042CE407, 0x042E6404, 0x04349004, 0x043D180B,
|
|
+ 0x043D5405, 0x04400003, 0x0440E016, 0x0441FC04, 0x0442C012,
|
|
+ 0x04433401, 0x04440003, 0x04449C0E, 0x04450004, 0x04451402,
|
|
+ 0x0445CC03, 0x04460003, 0x0446CC0E, 0x04471409, 0x04476C01,
|
|
+ 0x04477403, 0x0448B013, 0x044AA401, 0x044B7C0C, 0x044C0004,
|
|
+ 0x044CEC02, 0x044CF807, 0x044D1C02, 0x044D2C03, 0x044D5C01,
|
|
+ 0x044D8802, 0x044D9807, 0x044DC005, 0x0450D412, 0x04512C05,
|
|
+ 0x04516C01, 0x04517402, 0x0452C014, 0x04531801, 0x0456BC07,
|
|
+ 0x0456E020, 0x04577002, 0x0458C014, 0x0459800D, 0x045AAC0D,
|
|
+ 0x045C740F, 0x045CF004, 0x0460B010, 0x04674407, 0x04676807,
|
|
+ 0x04678801, 0x04679001, 0x0468040A, 0x0468CC07, 0x0468EC0D,
|
|
+ 0x0469440B, 0x046A2813, 0x046A7805, 0x0470BC08, 0x0470E008,
|
|
+ 0x04710405, 0x0471C002, 0x04724816, 0x0472A40E, 0x0474C406,
|
|
+ 0x0474E801, 0x0474F002, 0x0474FC07, 0x04751C01, 0x04762805,
|
|
+ 0x04764002, 0x04764C05, 0x047BCC06, 0x047F541D, 0x047FFC01,
|
|
+ 0x0491C005, 0x04D0C009, 0x05A9B802, 0x05ABC006, 0x05ACC010,
|
|
+ 0x05AD1002, 0x05BA5C04, 0x05BD3C01, 0x05BD4437, 0x05BE3C04,
|
|
+ 0x05BF8801, 0x06F27008, 0x074000F6, 0x07440027, 0x0744A4C0,
|
|
+ 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
|
|
+ 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
|
|
+ 0x075EA401, 0x075F0C01, 0x0760028C, 0x076A6C05, 0x076A840F,
|
|
+ 0x07800007, 0x07802011, 0x07806C07, 0x07808C02, 0x07809805,
|
|
+ 0x0784C007, 0x07853C01, 0x078BB004, 0x078BFC01, 0x07A34007,
|
|
+ 0x07A51007, 0x07A57802, 0x07B2B001, 0x07B2C001, 0x07B4B801,
|
|
+ 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F, 0x07C2C40F,
|
|
+ 0x07C3040F, 0x07C34425, 0x07C4405D, 0x07C5C03D, 0x07C7981D,
|
|
+ 0x07C8402C, 0x07C90009, 0x07C94002, 0x07C98006, 0x07CC03D6,
|
|
+ 0x07DB800D, 0x07DBC00B, 0x07DC0074, 0x07DE0059, 0x07DF800C,
|
|
+ 0x07E0000C, 0x07E04038, 0x07E1400A, 0x07E18028, 0x07E2401E,
|
|
+ 0x07E4000C, 0x07E43465, 0x07E5CC04, 0x07E5E829, 0x07E69406,
|
|
+ 0x07E6B81D, 0x07E73487, 0x07E9800E, 0x07E9C004, 0x07E9E003,
|
|
+ 0x07EA0003, 0x07EA4006, 0x38000401, 0x38008060, 0x380400F0,
|
|
};
|
|
static const unsigned int aAscii[4] = {
|
|
0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
|
|
@@ -178373,7 +179410,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsal
|
|
return 1;
|
|
}
|
|
|
|
-
|
|
+#if !defined(SQLITE_AMALGAMATION) || !defined(SQLITE_ENABLE_FTS5)
|
|
/*
|
|
** If the argument is a codepoint corresponding to a lowercase letter
|
|
** in the ASCII range with a diacritic added, return the codepoint
|
|
@@ -178383,7 +179420,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsal
|
|
** uppercase letter are undefined.
|
|
*/
|
|
static int remove_diacritic(int c, int bComplex){
|
|
- unsigned short aDia[] = {
|
|
+ static const unsigned short aDia[] = {
|
|
0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
|
|
2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
|
|
2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
|
|
@@ -178402,7 +179439,7 @@ static int remove_diacritic(int c, int b
|
|
63182, 63242, 63274, 63310, 63368, 63390,
|
|
};
|
|
#define HIBIT ((unsigned char)0x80)
|
|
- unsigned char aChar[] = {
|
|
+ static const unsigned char aChar[] = {
|
|
'\0', 'a', 'c', 'e', 'i', 'n',
|
|
'o', 'u', 'y', 'y', 'a', 'c',
|
|
'd', 'e', 'e', 'g', 'h', 'i',
|
|
@@ -178491,73 +179528,84 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
|
unsigned char flags;
|
|
unsigned char nRange;
|
|
} aEntry[] = {
|
|
- {65, 14, 26}, {181, 64, 1}, {192, 14, 23},
|
|
+ {65, 14, 26}, {181, 66, 1}, {192, 14, 23},
|
|
{216, 14, 7}, {256, 1, 48}, {306, 1, 6},
|
|
- {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
|
|
- {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
|
|
- {386, 1, 4}, {390, 44, 1}, {391, 0, 1},
|
|
- {393, 42, 2}, {395, 0, 1}, {398, 32, 1},
|
|
- {399, 38, 1}, {400, 40, 1}, {401, 0, 1},
|
|
- {403, 42, 1}, {404, 46, 1}, {406, 52, 1},
|
|
- {407, 48, 1}, {408, 0, 1}, {412, 52, 1},
|
|
- {413, 54, 1}, {415, 56, 1}, {416, 1, 6},
|
|
- {422, 60, 1}, {423, 0, 1}, {425, 60, 1},
|
|
- {428, 0, 1}, {430, 60, 1}, {431, 0, 1},
|
|
- {433, 58, 2}, {435, 1, 4}, {439, 62, 1},
|
|
+ {313, 1, 16}, {330, 1, 46}, {376, 156, 1},
|
|
+ {377, 1, 6}, {383, 144, 1}, {385, 52, 1},
|
|
+ {386, 1, 4}, {390, 46, 1}, {391, 0, 1},
|
|
+ {393, 44, 2}, {395, 0, 1}, {398, 34, 1},
|
|
+ {399, 40, 1}, {400, 42, 1}, {401, 0, 1},
|
|
+ {403, 44, 1}, {404, 48, 1}, {406, 54, 1},
|
|
+ {407, 50, 1}, {408, 0, 1}, {412, 54, 1},
|
|
+ {413, 56, 1}, {415, 58, 1}, {416, 1, 6},
|
|
+ {422, 62, 1}, {423, 0, 1}, {425, 62, 1},
|
|
+ {428, 0, 1}, {430, 62, 1}, {431, 0, 1},
|
|
+ {433, 60, 2}, {435, 1, 4}, {439, 64, 1},
|
|
{440, 0, 1}, {444, 0, 1}, {452, 2, 1},
|
|
{453, 0, 1}, {455, 2, 1}, {456, 0, 1},
|
|
{458, 2, 1}, {459, 1, 18}, {478, 1, 18},
|
|
- {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
|
|
- {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
|
|
- {546, 1, 18}, {570, 70, 1}, {571, 0, 1},
|
|
- {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
|
|
- {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
|
|
- {582, 1, 10}, {837, 36, 1}, {880, 1, 4},
|
|
- {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
|
|
- {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
|
|
- {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
|
|
- {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
|
|
- {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
|
|
- {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
|
|
- {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
|
|
- {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
|
|
- {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
|
|
- {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
|
|
- {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
|
|
- {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
|
|
- {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
|
|
- {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
|
|
- {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
|
|
- {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
|
|
- {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
|
|
- {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
|
|
- {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
|
|
- {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
|
|
- {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
|
|
- {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
|
|
- {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
|
|
- {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
|
|
- {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
|
|
- {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
|
|
- {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
|
|
+ {497, 2, 1}, {498, 1, 4}, {502, 162, 1},
|
|
+ {503, 174, 1}, {504, 1, 40}, {544, 150, 1},
|
|
+ {546, 1, 18}, {570, 74, 1}, {571, 0, 1},
|
|
+ {573, 148, 1}, {574, 72, 1}, {577, 0, 1},
|
|
+ {579, 146, 1}, {580, 30, 1}, {581, 32, 1},
|
|
+ {582, 1, 10}, {837, 38, 1}, {880, 1, 4},
|
|
+ {886, 0, 1}, {895, 38, 1}, {902, 20, 1},
|
|
+ {904, 18, 3}, {908, 28, 1}, {910, 26, 2},
|
|
+ {913, 14, 17}, {931, 14, 9}, {962, 0, 1},
|
|
+ {975, 4, 1}, {976, 180, 1}, {977, 182, 1},
|
|
+ {981, 186, 1}, {982, 184, 1}, {984, 1, 24},
|
|
+ {1008, 176, 1}, {1009, 178, 1}, {1012, 170, 1},
|
|
+ {1013, 168, 1}, {1015, 0, 1}, {1017, 192, 1},
|
|
+ {1018, 0, 1}, {1021, 150, 3}, {1024, 36, 16},
|
|
+ {1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54},
|
|
+ {1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96},
|
|
+ {1329, 24, 38}, {4256, 70, 38}, {4295, 70, 1},
|
|
+ {4301, 70, 1}, {5112, 190, 6}, {7296, 126, 1},
|
|
+ {7297, 128, 1}, {7298, 130, 1}, {7299, 134, 2},
|
|
+ {7301, 132, 1}, {7302, 136, 1}, {7303, 138, 1},
|
|
+ {7304, 100, 1}, {7312, 142, 43}, {7357, 142, 3},
|
|
+ {7680, 1, 150}, {7835, 172, 1}, {7838, 120, 1},
|
|
+ {7840, 1, 96}, {7944, 190, 8}, {7960, 190, 6},
|
|
+ {7976, 190, 8}, {7992, 190, 8}, {8008, 190, 6},
|
|
+ {8025, 191, 8}, {8040, 190, 8}, {8072, 190, 8},
|
|
+ {8088, 190, 8}, {8104, 190, 8}, {8120, 190, 2},
|
|
+ {8122, 166, 2}, {8124, 188, 1}, {8126, 124, 1},
|
|
+ {8136, 164, 4}, {8140, 188, 1}, {8152, 190, 2},
|
|
+ {8154, 160, 2}, {8168, 190, 2}, {8170, 158, 2},
|
|
+ {8172, 192, 1}, {8184, 152, 2}, {8186, 154, 2},
|
|
+ {8188, 188, 1}, {8486, 122, 1}, {8490, 116, 1},
|
|
+ {8491, 118, 1}, {8498, 12, 1}, {8544, 8, 16},
|
|
+ {8579, 0, 1}, {9398, 10, 26}, {11264, 24, 47},
|
|
+ {11360, 0, 1}, {11362, 112, 1}, {11363, 140, 1},
|
|
+ {11364, 114, 1}, {11367, 1, 6}, {11373, 108, 1},
|
|
+ {11374, 110, 1}, {11375, 104, 1}, {11376, 106, 1},
|
|
+ {11378, 0, 1}, {11381, 0, 1}, {11390, 102, 2},
|
|
{11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
|
|
- {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
|
|
- {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
|
|
- {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
|
|
- {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
|
|
- {65313, 14, 26},
|
|
+ {42560, 1, 46}, {42624, 1, 28}, {42786, 1, 14},
|
|
+ {42802, 1, 62}, {42873, 1, 4}, {42877, 98, 1},
|
|
+ {42878, 1, 10}, {42891, 0, 1}, {42893, 88, 1},
|
|
+ {42896, 1, 4}, {42902, 1, 20}, {42922, 80, 1},
|
|
+ {42923, 76, 1}, {42924, 78, 1}, {42925, 84, 1},
|
|
+ {42926, 80, 1}, {42928, 92, 1}, {42929, 86, 1},
|
|
+ {42930, 90, 1}, {42931, 68, 1}, {42932, 1, 12},
|
|
+ {42946, 0, 1}, {42948, 178, 1}, {42949, 82, 1},
|
|
+ {42950, 96, 1}, {43888, 94, 80}, {65313, 14, 26},
|
|
};
|
|
static const unsigned short aiOff[] = {
|
|
1, 2, 8, 15, 16, 26, 28, 32,
|
|
- 37, 38, 40, 48, 63, 64, 69, 71,
|
|
- 79, 80, 116, 202, 203, 205, 206, 207,
|
|
- 209, 210, 211, 213, 214, 217, 218, 219,
|
|
- 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
|
|
- 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
|
|
- 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
|
|
- 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
|
|
- 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
|
|
- 65514, 65521, 65527, 65528, 65529,
|
|
+ 34, 37, 38, 40, 48, 63, 64, 69,
|
|
+ 71, 79, 80, 116, 202, 203, 205, 206,
|
|
+ 207, 209, 210, 211, 213, 214, 217, 218,
|
|
+ 219, 775, 928, 7264, 10792, 10795, 23217, 23221,
|
|
+ 23228, 23229, 23231, 23254, 23256, 23275, 23278, 26672,
|
|
+ 30152, 30204, 35267, 54721, 54753, 54754, 54756, 54787,
|
|
+ 54793, 54809, 57153, 57274, 57921, 58019, 58363, 59314,
|
|
+ 59315, 59324, 59325, 59326, 59332, 59356, 61722, 62528,
|
|
+ 65268, 65341, 65373, 65406, 65408, 65410, 65415, 65424,
|
|
+ 65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480,
|
|
+ 65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528,
|
|
+ 65529,
|
|
};
|
|
|
|
int ret = c;
|
|
@@ -178599,9 +179647,25 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
|
else if( c>=66560 && c<66600 ){
|
|
ret = c + 40;
|
|
}
|
|
+ else if( c>=66736 && c<66772 ){
|
|
+ ret = c + 40;
|
|
+ }
|
|
+ else if( c>=68736 && c<68787 ){
|
|
+ ret = c + 64;
|
|
+ }
|
|
+ else if( c>=71840 && c<71872 ){
|
|
+ ret = c + 32;
|
|
+ }
|
|
+ else if( c>=93760 && c<93792 ){
|
|
+ ret = c + 32;
|
|
+ }
|
|
+ else if( c>=125184 && c<125218 ){
|
|
+ ret = c + 34;
|
|
+ }
|
|
|
|
return ret;
|
|
}
|
|
+#endif /* !defined(SQLITE_AMALGAMATION) || !defined(SQLITE_ENABLE_FTS5) */
|
|
#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */
|
|
#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
|
|
|
|
@@ -178697,7 +179761,6 @@ static const char jsonIsSpace[] = {
|
|
#ifndef SQLITE_AMALGAMATION
|
|
/* Unsigned integer types. These are already defined in the sqliteInt.h,
|
|
** but the definitions need to be repeated for separate compilation. */
|
|
- typedef sqlite3_uint64 u64;
|
|
typedef unsigned int u32;
|
|
typedef unsigned short int u16;
|
|
typedef unsigned char u8;
|
|
@@ -178715,8 +179778,8 @@ typedef struct JsonParse JsonParse;
|
|
struct JsonString {
|
|
sqlite3_context *pCtx; /* Function context - put error messages here */
|
|
char *zBuf; /* Append JSON content here */
|
|
- u64 nAlloc; /* Bytes of storage available in zBuf[] */
|
|
- u64 nUsed; /* Bytes of zBuf[] currently used */
|
|
+ size_t nAlloc; /* Bytes of storage available in zBuf[] */
|
|
+ size_t nUsed; /* Bytes of zBuf[] currently used */
|
|
u8 bStatic; /* True if zBuf is static space */
|
|
u8 bErr; /* True if an error has been encountered */
|
|
char zSpace[100]; /* Initial static space */
|
|
@@ -178739,7 +179802,7 @@ struct JsonString {
|
|
/*
|
|
** Names of the various JSON types:
|
|
*/
|
|
-static const char * const jsonType[] = {
|
|
+static const char jsonType[][8] = {
|
|
"null", "true", "false", "integer", "real", "text", "array", "object"
|
|
};
|
|
|
|
@@ -178836,11 +179899,11 @@ static void jsonOom(JsonString *p){
|
|
** Return zero on success. Return non-zero on an OOM error
|
|
*/
|
|
static int jsonGrow(JsonString *p, u32 N){
|
|
- u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
|
|
+ size_t nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
|
|
char *zNew;
|
|
if( p->bStatic ){
|
|
if( p->bErr ) return 1;
|
|
- zNew = sqlite3_malloc64(nTotal);
|
|
+ zNew = sqlite3_malloc(nTotal);
|
|
if( zNew==0 ){
|
|
jsonOom(p);
|
|
return SQLITE_NOMEM;
|
|
@@ -178849,7 +179912,7 @@ static int jsonGrow(JsonString *p, u32 N
|
|
p->zBuf = zNew;
|
|
p->bStatic = 0;
|
|
}else{
|
|
- zNew = sqlite3_realloc64(p->zBuf, nTotal);
|
|
+ zNew = sqlite3_realloc(p->zBuf, nTotal);
|
|
if( zNew==0 ){
|
|
jsonOom(p);
|
|
return SQLITE_NOMEM;
|
|
@@ -179574,7 +180637,7 @@ static void jsonParseFillInParentage(Jso
|
|
static int jsonParseFindParents(JsonParse *pParse){
|
|
u32 *aUp;
|
|
assert( pParse->aUp==0 );
|
|
- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
|
|
+ aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
|
|
if( aUp==0 ){
|
|
pParse->oom = 1;
|
|
return SQLITE_NOMEM;
|
|
@@ -179636,7 +180699,7 @@ static JsonParse *jsonParseCached(
|
|
pMatch->iHold = iMaxHold+1;
|
|
return pMatch;
|
|
}
|
|
- p = sqlite3_malloc64( sizeof(*p) + nJson + 1 );
|
|
+ p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
|
|
if( p==0 ){
|
|
sqlite3_result_error_nomem(pCtx);
|
|
return 0;
|
|
@@ -180925,7 +181988,7 @@ static int jsonEachFilter(
|
|
JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
const char *z;
|
|
const char *zRoot = 0;
|
|
- sqlite3_int64 n;
|
|
+ size_t n;
|
|
|
|
UNUSED_PARAM(idxStr);
|
|
UNUSED_PARAM(argc);
|
|
@@ -180933,10 +181996,10 @@ static int jsonEachFilter(
|
|
if( idxNum==0 ) return SQLITE_OK;
|
|
z = (const char*)sqlite3_value_text(argv[0]);
|
|
if( z==0 ) return SQLITE_OK;
|
|
- n = sqlite3_value_bytes(argv[0]);
|
|
- p->zJson = sqlite3_malloc64( n+1 );
|
|
+ n = (size_t)sqlite3_value_bytes(argv[0]);
|
|
+ p->zJson = sqlite3_malloc( n+1 );
|
|
if( p->zJson==0 ) return SQLITE_NOMEM;
|
|
- memcpy(p->zJson, z, (size_t)n+1);
|
|
+ memcpy(p->zJson, z, n+1);
|
|
if( jsonParse(&p->sParse, 0, p->zJson) ){
|
|
int rc = SQLITE_NOMEM;
|
|
if( p->sParse.oom==0 ){
|
|
@@ -180955,10 +182018,10 @@ static int jsonEachFilter(
|
|
const char *zErr = 0;
|
|
zRoot = (const char*)sqlite3_value_text(argv[1]);
|
|
if( zRoot==0 ) return SQLITE_OK;
|
|
- n = sqlite3_value_bytes(argv[1]);
|
|
- p->zRoot = sqlite3_malloc64( n+1 );
|
|
+ n = (size_t)sqlite3_value_bytes(argv[1]);
|
|
+ p->zRoot = sqlite3_malloc( n+1 );
|
|
if( p->zRoot==0 ) return SQLITE_NOMEM;
|
|
- memcpy(p->zRoot, zRoot, (size_t)n+1);
|
|
+ memcpy(p->zRoot, zRoot, n+1);
|
|
if( zRoot[0]!='$' ){
|
|
zErr = zRoot;
|
|
}else{
|
|
@@ -180996,7 +182059,7 @@ static int jsonEachFilter(
|
|
}
|
|
|
|
/* The methods of the json_each virtual table */
|
|
-static sqlite3_module jsonEachModule = {
|
|
+static const sqlite3_module jsonEachModule = {
|
|
0, /* iVersion */
|
|
0, /* xCreate */
|
|
jsonEachConnect, /* xConnect */
|
|
@@ -181024,7 +182087,7 @@ static sqlite3_module jsonEachModule = {
|
|
};
|
|
|
|
/* The methods of the json_tree virtual table. */
|
|
-static sqlite3_module jsonTreeModule = {
|
|
+static const sqlite3_module jsonTreeModule = {
|
|
0, /* iVersion */
|
|
0, /* xCreate */
|
|
jsonEachConnect, /* xConnect */
|
|
@@ -181104,7 +182167,7 @@ SQLITE_PRIVATE int sqlite3Json1Init(sqli
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
static const struct {
|
|
const char *zName;
|
|
- sqlite3_module *pModule;
|
|
+ const sqlite3_module *pModule;
|
|
} aMod[] = {
|
|
{ "json_each", &jsonEachModule },
|
|
{ "json_tree", &jsonTreeModule },
|
|
@@ -181532,11 +182595,11 @@ struct RtreeMatchArg {
|
|
RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
|
|
};
|
|
|
|
-#ifndef MAX
|
|
-# define MAX(x,y) ((x) < (y) ? (y) : (x))
|
|
-#endif
|
|
#ifndef MIN
|
|
-# define MIN(x,y) ((x) > (y) ? (y) : (x))
|
|
+# define MIN(A,B) ((A)<(B)?(A):(B))
|
|
+#endif
|
|
+#ifndef MAX
|
|
+# define MAX(A,B) ((A)>(B)?(A):(B))
|
|
#endif
|
|
|
|
/* What version of GCC is being used. 0 means GCC is not being used .
|
|
@@ -181764,7 +182827,7 @@ static void nodeHashDelete(Rtree *pRtree
|
|
*/
|
|
static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
|
|
RtreeNode *pNode;
|
|
- pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode) + pRtree->iNodeSize);
|
|
+ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize);
|
|
if( pNode ){
|
|
memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize);
|
|
pNode->zData = (u8 *)&pNode[1];
|
|
@@ -181857,7 +182920,7 @@ static int nodeAcquire(
|
|
RTREE_IS_CORRUPT(pRtree);
|
|
}
|
|
}else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){
|
|
- pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode)+pRtree->iNodeSize);
|
|
+ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize);
|
|
if( !pNode ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -182189,7 +183252,7 @@ static int rtreeOpen(sqlite3_vtab *pVTab
|
|
Rtree *pRtree = (Rtree *)pVTab;
|
|
RtreeCursor *pCsr;
|
|
|
|
- pCsr = (RtreeCursor *)sqlite3_malloc64(sizeof(RtreeCursor));
|
|
+ pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor));
|
|
if( pCsr ){
|
|
memset(pCsr, 0, sizeof(RtreeCursor));
|
|
pCsr->base.pVtab = pVTab;
|
|
@@ -182886,7 +183949,7 @@ static int deserializeGeometry(sqlite3_v
|
|
pSrc = sqlite3_value_pointer(pValue, "RtreeMatchArg");
|
|
if( pSrc==0 ) return SQLITE_ERROR;
|
|
pInfo = (sqlite3_rtree_query_info*)
|
|
- sqlite3_malloc64( sizeof(*pInfo)+pSrc->iSize );
|
|
+ sqlite3_malloc( sizeof(*pInfo)+pSrc->iSize );
|
|
if( !pInfo ) return SQLITE_NOMEM;
|
|
memset(pInfo, 0, sizeof(*pInfo));
|
|
pBlob = (RtreeMatchArg*)&pInfo[1];
|
|
@@ -182958,7 +184021,7 @@ static int rtreeFilter(
|
|
*/
|
|
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
|
if( rc==SQLITE_OK && argc>0 ){
|
|
- pCsr->aConstraint = sqlite3_malloc64(sizeof(RtreeConstraint)*argc);
|
|
+ pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc);
|
|
pCsr->nConstraint = argc;
|
|
if( !pCsr->aConstraint ){
|
|
rc = SQLITE_NOMEM;
|
|
@@ -183529,9 +184592,9 @@ static int splitNodeStartree(
|
|
int iBestSplit = 0;
|
|
RtreeDValue fBestMargin = RTREE_ZERO;
|
|
|
|
- sqlite3_int64 nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
|
|
+ size_t nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
|
|
|
|
- aaSorted = (int **)sqlite3_malloc64(nByte);
|
|
+ aaSorted = (int **)sqlite3_malloc(nByte);
|
|
if( !aaSorted ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -183652,7 +184715,7 @@ static int SplitNode(
|
|
/* Allocate an array and populate it with a copy of pCell and
|
|
** all cells from node pLeft. Then zero the original node.
|
|
*/
|
|
- aCell = sqlite3_malloc64((sizeof(RtreeCell)+sizeof(int))*(nCell+1));
|
|
+ aCell = sqlite3_malloc((sizeof(RtreeCell)+sizeof(int))*(nCell+1));
|
|
if( !aCell ){
|
|
rc = SQLITE_NOMEM;
|
|
goto splitnode_out;
|
|
@@ -183943,7 +185006,7 @@ static int Reinsert(
|
|
/* Allocate the buffers used by this operation. The allocation is
|
|
** relinquished before this function returns.
|
|
*/
|
|
- aCell = (RtreeCell *)sqlite3_malloc64(n * (
|
|
+ aCell = (RtreeCell *)sqlite3_malloc(n * (
|
|
sizeof(RtreeCell) + /* aCell array */
|
|
sizeof(int) + /* aOrder array */
|
|
sizeof(int) + /* aSpare array */
|
|
@@ -184538,7 +185601,7 @@ static int rtreeShadowName(const char *z
|
|
return 0;
|
|
}
|
|
|
|
-static sqlite3_module rtreeModule = {
|
|
+static const sqlite3_module rtreeModule = {
|
|
3, /* iVersion */
|
|
rtreeCreate, /* xCreate - create a table */
|
|
rtreeConnect, /* xConnect - connect to an existing table */
|
|
@@ -184575,7 +185638,7 @@ static int rtreeSqlInit(
|
|
int rc = SQLITE_OK;
|
|
|
|
#define N_STATEMENT 8
|
|
- static const char *azSql[N_STATEMENT] = {
|
|
+ static const char *const azSql[N_STATEMENT] = {
|
|
/* Write the xxx_node table */
|
|
"INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(?1, ?2)",
|
|
"DELETE FROM '%q'.'%q_node' WHERE nodeno = ?1",
|
|
@@ -184792,7 +185855,7 @@ static int rtreeInit(
|
|
int ii = 4;
|
|
int iErr;
|
|
|
|
- const char *aErrMsg[] = {
|
|
+ const char *const aErrMsg[] = {
|
|
0, /* 0 */
|
|
"Wrong number of columns for an rtree table", /* 1 */
|
|
"Too few columns for an rtree table", /* 2 */
|
|
@@ -184811,7 +185874,7 @@ static int rtreeInit(
|
|
/* Allocate the sqlite3_vtab structure */
|
|
nDb = (int)strlen(argv[1]);
|
|
nName = (int)strlen(argv[2]);
|
|
- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
|
|
+ pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
|
|
if( !pRtree ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -185085,9 +186148,9 @@ static u8 *rtreeCheckGetNode(RtreeCheck
|
|
if( pCheck->rc==SQLITE_OK ){
|
|
sqlite3_bind_int64(pCheck->pGetNode, 1, iNode);
|
|
if( sqlite3_step(pCheck->pGetNode)==SQLITE_ROW ){
|
|
- int nNode = sqlite3_column_bytes(pCheck->pGetNode, 0);
|
|
+ size_t nNode = sqlite3_column_bytes(pCheck->pGetNode, 0);
|
|
const u8 *pNode = (const u8*)sqlite3_column_blob(pCheck->pGetNode, 0);
|
|
- pRet = sqlite3_malloc64(nNode);
|
|
+ pRet = sqlite3_malloc(nNode);
|
|
if( pRet==0 ){
|
|
pCheck->rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -185711,7 +186774,7 @@ static GeoPoly *geopolyParseJson(const u
|
|
GeoPoly *pOut;
|
|
int x = 1;
|
|
s.nVertex--; /* Remove the redundant vertex at the end */
|
|
- pOut = sqlite3_malloc64( GEOPOLY_SZ((sqlite3_int64)s.nVertex) );
|
|
+ pOut = sqlite3_malloc( GEOPOLY_SZ((size_t)s.nVertex) );
|
|
x = 1;
|
|
if( pOut==0 ) goto parse_json_err;
|
|
pOut->nVertex = s.nVertex;
|
|
@@ -185756,7 +186819,7 @@ static GeoPoly *geopolyFuncParam(
|
|
if( (a[0]==0 || a[0]==1)
|
|
&& (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte
|
|
){
|
|
- p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
|
|
+ p = sqlite3_malloc( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
|
|
if( p==0 ){
|
|
if( pRc ) *pRc = SQLITE_NOMEM;
|
|
if( pCtx ) sqlite3_result_error_nomem(pCtx);
|
|
@@ -186034,7 +187097,7 @@ static void geopolyRegularFunc(
|
|
|
|
if( n<3 || r<=0.0 ) return;
|
|
if( n>1000 ) n = 1000;
|
|
- p = sqlite3_malloc64( sizeof(*p) + (n-1)*2*sizeof(GeoCoord) );
|
|
+ p = sqlite3_malloc( sizeof(*p) + (n-1)*2*sizeof(GeoCoord) );
|
|
if( p==0 ){
|
|
sqlite3_result_error_nomem(context);
|
|
return;
|
|
@@ -187332,12 +188395,12 @@ static void rtreeMatchArgFree(void *pArg
|
|
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
|
|
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
|
|
RtreeMatchArg *pBlob;
|
|
- sqlite3_int64 nBlob;
|
|
+ size_t nBlob;
|
|
int memErr = 0;
|
|
|
|
nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
|
|
+ nArg*sizeof(sqlite3_value*);
|
|
- pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
|
|
+ pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob);
|
|
if( !pBlob ){
|
|
sqlite3_result_error_nomem(ctx);
|
|
}else{
|
|
@@ -187661,7 +188724,7 @@ static void icuLikeFunc(
|
|
/* The escape character string must consist of a single UTF-8 character.
|
|
** Otherwise, return an error.
|
|
*/
|
|
- int nE= sqlite3_value_bytes(argv[2]);
|
|
+ size_t nE= sqlite3_value_bytes(argv[2]);
|
|
const unsigned char *zE = sqlite3_value_text(argv[2]);
|
|
int i = 0;
|
|
if( zE==0 ) return;
|
|
@@ -187792,8 +188855,8 @@ static void icuRegexpFunc(sqlite3_contex
|
|
static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
|
const UChar *zInput; /* Pointer to input string */
|
|
UChar *zOutput = 0; /* Pointer to output buffer */
|
|
- int nInput; /* Size of utf-16 input string in bytes */
|
|
- int nOut; /* Size of output buffer in bytes */
|
|
+ size_t nInput; /* Size of utf-16 input string in bytes */
|
|
+ size_t nOut; /* Size of output buffer in bytes */
|
|
int cnt;
|
|
int bToUpper; /* True for toupper(), false for tolower() */
|
|
UErrorCode status;
|
|
@@ -187958,10 +189021,10 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite
|
|
|
|
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
|
const struct IcuScalar *p = &scalars[i];
|
|
- rc = sqlite3_create_function(
|
|
+ rc = sqlite3_create_function_v2(
|
|
db, p->zName, p->nArg, p->enc,
|
|
p->iContext ? (void*)db : (void*)0,
|
|
- p->xFunc, 0, 0
|
|
+ p->xFunc, 0, 0, 0
|
|
);
|
|
}
|
|
|
|
@@ -187980,6 +189043,16 @@ SQLITE_API int sqlite3_icu_init(
|
|
SQLITE_EXTENSION_INIT2(pApi)
|
|
return sqlite3IcuInit(db);
|
|
}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+SQLITE_API int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi)
|
|
+ return sqlite3IcuInit(db);
|
|
+}
|
|
+#endif
|
|
#endif
|
|
|
|
#endif
|
|
@@ -188048,7 +189121,7 @@ static int icuCreate(
|
|
if( argc>0 ){
|
|
n = strlen(argv[0])+1;
|
|
}
|
|
- p = (IcuTokenizer *)sqlite3_malloc64(sizeof(IcuTokenizer)+n);
|
|
+ p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n);
|
|
if( !p ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -188105,7 +189178,7 @@ static int icuOpen(
|
|
nInput = strlen(zInput);
|
|
}
|
|
nChar = nInput+1;
|
|
- pCsr = (IcuCursor *)sqlite3_malloc64(
|
|
+ pCsr = (IcuCursor *)sqlite3_malloc(
|
|
sizeof(IcuCursor) + /* IcuCursor */
|
|
((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */
|
|
(nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
|
|
@@ -189906,7 +190979,7 @@ static void *rbuMalloc(sqlite3rbu *p, sq
|
|
void *pRet = 0;
|
|
if( p->rc==SQLITE_OK ){
|
|
assert( nByte>0 );
|
|
- pRet = sqlite3_malloc64(nByte);
|
|
+ pRet = sqlite3_malloc(nByte);
|
|
if( pRet==0 ){
|
|
p->rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -189953,7 +191026,7 @@ static char *rbuStrndup(const char *zStr
|
|
assert( *pRc==SQLITE_OK );
|
|
if( zStr ){
|
|
size_t nCopy = strlen(zStr) + 1;
|
|
- zRet = (char*)sqlite3_malloc64(nCopy);
|
|
+ zRet = (char*)sqlite3_malloc(nCopy);
|
|
if( zRet ){
|
|
memcpy(zRet, zStr, nCopy);
|
|
}else{
|
|
@@ -191627,7 +192700,7 @@ static int rbuCaptureWalRead(sqlite3rbu
|
|
if( pRbu->nFrame==pRbu->nFrameAlloc ){
|
|
int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2;
|
|
RbuFrame *aNew;
|
|
- aNew = (RbuFrame*)sqlite3_realloc64(pRbu->aFrame, nNew * sizeof(RbuFrame));
|
|
+ aNew = (RbuFrame*)sqlite3_realloc(pRbu->aFrame, nNew * sizeof(RbuFrame));
|
|
if( aNew==0 ) return SQLITE_NOMEM;
|
|
pRbu->aFrame = aNew;
|
|
pRbu->nFrameAlloc = nNew;
|
|
@@ -191692,7 +192765,7 @@ static LPWSTR rbuWinUtf8ToUnicode(const
|
|
if( nChar==0 ){
|
|
return 0;
|
|
}
|
|
- zWideFilename = sqlite3_malloc64( nChar*sizeof(zWideFilename[0]) );
|
|
+ zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) );
|
|
if( zWideFilename==0 ){
|
|
return 0;
|
|
}
|
|
@@ -192499,7 +193572,7 @@ static sqlite3rbu *openRbuHandle(
|
|
size_t nRbu = strlen(zRbu);
|
|
size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;
|
|
|
|
- p = (sqlite3rbu*)sqlite3_malloc64(nByte);
|
|
+ p = (sqlite3rbu*)sqlite3_malloc(nByte);
|
|
if( p ){
|
|
RbuState *pState = 0;
|
|
|
|
@@ -192647,7 +193720,7 @@ static sqlite3rbu *openRbuHandle(
|
|
*/
|
|
static sqlite3rbu *rbuMisuseError(void){
|
|
sqlite3rbu *pRet;
|
|
- pRet = sqlite3_malloc64(sizeof(sqlite3rbu));
|
|
+ pRet = sqlite3_malloc(sizeof(sqlite3rbu));
|
|
if( pRet ){
|
|
memset(pRet, 0, sizeof(sqlite3rbu));
|
|
pRet->rc = SQLITE_MISUSE;
|
|
@@ -193423,8 +194496,8 @@ static int rbuVfsShmMap(
|
|
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
|
if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
|
|
if( iRegion<=p->nShm ){
|
|
- sqlite3_int64 nByte = (iRegion+1) * sizeof(char*);
|
|
- char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
|
|
+ size_t nByte = (iRegion+1) * sizeof(char*);
|
|
+ char **apNew = (char**)sqlite3_realloc(p->apShm, nByte);
|
|
if( apNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -193435,7 +194508,7 @@ static int rbuVfsShmMap(
|
|
}
|
|
|
|
if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
|
|
- char *pNew = (char*)sqlite3_malloc64(szRegion);
|
|
+ char *pNew = (char*)sqlite3_malloc(szRegion);
|
|
if( pNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -193577,7 +194650,7 @@ static int rbuVfsOpen(
|
|
zBase = rbuMainToWal(zBase, SQLITE_OPEN_URI);
|
|
}
|
|
nCopy = strlen(zBase);
|
|
- zCopy = sqlite3_malloc64(nCopy+2);
|
|
+ zCopy = sqlite3_malloc(nCopy+2);
|
|
if( zCopy ){
|
|
memcpy(zCopy, zBase, nCopy);
|
|
zCopy[nCopy-3] = 'o';
|
|
@@ -193822,7 +194895,7 @@ SQLITE_API int sqlite3rbu_create_vfs(con
|
|
|
|
nName = strlen(zName);
|
|
nByte = sizeof(rbu_vfs) + nName + 1;
|
|
- pNew = (rbu_vfs*)sqlite3_malloc64(nByte);
|
|
+ pNew = (rbu_vfs*)sqlite3_malloc(nByte);
|
|
if( pNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -193996,7 +195069,7 @@ struct StatCursor {
|
|
char *zName; /* Value of 'name' column */
|
|
char *zPath; /* Value of 'path' column */
|
|
u32 iPageno; /* Value of 'pageno' column */
|
|
- char *zPagetype; /* Value of 'pagetype' column */
|
|
+ const char *zPagetype; /* Value of 'pagetype' column */
|
|
int nCell; /* Value of 'ncell' column */
|
|
int nPayload; /* Value of 'payload' column */
|
|
int nUnused; /* Value of 'unused' column */
|
|
@@ -194042,7 +195115,7 @@ static int statConnect(
|
|
}
|
|
rc = sqlite3_declare_vtab(db, VTAB_SCHEMA);
|
|
if( rc==SQLITE_OK ){
|
|
- pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable));
|
|
+ pTab = (StatTable *)sqlite3Malloc(sizeof(StatTable));
|
|
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
|
|
}
|
|
|
|
@@ -194120,7 +195193,7 @@ static int statOpen(sqlite3_vtab *pVTab,
|
|
StatTable *pTab = (StatTable *)pVTab;
|
|
StatCursor *pCsr;
|
|
|
|
- pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor));
|
|
+ pCsr = (StatCursor *)sqlite3Malloc(sizeof(StatCursor));
|
|
if( pCsr==0 ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
@@ -194244,7 +195317,7 @@ static int statDecodePage(Btree *pBt, St
|
|
sqlite3BtreeEnter(pBt);
|
|
nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt);
|
|
sqlite3BtreeLeave(pBt);
|
|
- p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell));
|
|
+ p->aCell = sqlite3Malloc((p->nCell+1) * sizeof(StatCell));
|
|
if( p->aCell==0 ) return SQLITE_NOMEM_BKPT;
|
|
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
|
|
|
|
@@ -194279,7 +195352,7 @@ static int statDecodePage(Btree *pBt, St
|
|
if( iOff+nLocal>nUsable ) goto statPageIsCorrupt;
|
|
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
|
|
pCell->nOvfl = nOvfl;
|
|
- pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
|
|
+ pCell->aOvfl = sqlite3Malloc(sizeof(u32)*nOvfl);
|
|
if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT;
|
|
pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]);
|
|
for(j=1; j<nOvfl; j++){
|
|
@@ -194578,7 +195651,7 @@ static int statRowid(sqlite3_vtab_cursor
|
|
** Invoke this routine to register the "dbstat" virtual table module
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
|
|
- static sqlite3_module dbstat_module = {
|
|
+ static const sqlite3_module dbstat_module = {
|
|
0, /* iVersion */
|
|
statConnect, /* xCreate */
|
|
statConnect, /* xConnect */
|
|
@@ -194604,7 +195677,7 @@ SQLITE_PRIVATE int sqlite3DbstatRegister
|
|
0, /* xRollbackTo */
|
|
0 /* xShadowName */
|
|
};
|
|
- return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
|
|
+ return sqlite3_create_module_v2(db, "dbstat", &dbstat_module, 0, 0);
|
|
}
|
|
#elif defined(SQLITE_ENABLE_DBSTAT_VTAB)
|
|
SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; }
|
|
@@ -194996,7 +196069,7 @@ static int dbpageBegin(sqlite3_vtab *pVt
|
|
** Invoke this routine to register the "dbpage" virtual table module
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
|
|
- static sqlite3_module dbpage_module = {
|
|
+ static const sqlite3_module dbpage_module = {
|
|
0, /* iVersion */
|
|
dbpageConnect, /* xCreate */
|
|
dbpageConnect, /* xConnect */
|
|
@@ -195866,7 +196939,6 @@ static int sessionPreupdateEqual(
|
|
a += sessionSerialLen(a);
|
|
}else{
|
|
sqlite3_value *pVal; /* Value returned by preupdate_new/old */
|
|
- int rc; /* Error code from preupdate_new/old */
|
|
int eType = *a++; /* Type of value from change record */
|
|
|
|
/* The following calls to preupdate_new() and preupdate_old() can not
|
|
@@ -195876,12 +196948,11 @@ static int sessionPreupdateEqual(
|
|
** this (that the method has already been called). */
|
|
if( op==SQLITE_INSERT ){
|
|
/* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */
|
|
- rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal);
|
|
+ pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal);
|
|
}else{
|
|
/* assert( db->pPreUpdate->pUnpacked ); */
|
|
- rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
|
|
+ pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
|
|
}
|
|
- assert( rc==SQLITE_OK );
|
|
if( sqlite3_value_type(pVal)!=eType ) return 0;
|
|
|
|
/* A SessionChange object never has a NULL value in a PK column */
|
|
@@ -195904,7 +196975,7 @@ static int sessionPreupdateEqual(
|
|
int n;
|
|
const u8 *z;
|
|
a += sessionVarintGet(a, &n);
|
|
- if( sqlite3_value_bytes(pVal)!=n ) return 0;
|
|
+ if( sqlite3_value_bytes(pVal)!=(size_t)n ) return 0;
|
|
if( eType==SQLITE_TEXT ){
|
|
z = sqlite3_value_text(pVal);
|
|
}else{
|
|
@@ -195936,7 +197007,7 @@ static int sessionGrowHash(int bPatchset
|
|
SessionChange **apNew;
|
|
sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);
|
|
|
|
- apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew);
|
|
+ apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
|
|
if( apNew==0 ){
|
|
if( pTab->nChange==0 ){
|
|
return SQLITE_ERROR;
|
|
@@ -196002,7 +197073,7 @@ static int sessionTableInfo(
|
|
char *zPragma;
|
|
sqlite3_stmt *pStmt;
|
|
int rc;
|
|
- sqlite3_int64 nByte;
|
|
+ size_t nByte;
|
|
int nDbCol = 0;
|
|
int nThis;
|
|
int i;
|
|
@@ -196045,7 +197116,7 @@ static int sessionTableInfo(
|
|
|
|
if( rc==SQLITE_OK ){
|
|
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
|
|
- pAlloc = sqlite3_malloc64(nByte);
|
|
+ pAlloc = sqlite3_malloc(nByte);
|
|
if( pAlloc==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}
|
|
@@ -196268,7 +197339,7 @@ static void sessionPreupdateOneChange(
|
|
}
|
|
|
|
/* Allocate the change object */
|
|
- pChange = (SessionChange *)sqlite3_malloc64(nByte);
|
|
+ pChange = (SessionChange *)sqlite3_malloc(nByte);
|
|
if( !pChange ){
|
|
rc = SQLITE_NOMEM;
|
|
goto error_out;
|
|
@@ -196712,7 +197783,7 @@ SQLITE_API int sqlite3session_create(
|
|
*ppSession = 0;
|
|
|
|
/* Allocate and populate the new session object. */
|
|
- pNew = (sqlite3_session *)sqlite3_malloc64(sizeof(sqlite3_session) + nDb + 1);
|
|
+ pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
|
|
if( !pNew ) return SQLITE_NOMEM;
|
|
memset(pNew, 0, sizeof(sqlite3_session));
|
|
pNew->db = db;
|
|
@@ -196831,7 +197902,7 @@ SQLITE_API int sqlite3session_attach(
|
|
|
|
if( !pTab ){
|
|
/* Allocate new SessionTable object. */
|
|
- pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1);
|
|
+ pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
|
|
if( !pTab ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -197133,7 +198204,7 @@ static int sessionAppendUpdate(
|
|
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
|
nAdvance = nHdr + n;
|
|
if( eType==sqlite3_column_type(pStmt, i)
|
|
- && n==sqlite3_column_bytes(pStmt, i)
|
|
+ && (size_t)n==sqlite3_column_bytes(pStmt, i)
|
|
&& (n==0 || 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), n))
|
|
){
|
|
break;
|
|
@@ -197767,7 +198838,7 @@ static int sessionValueSetStr(
|
|
** argument to sqlite3ValueSetStr() and have the copy created
|
|
** automatically. But doing so makes it difficult to detect any OOM
|
|
** error. Hence the code to create the copy externally. */
|
|
- u8 *aCopy = sqlite3_malloc64((sqlite3_int64)nData+1);
|
|
+ u8 *aCopy = sqlite3_malloc(nData+1);
|
|
if( aCopy==0 ) return SQLITE_NOMEM;
|
|
memcpy(aCopy, aData, nData);
|
|
sqlite3ValueSetStr(pVal, nData, (char*)aCopy, enc, sqlite3_free);
|
|
@@ -198380,7 +199451,7 @@ static int sessionChangesetInvert(
|
|
int iCol;
|
|
|
|
if( 0==apVal ){
|
|
- apVal = (sqlite3_value **)sqlite3_malloc64(sizeof(apVal[0])*nCol*2);
|
|
+ apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2);
|
|
if( 0==apVal ){
|
|
rc = SQLITE_NOMEM;
|
|
goto finished_invert;
|
|
@@ -199653,7 +200724,7 @@ static int sessionChangeMerge(
|
|
int rc = SQLITE_OK;
|
|
|
|
if( !pExist ){
|
|
- pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec);
|
|
+ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec);
|
|
if( !pNew ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -199687,7 +200758,7 @@ static int sessionChangeMerge(
|
|
*ppNew = pExist;
|
|
}else{
|
|
sqlite3_int64 nByte = nRec + pExist->nRecord + sizeof(SessionChange);
|
|
- pNew = (SessionChange*)sqlite3_malloc64(nByte);
|
|
+ pNew = (SessionChange*)sqlite3_malloc(nByte);
|
|
if( pNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -199754,7 +200825,7 @@ static int sessionChangeMerge(
|
|
** buffer of the new object is large enough to hold any record that
|
|
** may be generated by combining the input records. */
|
|
nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
|
|
- pNew = (SessionChange *)sqlite3_malloc64(nByte);
|
|
+ pNew = (SessionChange *)sqlite3_malloc(nByte);
|
|
if( !pNew ){
|
|
sqlite3_free(pExist);
|
|
return SQLITE_NOMEM;
|
|
@@ -199860,7 +200931,7 @@ static int sessionChangesetToHash(
|
|
if( !pTab ){
|
|
SessionTable **ppTab;
|
|
|
|
- pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1);
|
|
+ pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1);
|
|
if( !pTab ){
|
|
rc = SQLITE_NOMEM;
|
|
break;
|
|
@@ -201056,7 +202127,7 @@ struct fts5_api {
|
|
fts5_api *pApi,
|
|
const char *zName,
|
|
void *pContext,
|
|
- fts5_tokenizer *pTokenizer,
|
|
+ const fts5_tokenizer *pTokenizer,
|
|
void (*xDestroy)(void*)
|
|
);
|
|
|
|
@@ -201128,8 +202199,12 @@ typedef sqlite3_uint64 u64;
|
|
#define ALWAYS(x) 1
|
|
#define NEVER(x) 0
|
|
|
|
-#define MIN(x,y) (((x) < (y)) ? (x) : (y))
|
|
-#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
|
+#ifndef MIN
|
|
+# define MIN(A,B) ((A)<(B)?(A):(B))
|
|
+#endif
|
|
+#ifndef MAX
|
|
+# define MAX(A,B) ((A)>(B)?(A):(B))
|
|
+#endif
|
|
|
|
/*
|
|
** Constants for the largest and smallest possible 64-bit signed integers.
|
|
@@ -201139,6 +202214,10 @@ typedef sqlite3_uint64 u64;
|
|
|
|
#endif
|
|
|
|
+#ifndef SQLITE_PRIVATE
|
|
+# define SQLITE_PRIVATE static
|
|
+#endif
|
|
+
|
|
/* Truncate very long tokens to this many bytes. Hard limit is
|
|
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
|
|
** field that occurs at the start of each leaf page (see fts5_index.c). */
|
|
@@ -201350,7 +202429,7 @@ static void sqlite3Fts5BufferAppendStrin
|
|
static void sqlite3Fts5BufferFree(Fts5Buffer*);
|
|
static void sqlite3Fts5BufferZero(Fts5Buffer*);
|
|
static void sqlite3Fts5BufferSet(int*, Fts5Buffer*, int, const u8*);
|
|
-static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...);
|
|
+static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, const char *zFmt, ...);
|
|
|
|
static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
|
|
|
|
@@ -201405,8 +202484,8 @@ static int sqlite3Fts5PoslistNext64(
|
|
);
|
|
|
|
/* Malloc utility */
|
|
-static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte);
|
|
-static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn);
|
|
+static void *sqlite3Fts5MallocZero(int *pRc, size_t nByte);
|
|
+static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, size_t nIn);
|
|
|
|
/* Character set tests (like isspace(), isalpha() etc.) */
|
|
static int sqlite3Fts5IsBareword(char t);
|
|
@@ -201893,8 +202972,8 @@ static int sqlite3Fts5VocabInit(Fts5Glob
|
|
/**************************************************************************
|
|
** Interface to automatically generated code in fts5_unicode2.c.
|
|
*/
|
|
-static int sqlite3Fts5UnicodeIsdiacritic(int c);
|
|
-static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
|
|
+SQLITE_PRIVATE int sqlite3Fts5UnicodeIsdiacritic(int c);
|
|
+SQLITE_PRIVATE int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
|
|
|
|
static int sqlite3Fts5UnicodeCatParse(const char*, u8*);
|
|
static int sqlite3Fts5UnicodeCategory(u32 iCode);
|
|
@@ -201973,7 +203052,7 @@ static void sqlite3Fts5UnicodeAscii(u8*,
|
|
** Alternative datatype for the argument to the malloc() routine passed
|
|
** into sqlite3ParserAlloc(). The default is size_t.
|
|
*/
|
|
-#define fts5YYMALLOCARGTYPE u64
|
|
+#define fts5YYMALLOCARGTYPE size_t
|
|
|
|
/**************** End of %include directives **********************************/
|
|
/* These constants specify the various numeric values for terminal symbols
|
|
@@ -203095,7 +204174,7 @@ static fts5YYACTIONTYPE fts5yy_reduce(
|
|
break;
|
|
/********** End reduce actions ************************************************/
|
|
};
|
|
- assert( fts5yyruleno<sizeof(fts5yyRuleInfoLhs)/sizeof(fts5yyRuleInfoLhs[0]) );
|
|
+ assert( (size_t)fts5yyruleno<sizeof(fts5yyRuleInfoLhs)/sizeof(fts5yyRuleInfoLhs[0]) );
|
|
fts5yygoto = fts5yyRuleInfoLhs[fts5yyruleno];
|
|
fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno];
|
|
fts5yyact = fts5yy_find_reduce_action(fts5yymsp[fts5yysize].stateno,(fts5YYCODETYPE)fts5yygoto);
|
|
@@ -203963,13 +205042,13 @@ static int fts5Bm25GetData(
|
|
int nPhrase; /* Number of phrases in query */
|
|
sqlite3_int64 nRow = 0; /* Number of rows in table */
|
|
sqlite3_int64 nToken = 0; /* Number of tokens in table */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate */
|
|
+ size_t nByte; /* Bytes of space to allocate */
|
|
int i;
|
|
|
|
/* Allocate the Fts5Bm25Data object */
|
|
nPhrase = pApi->xPhraseCount(pFts);
|
|
nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double);
|
|
- p = (Fts5Bm25Data*)sqlite3_malloc64(nByte);
|
|
+ p = (Fts5Bm25Data*)sqlite3_malloc(nByte);
|
|
if( p==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -204093,7 +205172,7 @@ static int sqlite3Fts5AuxInit(fts5_api *
|
|
{ "bm25", 0, fts5Bm25Function, 0 },
|
|
};
|
|
int rc = SQLITE_OK; /* Return code */
|
|
- int i; /* To iterate through builtin functions */
|
|
+ unsigned int i; /* To iterate through builtin functions */
|
|
|
|
for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){
|
|
rc = pApi->xCreateFunction(pApi,
|
|
@@ -204209,7 +205288,7 @@ static void sqlite3Fts5BufferAppendStrin
|
|
static void sqlite3Fts5BufferAppendPrintf(
|
|
int *pRc,
|
|
Fts5Buffer *pBuf,
|
|
- char *zFmt, ...
|
|
+ const char *zFmt, ...
|
|
){
|
|
if( *pRc==SQLITE_OK ){
|
|
char *zTmp;
|
|
@@ -204353,10 +205432,10 @@ static int sqlite3Fts5PoslistWriterAppen
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
-static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){
|
|
+static void *sqlite3Fts5MallocZero(int *pRc, size_t nByte){
|
|
void *pRet = 0;
|
|
if( *pRc==SQLITE_OK ){
|
|
- pRet = sqlite3_malloc64(nByte);
|
|
+ pRet = sqlite3_malloc(nByte);
|
|
if( pRet==0 ){
|
|
if( nByte>0 ) *pRc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -204374,11 +205453,11 @@ static void *sqlite3Fts5MallocZero(int *
|
|
** It is the responsibility of the caller to eventually free the returned
|
|
** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned.
|
|
*/
|
|
-static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
|
|
+static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, size_t nIn){
|
|
char *zRet = 0;
|
|
if( *pRc==SQLITE_OK ){
|
|
- if( nIn<0 ){
|
|
- nIn = (int)strlen(pIn);
|
|
+ if( nIn>(((size_t)-1)>>1) ){
|
|
+ nIn = strlen(pIn);
|
|
}
|
|
zRet = (char*)sqlite3_malloc(nIn+1);
|
|
if( zRet ){
|
|
@@ -204799,7 +205878,7 @@ static int fts5ConfigParseSpecial(
|
|
|
|
if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
|
|
const char *p = (const char*)zArg;
|
|
- sqlite3_int64 nArg = strlen(zArg) + 1;
|
|
+ size_t nArg = strlen(zArg) + 1;
|
|
char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
|
|
char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
|
|
char *pSpace = pDel;
|
|
@@ -204929,8 +206008,8 @@ static const char *fts5ConfigGobbleWord(
|
|
){
|
|
const char *zRet = 0;
|
|
|
|
- sqlite3_int64 nIn = strlen(zIn);
|
|
- char *zOut = sqlite3_malloc64(nIn+1);
|
|
+ size_t nIn = strlen(zIn);
|
|
+ char *zOut = sqlite3_malloc(nIn+1);
|
|
|
|
assert( *pRc==SQLITE_OK );
|
|
*pbQuoted = 0;
|
|
@@ -205033,7 +206112,7 @@ static int sqlite3Fts5ConfigParse(
|
|
int rc = SQLITE_OK; /* Return code */
|
|
Fts5Config *pRet; /* New object to return */
|
|
int i;
|
|
- sqlite3_int64 nByte;
|
|
+ size_t nByte;
|
|
|
|
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
|
|
if( pRet==0 ) return SQLITE_NOMEM;
|
|
@@ -205495,7 +206574,7 @@ typedef struct Fts5ExprTerm Fts5ExprTerm
|
|
/*
|
|
** Functions generated by lemon from fts5parse.y.
|
|
*/
|
|
-static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64));
|
|
+static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(size_t));
|
|
static void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*));
|
|
static void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*);
|
|
#ifndef NDEBUG
|
|
@@ -205510,7 +206589,7 @@ struct Fts5Expr {
|
|
Fts5Config *pConfig;
|
|
Fts5ExprNode *pRoot;
|
|
int bDesc; /* Iterate in descending rowid order */
|
|
- int nPhrase; /* Number of phrases in expression */
|
|
+ unsigned int nPhrase; /* Number of phrases in expression */
|
|
Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */
|
|
};
|
|
|
|
@@ -205677,7 +206756,7 @@ static int fts5ExprGetToken(
|
|
return tok;
|
|
}
|
|
|
|
-static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc64((sqlite3_int64)t);}
|
|
+static void *fts5ParseAlloc(size_t t){ return sqlite3_malloc(t);}
|
|
static void fts5ParseFree(void *p){ sqlite3_free(p); }
|
|
|
|
static int sqlite3Fts5ExprNew(
|
|
@@ -205822,8 +206901,8 @@ static int fts5ExprSynonymList(
|
|
if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){
|
|
if( pIter->nData==0 ) continue;
|
|
if( nIter==nAlloc ){
|
|
- sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
|
|
- Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc64(nByte);
|
|
+ size_t nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
|
|
+ Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
|
|
if( aNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
goto synonym_poslist_out;
|
|
@@ -205903,8 +206982,8 @@ static int fts5ExprPhraseIsMatch(
|
|
/* If the aStatic[] array is not large enough, allocate a large array
|
|
** using sqlite3_malloc(). This approach could be improved upon. */
|
|
if( pPhrase->nTerm>ArraySize(aStatic) ){
|
|
- sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
|
|
- aIter = (Fts5PoslistReader*)sqlite3_malloc64(nByte);
|
|
+ size_t nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
|
|
+ aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
|
|
if( !aIter ) return SQLITE_NOMEM;
|
|
}
|
|
memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm);
|
|
@@ -206038,7 +207117,7 @@ static int fts5ExprNearIsMatch(int *pRc,
|
|
/* If the aStatic[] array is not large enough, allocate a large array
|
|
** using sqlite3_malloc(). This approach could be improved upon. */
|
|
if( pNear->nPhrase>ArraySize(aStatic) ){
|
|
- sqlite3_int64 nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
|
|
+ size_t nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
|
|
a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte);
|
|
}else{
|
|
memset(aStatic, 0, sizeof(aStatic));
|
|
@@ -206947,9 +208026,9 @@ static Fts5ExprNearset *sqlite3Fts5Parse
|
|
return pNear;
|
|
}
|
|
if( pNear==0 ){
|
|
- sqlite3_int64 nByte;
|
|
+ size_t nByte;
|
|
nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
|
|
- pRet = sqlite3_malloc64(nByte);
|
|
+ pRet = sqlite3_malloc(nByte);
|
|
if( pRet==0 ){
|
|
pParse->rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -207024,8 +208103,8 @@ static int fts5ParseTokenize(
|
|
|
|
if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED) ){
|
|
Fts5ExprTerm *pSyn;
|
|
- sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
|
|
- pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte);
|
|
+ size_t nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
|
|
+ pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte);
|
|
if( pSyn==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -207557,7 +208636,7 @@ static Fts5ExprNode *sqlite3Fts5ParseNod
|
|
|
|
if( pParse->rc==SQLITE_OK ){
|
|
int nChild = 0; /* Number of children of returned node */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate for this node */
|
|
+ size_t nByte; /* Bytes of space to allocate for this node */
|
|
|
|
assert( (eType!=FTS5_STRING && !pNear)
|
|
|| (eType==FTS5_STRING && !pLeft && !pRight)
|
|
@@ -207689,7 +208768,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImp
|
|
}
|
|
|
|
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
|
|
- sqlite3_int64 nByte = 0;
|
|
+ size_t nByte = 0;
|
|
Fts5ExprTerm *p;
|
|
char *zQuoted;
|
|
|
|
@@ -207697,7 +208776,7 @@ static char *fts5ExprTermPrint(Fts5ExprT
|
|
for(p=pTerm; p; p=p->pSynonym){
|
|
nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
|
|
}
|
|
- zQuoted = sqlite3_malloc64(nByte);
|
|
+ zQuoted = sqlite3_malloc(nByte);
|
|
|
|
if( zQuoted ){
|
|
int i = 0;
|
|
@@ -207937,7 +209016,7 @@ static void fts5ExprFunction(
|
|
}
|
|
|
|
nConfig = 3 + (nArg-iArg);
|
|
- azConfig = (const char**)sqlite3_malloc64(sizeof(char*) * nConfig);
|
|
+ azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig);
|
|
if( azConfig==0 ){
|
|
sqlite3_result_error_nomem(pCtx);
|
|
return;
|
|
@@ -208058,13 +209137,13 @@ static int sqlite3Fts5ExprInit(Fts5Globa
|
|
{ "fts5_isalnum", fts5ExprIsAlnum },
|
|
{ "fts5_fold", fts5ExprFold },
|
|
};
|
|
- int i;
|
|
+ unsigned int i;
|
|
int rc = SQLITE_OK;
|
|
void *pCtx = (void*)pGlobal;
|
|
|
|
for(i=0; rc==SQLITE_OK && i<ArraySize(aFunc); i++){
|
|
struct Fts5ExprFunc *p = &aFunc[i];
|
|
- rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0);
|
|
+ rc = sqlite3_create_function_v2(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0, 0);
|
|
}
|
|
|
|
/* Avoid warnings indicating that sqlite3Fts5ParserTrace() and
|
|
@@ -208088,7 +209167,7 @@ static int sqlite3Fts5ExprPhraseCount(Ft
|
|
** Return the number of terms in the iPhrase'th phrase in pExpr.
|
|
*/
|
|
static int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){
|
|
- if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0;
|
|
+ if( iPhrase<0 || iPhrase>=(int)pExpr->nPhrase ) return 0;
|
|
return pExpr->apExprPhrase[iPhrase]->nTerm;
|
|
}
|
|
|
|
@@ -208118,9 +209197,9 @@ struct Fts5PoslistPopulator {
|
|
|
|
static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){
|
|
Fts5PoslistPopulator *pRet;
|
|
- pRet = sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
|
|
+ pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
|
|
if( pRet ){
|
|
- int i;
|
|
+ size_t i;
|
|
memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
|
|
for(i=0; i<pExpr->nPhrase; i++){
|
|
Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist;
|
|
@@ -208166,7 +209245,7 @@ static int fts5ExprPopulatePoslistsCb(
|
|
){
|
|
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
|
|
Fts5Expr *pExpr = p->pExpr;
|
|
- int i;
|
|
+ size_t i;
|
|
|
|
UNUSED_PARAM2(iUnused1, iUnused2);
|
|
|
|
@@ -208198,7 +209277,7 @@ static int sqlite3Fts5ExprPopulatePoslis
|
|
int iCol,
|
|
const char *z, int n
|
|
){
|
|
- int i;
|
|
+ size_t i;
|
|
Fts5ExprCtx sCtx;
|
|
sCtx.pExpr = pExpr;
|
|
sCtx.aPopulator = aPopulator;
|
|
@@ -208410,14 +209489,14 @@ static int sqlite3Fts5HashNew(Fts5Config
|
|
if( pNew==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
- sqlite3_int64 nByte;
|
|
+ size_t nByte;
|
|
memset(pNew, 0, sizeof(Fts5Hash));
|
|
pNew->pnByte = pnByte;
|
|
pNew->eDetail = pConfig->eDetail;
|
|
|
|
pNew->nSlot = 1024;
|
|
nByte = sizeof(Fts5HashEntry*) * pNew->nSlot;
|
|
- pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc64(nByte);
|
|
+ pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte);
|
|
if( pNew->aSlot==0 ){
|
|
sqlite3_free(pNew);
|
|
*ppNew = 0;
|
|
@@ -208485,7 +209564,7 @@ static int fts5HashResize(Fts5Hash *pHas
|
|
Fts5HashEntry **apNew;
|
|
Fts5HashEntry **apOld = pHash->aSlot;
|
|
|
|
- apNew = (Fts5HashEntry**)sqlite3_malloc64(nNew*sizeof(Fts5HashEntry*));
|
|
+ apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*));
|
|
if( !apNew ) return SQLITE_NOMEM;
|
|
memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));
|
|
|
|
@@ -208590,7 +209669,7 @@ static int sqlite3Fts5HashWrite(
|
|
if( p==0 ){
|
|
/* Figure out how much space to allocate */
|
|
char *zKey;
|
|
- sqlite3_int64 nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64;
|
|
+ size_t nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64;
|
|
if( nByte<128 ) nByte = 128;
|
|
|
|
/* Grow the Fts5Hash.aSlot[] array if necessary. */
|
|
@@ -208601,7 +209680,7 @@ static int sqlite3Fts5HashWrite(
|
|
}
|
|
|
|
/* Allocate new Fts5HashEntry and add it to the hash table. */
|
|
- p = (Fts5HashEntry*)sqlite3_malloc64(nByte);
|
|
+ p = (Fts5HashEntry*)sqlite3_malloc(nByte);
|
|
if( !p ) return SQLITE_NOMEM;
|
|
memset(p, 0, sizeof(Fts5HashEntry));
|
|
p->nAlloc = (int)nByte;
|
|
@@ -208769,7 +209848,7 @@ static int fts5HashEntrySort(
|
|
int i;
|
|
|
|
*ppSorted = 0;
|
|
- ap = sqlite3_malloc64(sizeof(Fts5HashEntry*) * nMergeSlot);
|
|
+ ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot);
|
|
if( !ap ) return SQLITE_NOMEM;
|
|
memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);
|
|
|
|
@@ -209453,7 +210532,7 @@ static u16 fts5GetU16(const u8 *aIn){
|
|
** If an OOM error is encountered, return NULL and set the error code in
|
|
** the Fts5Index handle passed as the first argument.
|
|
*/
|
|
-static void *fts5IdxMalloc(Fts5Index *p, sqlite3_int64 nByte){
|
|
+static void *fts5IdxMalloc(Fts5Index *p, size_t nByte){
|
|
return sqlite3Fts5MallocZero(&p->rc, nByte);
|
|
}
|
|
|
|
@@ -209553,8 +210632,8 @@ static Fts5Data *fts5DataRead(Fts5Index
|
|
if( rc==SQLITE_OK ){
|
|
u8 *aOut = 0; /* Read blob data into this buffer */
|
|
int nByte = sqlite3_blob_bytes(p->pReader);
|
|
- sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
|
|
- pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
|
|
+ size_t nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
|
|
+ pRet = (Fts5Data*)sqlite3_malloc(nAlloc);
|
|
if( pRet ){
|
|
pRet->nn = nByte;
|
|
aOut = pRet->p = (u8*)&pRet[1];
|
|
@@ -209729,7 +210808,7 @@ static int fts5StructureDecode(
|
|
int iLvl;
|
|
int nLevel = 0;
|
|
int nSegment = 0;
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */
|
|
+ size_t nByte; /* Bytes of space to allocate at pRet */
|
|
Fts5Structure *pRet = 0; /* Structure object to return */
|
|
|
|
/* Grab the cookie value */
|
|
@@ -213545,7 +214624,7 @@ static Fts5Structure *fts5IndexOptimizeS
|
|
Fts5Structure *pStruct
|
|
){
|
|
Fts5Structure *pNew = 0;
|
|
- sqlite3_int64 nByte = sizeof(Fts5Structure);
|
|
+ size_t nByte = sizeof(Fts5Structure);
|
|
int nSeg = pStruct->nSegment;
|
|
int i;
|
|
|
|
@@ -215425,8 +216504,8 @@ static void fts5RowidFunction(
|
|
** SQLite error code is returned instead.
|
|
*/
|
|
static int sqlite3Fts5IndexInit(sqlite3 *db){
|
|
- int rc = sqlite3_create_function(
|
|
- db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0
|
|
+ int rc = sqlite3_create_function_v2(
|
|
+ db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0, 0
|
|
);
|
|
|
|
if( rc==SQLITE_OK ){
|
|
@@ -215437,8 +216516,8 @@ static int sqlite3Fts5IndexInit(sqlite3
|
|
}
|
|
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_create_function(
|
|
- db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
|
|
+ rc = sqlite3_create_function_v2(
|
|
+ db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0, 0
|
|
);
|
|
}
|
|
return rc;
|
|
@@ -215477,7 +216556,9 @@ static int sqlite3Fts5IndexReset(Fts5Ind
|
|
** assert() conditions in the fts5 code are activated - conditions that are
|
|
** only true if it is guaranteed that the fts5 database is not corrupt.
|
|
*/
|
|
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
|
SQLITE_API int sqlite3_fts5_may_be_corrupt = 1;
|
|
+#endif
|
|
|
|
|
|
typedef struct Fts5Auxdata Fts5Auxdata;
|
|
@@ -215577,7 +216658,7 @@ struct Fts5FullTable {
|
|
|
|
struct Fts5MatchPhrase {
|
|
Fts5Buffer *pPoslist; /* Pointer to current poslist */
|
|
- int nTerm; /* Size of phrase in terms */
|
|
+ unsigned int nTerm; /* Size of phrase in terms */
|
|
};
|
|
|
|
/*
|
|
@@ -215992,7 +217073,7 @@ static int fts5BestIndexMethod(sqlite3_v
|
|
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
|
|
/* Set idxFlags flags for all WHERE clause terms that will be used. */
|
|
- for(i=0; i<pInfo->nConstraint; i++){
|
|
+ for(i=0; (int)i<pInfo->nConstraint; i++){
|
|
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
|
|
int iCol = p->iColumn;
|
|
|
|
@@ -216079,13 +217160,13 @@ static int fts5OpenMethod(sqlite3_vtab *
|
|
Fts5FullTable *pTab = (Fts5FullTable*)pVTab;
|
|
Fts5Config *pConfig = pTab->p.pConfig;
|
|
Fts5Cursor *pCsr = 0; /* New cursor object */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate */
|
|
+ size_t nByte; /* Bytes of space to allocate */
|
|
int rc; /* Return code */
|
|
|
|
rc = fts5NewTransaction(pTab);
|
|
if( rc==SQLITE_OK ){
|
|
nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
|
|
- pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte);
|
|
+ pCsr = (Fts5Cursor*)sqlite3_malloc(nByte);
|
|
if( pCsr ){
|
|
Fts5Global *pGlobal = pTab->pGlobal;
|
|
memset(pCsr, 0, (size_t)nByte);
|
|
@@ -216360,14 +217441,14 @@ static int fts5CursorFirstSorted(
|
|
Fts5Config *pConfig = pTab->p.pConfig;
|
|
Fts5Sorter *pSorter;
|
|
int nPhrase;
|
|
- sqlite3_int64 nByte;
|
|
+ size_t nByte;
|
|
int rc;
|
|
const char *zRank = pCsr->zRank;
|
|
const char *zRankArgs = pCsr->zRankArgs;
|
|
|
|
nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
|
|
nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
|
|
- pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
|
|
+ pSorter = (Fts5Sorter*)sqlite3_malloc(nByte);
|
|
if( pSorter==0 ) return SQLITE_NOMEM;
|
|
memset(pSorter, 0, (size_t)nByte);
|
|
pSorter->nIdx = nPhrase;
|
|
@@ -217923,8 +219004,8 @@ static int fts5CreateAux(
|
|
int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
|
|
if( rc==SQLITE_OK ){
|
|
Fts5Auxiliary *pAux;
|
|
- sqlite3_int64 nName; /* Size of zName in bytes, including \0 */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate */
|
|
+ size_t nName; /* Size of zName in bytes, including \0 */
|
|
+ size_t nByte; /* Bytes of space to allocate */
|
|
|
|
nName = strlen(zName) + 1;
|
|
nByte = sizeof(Fts5Auxiliary) + nName;
|
|
@@ -217955,13 +219036,13 @@ static int fts5CreateTokenizer(
|
|
fts5_api *pApi, /* Global context (one per db handle) */
|
|
const char *zName, /* Name of new function */
|
|
void *pUserData, /* User data for aux. function */
|
|
- fts5_tokenizer *pTokenizer, /* Tokenizer implementation */
|
|
+ const fts5_tokenizer *pTokenizer,/* Tokenizer implementation */
|
|
void(*xDestroy)(void*) /* Destructor for pUserData */
|
|
){
|
|
Fts5Global *pGlobal = (Fts5Global*)pApi;
|
|
Fts5TokenizerModule *pNew;
|
|
- sqlite3_int64 nName; /* Size of zName and its \0 terminator */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate */
|
|
+ size_t nName; /* Size of zName and its \0 terminator */
|
|
+ size_t nByte; /* Bytes of space to allocate */
|
|
int rc = SQLITE_OK;
|
|
|
|
nName = strlen(zName) + 1;
|
|
@@ -218170,13 +219251,13 @@ static int fts5Init(sqlite3 *db){
|
|
if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api);
|
|
if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_create_function(
|
|
- db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
|
|
+ rc = sqlite3_create_function_v2(
|
|
+ db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0, 0
|
|
);
|
|
}
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_create_function(
|
|
- db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0
|
|
+ rc = sqlite3_create_function_v2(
|
|
+ db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0, 0
|
|
);
|
|
}
|
|
}
|
|
@@ -218306,7 +219387,7 @@ static int fts5StorageGetStmt(
|
|
|
|
assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
|
|
if( p->aStmt[eStmt]==0 ){
|
|
- const char *azStmt[] = {
|
|
+ const char *const azStmt[] = {
|
|
"SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
|
|
"SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
|
|
"SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */
|
|
@@ -218348,11 +219429,11 @@ static int fts5StorageGetStmt(
|
|
|
|
case FTS5_STMT_INSERT_CONTENT:
|
|
case FTS5_STMT_REPLACE_CONTENT: {
|
|
- int nCol = pC->nCol + 1;
|
|
+ size_t nCol = pC->nCol + 1;
|
|
char *zBind;
|
|
- int i;
|
|
+ size_t i;
|
|
|
|
- zBind = sqlite3_malloc64(1 + nCol*2);
|
|
+ zBind = sqlite3_malloc(1 + nCol*2);
|
|
if( zBind ){
|
|
for(i=0; i<nCol; i++){
|
|
zBind[i*2] = '?';
|
|
@@ -218519,11 +219600,11 @@ static int sqlite3Fts5StorageOpen(
|
|
){
|
|
int rc = SQLITE_OK;
|
|
Fts5Storage *p; /* New object */
|
|
- sqlite3_int64 nByte; /* Bytes of space to allocate */
|
|
+ size_t nByte; /* Bytes of space to allocate */
|
|
|
|
nByte = sizeof(Fts5Storage) /* Fts5Storage object */
|
|
+ pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */
|
|
- *pp = p = (Fts5Storage*)sqlite3_malloc64(nByte);
|
|
+ *pp = p = (Fts5Storage*)sqlite3_malloc(nByte);
|
|
if( !p ) return SQLITE_NOMEM;
|
|
|
|
memset(p, 0, (size_t)nByte);
|
|
@@ -218534,7 +219615,7 @@ static int sqlite3Fts5StorageOpen(
|
|
if( bCreate ){
|
|
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
|
int nDefn = 32 + pConfig->nCol*10;
|
|
- char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10);
|
|
+ char *zDefn = sqlite3_malloc(32 + (size_t)pConfig->nCol * 10);
|
|
if( zDefn==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -219115,7 +220196,7 @@ static int sqlite3Fts5StorageIntegrity(F
|
|
|
|
memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
|
|
ctx.pConfig = p->pConfig;
|
|
- aTotalSize = (i64*)sqlite3_malloc64(pConfig->nCol*(sizeof(int)+sizeof(i64)));
|
|
+ aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol*(sizeof(int)+sizeof(i64)));
|
|
if( !aTotalSize ) return SQLITE_NOMEM;
|
|
aColSize = (int*)&aTotalSize[pConfig->nCol];
|
|
memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
|
|
@@ -219401,7 +220482,7 @@ static int sqlite3Fts5StorageConfigValue
|
|
** For tokenizers with no "unicode" modifier, the set of token characters
|
|
** is the same as the set of ASCII range alphanumeric characters.
|
|
*/
|
|
-static unsigned char aAsciiTokenChar[128] = {
|
|
+static const unsigned char aAsciiTokenChar[128] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00..0x0F */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10..0x1F */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20..0x2F */
|
|
@@ -219506,14 +220587,14 @@ static int fts5AsciiTokenize(
|
|
int is = 0;
|
|
|
|
char aFold[64];
|
|
- int nFold = sizeof(aFold);
|
|
+ size_t nFold = sizeof(aFold);
|
|
char *pFold = aFold;
|
|
unsigned char *a = p->aTokenChar;
|
|
|
|
UNUSED_PARAM(iUnused);
|
|
|
|
while( is<nText && rc==SQLITE_OK ){
|
|
- int nByte;
|
|
+ size_t nByte;
|
|
|
|
/* Skip any leading divider characters. */
|
|
while( is<nText && ((pText[is]&0x80)==0 && a[(int)pText[is]]==0) ){
|
|
@@ -219531,7 +220612,7 @@ static int fts5AsciiTokenize(
|
|
nByte = ie-is;
|
|
if( nByte>nFold ){
|
|
if( pFold!=aFold ) sqlite3_free(pFold);
|
|
- pFold = sqlite3_malloc64((sqlite3_int64)nByte*2);
|
|
+ pFold = sqlite3_malloc(nByte*2);
|
|
if( pFold==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
break;
|
|
@@ -219830,7 +220911,7 @@ static int fts5UnicodeTokenize(
|
|
|
|
/* Output buffer */
|
|
char *aFold = p->aFold;
|
|
- int nFold = p->nFold;
|
|
+ size_t nFold = p->nFold;
|
|
const char *pEnd = &aFold[nFold-6];
|
|
|
|
UNUSED_PARAM(iUnused);
|
|
@@ -219870,7 +220951,7 @@ static int fts5UnicodeTokenize(
|
|
/* Grow the output buffer so that there is sufficient space to fit the
|
|
** largest possible utf-8 character. */
|
|
if( zOut>pEnd ){
|
|
- aFold = sqlite3_malloc64((sqlite3_int64)nFold*2);
|
|
+ aFold = sqlite3_malloc(nFold*2);
|
|
if( aFold==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
goto tokenize_done;
|
|
@@ -220641,17 +221722,18 @@ static int fts5PorterTokenize(
|
|
** Register all built-in tokenizers with FTS5.
|
|
*/
|
|
static int sqlite3Fts5TokenizerInit(fts5_api *pApi){
|
|
- struct BuiltinTokenizer {
|
|
+ static const struct BuiltinTokenizer {
|
|
const char *zName;
|
|
fts5_tokenizer x;
|
|
} aBuiltin[] = {
|
|
{ "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
|
|
+ { "unicode", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
|
|
{ "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
|
|
{ "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
|
|
};
|
|
|
|
int rc = SQLITE_OK; /* Return code */
|
|
- int i; /* To iterate through builtin functions */
|
|
+ unsigned int i; /* To iterate through builtin functions */
|
|
|
|
for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){
|
|
rc = pApi->xCreateTokenizer(pApi,
|
|
@@ -220696,7 +221778,7 @@ static int sqlite3Fts5TokenizerInit(fts5
|
|
** uppercase letter are undefined.
|
|
*/
|
|
static int fts5_remove_diacritic(int c, int bComplex){
|
|
- unsigned short aDia[] = {
|
|
+ static const unsigned short aDia[] = {
|
|
0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
|
|
2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
|
|
2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
|
|
@@ -220715,7 +221797,7 @@ static int fts5_remove_diacritic(int c,
|
|
63182, 63242, 63274, 63310, 63368, 63390,
|
|
};
|
|
#define HIBIT ((unsigned char)0x80)
|
|
- unsigned char aChar[] = {
|
|
+ static const unsigned char aChar[] = {
|
|
'\0', 'a', 'c', 'e', 'i', 'n',
|
|
'o', 'u', 'y', 'y', 'a', 'c',
|
|
'd', 'e', 'e', 'g', 'h', 'i',
|
|
@@ -220762,7 +221844,7 @@ static int fts5_remove_diacritic(int c,
|
|
** Return true if the argument interpreted as a unicode codepoint
|
|
** is a diacritical modifier character.
|
|
*/
|
|
-static int sqlite3Fts5UnicodeIsdiacritic(int c){
|
|
+SQLITE_PRIVATE int sqlite3Fts5UnicodeIsdiacritic(int c){
|
|
unsigned int mask0 = 0x08029FDF;
|
|
unsigned int mask1 = 0x000361F8;
|
|
if( c<768 || c>817 ) return 0;
|
|
@@ -220781,7 +221863,7 @@ static int sqlite3Fts5UnicodeIsdiacritic
|
|
** The results are undefined if the value passed to this function
|
|
** is less than zero.
|
|
*/
|
|
-static int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){
|
|
+SQLITE_PRIVATE int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){
|
|
/* Each entry in the following array defines a rule for folding a range
|
|
** of codepoints to lower case. The rule applies to a range of nRange
|
|
** codepoints starting at codepoint iCode.
|
|
@@ -220804,73 +221886,84 @@ static int sqlite3Fts5UnicodeFold(int c,
|
|
unsigned char flags;
|
|
unsigned char nRange;
|
|
} aEntry[] = {
|
|
- {65, 14, 26}, {181, 64, 1}, {192, 14, 23},
|
|
+ {65, 14, 26}, {181, 66, 1}, {192, 14, 23},
|
|
{216, 14, 7}, {256, 1, 48}, {306, 1, 6},
|
|
- {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
|
|
- {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
|
|
- {386, 1, 4}, {390, 44, 1}, {391, 0, 1},
|
|
- {393, 42, 2}, {395, 0, 1}, {398, 32, 1},
|
|
- {399, 38, 1}, {400, 40, 1}, {401, 0, 1},
|
|
- {403, 42, 1}, {404, 46, 1}, {406, 52, 1},
|
|
- {407, 48, 1}, {408, 0, 1}, {412, 52, 1},
|
|
- {413, 54, 1}, {415, 56, 1}, {416, 1, 6},
|
|
- {422, 60, 1}, {423, 0, 1}, {425, 60, 1},
|
|
- {428, 0, 1}, {430, 60, 1}, {431, 0, 1},
|
|
- {433, 58, 2}, {435, 1, 4}, {439, 62, 1},
|
|
+ {313, 1, 16}, {330, 1, 46}, {376, 156, 1},
|
|
+ {377, 1, 6}, {383, 144, 1}, {385, 52, 1},
|
|
+ {386, 1, 4}, {390, 46, 1}, {391, 0, 1},
|
|
+ {393, 44, 2}, {395, 0, 1}, {398, 34, 1},
|
|
+ {399, 40, 1}, {400, 42, 1}, {401, 0, 1},
|
|
+ {403, 44, 1}, {404, 48, 1}, {406, 54, 1},
|
|
+ {407, 50, 1}, {408, 0, 1}, {412, 54, 1},
|
|
+ {413, 56, 1}, {415, 58, 1}, {416, 1, 6},
|
|
+ {422, 62, 1}, {423, 0, 1}, {425, 62, 1},
|
|
+ {428, 0, 1}, {430, 62, 1}, {431, 0, 1},
|
|
+ {433, 60, 2}, {435, 1, 4}, {439, 64, 1},
|
|
{440, 0, 1}, {444, 0, 1}, {452, 2, 1},
|
|
{453, 0, 1}, {455, 2, 1}, {456, 0, 1},
|
|
{458, 2, 1}, {459, 1, 18}, {478, 1, 18},
|
|
- {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
|
|
- {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
|
|
- {546, 1, 18}, {570, 70, 1}, {571, 0, 1},
|
|
- {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
|
|
- {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
|
|
- {582, 1, 10}, {837, 36, 1}, {880, 1, 4},
|
|
- {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
|
|
- {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
|
|
- {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
|
|
- {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
|
|
- {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
|
|
- {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
|
|
- {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
|
|
- {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
|
|
- {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
|
|
- {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
|
|
- {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
|
|
- {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
|
|
- {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
|
|
- {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
|
|
- {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
|
|
- {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
|
|
- {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
|
|
- {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
|
|
- {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
|
|
- {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
|
|
- {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
|
|
- {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
|
|
- {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
|
|
- {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
|
|
- {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
|
|
- {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
|
|
- {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
|
|
+ {497, 2, 1}, {498, 1, 4}, {502, 162, 1},
|
|
+ {503, 174, 1}, {504, 1, 40}, {544, 150, 1},
|
|
+ {546, 1, 18}, {570, 74, 1}, {571, 0, 1},
|
|
+ {573, 148, 1}, {574, 72, 1}, {577, 0, 1},
|
|
+ {579, 146, 1}, {580, 30, 1}, {581, 32, 1},
|
|
+ {582, 1, 10}, {837, 38, 1}, {880, 1, 4},
|
|
+ {886, 0, 1}, {895, 38, 1}, {902, 20, 1},
|
|
+ {904, 18, 3}, {908, 28, 1}, {910, 26, 2},
|
|
+ {913, 14, 17}, {931, 14, 9}, {962, 0, 1},
|
|
+ {975, 4, 1}, {976, 180, 1}, {977, 182, 1},
|
|
+ {981, 186, 1}, {982, 184, 1}, {984, 1, 24},
|
|
+ {1008, 176, 1}, {1009, 178, 1}, {1012, 170, 1},
|
|
+ {1013, 168, 1}, {1015, 0, 1}, {1017, 192, 1},
|
|
+ {1018, 0, 1}, {1021, 150, 3}, {1024, 36, 16},
|
|
+ {1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54},
|
|
+ {1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96},
|
|
+ {1329, 24, 38}, {4256, 70, 38}, {4295, 70, 1},
|
|
+ {4301, 70, 1}, {5112, 190, 6}, {7296, 126, 1},
|
|
+ {7297, 128, 1}, {7298, 130, 1}, {7299, 134, 2},
|
|
+ {7301, 132, 1}, {7302, 136, 1}, {7303, 138, 1},
|
|
+ {7304, 100, 1}, {7312, 142, 43}, {7357, 142, 3},
|
|
+ {7680, 1, 150}, {7835, 172, 1}, {7838, 120, 1},
|
|
+ {7840, 1, 96}, {7944, 190, 8}, {7960, 190, 6},
|
|
+ {7976, 190, 8}, {7992, 190, 8}, {8008, 190, 6},
|
|
+ {8025, 191, 8}, {8040, 190, 8}, {8072, 190, 8},
|
|
+ {8088, 190, 8}, {8104, 190, 8}, {8120, 190, 2},
|
|
+ {8122, 166, 2}, {8124, 188, 1}, {8126, 124, 1},
|
|
+ {8136, 164, 4}, {8140, 188, 1}, {8152, 190, 2},
|
|
+ {8154, 160, 2}, {8168, 190, 2}, {8170, 158, 2},
|
|
+ {8172, 192, 1}, {8184, 152, 2}, {8186, 154, 2},
|
|
+ {8188, 188, 1}, {8486, 122, 1}, {8490, 116, 1},
|
|
+ {8491, 118, 1}, {8498, 12, 1}, {8544, 8, 16},
|
|
+ {8579, 0, 1}, {9398, 10, 26}, {11264, 24, 47},
|
|
+ {11360, 0, 1}, {11362, 112, 1}, {11363, 140, 1},
|
|
+ {11364, 114, 1}, {11367, 1, 6}, {11373, 108, 1},
|
|
+ {11374, 110, 1}, {11375, 104, 1}, {11376, 106, 1},
|
|
+ {11378, 0, 1}, {11381, 0, 1}, {11390, 102, 2},
|
|
{11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
|
|
- {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
|
|
- {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
|
|
- {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
|
|
- {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
|
|
- {65313, 14, 26},
|
|
+ {42560, 1, 46}, {42624, 1, 28}, {42786, 1, 14},
|
|
+ {42802, 1, 62}, {42873, 1, 4}, {42877, 98, 1},
|
|
+ {42878, 1, 10}, {42891, 0, 1}, {42893, 88, 1},
|
|
+ {42896, 1, 4}, {42902, 1, 20}, {42922, 80, 1},
|
|
+ {42923, 76, 1}, {42924, 78, 1}, {42925, 84, 1},
|
|
+ {42926, 80, 1}, {42928, 92, 1}, {42929, 86, 1},
|
|
+ {42930, 90, 1}, {42931, 68, 1}, {42932, 1, 12},
|
|
+ {42946, 0, 1}, {42948, 178, 1}, {42949, 82, 1},
|
|
+ {42950, 96, 1}, {43888, 94, 80}, {65313, 14, 26},
|
|
};
|
|
static const unsigned short aiOff[] = {
|
|
1, 2, 8, 15, 16, 26, 28, 32,
|
|
- 37, 38, 40, 48, 63, 64, 69, 71,
|
|
- 79, 80, 116, 202, 203, 205, 206, 207,
|
|
- 209, 210, 211, 213, 214, 217, 218, 219,
|
|
- 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
|
|
- 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
|
|
- 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
|
|
- 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
|
|
- 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
|
|
- 65514, 65521, 65527, 65528, 65529,
|
|
+ 34, 37, 38, 40, 48, 63, 64, 69,
|
|
+ 71, 79, 80, 116, 202, 203, 205, 206,
|
|
+ 207, 209, 210, 211, 213, 214, 217, 218,
|
|
+ 219, 775, 928, 7264, 10792, 10795, 23217, 23221,
|
|
+ 23228, 23229, 23231, 23254, 23256, 23275, 23278, 26672,
|
|
+ 30152, 30204, 35267, 54721, 54753, 54754, 54756, 54787,
|
|
+ 54793, 54809, 57153, 57274, 57921, 58019, 58363, 59314,
|
|
+ 59315, 59324, 59325, 59326, 59332, 59356, 61722, 62528,
|
|
+ 65268, 65341, 65373, 65406, 65408, 65410, 65415, 65424,
|
|
+ 65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480,
|
|
+ 65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528,
|
|
+ 65529,
|
|
};
|
|
|
|
int ret = c;
|
|
@@ -220912,6 +222005,21 @@ static int sqlite3Fts5UnicodeFold(int c,
|
|
else if( c>=66560 && c<66600 ){
|
|
ret = c + 40;
|
|
}
|
|
+ else if( c>=66736 && c<66772 ){
|
|
+ ret = c + 40;
|
|
+ }
|
|
+ else if( c>=68736 && c<68787 ){
|
|
+ ret = c + 64;
|
|
+ }
|
|
+ else if( c>=71840 && c<71872 ){
|
|
+ ret = c + 32;
|
|
+ }
|
|
+ else if( c>=93760 && c<93792 ){
|
|
+ ret = c + 32;
|
|
+ }
|
|
+ else if( c>=125184 && c<125218 ){
|
|
+ ret = c + 34;
|
|
+ }
|
|
|
|
return ret;
|
|
}
|
|
@@ -221035,11 +222143,11 @@ static int sqlite3Fts5UnicodeCatParse(co
|
|
return 0;
|
|
}
|
|
|
|
-static u16 aFts5UnicodeBlock[] = {
|
|
- 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760,
|
|
- 1760, 1760, 1760, 1760, 1760, 1763, 1765,
|
|
+static const u16 aFts5UnicodeBlock[] = {
|
|
+ 0, 1533, 2167, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
|
|
+ 2178, 2178, 2178, 2178, 2178, 2181, 2183,
|
|
};
|
|
-static u16 aFts5UnicodeMap[] = {
|
|
+static const u16 aFts5UnicodeMap[] = {
|
|
0, 32, 33, 36, 37, 40, 41, 42, 43, 44,
|
|
45, 46, 48, 58, 60, 63, 65, 91, 92, 93,
|
|
94, 95, 96, 97, 123, 124, 125, 126, 127, 160,
|
|
@@ -221054,171 +222162,213 @@ static u16 aFts5UnicodeMap[] = {
|
|
497, 498, 499, 500, 503, 505, 506, 564, 570, 572,
|
|
573, 575, 577, 580, 583, 584, 592, 660, 661, 688,
|
|
706, 710, 722, 736, 741, 748, 749, 750, 751, 768,
|
|
- 880, 884, 885, 886, 890, 891, 894, 900, 902, 903,
|
|
- 904, 908, 910, 912, 913, 931, 940, 975, 977, 978,
|
|
- 981, 984, 1008, 1012, 1014, 1015, 1018, 1020, 1021, 1072,
|
|
- 1120, 1154, 1155, 1160, 1162, 1217, 1231, 1232, 1329, 1369,
|
|
- 1370, 1377, 1417, 1418, 1423, 1425, 1470, 1471, 1472, 1473,
|
|
- 1475, 1476, 1478, 1479, 1488, 1520, 1523, 1536, 1542, 1545,
|
|
- 1547, 1548, 1550, 1552, 1563, 1566, 1568, 1600, 1601, 1611,
|
|
- 1632, 1642, 1646, 1648, 1649, 1748, 1749, 1750, 1757, 1758,
|
|
- 1759, 1765, 1767, 1769, 1770, 1774, 1776, 1786, 1789, 1791,
|
|
- 1792, 1807, 1808, 1809, 1810, 1840, 1869, 1958, 1969, 1984,
|
|
- 1994, 2027, 2036, 2038, 2039, 2042, 2048, 2070, 2074, 2075,
|
|
- 2084, 2085, 2088, 2089, 2096, 2112, 2137, 2142, 2208, 2210,
|
|
- 2276, 2304, 2307, 2308, 2362, 2363, 2364, 2365, 2366, 2369,
|
|
- 2377, 2381, 2382, 2384, 2385, 2392, 2402, 2404, 2406, 2416,
|
|
- 2417, 2418, 2425, 2433, 2434, 2437, 2447, 2451, 2474, 2482,
|
|
- 2486, 2492, 2493, 2494, 2497, 2503, 2507, 2509, 2510, 2519,
|
|
- 2524, 2527, 2530, 2534, 2544, 2546, 2548, 2554, 2555, 2561,
|
|
- 2563, 2565, 2575, 2579, 2602, 2610, 2613, 2616, 2620, 2622,
|
|
- 2625, 2631, 2635, 2641, 2649, 2654, 2662, 2672, 2674, 2677,
|
|
+ 880, 884, 885, 886, 890, 891, 894, 895, 900, 902,
|
|
+ 903, 904, 908, 910, 912, 913, 931, 940, 975, 977,
|
|
+ 978, 981, 984, 1008, 1012, 1014, 1015, 1018, 1020, 1021,
|
|
+ 1072, 1120, 1154, 1155, 1160, 1162, 1217, 1231, 1232, 1329,
|
|
+ 1369, 1370, 1376, 1417, 1418, 1421, 1423, 1425, 1470, 1471,
|
|
+ 1472, 1473, 1475, 1476, 1478, 1479, 1488, 1519, 1523, 1536,
|
|
+ 1542, 1545, 1547, 1548, 1550, 1552, 1563, 1564, 1566, 1568,
|
|
+ 1600, 1601, 1611, 1632, 1642, 1646, 1648, 1649, 1748, 1749,
|
|
+ 1750, 1757, 1758, 1759, 1765, 1767, 1769, 1770, 1774, 1776,
|
|
+ 1786, 1789, 1791, 1792, 1807, 1808, 1809, 1810, 1840, 1869,
|
|
+ 1958, 1969, 1984, 1994, 2027, 2036, 2038, 2039, 2042, 2045,
|
|
+ 2046, 2048, 2070, 2074, 2075, 2084, 2085, 2088, 2089, 2096,
|
|
+ 2112, 2137, 2142, 2144, 2208, 2230, 2259, 2274, 2275, 2307,
|
|
+ 2308, 2362, 2363, 2364, 2365, 2366, 2369, 2377, 2381, 2382,
|
|
+ 2384, 2385, 2392, 2402, 2404, 2406, 2416, 2417, 2418, 2433,
|
|
+ 2434, 2437, 2447, 2451, 2474, 2482, 2486, 2492, 2493, 2494,
|
|
+ 2497, 2503, 2507, 2509, 2510, 2519, 2524, 2527, 2530, 2534,
|
|
+ 2544, 2546, 2548, 2554, 2555, 2556, 2557, 2558, 2561, 2563,
|
|
+ 2565, 2575, 2579, 2602, 2610, 2613, 2616, 2620, 2622, 2625,
|
|
+ 2631, 2635, 2641, 2649, 2654, 2662, 2672, 2674, 2677, 2678,
|
|
2689, 2691, 2693, 2703, 2707, 2730, 2738, 2741, 2748, 2749,
|
|
2750, 2753, 2759, 2761, 2763, 2765, 2768, 2784, 2786, 2790,
|
|
- 2800, 2801, 2817, 2818, 2821, 2831, 2835, 2858, 2866, 2869,
|
|
- 2876, 2877, 2878, 2879, 2880, 2881, 2887, 2891, 2893, 2902,
|
|
- 2903, 2908, 2911, 2914, 2918, 2928, 2929, 2930, 2946, 2947,
|
|
- 2949, 2958, 2962, 2969, 2972, 2974, 2979, 2984, 2990, 3006,
|
|
- 3008, 3009, 3014, 3018, 3021, 3024, 3031, 3046, 3056, 3059,
|
|
- 3065, 3066, 3073, 3077, 3086, 3090, 3114, 3125, 3133, 3134,
|
|
- 3137, 3142, 3146, 3157, 3160, 3168, 3170, 3174, 3192, 3199,
|
|
- 3202, 3205, 3214, 3218, 3242, 3253, 3260, 3261, 3262, 3263,
|
|
- 3264, 3270, 3271, 3274, 3276, 3285, 3294, 3296, 3298, 3302,
|
|
- 3313, 3330, 3333, 3342, 3346, 3389, 3390, 3393, 3398, 3402,
|
|
- 3405, 3406, 3415, 3424, 3426, 3430, 3440, 3449, 3450, 3458,
|
|
- 3461, 3482, 3507, 3517, 3520, 3530, 3535, 3538, 3542, 3544,
|
|
- 3570, 3572, 3585, 3633, 3634, 3636, 3647, 3648, 3654, 3655,
|
|
- 3663, 3664, 3674, 3713, 3716, 3719, 3722, 3725, 3732, 3737,
|
|
- 3745, 3749, 3751, 3754, 3757, 3761, 3762, 3764, 3771, 3773,
|
|
- 3776, 3782, 3784, 3792, 3804, 3840, 3841, 3844, 3859, 3860,
|
|
- 3861, 3864, 3866, 3872, 3882, 3892, 3893, 3894, 3895, 3896,
|
|
- 3897, 3898, 3899, 3900, 3901, 3902, 3904, 3913, 3953, 3967,
|
|
- 3968, 3973, 3974, 3976, 3981, 3993, 4030, 4038, 4039, 4046,
|
|
- 4048, 4053, 4057, 4096, 4139, 4141, 4145, 4146, 4152, 4153,
|
|
- 4155, 4157, 4159, 4160, 4170, 4176, 4182, 4184, 4186, 4190,
|
|
- 4193, 4194, 4197, 4199, 4206, 4209, 4213, 4226, 4227, 4229,
|
|
- 4231, 4237, 4238, 4239, 4240, 4250, 4253, 4254, 4256, 4295,
|
|
- 4301, 4304, 4347, 4348, 4349, 4682, 4688, 4696, 4698, 4704,
|
|
- 4746, 4752, 4786, 4792, 4800, 4802, 4808, 4824, 4882, 4888,
|
|
- 4957, 4960, 4969, 4992, 5008, 5024, 5120, 5121, 5741, 5743,
|
|
- 5760, 5761, 5787, 5788, 5792, 5867, 5870, 5888, 5902, 5906,
|
|
+ 2800, 2801, 2809, 2810, 2817, 2818, 2821, 2831, 2835, 2858,
|
|
+ 2866, 2869, 2876, 2877, 2878, 2879, 2880, 2881, 2887, 2891,
|
|
+ 2893, 2902, 2903, 2908, 2911, 2914, 2918, 2928, 2929, 2930,
|
|
+ 2946, 2947, 2949, 2958, 2962, 2969, 2972, 2974, 2979, 2984,
|
|
+ 2990, 3006, 3008, 3009, 3014, 3018, 3021, 3024, 3031, 3046,
|
|
+ 3056, 3059, 3065, 3066, 3072, 3073, 3076, 3077, 3086, 3090,
|
|
+ 3114, 3133, 3134, 3137, 3142, 3146, 3157, 3160, 3168, 3170,
|
|
+ 3174, 3191, 3192, 3199, 3200, 3201, 3202, 3204, 3205, 3214,
|
|
+ 3218, 3242, 3253, 3260, 3261, 3262, 3263, 3264, 3270, 3271,
|
|
+ 3274, 3276, 3285, 3294, 3296, 3298, 3302, 3313, 3328, 3330,
|
|
+ 3333, 3342, 3346, 3387, 3389, 3390, 3393, 3398, 3402, 3405,
|
|
+ 3406, 3407, 3412, 3415, 3416, 3423, 3426, 3430, 3440, 3449,
|
|
+ 3450, 3458, 3461, 3482, 3507, 3517, 3520, 3530, 3535, 3538,
|
|
+ 3542, 3544, 3558, 3570, 3572, 3585, 3633, 3634, 3636, 3647,
|
|
+ 3648, 3654, 3655, 3663, 3664, 3674, 3713, 3716, 3718, 3724,
|
|
+ 3749, 3751, 3761, 3762, 3764, 3773, 3776, 3782, 3784, 3792,
|
|
+ 3804, 3840, 3841, 3844, 3859, 3860, 3861, 3864, 3866, 3872,
|
|
+ 3882, 3892, 3893, 3894, 3895, 3896, 3897, 3898, 3899, 3900,
|
|
+ 3901, 3902, 3904, 3913, 3953, 3967, 3968, 3973, 3974, 3976,
|
|
+ 3981, 3993, 4030, 4038, 4039, 4046, 4048, 4053, 4057, 4096,
|
|
+ 4139, 4141, 4145, 4146, 4152, 4153, 4155, 4157, 4159, 4160,
|
|
+ 4170, 4176, 4182, 4184, 4186, 4190, 4193, 4194, 4197, 4199,
|
|
+ 4206, 4209, 4213, 4226, 4227, 4229, 4231, 4237, 4238, 4239,
|
|
+ 4240, 4250, 4253, 4254, 4256, 4295, 4301, 4304, 4347, 4348,
|
|
+ 4349, 4352, 4682, 4688, 4696, 4698, 4704, 4746, 4752, 4786,
|
|
+ 4792, 4800, 4802, 4808, 4824, 4882, 4888, 4957, 4960, 4969,
|
|
+ 4992, 5008, 5024, 5112, 5120, 5121, 5741, 5742, 5743, 5760,
|
|
+ 5761, 5787, 5788, 5792, 5867, 5870, 5873, 5888, 5902, 5906,
|
|
5920, 5938, 5941, 5952, 5970, 5984, 5998, 6002, 6016, 6068,
|
|
6070, 6071, 6078, 6086, 6087, 6089, 6100, 6103, 6104, 6107,
|
|
6108, 6109, 6112, 6128, 6144, 6150, 6151, 6155, 6158, 6160,
|
|
- 6176, 6211, 6212, 6272, 6313, 6314, 6320, 6400, 6432, 6435,
|
|
- 6439, 6441, 6448, 6450, 6451, 6457, 6464, 6468, 6470, 6480,
|
|
- 6512, 6528, 6576, 6593, 6600, 6608, 6618, 6622, 6656, 6679,
|
|
- 6681, 6686, 6688, 6741, 6742, 6743, 6744, 6752, 6753, 6754,
|
|
- 6755, 6757, 6765, 6771, 6783, 6784, 6800, 6816, 6823, 6824,
|
|
- 6912, 6916, 6917, 6964, 6965, 6966, 6971, 6972, 6973, 6978,
|
|
- 6979, 6981, 6992, 7002, 7009, 7019, 7028, 7040, 7042, 7043,
|
|
- 7073, 7074, 7078, 7080, 7082, 7083, 7084, 7086, 7088, 7098,
|
|
- 7142, 7143, 7144, 7146, 7149, 7150, 7151, 7154, 7164, 7168,
|
|
- 7204, 7212, 7220, 7222, 7227, 7232, 7245, 7248, 7258, 7288,
|
|
- 7294, 7360, 7376, 7379, 7380, 7393, 7394, 7401, 7405, 7406,
|
|
- 7410, 7412, 7413, 7424, 7468, 7531, 7544, 7545, 7579, 7616,
|
|
- 7676, 7680, 7830, 7838, 7936, 7944, 7952, 7960, 7968, 7976,
|
|
- 7984, 7992, 8000, 8008, 8016, 8025, 8027, 8029, 8031, 8033,
|
|
- 8040, 8048, 8064, 8072, 8080, 8088, 8096, 8104, 8112, 8118,
|
|
- 8120, 8124, 8125, 8126, 8127, 8130, 8134, 8136, 8140, 8141,
|
|
- 8144, 8150, 8152, 8157, 8160, 8168, 8173, 8178, 8182, 8184,
|
|
- 8188, 8189, 8192, 8203, 8208, 8214, 8216, 8217, 8218, 8219,
|
|
- 8221, 8222, 8223, 8224, 8232, 8233, 8234, 8239, 8240, 8249,
|
|
- 8250, 8251, 8255, 8257, 8260, 8261, 8262, 8263, 8274, 8275,
|
|
- 8276, 8277, 8287, 8288, 8298, 8304, 8305, 8308, 8314, 8317,
|
|
- 8318, 8319, 8320, 8330, 8333, 8334, 8336, 8352, 8400, 8413,
|
|
- 8417, 8418, 8421, 8448, 8450, 8451, 8455, 8456, 8458, 8459,
|
|
- 8462, 8464, 8467, 8468, 8469, 8470, 8472, 8473, 8478, 8484,
|
|
- 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8496, 8500,
|
|
- 8501, 8505, 8506, 8508, 8510, 8512, 8517, 8519, 8522, 8523,
|
|
- 8524, 8526, 8527, 8528, 8544, 8579, 8581, 8585, 8592, 8597,
|
|
- 8602, 8604, 8608, 8609, 8611, 8612, 8614, 8615, 8622, 8623,
|
|
- 8654, 8656, 8658, 8659, 8660, 8661, 8692, 8960, 8968, 8972,
|
|
- 8992, 8994, 9001, 9002, 9003, 9084, 9085, 9115, 9140, 9180,
|
|
- 9186, 9216, 9280, 9312, 9372, 9450, 9472, 9655, 9656, 9665,
|
|
- 9666, 9720, 9728, 9839, 9840, 9985, 10088, 10089, 10090, 10091,
|
|
- 10092, 10093, 10094, 10095, 10096, 10097, 10098, 10099, 10100, 10101,
|
|
- 10102, 10132, 10176, 10181, 10182, 10183, 10214, 10215, 10216, 10217,
|
|
- 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10240, 10496, 10627,
|
|
- 10628, 10629, 10630, 10631, 10632, 10633, 10634, 10635, 10636, 10637,
|
|
- 10638, 10639, 10640, 10641, 10642, 10643, 10644, 10645, 10646, 10647,
|
|
- 10648, 10649, 10712, 10713, 10714, 10715, 10716, 10748, 10749, 10750,
|
|
- 11008, 11056, 11077, 11079, 11088, 11264, 11312, 11360, 11363, 11365,
|
|
- 11367, 11374, 11377, 11378, 11380, 11381, 11383, 11388, 11390, 11393,
|
|
- 11394, 11492, 11493, 11499, 11503, 11506, 11513, 11517, 11518, 11520,
|
|
- 11559, 11565, 11568, 11631, 11632, 11647, 11648, 11680, 11688, 11696,
|
|
- 11704, 11712, 11720, 11728, 11736, 11744, 11776, 11778, 11779, 11780,
|
|
- 11781, 11782, 11785, 11786, 11787, 11788, 11789, 11790, 11799, 11800,
|
|
- 11802, 11803, 11804, 11805, 11806, 11808, 11809, 11810, 11811, 11812,
|
|
- 11813, 11814, 11815, 11816, 11817, 11818, 11823, 11824, 11834, 11904,
|
|
- 11931, 12032, 12272, 12288, 12289, 12292, 12293, 12294, 12295, 12296,
|
|
- 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12306,
|
|
- 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12317,
|
|
- 12318, 12320, 12321, 12330, 12334, 12336, 12337, 12342, 12344, 12347,
|
|
- 12348, 12349, 12350, 12353, 12441, 12443, 12445, 12447, 12448, 12449,
|
|
- 12539, 12540, 12543, 12549, 12593, 12688, 12690, 12694, 12704, 12736,
|
|
- 12784, 12800, 12832, 12842, 12872, 12880, 12881, 12896, 12928, 12938,
|
|
- 12977, 12992, 13056, 13312, 19893, 19904, 19968, 40908, 40960, 40981,
|
|
- 40982, 42128, 42192, 42232, 42238, 42240, 42508, 42509, 42512, 42528,
|
|
- 42538, 42560, 42606, 42607, 42608, 42611, 42612, 42622, 42623, 42624,
|
|
- 42655, 42656, 42726, 42736, 42738, 42752, 42775, 42784, 42786, 42800,
|
|
- 42802, 42864, 42865, 42873, 42878, 42888, 42889, 42891, 42896, 42912,
|
|
- 43000, 43002, 43003, 43010, 43011, 43014, 43015, 43019, 43020, 43043,
|
|
- 43045, 43047, 43048, 43056, 43062, 43064, 43065, 43072, 43124, 43136,
|
|
- 43138, 43188, 43204, 43214, 43216, 43232, 43250, 43256, 43259, 43264,
|
|
- 43274, 43302, 43310, 43312, 43335, 43346, 43359, 43360, 43392, 43395,
|
|
- 43396, 43443, 43444, 43446, 43450, 43452, 43453, 43457, 43471, 43472,
|
|
- 43486, 43520, 43561, 43567, 43569, 43571, 43573, 43584, 43587, 43588,
|
|
- 43596, 43597, 43600, 43612, 43616, 43632, 43633, 43639, 43642, 43643,
|
|
- 43648, 43696, 43697, 43698, 43701, 43703, 43705, 43710, 43712, 43713,
|
|
- 43714, 43739, 43741, 43742, 43744, 43755, 43756, 43758, 43760, 43762,
|
|
- 43763, 43765, 43766, 43777, 43785, 43793, 43808, 43816, 43968, 44003,
|
|
- 44005, 44006, 44008, 44009, 44011, 44012, 44013, 44016, 44032, 55203,
|
|
- 55216, 55243, 55296, 56191, 56319, 57343, 57344, 63743, 63744, 64112,
|
|
- 64256, 64275, 64285, 64286, 64287, 64297, 64298, 64312, 64318, 64320,
|
|
- 64323, 64326, 64434, 64467, 64830, 64831, 64848, 64914, 65008, 65020,
|
|
- 65021, 65024, 65040, 65047, 65048, 65049, 65056, 65072, 65073, 65075,
|
|
- 65077, 65078, 65079, 65080, 65081, 65082, 65083, 65084, 65085, 65086,
|
|
- 65087, 65088, 65089, 65090, 65091, 65092, 65093, 65095, 65096, 65097,
|
|
- 65101, 65104, 65108, 65112, 65113, 65114, 65115, 65116, 65117, 65118,
|
|
- 65119, 65122, 65123, 65124, 65128, 65129, 65130, 65136, 65142, 65279,
|
|
- 65281, 65284, 65285, 65288, 65289, 65290, 65291, 65292, 65293, 65294,
|
|
- 65296, 65306, 65308, 65311, 65313, 65339, 65340, 65341, 65342, 65343,
|
|
- 65344, 65345, 65371, 65372, 65373, 65374, 65375, 65376, 65377, 65378,
|
|
- 65379, 65380, 65382, 65392, 65393, 65438, 65440, 65474, 65482, 65490,
|
|
- 65498, 65504, 65506, 65507, 65508, 65509, 65512, 65513, 65517, 65529,
|
|
- 65532, 0, 13, 40, 60, 63, 80, 128, 256, 263,
|
|
- 311, 320, 373, 377, 394, 400, 464, 509, 640, 672,
|
|
- 768, 800, 816, 833, 834, 842, 896, 927, 928, 968,
|
|
- 976, 977, 1024, 1064, 1104, 1184, 2048, 2056, 2058, 2103,
|
|
- 2108, 2111, 2135, 2136, 2304, 2326, 2335, 2336, 2367, 2432,
|
|
- 2494, 2560, 2561, 2565, 2572, 2576, 2581, 2585, 2616, 2623,
|
|
- 2624, 2640, 2656, 2685, 2687, 2816, 2873, 2880, 2904, 2912,
|
|
- 2936, 3072, 3680, 4096, 4097, 4098, 4099, 4152, 4167, 4178,
|
|
- 4198, 4224, 4226, 4227, 4272, 4275, 4279, 4281, 4283, 4285,
|
|
- 4286, 4304, 4336, 4352, 4355, 4391, 4396, 4397, 4406, 4416,
|
|
- 4480, 4482, 4483, 4531, 4534, 4543, 4545, 4549, 4560, 5760,
|
|
- 5803, 5804, 5805, 5806, 5808, 5814, 5815, 5824, 8192, 9216,
|
|
- 9328, 12288, 26624, 28416, 28496, 28497, 28559, 28563, 45056, 53248,
|
|
- 53504, 53545, 53605, 53607, 53610, 53613, 53619, 53627, 53635, 53637,
|
|
- 53644, 53674, 53678, 53760, 53826, 53829, 54016, 54112, 54272, 54298,
|
|
- 54324, 54350, 54358, 54376, 54402, 54428, 54430, 54434, 54437, 54441,
|
|
- 54446, 54454, 54459, 54461, 54469, 54480, 54506, 54532, 54535, 54541,
|
|
- 54550, 54558, 54584, 54587, 54592, 54598, 54602, 54610, 54636, 54662,
|
|
- 54688, 54714, 54740, 54766, 54792, 54818, 54844, 54870, 54896, 54922,
|
|
- 54952, 54977, 54978, 55003, 55004, 55010, 55035, 55036, 55061, 55062,
|
|
- 55068, 55093, 55094, 55119, 55120, 55126, 55151, 55152, 55177, 55178,
|
|
- 55184, 55209, 55210, 55235, 55236, 55242, 55246, 60928, 60933, 60961,
|
|
- 60964, 60967, 60969, 60980, 60985, 60987, 60994, 60999, 61001, 61003,
|
|
- 61005, 61009, 61012, 61015, 61017, 61019, 61021, 61023, 61025, 61028,
|
|
- 61031, 61036, 61044, 61049, 61054, 61056, 61067, 61089, 61093, 61099,
|
|
- 61168, 61440, 61488, 61600, 61617, 61633, 61649, 61696, 61712, 61744,
|
|
- 61808, 61926, 61968, 62016, 62032, 62208, 62256, 62263, 62336, 62368,
|
|
- 62406, 62432, 62464, 62528, 62530, 62713, 62720, 62784, 62800, 62971,
|
|
- 63045, 63104, 63232, 0, 42710, 42752, 46900, 46912, 47133, 63488,
|
|
- 1, 32, 256, 0, 65533,
|
|
+ 6176, 6211, 6212, 6272, 6277, 6279, 6313, 6314, 6320, 6400,
|
|
+ 6432, 6435, 6439, 6441, 6448, 6450, 6451, 6457, 6464, 6468,
|
|
+ 6470, 6480, 6512, 6528, 6576, 6608, 6618, 6622, 6656, 6679,
|
|
+ 6681, 6683, 6686, 6688, 6741, 6742, 6743, 6744, 6752, 6753,
|
|
+ 6754, 6755, 6757, 6765, 6771, 6783, 6784, 6800, 6816, 6823,
|
|
+ 6824, 6832, 6846, 6912, 6916, 6917, 6964, 6965, 6966, 6971,
|
|
+ 6972, 6973, 6978, 6979, 6981, 6992, 7002, 7009, 7019, 7028,
|
|
+ 7040, 7042, 7043, 7073, 7074, 7078, 7080, 7082, 7083, 7086,
|
|
+ 7088, 7098, 7142, 7143, 7144, 7146, 7149, 7150, 7151, 7154,
|
|
+ 7164, 7168, 7204, 7212, 7220, 7222, 7227, 7232, 7245, 7248,
|
|
+ 7258, 7288, 7294, 7296, 7312, 7357, 7360, 7376, 7379, 7380,
|
|
+ 7393, 7394, 7401, 7405, 7406, 7412, 7413, 7415, 7416, 7418,
|
|
+ 7424, 7468, 7531, 7544, 7545, 7579, 7616, 7675, 7680, 7830,
|
|
+ 7838, 7936, 7944, 7952, 7960, 7968, 7976, 7984, 7992, 8000,
|
|
+ 8008, 8016, 8025, 8027, 8029, 8031, 8033, 8040, 8048, 8064,
|
|
+ 8072, 8080, 8088, 8096, 8104, 8112, 8118, 8120, 8124, 8125,
|
|
+ 8126, 8127, 8130, 8134, 8136, 8140, 8141, 8144, 8150, 8152,
|
|
+ 8157, 8160, 8168, 8173, 8178, 8182, 8184, 8188, 8189, 8192,
|
|
+ 8203, 8208, 8214, 8216, 8217, 8218, 8219, 8221, 8222, 8223,
|
|
+ 8224, 8232, 8233, 8234, 8239, 8240, 8249, 8250, 8251, 8255,
|
|
+ 8257, 8260, 8261, 8262, 8263, 8274, 8275, 8276, 8277, 8287,
|
|
+ 8288, 8294, 8304, 8305, 8308, 8314, 8317, 8318, 8319, 8320,
|
|
+ 8330, 8333, 8334, 8336, 8352, 8400, 8413, 8417, 8418, 8421,
|
|
+ 8448, 8450, 8451, 8455, 8456, 8458, 8459, 8462, 8464, 8467,
|
|
+ 8468, 8469, 8470, 8472, 8473, 8478, 8484, 8485, 8486, 8487,
|
|
+ 8488, 8489, 8490, 8494, 8495, 8496, 8500, 8501, 8505, 8506,
|
|
+ 8508, 8510, 8512, 8517, 8519, 8522, 8523, 8524, 8526, 8527,
|
|
+ 8528, 8544, 8579, 8581, 8585, 8586, 8592, 8597, 8602, 8604,
|
|
+ 8608, 8609, 8611, 8612, 8614, 8615, 8622, 8623, 8654, 8656,
|
|
+ 8658, 8659, 8660, 8661, 8692, 8960, 8968, 8969, 8970, 8971,
|
|
+ 8972, 8992, 8994, 9001, 9002, 9003, 9084, 9085, 9115, 9140,
|
|
+ 9180, 9186, 9280, 9312, 9372, 9450, 9472, 9655, 9656, 9665,
|
|
+ 9666, 9720, 9728, 9839, 9840, 10088, 10089, 10090, 10091, 10092,
|
|
+ 10093, 10094, 10095, 10096, 10097, 10098, 10099, 10100, 10101, 10102,
|
|
+ 10132, 10176, 10181, 10182, 10183, 10214, 10215, 10216, 10217, 10218,
|
|
+ 10219, 10220, 10221, 10222, 10223, 10224, 10240, 10496, 10627, 10628,
|
|
+ 10629, 10630, 10631, 10632, 10633, 10634, 10635, 10636, 10637, 10638,
|
|
+ 10639, 10640, 10641, 10642, 10643, 10644, 10645, 10646, 10647, 10648,
|
|
+ 10649, 10712, 10713, 10714, 10715, 10716, 10748, 10749, 10750, 11008,
|
|
+ 11056, 11077, 11079, 11085, 11126, 11160, 11264, 11312, 11360, 11363,
|
|
+ 11365, 11367, 11374, 11377, 11378, 11380, 11381, 11383, 11388, 11390,
|
|
+ 11393, 11394, 11492, 11493, 11499, 11503, 11506, 11513, 11517, 11518,
|
|
+ 11520, 11559, 11565, 11568, 11631, 11632, 11647, 11648, 11680, 11688,
|
|
+ 11696, 11704, 11712, 11720, 11728, 11736, 11744, 11776, 11778, 11779,
|
|
+ 11780, 11781, 11782, 11785, 11786, 11787, 11788, 11789, 11790, 11799,
|
|
+ 11800, 11802, 11803, 11804, 11805, 11806, 11808, 11809, 11810, 11811,
|
|
+ 11812, 11813, 11814, 11815, 11816, 11817, 11818, 11823, 11824, 11834,
|
|
+ 11836, 11840, 11841, 11842, 11843, 11904, 11931, 12032, 12272, 12288,
|
|
+ 12289, 12292, 12293, 12294, 12295, 12296, 12297, 12298, 12299, 12300,
|
|
+ 12301, 12302, 12303, 12304, 12305, 12306, 12308, 12309, 12310, 12311,
|
|
+ 12312, 12313, 12314, 12315, 12316, 12317, 12318, 12320, 12321, 12330,
|
|
+ 12334, 12336, 12337, 12342, 12344, 12347, 12348, 12349, 12350, 12353,
|
|
+ 12441, 12443, 12445, 12447, 12448, 12449, 12539, 12540, 12543, 12549,
|
|
+ 12593, 12688, 12690, 12694, 12704, 12736, 12784, 12800, 12832, 12842,
|
|
+ 12872, 12880, 12881, 12896, 12928, 12938, 12977, 12992, 13312, 19893,
|
|
+ 19904, 19968, 40943, 40960, 40981, 40982, 42128, 42192, 42232, 42238,
|
|
+ 42240, 42508, 42509, 42512, 42528, 42538, 42560, 42606, 42607, 42608,
|
|
+ 42611, 42612, 42622, 42623, 42624, 42652, 42654, 42656, 42726, 42736,
|
|
+ 42738, 42752, 42775, 42784, 42786, 42800, 42802, 42864, 42865, 42873,
|
|
+ 42878, 42888, 42889, 42891, 42895, 42896, 42900, 42902, 42923, 42927,
|
|
+ 42928, 42933, 42934, 42946, 42949, 42999, 43000, 43002, 43003, 43010,
|
|
+ 43011, 43014, 43015, 43019, 43020, 43043, 43045, 43047, 43048, 43056,
|
|
+ 43062, 43064, 43065, 43072, 43124, 43136, 43138, 43188, 43204, 43214,
|
|
+ 43216, 43232, 43250, 43256, 43259, 43260, 43261, 43263, 43264, 43274,
|
|
+ 43302, 43310, 43312, 43335, 43346, 43359, 43360, 43392, 43395, 43396,
|
|
+ 43443, 43444, 43446, 43450, 43452, 43454, 43457, 43471, 43472, 43486,
|
|
+ 43488, 43493, 43494, 43495, 43504, 43514, 43520, 43561, 43567, 43569,
|
|
+ 43571, 43573, 43584, 43587, 43588, 43596, 43597, 43600, 43612, 43616,
|
|
+ 43632, 43633, 43639, 43642, 43643, 43644, 43645, 43646, 43696, 43697,
|
|
+ 43698, 43701, 43703, 43705, 43710, 43712, 43713, 43714, 43739, 43741,
|
|
+ 43742, 43744, 43755, 43756, 43758, 43760, 43762, 43763, 43765, 43766,
|
|
+ 43777, 43785, 43793, 43808, 43816, 43824, 43867, 43868, 43872, 43888,
|
|
+ 43968, 44003, 44005, 44006, 44008, 44009, 44011, 44012, 44013, 44016,
|
|
+ 44032, 55203, 55216, 55243, 55296, 56191, 56319, 57343, 57344, 63743,
|
|
+ 63744, 64112, 64256, 64275, 64285, 64286, 64287, 64297, 64298, 64312,
|
|
+ 64318, 64320, 64323, 64326, 64434, 64467, 64830, 64831, 64848, 64914,
|
|
+ 65008, 65020, 65021, 65024, 65040, 65047, 65048, 65049, 65056, 65072,
|
|
+ 65073, 65075, 65077, 65078, 65079, 65080, 65081, 65082, 65083, 65084,
|
|
+ 65085, 65086, 65087, 65088, 65089, 65090, 65091, 65092, 65093, 65095,
|
|
+ 65096, 65097, 65101, 65104, 65108, 65112, 65113, 65114, 65115, 65116,
|
|
+ 65117, 65118, 65119, 65122, 65123, 65124, 65128, 65129, 65130, 65136,
|
|
+ 65142, 65279, 65281, 65284, 65285, 65288, 65289, 65290, 65291, 65292,
|
|
+ 65293, 65294, 65296, 65306, 65308, 65311, 65313, 65339, 65340, 65341,
|
|
+ 65342, 65343, 65344, 65345, 65371, 65372, 65373, 65374, 65375, 65376,
|
|
+ 65377, 65378, 65379, 65380, 65382, 65392, 65393, 65438, 65440, 65474,
|
|
+ 65482, 65490, 65498, 65504, 65506, 65507, 65508, 65509, 65512, 65513,
|
|
+ 65517, 65529, 65532, 0, 13, 40, 60, 63, 80, 128,
|
|
+ 256, 263, 311, 320, 373, 377, 394, 396, 400, 416,
|
|
+ 464, 509, 640, 672, 736, 737, 768, 800, 813, 833,
|
|
+ 834, 842, 848, 886, 896, 927, 928, 968, 976, 977,
|
|
+ 1024, 1064, 1104, 1184, 1200, 1240, 1280, 1328, 1391, 1536,
|
|
+ 1856, 1888, 2048, 2056, 2058, 2103, 2108, 2111, 2135, 2136,
|
|
+ 2144, 2167, 2169, 2176, 2215, 2272, 2292, 2299, 2304, 2326,
|
|
+ 2335, 2336, 2367, 2432, 2492, 2494, 2496, 2514, 2560, 2561,
|
|
+ 2565, 2572, 2576, 2581, 2585, 2616, 2623, 2624, 2640, 2656,
|
|
+ 2685, 2687, 2688, 2717, 2752, 2760, 2761, 2789, 2795, 2800,
|
|
+ 2816, 2873, 2880, 2904, 2912, 2936, 2944, 2969, 2985, 3072,
|
|
+ 3200, 3264, 3322, 3328, 3364, 3376, 3680, 3840, 3869, 3879,
|
|
+ 3888, 3910, 3921, 3925, 4064, 4096, 4097, 4098, 4099, 4152,
|
|
+ 4167, 4178, 4198, 4223, 4226, 4227, 4272, 4275, 4279, 4281,
|
|
+ 4283, 4285, 4286, 4301, 4304, 4336, 4352, 4355, 4391, 4396,
|
|
+ 4397, 4406, 4416, 4420, 4421, 4432, 4467, 4468, 4470, 4480,
|
|
+ 4482, 4483, 4531, 4534, 4543, 4545, 4549, 4553, 4557, 4560,
|
|
+ 4570, 4571, 4572, 4573, 4577, 4608, 4627, 4652, 4655, 4658,
|
|
+ 4660, 4661, 4662, 4664, 4670, 4736, 4744, 4746, 4751, 4767,
|
|
+ 4777, 4784, 4831, 4832, 4835, 4848, 4864, 4866, 4869, 4879,
|
|
+ 4883, 4906, 4914, 4917, 4923, 4925, 4926, 4928, 4929, 4935,
|
|
+ 4939, 4944, 4951, 4957, 4962, 4966, 4976, 5120, 5173, 5176,
|
|
+ 5184, 5186, 5189, 5190, 5191, 5195, 5200, 5211, 5213, 5214,
|
|
+ 5215, 5248, 5296, 5299, 5305, 5306, 5307, 5311, 5313, 5314,
|
|
+ 5316, 5318, 5319, 5328, 5504, 5551, 5554, 5560, 5564, 5566,
|
|
+ 5567, 5569, 5592, 5596, 5632, 5680, 5683, 5691, 5693, 5694,
|
|
+ 5695, 5697, 5700, 5712, 5728, 5760, 5803, 5804, 5805, 5806,
|
|
+ 5808, 5814, 5815, 5816, 5824, 5888, 5917, 5920, 5922, 5926,
|
|
+ 5927, 5936, 5946, 5948, 5951, 6144, 6188, 6191, 6200, 6201,
|
|
+ 6203, 6304, 6336, 6368, 6378, 6399, 6560, 6570, 6609, 6612,
|
|
+ 6618, 6620, 6624, 6625, 6626, 6627, 6628, 6656, 6657, 6667,
|
|
+ 6707, 6713, 6714, 6715, 6719, 6727, 6736, 6737, 6743, 6745,
|
|
+ 6748, 6794, 6807, 6808, 6810, 6813, 6814, 6848, 7168, 7178,
|
|
+ 7215, 7216, 7224, 7230, 7231, 7232, 7233, 7248, 7258, 7280,
|
|
+ 7282, 7314, 7337, 7338, 7345, 7346, 7348, 7349, 7424, 7432,
|
|
+ 7435, 7473, 7482, 7484, 7487, 7494, 7495, 7504, 7520, 7527,
|
|
+ 7530, 7562, 7568, 7571, 7573, 7574, 7575, 7576, 7584, 7904,
|
|
+ 7923, 7925, 7927, 8128, 8149, 8157, 8161, 8191, 8192, 9216,
|
|
+ 9328, 9344, 12288, 13360, 17408, 26624, 27200, 27232, 27246, 27344,
|
|
+ 27376, 27381, 27392, 27440, 27447, 27452, 27456, 27460, 27461, 27472,
|
|
+ 27483, 27491, 27517, 28224, 28256, 28288, 28311, 28416, 28495, 28496,
|
|
+ 28497, 28559, 28563, 28640, 28642, 28643, 28672, 34807, 34816, 45056,
|
|
+ 45392, 45412, 45424, 48128, 48240, 48256, 48272, 48284, 48285, 48287,
|
|
+ 48288, 53248, 53504, 53545, 53605, 53607, 53610, 53613, 53619, 53627,
|
|
+ 53635, 53637, 53644, 53674, 53678, 53760, 53826, 53829, 53984, 54016,
|
|
+ 54112, 54272, 54298, 54324, 54350, 54358, 54376, 54402, 54428, 54430,
|
|
+ 54434, 54437, 54441, 54446, 54454, 54459, 54461, 54469, 54480, 54506,
|
|
+ 54532, 54535, 54541, 54550, 54558, 54584, 54587, 54592, 54598, 54602,
|
|
+ 54610, 54636, 54662, 54688, 54714, 54740, 54766, 54792, 54818, 54844,
|
|
+ 54870, 54896, 54922, 54952, 54977, 54978, 55003, 55004, 55010, 55035,
|
|
+ 55036, 55061, 55062, 55068, 55093, 55094, 55119, 55120, 55126, 55151,
|
|
+ 55152, 55177, 55178, 55184, 55209, 55210, 55235, 55236, 55242, 55246,
|
|
+ 55296, 55808, 55863, 55867, 55917, 55925, 55926, 55940, 55941, 55943,
|
|
+ 55963, 55969, 57344, 57352, 57371, 57379, 57382, 57600, 57648, 57655,
|
|
+ 57664, 57678, 57679, 58048, 58092, 58096, 58111, 59392, 59591, 59600,
|
|
+ 59648, 59682, 59716, 59723, 59728, 59742, 60529, 60588, 60589, 60592,
|
|
+ 60593, 60673, 60718, 60719, 60928, 60933, 60961, 60964, 60967, 60969,
|
|
+ 60980, 60985, 60987, 60994, 60999, 61001, 61003, 61005, 61009, 61012,
|
|
+ 61015, 61017, 61019, 61021, 61023, 61025, 61028, 61031, 61036, 61044,
|
|
+ 61049, 61054, 61056, 61067, 61089, 61093, 61099, 61168, 61440, 61488,
|
|
+ 61600, 61617, 61633, 61649, 61696, 61712, 61808, 61926, 61968, 62016,
|
|
+ 62032, 62048, 62208, 62459, 62464, 63200, 63216, 63232, 63360, 63456,
|
|
+ 63488, 63504, 63568, 63584, 63632, 63744, 63757, 63859, 63866, 63909,
|
|
+ 63918, 63949, 64096, 64112, 64120, 64128, 64144, 0, 42710, 42752,
|
|
+ 46900, 46912, 47133, 47136, 52897, 52912, 60384, 63488, 1, 32,
|
|
+ 256, 0, 65533,
|
|
};
|
|
-static u16 aFts5UnicodeData[] = {
|
|
+static const u16 aFts5UnicodeData[] = {
|
|
1025, 61, 117, 55, 117, 54, 50, 53, 57, 53,
|
|
49, 85, 333, 85, 121, 85, 841, 54, 53, 50,
|
|
56, 48, 56, 837, 54, 57, 50, 57, 1057, 61,
|
|
@@ -221233,169 +222383,211 @@ static u16 aFts5UnicodeData[] = {
|
|
41, 40, 37, 126, 73, 37, 1886, 197, 73, 37,
|
|
73, 69, 126, 105, 37, 286, 2181, 39, 869, 582,
|
|
152, 390, 472, 166, 248, 38, 56, 38, 568, 3596,
|
|
- 158, 38, 56, 94, 38, 101, 53, 88, 41, 53,
|
|
- 105, 41, 73, 37, 553, 297, 1125, 94, 37, 105,
|
|
- 101, 798, 133, 94, 57, 126, 94, 37, 1641, 1541,
|
|
- 1118, 58, 172, 75, 1790, 478, 37, 2846, 1225, 38,
|
|
- 213, 1253, 53, 49, 55, 1452, 49, 44, 53, 76,
|
|
- 53, 76, 53, 44, 871, 103, 85, 162, 121, 85,
|
|
- 55, 85, 90, 364, 53, 85, 1031, 38, 327, 684,
|
|
- 333, 149, 71, 44, 3175, 53, 39, 236, 34, 58,
|
|
- 204, 70, 76, 58, 140, 71, 333, 103, 90, 39,
|
|
- 469, 34, 39, 44, 967, 876, 2855, 364, 39, 333,
|
|
- 1063, 300, 70, 58, 117, 38, 711, 140, 38, 300,
|
|
- 38, 108, 38, 172, 501, 807, 108, 53, 39, 359,
|
|
- 876, 108, 42, 1735, 44, 42, 44, 39, 106, 268,
|
|
- 138, 44, 74, 39, 236, 327, 76, 85, 333, 53,
|
|
- 38, 199, 231, 44, 74, 263, 71, 711, 231, 39,
|
|
- 135, 44, 39, 106, 140, 74, 74, 44, 39, 42,
|
|
- 71, 103, 76, 333, 71, 87, 207, 58, 55, 76,
|
|
- 42, 199, 71, 711, 231, 71, 71, 71, 44, 106,
|
|
- 76, 76, 108, 44, 135, 39, 333, 76, 103, 44,
|
|
+ 158, 38, 56, 94, 38, 101, 53, 41, 88, 41,
|
|
+ 53, 105, 41, 73, 37, 553, 297, 1125, 94, 37,
|
|
+ 105, 101, 798, 133, 94, 57, 126, 94, 37, 1641,
|
|
+ 1541, 1118, 58, 172, 75, 1790, 478, 37, 3102, 1225,
|
|
+ 38, 213, 1317, 53, 49, 90, 55, 1452, 49, 44,
|
|
+ 53, 76, 53, 76, 53, 44, 871, 135, 85, 194,
|
|
+ 121, 85, 55, 85, 90, 364, 53, 34, 85, 1031,
|
|
+ 38, 327, 684, 333, 149, 71, 44, 3175, 53, 39,
|
|
+ 236, 34, 58, 204, 70, 76, 58, 140, 71, 333,
|
|
+ 103, 90, 39, 469, 34, 39, 44, 967, 876, 2855,
|
|
+ 364, 39, 333, 1063, 300, 70, 58, 117, 38, 44,
|
|
+ 87, 711, 140, 38, 300, 38, 108, 38, 172, 501,
|
|
+ 807, 108, 53, 359, 679, 263, 492, 34, 1036, 42,
|
|
+ 1735, 44, 42, 44, 39, 106, 268, 138, 44, 74,
|
|
+ 39, 236, 327, 76, 85, 333, 53, 38, 487, 44,
|
|
+ 74, 263, 71, 711, 231, 39, 135, 44, 39, 106,
|
|
+ 140, 74, 74, 44, 39, 42, 71, 103, 76, 333,
|
|
+ 71, 87, 207, 58, 55, 39, 53, 44, 76, 42,
|
|
+ 199, 71, 711, 231, 71, 71, 71, 44, 106, 76,
|
|
+ 76, 108, 44, 135, 39, 333, 76, 103, 44, 53,
|
|
76, 42, 295, 103, 711, 231, 71, 167, 44, 39,
|
|
106, 172, 76, 42, 74, 44, 39, 71, 76, 333,
|
|
- 53, 55, 44, 74, 263, 71, 711, 231, 71, 167,
|
|
- 44, 39, 42, 44, 42, 140, 74, 74, 44, 44,
|
|
- 42, 71, 103, 76, 333, 58, 39, 207, 44, 39,
|
|
- 199, 103, 135, 71, 39, 71, 71, 103, 391, 74,
|
|
- 44, 74, 106, 106, 44, 39, 42, 333, 111, 218,
|
|
- 55, 58, 106, 263, 103, 743, 327, 167, 39, 108,
|
|
- 138, 108, 140, 76, 71, 71, 76, 333, 239, 58,
|
|
- 74, 263, 103, 743, 327, 167, 44, 39, 42, 44,
|
|
- 170, 44, 74, 74, 76, 74, 39, 71, 76, 333,
|
|
- 71, 74, 263, 103, 1319, 39, 106, 140, 106, 106,
|
|
- 44, 39, 42, 71, 76, 333, 207, 58, 199, 74,
|
|
- 583, 775, 295, 39, 231, 44, 106, 108, 44, 266,
|
|
- 74, 53, 1543, 44, 71, 236, 55, 199, 38, 268,
|
|
- 53, 333, 85, 71, 39, 71, 39, 39, 135, 231,
|
|
- 103, 39, 39, 71, 135, 44, 71, 204, 76, 39,
|
|
- 167, 38, 204, 333, 135, 39, 122, 501, 58, 53,
|
|
- 122, 76, 218, 333, 335, 58, 44, 58, 44, 58,
|
|
- 44, 54, 50, 54, 50, 74, 263, 1159, 460, 42,
|
|
- 172, 53, 76, 167, 364, 1164, 282, 44, 218, 90,
|
|
- 181, 154, 85, 1383, 74, 140, 42, 204, 42, 76,
|
|
- 74, 76, 39, 333, 213, 199, 74, 76, 135, 108,
|
|
- 39, 106, 71, 234, 103, 140, 423, 44, 74, 76,
|
|
- 202, 44, 39, 42, 333, 106, 44, 90, 1225, 41,
|
|
- 41, 1383, 53, 38, 10631, 135, 231, 39, 135, 1319,
|
|
- 135, 1063, 135, 231, 39, 135, 487, 1831, 135, 2151,
|
|
- 108, 309, 655, 519, 346, 2727, 49, 19847, 85, 551,
|
|
- 61, 839, 54, 50, 2407, 117, 110, 423, 135, 108,
|
|
+ 53, 55, 39, 204, 44, 74, 263, 71, 711, 231,
|
|
+ 71, 167, 44, 39, 42, 44, 42, 140, 74, 74,
|
|
+ 44, 44, 42, 71, 103, 76, 333, 58, 39, 207,
|
|
+ 44, 39, 199, 103, 135, 71, 39, 71, 71, 103,
|
|
+ 391, 74, 44, 74, 106, 106, 44, 39, 42, 333,
|
|
+ 111, 218, 55, 58, 44, 106, 44, 263, 103, 743,
|
|
+ 519, 39, 108, 138, 108, 140, 76, 103, 71, 76,
|
|
+ 333, 53, 239, 58, 39, 44, 74, 53, 263, 103,
|
|
+ 743, 327, 167, 44, 39, 42, 44, 170, 44, 74,
|
|
+ 74, 76, 74, 39, 71, 76, 333, 71, 76, 74,
|
|
+ 263, 103, 1319, 76, 39, 106, 140, 106, 106, 44,
|
|
+ 39, 58, 103, 42, 239, 103, 76, 333, 303, 58,
|
|
+ 199, 74, 583, 775, 295, 39, 231, 44, 106, 108,
|
|
+ 44, 266, 333, 74, 53, 1543, 44, 71, 236, 55,
|
|
+ 199, 38, 268, 53, 333, 85, 71, 39, 167, 775,
|
|
+ 39, 327, 44, 71, 300, 39, 167, 38, 204, 333,
|
|
+ 135, 39, 122, 501, 58, 53, 122, 76, 218, 333,
|
|
+ 335, 58, 44, 58, 44, 58, 44, 54, 50, 54,
|
|
+ 50, 74, 263, 1159, 460, 42, 172, 53, 76, 167,
|
|
+ 364, 1164, 282, 44, 218, 90, 181, 154, 85, 1383,
|
|
+ 74, 140, 42, 204, 42, 76, 74, 76, 39, 333,
|
|
+ 213, 199, 74, 76, 135, 108, 39, 106, 71, 234,
|
|
+ 103, 140, 423, 44, 74, 76, 202, 44, 39, 42,
|
|
+ 333, 106, 44, 90, 1225, 41, 41, 1381, 53, 38,
|
|
+ 101, 10535, 135, 231, 39, 135, 1319, 135, 1063, 135,
|
|
+ 231, 39, 135, 487, 1831, 135, 2151, 108, 309, 655,
|
|
+ 519, 346, 2761, 197, 49, 19847, 58, 53, 551, 61,
|
|
+ 839, 54, 50, 2407, 117, 110, 263, 423, 135, 108,
|
|
583, 108, 85, 583, 76, 423, 103, 76, 1671, 76,
|
|
42, 236, 266, 44, 74, 364, 117, 38, 117, 55,
|
|
- 39, 44, 333, 335, 213, 49, 149, 108, 61, 333,
|
|
- 1127, 38, 1671, 1319, 44, 39, 2247, 935, 108, 138,
|
|
- 76, 106, 74, 44, 202, 108, 58, 85, 333, 967,
|
|
- 167, 1415, 554, 231, 74, 333, 47, 1114, 743, 76,
|
|
- 106, 85, 1703, 42, 44, 42, 236, 44, 42, 44,
|
|
- 74, 268, 202, 332, 44, 333, 333, 245, 38, 213,
|
|
- 140, 42, 1511, 44, 42, 172, 42, 44, 170, 44,
|
|
- 74, 231, 333, 245, 346, 300, 314, 76, 42, 967,
|
|
- 42, 140, 74, 76, 42, 44, 74, 71, 333, 1415,
|
|
- 44, 42, 76, 106, 44, 42, 108, 74, 149, 1159,
|
|
- 266, 268, 74, 76, 181, 333, 103, 333, 967, 198,
|
|
- 85, 277, 108, 53, 428, 42, 236, 135, 44, 135,
|
|
- 74, 44, 71, 1413, 2022, 421, 38, 1093, 1190, 1260,
|
|
- 140, 4830, 261, 3166, 261, 265, 197, 201, 261, 265,
|
|
- 261, 265, 197, 201, 261, 41, 41, 41, 94, 229,
|
|
- 265, 453, 261, 264, 261, 264, 261, 264, 165, 69,
|
|
- 137, 40, 56, 37, 120, 101, 69, 137, 40, 120,
|
|
- 133, 69, 137, 120, 261, 169, 120, 101, 69, 137,
|
|
- 40, 88, 381, 162, 209, 85, 52, 51, 54, 84,
|
|
- 51, 54, 52, 277, 59, 60, 162, 61, 309, 52,
|
|
- 51, 149, 80, 117, 57, 54, 50, 373, 57, 53,
|
|
- 48, 341, 61, 162, 194, 47, 38, 207, 121, 54,
|
|
- 50, 38, 335, 121, 54, 50, 422, 855, 428, 139,
|
|
- 44, 107, 396, 90, 41, 154, 41, 90, 37, 105,
|
|
- 69, 105, 37, 58, 41, 90, 57, 169, 218, 41,
|
|
- 58, 41, 58, 41, 58, 137, 58, 37, 137, 37,
|
|
- 135, 37, 90, 69, 73, 185, 94, 101, 58, 57,
|
|
- 90, 37, 58, 527, 1134, 94, 142, 47, 185, 186,
|
|
- 89, 154, 57, 90, 57, 90, 57, 250, 57, 1018,
|
|
- 89, 90, 57, 58, 57, 1018, 8601, 282, 153, 666,
|
|
- 89, 250, 54, 50, 2618, 57, 986, 825, 1306, 217,
|
|
- 602, 1274, 378, 1935, 2522, 719, 5882, 57, 314, 57,
|
|
- 1754, 281, 3578, 57, 4634, 3322, 54, 50, 54, 50,
|
|
+ 39, 44, 333, 335, 213, 49, 149, 108, 34, 333,
|
|
+ 1127, 38, 1703, 167, 76, 1095, 44, 39, 2247, 999,
|
|
+ 108, 138, 76, 106, 74, 44, 202, 108, 58, 85,
|
|
+ 333, 967, 167, 1415, 839, 333, 47, 1114, 743, 76,
|
|
+ 74, 44, 85, 1703, 42, 44, 42, 236, 44, 42,
|
|
+ 44, 74, 268, 202, 332, 44, 333, 333, 245, 38,
|
|
+ 213, 460, 43, 140, 42, 1511, 44, 42, 172, 42,
|
|
+ 44, 170, 44, 74, 231, 333, 245, 346, 300, 314,
|
|
+ 76, 42, 967, 42, 140, 74, 76, 42, 108, 71,
|
|
+ 333, 1415, 44, 42, 76, 106, 44, 42, 108, 74,
|
|
+ 149, 1159, 266, 268, 74, 76, 181, 333, 103, 333,
|
|
+ 967, 198, 85, 293, 1385, 105, 277, 108, 53, 428,
|
|
+ 42, 236, 135, 44, 199, 44, 71, 42, 76, 39,
|
|
+ 1413, 2022, 421, 38, 1093, 1190, 1868, 172, 4830, 261,
|
|
+ 3166, 261, 265, 197, 201, 261, 265, 261, 265, 197,
|
|
+ 201, 261, 41, 41, 41, 94, 229, 265, 453, 261,
|
|
+ 264, 261, 264, 261, 264, 165, 69, 137, 40, 56,
|
|
+ 37, 120, 101, 69, 137, 40, 120, 133, 69, 137,
|
|
+ 120, 261, 169, 120, 101, 69, 137, 40, 88, 381,
|
|
+ 162, 209, 85, 52, 51, 54, 84, 51, 54, 52,
|
|
+ 277, 59, 60, 162, 61, 309, 52, 51, 149, 80,
|
|
+ 117, 57, 54, 50, 373, 57, 53, 48, 341, 61,
|
|
+ 162, 322, 47, 38, 207, 121, 54, 50, 38, 335,
|
|
+ 121, 54, 50, 422, 1047, 428, 139, 44, 107, 396,
|
|
+ 90, 41, 154, 41, 90, 37, 105, 69, 105, 37,
|
|
+ 58, 41, 90, 57, 169, 218, 41, 58, 41, 58,
|
|
+ 41, 58, 137, 58, 37, 137, 37, 135, 37, 90,
|
|
+ 69, 73, 185, 94, 101, 58, 57, 90, 37, 58,
|
|
+ 527, 1134, 94, 142, 47, 90, 185, 186, 89, 154,
|
|
+ 57, 90, 57, 90, 57, 250, 57, 1018, 89, 90,
|
|
+ 57, 58, 57, 1018, 8601, 282, 54, 50, 54, 50,
|
|
+ 666, 89, 250, 54, 50, 2618, 57, 986, 825, 1306,
|
|
+ 217, 2234, 378, 1935, 2522, 719, 5882, 57, 314, 57,
|
|
+ 1754, 281, 3578, 57, 7962, 54, 50, 54, 50, 54,
|
|
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 975,
|
|
+ 1434, 185, 54, 50, 1017, 54, 50, 54, 50, 54,
|
|
+ 50, 54, 50, 54, 50, 537, 8218, 4217, 54, 50,
|
|
54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
|
|
- 975, 1434, 185, 54, 50, 1017, 54, 50, 54, 50,
|
|
- 54, 50, 54, 50, 54, 50, 537, 8218, 4217, 54,
|
|
- 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
|
|
- 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
|
|
- 50, 2041, 54, 50, 54, 50, 1049, 54, 50, 8281,
|
|
- 1562, 697, 90, 217, 346, 1513, 1509, 126, 73, 69,
|
|
- 254, 105, 37, 94, 37, 94, 165, 70, 105, 37,
|
|
- 3166, 37, 218, 158, 108, 94, 149, 47, 85, 1221,
|
|
- 37, 37, 1799, 38, 53, 44, 743, 231, 231, 231,
|
|
- 231, 231, 231, 231, 231, 1036, 85, 52, 51, 52,
|
|
- 51, 117, 52, 51, 53, 52, 51, 309, 49, 85,
|
|
- 49, 53, 52, 51, 85, 52, 51, 54, 50, 54,
|
|
- 50, 54, 50, 54, 50, 181, 38, 341, 81, 858,
|
|
- 2874, 6874, 410, 61, 117, 58, 38, 39, 46, 54,
|
|
- 50, 54, 50, 54, 50, 54, 50, 54, 50, 90,
|
|
- 54, 50, 54, 50, 54, 50, 54, 50, 49, 54,
|
|
- 82, 58, 302, 140, 74, 49, 166, 90, 110, 38,
|
|
- 39, 53, 90, 2759, 76, 88, 70, 39, 49, 2887,
|
|
- 53, 102, 39, 1319, 3015, 90, 143, 346, 871, 1178,
|
|
- 519, 1018, 335, 986, 271, 58, 495, 1050, 335, 1274,
|
|
- 495, 2042, 8218, 39, 39, 2074, 39, 39, 679, 38,
|
|
- 36583, 1786, 1287, 198, 85, 8583, 38, 117, 519, 333,
|
|
- 71, 1502, 39, 44, 107, 53, 332, 53, 38, 798,
|
|
- 44, 2247, 334, 76, 213, 760, 294, 88, 478, 69,
|
|
- 2014, 38, 261, 190, 350, 38, 88, 158, 158, 382,
|
|
- 70, 37, 231, 44, 103, 44, 135, 44, 743, 74,
|
|
- 76, 42, 154, 207, 90, 55, 58, 1671, 149, 74,
|
|
- 1607, 522, 44, 85, 333, 588, 199, 117, 39, 333,
|
|
- 903, 268, 85, 743, 364, 74, 53, 935, 108, 42,
|
|
- 1511, 44, 74, 140, 74, 44, 138, 437, 38, 333,
|
|
- 85, 1319, 204, 74, 76, 74, 76, 103, 44, 263,
|
|
- 44, 42, 333, 149, 519, 38, 199, 122, 39, 42,
|
|
- 1543, 44, 39, 108, 71, 76, 167, 76, 39, 44,
|
|
- 39, 71, 38, 85, 359, 42, 76, 74, 85, 39,
|
|
- 70, 42, 44, 199, 199, 199, 231, 231, 1127, 74,
|
|
- 44, 74, 44, 74, 53, 42, 44, 333, 39, 39,
|
|
- 743, 1575, 36, 68, 68, 36, 63, 63, 11719, 3399,
|
|
- 229, 165, 39, 44, 327, 57, 423, 167, 39, 71,
|
|
- 71, 3463, 536, 11623, 54, 50, 2055, 1735, 391, 55,
|
|
- 58, 524, 245, 54, 50, 53, 236, 53, 81, 80,
|
|
54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
|
|
- 54, 50, 54, 50, 54, 50, 85, 54, 50, 149,
|
|
- 112, 117, 149, 49, 54, 50, 54, 50, 54, 50,
|
|
- 117, 57, 49, 121, 53, 55, 85, 167, 4327, 34,
|
|
- 117, 55, 117, 54, 50, 53, 57, 53, 49, 85,
|
|
- 333, 85, 121, 85, 841, 54, 53, 50, 56, 48,
|
|
- 56, 837, 54, 57, 50, 57, 54, 50, 53, 54,
|
|
- 50, 85, 327, 38, 1447, 70, 999, 199, 199, 199,
|
|
- 103, 87, 57, 56, 58, 87, 58, 153, 90, 98,
|
|
- 90, 391, 839, 615, 71, 487, 455, 3943, 117, 1455,
|
|
- 314, 1710, 143, 570, 47, 410, 1466, 44, 935, 1575,
|
|
- 999, 143, 551, 46, 263, 46, 967, 53, 1159, 263,
|
|
- 53, 174, 1289, 1285, 2503, 333, 199, 39, 1415, 71,
|
|
- 39, 743, 53, 271, 711, 207, 53, 839, 53, 1799,
|
|
- 71, 39, 108, 76, 140, 135, 103, 871, 108, 44,
|
|
- 271, 309, 935, 79, 53, 1735, 245, 711, 271, 615,
|
|
- 271, 2343, 1007, 42, 44, 42, 1703, 492, 245, 655,
|
|
- 333, 76, 42, 1447, 106, 140, 74, 76, 85, 34,
|
|
- 149, 807, 333, 108, 1159, 172, 42, 268, 333, 149,
|
|
- 76, 42, 1543, 106, 300, 74, 135, 149, 333, 1383,
|
|
- 44, 42, 44, 74, 204, 42, 44, 333, 28135, 3182,
|
|
- 149, 34279, 18215, 2215, 39, 1482, 140, 422, 71, 7898,
|
|
- 1274, 1946, 74, 108, 122, 202, 258, 268, 90, 236,
|
|
- 986, 140, 1562, 2138, 108, 58, 2810, 591, 841, 837,
|
|
- 841, 229, 581, 841, 837, 41, 73, 41, 73, 137,
|
|
- 265, 133, 37, 229, 357, 841, 837, 73, 137, 265,
|
|
- 233, 837, 73, 137, 169, 41, 233, 837, 841, 837,
|
|
- 841, 837, 841, 837, 841, 837, 841, 837, 841, 901,
|
|
- 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
|
|
- 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
|
|
- 809, 57, 805, 57, 197, 94, 1613, 135, 871, 71,
|
|
- 39, 39, 327, 135, 39, 39, 39, 39, 39, 39,
|
|
- 103, 71, 39, 39, 39, 39, 39, 39, 71, 39,
|
|
- 135, 231, 135, 135, 39, 327, 551, 103, 167, 551,
|
|
- 89, 1434, 3226, 506, 474, 506, 506, 367, 1018, 1946,
|
|
- 1402, 954, 1402, 314, 90, 1082, 218, 2266, 666, 1210,
|
|
- 186, 570, 2042, 58, 5850, 154, 2010, 154, 794, 2266,
|
|
- 378, 2266, 3738, 39, 39, 39, 39, 39, 39, 17351,
|
|
- 34, 3074, 7692, 63, 63,
|
|
+ 2041, 54, 50, 54, 50, 1049, 54, 50, 8281, 1562,
|
|
+ 697, 90, 217, 1274, 1050, 3354, 1513, 1509, 126, 73,
|
|
+ 69, 254, 105, 37, 94, 37, 94, 165, 70, 105,
|
|
+ 37, 3166, 37, 218, 158, 108, 94, 149, 47, 85,
|
|
+ 1221, 37, 37, 1799, 38, 53, 44, 743, 231, 231,
|
|
+ 231, 231, 231, 231, 231, 231, 1036, 85, 52, 51,
|
|
+ 52, 51, 117, 52, 51, 53, 52, 51, 309, 49,
|
|
+ 85, 49, 53, 52, 51, 85, 52, 51, 54, 50,
|
|
+ 54, 50, 54, 50, 54, 50, 181, 38, 341, 81,
|
|
+ 149, 49, 53, 54, 437, 858, 2874, 6874, 410, 61,
|
|
+ 117, 58, 38, 39, 46, 54, 50, 54, 50, 54,
|
|
+ 50, 54, 50, 54, 50, 90, 54, 50, 54, 50,
|
|
+ 54, 50, 54, 50, 49, 54, 82, 58, 302, 140,
|
|
+ 74, 49, 166, 90, 110, 38, 39, 53, 90, 2759,
|
|
+ 76, 88, 70, 39, 49, 2887, 53, 102, 39, 1383,
|
|
+ 3015, 90, 143, 346, 871, 1178, 519, 1018, 335, 986,
|
|
+ 271, 58, 495, 1050, 335, 1274, 495, 10266, 39, 39,
|
|
+ 2074, 39, 39, 679, 38, 36583, 1786, 1287, 198, 85,
|
|
+ 8583, 38, 117, 519, 333, 71, 1502, 39, 44, 107,
|
|
+ 53, 332, 53, 38, 926, 70, 76, 2247, 334, 76,
|
|
+ 213, 760, 294, 88, 478, 69, 2014, 38, 261, 190,
|
|
+ 350, 38, 88, 158, 39, 158, 69, 702, 137, 37,
|
|
+ 169, 37, 350, 126, 73, 39, 70, 37, 231, 44,
|
|
+ 103, 44, 135, 44, 743, 74, 76, 42, 154, 207,
|
|
+ 90, 55, 58, 1671, 149, 74, 1607, 522, 76, 85,
|
|
+ 333, 588, 199, 117, 39, 53, 71, 44, 333, 903,
|
|
+ 268, 85, 743, 364, 74, 53, 935, 108, 42, 1511,
|
|
+ 44, 74, 140, 74, 76, 106, 437, 38, 333, 85,
|
|
+ 167, 44, 38, 295, 333, 167, 1319, 204, 74, 76,
|
|
+ 74, 76, 103, 44, 263, 44, 42, 333, 149, 519,
|
|
+ 38, 199, 122, 39, 42, 44, 42, 1607, 44, 39,
|
|
+ 108, 71, 76, 167, 76, 39, 44, 39, 71, 38,
|
|
+ 85, 359, 42, 76, 74, 85, 39, 70, 42, 44,
|
|
+ 199, 199, 199, 231, 231, 1381, 56, 134, 261, 2565,
|
|
+ 1127, 74, 44, 74, 44, 74, 53, 42, 44, 333,
|
|
+ 39, 39, 743, 1575, 36, 68, 68, 36, 63, 63,
|
|
+ 11719, 3399, 229, 165, 39, 44, 327, 57, 423, 167,
|
|
+ 39, 71, 71, 3463, 536, 11623, 50, 54, 2055, 1735,
|
|
+ 391, 55, 58, 524, 245, 54, 50, 53, 524, 53,
|
|
+ 81, 80, 54, 50, 54, 50, 54, 50, 54, 50,
|
|
+ 54, 50, 54, 50, 54, 50, 54, 50, 85, 54,
|
|
+ 50, 149, 112, 117, 149, 49, 54, 50, 54, 50,
|
|
+ 54, 50, 117, 57, 49, 121, 53, 55, 85, 167,
|
|
+ 4327, 34, 117, 55, 117, 54, 50, 53, 57, 53,
|
|
+ 49, 85, 333, 85, 121, 85, 841, 54, 53, 50,
|
|
+ 56, 48, 56, 837, 54, 57, 50, 57, 54, 50,
|
|
+ 53, 54, 50, 85, 327, 38, 1447, 70, 999, 199,
|
|
+ 199, 199, 103, 87, 57, 56, 58, 87, 58, 153,
|
|
+ 90, 98, 90, 391, 839, 615, 71, 487, 455, 3943,
|
|
+ 117, 1455, 314, 1710, 143, 570, 79, 122, 410, 58,
|
|
+ 1466, 44, 935, 1575, 44, 879, 1031, 143, 647, 46,
|
|
+ 263, 46, 1223, 172, 967, 53, 1159, 263, 53, 174,
|
|
+ 1289, 1285, 2503, 333, 1161, 1157, 1287, 1671, 53, 9959,
|
|
+ 711, 263, 199, 39, 1415, 71, 39, 743, 53, 271,
|
|
+ 743, 90, 239, 999, 303, 615, 71, 175, 711, 207,
|
|
+ 53, 839, 53, 1799, 79, 71, 527, 1487, 39, 108,
|
|
+ 76, 140, 135, 103, 935, 108, 44, 303, 309, 935,
|
|
+ 79, 53, 935, 111, 263, 58, 903, 76, 175, 245,
|
|
+ 1735, 245, 711, 271, 615, 271, 583, 149, 239, 2343,
|
|
+ 1641, 1637, 207, 1159, 140, 333, 1007, 935, 335, 39,
|
|
+ 711, 364, 143, 181, 743, 42, 44, 42, 1703, 492,
|
|
+ 245, 655, 333, 108, 42, 1447, 106, 140, 74, 76,
|
|
+ 85, 34, 149, 34, 807, 333, 108, 1159, 172, 42,
|
|
+ 268, 333, 149, 39, 74, 1127, 44, 85, 39, 76,
|
|
+ 42, 1543, 106, 300, 74, 135, 149, 140, 53, 333,
|
|
+ 39, 53, 39, 117, 655, 583, 807, 106, 108, 74,
|
|
+ 44, 42, 76, 213, 44, 231, 39, 135, 487, 327,
|
|
+ 53, 1511, 44, 106, 268, 333, 76, 74, 263, 71,
|
|
+ 711, 231, 71, 167, 76, 39, 74, 44, 138, 74,
|
|
+ 106, 39, 42, 167, 74, 236, 172, 1703, 106, 268,
|
|
+ 74, 108, 42, 44, 135, 181, 333, 53, 53, 44,
|
|
+ 39, 1543, 106, 204, 42, 44, 138, 76, 42, 76,
|
|
+ 71, 53, 39, 333, 1511, 106, 140, 138, 76, 42,
|
|
+ 76, 757, 135, 76, 1543, 106, 268, 74, 44, 42,
|
|
+ 76, 117, 39, 333, 437, 1383, 44, 42, 44, 74,
|
|
+ 204, 42, 44, 39, 333, 871, 108, 74, 140, 42,
|
|
+ 172, 333, 79, 117, 58, 1415, 106, 300, 42, 76,
|
|
+ 53, 1033, 1029, 333, 303, 39, 263, 1255, 106, 140,
|
|
+ 76, 138, 44, 39, 53, 39, 42, 39, 332, 1287,
|
|
+ 204, 42, 39, 140, 277, 44, 39, 204, 74, 108,
|
|
+ 1479, 428, 42, 76, 117, 39, 181, 1831, 295, 1191,
|
|
+ 42, 236, 204, 42, 44, 39, 181, 333, 623, 85,
|
|
+ 967, 716, 42, 236, 42, 76, 42, 76, 231, 71,
|
|
+ 1223, 204, 44, 76, 236, 39, 44, 333, 199, 71,
|
|
+ 1031, 170, 76, 74, 44, 42, 44, 39, 333, 615,
|
|
+ 76, 74, 85, 687, 282, 151, 570, 53, 29511, 3566,
|
|
+ 181, 6279, 34279, 290, 18663, 18215, 999, 333, 85, 967,
|
|
+ 172, 53, 1543, 236, 181, 154, 134, 53, 58, 333,
|
|
+ 239, 679, 615, 1033, 1029, 751, 149, 2407, 44, 39,
|
|
+ 1770, 140, 422, 70, 53, 38, 39, 39, 24167, 9191,
|
|
+ 103, 135, 12679, 3431, 423, 295, 327, 58, 76, 53,
|
|
+ 130, 7898, 1274, 1946, 74, 108, 122, 202, 258, 268,
|
|
+ 90, 236, 986, 140, 1914, 2138, 108, 58, 655, 2810,
|
|
+ 815, 841, 837, 841, 229, 581, 841, 837, 41, 73,
|
|
+ 41, 73, 137, 265, 133, 37, 229, 357, 841, 837,
|
|
+ 73, 137, 265, 233, 837, 73, 137, 169, 41, 233,
|
|
+ 837, 841, 837, 841, 837, 841, 837, 841, 837, 841,
|
|
+ 837, 841, 901, 809, 57, 805, 57, 197, 809, 57,
|
|
+ 805, 57, 197, 809, 57, 805, 57, 197, 809, 57,
|
|
+ 805, 57, 197, 809, 57, 805, 57, 197, 94, 1613,
|
|
+ 16410, 1772, 154, 1612, 282, 44, 474, 44, 90, 181,
|
|
+ 172, 492, 236, 556, 236, 76, 172, 1447, 236, 230,
|
|
+ 333, 39, 58, 1415, 140, 333, 55, 6311, 303, 236,
|
|
+ 1097, 1093, 236, 38, 333, 85, 1903, 58, 111, 55,
|
|
+ 143, 1455, 58, 495, 135, 871, 71, 39, 39, 327,
|
|
+ 135, 39, 39, 39, 39, 39, 39, 103, 71, 39,
|
|
+ 39, 39, 39, 39, 39, 71, 39, 135, 231, 135,
|
|
+ 135, 39, 327, 551, 103, 167, 551, 89, 1434, 3226,
|
|
+ 506, 506, 506, 1210, 431, 3002, 1978, 954, 1434, 314,
|
|
+ 90, 218, 8058, 184, 23258, 442, 378, 3738, 2874, 410,
|
|
+ 410, 1818, 346, 1306, 986, 410, 3258, 154, 1338, 218,
|
|
+ 954, 4346, 474, 154, 122, 122, 218, 39, 39, 39,
|
|
+ 39, 39, 39, 39, 39, 39, 39, 17351, 34, 3074,
|
|
+ 7692, 63, 63,
|
|
};
|
|
|
|
static int sqlite3Fts5UnicodeCategory(u32 iCode) {
|
|
@@ -221442,6 +222634,7 @@ static void sqlite3Fts5UnicodeAscii(u8 *
|
|
}
|
|
}
|
|
|
|
+
|
|
/*
|
|
** 2015 May 30
|
|
**
|
|
@@ -221961,7 +223154,7 @@ static int fts5VocabInitVtab(
|
|
sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
|
|
char **pzErr /* Write any error message here */
|
|
){
|
|
- const char *azSchema[] = {
|
|
+ const char *const azSchema[] = {
|
|
"CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
|
|
"CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")",
|
|
"CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")"
|
|
@@ -221977,12 +223170,12 @@ static int fts5VocabInitVtab(
|
|
*pzErr = sqlite3_mprintf("wrong number of vtable arguments");
|
|
rc = SQLITE_ERROR;
|
|
}else{
|
|
- int nByte; /* Bytes of space to allocate */
|
|
+ size_t nByte; /* Bytes of space to allocate */
|
|
const char *zDb = bDb ? argv[3] : argv[1];
|
|
const char *zTab = bDb ? argv[4] : argv[3];
|
|
const char *zType = bDb ? argv[5] : argv[4];
|
|
- int nDb = (int)strlen(zDb)+1;
|
|
- int nTab = (int)strlen(zTab)+1;
|
|
+ size_t nDb = strlen(zDb)+1;
|
|
+ size_t nTab = strlen(zTab)+1;
|
|
int eType = 0;
|
|
|
|
rc = fts5VocabTableType(zType, pzErr, &eType);
|
|
@@ -222153,7 +223346,7 @@ static int fts5VocabOpenMethod(
|
|
}
|
|
|
|
if( rc==SQLITE_OK ){
|
|
- int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor);
|
|
+ size_t nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor);
|
|
pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
|
|
}
|
|
|
|
@@ -222810,7 +224003,7 @@ static int stmtBestIndex(
|
|
** This following structure defines all the methods for the
|
|
** stmt virtual table.
|
|
*/
|
|
-static sqlite3_module stmtModule = {
|
|
+static const sqlite3_module stmtModule = {
|
|
0, /* iVersion */
|
|
0, /* xCreate */
|
|
stmtConnect, /* xConnect */
|
|
@@ -222863,11 +224056,25 @@ SQLITE_API int sqlite3_stmt_init(
|
|
#endif
|
|
return rc;
|
|
}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+SQLITE_API int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3StmtVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
#endif /* SQLITE_CORE */
|
|
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
|
|
|
|
/************** End of stmt.c ************************************************/
|
|
-#if __LINE__!=222870
|
|
+#if __LINE__!=224077
|
|
#undef SQLITE_SOURCE_ID
|
|
#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f8315alt2"
|
|
#endif
|
|
--- origsrc/sqlite-autoconf-3280000/sqlite3.h 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/sqlite3.h 2019-05-14 16:22:31.210754100 +0200
|
|
@@ -2027,6 +2027,8 @@ struct sqlite3_mem_methods {
|
|
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
|
|
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
|
|
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
|
|
+#define SQLITE_CONFIG_MALLOC2 98 /* sqlite3_mem_methods2* */
|
|
+#define SQLITE_CONFIG_GETMALLOC2 99 /* sqlite3_mem_methods2* */
|
|
|
|
/*
|
|
** CAPI3REF: Database Connection Configuration Options
|
|
@@ -2367,7 +2369,9 @@ SQLITE_API void sqlite3_set_last_insert_
|
|
** <li> the [data_version pragma]
|
|
** </ul>
|
|
*/
|
|
-SQLITE_API int sqlite3_changes(sqlite3*);
|
|
+SQLITE_API sqlite3_uint64 sqlite3_changes(sqlite3*);
|
|
+#define sqlite3_changes(db) ((int)(sqlite3_changes)(db))
|
|
+#define sqlite3_changes64(db) ((sqlite3_changes)(db))
|
|
|
|
/*
|
|
** CAPI3REF: Total Number Of Rows Modified
|
|
@@ -2404,7 +2408,9 @@ SQLITE_API int sqlite3_changes(sqlite3*)
|
|
** <li> the [SQLITE_FCNTL_DATA_VERSION] [file control]
|
|
** </ul>
|
|
*/
|
|
-SQLITE_API int sqlite3_total_changes(sqlite3*);
|
|
+SQLITE_API sqlite3_uint64 sqlite3_total_changes(sqlite3*);
|
|
+#define sqlite3_total_changes(db) ((int)(sqlite3_total_changes)(db))
|
|
+#define sqlite3_total_changes64(db) ((sqlite3_total_changes)(db))
|
|
|
|
/*
|
|
** CAPI3REF: Interrupt A Long-Running Query
|
|
@@ -4116,7 +4122,7 @@ typedef struct sqlite3_context sqlite3_c
|
|
** See also: [sqlite3_bind_parameter_count()],
|
|
** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
|
|
*/
|
|
-SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
|
|
+SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, unsigned int n, void(*)(void*));
|
|
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,
|
|
void(*)(void*));
|
|
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);
|
|
@@ -4694,8 +4700,8 @@ SQLITE_API sqlite3_int64 sqlite3_column_
|
|
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
|
|
SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
|
|
SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
|
|
-SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
|
|
-SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
|
|
+SQLITE_API unsigned int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
|
|
+SQLITE_API unsigned int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
|
|
SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
|
|
|
|
/*
|
|
@@ -4873,6 +4879,8 @@ SQLITE_API int sqlite3_create_function(
|
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
void (*xFinal)(sqlite3_context*)
|
|
);
|
|
+/* A macro is more efficient than a wrapped call */
|
|
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
|
SQLITE_API int sqlite3_create_function16(
|
|
sqlite3 *db,
|
|
const void *zFunctionName,
|
|
@@ -4945,9 +4953,12 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
|
|
+#define sqlite3_global_recover() SQLITE_OK
|
|
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
|
|
+#define sqlite3_thread_cleanup() /**/
|
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
|
|
void*,sqlite3_int64);
|
|
+#define sqlite3_memory_alarm(a,b,c) SQLITE_OK
|
|
#endif
|
|
|
|
/*
|
|
@@ -5087,8 +5098,8 @@ SQLITE_API const unsigned char *sqlite3_
|
|
SQLITE_API const void *sqlite3_value_text16(sqlite3_value*);
|
|
SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*);
|
|
SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*);
|
|
-SQLITE_API int sqlite3_value_bytes(sqlite3_value*);
|
|
-SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
|
|
+SQLITE_API unsigned int sqlite3_value_bytes(sqlite3_value*);
|
|
+SQLITE_API unsigned int sqlite3_value_bytes16(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
|
@@ -5400,7 +5411,7 @@ typedef void (*sqlite3_destructor_type)(
|
|
** than the one containing the application-defined function that received
|
|
** the [sqlite3_context] pointer, the results are undefined.
|
|
*/
|
|
-SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
|
|
+SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, unsigned int, void(*)(void*));
|
|
SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*,
|
|
sqlite3_uint64,void(*)(void*));
|
|
SQLITE_API void sqlite3_result_double(sqlite3_context*, double);
|
|
@@ -5425,6 +5436,19 @@ SQLITE_API int sqlite3_result_zeroblob64
|
|
|
|
|
|
/*
|
|
+ * If we are not sure the platform is 32-bit, always use sqlite3_????64()
|
|
+ * in stead of sqlite3_????() for certain functions, in order to prevent overflow.
|
|
+ */
|
|
+#if !defined(i386) && !defined(__i386__) && !defined(_M_IX86) && \
|
|
+ !defined(_M_ARM) && !defined(__arm__) && !defined(__x86) && \
|
|
+ (!defined(__SIZEOF_POINTER__) || (__SIZEOF_POINTER__ != 4))
|
|
+# define sqlite3_bind_blob(pStmt, i, zData, nData, xDel) sqlite3_bind_blob64(pStmt, i, zData, nData, xDel)
|
|
+# define sqlite3_result_blob(pCtx, z, n, xDel) sqlite3_result_blob64(pCtx, z, n, xDel)
|
|
+# define sqlite3_malloc(x) sqlite3_malloc64(x)
|
|
+# define sqlite3_realloc(x,y) sqlite3_realloc64(x,y)
|
|
+#endif
|
|
+
|
|
+/*
|
|
** CAPI3REF: Setting The Subtype Of An SQL Function
|
|
** METHOD: sqlite3_context
|
|
**
|
|
@@ -5525,6 +5549,8 @@ SQLITE_API int sqlite3_create_collation(
|
|
void *pArg,
|
|
int(*xCompare)(void*,int,const void*,int,const void*)
|
|
);
|
|
+/* A macro is more efficient than a wrapped call */
|
|
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
|
SQLITE_API int sqlite3_create_collation_v2(
|
|
sqlite3*,
|
|
const char *zName,
|
|
@@ -6099,6 +6125,7 @@ SQLITE_API sqlite3_int64 sqlite3_soft_he
|
|
*/
|
|
SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
|
|
|
|
+#define sqlite3_soft_heap_limit(N) sqlite3_soft_heap_limit64(N)
|
|
|
|
/*
|
|
** CAPI3REF: Extract Metadata About A Column Of A Table
|
|
@@ -6588,6 +6615,8 @@ SQLITE_API int sqlite3_create_module_v2(
|
|
void *pClientData, /* Client data for xCreate/xConnect */
|
|
void(*xDestroy)(void*) /* Module destructor function */
|
|
);
|
|
+/* A macro is more efficient than a (possible future) wrapped call */
|
|
+#define sqlite3_create_module(a,b,c,d) sqlite3_create_module_v2(a,b,c,d,0);
|
|
|
|
/*
|
|
** CAPI3REF: Virtual Table Instance Object
|
|
@@ -7319,7 +7348,8 @@ SQLITE_API int sqlite3_test_control(int
|
|
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
|
#define SQLITE_TESTCTRL_IMPOSTER 25
|
|
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
|
-#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
|
+#define SQLITE_TESTCTRL_RESULT_INTREAL 27
|
|
+#define SQLITE_TESTCTRL_LAST 27 /* Largest TESTCTRL */
|
|
|
|
/*
|
|
** CAPI3REF: SQL Keyword Checking
|
|
@@ -8582,6 +8612,8 @@ SQLITE_API int sqlite3_wal_autocheckpoin
|
|
** complication) of [sqlite3_wal_checkpoint_v2()].
|
|
*/
|
|
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
|
|
+/* A macro is more efficient than a wrapped call */
|
|
+#define sqlite3_wal_checkpoint(a,b) sqlite3_wal_checkpoint_v2(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0);
|
|
|
|
/*
|
|
** CAPI3REF: Checkpoint a database
|
|
@@ -11718,7 +11750,7 @@ struct fts5_api {
|
|
fts5_api *pApi,
|
|
const char *zName,
|
|
void *pContext,
|
|
- fts5_tokenizer *pTokenizer,
|
|
+ const fts5_tokenizer *pTokenizer,
|
|
void (*xDestroy)(void*)
|
|
);
|
|
|
|
--- origsrc/sqlite-autoconf-3280000/sqlite3ext.h 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/sqlite3ext.h 2019-05-14 16:22:31.212706200 +0200
|
|
@@ -32,7 +32,7 @@
|
|
struct sqlite3_api_routines {
|
|
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
|
int (*aggregate_count)(sqlite3_context*);
|
|
- int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
|
+ int (*bind_blob)(sqlite3_stmt*,int,const void*,unsigned int,void(*)(void*));
|
|
int (*bind_double)(sqlite3_stmt*,int,double);
|
|
int (*bind_int)(sqlite3_stmt*,int,int);
|
|
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
|
@@ -40,20 +40,20 @@ struct sqlite3_api_routines {
|
|
int (*bind_parameter_count)(sqlite3_stmt*);
|
|
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
|
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
|
- int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
|
+ int (*bind_text)(sqlite3_stmt*,int,const char*,int,void(*)(void*));
|
|
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
|
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
|
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
|
int (*busy_timeout)(sqlite3*,int ms);
|
|
- int (*changes)(sqlite3*);
|
|
+ sqlite_uint64 (*changes)(sqlite3*);
|
|
int (*close)(sqlite3*);
|
|
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
int eTextRep,const char*));
|
|
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
int eTextRep,const void*));
|
|
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
|
- int (*column_bytes)(sqlite3_stmt*,int iCol);
|
|
- int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
|
+ unsigned int (*column_bytes)(sqlite3_stmt*,int iCol);
|
|
+ unsigned int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
|
int (*column_count)(sqlite3_stmt*pStmt);
|
|
const char * (*column_database_name)(sqlite3_stmt*,int);
|
|
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
|
@@ -118,7 +118,7 @@ struct sqlite3_api_routines {
|
|
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
|
void *(*realloc)(void*,int);
|
|
int (*reset)(sqlite3_stmt*pStmt);
|
|
- void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
+ void (*result_blob)(sqlite3_context*,const void*,unsigned int,void(*)(void*));
|
|
void (*result_double)(sqlite3_context*,double);
|
|
void (*result_error)(sqlite3_context*,const char*,int);
|
|
void (*result_error16)(sqlite3_context*,const void*,int);
|
|
@@ -139,15 +139,15 @@ struct sqlite3_api_routines {
|
|
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
|
char const**,char const**,int*,int*,int*);
|
|
void (*thread_cleanup)(void);
|
|
- int (*total_changes)(sqlite3*);
|
|
+ sqlite_uint64 (*total_changes)(sqlite3*);
|
|
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
|
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
|
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
|
sqlite_int64),void*);
|
|
void * (*user_data)(sqlite3_context*);
|
|
const void * (*value_blob)(sqlite3_value*);
|
|
- int (*value_bytes)(sqlite3_value*);
|
|
- int (*value_bytes16)(sqlite3_value*);
|
|
+ unsigned int (*value_bytes)(sqlite3_value*);
|
|
+ unsigned int (*value_bytes16)(sqlite3_value*);
|
|
double (*value_double)(sqlite3_value*);
|
|
int (*value_int)(sqlite3_value*);
|
|
sqlite_int64 (*value_int64)(sqlite3_value*);
|
|
@@ -347,10 +347,9 @@ typedef int (*sqlite3_loadext_entry)(
|
|
*/
|
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
|
-#endif
|
|
+#ifndef sqlite3_bind_blob
|
|
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
|
+#endif
|
|
#define sqlite3_bind_double sqlite3_api->bind_double
|
|
#define sqlite3_bind_int sqlite3_api->bind_int
|
|
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
|
@@ -358,12 +357,19 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
|
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
|
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
|
+#ifndef sqlite3_bind_text
|
|
#define sqlite3_bind_text sqlite3_api->bind_text
|
|
+#endif
|
|
+#ifndef sqlite3_bind_text16
|
|
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
|
+#endif
|
|
#define sqlite3_bind_value sqlite3_api->bind_value
|
|
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
|
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
|
-#define sqlite3_changes sqlite3_api->changes
|
|
+#undef sqlite3_changes
|
|
+#define sqlite3_changes (int)sqlite3_api->changes
|
|
+#undef sqlite3_changes64
|
|
+#define sqlite3_changes64 sqlite3_api->changes
|
|
#define sqlite3_close sqlite3_api->close
|
|
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
|
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
|
@@ -391,11 +397,14 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
|
#define sqlite3_complete sqlite3_api->complete
|
|
#define sqlite3_complete16 sqlite3_api->complete16
|
|
-#define sqlite3_create_collation sqlite3_api->create_collation
|
|
+#undef sqlite3_create_collation
|
|
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
|
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
|
-#define sqlite3_create_function sqlite3_api->create_function
|
|
+#undef sqlite3_create_function
|
|
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
|
#define sqlite3_create_function16 sqlite3_api->create_function16
|
|
-#define sqlite3_create_module sqlite3_api->create_module
|
|
+#undef sqlite3_create_module
|
|
+#define sqlite3_create_module(a,b,c,d) sqlite3_create_module_v2(a,b,c,d,0)
|
|
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
|
#define sqlite3_data_count sqlite3_api->data_count
|
|
#define sqlite3_db_handle sqlite3_api->db_handle
|
|
@@ -405,23 +414,19 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_errmsg sqlite3_api->errmsg
|
|
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
|
#define sqlite3_exec sqlite3_api->exec
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_expired sqlite3_api->expired
|
|
-#endif
|
|
#define sqlite3_finalize sqlite3_api->finalize
|
|
#define sqlite3_free sqlite3_api->free
|
|
#define sqlite3_free_table sqlite3_api->free_table
|
|
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
|
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
|
#define sqlite3_get_table sqlite3_api->get_table
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_global_recover sqlite3_api->global_recover
|
|
-#endif
|
|
#define sqlite3_interrupt sqlite3_api->interruptx
|
|
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
|
#define sqlite3_libversion sqlite3_api->libversion
|
|
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
|
+#ifndef sqlite3_malloc
|
|
#define sqlite3_malloc sqlite3_api->malloc
|
|
+#endif
|
|
#define sqlite3_mprintf sqlite3_api->mprintf
|
|
#define sqlite3_open sqlite3_api->open
|
|
#define sqlite3_open16 sqlite3_api->open16
|
|
@@ -431,19 +436,31 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
#define sqlite3_profile sqlite3_api->profile
|
|
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
|
+#ifndef sqlite3_realloc
|
|
#define sqlite3_realloc sqlite3_api->realloc
|
|
+#endif
|
|
#define sqlite3_reset sqlite3_api->reset
|
|
+#ifndef sqlite3_result_blob
|
|
#define sqlite3_result_blob sqlite3_api->result_blob
|
|
+#endif
|
|
#define sqlite3_result_double sqlite3_api->result_double
|
|
#define sqlite3_result_error sqlite3_api->result_error
|
|
#define sqlite3_result_error16 sqlite3_api->result_error16
|
|
#define sqlite3_result_int sqlite3_api->result_int
|
|
#define sqlite3_result_int64 sqlite3_api->result_int64
|
|
#define sqlite3_result_null sqlite3_api->result_null
|
|
+#ifndef sqlite3_result_text
|
|
#define sqlite3_result_text sqlite3_api->result_text
|
|
+#endif
|
|
+#ifndef sqlite3_result_text16
|
|
#define sqlite3_result_text16 sqlite3_api->result_text16
|
|
+#endif
|
|
+#ifndef sqlite3_result_text16be
|
|
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
|
+#endif
|
|
+#ifndef sqlite3_result_text16le
|
|
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
|
+#endif
|
|
#define sqlite3_result_value sqlite3_api->result_value
|
|
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
|
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
|
@@ -451,12 +468,11 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
|
#define sqlite3_step sqlite3_api->step
|
|
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
|
-#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
|
-#define sqlite3_total_changes sqlite3_api->total_changes
|
|
+#undef sqlite3_total_changes
|
|
+#define sqlite3_total_changes (int)sqlite3_api->total_changes
|
|
+#undef sqlite3_total_changes64
|
|
+#define sqlite3_total_changes64 sqlite3_api->total_changes
|
|
#define sqlite3_trace sqlite3_api->trace
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
-#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
|
-#endif
|
|
#define sqlite3_update_hook sqlite3_api->update_hook
|
|
#define sqlite3_user_data sqlite3_api->user_data
|
|
#define sqlite3_value_blob sqlite3_api->value_blob
|
|
@@ -497,12 +513,13 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
|
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
|
#define sqlite3_sleep sqlite3_api->sleep
|
|
-#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
|
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
|
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
|
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
|
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
|
+#ifndef sqlite3_result_zeroblob
|
|
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
|
+#endif
|
|
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
|
#define sqlite3_test_control sqlite3_api->test_control
|
|
#define sqlite3_randomness sqlite3_api->randomness
|
|
@@ -531,7 +548,8 @@ typedef int (*sqlite3_loadext_entry)(
|
|
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
|
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
|
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
|
-#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
|
+#undef sqlite3_wal_checkpoint
|
|
+#define sqlite3_wal_checkpoint(a,b) sqlite3_wal_checkpoint_v2(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0)
|
|
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
|
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
|
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
|
--- origsrc/sqlite-autoconf-3280000/sqlite3rbu.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/sqlite3rbu.c 2019-03-05 13:14:41.021716300 +0100
|
|
@@ -0,0 +1,4946 @@
|
|
+/*
|
|
+** 2014 August 30
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+**
|
|
+** OVERVIEW
|
|
+**
|
|
+** The RBU extension requires that the RBU update be packaged as an
|
|
+** SQLite database. The tables it expects to find are described in
|
|
+** sqlite3rbu.h. Essentially, for each table xyz in the target database
|
|
+** that the user wishes to write to, a corresponding data_xyz table is
|
|
+** created in the RBU database and populated with one row for each row to
|
|
+** update, insert or delete from the target table.
|
|
+**
|
|
+** The update proceeds in three stages:
|
|
+**
|
|
+** 1) The database is updated. The modified database pages are written
|
|
+** to a *-oal file. A *-oal file is just like a *-wal file, except
|
|
+** that it is named "<database>-oal" instead of "<database>-wal".
|
|
+** Because regular SQLite clients do not look for file named
|
|
+** "<database>-oal", they go on using the original database in
|
|
+** rollback mode while the *-oal file is being generated.
|
|
+**
|
|
+** During this stage RBU does not update the database by writing
|
|
+** directly to the target tables. Instead it creates "imposter"
|
|
+** tables using the SQLITE_TESTCTRL_IMPOSTER interface that it uses
|
|
+** to update each b-tree individually. All updates required by each
|
|
+** b-tree are completed before moving on to the next, and all
|
|
+** updates are done in sorted key order.
|
|
+**
|
|
+** 2) The "<database>-oal" file is moved to the equivalent "<database>-wal"
|
|
+** location using a call to rename(2). Before doing this the RBU
|
|
+** module takes an EXCLUSIVE lock on the database file, ensuring
|
|
+** that there are no other active readers.
|
|
+**
|
|
+** Once the EXCLUSIVE lock is released, any other database readers
|
|
+** detect the new *-wal file and read the database in wal mode. At
|
|
+** this point they see the new version of the database - including
|
|
+** the updates made as part of the RBU update.
|
|
+**
|
|
+** 3) The new *-wal file is checkpointed. This proceeds in the same way
|
|
+** as a regular database checkpoint, except that a single frame is
|
|
+** checkpointed each time sqlite3rbu_step() is called. If the RBU
|
|
+** handle is closed before the entire *-wal file is checkpointed,
|
|
+** the checkpoint progress is saved in the RBU database and the
|
|
+** checkpoint can be resumed by another RBU client at some point in
|
|
+** the future.
|
|
+**
|
|
+** POTENTIAL PROBLEMS
|
|
+**
|
|
+** The rename() call might not be portable. And RBU is not currently
|
|
+** syncing the directory after renaming the file.
|
|
+**
|
|
+** When state is saved, any commit to the *-oal file and the commit to
|
|
+** the RBU update database are not atomic. So if the power fails at the
|
|
+** wrong moment they might get out of sync. As the main database will be
|
|
+** committed before the RBU update database this will likely either just
|
|
+** pass unnoticed, or result in SQLITE_CONSTRAINT errors (due to UNIQUE
|
|
+** constraint violations).
|
|
+**
|
|
+** If some client does modify the target database mid RBU update, or some
|
|
+** other error occurs, the RBU extension will keep throwing errors. It's
|
|
+** not really clear how to get out of this state. The system could just
|
|
+** by delete the RBU update database and *-oal file and have the device
|
|
+** download the update again and start over.
|
|
+**
|
|
+** At present, for an UPDATE, both the new.* and old.* records are
|
|
+** collected in the rbu_xyz table. And for both UPDATEs and DELETEs all
|
|
+** fields are collected. This means we're probably writing a lot more
|
|
+** data to disk when saving the state of an ongoing update to the RBU
|
|
+** update database than is strictly necessary.
|
|
+**
|
|
+*/
|
|
+
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#include "sqlite3.h"
|
|
+
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU)
|
|
+#include "sqlite3rbu.h"
|
|
+
|
|
+#if defined(_WIN32_WCE)
|
|
+#include "windows.h"
|
|
+#endif
|
|
+
|
|
+/* Maximum number of prepared UPDATE statements held by this module */
|
|
+#define SQLITE_RBU_UPDATE_CACHESIZE 16
|
|
+
|
|
+/* Delta checksums disabled by default. Compile with -DRBU_ENABLE_DELTA_CKSUM
|
|
+** to enable checksum verification.
|
|
+*/
|
|
+#ifndef RBU_ENABLE_DELTA_CKSUM
|
|
+# define RBU_ENABLE_DELTA_CKSUM 0
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Swap two objects of type TYPE.
|
|
+*/
|
|
+#if !defined(SQLITE_AMALGAMATION)
|
|
+# define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The rbu_state table is used to save the state of a partially applied
|
|
+** update so that it can be resumed later. The table consists of integer
|
|
+** keys mapped to values as follows:
|
|
+**
|
|
+** RBU_STATE_STAGE:
|
|
+** May be set to integer values 1, 2, 4 or 5. As follows:
|
|
+** 1: the *-rbu file is currently under construction.
|
|
+** 2: the *-rbu file has been constructed, but not yet moved
|
|
+** to the *-wal path.
|
|
+** 4: the checkpoint is underway.
|
|
+** 5: the rbu update has been checkpointed.
|
|
+**
|
|
+** RBU_STATE_TBL:
|
|
+** Only valid if STAGE==1. The target database name of the table
|
|
+** currently being written.
|
|
+**
|
|
+** RBU_STATE_IDX:
|
|
+** Only valid if STAGE==1. The target database name of the index
|
|
+** currently being written, or NULL if the main table is currently being
|
|
+** updated.
|
|
+**
|
|
+** RBU_STATE_ROW:
|
|
+** Only valid if STAGE==1. Number of rows already processed for the current
|
|
+** table/index.
|
|
+**
|
|
+** RBU_STATE_PROGRESS:
|
|
+** Trbul number of sqlite3rbu_step() calls made so far as part of this
|
|
+** rbu update.
|
|
+**
|
|
+** RBU_STATE_CKPT:
|
|
+** Valid if STAGE==4. The 64-bit checksum associated with the wal-index
|
|
+** header created by recovering the *-wal file. This is used to detect
|
|
+** cases when another client appends frames to the *-wal file in the
|
|
+** middle of an incremental checkpoint (an incremental checkpoint cannot
|
|
+** be continued if this happens).
|
|
+**
|
|
+** RBU_STATE_COOKIE:
|
|
+** Valid if STAGE==1. The current change-counter cookie value in the
|
|
+** target db file.
|
|
+**
|
|
+** RBU_STATE_OALSZ:
|
|
+** Valid if STAGE==1. The size in bytes of the *-oal file.
|
|
+**
|
|
+** RBU_STATE_DATATBL:
|
|
+** Only valid if STAGE==1. The RBU database name of the table
|
|
+** currently being read.
|
|
+*/
|
|
+#define RBU_STATE_STAGE 1
|
|
+#define RBU_STATE_TBL 2
|
|
+#define RBU_STATE_IDX 3
|
|
+#define RBU_STATE_ROW 4
|
|
+#define RBU_STATE_PROGRESS 5
|
|
+#define RBU_STATE_CKPT 6
|
|
+#define RBU_STATE_COOKIE 7
|
|
+#define RBU_STATE_OALSZ 8
|
|
+#define RBU_STATE_PHASEONESTEP 9
|
|
+#define RBU_STATE_DATATBL 10
|
|
+
|
|
+#define RBU_STAGE_OAL 1
|
|
+#define RBU_STAGE_MOVE 2
|
|
+#define RBU_STAGE_CAPTURE 3
|
|
+#define RBU_STAGE_CKPT 4
|
|
+#define RBU_STAGE_DONE 5
|
|
+
|
|
+
|
|
+#define RBU_CREATE_STATE \
|
|
+ "CREATE TABLE IF NOT EXISTS %s.rbu_state(k INTEGER PRIMARY KEY, v)"
|
|
+
|
|
+typedef struct RbuFrame RbuFrame;
|
|
+typedef struct RbuObjIter RbuObjIter;
|
|
+typedef struct RbuState RbuState;
|
|
+typedef struct rbu_vfs rbu_vfs;
|
|
+typedef struct rbu_file rbu_file;
|
|
+typedef struct RbuUpdateStmt RbuUpdateStmt;
|
|
+
|
|
+#if !defined(SQLITE_AMALGAMATION)
|
|
+typedef unsigned int u32;
|
|
+typedef unsigned short u16;
|
|
+typedef unsigned char u8;
|
|
+typedef sqlite3_int64 i64;
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** These values must match the values defined in wal.c for the equivalent
|
|
+** locks. These are not magic numbers as they are part of the SQLite file
|
|
+** format.
|
|
+*/
|
|
+#define WAL_LOCK_WRITE 0
|
|
+#define WAL_LOCK_CKPT 1
|
|
+#define WAL_LOCK_READ0 3
|
|
+
|
|
+#define SQLITE_FCNTL_RBUCNT 5149216
|
|
+
|
|
+/*
|
|
+** A structure to store values read from the rbu_state table in memory.
|
|
+*/
|
|
+struct RbuState {
|
|
+ int eStage;
|
|
+ char *zTbl;
|
|
+ char *zDataTbl;
|
|
+ char *zIdx;
|
|
+ i64 iWalCksum;
|
|
+ int nRow;
|
|
+ i64 nProgress;
|
|
+ u32 iCookie;
|
|
+ i64 iOalSz;
|
|
+ i64 nPhaseOneStep;
|
|
+};
|
|
+
|
|
+struct RbuUpdateStmt {
|
|
+ char *zMask; /* Copy of update mask used with pUpdate */
|
|
+ sqlite3_stmt *pUpdate; /* Last update statement (or NULL) */
|
|
+ RbuUpdateStmt *pNext;
|
|
+};
|
|
+
|
|
+/*
|
|
+** An iterator of this type is used to iterate through all objects in
|
|
+** the target database that require updating. For each such table, the
|
|
+** iterator visits, in order:
|
|
+**
|
|
+** * the table itself,
|
|
+** * each index of the table (zero or more points to visit), and
|
|
+** * a special "cleanup table" state.
|
|
+**
|
|
+** abIndexed:
|
|
+** If the table has no indexes on it, abIndexed is set to NULL. Otherwise,
|
|
+** it points to an array of flags nTblCol elements in size. The flag is
|
|
+** set for each column that is either a part of the PK or a part of an
|
|
+** index. Or clear otherwise.
|
|
+**
|
|
+*/
|
|
+struct RbuObjIter {
|
|
+ sqlite3_stmt *pTblIter; /* Iterate through tables */
|
|
+ sqlite3_stmt *pIdxIter; /* Index iterator */
|
|
+ int nTblCol; /* Size of azTblCol[] array */
|
|
+ char **azTblCol; /* Array of unquoted target column names */
|
|
+ char **azTblType; /* Array of target column types */
|
|
+ int *aiSrcOrder; /* src table col -> target table col */
|
|
+ u8 *abTblPk; /* Array of flags, set on target PK columns */
|
|
+ u8 *abNotNull; /* Array of flags, set on NOT NULL columns */
|
|
+ u8 *abIndexed; /* Array of flags, set on indexed & PK cols */
|
|
+ int eType; /* Table type - an RBU_PK_XXX value */
|
|
+
|
|
+ /* Output variables. zTbl==0 implies EOF. */
|
|
+ int bCleanup; /* True in "cleanup" state */
|
|
+ const char *zTbl; /* Name of target db table */
|
|
+ const char *zDataTbl; /* Name of rbu db table (or null) */
|
|
+ const char *zIdx; /* Name of target db index (or null) */
|
|
+ int iTnum; /* Root page of current object */
|
|
+ int iPkTnum; /* If eType==EXTERNAL, root of PK index */
|
|
+ int bUnique; /* Current index is unique */
|
|
+ int nIndex; /* Number of aux. indexes on table zTbl */
|
|
+
|
|
+ /* Statements created by rbuObjIterPrepareAll() */
|
|
+ int nCol; /* Number of columns in current object */
|
|
+ sqlite3_stmt *pSelect; /* Source data */
|
|
+ sqlite3_stmt *pInsert; /* Statement for INSERT operations */
|
|
+ sqlite3_stmt *pDelete; /* Statement for DELETE ops */
|
|
+ sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
|
|
+
|
|
+ /* Last UPDATE used (for PK b-tree updates only), or NULL. */
|
|
+ RbuUpdateStmt *pRbuUpdate;
|
|
+};
|
|
+
|
|
+/*
|
|
+** Values for RbuObjIter.eType
|
|
+**
|
|
+** 0: Table does not exist (error)
|
|
+** 1: Table has an implicit rowid.
|
|
+** 2: Table has an explicit IPK column.
|
|
+** 3: Table has an external PK index.
|
|
+** 4: Table is WITHOUT ROWID.
|
|
+** 5: Table is a virtual table.
|
|
+*/
|
|
+#define RBU_PK_NOTABLE 0
|
|
+#define RBU_PK_NONE 1
|
|
+#define RBU_PK_IPK 2
|
|
+#define RBU_PK_EXTERNAL 3
|
|
+#define RBU_PK_WITHOUT_ROWID 4
|
|
+#define RBU_PK_VTAB 5
|
|
+
|
|
+
|
|
+/*
|
|
+** Within the RBU_STAGE_OAL stage, each call to sqlite3rbu_step() performs
|
|
+** one of the following operations.
|
|
+*/
|
|
+#define RBU_INSERT 1 /* Insert on a main table b-tree */
|
|
+#define RBU_DELETE 2 /* Delete a row from a main table b-tree */
|
|
+#define RBU_REPLACE 3 /* Delete and then insert a row */
|
|
+#define RBU_IDX_DELETE 4 /* Delete a row from an aux. index b-tree */
|
|
+#define RBU_IDX_INSERT 5 /* Insert on an aux. index b-tree */
|
|
+
|
|
+#define RBU_UPDATE 6 /* Update a row in a main table b-tree */
|
|
+
|
|
+/*
|
|
+** A single step of an incremental checkpoint - frame iWalFrame of the wal
|
|
+** 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()
|
|
+** API to calculate the permyriadage progress of populating the *-oal file
|
|
+** using the formula:
|
|
+**
|
|
+** permyriadage = (10000 * nProgress) / nPhaseOneStep
|
|
+**
|
|
+** nPhaseOneStep is initialized to the sum of:
|
|
+**
|
|
+** nRow * (nIndex + 1)
|
|
+**
|
|
+** for all source tables in the RBU database, where nRow is the number
|
|
+** of rows in the source table and nIndex the number of indexes on the
|
|
+** corresponding target database table.
|
|
+**
|
|
+** This estimate is accurate if the RBU update consists entirely of
|
|
+** INSERT operations. However, it is inaccurate if:
|
|
+**
|
|
+** * the RBU update contains any UPDATE operations. If the PK specified
|
|
+** for an UPDATE operation does not exist in the target table, then
|
|
+** no b-tree operations are required on index b-trees. Or if the
|
|
+** specified PK does exist, then (nIndex*2) such operations are
|
|
+** required (one delete and one insert on each index b-tree).
|
|
+**
|
|
+** * the RBU update contains any DELETE operations for which the specified
|
|
+** PK does not exist. In this case no operations are required on index
|
|
+** b-trees.
|
|
+**
|
|
+** * the RBU update contains REPLACE operations. These are similar to
|
|
+** UPDATE operations.
|
|
+**
|
|
+** nPhaseOneStep is updated to account for the conditions above during the
|
|
+** first pass of each source table. The updated nPhaseOneStep value is
|
|
+** stored in the rbu_state table if the RBU update is suspended.
|
|
+*/
|
|
+struct sqlite3rbu {
|
|
+ int eStage; /* Value of RBU_STATE_STAGE field */
|
|
+ sqlite3 *dbMain; /* target database handle */
|
|
+ sqlite3 *dbRbu; /* rbu database handle */
|
|
+ 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 */
|
|
+ int 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;
|
|
+
|
|
+ /* The following state variables are used as part of the incremental
|
|
+ ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
|
|
+ ** function rbuSetupCheckpoint() for details. */
|
|
+ u32 iMaxFrame; /* Largest iWalFrame value in aFrame[] */
|
|
+ u32 mLock;
|
|
+ int nFrame; /* Entries in aFrame[] array */
|
|
+ int nFrameAlloc; /* Allocated size of aFrame[] array */
|
|
+ RbuFrame *aFrame;
|
|
+ int pgsz;
|
|
+ u8 *aBuf;
|
|
+ i64 iWalCksum;
|
|
+ i64 szTemp; /* Current size of all temp files in use */
|
|
+ i64 szTempLimit; /* Total size limit for temp files */
|
|
+
|
|
+ /* Used in RBU vacuum mode only */
|
|
+ int nRbu; /* Number of RBU VFS in the stack */
|
|
+ rbu_file *pRbuFd; /* Fd for main db of dbRbu */
|
|
+};
|
|
+
|
|
+/*
|
|
+** An rbu VFS is implemented using an instance of this structure.
|
|
+**
|
|
+** Variable pRbu is only non-NULL for automatically created RBU VFS objects.
|
|
+** It is NULL for RBU VFS objects created explicitly using
|
|
+** sqlite3rbu_create_vfs(). It is used to track the total amount of temp
|
|
+** space used by the RBU handle.
|
|
+*/
|
|
+struct rbu_vfs {
|
|
+ sqlite3_vfs base; /* rbu VFS shim methods */
|
|
+ sqlite3_vfs *pRealVfs; /* Underlying VFS */
|
|
+ sqlite3_mutex *mutex; /* Mutex to protect pMain */
|
|
+ sqlite3rbu *pRbu; /* Owner RBU object */
|
|
+ rbu_file *pMain; /* List of main db files */
|
|
+ rbu_file *pMainRbu; /* List of main db files with pRbu!=0 */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Each file opened by an rbu VFS is represented by an instance of
|
|
+** the following structure.
|
|
+**
|
|
+** If this is a temporary file (pRbu!=0 && flags&DELETE_ON_CLOSE), variable
|
|
+** "sz" is set to the current size of the database file.
|
|
+*/
|
|
+struct rbu_file {
|
|
+ sqlite3_file base; /* sqlite3_file methods */
|
|
+ sqlite3_file *pReal; /* Underlying file handle */
|
|
+ rbu_vfs *pRbuVfs; /* Pointer to the rbu_vfs object */
|
|
+ sqlite3rbu *pRbu; /* Pointer to rbu object (rbu target only) */
|
|
+ i64 sz; /* Size of file in bytes (temp only) */
|
|
+
|
|
+ int openFlags; /* Flags this file was opened with */
|
|
+ u32 iCookie; /* Cookie value for main db files */
|
|
+ u8 iWriteVer; /* "write-version" value for main db files */
|
|
+ u8 bNolock; /* True to fail EXCLUSIVE locks */
|
|
+
|
|
+ int nShm; /* Number of entries in apShm[] array */
|
|
+ char **apShm; /* Array of mmap'd *-shm regions */
|
|
+ char *zDel; /* Delete this when closing file */
|
|
+
|
|
+ const char *zWal; /* Wal filename for this main db file */
|
|
+ rbu_file *pWalFd; /* Wal file descriptor for this main db */
|
|
+ rbu_file *pMainNext; /* Next MAIN_DB file */
|
|
+ rbu_file *pMainRbuNext; /* Next MAIN_DB file with pRbu!=0 */
|
|
+};
|
|
+
|
|
+/*
|
|
+** True for an RBU vacuum handle, or false otherwise.
|
|
+*/
|
|
+#define rbuIsVacuum(p) ((p)->zTarget==0)
|
|
+
|
|
+
|
|
+/*************************************************************************
|
|
+** The following three functions, found below:
|
|
+**
|
|
+** rbuDeltaGetInt()
|
|
+** rbuDeltaChecksum()
|
|
+** rbuDeltaApply()
|
|
+**
|
|
+** are lifted from the fossil source code (http://fossil-scm.org). They
|
|
+** are used to implement the scalar SQL function rbu_fossil_delta().
|
|
+*/
|
|
+
|
|
+/*
|
|
+** Read bytes from *pz and convert them into a positive integer. When
|
|
+** finished, leave *pz pointing to the first character past the end of
|
|
+** the integer. The *pLen parameter holds the length of the string
|
|
+** in *pz and is decremented once for each character in the integer.
|
|
+*/
|
|
+static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){
|
|
+ static const signed char zValue[] = {
|
|
+ -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,
|
|
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
|
|
+ -1, 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, -1, -1, -1, -1, 36,
|
|
+ -1, 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, -1, -1, -1, 63, -1,
|
|
+ };
|
|
+ unsigned int v = 0;
|
|
+ int c;
|
|
+ unsigned char *z = (unsigned char*)*pz;
|
|
+ unsigned char *zStart = z;
|
|
+ while( (c = zValue[0x7f&*(z++)])>=0 ){
|
|
+ v = (v<<6) + c;
|
|
+ }
|
|
+ z--;
|
|
+ *pLen -= 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.
|
|
+*/
|
|
+static unsigned int rbuDeltaChecksum(const char *zIn, size_t N){
|
|
+ const unsigned char *z = (const unsigned char *)zIn;
|
|
+ unsigned sum0 = 0;
|
|
+ unsigned sum1 = 0;
|
|
+ unsigned sum2 = 0;
|
|
+ unsigned sum3 = 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]);
|
|
+ sum3 += ((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];
|
|
+ sum3 += z[3];
|
|
+ z += 4;
|
|
+ N -= 4;
|
|
+ }
|
|
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
|
|
+ switch(N){
|
|
+ case 3: sum3 += (z[2] << 8);
|
|
+ case 2: sum3 += (z[1] << 16);
|
|
+ case 1: sum3 += (z[0] << 24);
|
|
+ default: ;
|
|
+ }
|
|
+ return sum3;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Apply a delta.
|
|
+**
|
|
+** The output buffer should be big enough to hold the whole output
|
|
+** file and a NUL terminator at the end. The delta_output_size()
|
|
+** routine will determine this size for you.
|
|
+**
|
|
+** The delta string should be null-terminated. But the delta string
|
|
+** may contain embedded NUL characters (if the input and output are
|
|
+** binary files) so we also have to pass in the length of the delta in
|
|
+** the lenDelta parameter.
|
|
+**
|
|
+** This function returns the size of the output file in bytes (excluding
|
|
+** the final NUL terminator character). Except, if the delta string is
|
|
+** malformed or intended for use with a source file other than zSrc,
|
|
+** then this routine returns -1.
|
|
+**
|
|
+** Refer to the delta_create() documentation above for a description
|
|
+** of the delta file format.
|
|
+*/
|
|
+static int rbuDeltaApply(
|
|
+ const char *zSrc, /* The source or pattern file */
|
|
+ int lenSrc, /* Length of the source file */
|
|
+ const char *zDelta, /* Delta to apply to the pattern */
|
|
+ int lenDelta, /* Length of the delta */
|
|
+ char *zOut /* Write the output into this preallocated buffer */
|
|
+){
|
|
+ unsigned int limit;
|
|
+ unsigned int total = 0;
|
|
+#if RBU_ENABLE_DELTA_CKSUM
|
|
+ char *zOrigOut = zOut;
|
|
+#endif
|
|
+
|
|
+ limit = rbuDeltaGetInt(&zDelta, &lenDelta);
|
|
+ if( *zDelta!='\n' ){
|
|
+ /* ERROR: size integer not terminated by "\n" */
|
|
+ return -1;
|
|
+ }
|
|
+ zDelta++; lenDelta--;
|
|
+ while( *zDelta && lenDelta>0 ){
|
|
+ unsigned int cnt, ofst;
|
|
+ cnt = rbuDeltaGetInt(&zDelta, &lenDelta);
|
|
+ switch( zDelta[0] ){
|
|
+ case '@': {
|
|
+ zDelta++; lenDelta--;
|
|
+ ofst = rbuDeltaGetInt(&zDelta, &lenDelta);
|
|
+ if( lenDelta>0 && zDelta[0]!=',' ){
|
|
+ /* ERROR: copy command not terminated by ',' */
|
|
+ return -1;
|
|
+ }
|
|
+ zDelta++; lenDelta--;
|
|
+ total += cnt;
|
|
+ if( total>limit ){
|
|
+ /* ERROR: copy exceeds output file size */
|
|
+ return -1;
|
|
+ }
|
|
+ if( (int)(ofst+cnt) > lenSrc ){
|
|
+ /* ERROR: copy extends past end of input */
|
|
+ return -1;
|
|
+ }
|
|
+ memcpy(zOut, &zSrc[ofst], cnt);
|
|
+ zOut += cnt;
|
|
+ break;
|
|
+ }
|
|
+ case ':': {
|
|
+ zDelta++; lenDelta--;
|
|
+ total += cnt;
|
|
+ if( total>limit ){
|
|
+ /* ERROR: insert command gives an output larger than predicted */
|
|
+ return -1;
|
|
+ }
|
|
+ if( (int)cnt>lenDelta ){
|
|
+ /* ERROR: insert count exceeds size of delta */
|
|
+ return -1;
|
|
+ }
|
|
+ memcpy(zOut, zDelta, cnt);
|
|
+ zOut += cnt;
|
|
+ zDelta += cnt;
|
|
+ lenDelta -= cnt;
|
|
+ break;
|
|
+ }
|
|
+ case ';': {
|
|
+ zDelta++; lenDelta--;
|
|
+ zOut[0] = 0;
|
|
+#if RBU_ENABLE_DELTA_CKSUM
|
|
+ if( cnt!=rbuDeltaChecksum(zOrigOut, total) ){
|
|
+ /* ERROR: bad checksum */
|
|
+ return -1;
|
|
+ }
|
|
+#endif
|
|
+ if( total!=limit ){
|
|
+ /* ERROR: generated size does not match predicted size */
|
|
+ return -1;
|
|
+ }
|
|
+ return total;
|
|
+ }
|
|
+ default: {
|
|
+ /* ERROR: unknown delta operator */
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ /* ERROR: unterminated delta */
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int rbuDeltaOutputSize(const char *zDelta, int lenDelta){
|
|
+ int size;
|
|
+ size = rbuDeltaGetInt(&zDelta, &lenDelta);
|
|
+ if( *zDelta!='\n' ){
|
|
+ /* ERROR: size integer not terminated by "\n" */
|
|
+ return -1;
|
|
+ }
|
|
+ return size;
|
|
+}
|
|
+
|
|
+/*
|
|
+** End of code taken from fossil.
|
|
+*************************************************************************/
|
|
+
|
|
+/*
|
|
+** Implementation of SQL scalar function rbu_fossil_delta().
|
|
+**
|
|
+** This function applies a fossil delta patch to a blob. Exactly two
|
|
+** arguments must be passed to this function. The first is the blob to
|
|
+** patch and the second the patch to apply. If no error occurs, this
|
|
+** function returns the patched blob.
|
|
+*/
|
|
+static void rbuFossilDeltaFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *aDelta;
|
|
+ int nDelta;
|
|
+ const char *aOrig;
|
|
+ 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 */
|
|
+ nOut = rbuDeltaOutputSize(aDelta, nDelta);
|
|
+ if( nOut<0 ){
|
|
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ aOut = sqlite3_malloc(nOut+1);
|
|
+ if( aOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut);
|
|
+ if( nOut2!=nOut ){
|
|
+ sqlite3_free(aOut);
|
|
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
|
|
+ }else{
|
|
+ sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Prepare the SQL statement in buffer zSql against database handle db.
|
|
+** If successful, set *ppStmt to point to the new statement and return
|
|
+** SQLITE_OK.
|
|
+**
|
|
+** Otherwise, if an error does occur, set *ppStmt to NULL and return
|
|
+** an SQLite error code. Additionally, set output variable *pzErrmsg to
|
|
+** point to a buffer containing an error message. It is the responsibility
|
|
+** of the caller to (eventually) free this buffer using sqlite3_free().
|
|
+*/
|
|
+static int prepareAndCollectError(
|
|
+ sqlite3 *db,
|
|
+ sqlite3_stmt **ppStmt,
|
|
+ char **pzErrmsg,
|
|
+ const char *zSql
|
|
+){
|
|
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ *ppStmt = 0;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset the SQL statement passed as the first argument. Return a copy
|
|
+** of the value returned by sqlite3_reset().
|
|
+**
|
|
+** If an error has occurred, then set *pzErrmsg to point to a buffer
|
|
+** containing an error message. It is the responsibility of the caller
|
|
+** to eventually free this buffer using sqlite3_free().
|
|
+*/
|
|
+static int resetAndCollectError(sqlite3_stmt *pStmt, char **pzErrmsg){
|
|
+ int rc = sqlite3_reset(pStmt);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt)));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unless it is NULL, argument zSql points to a buffer allocated using
|
|
+** sqlite3_malloc containing an SQL statement. This function prepares the SQL
|
|
+** statement against database db and frees the buffer. If statement
|
|
+** compilation is successful, *ppStmt is set to point to the new statement
|
|
+** handle and SQLITE_OK is returned.
|
|
+**
|
|
+** Otherwise, if an error occurs, *ppStmt is set to NULL and an error code
|
|
+** returned. In this case, *pzErrmsg may also be set to point to an error
|
|
+** message. It is the responsibility of the caller to free this error message
|
|
+** buffer using sqlite3_free().
|
|
+**
|
|
+** If argument zSql is NULL, this function assumes that an OOM has occurred.
|
|
+** In this case SQLITE_NOMEM is returned and *ppStmt set to NULL.
|
|
+*/
|
|
+static int prepareFreeAndCollectError(
|
|
+ sqlite3 *db,
|
|
+ sqlite3_stmt **ppStmt,
|
|
+ char **pzErrmsg,
|
|
+ char *zSql
|
|
+){
|
|
+ int rc;
|
|
+ assert( *pzErrmsg==0 );
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ *ppStmt = 0;
|
|
+ }else{
|
|
+ rc = prepareAndCollectError(db, ppStmt, pzErrmsg, zSql);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the RbuObjIter.azTblCol[] and RbuObjIter.abTblPk[] arrays allocated
|
|
+** by an earlier call to rbuObjIterCacheTableInfo().
|
|
+*/
|
|
+static void rbuObjIterFreeCols(RbuObjIter *pIter){
|
|
+ int i;
|
|
+ for(i=0; i<pIter->nTblCol; i++){
|
|
+ sqlite3_free(pIter->azTblCol[i]);
|
|
+ sqlite3_free(pIter->azTblType[i]);
|
|
+ }
|
|
+ sqlite3_free(pIter->azTblCol);
|
|
+ pIter->azTblCol = 0;
|
|
+ pIter->azTblType = 0;
|
|
+ pIter->aiSrcOrder = 0;
|
|
+ pIter->abTblPk = 0;
|
|
+ pIter->abNotNull = 0;
|
|
+ pIter->nTblCol = 0;
|
|
+ pIter->eType = 0; /* Invalid value */
|
|
+}
|
|
+
|
|
+/*
|
|
+** Finalize all statements and free all allocations that are specific to
|
|
+** the current object (table/index pair).
|
|
+*/
|
|
+static void rbuObjIterClearStatements(RbuObjIter *pIter){
|
|
+ RbuUpdateStmt *pUp;
|
|
+
|
|
+ sqlite3_finalize(pIter->pSelect);
|
|
+ sqlite3_finalize(pIter->pInsert);
|
|
+ sqlite3_finalize(pIter->pDelete);
|
|
+ sqlite3_finalize(pIter->pTmpInsert);
|
|
+ pUp = pIter->pRbuUpdate;
|
|
+ while( pUp ){
|
|
+ RbuUpdateStmt *pTmp = pUp->pNext;
|
|
+ sqlite3_finalize(pUp->pUpdate);
|
|
+ sqlite3_free(pUp);
|
|
+ pUp = pTmp;
|
|
+ }
|
|
+
|
|
+ pIter->pSelect = 0;
|
|
+ pIter->pInsert = 0;
|
|
+ pIter->pDelete = 0;
|
|
+ pIter->pRbuUpdate = 0;
|
|
+ pIter->pTmpInsert = 0;
|
|
+ pIter->nCol = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Clean up any resources allocated as part of the iterator object passed
|
|
+** as the only argument.
|
|
+*/
|
|
+static void rbuObjIterFinalize(RbuObjIter *pIter){
|
|
+ rbuObjIterClearStatements(pIter);
|
|
+ sqlite3_finalize(pIter->pTblIter);
|
|
+ sqlite3_finalize(pIter->pIdxIter);
|
|
+ rbuObjIterFreeCols(pIter);
|
|
+ memset(pIter, 0, sizeof(RbuObjIter));
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance the iterator to the next position.
|
|
+**
|
|
+** If no error occurs, SQLITE_OK is returned and the iterator is left
|
|
+** pointing to the next entry. Otherwise, an error code and message is
|
|
+** left in the RBU handle passed as the first argument. A copy of the
|
|
+** error code is returned.
|
|
+*/
|
|
+static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ int rc = p->rc;
|
|
+ if( rc==SQLITE_OK ){
|
|
+
|
|
+ /* Free any SQLite statements used while processing the previous object */
|
|
+ rbuObjIterClearStatements(pIter);
|
|
+ if( pIter->zIdx==0 ){
|
|
+ rc = sqlite3_exec(p->dbMain,
|
|
+ "DROP TRIGGER IF EXISTS temp.rbu_insert_tr;"
|
|
+ "DROP TRIGGER IF EXISTS temp.rbu_update1_tr;"
|
|
+ "DROP TRIGGER IF EXISTS temp.rbu_update2_tr;"
|
|
+ "DROP TRIGGER IF EXISTS temp.rbu_delete_tr;"
|
|
+ , 0, 0, &p->zErrmsg
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pIter->bCleanup ){
|
|
+ rbuObjIterFreeCols(pIter);
|
|
+ pIter->bCleanup = 0;
|
|
+ rc = sqlite3_step(pIter->pTblIter);
|
|
+ if( rc!=SQLITE_ROW ){
|
|
+ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
|
|
+ pIter->zTbl = 0;
|
|
+ }else{
|
|
+ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
|
|
+ pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
|
|
+ rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM;
|
|
+ }
|
|
+ }else{
|
|
+ if( pIter->zIdx==0 ){
|
|
+ sqlite3_stmt *pIdx = pIter->pIdxIter;
|
|
+ rc = sqlite3_bind_text(pIdx, 1, pIter->zTbl, -1, SQLITE_STATIC);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_step(pIter->pIdxIter);
|
|
+ if( rc!=SQLITE_ROW ){
|
|
+ rc = resetAndCollectError(pIter->pIdxIter, &p->zErrmsg);
|
|
+ pIter->bCleanup = 1;
|
|
+ pIter->zIdx = 0;
|
|
+ }else{
|
|
+ pIter->zIdx = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
|
|
+ pIter->iTnum = sqlite3_column_int(pIter->pIdxIter, 1);
|
|
+ pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
|
|
+ rc = pIter->zIdx ? SQLITE_OK : SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ rbuObjIterFinalize(pIter);
|
|
+ p->rc = rc;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** The implementation of the rbu_target_name() SQL function. This function
|
|
+** accepts one or two arguments. The first argument is the name of a table -
|
|
+** the name of a table in the RBU database. The second, if it is present, is 1
|
|
+** for a view or 0 for a table.
|
|
+**
|
|
+** For a non-vacuum RBU handle, if the table name matches the pattern:
|
|
+**
|
|
+** data[0-9]_<name>
|
|
+**
|
|
+** where <name> is any sequence of 1 or more characters, <name> is returned.
|
|
+** Otherwise, if the only argument does not match the above pattern, an SQL
|
|
+** NULL is returned.
|
|
+**
|
|
+** "data_t1" -> "t1"
|
|
+** "data0123_t2" -> "t2"
|
|
+** "dataAB_t3" -> NULL
|
|
+**
|
|
+** For an rbu vacuum handle, a copy of the first argument is returned if
|
|
+** the second argument is either missing or 0 (not a view).
|
|
+*/
|
|
+static void rbuTargetNameFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3rbu *p = sqlite3_user_data(pCtx);
|
|
+ const char *zIn;
|
|
+ assert( argc==1 || argc==2 );
|
|
+
|
|
+ zIn = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zIn ){
|
|
+ if( rbuIsVacuum(p) ){
|
|
+ if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
|
|
+ sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
|
|
+ }
|
|
+ }else{
|
|
+ if( strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
|
|
+ int i;
|
|
+ for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++);
|
|
+ if( zIn[i]=='_' && zIn[i+1] ){
|
|
+ sqlite3_result_text(pCtx, &zIn[i+1], -1, SQLITE_STATIC);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Initialize the iterator structure passed as the second argument.
|
|
+**
|
|
+** If no error occurs, SQLITE_OK is returned and the iterator is left
|
|
+** pointing to the first entry. Otherwise, an error code and message is
|
|
+** left in the RBU handle passed as the first argument. A copy of the
|
|
+** error code is returned.
|
|
+*/
|
|
+static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ int rc;
|
|
+ memset(pIter, 0, sizeof(RbuObjIter));
|
|
+
|
|
+ rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg,
|
|
+ sqlite3_mprintf(
|
|
+ "SELECT rbu_target_name(name, type='view') AS target, name "
|
|
+ "FROM sqlite_master "
|
|
+ "WHERE type IN ('table', 'view') AND target IS NOT NULL "
|
|
+ " %s "
|
|
+ "ORDER BY name"
|
|
+ , rbuIsVacuum(p) ? "AND rootpage!=0 AND rootpage IS NOT NULL" : ""));
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
|
|
+ "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
|
|
+ " FROM main.sqlite_master "
|
|
+ " WHERE type='index' AND tbl_name = ?"
|
|
+ );
|
|
+ }
|
|
+
|
|
+ pIter->bCleanup = 1;
|
|
+ p->rc = rc;
|
|
+ return rbuObjIterNext(p, pIter);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is a wrapper around "sqlite3_mprintf(zFmt, ...)". If an OOM occurs,
|
|
+** an error code is stored in the RBU handle passed as the first argument.
|
|
+**
|
|
+** If an error has already occurred (p->rc is already set to something other
|
|
+** than SQLITE_OK), then this function returns NULL without modifying the
|
|
+** stored error code. In this case it still calls sqlite3_free() on any
|
|
+** printf() parameters associated with %z conversions.
|
|
+*/
|
|
+static char *rbuMPrintf(sqlite3rbu *p, const char *zFmt, ...){
|
|
+ char *zSql = 0;
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ zSql = sqlite3_vmprintf(zFmt, ap);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( zSql==0 ) p->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ sqlite3_free(zSql);
|
|
+ zSql = 0;
|
|
+ }
|
|
+ va_end(ap);
|
|
+ return zSql;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
|
|
+** arguments are the usual subsitution values. This function performs
|
|
+** the printf() style substitutions and executes the result as an SQL
|
|
+** statement on the RBU handles database.
|
|
+**
|
|
+** If an error occurs, an error code and error message is stored in the
|
|
+** RBU handle. If an error has already occurred when this function is
|
|
+** called, it is a no-op.
|
|
+*/
|
|
+static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ char *zSql;
|
|
+ va_start(ap, zFmt);
|
|
+ zSql = sqlite3_vmprintf(zFmt, ap);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( zSql==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ p->rc = sqlite3_exec(db, zSql, 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+ va_end(ap);
|
|
+ return p->rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Attempt to allocate and return a pointer to a zeroed block of nByte
|
|
+** bytes.
|
|
+**
|
|
+** If an error (i.e. an OOM condition) occurs, return NULL and leave an
|
|
+** error code in the rbu handle passed as the first argument. Or, if an
|
|
+** error has already occurred when this function is called, return NULL
|
|
+** immediately without attempting the allocation or modifying the stored
|
|
+** error code.
|
|
+*/
|
|
+static void *rbuMalloc(sqlite3rbu *p, int nByte){
|
|
+ void *pRet = 0;
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ assert( nByte>0 );
|
|
+ pRet = sqlite3_malloc(nByte);
|
|
+ if( pRet==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pRet, 0, nByte);
|
|
+ }
|
|
+ }
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
|
|
+** there is room for at least nCol elements. If an OOM occurs, store an
|
|
+** error code in the RBU handle passed as the first argument.
|
|
+*/
|
|
+static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
|
|
+ int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
|
|
+ char **azNew;
|
|
+
|
|
+ azNew = (char**)rbuMalloc(p, nByte);
|
|
+ if( azNew ){
|
|
+ pIter->azTblCol = azNew;
|
|
+ pIter->azTblType = &azNew[nCol];
|
|
+ pIter->aiSrcOrder = (int*)&pIter->azTblType[nCol];
|
|
+ pIter->abTblPk = (u8*)&pIter->aiSrcOrder[nCol];
|
|
+ pIter->abNotNull = (u8*)&pIter->abTblPk[nCol];
|
|
+ pIter->abIndexed = (u8*)&pIter->abNotNull[nCol];
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The first argument must be a nul-terminated string. This function
|
|
+** returns a copy of the string in memory obtained from sqlite3_malloc().
|
|
+** It is the responsibility of the caller to eventually free this memory
|
|
+** using sqlite3_free().
|
|
+**
|
|
+** If an OOM condition is encountered when attempting to allocate memory,
|
|
+** output variable (*pRc) is set to SQLITE_NOMEM before returning. Otherwise,
|
|
+** if the allocation succeeds, (*pRc) is left unchanged.
|
|
+*/
|
|
+static char *rbuStrndup(const char *zStr, int *pRc){
|
|
+ char *zRet = 0;
|
|
+
|
|
+ assert( *pRc==SQLITE_OK );
|
|
+ if( zStr ){
|
|
+ size_t nCopy = strlen(zStr) + 1;
|
|
+ zRet = (char*)sqlite3_malloc(nCopy);
|
|
+ if( zRet ){
|
|
+ memcpy(zRet, zStr, nCopy);
|
|
+ }else{
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Finalize the statement passed as the second argument.
|
|
+**
|
|
+** If the sqlite3_finalize() call indicates that an error occurs, and the
|
|
+** rbu handle error code is not already set, set the error code and error
|
|
+** message accordingly.
|
|
+*/
|
|
+static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
|
|
+ sqlite3 *db = sqlite3_db_handle(pStmt);
|
|
+ int rc = sqlite3_finalize(pStmt);
|
|
+ if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){
|
|
+ p->rc = rc;
|
|
+ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Determine the type of a table.
|
|
+**
|
|
+** peType is of type (int*), a pointer to an output parameter of type
|
|
+** (int). This call sets the output parameter as follows, depending
|
|
+** on the type of the table specified by parameters dbName and zTbl.
|
|
+**
|
|
+** RBU_PK_NOTABLE: No such table.
|
|
+** RBU_PK_NONE: Table has an implicit rowid.
|
|
+** RBU_PK_IPK: Table has an explicit IPK column.
|
|
+** RBU_PK_EXTERNAL: Table has an external PK index.
|
|
+** RBU_PK_WITHOUT_ROWID: Table is WITHOUT ROWID.
|
|
+** RBU_PK_VTAB: Table is a virtual table.
|
|
+**
|
|
+** Argument *piPk is also of type (int*), and also points to an output
|
|
+** parameter. Unless the table has an external primary key index
|
|
+** (i.e. unless *peType is set to 3), then *piPk is set to zero. Or,
|
|
+** if the table does have an external primary key index, then *piPk
|
|
+** is set to the root page number of the primary key index before
|
|
+** returning.
|
|
+**
|
|
+** ALGORITHM:
|
|
+**
|
|
+** if( no entry exists in sqlite_master ){
|
|
+** return RBU_PK_NOTABLE
|
|
+** }else if( sql for the entry starts with "CREATE VIRTUAL" ){
|
|
+** return RBU_PK_VTAB
|
|
+** }else if( "PRAGMA index_list()" for the table contains a "pk" index ){
|
|
+** if( the index that is the pk exists in sqlite_master ){
|
|
+** *piPK = rootpage of that index.
|
|
+** return RBU_PK_EXTERNAL
|
|
+** }else{
|
|
+** return RBU_PK_WITHOUT_ROWID
|
|
+** }
|
|
+** }else if( "PRAGMA table_info()" lists one or more "pk" columns ){
|
|
+** return RBU_PK_IPK
|
|
+** }else{
|
|
+** return RBU_PK_NONE
|
|
+** }
|
|
+*/
|
|
+static void rbuTableType(
|
|
+ sqlite3rbu *p,
|
|
+ const char *zTab,
|
|
+ int *peType,
|
|
+ int *piTnum,
|
|
+ int *piPk
|
|
+){
|
|
+ /*
|
|
+ ** 0) SELECT count(*) FROM sqlite_master where name=%Q AND IsVirtual(%Q)
|
|
+ ** 1) PRAGMA index_list = ?
|
|
+ ** 2) SELECT count(*) FROM sqlite_master where name=%Q
|
|
+ ** 3) PRAGMA table_info = ?
|
|
+ */
|
|
+ sqlite3_stmt *aStmt[4] = {0, 0, 0, 0};
|
|
+
|
|
+ *peType = RBU_PK_NOTABLE;
|
|
+ *piPk = 0;
|
|
+
|
|
+ assert( p->rc==SQLITE_OK );
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg,
|
|
+ sqlite3_mprintf(
|
|
+ "SELECT (sql LIKE 'create virtual%%'), rootpage"
|
|
+ " FROM sqlite_master"
|
|
+ " WHERE name=%Q", zTab
|
|
+ ));
|
|
+ if( p->rc!=SQLITE_OK || sqlite3_step(aStmt[0])!=SQLITE_ROW ){
|
|
+ /* Either an error, or no such table. */
|
|
+ goto rbuTableType_end;
|
|
+ }
|
|
+ if( sqlite3_column_int(aStmt[0], 0) ){
|
|
+ *peType = RBU_PK_VTAB; /* virtual table */
|
|
+ goto rbuTableType_end;
|
|
+ }
|
|
+ *piTnum = sqlite3_column_int(aStmt[0], 1);
|
|
+
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[1], &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA index_list=%Q",zTab)
|
|
+ );
|
|
+ if( p->rc ) goto rbuTableType_end;
|
|
+ while( sqlite3_step(aStmt[1])==SQLITE_ROW ){
|
|
+ const u8 *zOrig = sqlite3_column_text(aStmt[1], 3);
|
|
+ const u8 *zIdx = sqlite3_column_text(aStmt[1], 1);
|
|
+ if( zOrig && zIdx && zOrig[0]=='p' ){
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg,
|
|
+ sqlite3_mprintf(
|
|
+ "SELECT rootpage FROM sqlite_master WHERE name = %Q", zIdx
|
|
+ ));
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( sqlite3_step(aStmt[2])==SQLITE_ROW ){
|
|
+ *piPk = sqlite3_column_int(aStmt[2], 0);
|
|
+ *peType = RBU_PK_EXTERNAL;
|
|
+ }else{
|
|
+ *peType = RBU_PK_WITHOUT_ROWID;
|
|
+ }
|
|
+ }
|
|
+ goto rbuTableType_end;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[3], &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA table_info=%Q",zTab)
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ while( sqlite3_step(aStmt[3])==SQLITE_ROW ){
|
|
+ if( sqlite3_column_int(aStmt[3],5)>0 ){
|
|
+ *peType = RBU_PK_IPK; /* explicit IPK column */
|
|
+ goto rbuTableType_end;
|
|
+ }
|
|
+ }
|
|
+ *peType = RBU_PK_NONE;
|
|
+ }
|
|
+
|
|
+rbuTableType_end: {
|
|
+ unsigned int i;
|
|
+ for(i=0; i<sizeof(aStmt)/sizeof(aStmt[0]); i++){
|
|
+ rbuFinalize(p, aStmt[i]);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is a helper function for rbuObjIterCacheTableInfo(). It populates
|
|
+** the pIter->abIndexed[] array.
|
|
+*/
|
|
+static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ sqlite3_stmt *pList = 0;
|
|
+ int bIndex = 0;
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ memcpy(pIter->abIndexed, pIter->abTblPk, sizeof(u8)*pIter->nTblCol);
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pList, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ pIter->nIndex = 0;
|
|
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){
|
|
+ const char *zIdx = (const char*)sqlite3_column_text(pList, 1);
|
|
+ sqlite3_stmt *pXInfo = 0;
|
|
+ if( zIdx==0 ) break;
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
|
|
+ );
|
|
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
|
|
+ int iCid = sqlite3_column_int(pXInfo, 1);
|
|
+ if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
|
|
+ }
|
|
+ rbuFinalize(p, pXInfo);
|
|
+ bIndex = 1;
|
|
+ pIter->nIndex++;
|
|
+ }
|
|
+
|
|
+ if( pIter->eType==RBU_PK_WITHOUT_ROWID ){
|
|
+ /* "PRAGMA index_list" includes the main PK b-tree */
|
|
+ pIter->nIndex--;
|
|
+ }
|
|
+
|
|
+ rbuFinalize(p, pList);
|
|
+ if( bIndex==0 ) pIter->abIndexed = 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** If they are not already populated, populate the pIter->azTblCol[],
|
|
+** pIter->abTblPk[], pIter->nTblCol and pIter->bRowid variables according to
|
|
+** the table (not index) that the iterator currently points to.
|
|
+**
|
|
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. If
|
|
+** an error does occur, an error code and error message are also left in
|
|
+** the RBU handle.
|
|
+*/
|
|
+static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ if( pIter->azTblCol==0 ){
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int nCol = 0;
|
|
+ int i; /* for() loop iterator variable */
|
|
+ int bRbuRowid = 0; /* If input table has column "rbu_rowid" */
|
|
+ int iOrder = 0;
|
|
+ int iTnum = 0;
|
|
+
|
|
+ /* Figure out the type of table this step will deal with. */
|
|
+ assert( pIter->eType==0 );
|
|
+ rbuTableType(p, pIter->zTbl, &pIter->eType, &iTnum, &pIter->iPkTnum);
|
|
+ if( p->rc==SQLITE_OK && pIter->eType==RBU_PK_NOTABLE ){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("no such table: %s", pIter->zTbl);
|
|
+ }
|
|
+ if( p->rc ) return p->rc;
|
|
+ if( pIter->zIdx==0 ) pIter->iTnum = iTnum;
|
|
+
|
|
+ assert( pIter->eType==RBU_PK_NONE || pIter->eType==RBU_PK_IPK
|
|
+ || pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_WITHOUT_ROWID
|
|
+ || pIter->eType==RBU_PK_VTAB
|
|
+ );
|
|
+
|
|
+ /* Populate the azTblCol[] and nTblCol variables based on the columns
|
|
+ ** of the input table. Ignore any input table columns that begin with
|
|
+ ** "rbu_". */
|
|
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
|
|
+ sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl)
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ nCol = sqlite3_column_count(pStmt);
|
|
+ rbuAllocateIterArrays(p, pIter, nCol);
|
|
+ }
|
|
+ for(i=0; p->rc==SQLITE_OK && i<nCol; i++){
|
|
+ const char *zName = (const char*)sqlite3_column_name(pStmt, i);
|
|
+ if( sqlite3_strnicmp("rbu_", zName, 4) ){
|
|
+ char *zCopy = rbuStrndup(zName, &p->rc);
|
|
+ pIter->aiSrcOrder[pIter->nTblCol] = pIter->nTblCol;
|
|
+ pIter->azTblCol[pIter->nTblCol++] = zCopy;
|
|
+ }
|
|
+ else if( 0==sqlite3_stricmp("rbu_rowid", zName) ){
|
|
+ bRbuRowid = 1;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ pStmt = 0;
|
|
+
|
|
+ if( p->rc==SQLITE_OK
|
|
+ && rbuIsVacuum(p)==0
|
|
+ && bRbuRowid!=(pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
|
|
+ ){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf(
|
|
+ "table %q %s rbu_rowid column", pIter->zDataTbl,
|
|
+ (bRbuRowid ? "may not have" : "requires")
|
|
+ );
|
|
+ }
|
|
+
|
|
+ /* Check that all non-HIDDEN columns in the destination table are also
|
|
+ ** present in the input table. Populate the abTblPk[], azTblType[] and
|
|
+ ** aiTblOrder[] arrays at the same time. */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl)
|
|
+ );
|
|
+ }
|
|
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
|
|
+ if( zName==0 ) break; /* An OOM - finalize() below returns S_NOMEM */
|
|
+ for(i=iOrder; i<pIter->nTblCol; i++){
|
|
+ if( 0==strcmp(zName, pIter->azTblCol[i]) ) break;
|
|
+ }
|
|
+ if( i==pIter->nTblCol ){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("column missing from %q: %s",
|
|
+ pIter->zDataTbl, zName
|
|
+ );
|
|
+ }else{
|
|
+ int iPk = sqlite3_column_int(pStmt, 5);
|
|
+ int bNotNull = sqlite3_column_int(pStmt, 3);
|
|
+ const char *zType = (const char*)sqlite3_column_text(pStmt, 2);
|
|
+
|
|
+ if( i!=iOrder ){
|
|
+ SWAP(int, pIter->aiSrcOrder[i], pIter->aiSrcOrder[iOrder]);
|
|
+ SWAP(char*, pIter->azTblCol[i], pIter->azTblCol[iOrder]);
|
|
+ }
|
|
+
|
|
+ pIter->azTblType[iOrder] = rbuStrndup(zType, &p->rc);
|
|
+ pIter->abTblPk[iOrder] = (iPk!=0);
|
|
+ pIter->abNotNull[iOrder] = (u8)bNotNull || (iPk!=0);
|
|
+ iOrder++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rbuFinalize(p, pStmt);
|
|
+ rbuObjIterCacheIndexedCols(p, pIter);
|
|
+ assert( pIter->eType!=RBU_PK_VTAB || pIter->abIndexed==0 );
|
|
+ assert( pIter->eType!=RBU_PK_VTAB || pIter->nIndex==0 );
|
|
+ }
|
|
+
|
|
+ return p->rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function constructs and returns a pointer to a nul-terminated
|
|
+** string containing some SQL clause or list based on one or more of the
|
|
+** column names currently stored in the pIter->azTblCol[] array.
|
|
+*/
|
|
+static char *rbuObjIterGetCollist(
|
|
+ sqlite3rbu *p, /* RBU object */
|
|
+ RbuObjIter *pIter /* Object iterator for column names */
|
|
+){
|
|
+ char *zList = 0;
|
|
+ const char *zSep = "";
|
|
+ int i;
|
|
+ for(i=0; i<pIter->nTblCol; i++){
|
|
+ const char *z = pIter->azTblCol[i];
|
|
+ zList = rbuMPrintf(p, "%z%s\"%w\"", zList, zSep, z);
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ return zList;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is used to create a SELECT list (the list of SQL
|
|
+** expressions that follows a SELECT keyword) for a SELECT statement
|
|
+** used to read from an data_xxx or rbu_tmp_xxx table while updating the
|
|
+** index object currently indicated by the iterator object passed as the
|
|
+** second argument. A "PRAGMA index_xinfo = <idxname>" statement is used
|
|
+** to obtain the required information.
|
|
+**
|
|
+** If the index is of the following form:
|
|
+**
|
|
+** CREATE INDEX i1 ON t1(c, b COLLATE nocase);
|
|
+**
|
|
+** and "t1" is a table with an explicit INTEGER PRIMARY KEY column
|
|
+** "ipk", the returned string is:
|
|
+**
|
|
+** "`c` COLLATE 'BINARY', `b` COLLATE 'NOCASE', `ipk` COLLATE 'BINARY'"
|
|
+**
|
|
+** As well as the returned string, three other malloc'd strings are
|
|
+** returned via output parameters. As follows:
|
|
+**
|
|
+** pzImposterCols: ...
|
|
+** pzImposterPk: ...
|
|
+** pzWhere: ...
|
|
+*/
|
|
+static char *rbuObjIterGetIndexCols(
|
|
+ sqlite3rbu *p, /* RBU object */
|
|
+ RbuObjIter *pIter, /* Object iterator for column names */
|
|
+ char **pzImposterCols, /* OUT: Columns for imposter table */
|
|
+ char **pzImposterPk, /* OUT: Imposter PK clause */
|
|
+ char **pzWhere, /* OUT: WHERE clause */
|
|
+ int *pnBind /* OUT: Trbul number of columns */
|
|
+){
|
|
+ int rc = p->rc; /* Error code */
|
|
+ int rc2; /* sqlite3_finalize() return code */
|
|
+ char *zRet = 0; /* String to return */
|
|
+ char *zImpCols = 0; /* String to return via *pzImposterCols */
|
|
+ char *zImpPK = 0; /* String to return via *pzImposterPK */
|
|
+ char *zWhere = 0; /* String to return via *pzWhere */
|
|
+ int nBind = 0; /* Value to return via *pnBind */
|
|
+ const char *zCom = ""; /* Set to ", " later on */
|
|
+ const char *zAnd = ""; /* Set to " AND " later on */
|
|
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = ? */
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ assert( p->zErrmsg==0 );
|
|
+ rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
|
|
+ int iCid = sqlite3_column_int(pXInfo, 1);
|
|
+ int bDesc = sqlite3_column_int(pXInfo, 3);
|
|
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
|
|
+ const char *zCol;
|
|
+ const char *zType;
|
|
+
|
|
+ if( iCid<0 ){
|
|
+ /* An integer primary key. If the table has an explicit IPK, use
|
|
+ ** its name. Otherwise, use "rbu_rowid". */
|
|
+ if( pIter->eType==RBU_PK_IPK ){
|
|
+ int i;
|
|
+ for(i=0; pIter->abTblPk[i]==0; i++);
|
|
+ assert( i<pIter->nTblCol );
|
|
+ zCol = pIter->azTblCol[i];
|
|
+ }else if( rbuIsVacuum(p) ){
|
|
+ zCol = "_rowid_";
|
|
+ }else{
|
|
+ zCol = "rbu_rowid";
|
|
+ }
|
|
+ zType = "INTEGER";
|
|
+ }else{
|
|
+ zCol = pIter->azTblCol[iCid];
|
|
+ zType = pIter->azTblType[iCid];
|
|
+ }
|
|
+
|
|
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
|
|
+ if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
|
|
+ const char *zOrder = (bDesc ? " DESC" : "");
|
|
+ zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
|
|
+ zImpPK, zCom, nBind, zCol, zOrder
|
|
+ );
|
|
+ }
|
|
+ zImpCols = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\" %s COLLATE %Q",
|
|
+ zImpCols, zCom, nBind, zCol, zType, zCollate
|
|
+ );
|
|
+ zWhere = sqlite3_mprintf(
|
|
+ "%z%s\"rbu_imp_%d%w\" IS ?", zWhere, zAnd, nBind, zCol
|
|
+ );
|
|
+ if( zRet==0 || zImpPK==0 || zImpCols==0 || zWhere==0 ) rc = SQLITE_NOMEM;
|
|
+ zCom = ", ";
|
|
+ zAnd = " AND ";
|
|
+ nBind++;
|
|
+ }
|
|
+
|
|
+ rc2 = sqlite3_finalize(pXInfo);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_free(zRet);
|
|
+ sqlite3_free(zImpCols);
|
|
+ sqlite3_free(zImpPK);
|
|
+ sqlite3_free(zWhere);
|
|
+ zRet = 0;
|
|
+ zImpCols = 0;
|
|
+ zImpPK = 0;
|
|
+ zWhere = 0;
|
|
+ p->rc = rc;
|
|
+ }
|
|
+
|
|
+ *pzImposterCols = zImpCols;
|
|
+ *pzImposterPk = zImpPK;
|
|
+ *pzWhere = zWhere;
|
|
+ *pnBind = nBind;
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Assuming the current table columns are "a", "b" and "c", and the zObj
|
|
+** paramter is passed "old", return a string of the form:
|
|
+**
|
|
+** "old.a, old.b, old.b"
|
|
+**
|
|
+** With the column names escaped.
|
|
+**
|
|
+** For tables with implicit rowids - RBU_PK_EXTERNAL and RBU_PK_NONE, append
|
|
+** the text ", old._rowid_" to the returned value.
|
|
+*/
|
|
+static char *rbuObjIterGetOldlist(
|
|
+ sqlite3rbu *p,
|
|
+ RbuObjIter *pIter,
|
|
+ const char *zObj
|
|
+){
|
|
+ char *zList = 0;
|
|
+ if( p->rc==SQLITE_OK && pIter->abIndexed ){
|
|
+ const char *zS = "";
|
|
+ int i;
|
|
+ for(i=0; i<pIter->nTblCol; i++){
|
|
+ if( pIter->abIndexed[i] ){
|
|
+ const char *zCol = pIter->azTblCol[i];
|
|
+ zList = sqlite3_mprintf("%z%s%s.\"%w\"", zList, zS, zObj, zCol);
|
|
+ }else{
|
|
+ zList = sqlite3_mprintf("%z%sNULL", zList, zS);
|
|
+ }
|
|
+ zS = ", ";
|
|
+ if( zList==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* For a table with implicit rowids, append "old._rowid_" to the list. */
|
|
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
|
|
+ zList = rbuMPrintf(p, "%z, %s._rowid_", zList, zObj);
|
|
+ }
|
|
+ }
|
|
+ return zList;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return an expression that can be used in a WHERE clause to match the
|
|
+** primary key of the current table. For example, if the table is:
|
|
+**
|
|
+** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c));
|
|
+**
|
|
+** Return the string:
|
|
+**
|
|
+** "b = ?1 AND c = ?2"
|
|
+*/
|
|
+static char *rbuObjIterGetWhere(
|
|
+ sqlite3rbu *p,
|
|
+ RbuObjIter *pIter
|
|
+){
|
|
+ char *zList = 0;
|
|
+ if( pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE ){
|
|
+ zList = rbuMPrintf(p, "_rowid_ = ?%d", pIter->nTblCol+1);
|
|
+ }else if( pIter->eType==RBU_PK_EXTERNAL ){
|
|
+ const char *zSep = "";
|
|
+ int i;
|
|
+ for(i=0; i<pIter->nTblCol; i++){
|
|
+ if( pIter->abTblPk[i] ){
|
|
+ zList = rbuMPrintf(p, "%z%sc%d=?%d", zList, zSep, i, i+1);
|
|
+ zSep = " AND ";
|
|
+ }
|
|
+ }
|
|
+ zList = rbuMPrintf(p,
|
|
+ "_rowid_ = (SELECT id FROM rbu_imposter2 WHERE %z)", zList
|
|
+ );
|
|
+
|
|
+ }else{
|
|
+ const char *zSep = "";
|
|
+ int i;
|
|
+ for(i=0; i<pIter->nTblCol; i++){
|
|
+ if( pIter->abTblPk[i] ){
|
|
+ const char *zCol = pIter->azTblCol[i];
|
|
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d", zList, zSep, zCol, i+1);
|
|
+ zSep = " AND ";
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return zList;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The SELECT statement iterating through the keys for the current object
|
|
+** (p->objiter.pSelect) currently points to a valid row. However, there
|
|
+** is something wrong with the rbu_control value in the rbu_control value
|
|
+** stored in the (p->nCol+1)'th column. Set the error code and error message
|
|
+** of the RBU handle to something reflecting this.
|
|
+*/
|
|
+static void rbuBadControlError(sqlite3rbu *p){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("invalid rbu_control value");
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Return a nul-terminated string containing the comma separated list of
|
|
+** assignments that should be included following the "SET" keyword of
|
|
+** an UPDATE statement used to update the table object that the iterator
|
|
+** passed as the second argument currently points to if the rbu_control
|
|
+** column of the data_xxx table entry is set to zMask.
|
|
+**
|
|
+** The memory for the returned string is obtained from sqlite3_malloc().
|
|
+** It is the responsibility of the caller to eventually free it using
|
|
+** sqlite3_free().
|
|
+**
|
|
+** If an OOM error is encountered when allocating space for the new
|
|
+** string, an error code is left in the rbu handle passed as the first
|
|
+** argument and NULL is returned. Or, if an error has already occurred
|
|
+** when this function is called, NULL is returned immediately, without
|
|
+** attempting the allocation or modifying the stored error code.
|
|
+*/
|
|
+static char *rbuObjIterGetSetlist(
|
|
+ sqlite3rbu *p,
|
|
+ RbuObjIter *pIter,
|
|
+ const char *zMask
|
|
+){
|
|
+ char *zList = 0;
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ int i;
|
|
+
|
|
+ if( (int)strlen(zMask)!=pIter->nTblCol ){
|
|
+ rbuBadControlError(p);
|
|
+ }else{
|
|
+ const char *zSep = "";
|
|
+ for(i=0; i<pIter->nTblCol; i++){
|
|
+ char c = zMask[pIter->aiSrcOrder[i]];
|
|
+ if( c=='x' ){
|
|
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d",
|
|
+ zList, zSep, pIter->azTblCol[i], i+1
|
|
+ );
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ else if( c=='d' ){
|
|
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_delta(\"%w\", ?%d)",
|
|
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
|
|
+ );
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ else if( c=='f' ){
|
|
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_fossil_delta(\"%w\", ?%d)",
|
|
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
|
|
+ );
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return zList;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a nul-terminated string consisting of nByte comma separated
|
|
+** "?" expressions. For example, if nByte is 3, return a pointer to
|
|
+** a buffer containing the string "?,?,?".
|
|
+**
|
|
+** The memory for the returned string is obtained from sqlite3_malloc().
|
|
+** It is the responsibility of the caller to eventually free it using
|
|
+** sqlite3_free().
|
|
+**
|
|
+** If an OOM error is encountered when allocating space for the new
|
|
+** string, an error code is left in the rbu handle passed as the first
|
|
+** argument and NULL is returned. Or, if an error has already occurred
|
|
+** when this function is called, NULL is returned immediately, without
|
|
+** attempting the allocation or modifying the stored error code.
|
|
+*/
|
|
+static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){
|
|
+ char *zRet = 0;
|
|
+ int nByte = nBind*2 + 1;
|
|
+
|
|
+ zRet = (char*)rbuMalloc(p, nByte);
|
|
+ if( zRet ){
|
|
+ int i;
|
|
+ for(i=0; i<nBind; i++){
|
|
+ zRet[i*2] = '?';
|
|
+ zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';
|
|
+ }
|
|
+ }
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The iterator currently points to a table (not index) of type
|
|
+** RBU_PK_WITHOUT_ROWID. This function creates the PRIMARY KEY
|
|
+** declaration for the corresponding imposter table. For example,
|
|
+** if the iterator points to a table created as:
|
|
+**
|
|
+** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, a DESC)) WITHOUT ROWID
|
|
+**
|
|
+** this function returns:
|
|
+**
|
|
+** PRIMARY KEY("b", "a" DESC)
|
|
+*/
|
|
+static char *rbuWithoutRowidPK(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ char *z = 0;
|
|
+ assert( pIter->zIdx==0 );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ const char *zSep = "PRIMARY KEY(";
|
|
+ sqlite3_stmt *pXList = 0; /* PRAGMA index_list = (pIter->zTbl) */
|
|
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = <pk-index> */
|
|
+
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXList, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
|
|
+ );
|
|
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXList) ){
|
|
+ const char *zOrig = (const char*)sqlite3_column_text(pXList,3);
|
|
+ if( zOrig && strcmp(zOrig, "pk")==0 ){
|
|
+ const char *zIdx = (const char*)sqlite3_column_text(pXList,1);
|
|
+ if( zIdx ){
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
|
|
+ );
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ rbuFinalize(p, pXList);
|
|
+
|
|
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
|
|
+ if( sqlite3_column_int(pXInfo, 5) ){
|
|
+ /* int iCid = sqlite3_column_int(pXInfo, 0); */
|
|
+ const char *zCol = (const char*)sqlite3_column_text(pXInfo, 2);
|
|
+ const char *zDesc = sqlite3_column_int(pXInfo, 3) ? " DESC" : "";
|
|
+ z = rbuMPrintf(p, "%z%s\"%w\"%s", z, zSep, zCol, zDesc);
|
|
+ zSep = ", ";
|
|
+ }
|
|
+ }
|
|
+ z = rbuMPrintf(p, "%z)", z);
|
|
+ rbuFinalize(p, pXInfo);
|
|
+ }
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function creates the second imposter table used when writing to
|
|
+** a table b-tree where the table has an external primary key. If the
|
|
+** iterator passed as the second argument does not currently point to
|
|
+** a table (not index) with an external primary key, this function is a
|
|
+** no-op.
|
|
+**
|
|
+** Assuming the iterator does point to a table with an external PK, this
|
|
+** function creates a WITHOUT ROWID imposter table named "rbu_imposter2"
|
|
+** used to access that PK index. For example, if the target table is
|
|
+** declared as follows:
|
|
+**
|
|
+** CREATE TABLE t1(a, b TEXT, c REAL, PRIMARY KEY(b, c));
|
|
+**
|
|
+** then the imposter table schema is:
|
|
+**
|
|
+** CREATE TABLE rbu_imposter2(c1 TEXT, c2 REAL, id INTEGER) WITHOUT ROWID;
|
|
+**
|
|
+*/
|
|
+static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ if( p->rc==SQLITE_OK && pIter->eType==RBU_PK_EXTERNAL ){
|
|
+ int tnum = pIter->iPkTnum; /* Root page of PK index */
|
|
+ sqlite3_stmt *pQuery = 0; /* SELECT name ... WHERE rootpage = $tnum */
|
|
+ const char *zIdx = 0; /* Name of PK index */
|
|
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA main.index_xinfo = $zIdx */
|
|
+ const char *zComma = "";
|
|
+ char *zCols = 0; /* Used to build up list of table cols */
|
|
+ char *zPk = 0; /* Used to build up table PK declaration */
|
|
+
|
|
+ /* Figure out the name of the primary key index for the current table.
|
|
+ ** This is needed for the argument to "PRAGMA index_xinfo". Set
|
|
+ ** zIdx to point to a nul-terminated string containing this name. */
|
|
+ p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg,
|
|
+ "SELECT name FROM sqlite_master WHERE rootpage = ?"
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_bind_int(pQuery, 1, tnum);
|
|
+ if( SQLITE_ROW==sqlite3_step(pQuery) ){
|
|
+ zIdx = (const char*)sqlite3_column_text(pQuery, 0);
|
|
+ }
|
|
+ }
|
|
+ if( zIdx ){
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
|
|
+ );
|
|
+ }
|
|
+ rbuFinalize(p, pQuery);
|
|
+
|
|
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
|
|
+ int bKey = sqlite3_column_int(pXInfo, 5);
|
|
+ if( bKey ){
|
|
+ int iCid = sqlite3_column_int(pXInfo, 1);
|
|
+ int bDesc = sqlite3_column_int(pXInfo, 3);
|
|
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
|
|
+ zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma,
|
|
+ iCid, pIter->azTblType[iCid], zCollate
|
|
+ );
|
|
+ zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":"");
|
|
+ zComma = ", ";
|
|
+ }
|
|
+ }
|
|
+ zCols = rbuMPrintf(p, "%z, id INTEGER", zCols);
|
|
+ rbuFinalize(p, pXInfo);
|
|
+
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
|
|
+ rbuMPrintfExec(p, p->dbMain,
|
|
+ "CREATE TABLE rbu_imposter2(%z, PRIMARY KEY(%z)) WITHOUT ROWID",
|
|
+ zCols, zPk
|
|
+ );
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** If an error has already occurred when this function is called, it
|
|
+** immediately returns zero (without doing any work). Or, if an error
|
|
+** occurs during the execution of this function, it sets the error code
|
|
+** in the sqlite3rbu object indicated by the first argument and returns
|
|
+** zero.
|
|
+**
|
|
+** The iterator passed as the second argument is guaranteed to point to
|
|
+** a table (not an index) when this function is called. This function
|
|
+** attempts to create any imposter table required to write to the main
|
|
+** table b-tree of the table before returning. Non-zero is returned if
|
|
+** an imposter table are created, or zero otherwise.
|
|
+**
|
|
+** An imposter table is required in all cases except RBU_PK_VTAB. Only
|
|
+** virtual tables are written to directly. The imposter table has the
|
|
+** same schema as the actual target table (less any UNIQUE constraints).
|
|
+** More precisely, the "same schema" means the same columns, types,
|
|
+** collation sequences. For tables that do not have an external PRIMARY
|
|
+** KEY, it also means the same PRIMARY KEY declaration.
|
|
+*/
|
|
+static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
|
|
+ if( p->rc==SQLITE_OK && pIter->eType!=RBU_PK_VTAB ){
|
|
+ int tnum = pIter->iTnum;
|
|
+ const char *zComma = "";
|
|
+ char *zSql = 0;
|
|
+ int iCol;
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
|
|
+
|
|
+ for(iCol=0; p->rc==SQLITE_OK && iCol<pIter->nTblCol; iCol++){
|
|
+ const char *zPk = "";
|
|
+ const char *zCol = pIter->azTblCol[iCol];
|
|
+ const char *zColl = 0;
|
|
+
|
|
+ p->rc = sqlite3_table_column_metadata(
|
|
+ p->dbMain, "main", pIter->zTbl, zCol, 0, &zColl, 0, 0, 0
|
|
+ );
|
|
+
|
|
+ if( pIter->eType==RBU_PK_IPK && pIter->abTblPk[iCol] ){
|
|
+ /* If the target table column is an "INTEGER PRIMARY KEY", add
|
|
+ ** "PRIMARY KEY" to the imposter table column declaration. */
|
|
+ zPk = "PRIMARY KEY ";
|
|
+ }
|
|
+ zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s",
|
|
+ zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl,
|
|
+ (pIter->abNotNull[iCol] ? " NOT NULL" : "")
|
|
+ );
|
|
+ zComma = ", ";
|
|
+ }
|
|
+
|
|
+ if( pIter->eType==RBU_PK_WITHOUT_ROWID ){
|
|
+ char *zPk = rbuWithoutRowidPK(p, pIter);
|
|
+ if( zPk ){
|
|
+ zSql = rbuMPrintf(p, "%z, %z", zSql, zPk);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
|
|
+ rbuMPrintfExec(p, p->dbMain, "CREATE TABLE \"rbu_imp_%w\"(%z)%s",
|
|
+ pIter->zTbl, zSql,
|
|
+ (pIter->eType==RBU_PK_WITHOUT_ROWID ? " WITHOUT ROWID" : "")
|
|
+ );
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Prepare a statement used to insert rows into the "rbu_tmp_xxx" table.
|
|
+** Specifically a statement of the form:
|
|
+**
|
|
+** INSERT INTO rbu_tmp_xxx VALUES(?, ?, ? ...);
|
|
+**
|
|
+** The number of bound variables is equal to the number of columns in
|
|
+** the target table, plus one (for the rbu_control column), plus one more
|
|
+** (for the rbu_rowid column) if the target table is an implicit IPK or
|
|
+** virtual table.
|
|
+*/
|
|
+static void rbuObjIterPrepareTmpInsert(
|
|
+ sqlite3rbu *p,
|
|
+ RbuObjIter *pIter,
|
|
+ const char *zCollist,
|
|
+ const char *zRbuRowid
|
|
+){
|
|
+ int bRbuRowid = (pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE);
|
|
+ char *zBind = rbuObjIterGetBindlist(p, pIter->nTblCol + 1 + bRbuRowid);
|
|
+ if( zBind ){
|
|
+ assert( pIter->pTmpInsert==0 );
|
|
+ p->rc = prepareFreeAndCollectError(
|
|
+ p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf(
|
|
+ "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)",
|
|
+ p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind
|
|
+ ));
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rbuTmpInsertFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal
|
|
+){
|
|
+ sqlite3rbu *p = sqlite3_user_data(pCtx);
|
|
+ int rc = SQLITE_OK;
|
|
+ int i;
|
|
+
|
|
+ assert( sqlite3_value_int(apVal[0])!=0
|
|
+ || p->objiter.eType==RBU_PK_EXTERNAL
|
|
+ || p->objiter.eType==RBU_PK_NONE
|
|
+ );
|
|
+ if( sqlite3_value_int(apVal[0])!=0 ){
|
|
+ p->nPhaseOneStep += p->objiter.nIndex;
|
|
+ }
|
|
+
|
|
+ for(i=0; rc==SQLITE_OK && i<nVal; i++){
|
|
+ rc = sqlite3_bind_value(p->objiter.pTmpInsert, i+1, apVal[i]);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_step(p->objiter.pTmpInsert);
|
|
+ rc = sqlite3_reset(p->objiter.pTmpInsert);
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_result_error_code(pCtx, rc);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Ensure that the SQLite statement handles required to update the
|
|
+** target database object currently indicated by the iterator passed
|
|
+** as the second argument are available.
|
|
+*/
|
|
+static int rbuObjIterPrepareAll(
|
|
+ sqlite3rbu *p,
|
|
+ RbuObjIter *pIter,
|
|
+ int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */
|
|
+){
|
|
+ assert( pIter->bCleanup==0 );
|
|
+ if( pIter->pSelect==0 && rbuObjIterCacheTableInfo(p, pIter)==SQLITE_OK ){
|
|
+ const int tnum = pIter->iTnum;
|
|
+ char *zCollist = 0; /* List of indexed columns */
|
|
+ char **pz = &p->zErrmsg;
|
|
+ const char *zIdx = pIter->zIdx;
|
|
+ char *zLimit = 0;
|
|
+
|
|
+ if( nOffset ){
|
|
+ zLimit = sqlite3_mprintf(" LIMIT -1 OFFSET %d", nOffset);
|
|
+ if( !zLimit ) p->rc = SQLITE_NOMEM;
|
|
+ }
|
|
+
|
|
+ if( zIdx ){
|
|
+ const char *zTbl = pIter->zTbl;
|
|
+ char *zImposterCols = 0; /* Columns for imposter table */
|
|
+ char *zImposterPK = 0; /* Primary key declaration for imposter */
|
|
+ char *zWhere = 0; /* WHERE clause on PK columns */
|
|
+ char *zBind = 0;
|
|
+ int nBind = 0;
|
|
+
|
|
+ assert( pIter->eType!=RBU_PK_VTAB );
|
|
+ zCollist = rbuObjIterGetIndexCols(
|
|
+ p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
|
|
+ );
|
|
+ zBind = rbuObjIterGetBindlist(p, nBind);
|
|
+
|
|
+ /* Create the imposter table used to write to this index. */
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1,tnum);
|
|
+ rbuMPrintfExec(p, p->dbMain,
|
|
+ "CREATE TABLE \"rbu_imp_%w\"( %s, PRIMARY KEY( %s ) ) WITHOUT ROWID",
|
|
+ zTbl, zImposterCols, zImposterPK
|
|
+ );
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
|
|
+
|
|
+ /* Create the statement to insert index entries */
|
|
+ pIter->nCol = nBind;
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareFreeAndCollectError(
|
|
+ p->dbMain, &pIter->pInsert, &p->zErrmsg,
|
|
+ sqlite3_mprintf("INSERT INTO \"rbu_imp_%w\" VALUES(%s)", zTbl, zBind)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ /* And to delete index entries */
|
|
+ if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareFreeAndCollectError(
|
|
+ p->dbMain, &pIter->pDelete, &p->zErrmsg,
|
|
+ sqlite3_mprintf("DELETE FROM \"rbu_imp_%w\" WHERE %s", zTbl, zWhere)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ /* Create the SELECT statement to read keys in sorted order */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ char *zSql;
|
|
+ if( rbuIsVacuum(p) ){
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s",
|
|
+ zCollist,
|
|
+ pIter->zDataTbl,
|
|
+ zCollist, zLimit
|
|
+ );
|
|
+ }else
|
|
+
|
|
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
|
|
+ zCollist, p->zStateDb, pIter->zDataTbl,
|
|
+ zCollist, zLimit
|
|
+ );
|
|
+ }else{
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
|
|
+ "UNION ALL "
|
|
+ "SELECT %s, rbu_control FROM '%q' "
|
|
+ "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
|
|
+ "ORDER BY %s%s",
|
|
+ zCollist, p->zStateDb, pIter->zDataTbl,
|
|
+ zCollist, pIter->zDataTbl,
|
|
+ zCollist, zLimit
|
|
+ );
|
|
+ }
|
|
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zImposterCols);
|
|
+ sqlite3_free(zImposterPK);
|
|
+ sqlite3_free(zWhere);
|
|
+ sqlite3_free(zBind);
|
|
+ }else{
|
|
+ int bRbuRowid = (pIter->eType==RBU_PK_VTAB)
|
|
+ ||(pIter->eType==RBU_PK_NONE)
|
|
+ ||(pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p));
|
|
+ const char *zTbl = pIter->zTbl; /* Table this step applies to */
|
|
+ const char *zWrite; /* Imposter table name */
|
|
+
|
|
+ char *zBindings = rbuObjIterGetBindlist(p, pIter->nTblCol + bRbuRowid);
|
|
+ char *zWhere = rbuObjIterGetWhere(p, pIter);
|
|
+ char *zOldlist = rbuObjIterGetOldlist(p, pIter, "old");
|
|
+ char *zNewlist = rbuObjIterGetOldlist(p, pIter, "new");
|
|
+
|
|
+ zCollist = rbuObjIterGetCollist(p, pIter);
|
|
+ pIter->nCol = pIter->nTblCol;
|
|
+
|
|
+ /* Create the imposter table or tables (if required). */
|
|
+ rbuCreateImposterTable(p, pIter);
|
|
+ rbuCreateImposterTable2(p, pIter);
|
|
+ zWrite = (pIter->eType==RBU_PK_VTAB ? "" : "rbu_imp_");
|
|
+
|
|
+ /* Create the INSERT statement to write to the target PK b-tree */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pInsert, pz,
|
|
+ sqlite3_mprintf(
|
|
+ "INSERT INTO \"%s%w\"(%s%s) VALUES(%s)",
|
|
+ zWrite, zTbl, zCollist, (bRbuRowid ? ", _rowid_" : ""), zBindings
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ /* Create the DELETE statement to write to the target PK b-tree.
|
|
+ ** Because it only performs INSERT operations, this is not required for
|
|
+ ** an rbu vacuum handle. */
|
|
+ if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pDelete, pz,
|
|
+ sqlite3_mprintf(
|
|
+ "DELETE FROM \"%s%w\" WHERE %s", zWrite, zTbl, zWhere
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
|
|
+ const char *zRbuRowid = "";
|
|
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
|
|
+ zRbuRowid = ", rbu_rowid";
|
|
+ }
|
|
+
|
|
+ /* Create the rbu_tmp_xxx table and the triggers to populate it. */
|
|
+ rbuMPrintfExec(p, p->dbRbu,
|
|
+ "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS "
|
|
+ "SELECT *%s FROM '%q' WHERE 0;"
|
|
+ , p->zStateDb, pIter->zDataTbl
|
|
+ , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
|
|
+ , pIter->zDataTbl
|
|
+ );
|
|
+
|
|
+ rbuMPrintfExec(p, p->dbMain,
|
|
+ "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
|
|
+ "BEGIN "
|
|
+ " SELECT rbu_tmp_insert(3, %s);"
|
|
+ "END;"
|
|
+
|
|
+ "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
|
|
+ "BEGIN "
|
|
+ " SELECT rbu_tmp_insert(3, %s);"
|
|
+ "END;"
|
|
+
|
|
+ "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
|
|
+ "BEGIN "
|
|
+ " SELECT rbu_tmp_insert(4, %s);"
|
|
+ "END;",
|
|
+ zWrite, zTbl, zOldlist,
|
|
+ zWrite, zTbl, zOldlist,
|
|
+ zWrite, zTbl, zNewlist
|
|
+ );
|
|
+
|
|
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
|
|
+ rbuMPrintfExec(p, p->dbMain,
|
|
+ "CREATE TEMP TRIGGER rbu_insert_tr AFTER INSERT ON \"%s%w\" "
|
|
+ "BEGIN "
|
|
+ " SELECT rbu_tmp_insert(0, %s);"
|
|
+ "END;",
|
|
+ zWrite, zTbl, zNewlist
|
|
+ );
|
|
+ }
|
|
+
|
|
+ rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid);
|
|
+ }
|
|
+
|
|
+ /* Create the SELECT statement to read keys from data_xxx */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ const char *zRbuRowid = "";
|
|
+ if( bRbuRowid ){
|
|
+ zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
|
|
+ }
|
|
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
|
|
+ sqlite3_mprintf(
|
|
+ "SELECT %s,%s rbu_control%s FROM '%q'%s",
|
|
+ zCollist,
|
|
+ (rbuIsVacuum(p) ? "0 AS " : ""),
|
|
+ zRbuRowid,
|
|
+ pIter->zDataTbl, zLimit
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zWhere);
|
|
+ sqlite3_free(zOldlist);
|
|
+ sqlite3_free(zNewlist);
|
|
+ sqlite3_free(zBindings);
|
|
+ }
|
|
+ sqlite3_free(zCollist);
|
|
+ sqlite3_free(zLimit);
|
|
+ }
|
|
+
|
|
+ return p->rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set output variable *ppStmt to point to an UPDATE statement that may
|
|
+** be used to update the imposter table for the main table b-tree of the
|
|
+** table object that pIter currently points to, assuming that the
|
|
+** rbu_control column of the data_xyz table contains zMask.
|
|
+**
|
|
+** If the zMask string does not specify any columns to update, then this
|
|
+** is not an error. Output variable *ppStmt is set to NULL in this case.
|
|
+*/
|
|
+static int rbuGetUpdateStmt(
|
|
+ sqlite3rbu *p, /* RBU handle */
|
|
+ RbuObjIter *pIter, /* Object iterator */
|
|
+ const char *zMask, /* rbu_control value ('x.x.') */
|
|
+ sqlite3_stmt **ppStmt /* OUT: UPDATE statement handle */
|
|
+){
|
|
+ RbuUpdateStmt **pp;
|
|
+ RbuUpdateStmt *pUp = 0;
|
|
+ int nUp = 0;
|
|
+
|
|
+ /* In case an error occurs */
|
|
+ *ppStmt = 0;
|
|
+
|
|
+ /* Search for an existing statement. If one is found, shift it to the front
|
|
+ ** of the LRU queue and return immediately. Otherwise, leave nUp pointing
|
|
+ ** to the number of statements currently in the cache and pUp to the
|
|
+ ** last object in the list. */
|
|
+ for(pp=&pIter->pRbuUpdate; *pp; pp=&((*pp)->pNext)){
|
|
+ pUp = *pp;
|
|
+ if( strcmp(pUp->zMask, zMask)==0 ){
|
|
+ *pp = pUp->pNext;
|
|
+ pUp->pNext = pIter->pRbuUpdate;
|
|
+ pIter->pRbuUpdate = pUp;
|
|
+ *ppStmt = pUp->pUpdate;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ nUp++;
|
|
+ }
|
|
+ assert( pUp==0 || pUp->pNext==0 );
|
|
+
|
|
+ if( nUp>=SQLITE_RBU_UPDATE_CACHESIZE ){
|
|
+ for(pp=&pIter->pRbuUpdate; *pp!=pUp; pp=&((*pp)->pNext));
|
|
+ *pp = 0;
|
|
+ sqlite3_finalize(pUp->pUpdate);
|
|
+ pUp->pUpdate = 0;
|
|
+ }else{
|
|
+ pUp = (RbuUpdateStmt*)rbuMalloc(p, sizeof(RbuUpdateStmt)+pIter->nTblCol+1);
|
|
+ }
|
|
+
|
|
+ if( pUp ){
|
|
+ char *zWhere = rbuObjIterGetWhere(p, pIter);
|
|
+ char *zSet = rbuObjIterGetSetlist(p, pIter, zMask);
|
|
+ char *zUpdate = 0;
|
|
+
|
|
+ pUp->zMask = (char*)&pUp[1];
|
|
+ memcpy(pUp->zMask, zMask, pIter->nTblCol);
|
|
+ pUp->pNext = pIter->pRbuUpdate;
|
|
+ pIter->pRbuUpdate = pUp;
|
|
+
|
|
+ if( zSet ){
|
|
+ const char *zPrefix = "";
|
|
+
|
|
+ if( pIter->eType!=RBU_PK_VTAB ) zPrefix = "rbu_imp_";
|
|
+ zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s",
|
|
+ zPrefix, pIter->zTbl, zSet, zWhere
|
|
+ );
|
|
+ p->rc = prepareFreeAndCollectError(
|
|
+ p->dbMain, &pUp->pUpdate, &p->zErrmsg, zUpdate
|
|
+ );
|
|
+ *ppStmt = pUp->pUpdate;
|
|
+ }
|
|
+ sqlite3_free(zWhere);
|
|
+ sqlite3_free(zSet);
|
|
+ }
|
|
+
|
|
+ return p->rc;
|
|
+}
|
|
+
|
|
+static sqlite3 *rbuOpenDbhandle(
|
|
+ sqlite3rbu *p,
|
|
+ const char *zName,
|
|
+ int bUseVfs
|
|
+){
|
|
+ sqlite3 *db = 0;
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ const int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_URI;
|
|
+ p->rc = sqlite3_open_v2(zName, &db, flags, bUseVfs ? p->zVfsName : 0);
|
|
+ if( p->rc ){
|
|
+ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ sqlite3_close(db);
|
|
+ db = 0;
|
|
+ }
|
|
+ }
|
|
+ return db;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free an RbuState object allocated by rbuLoadState().
|
|
+*/
|
|
+static void rbuFreeState(RbuState *p){
|
|
+ if( p ){
|
|
+ sqlite3_free(p->zTbl);
|
|
+ sqlite3_free(p->zDataTbl);
|
|
+ sqlite3_free(p->zIdx);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate an RbuState object and load the contents of the rbu_state
|
|
+** table into it. Return a pointer to the new object. It is the
|
|
+** responsibility of the caller to eventually free the object using
|
|
+** sqlite3_free().
|
|
+**
|
|
+** If an error occurs, leave an error code and message in the rbu handle
|
|
+** and return NULL.
|
|
+*/
|
|
+static RbuState *rbuLoadState(sqlite3rbu *p){
|
|
+ RbuState *pRet = 0;
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int rc;
|
|
+ int rc2;
|
|
+
|
|
+ pRet = (RbuState*)rbuMalloc(p, sizeof(RbuState));
|
|
+ if( pRet==0 ) return 0;
|
|
+
|
|
+ rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
|
|
+ sqlite3_mprintf("SELECT k, v FROM %s.rbu_state", p->zStateDb)
|
|
+ );
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ switch( sqlite3_column_int(pStmt, 0) ){
|
|
+ case RBU_STATE_STAGE:
|
|
+ pRet->eStage = sqlite3_column_int(pStmt, 1);
|
|
+ if( pRet->eStage!=RBU_STAGE_OAL
|
|
+ && pRet->eStage!=RBU_STAGE_MOVE
|
|
+ && pRet->eStage!=RBU_STAGE_CKPT
|
|
+ ){
|
|
+ p->rc = SQLITE_CORRUPT;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_TBL:
|
|
+ pRet->zTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_IDX:
|
|
+ pRet->zIdx = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_ROW:
|
|
+ pRet->nRow = sqlite3_column_int(pStmt, 1);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_PROGRESS:
|
|
+ pRet->nProgress = sqlite3_column_int64(pStmt, 1);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_CKPT:
|
|
+ pRet->iWalCksum = sqlite3_column_int64(pStmt, 1);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_COOKIE:
|
|
+ pRet->iCookie = (u32)sqlite3_column_int64(pStmt, 1);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_OALSZ:
|
|
+ pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_PHASEONESTEP:
|
|
+ pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
|
|
+ break;
|
|
+
|
|
+ case RBU_STATE_DATATBL:
|
|
+ pRet->zDataTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ rc = SQLITE_CORRUPT;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ rc2 = sqlite3_finalize(pStmt);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+
|
|
+ p->rc = rc;
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Open the database handle and attach the RBU database as "rbu". If an
|
|
+** error occurs, leave an error code and message in the RBU handle.
|
|
+*/
|
|
+static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
|
|
+ assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
|
|
+ assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
|
|
+
|
|
+ /* Open the RBU database */
|
|
+ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
|
|
+
|
|
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
|
|
+ sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
|
|
+ if( p->zState==0 ){
|
|
+ const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
|
|
+ p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If using separate RBU and state databases, attach the state database to
|
|
+ ** the RBU db handle now. */
|
|
+ if( p->zState ){
|
|
+ rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
|
|
+ memcpy(p->zStateDb, "stat", 4);
|
|
+ }else{
|
|
+ memcpy(p->zStateDb, "main", 4);
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
|
|
+ p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, 0);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* If it has not already been created, create the rbu_state table */
|
|
+ rbuMPrintfExec(p, p->dbRbu, RBU_CREATE_STATE, p->zStateDb);
|
|
+
|
|
+#if 0
|
|
+ if( rbuIsVacuum(p) ){
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ int rc2;
|
|
+ int bOk = 0;
|
|
+ sqlite3_stmt *pCnt = 0;
|
|
+ p->rc = prepareAndCollectError(p->dbRbu, &pCnt, &p->zErrmsg,
|
|
+ "SELECT count(*) FROM stat.sqlite_master"
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK
|
|
+ && sqlite3_step(pCnt)==SQLITE_ROW
|
|
+ && 1==sqlite3_column_int(pCnt, 0)
|
|
+ ){
|
|
+ bOk = 1;
|
|
+ }
|
|
+ rc2 = sqlite3_finalize(pCnt);
|
|
+ if( p->rc==SQLITE_OK ) p->rc = rc2;
|
|
+
|
|
+ if( p->rc==SQLITE_OK && bOk==0 ){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("invalid state database");
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
|
|
+ int bOpen = 0;
|
|
+ int rc;
|
|
+ p->nRbu = 0;
|
|
+ p->pRbuFd = 0;
|
|
+ rc = sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
|
|
+ if( rc!=SQLITE_NOTFOUND ) p->rc = rc;
|
|
+ if( p->eStage>=RBU_STAGE_MOVE ){
|
|
+ bOpen = 1;
|
|
+ }else{
|
|
+ RbuState *pState = rbuLoadState(p);
|
|
+ if( pState ){
|
|
+ bOpen = (pState->eStage>=RBU_STAGE_MOVE);
|
|
+ rbuFreeState(pState);
|
|
+ }
|
|
+ }
|
|
+ if( bOpen ) p->dbMain = rbuOpenDbhandle(p, p->zRbu, p->nRbu<=1);
|
|
+ }
|
|
+
|
|
+ p->eStage = 0;
|
|
+ if( p->rc==SQLITE_OK && p->dbMain==0 ){
|
|
+ if( !rbuIsVacuum(p) ){
|
|
+ p->dbMain = rbuOpenDbhandle(p, p->zTarget, 1);
|
|
+ }else if( p->pRbuFd->pWalFd ){
|
|
+ if( pbRetry ){
|
|
+ p->pRbuFd->bNolock = 0;
|
|
+ sqlite3_close(p->dbRbu);
|
|
+ sqlite3_close(p->dbMain);
|
|
+ p->dbMain = 0;
|
|
+ p->dbRbu = 0;
|
|
+ *pbRetry = 1;
|
|
+ return;
|
|
+ }
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("cannot vacuum wal mode database");
|
|
+ }else{
|
|
+ char *zTarget;
|
|
+ char *zExtra = 0;
|
|
+ if( strlen(p->zRbu)>=5 && 0==memcmp("file:", p->zRbu, 5) ){
|
|
+ zExtra = &p->zRbu[5];
|
|
+ while( *zExtra ){
|
|
+ if( *zExtra++=='?' ) break;
|
|
+ }
|
|
+ if( *zExtra=='\0' ) zExtra = 0;
|
|
+ }
|
|
+
|
|
+ zTarget = sqlite3_mprintf("file:%s-vactmp?rbu_memory=1%s%s",
|
|
+ sqlite3_db_filename(p->dbRbu, "main"),
|
|
+ (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra)
|
|
+ );
|
|
+
|
|
+ if( zTarget==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ return;
|
|
+ }
|
|
+ p->dbMain = rbuOpenDbhandle(p, zTarget, p->nRbu<=1);
|
|
+ sqlite3_free(zTarget);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_create_function(p->dbMain,
|
|
+ "rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_create_function(p->dbMain,
|
|
+ "rbu_fossil_delta", 2, SQLITE_UTF8, 0, rbuFossilDeltaFunc, 0, 0
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_create_function(p->dbRbu,
|
|
+ "rbu_target_name", -1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
|
|
+ }
|
|
+ rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
|
|
+
|
|
+ /* Mark the database file just opened as an RBU target database. If
|
|
+ ** this call returns SQLITE_NOTFOUND, then the RBU vfs is not in use.
|
|
+ ** This is an error. */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_NOTFOUND ){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("rbu vfs not found");
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This routine is a copy of the sqlite3FileSuffix3() routine from the core.
|
|
+** It is a no-op unless SQLITE_ENABLE_8_3_NAMES is defined.
|
|
+**
|
|
+** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
|
|
+** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
|
|
+** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
|
|
+** three characters, then shorten the suffix on z[] to be the last three
|
|
+** characters of the original suffix.
|
|
+**
|
|
+** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
|
|
+** do the suffix shortening regardless of URI parameter.
|
|
+**
|
|
+** Examples:
|
|
+**
|
|
+** test.db-journal => test.nal
|
|
+** test.db-wal => test.wal
|
|
+** test.db-shm => test.shm
|
|
+** test.db-mj7f3319fa => test.9fa
|
|
+*/
|
|
+static void rbuFileSuffix3(const char *zBase, char *z){
|
|
+#ifdef SQLITE_ENABLE_8_3_NAMES
|
|
+#if SQLITE_ENABLE_8_3_NAMES<2
|
|
+ if( sqlite3_uri_boolean(zBase, "8_3_names", 0) )
|
|
+#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.
|
|
+**
|
|
+** The checksum is store in the first page of xShmMap memory as an 8-byte
|
|
+** blob starting at byte offset 40.
|
|
+*/
|
|
+static i64 rbuShmChecksum(sqlite3rbu *p){
|
|
+ i64 iRet = 0;
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_file *pDb = p->pTargetFd->pReal;
|
|
+ u32 volatile *ptr;
|
|
+ p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ iRet = ((i64)ptr[10] << 32) + ptr[11];
|
|
+ }
|
|
+ }
|
|
+ return iRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called as part of initializing or reinitializing an
|
|
+** incremental checkpoint.
|
|
+**
|
|
+** It populates the sqlite3rbu.aFrame[] array with the set of
|
|
+** (wal frame -> db page) copy operations required to checkpoint the
|
|
+** current wal file, and obtains the set of shm locks required to safely
|
|
+** perform the copy operations directly on the file-system.
|
|
+**
|
|
+** If argument pState is not NULL, then the incremental checkpoint is
|
|
+** being resumed. In this case, if the checksum of the wal-index-header
|
|
+** following recovery is not the same as the checksum saved in the RbuState
|
|
+** object, then the rbu handle is set to DONE state. This occurs if some
|
|
+** other client appends a transaction to the wal file in the middle of
|
|
+** an incremental checkpoint.
|
|
+*/
|
|
+static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
|
|
+
|
|
+ /* If pState is NULL, then the wal file may not have been opened and
|
|
+ ** recovered. Running a read-statement here to ensure that doing so
|
|
+ ** does not interfere with the "capture" process below. */
|
|
+ if( pState==0 ){
|
|
+ p->eStage = 0;
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_master", 0, 0, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Assuming no error has occurred, run a "restart" checkpoint with the
|
|
+ ** sqlite3rbu.eStage variable set to CAPTURE. This turns on the following
|
|
+ ** special behaviour in the rbu VFS:
|
|
+ **
|
|
+ ** * If the exclusive shm WRITER or READ0 lock cannot be obtained,
|
|
+ ** the checkpoint fails with SQLITE_BUSY (normally SQLite would
|
|
+ ** proceed with running a passive checkpoint instead of failing).
|
|
+ **
|
|
+ ** * Attempts to read from the *-wal file or write to the database file
|
|
+ ** do not perform any IO. Instead, the frame/page combinations that
|
|
+ ** would be read/written are recorded in the sqlite3rbu.aFrame[]
|
|
+ ** array.
|
|
+ **
|
|
+ ** * Calls to xShmLock(UNLOCK) to release the exclusive shm WRITER,
|
|
+ ** READ0 and CHECKPOINT locks taken as part of the checkpoint are
|
|
+ ** no-ops. These locks will not be released until the connection
|
|
+ ** is closed.
|
|
+ **
|
|
+ ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL
|
|
+ ** error.
|
|
+ **
|
|
+ ** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
|
|
+ ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[]
|
|
+ ** array populated with a set of (frame -> page) mappings. Because the
|
|
+ ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
|
|
+ ** data from the wal file into the database file according to the
|
|
+ ** contents of aFrame[].
|
|
+ */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ int rc2;
|
|
+ p->eStage = RBU_STAGE_CAPTURE;
|
|
+ rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
|
|
+ if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK && p->nFrame>0 ){
|
|
+ p->eStage = RBU_STAGE_CKPT;
|
|
+ p->nStep = (pState ? pState->nRow : 0);
|
|
+ p->aBuf = rbuMalloc(p, p->pgsz);
|
|
+ p->iWalCksum = rbuShmChecksum(p);
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( p->nFrame==0 || (pState && pState->iWalCksum!=p->iWalCksum) ){
|
|
+ p->rc = SQLITE_DONE;
|
|
+ p->eStage = RBU_STAGE_DONE;
|
|
+ }else{
|
|
+ int nSectorSize;
|
|
+ sqlite3_file *pDb = p->pTargetFd->pReal;
|
|
+ sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal;
|
|
+ assert( p->nPagePerSector==0 );
|
|
+ nSectorSize = pDb->pMethods->xSectorSize(pDb);
|
|
+ if( nSectorSize>p->pgsz ){
|
|
+ p->nPagePerSector = nSectorSize / p->pgsz;
|
|
+ }else{
|
|
+ p->nPagePerSector = 1;
|
|
+ }
|
|
+
|
|
+ /* Call xSync() on the wal file. This causes SQLite to sync the
|
|
+ ** directory in which the target database and the wal file reside, in
|
|
+ ** case it has not been synced since the rename() call in
|
|
+ ** rbuMoveOalFile(). */
|
|
+ p->rc = pWal->pMethods->xSync(pWal, SQLITE_SYNC_NORMAL);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called when iAmt bytes are read from offset iOff of the wal file while
|
|
+** the rbu object is in capture mode. Record the frame number of the frame
|
|
+** being read in the aFrame[] array.
|
|
+*/
|
|
+static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
|
|
+ const u32 mReq = (1<<WAL_LOCK_WRITE)|(1<<WAL_LOCK_CKPT)|(1<<WAL_LOCK_READ0);
|
|
+ u32 iFrame;
|
|
+
|
|
+ if( pRbu->mLock!=mReq ){
|
|
+ pRbu->rc = SQLITE_BUSY;
|
|
+ return SQLITE_INTERNAL;
|
|
+ }
|
|
+
|
|
+ pRbu->pgsz = iAmt;
|
|
+ if( pRbu->nFrame==pRbu->nFrameAlloc ){
|
|
+ int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2;
|
|
+ RbuFrame *aNew;
|
|
+ aNew = (RbuFrame*)sqlite3_realloc(pRbu->aFrame, nNew * sizeof(RbuFrame));
|
|
+ if( aNew==0 ) return SQLITE_NOMEM;
|
|
+ pRbu->aFrame = aNew;
|
|
+ pRbu->nFrameAlloc = nNew;
|
|
+ }
|
|
+
|
|
+ iFrame = (u32)((iOff-32) / (i64)(iAmt+24)) + 1;
|
|
+ if( pRbu->iMaxFrame<iFrame ) pRbu->iMaxFrame = iFrame;
|
|
+ pRbu->aFrame[pRbu->nFrame].iWalFrame = iFrame;
|
|
+ pRbu->aFrame[pRbu->nFrame].iDbPage = 0;
|
|
+ pRbu->nFrame++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called when a page of data is written to offset iOff of the database
|
|
+** file while the rbu handle is in capture mode. Record the page number
|
|
+** of the page being written in the aFrame[] array.
|
|
+*/
|
|
+static int rbuCaptureDbWrite(sqlite3rbu *pRbu, i64 iOff){
|
|
+ pRbu->aFrame[pRbu->nFrame-1].iDbPage = (u32)(iOff / pRbu->pgsz) + 1;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is called as part of an incremental checkpoint operation. Copy
|
|
+** a single frame of data from the wal file into the database file, as
|
|
+** indicated by the RbuFrame object.
|
|
+*/
|
|
+static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){
|
|
+ sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal;
|
|
+ sqlite3_file *pDb = p->pTargetFd->pReal;
|
|
+ i64 iOff;
|
|
+
|
|
+ assert( p->rc==SQLITE_OK );
|
|
+ iOff = (i64)(pFrame->iWalFrame-1) * (p->pgsz + 24) + 32 + 24;
|
|
+ p->rc = pWal->pMethods->xRead(pWal, p->aBuf, p->pgsz, iOff);
|
|
+ if( p->rc ) return;
|
|
+
|
|
+ iOff = (i64)(pFrame->iDbPage-1) * p->pgsz;
|
|
+ p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Take an EXCLUSIVE lock on the database file.
|
|
+*/
|
|
+static void rbuLockDatabase(sqlite3rbu *p){
|
|
+ sqlite3_file *pReal = p->pTargetFd->pReal;
|
|
+ assert( p->rc==SQLITE_OK );
|
|
+ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE);
|
|
+ }
|
|
+}
|
|
+
|
|
+#if defined(_WIN32_WCE)
|
|
+static LPWSTR rbuWinUtf8ToUnicode(const char *zFilename){
|
|
+ int nChar;
|
|
+ LPWSTR zWideFilename;
|
|
+
|
|
+ nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
|
|
+ if( nChar==0 ){
|
|
+ return 0;
|
|
+ }
|
|
+ zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) );
|
|
+ if( zWideFilename==0 ){
|
|
+ return 0;
|
|
+ }
|
|
+ memset(zWideFilename, 0, nChar*sizeof(zWideFilename[0]));
|
|
+ nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename,
|
|
+ nChar);
|
|
+ if( nChar==0 ){
|
|
+ sqlite3_free(zWideFilename);
|
|
+ zWideFilename = 0;
|
|
+ }
|
|
+ return zWideFilename;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The RBU handle is currently in RBU_STAGE_OAL state, with a SHARED lock
|
|
+** on the database file. This proc moves the *-oal file to the *-wal path,
|
|
+** then reopens the database file (this time in vanilla, non-oal, WAL mode).
|
|
+** If an error occurs, leave an error code and error message in the rbu
|
|
+** handle.
|
|
+*/
|
|
+static void rbuMoveOalFile(sqlite3rbu *p){
|
|
+ const char *zBase = sqlite3_db_filename(p->dbMain, "main");
|
|
+ const char *zMove = zBase;
|
|
+ char *zOal;
|
|
+ char *zWal;
|
|
+
|
|
+ if( rbuIsVacuum(p) ){
|
|
+ zMove = sqlite3_db_filename(p->dbRbu, "main");
|
|
+ }
|
|
+ zOal = sqlite3_mprintf("%s-oal", zMove);
|
|
+ zWal = sqlite3_mprintf("%s-wal", zMove);
|
|
+
|
|
+ assert( p->eStage==RBU_STAGE_MOVE );
|
|
+ assert( p->rc==SQLITE_OK && p->zErrmsg==0 );
|
|
+ if( zWal==0 || zOal==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ /* Move the *-oal file to *-wal. At this point connection p->db is
|
|
+ ** holding a SHARED lock on the target database file (because it is
|
|
+ ** in WAL mode). So no other connection may be writing the db.
|
|
+ **
|
|
+ ** In order to ensure that there are no database readers, an EXCLUSIVE
|
|
+ ** lock is obtained here before the *-oal is moved to *-wal.
|
|
+ */
|
|
+ rbuLockDatabase(p);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ rbuFileSuffix3(zBase, zWal);
|
|
+ rbuFileSuffix3(zBase, zOal);
|
|
+
|
|
+ /* Re-open the databases. */
|
|
+ rbuObjIterFinalize(&p->objiter);
|
|
+ sqlite3_close(p->dbRbu);
|
|
+ sqlite3_close(p->dbMain);
|
|
+ p->dbMain = 0;
|
|
+ p->dbRbu = 0;
|
|
+
|
|
+#if defined(_WIN32_WCE)
|
|
+ {
|
|
+ LPWSTR zWideOal;
|
|
+ LPWSTR zWideWal;
|
|
+
|
|
+ zWideOal = rbuWinUtf8ToUnicode(zOal);
|
|
+ if( zWideOal ){
|
|
+ zWideWal = rbuWinUtf8ToUnicode(zWal);
|
|
+ if( zWideWal ){
|
|
+ if( MoveFileW(zWideOal, zWideWal) ){
|
|
+ p->rc = SQLITE_OK;
|
|
+ }else{
|
|
+ p->rc = SQLITE_IOERR;
|
|
+ }
|
|
+ sqlite3_free(zWideWal);
|
|
+ }else{
|
|
+ p->rc = SQLITE_IOERR_NOMEM;
|
|
+ }
|
|
+ sqlite3_free(zWideOal);
|
|
+ }else{
|
|
+ p->rc = SQLITE_IOERR_NOMEM;
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
|
|
+#endif
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ rbuOpenDatabase(p, 0);
|
|
+ rbuSetupCheckpoint(p, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zWal);
|
|
+ sqlite3_free(zOal);
|
|
+}
|
|
+
|
|
+/*
|
|
+** The SELECT statement iterating through the keys for the current object
|
|
+** (p->objiter.pSelect) currently points to a valid row. This function
|
|
+** determines the type of operation requested by this row and returns
|
|
+** one of the following values to indicate the result:
|
|
+**
|
|
+** * RBU_INSERT
|
|
+** * RBU_DELETE
|
|
+** * RBU_IDX_DELETE
|
|
+** * RBU_UPDATE
|
|
+**
|
|
+** If RBU_UPDATE is returned, then output variable *pzMask is set to
|
|
+** point to the text value indicating the columns to update.
|
|
+**
|
|
+** If the rbu_control field contains an invalid value, an error code and
|
|
+** message are left in the RBU handle and zero returned.
|
|
+*/
|
|
+static int rbuStepType(sqlite3rbu *p, const char **pzMask){
|
|
+ int iCol = p->objiter.nCol; /* Index of rbu_control column */
|
|
+ int res = 0; /* Return value */
|
|
+
|
|
+ switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
|
|
+ case SQLITE_INTEGER: {
|
|
+ int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
|
|
+ switch( iVal ){
|
|
+ case 0: res = RBU_INSERT; break;
|
|
+ case 1: res = RBU_DELETE; break;
|
|
+ case 2: res = RBU_REPLACE; break;
|
|
+ case 3: res = RBU_IDX_DELETE; break;
|
|
+ case 4: res = RBU_IDX_INSERT; break;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case SQLITE_TEXT: {
|
|
+ const unsigned char *z = sqlite3_column_text(p->objiter.pSelect, iCol);
|
|
+ if( z==0 ){
|
|
+ p->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ *pzMask = (const char*)z;
|
|
+ }
|
|
+ res = RBU_UPDATE;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if( res==0 ){
|
|
+ rbuBadControlError(p);
|
|
+ }
|
|
+ return res;
|
|
+}
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+/*
|
|
+** Assert that column iCol of statement pStmt is named zName.
|
|
+*/
|
|
+static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
|
|
+ const char *zCol = sqlite3_column_name(pStmt, iCol);
|
|
+ assert( 0==sqlite3_stricmp(zName, zCol) );
|
|
+}
|
|
+#else
|
|
+# define assertColumnName(x,y,z)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or
|
|
+** RBU_IDX_DELETE. This function performs the work of a single
|
|
+** sqlite3rbu_step() call for the type of operation specified by eType.
|
|
+*/
|
|
+static void rbuStepOneOp(sqlite3rbu *p, int eType){
|
|
+ RbuObjIter *pIter = &p->objiter;
|
|
+ sqlite3_value *pVal;
|
|
+ sqlite3_stmt *pWriter;
|
|
+ int i;
|
|
+
|
|
+ assert( p->rc==SQLITE_OK );
|
|
+ assert( eType!=RBU_DELETE || pIter->zIdx==0 );
|
|
+ assert( eType==RBU_DELETE || eType==RBU_IDX_DELETE
|
|
+ || eType==RBU_INSERT || eType==RBU_IDX_INSERT
|
|
+ );
|
|
+
|
|
+ /* If this is a delete, decrement nPhaseOneStep by nIndex. If the DELETE
|
|
+ ** statement below does actually delete a row, nPhaseOneStep will be
|
|
+ ** incremented by the same amount when SQL function rbu_tmp_insert()
|
|
+ ** is invoked by the trigger. */
|
|
+ if( eType==RBU_DELETE ){
|
|
+ p->nPhaseOneStep -= p->objiter.nIndex;
|
|
+ }
|
|
+
|
|
+ if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
|
|
+ pWriter = pIter->pDelete;
|
|
+ }else{
|
|
+ pWriter = pIter->pInsert;
|
|
+ }
|
|
+
|
|
+ for(i=0; i<pIter->nCol; i++){
|
|
+ /* If this is an INSERT into a table b-tree and the table has an
|
|
+ ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
|
|
+ ** to write a NULL into the IPK column. That is not permitted. */
|
|
+ if( eType==RBU_INSERT
|
|
+ && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
|
|
+ && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
|
|
+ ){
|
|
+ p->rc = SQLITE_MISMATCH;
|
|
+ p->zErrmsg = sqlite3_mprintf("datatype mismatch");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ pVal = sqlite3_column_value(pIter->pSelect, i);
|
|
+ p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
|
|
+ if( p->rc ) return;
|
|
+ }
|
|
+ if( pIter->zIdx==0 ){
|
|
+ if( pIter->eType==RBU_PK_VTAB
|
|
+ || pIter->eType==RBU_PK_NONE
|
|
+ || (pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p))
|
|
+ ){
|
|
+ /* For a virtual table, or a table with no primary key, the
|
|
+ ** SELECT statement is:
|
|
+ **
|
|
+ ** SELECT <cols>, rbu_control, rbu_rowid FROM ....
|
|
+ **
|
|
+ ** Hence column_value(pIter->nCol+1).
|
|
+ */
|
|
+ assertColumnName(pIter->pSelect, pIter->nCol+1,
|
|
+ rbuIsVacuum(p) ? "rowid" : "rbu_rowid"
|
|
+ );
|
|
+ pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
|
|
+ p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
|
|
+ }
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_step(pWriter);
|
|
+ p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function does the work for an sqlite3rbu_step() call.
|
|
+**
|
|
+** The object-iterator (p->objiter) currently points to a valid object,
|
|
+** and the input cursor (p->objiter.pSelect) currently points to a valid
|
|
+** input row. Perform whatever processing is required and return.
|
|
+**
|
|
+** If no error occurs, SQLITE_OK is returned. Otherwise, an error code
|
|
+** and message is left in the RBU handle and a copy of the error code
|
|
+** returned.
|
|
+*/
|
|
+static int rbuStep(sqlite3rbu *p){
|
|
+ RbuObjIter *pIter = &p->objiter;
|
|
+ const char *zMask = 0;
|
|
+ int eType = rbuStepType(p, &zMask);
|
|
+
|
|
+ if( eType ){
|
|
+ assert( eType==RBU_INSERT || eType==RBU_DELETE
|
|
+ || eType==RBU_REPLACE || eType==RBU_IDX_DELETE
|
|
+ || eType==RBU_IDX_INSERT || eType==RBU_UPDATE
|
|
+ );
|
|
+ assert( eType!=RBU_UPDATE || pIter->zIdx==0 );
|
|
+
|
|
+ if( pIter->zIdx==0 && (eType==RBU_IDX_DELETE || eType==RBU_IDX_INSERT) ){
|
|
+ rbuBadControlError(p);
|
|
+ }
|
|
+ else if( eType==RBU_REPLACE ){
|
|
+ if( pIter->zIdx==0 ){
|
|
+ p->nPhaseOneStep += p->objiter.nIndex;
|
|
+ rbuStepOneOp(p, RBU_DELETE);
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT);
|
|
+ }
|
|
+ else if( eType!=RBU_UPDATE ){
|
|
+ rbuStepOneOp(p, eType);
|
|
+ }
|
|
+ else{
|
|
+ sqlite3_value *pVal;
|
|
+ sqlite3_stmt *pUpdate = 0;
|
|
+ assert( eType==RBU_UPDATE );
|
|
+ p->nPhaseOneStep -= p->objiter.nIndex;
|
|
+ rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
|
|
+ if( pUpdate ){
|
|
+ int i;
|
|
+ for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
|
|
+ char c = zMask[pIter->aiSrcOrder[i]];
|
|
+ pVal = sqlite3_column_value(pIter->pSelect, i);
|
|
+ if( pIter->abTblPk[i] || c!='.' ){
|
|
+ p->rc = sqlite3_bind_value(pUpdate, i+1, pVal);
|
|
+ }
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK
|
|
+ && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
|
|
+ ){
|
|
+ /* Bind the rbu_rowid value to column _rowid_ */
|
|
+ assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
|
|
+ pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
|
|
+ p->rc = sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal);
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_step(pUpdate);
|
|
+ p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return p->rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Increment the schema cookie of the main database opened by p->dbMain.
|
|
+**
|
|
+** Or, if this is an RBU vacuum, set the schema cookie of the main db
|
|
+** opened by p->dbMain to one more than the schema cookie of the main
|
|
+** db opened by p->dbRbu.
|
|
+*/
|
|
+static void rbuIncrSchemaCookie(sqlite3rbu *p){
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3 *dbread = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
|
|
+ int iCookie = 1000000;
|
|
+ sqlite3_stmt *pStmt;
|
|
+
|
|
+ p->rc = prepareAndCollectError(dbread, &pStmt, &p->zErrmsg,
|
|
+ "PRAGMA schema_version"
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ /* Coverage: it may be that this sqlite3_step() cannot fail. There
|
|
+ ** is already a transaction open, so the prepared statement cannot
|
|
+ ** throw an SQLITE_SCHEMA exception. The only database page the
|
|
+ ** statement reads is page 1, which is guaranteed to be in the cache.
|
|
+ ** And no memory allocations are required. */
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ iCookie = sqlite3_column_int(pStmt, 0);
|
|
+ }
|
|
+ rbuFinalize(p, pStmt);
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ rbuMPrintfExec(p, p->dbMain, "PRAGMA schema_version = %d", iCookie+1);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Update the contents of the rbu_state table within the rbu database. The
|
|
+** value stored in the RBU_STATE_STAGE column is eStage. All other values
|
|
+** are determined by inspecting the rbu handle passed as the first argument.
|
|
+*/
|
|
+static void rbuSaveState(sqlite3rbu *p, int eStage){
|
|
+ if( p->rc==SQLITE_OK || p->rc==SQLITE_DONE ){
|
|
+ sqlite3_stmt *pInsert = 0;
|
|
+ rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
|
|
+ int rc;
|
|
+
|
|
+ assert( p->zErrmsg==0 );
|
|
+ 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, %d), "
|
|
+ "(%d, %lld), "
|
|
+ "(%d, %lld), "
|
|
+ "(%d, %lld), "
|
|
+ "(%d, %lld), "
|
|
+ "(%d, %Q) ",
|
|
+ p->zStateDb,
|
|
+ RBU_STATE_STAGE, eStage,
|
|
+ RBU_STATE_TBL, p->objiter.zTbl,
|
|
+ RBU_STATE_IDX, p->objiter.zIdx,
|
|
+ RBU_STATE_ROW, p->nStep,
|
|
+ RBU_STATE_PROGRESS, p->nProgress,
|
|
+ RBU_STATE_CKPT, p->iWalCksum,
|
|
+ RBU_STATE_COOKIE, (i64)pFd->iCookie,
|
|
+ RBU_STATE_OALSZ, p->iOalSz,
|
|
+ RBU_STATE_PHASEONESTEP, p->nPhaseOneStep,
|
|
+ RBU_STATE_DATATBL, p->objiter.zDataTbl
|
|
+ )
|
|
+ );
|
|
+ assert( pInsert==0 || rc==SQLITE_OK );
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_step(pInsert);
|
|
+ rc = sqlite3_finalize(pInsert);
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ) p->rc = rc;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** The second argument passed to this function is the name of a PRAGMA
|
|
+** setting - "page_size", "auto_vacuum", "user_version" or "application_id".
|
|
+** This function executes the following on sqlite3rbu.dbRbu:
|
|
+**
|
|
+** "PRAGMA main.$zPragma"
|
|
+**
|
|
+** where $zPragma is the string passed as the second argument, then
|
|
+** on sqlite3rbu.dbMain:
|
|
+**
|
|
+** "PRAGMA main.$zPragma = $val"
|
|
+**
|
|
+** where $val is the value returned by the first PRAGMA invocation.
|
|
+**
|
|
+** In short, it copies the value of the specified PRAGMA setting from
|
|
+** dbRbu to dbMain.
|
|
+*/
|
|
+static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_stmt *pPragma = 0;
|
|
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
|
|
+ sqlite3_mprintf("PRAGMA main.%s", zPragma)
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPragma) ){
|
|
+ p->rc = rbuMPrintfExec(p, p->dbMain, "PRAGMA main.%s = %d",
|
|
+ zPragma, sqlite3_column_int(pPragma, 0)
|
|
+ );
|
|
+ }
|
|
+ rbuFinalize(p, pPragma);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The RBU handle passed as the only argument has just been opened and
|
|
+** the state database is empty. If this RBU handle was opened for an
|
|
+** RBU vacuum operation, create the schema in the target db.
|
|
+*/
|
|
+static void rbuCreateTargetSchema(sqlite3rbu *p){
|
|
+ sqlite3_stmt *pSql = 0;
|
|
+ sqlite3_stmt *pInsert = 0;
|
|
+
|
|
+ assert( rbuIsVacuum(p) );
|
|
+ p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
|
+ "SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
|
|
+ " AND name!='sqlite_sequence' "
|
|
+ " ORDER BY type DESC"
|
|
+ );
|
|
+ }
|
|
+
|
|
+ while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
|
|
+ const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
|
|
+ p->rc = sqlite3_exec(p->dbMain, zSql, 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+ rbuFinalize(p, pSql);
|
|
+ if( p->rc!=SQLITE_OK ) return;
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
|
+ "SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
|
|
+ "INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
|
|
+ );
|
|
+ }
|
|
+
|
|
+ while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
|
|
+ int i;
|
|
+ for(i=0; i<5; i++){
|
|
+ sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
|
|
+ }
|
|
+ sqlite3_step(pInsert);
|
|
+ p->rc = sqlite3_reset(pInsert);
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=0",0,0,&p->zErrmsg);
|
|
+ }
|
|
+
|
|
+ rbuFinalize(p, pSql);
|
|
+ rbuFinalize(p, pInsert);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Step the RBU object.
|
|
+*/
|
|
+int sqlite3rbu_step(sqlite3rbu *p){
|
|
+ if( p ){
|
|
+ switch( p->eStage ){
|
|
+ case RBU_STAGE_OAL: {
|
|
+ RbuObjIter *pIter = &p->objiter;
|
|
+
|
|
+ /* If this is an RBU vacuum operation and the state table was empty
|
|
+ ** when this handle was opened, create the target database schema. */
|
|
+ if( rbuIsVacuum(p) && p->nProgress==0 && p->rc==SQLITE_OK ){
|
|
+ rbuCreateTargetSchema(p);
|
|
+ rbuCopyPragma(p, "user_version");
|
|
+ rbuCopyPragma(p, "application_id");
|
|
+ }
|
|
+
|
|
+ while( p->rc==SQLITE_OK && pIter->zTbl ){
|
|
+
|
|
+ if( pIter->bCleanup ){
|
|
+ /* Clean up the rbu_tmp_xxx table for the previous table. It
|
|
+ ** cannot be dropped as there are currently active SQL statements.
|
|
+ ** But the contents can be deleted. */
|
|
+ if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
|
|
+ rbuMPrintfExec(p, p->dbRbu,
|
|
+ "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl
|
|
+ );
|
|
+ }
|
|
+ }else{
|
|
+ rbuObjIterPrepareAll(p, pIter, 0);
|
|
+
|
|
+ /* Advance to the next row to process. */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ int rc = sqlite3_step(pIter->pSelect);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ p->nProgress++;
|
|
+ p->nStep++;
|
|
+ return rbuStep(p);
|
|
+ }
|
|
+ p->rc = sqlite3_reset(pIter->pSelect);
|
|
+ p->nStep = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rbuObjIterNext(p, pIter);
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ assert( pIter->zTbl==0 );
|
|
+ rbuSaveState(p, RBU_STAGE_MOVE);
|
|
+ rbuIncrSchemaCookie(p);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+ p->eStage = RBU_STAGE_MOVE;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case RBU_STAGE_MOVE: {
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ rbuMoveOalFile(p);
|
|
+ p->nProgress++;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case RBU_STAGE_CKPT: {
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( p->nStep>=p->nFrame ){
|
|
+ sqlite3_file *pDb = p->pTargetFd->pReal;
|
|
+
|
|
+ /* Sync the db file */
|
|
+ p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL);
|
|
+
|
|
+ /* Update nBackfill */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ void volatile *ptr;
|
|
+ p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, &ptr);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ ((u32 volatile*)ptr)[24] = p->iMaxFrame;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->eStage = RBU_STAGE_DONE;
|
|
+ p->rc = SQLITE_DONE;
|
|
+ }
|
|
+ }else{
|
|
+ /* At one point the following block copied a single frame from the
|
|
+ ** wal file to the database file. So that one call to sqlite3rbu_step()
|
|
+ ** checkpointed a single frame.
|
|
+ **
|
|
+ ** However, if the sector-size is larger than the page-size, and the
|
|
+ ** application calls sqlite3rbu_savestate() or close() immediately
|
|
+ ** after this step, then rbu_step() again, then a power failure occurs,
|
|
+ ** then the database page written here may be damaged. Work around
|
|
+ ** this by checkpointing frames until the next page in the aFrame[]
|
|
+ ** lies on a different disk sector to the current one. */
|
|
+ u32 iSector;
|
|
+ do{
|
|
+ RbuFrame *pFrame = &p->aFrame[p->nStep];
|
|
+ iSector = (pFrame->iDbPage-1) / p->nPagePerSector;
|
|
+ rbuCheckpointFrame(p, pFrame);
|
|
+ p->nStep++;
|
|
+ }while( p->nStep<p->nFrame
|
|
+ && iSector==((p->aFrame[p->nStep].iDbPage-1) / p->nPagePerSector)
|
|
+ && p->rc==SQLITE_OK
|
|
+ );
|
|
+ }
|
|
+ p->nProgress++;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return p->rc;
|
|
+ }else{
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compare strings z1 and z2, returning 0 if they are identical, or non-zero
|
|
+** otherwise. Either or both argument may be NULL. Two NULL values are
|
|
+** considered equal, and NULL is considered distinct from all other values.
|
|
+*/
|
|
+static int rbuStrCompare(const char *z1, const char *z2){
|
|
+ if( z1==0 && z2==0 ) return 0;
|
|
+ if( z1==0 || z2==0 ) return 1;
|
|
+ return (sqlite3_stricmp(z1, z2)!=0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called as part of sqlite3rbu_open() when initializing
|
|
+** an rbu handle in OAL stage. If the rbu update has not started (i.e.
|
|
+** the rbu_state table was empty) it is a no-op. Otherwise, it arranges
|
|
+** things so that the next call to sqlite3rbu_step() continues on from
|
|
+** where the previous rbu handle left off.
|
|
+**
|
|
+** If an error occurs, an error code and error message are left in the
|
|
+** rbu handle passed as the first argument.
|
|
+*/
|
|
+static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
|
|
+ assert( p->rc==SQLITE_OK );
|
|
+ if( pState->zTbl ){
|
|
+ RbuObjIter *pIter = &p->objiter;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
|
|
+ || rbuStrCompare(pIter->zIdx, pState->zIdx)
|
|
+ || (pState->zDataTbl==0 && rbuStrCompare(pIter->zTbl, pState->zTbl))
|
|
+ || (pState->zDataTbl && rbuStrCompare(pIter->zDataTbl, pState->zDataTbl))
|
|
+ )){
|
|
+ rc = rbuObjIterNext(p, pIter);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK && !pIter->zTbl ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("rbu_state mismatch error");
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->nStep = pState->nRow;
|
|
+ rc = rbuObjIterPrepareAll(p, &p->objiter, p->nStep);
|
|
+ }
|
|
+
|
|
+ p->rc = rc;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** If there is a "*-oal" file in the file-system corresponding to the
|
|
+** target database in the file-system, delete it. If an error occurs,
|
|
+** leave an error code and error message in the rbu handle.
|
|
+*/
|
|
+static void rbuDeleteOalFile(sqlite3rbu *p){
|
|
+ char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget);
|
|
+ if( zOal ){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
|
|
+ assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 );
|
|
+ pVfs->xDelete(pVfs, zOal, 0);
|
|
+ sqlite3_free(zOal);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate a private rbu VFS for the rbu handle passed as the only
|
|
+** argument. This VFS will be used unless the call to sqlite3rbu_open()
|
|
+** specified a URI with a vfs=? option in place of a target database
|
|
+** file name.
|
|
+*/
|
|
+static void rbuCreateVfs(sqlite3rbu *p){
|
|
+ int rnd;
|
|
+ char zRnd[64];
|
|
+
|
|
+ assert( p->rc==SQLITE_OK );
|
|
+ sqlite3_randomness(sizeof(int), (void*)&rnd);
|
|
+ sqlite3_snprintf(sizeof(zRnd), zRnd, "rbu_vfs_%d", rnd);
|
|
+ p->rc = sqlite3rbu_create_vfs(zRnd, 0);
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd);
|
|
+ assert( pVfs );
|
|
+ p->zVfsName = pVfs->zName;
|
|
+ ((rbu_vfs*)pVfs)->pRbu = p;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destroy the private VFS created for the rbu handle passed as the only
|
|
+** argument by an earlier call to rbuCreateVfs().
|
|
+*/
|
|
+static void rbuDeleteVfs(sqlite3rbu *p){
|
|
+ if( p->zVfsName ){
|
|
+ sqlite3rbu_destroy_vfs(p->zVfsName);
|
|
+ p->zVfsName = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This user-defined SQL function is invoked with a single argument - the
|
|
+** name of a table expected to appear in the target database. It returns
|
|
+** the number of auxilliary indexes on the table.
|
|
+*/
|
|
+static void rbuIndexCntFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal
|
|
+){
|
|
+ sqlite3rbu *p = (sqlite3rbu*)sqlite3_user_data(pCtx);
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ char *zErrmsg = 0;
|
|
+ int rc;
|
|
+
|
|
+ assert( nVal==1 );
|
|
+
|
|
+ rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
|
|
+ sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
|
|
+ "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
|
|
+ );
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_result_error(pCtx, zErrmsg, -1);
|
|
+ }else{
|
|
+ int nIndex = 0;
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ nIndex = sqlite3_column_int(pStmt, 0);
|
|
+ }
|
|
+ rc = sqlite3_finalize(pStmt);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_result_int(pCtx, nIndex);
|
|
+ }else{
|
|
+ sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zErrmsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** If the RBU database contains the rbu_count table, use it to initialize
|
|
+** the sqlite3rbu.nPhaseOneStep variable. The schema of the rbu_count table
|
|
+** is assumed to contain the same columns as:
|
|
+**
|
|
+** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
|
|
+**
|
|
+** There should be one row in the table for each data_xxx table in the
|
|
+** database. The 'tbl' column should contain the name of a data_xxx table,
|
|
+** and the cnt column the number of rows it contains.
|
|
+**
|
|
+** sqlite3rbu.nPhaseOneStep is initialized to the sum of (1 + nIndex) * cnt
|
|
+** for all rows in the rbu_count table, where nIndex is the number of
|
|
+** indexes on the corresponding target database table.
|
|
+*/
|
|
+static void rbuInitPhaseOneSteps(sqlite3rbu *p){
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int bExists = 0; /* True if rbu_count exists */
|
|
+
|
|
+ p->nPhaseOneStep = -1;
|
|
+
|
|
+ p->rc = sqlite3_create_function(p->dbRbu,
|
|
+ "rbu_index_cnt", 1, SQLITE_UTF8, (void*)p, rbuIndexCntFunc, 0, 0
|
|
+ );
|
|
+
|
|
+ /* Check for the rbu_count table. If it does not exist, or if an error
|
|
+ ** occurs, nPhaseOneStep will be left set to -1. */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
|
|
+ "SELECT 1 FROM sqlite_master WHERE tbl_name = 'rbu_count'"
|
|
+ );
|
|
+ }
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ bExists = 1;
|
|
+ }
|
|
+ p->rc = sqlite3_finalize(pStmt);
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK && bExists ){
|
|
+ p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
|
|
+ "SELECT sum(cnt * (1 + rbu_index_cnt(rbu_target_name(tbl))))"
|
|
+ "FROM rbu_count"
|
|
+ );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ p->nPhaseOneStep = sqlite3_column_int64(pStmt, 0);
|
|
+ }
|
|
+ p->rc = sqlite3_finalize(pStmt);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static sqlite3rbu *openRbuHandle(
|
|
+ const char *zTarget,
|
|
+ const char *zRbu,
|
|
+ const char *zState
|
|
+){
|
|
+ sqlite3rbu *p;
|
|
+ size_t nTarget = zTarget ? strlen(zTarget) : 0;
|
|
+ size_t nRbu = strlen(zRbu);
|
|
+ size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;
|
|
+
|
|
+ p = (sqlite3rbu*)sqlite3_malloc(nByte);
|
|
+ if( p ){
|
|
+ RbuState *pState = 0;
|
|
+
|
|
+ /* Create the custom VFS. */
|
|
+ memset(p, 0, sizeof(sqlite3rbu));
|
|
+ rbuCreateVfs(p);
|
|
+
|
|
+ /* Open the target, RBU and state databases */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ char *pCsr = (char*)&p[1];
|
|
+ int bRetry = 0;
|
|
+ if( zTarget ){
|
|
+ p->zTarget = pCsr;
|
|
+ memcpy(p->zTarget, zTarget, nTarget+1);
|
|
+ pCsr += nTarget+1;
|
|
+ }
|
|
+ p->zRbu = pCsr;
|
|
+ memcpy(p->zRbu, zRbu, nRbu+1);
|
|
+ pCsr += nRbu+1;
|
|
+ if( zState ){
|
|
+ p->zState = rbuMPrintf(p, "%s", zState);
|
|
+ }
|
|
+
|
|
+ /* If the first attempt to open the database file fails and the bRetry
|
|
+ ** flag it set, this means that the db was not opened because it seemed
|
|
+ ** to be a wal-mode db. But, this may have happened due to an earlier
|
|
+ ** RBU vacuum operation leaving an old wal file in the directory.
|
|
+ ** If this is the case, it will have been checkpointed and deleted
|
|
+ ** when the handle was closed and a second attempt to open the
|
|
+ ** database may succeed. */
|
|
+ rbuOpenDatabase(p, &bRetry);
|
|
+ if( bRetry ){
|
|
+ rbuOpenDatabase(p, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ pState = rbuLoadState(p);
|
|
+ assert( pState || p->rc!=SQLITE_OK );
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+
|
|
+ if( pState->eStage==0 ){
|
|
+ rbuDeleteOalFile(p);
|
|
+ rbuInitPhaseOneSteps(p);
|
|
+ p->eStage = RBU_STAGE_OAL;
|
|
+ }else{
|
|
+ p->eStage = pState->eStage;
|
|
+ p->nPhaseOneStep = pState->nPhaseOneStep;
|
|
+ }
|
|
+ p->nProgress = pState->nProgress;
|
|
+ p->iOalSz = pState->iOalSz;
|
|
+ }
|
|
+ }
|
|
+ assert( p->rc!=SQLITE_OK || p->eStage!=0 );
|
|
+
|
|
+ if( p->rc==SQLITE_OK && p->pTargetFd->pWalFd ){
|
|
+ if( p->eStage==RBU_STAGE_OAL ){
|
|
+ p->rc = SQLITE_ERROR;
|
|
+ p->zErrmsg = sqlite3_mprintf("cannot update wal mode database");
|
|
+ }else if( p->eStage==RBU_STAGE_MOVE ){
|
|
+ p->eStage = RBU_STAGE_CKPT;
|
|
+ p->nStep = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK
|
|
+ && (p->eStage==RBU_STAGE_OAL || p->eStage==RBU_STAGE_MOVE)
|
|
+ && pState->eStage!=0
|
|
+ ){
|
|
+ rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
|
|
+ if( pFd->iCookie!=pState->iCookie ){
|
|
+ /* At this point (pTargetFd->iCookie) contains the value of the
|
|
+ ** change-counter cookie (the thing that gets incremented when a
|
|
+ ** transaction is committed in rollback mode) currently stored on
|
|
+ ** page 1 of the database file. */
|
|
+ p->rc = SQLITE_BUSY;
|
|
+ p->zErrmsg = sqlite3_mprintf("database modified during rbu %s",
|
|
+ (rbuIsVacuum(p) ? "vacuum" : "update")
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ if( p->eStage==RBU_STAGE_OAL ){
|
|
+ sqlite3 *db = p->dbMain;
|
|
+ p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
|
|
+
|
|
+ /* Point the object iterator at the first object */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = rbuObjIterFirst(p, &p->objiter);
|
|
+ }
|
|
+
|
|
+ /* If the RBU database contains no data_xxx tables, declare the RBU
|
|
+ ** update finished. */
|
|
+ if( p->rc==SQLITE_OK && p->objiter.zTbl==0 ){
|
|
+ p->rc = SQLITE_DONE;
|
|
+ p->eStage = RBU_STAGE_DONE;
|
|
+ }else{
|
|
+ if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){
|
|
+ rbuCopyPragma(p, "page_size");
|
|
+ rbuCopyPragma(p, "auto_vacuum");
|
|
+ }
|
|
+
|
|
+ /* Open transactions both databases. The *-oal file is opened or
|
|
+ ** created at this point. */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+
|
|
+ /* Check if the main database is a zipvfs db. If it is, set the upper
|
|
+ ** level pager to use "journal_mode=off". This prevents it from
|
|
+ ** generating a large journal using a temp file. */
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
|
|
+ if( frc==SQLITE_OK ){
|
|
+ p->rc = sqlite3_exec(
|
|
+ db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( p->rc==SQLITE_OK ){
|
|
+ rbuSetupOal(p, pState);
|
|
+ }
|
|
+ }
|
|
+ }else if( p->eStage==RBU_STAGE_MOVE ){
|
|
+ /* no-op */
|
|
+ }else if( p->eStage==RBU_STAGE_CKPT ){
|
|
+ rbuSetupCheckpoint(p, pState);
|
|
+ }else if( p->eStage==RBU_STAGE_DONE ){
|
|
+ p->rc = SQLITE_DONE;
|
|
+ }else{
|
|
+ p->rc = SQLITE_CORRUPT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rbuFreeState(pState);
|
|
+ }
|
|
+
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate and return an RBU handle with all fields zeroed except for the
|
|
+** error code, which is set to SQLITE_MISUSE.
|
|
+*/
|
|
+static sqlite3rbu *rbuMisuseError(void){
|
|
+ sqlite3rbu *pRet;
|
|
+ pRet = sqlite3_malloc(sizeof(sqlite3rbu));
|
|
+ if( pRet ){
|
|
+ memset(pRet, 0, sizeof(sqlite3rbu));
|
|
+ pRet->rc = SQLITE_MISUSE;
|
|
+ }
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open and return a new RBU handle.
|
|
+*/
|
|
+sqlite3rbu *sqlite3rbu_open(
|
|
+ const char *zTarget,
|
|
+ const char *zRbu,
|
|
+ const char *zState
|
|
+){
|
|
+ if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
|
|
+ /* TODO: Check that zTarget and zRbu are non-NULL */
|
|
+ return openRbuHandle(zTarget, zRbu, zState);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a handle to begin or resume an RBU VACUUM operation.
|
|
+*/
|
|
+sqlite3rbu *sqlite3rbu_vacuum(
|
|
+ const char *zTarget,
|
|
+ const char *zState
|
|
+){
|
|
+ if( zTarget==0 ){ return rbuMisuseError(); }
|
|
+ if( zState ){
|
|
+ int 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);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the database handle used by pRbu.
|
|
+*/
|
|
+sqlite3 *sqlite3rbu_db(sqlite3rbu *pRbu, int bRbu){
|
|
+ sqlite3 *db = 0;
|
|
+ if( pRbu ){
|
|
+ db = (bRbu ? pRbu->dbRbu : pRbu->dbMain);
|
|
+ }
|
|
+ return db;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** If the error code currently stored in the RBU handle is SQLITE_CONSTRAINT,
|
|
+** then edit any error message string so as to remove all occurrences of
|
|
+** the pattern "rbu_imp_[0-9]*".
|
|
+*/
|
|
+static void rbuEditErrmsg(sqlite3rbu *p){
|
|
+ if( p->rc==SQLITE_CONSTRAINT && p->zErrmsg ){
|
|
+ unsigned int i;
|
|
+ size_t nErrmsg = strlen(p->zErrmsg);
|
|
+ for(i=0; i<(nErrmsg-8); i++){
|
|
+ if( memcmp(&p->zErrmsg[i], "rbu_imp_", 8)==0 ){
|
|
+ int nDel = 8;
|
|
+ while( p->zErrmsg[i+nDel]>='0' && p->zErrmsg[i+nDel]<='9' ) nDel++;
|
|
+ memmove(&p->zErrmsg[i], &p->zErrmsg[i+nDel], nErrmsg + 1 - i - nDel);
|
|
+ nErrmsg -= nDel;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close the RBU handle.
|
|
+*/
|
|
+int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
|
|
+ int rc;
|
|
+ if( p ){
|
|
+
|
|
+ /* Commit the transaction to the *-oal file. */
|
|
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){
|
|
+ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+
|
|
+ /* Sync the db file if currently doing an incremental checkpoint */
|
|
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_CKPT ){
|
|
+ sqlite3_file *pDb = p->pTargetFd->pReal;
|
|
+ p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL);
|
|
+ }
|
|
+
|
|
+ rbuSaveState(p, p->eStage);
|
|
+
|
|
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){
|
|
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
|
|
+ }
|
|
+
|
|
+ /* Close any open statement handles. */
|
|
+ rbuObjIterFinalize(&p->objiter);
|
|
+
|
|
+ /* If this is an RBU vacuum handle and the vacuum has either finished
|
|
+ ** successfully or encountered an error, delete the contents of the
|
|
+ ** state table. This causes the next call to sqlite3rbu_vacuum()
|
|
+ ** specifying the current target and state databases to start a new
|
|
+ ** vacuum from scratch. */
|
|
+ if( rbuIsVacuum(p) && p->rc!=SQLITE_OK && p->dbRbu ){
|
|
+ int rc2 = sqlite3_exec(p->dbRbu, "DELETE FROM stat.rbu_state", 0, 0, 0);
|
|
+ if( p->rc==SQLITE_DONE && rc2!=SQLITE_OK ) p->rc = rc2;
|
|
+ }
|
|
+
|
|
+ /* Close the open database handle and VFS object. */
|
|
+ sqlite3_close(p->dbRbu);
|
|
+ sqlite3_close(p->dbMain);
|
|
+ assert( p->szTemp==0 );
|
|
+ rbuDeleteVfs(p);
|
|
+ sqlite3_free(p->aBuf);
|
|
+ sqlite3_free(p->aFrame);
|
|
+
|
|
+ rbuEditErrmsg(p);
|
|
+ rc = p->rc;
|
|
+ if( pzErrmsg ){
|
|
+ *pzErrmsg = p->zErrmsg;
|
|
+ }else{
|
|
+ sqlite3_free(p->zErrmsg);
|
|
+ }
|
|
+ sqlite3_free(p->zState);
|
|
+ sqlite3_free(p);
|
|
+ }else{
|
|
+ rc = SQLITE_NOMEM;
|
|
+ *pzErrmsg = 0;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the total number of key-value operations (inserts, deletes or
|
|
+** updates) that have been performed on the target database since the
|
|
+** current RBU update was started.
|
|
+*/
|
|
+sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu){
|
|
+ return pRbu->nProgress;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return permyriadage progress indications for the two main stages of
|
|
+** an RBU update.
|
|
+*/
|
|
+void sqlite3rbu_bp_progress(sqlite3rbu *p, int *pnOne, int *pnTwo){
|
|
+ const int MAX_PROGRESS = 10000;
|
|
+ switch( p->eStage ){
|
|
+ case RBU_STAGE_OAL:
|
|
+ if( p->nPhaseOneStep>0 ){
|
|
+ *pnOne = (int)(MAX_PROGRESS * (i64)p->nProgress/(i64)p->nPhaseOneStep);
|
|
+ }else{
|
|
+ *pnOne = -1;
|
|
+ }
|
|
+ *pnTwo = 0;
|
|
+ break;
|
|
+
|
|
+ case RBU_STAGE_MOVE:
|
|
+ *pnOne = MAX_PROGRESS;
|
|
+ *pnTwo = 0;
|
|
+ break;
|
|
+
|
|
+ case RBU_STAGE_CKPT:
|
|
+ *pnOne = MAX_PROGRESS;
|
|
+ *pnTwo = (int)(MAX_PROGRESS * (i64)p->nStep / (i64)p->nFrame);
|
|
+ break;
|
|
+
|
|
+ case RBU_STAGE_DONE:
|
|
+ *pnOne = MAX_PROGRESS;
|
|
+ *pnTwo = MAX_PROGRESS;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ assert( 0 );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current state of the RBU vacuum or update operation.
|
|
+*/
|
|
+int sqlite3rbu_state(sqlite3rbu *p){
|
|
+ int aRes[] = {
|
|
+ 0, SQLITE_RBU_STATE_OAL, SQLITE_RBU_STATE_MOVE,
|
|
+ 0, SQLITE_RBU_STATE_CHECKPOINT, SQLITE_RBU_STATE_DONE
|
|
+ };
|
|
+
|
|
+ assert( RBU_STAGE_OAL==1 );
|
|
+ assert( RBU_STAGE_MOVE==2 );
|
|
+ assert( RBU_STAGE_CKPT==4 );
|
|
+ assert( RBU_STAGE_DONE==5 );
|
|
+ assert( aRes[RBU_STAGE_OAL]==SQLITE_RBU_STATE_OAL );
|
|
+ assert( aRes[RBU_STAGE_MOVE]==SQLITE_RBU_STATE_MOVE );
|
|
+ assert( aRes[RBU_STAGE_CKPT]==SQLITE_RBU_STATE_CHECKPOINT );
|
|
+ assert( aRes[RBU_STAGE_DONE]==SQLITE_RBU_STATE_DONE );
|
|
+
|
|
+ if( p->rc!=SQLITE_OK && p->rc!=SQLITE_DONE ){
|
|
+ return SQLITE_RBU_STATE_ERROR;
|
|
+ }else{
|
|
+ assert( p->rc!=SQLITE_DONE || p->eStage==RBU_STAGE_DONE );
|
|
+ assert( p->eStage==RBU_STAGE_OAL
|
|
+ || p->eStage==RBU_STAGE_MOVE
|
|
+ || p->eStage==RBU_STAGE_CKPT
|
|
+ || p->eStage==RBU_STAGE_DONE
|
|
+ );
|
|
+ return aRes[p->eStage];
|
|
+ }
|
|
+}
|
|
+
|
|
+int sqlite3rbu_savestate(sqlite3rbu *p){
|
|
+ int rc = p->rc;
|
|
+ if( rc==SQLITE_DONE ) return SQLITE_OK;
|
|
+
|
|
+ assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE );
|
|
+ if( p->eStage==RBU_STAGE_OAL ){
|
|
+ assert( rc!=SQLITE_DONE );
|
|
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0);
|
|
+ }
|
|
+
|
|
+ /* Sync the db file */
|
|
+ if( rc==SQLITE_OK && p->eStage==RBU_STAGE_CKPT ){
|
|
+ sqlite3_file *pDb = p->pTargetFd->pReal;
|
|
+ rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL);
|
|
+ }
|
|
+
|
|
+ p->rc = rc;
|
|
+ rbuSaveState(p, p->eStage);
|
|
+ rc = p->rc;
|
|
+
|
|
+ if( p->eStage==RBU_STAGE_OAL ){
|
|
+ assert( rc!=SQLITE_DONE );
|
|
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ const char *zBegin = rbuIsVacuum(p) ? "BEGIN" : "BEGIN IMMEDIATE";
|
|
+ rc = sqlite3_exec(p->dbRbu, zBegin, 0, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0);
|
|
+ }
|
|
+
|
|
+ p->rc = rc;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**************************************************************************
|
|
+** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
|
|
+** of a standard VFS in the following ways:
|
|
+**
|
|
+** 1. Whenever the first page of a main database file is read or
|
|
+** written, the value of the change-counter cookie is stored in
|
|
+** rbu_file.iCookie. Similarly, the value of the "write-version"
|
|
+** database header field is stored in rbu_file.iWriteVer. This ensures
|
|
+** that the values are always trustworthy within an open transaction.
|
|
+**
|
|
+** 2. Whenever an SQLITE_OPEN_WAL file is opened, the (rbu_file.pWalFd)
|
|
+** member variable of the associated database file descriptor is set
|
|
+** to point to the new file. A mutex protected linked list of all main
|
|
+** db fds opened using a particular RBU VFS is maintained at
|
|
+** rbu_vfs.pMain to facilitate this.
|
|
+**
|
|
+** 3. Using a new file-control "SQLITE_FCNTL_RBU", a main db rbu_file
|
|
+** object can be marked as the target database of an RBU update. This
|
|
+** turns on the following extra special behaviour:
|
|
+**
|
|
+** 3a. If xAccess() is called to check if there exists a *-wal file
|
|
+** associated with an RBU target database currently in RBU_STAGE_OAL
|
|
+** stage (preparing the *-oal file), the following special handling
|
|
+** applies:
|
|
+**
|
|
+** * if the *-wal file does exist, return SQLITE_CANTOPEN. An RBU
|
|
+** target database may not be in wal mode already.
|
|
+**
|
|
+** * if the *-wal file does not exist, set the output parameter to
|
|
+** non-zero (to tell SQLite that it does exist) anyway.
|
|
+**
|
|
+** Then, when xOpen() is called to open the *-wal file associated with
|
|
+** the RBU target in RBU_STAGE_OAL stage, instead of opening the *-wal
|
|
+** file, the rbu vfs opens the corresponding *-oal file instead.
|
|
+**
|
|
+** 3b. The *-shm pages returned by xShmMap() for a target db file in
|
|
+** RBU_STAGE_OAL mode are actually stored in heap memory. This is to
|
|
+** avoid creating a *-shm file on disk. Additionally, xShmLock() calls
|
|
+** are no-ops on target database files in RBU_STAGE_OAL mode. This is
|
|
+** because assert() statements in some VFS implementations fail if
|
|
+** xShmLock() is called before xShmMap().
|
|
+**
|
|
+** 3c. If an EXCLUSIVE lock is attempted on a target database file in any
|
|
+** mode except RBU_STAGE_DONE (all work completed and checkpointed), it
|
|
+** fails with an SQLITE_BUSY error. This is to stop RBU connections
|
|
+** from automatically checkpointing a *-wal (or *-oal) file from within
|
|
+** sqlite3_close().
|
|
+**
|
|
+** 3d. In RBU_STAGE_CAPTURE mode, all xRead() calls on the wal file, and
|
|
+** all xWrite() calls on the target database file perform no IO.
|
|
+** Instead the frame and page numbers that would be read and written
|
|
+** are recorded. Additionally, successful attempts to obtain exclusive
|
|
+** xShmLock() WRITER, CHECKPOINTER and READ0 locks on the target
|
|
+** database file are recorded. xShmLock() calls to unlock the same
|
|
+** locks are no-ops (so that once obtained, these locks are never
|
|
+** relinquished). Finally, calls to xSync() on the target database
|
|
+** file fail with SQLITE_INTERNAL errors.
|
|
+*/
|
|
+
|
|
+static void rbuUnlockShm(rbu_file *p){
|
|
+ assert( p->openFlags & SQLITE_OPEN_MAIN_DB );
|
|
+ if( p->pRbu ){
|
|
+ int (*xShmLock)(sqlite3_file*,int,int,int) = p->pReal->pMethods->xShmLock;
|
|
+ int i;
|
|
+ for(i=0; i<SQLITE_SHM_NLOCK;i++){
|
|
+ if( (1<<i) & p->pRbu->mLock ){
|
|
+ xShmLock(p->pReal, i, 1, SQLITE_SHM_UNLOCK|SQLITE_SHM_EXCLUSIVE);
|
|
+ }
|
|
+ }
|
|
+ p->pRbu->mLock = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+*/
|
|
+static int rbuUpdateTempSize(rbu_file *pFd, sqlite3_int64 nNew){
|
|
+ sqlite3rbu *pRbu = pFd->pRbu;
|
|
+ i64 nDiff = nNew - pFd->sz;
|
|
+ pRbu->szTemp += nDiff;
|
|
+ pFd->sz = nNew;
|
|
+ assert( pRbu->szTemp>=0 );
|
|
+ if( pRbu->szTempLimit && pRbu->szTemp>pRbu->szTempLimit ) return SQLITE_FULL;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add an item to the main-db lists, if it is not already present.
|
|
+**
|
|
+** There are two main-db lists. One for all file descriptors, and one
|
|
+** for all file descriptors with rbu_file.pDb!=0. If the argument has
|
|
+** rbu_file.pDb!=0, then it is assumed to already be present on the
|
|
+** main list and is only added to the pDb!=0 list.
|
|
+*/
|
|
+static void rbuMainlistAdd(rbu_file *p){
|
|
+ rbu_vfs *pRbuVfs = p->pRbuVfs;
|
|
+ rbu_file *pIter;
|
|
+ assert( (p->openFlags & SQLITE_OPEN_MAIN_DB) );
|
|
+ sqlite3_mutex_enter(pRbuVfs->mutex);
|
|
+ if( p->pRbu==0 ){
|
|
+ for(pIter=pRbuVfs->pMain; pIter; pIter=pIter->pMainNext);
|
|
+ p->pMainNext = pRbuVfs->pMain;
|
|
+ pRbuVfs->pMain = p;
|
|
+ }else{
|
|
+ for(pIter=pRbuVfs->pMainRbu; pIter && pIter!=p; pIter=pIter->pMainRbuNext){}
|
|
+ if( pIter==0 ){
|
|
+ p->pMainRbuNext = pRbuVfs->pMainRbu;
|
|
+ pRbuVfs->pMainRbu = p;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_mutex_leave(pRbuVfs->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Remove an item from the main-db lists.
|
|
+*/
|
|
+static void rbuMainlistRemove(rbu_file *p){
|
|
+ rbu_file **pp;
|
|
+ sqlite3_mutex_enter(p->pRbuVfs->mutex);
|
|
+ for(pp=&p->pRbuVfs->pMain; *pp && *pp!=p; pp=&((*pp)->pMainNext)){}
|
|
+ if( *pp ) *pp = p->pMainNext;
|
|
+ p->pMainNext = 0;
|
|
+ for(pp=&p->pRbuVfs->pMainRbu; *pp && *pp!=p; pp=&((*pp)->pMainRbuNext)){}
|
|
+ if( *pp ) *pp = p->pMainRbuNext;
|
|
+ p->pMainRbuNext = 0;
|
|
+ sqlite3_mutex_leave(p->pRbuVfs->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Given that zWal points to a buffer containing a wal file name passed to
|
|
+** either the xOpen() or xAccess() VFS method, search the main-db list for
|
|
+** a file-handle opened by the same database connection on the corresponding
|
|
+** database file.
|
|
+**
|
|
+** If parameter bRbu is true, only search for file-descriptors with
|
|
+** rbu_file.pDb!=0.
|
|
+*/
|
|
+static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal, int bRbu){
|
|
+ rbu_file *pDb;
|
|
+ sqlite3_mutex_enter(pRbuVfs->mutex);
|
|
+ if( bRbu ){
|
|
+ for(pDb=pRbuVfs->pMainRbu; pDb && pDb->zWal!=zWal; pDb=pDb->pMainRbuNext){}
|
|
+ }else{
|
|
+ for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
|
|
+ }
|
|
+ sqlite3_mutex_leave(pRbuVfs->mutex);
|
|
+ return pDb;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close an rbu file.
|
|
+*/
|
|
+static int rbuVfsClose(sqlite3_file *pFile){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ int rc;
|
|
+ int i;
|
|
+
|
|
+ /* Free the contents of the apShm[] array. And the array itself. */
|
|
+ for(i=0; i<p->nShm; i++){
|
|
+ sqlite3_free(p->apShm[i]);
|
|
+ }
|
|
+ sqlite3_free(p->apShm);
|
|
+ p->apShm = 0;
|
|
+ sqlite3_free(p->zDel);
|
|
+
|
|
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
|
+ rbuMainlistRemove(p);
|
|
+ rbuUnlockShm(p);
|
|
+ p->pReal->pMethods->xShmUnmap(p->pReal, 0);
|
|
+ }
|
|
+ else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
|
|
+ rbuUpdateTempSize(p, 0);
|
|
+ }
|
|
+ assert( p->pMainNext==0 && p->pRbuVfs->pMain!=p );
|
|
+
|
|
+ /* Close the underlying file handle */
|
|
+ rc = p->pReal->pMethods->xClose(p->pReal);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Read and return an unsigned 32-bit big-endian integer from the buffer
|
|
+** passed as the only argument.
|
|
+*/
|
|
+static u32 rbuGetU32(u8 *aBuf){
|
|
+ return ((u32)aBuf[0] << 24)
|
|
+ + ((u32)aBuf[1] << 16)
|
|
+ + ((u32)aBuf[2] << 8)
|
|
+ + ((u32)aBuf[3]);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write an unsigned 32-bit value in big-endian format to the supplied
|
|
+** buffer.
|
|
+*/
|
|
+static void rbuPutU32(u8 *aBuf, u32 iVal){
|
|
+ aBuf[0] = (iVal >> 24) & 0xFF;
|
|
+ aBuf[1] = (iVal >> 16) & 0xFF;
|
|
+ aBuf[2] = (iVal >> 8) & 0xFF;
|
|
+ aBuf[3] = (iVal >> 0) & 0xFF;
|
|
+}
|
|
+
|
|
+static void rbuPutU16(u8 *aBuf, u16 iVal){
|
|
+ aBuf[0] = (iVal >> 8) & 0xFF;
|
|
+ aBuf[1] = (iVal >> 0) & 0xFF;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read data from an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ sqlite3rbu *pRbu = p->pRbu;
|
|
+ int rc;
|
|
+
|
|
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
|
|
+ assert( p->openFlags & SQLITE_OPEN_WAL );
|
|
+ rc = rbuCaptureWalRead(p->pRbu, iOfst, iAmt);
|
|
+ }else{
|
|
+ if( pRbu && pRbu->eStage==RBU_STAGE_OAL
|
|
+ && (p->openFlags & SQLITE_OPEN_WAL)
|
|
+ && iOfst>=pRbu->iOalSz
|
|
+ ){
|
|
+ rc = SQLITE_OK;
|
|
+ memset(zBuf, 0, iAmt);
|
|
+ }else{
|
|
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
|
|
+#if 1
|
|
+ /* If this is being called to read the first page of the target
|
|
+ ** database as part of an rbu vacuum operation, synthesize the
|
|
+ ** contents of the first page if it does not yet exist. Otherwise,
|
|
+ ** SQLite will not check for a *-wal file. */
|
|
+ if( pRbu && rbuIsVacuum(pRbu)
|
|
+ && rc==SQLITE_IOERR_SHORT_READ && iOfst==0
|
|
+ && (p->openFlags & SQLITE_OPEN_MAIN_DB)
|
|
+ && pRbu->rc==SQLITE_OK
|
|
+ ){
|
|
+ sqlite3_file *pFd = (sqlite3_file*)pRbu->pRbuFd;
|
|
+ rc = pFd->pMethods->xRead(pFd, zBuf, iAmt, iOfst);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ u8 *aBuf = (u8*)zBuf;
|
|
+ u32 iRoot = rbuGetU32(&aBuf[52]) ? 1 : 0;
|
|
+ rbuPutU32(&aBuf[52], iRoot); /* largest root page number */
|
|
+ rbuPutU32(&aBuf[36], 0); /* number of free pages */
|
|
+ rbuPutU32(&aBuf[32], 0); /* first page on free list trunk */
|
|
+ rbuPutU32(&aBuf[28], 1); /* size of db file in pages */
|
|
+ rbuPutU32(&aBuf[24], pRbu->pRbuFd->iCookie+1); /* Change counter */
|
|
+
|
|
+ if( iAmt>100 ){
|
|
+ memset(&aBuf[100], 0, iAmt-100);
|
|
+ rbuPutU16(&aBuf[105], iAmt & 0xFFFF);
|
|
+ aBuf[100] = 0x0D;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+ if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
|
|
+ /* These look like magic numbers. But they are stable, as they are part
|
|
+ ** of the definition of the SQLite file format, which may not change. */
|
|
+ u8 *pBuf = (u8*)zBuf;
|
|
+ p->iCookie = rbuGetU32(&pBuf[24]);
|
|
+ p->iWriteVer = pBuf[19];
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ sqlite3rbu *pRbu = p->pRbu;
|
|
+ int rc;
|
|
+
|
|
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
|
|
+ assert( p->openFlags & SQLITE_OPEN_MAIN_DB );
|
|
+ rc = rbuCaptureDbWrite(p->pRbu, iOfst);
|
|
+ }else{
|
|
+ if( pRbu ){
|
|
+ if( pRbu->eStage==RBU_STAGE_OAL
|
|
+ && (p->openFlags & SQLITE_OPEN_WAL)
|
|
+ && iOfst>=pRbu->iOalSz
|
|
+ ){
|
|
+ pRbu->iOalSz = iAmt + iOfst;
|
|
+ }else if( p->openFlags & SQLITE_OPEN_DELETEONCLOSE ){
|
|
+ i64 szNew = iAmt+iOfst;
|
|
+ if( szNew>p->sz ){
|
|
+ rc = rbuUpdateTempSize(p, szNew);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
|
|
+ if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
|
|
+ /* These look like magic numbers. But they are stable, as they are part
|
|
+ ** of the definition of the SQLite file format, which may not change. */
|
|
+ u8 *pBuf = (u8*)zBuf;
|
|
+ p->iCookie = rbuGetU32(&pBuf[24]);
|
|
+ p->iWriteVer = pBuf[19];
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
|
|
+ int rc = rbuUpdateTempSize(p, size);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ }
|
|
+ return p->pReal->pMethods->xTruncate(p->pReal, size);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsSync(sqlite3_file *pFile, int flags){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
|
|
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
|
+ return SQLITE_INTERNAL;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ return p->pReal->pMethods->xSync(p->pReal, flags);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ int rc;
|
|
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
|
|
+
|
|
+ /* If this is an RBU vacuum operation and this is the target database,
|
|
+ ** pretend that it has at least one page. Otherwise, SQLite will not
|
|
+ ** check for the existance of a *-wal file. rbuVfsRead() contains
|
|
+ ** similar logic. */
|
|
+ if( rc==SQLITE_OK && *pSize==0
|
|
+ && p->pRbu && rbuIsVacuum(p->pRbu)
|
|
+ && (p->openFlags & SQLITE_OPEN_MAIN_DB)
|
|
+ ){
|
|
+ *pSize = 1024;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsLock(sqlite3_file *pFile, int eLock){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ sqlite3rbu *pRbu = p->pRbu;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
|
+ if( eLock==SQLITE_LOCK_EXCLUSIVE
|
|
+ && (p->bNolock || (pRbu && pRbu->eStage!=RBU_STAGE_DONE))
|
|
+ ){
|
|
+ /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this
|
|
+ ** prevents it from checkpointing the database from sqlite3_close(). */
|
|
+ rc = SQLITE_BUSY;
|
|
+ }else{
|
|
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unlock an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsUnlock(sqlite3_file *pFile, int eLock){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ return p->pReal->pMethods->xUnlock(p->pReal, eLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ return p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ int (*xControl)(sqlite3_file*,int,void*) = p->pReal->pMethods->xFileControl;
|
|
+ int rc;
|
|
+
|
|
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB)
|
|
+ || p->openFlags & (SQLITE_OPEN_TRANSIENT_DB|SQLITE_OPEN_TEMP_JOURNAL)
|
|
+ );
|
|
+ if( op==SQLITE_FCNTL_RBU ){
|
|
+ sqlite3rbu *pRbu = (sqlite3rbu*)pArg;
|
|
+
|
|
+ /* First try to find another RBU vfs lower down in the vfs stack. If
|
|
+ ** one is found, this vfs will operate in pass-through mode. The lower
|
|
+ ** level vfs will do the special RBU handling. */
|
|
+ rc = xControl(p->pReal, op, pArg);
|
|
+
|
|
+ if( rc==SQLITE_NOTFOUND ){
|
|
+ /* Now search for a zipvfs instance lower down in the VFS stack. If
|
|
+ ** one is found, this is an error. */
|
|
+ void *dummy = 0;
|
|
+ rc = xControl(p->pReal, SQLITE_FCNTL_ZIPVFS, &dummy);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ pRbu->zErrmsg = sqlite3_mprintf("rbu/zipvfs setup error");
|
|
+ }else if( rc==SQLITE_NOTFOUND ){
|
|
+ pRbu->pTargetFd = p;
|
|
+ p->pRbu = pRbu;
|
|
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
|
+ rbuMainlistAdd(p);
|
|
+ }
|
|
+ if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
|
|
+ rc = SQLITE_OK;
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+ }
|
|
+ else if( op==SQLITE_FCNTL_RBUCNT ){
|
|
+ sqlite3rbu *pRbu = (sqlite3rbu*)pArg;
|
|
+ pRbu->nRbu++;
|
|
+ pRbu->pRbuFd = p;
|
|
+ p->bNolock = 1;
|
|
+ }
|
|
+
|
|
+ rc = xControl(p->pReal, op, pArg);
|
|
+ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
|
|
+ rbu_vfs *pRbuVfs = p->pRbuVfs;
|
|
+ char *zIn = *(char**)pArg;
|
|
+ char *zOut = sqlite3_mprintf("rbu(%s)/%z", pRbuVfs->base.zName, zIn);
|
|
+ *(char**)pArg = zOut;
|
|
+ if( zOut==0 ) rc = SQLITE_NOMEM;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the sector-size in bytes for an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsSectorSize(sqlite3_file *pFile){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ return p->pReal->pMethods->xSectorSize(p->pReal);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an rbuVfs-file.
|
|
+*/
|
|
+static int rbuVfsDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ return p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Take or release a shared-memory lock.
|
|
+*/
|
|
+static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ sqlite3rbu *pRbu = p->pRbu;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+#ifdef SQLITE_AMALGAMATION
|
|
+ assert( WAL_CKPT_LOCK==1 );
|
|
+#endif
|
|
+
|
|
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
|
+ if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
|
|
+ /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
|
|
+ ** taking this lock also prevents any checkpoints from occurring.
|
|
+ ** todo: really, it's not clear why this might occur, as
|
|
+ ** wal_autocheckpoint ought to be turned off. */
|
|
+ if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
|
|
+ }else{
|
|
+ int bCapture = 0;
|
|
+ if( n==1 && (flags & SQLITE_SHM_EXCLUSIVE)
|
|
+ && pRbu && pRbu->eStage==RBU_STAGE_CAPTURE
|
|
+ && (ofst==WAL_LOCK_WRITE || ofst==WAL_LOCK_CKPT || ofst==WAL_LOCK_READ0)
|
|
+ ){
|
|
+ bCapture = 1;
|
|
+ }
|
|
+
|
|
+ if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
|
|
+ rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
|
|
+ if( bCapture && rc==SQLITE_OK ){
|
|
+ pRbu->mLock |= (1 << ofst);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Obtain a pointer to a mapping of a single 32KiB page of the *-shm file.
|
|
+*/
|
|
+static int rbuVfsShmMap(
|
|
+ sqlite3_file *pFile,
|
|
+ int iRegion,
|
|
+ int szRegion,
|
|
+ int isWrite,
|
|
+ void volatile **pp
|
|
+){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ int rc = SQLITE_OK;
|
|
+ int eStage = (p->pRbu ? p->pRbu->eStage : 0);
|
|
+
|
|
+ /* If not in RBU_STAGE_OAL, allow this call to pass through. Or, if this
|
|
+ ** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space
|
|
+ ** instead of a file on disk. */
|
|
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
|
+ if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
|
|
+ if( iRegion<=p->nShm ){
|
|
+ size_t nByte = (iRegion+1) * sizeof(char*);
|
|
+ char **apNew = (char**)sqlite3_realloc(p->apShm, nByte);
|
|
+ if( apNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
|
|
+ p->apShm = apNew;
|
|
+ p->nShm = iRegion+1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
|
|
+ char *pNew = (char*)sqlite3_malloc(szRegion);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pNew, 0, szRegion);
|
|
+ p->apShm[iRegion] = pNew;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ *pp = p->apShm[iRegion];
|
|
+ }else{
|
|
+ *pp = 0;
|
|
+ }
|
|
+ }else{
|
|
+ assert( p->apShm==0 );
|
|
+ rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Memory barrier.
|
|
+*/
|
|
+static void rbuVfsShmBarrier(sqlite3_file *pFile){
|
|
+ rbu_file *p = (rbu_file *)pFile;
|
|
+ p->pReal->pMethods->xShmBarrier(p->pReal);
|
|
+}
|
|
+
|
|
+/*
|
|
+** The xShmUnmap method.
|
|
+*/
|
|
+static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){
|
|
+ rbu_file *p = (rbu_file*)pFile;
|
|
+ int rc = SQLITE_OK;
|
|
+ int eStage = (p->pRbu ? p->pRbu->eStage : 0);
|
|
+
|
|
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
|
+ if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
|
|
+ /* no-op */
|
|
+ }else{
|
|
+ /* Release the checkpointer and writer locks */
|
|
+ rbuUnlockShm(p);
|
|
+ rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** A main database named zName has just been opened. The following
|
|
+** function returns a pointer to a buffer owned by SQLite that contains
|
|
+** the name of the *-wal file this db connection will use. SQLite
|
|
+** happens to pass a pointer to this buffer when using xAccess()
|
|
+** or xOpen() to operate on the *-wal file.
|
|
+*/
|
|
+static const char *rbuMainToWal(const char *zName, int flags){
|
|
+ int n = (int)strlen(zName);
|
|
+ const char *z = &zName[n];
|
|
+ if( flags & SQLITE_OPEN_URI ){
|
|
+ int odd = 0;
|
|
+ while( 1 ){
|
|
+ if( z[0]==0 ){
|
|
+ odd = 1 - odd;
|
|
+ if( odd && z[1]==0 ) break;
|
|
+ }
|
|
+ z++;
|
|
+ }
|
|
+ z += 2;
|
|
+ }else{
|
|
+ while( *z==0 ) z++;
|
|
+ }
|
|
+ z += (n + 8 + 1);
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an rbu file handle.
|
|
+*/
|
|
+static int rbuVfsOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ static sqlite3_io_methods rbuvfs_io_methods = {
|
|
+ 2, /* iVersion */
|
|
+ rbuVfsClose, /* xClose */
|
|
+ rbuVfsRead, /* xRead */
|
|
+ rbuVfsWrite, /* xWrite */
|
|
+ rbuVfsTruncate, /* xTruncate */
|
|
+ rbuVfsSync, /* xSync */
|
|
+ rbuVfsFileSize, /* xFileSize */
|
|
+ rbuVfsLock, /* xLock */
|
|
+ rbuVfsUnlock, /* xUnlock */
|
|
+ rbuVfsCheckReservedLock, /* xCheckReservedLock */
|
|
+ rbuVfsFileControl, /* xFileControl */
|
|
+ rbuVfsSectorSize, /* xSectorSize */
|
|
+ rbuVfsDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ rbuVfsShmMap, /* xShmMap */
|
|
+ rbuVfsShmLock, /* xShmLock */
|
|
+ rbuVfsShmBarrier, /* xShmBarrier */
|
|
+ rbuVfsShmUnmap, /* xShmUnmap */
|
|
+ 0, 0 /* xFetch, xUnfetch */
|
|
+ };
|
|
+ rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
|
|
+ sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
|
|
+ rbu_file *pFd = (rbu_file *)pFile;
|
|
+ int rc = SQLITE_OK;
|
|
+ const char *zOpen = zName;
|
|
+ int oflags = flags;
|
|
+
|
|
+ memset(pFd, 0, sizeof(rbu_file));
|
|
+ pFd->pReal = (sqlite3_file*)&pFd[1];
|
|
+ pFd->pRbuVfs = pRbuVfs;
|
|
+ pFd->openFlags = flags;
|
|
+ if( zName ){
|
|
+ if( flags & SQLITE_OPEN_MAIN_DB ){
|
|
+ /* A main database has just been opened. The following block sets
|
|
+ ** (pFd->zWal) to point to a buffer owned by SQLite that contains
|
|
+ ** the name of the *-wal file this db connection will use. SQLite
|
|
+ ** happens to pass a pointer to this buffer when using xAccess()
|
|
+ ** or xOpen() to operate on the *-wal file. */
|
|
+ pFd->zWal = rbuMainToWal(zName, flags);
|
|
+ }
|
|
+ else if( flags & SQLITE_OPEN_WAL ){
|
|
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0);
|
|
+ if( pDb ){
|
|
+ if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
|
|
+ /* This call is to open a *-wal file. Intead, open the *-oal. This
|
|
+ ** code ensures that the string passed to xOpen() is terminated by a
|
|
+ ** pair of '\0' bytes in case the VFS attempts to extract a URI
|
|
+ ** parameter from it. */
|
|
+ const char *zBase = zName;
|
|
+ size_t nCopy;
|
|
+ char *zCopy;
|
|
+ if( rbuIsVacuum(pDb->pRbu) ){
|
|
+ zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main");
|
|
+ zBase = rbuMainToWal(zBase, SQLITE_OPEN_URI);
|
|
+ }
|
|
+ nCopy = strlen(zBase);
|
|
+ zCopy = sqlite3_malloc(nCopy+2);
|
|
+ if( zCopy ){
|
|
+ memcpy(zCopy, zBase, nCopy);
|
|
+ zCopy[nCopy-3] = 'o';
|
|
+ zCopy[nCopy] = '\0';
|
|
+ zCopy[nCopy+1] = '\0';
|
|
+ zOpen = (const char*)(pFd->zDel = zCopy);
|
|
+ }else{
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }
|
|
+ pFd->pRbu = pDb->pRbu;
|
|
+ }
|
|
+ pDb->pWalFd = pFd;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ pFd->pRbu = pRbuVfs->pRbu;
|
|
+ }
|
|
+
|
|
+ if( oflags & SQLITE_OPEN_MAIN_DB
|
|
+ && sqlite3_uri_boolean(zName, "rbu_memory", 0)
|
|
+ ){
|
|
+ assert( oflags & SQLITE_OPEN_MAIN_DB );
|
|
+ oflags = SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
|
|
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
|
|
+ zOpen = 0;
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags);
|
|
+ }
|
|
+ if( pFd->pReal->pMethods ){
|
|
+ /* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods
|
|
+ ** pointer and, if the file is a main database file, link it into the
|
|
+ ** mutex protected linked list of all such files. */
|
|
+ pFile->pMethods = &rbuvfs_io_methods;
|
|
+ if( flags & SQLITE_OPEN_MAIN_DB ){
|
|
+ rbuMainlistAdd(pFd);
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_free(pFd->zDel);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Delete the file located at zPath.
|
|
+*/
|
|
+static int rbuVfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ return pRealVfs->xDelete(pRealVfs, zPath, dirSync);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Test for access permissions. Return true if the requested permission
|
|
+** is available, or false otherwise.
|
|
+*/
|
|
+static int rbuVfsAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
|
|
+ sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
|
|
+ int rc;
|
|
+
|
|
+ rc = pRealVfs->xAccess(pRealVfs, zPath, flags, pResOut);
|
|
+
|
|
+ /* If this call is to check if a *-wal file associated with an RBU target
|
|
+ ** database connection exists, and the RBU update is in RBU_STAGE_OAL,
|
|
+ ** the following special handling is activated:
|
|
+ **
|
|
+ ** a) if the *-wal file does exist, return SQLITE_CANTOPEN. This
|
|
+ ** ensures that the RBU extension never tries to update a database
|
|
+ ** in wal mode, even if the first page of the database file has
|
|
+ ** been damaged.
|
|
+ **
|
|
+ ** b) if the *-wal file does not exist, claim that it does anyway,
|
|
+ ** causing SQLite to call xOpen() to open it. This call will also
|
|
+ ** be intercepted (see the rbuVfsOpen() function) and the *-oal
|
|
+ ** file opened instead.
|
|
+ */
|
|
+ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
|
|
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath, 1);
|
|
+ if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
|
|
+ if( *pResOut ){
|
|
+ rc = SQLITE_CANTOPEN;
|
|
+ }else{
|
|
+ sqlite3_int64 sz = 0;
|
|
+ rc = rbuVfsFileSize(&pDb->base, &sz);
|
|
+ *pResOut = (sz>0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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 rbuVfsFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ return pRealVfs->xFullPathname(pRealVfs, zPath, nOut, zOut);
|
|
+}
|
|
+
|
|
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
+/*
|
|
+** Open the dynamic library located at zPath and return a handle.
|
|
+*/
|
|
+static void *rbuVfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ return pRealVfs->xDlOpen(pRealVfs, 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 rbuVfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ pRealVfs->xDlError(pRealVfs, nByte, zErrMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
|
+*/
|
|
+static void (*rbuVfsDlSym(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ void *pArg,
|
|
+ const char *zSym
|
|
+))(void){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ return pRealVfs->xDlSym(pRealVfs, pArg, zSym);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close the dynamic library handle pHandle.
|
|
+*/
|
|
+static void rbuVfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ pRealVfs->xDlClose(pRealVfs, pHandle);
|
|
+}
|
|
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
|
+
|
|
+/*
|
|
+** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
+** random data.
|
|
+*/
|
|
+static int rbuVfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ return pRealVfs->xRandomness(pRealVfs, nByte, zBufOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sleep for nMicro microseconds. Return the number of microseconds
|
|
+** actually slept.
|
|
+*/
|
|
+static int rbuVfsSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ return pRealVfs->xSleep(pRealVfs, nMicro);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current time as a Julian Day number in *pTimeOut.
|
|
+*/
|
|
+static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
|
|
+ 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().
|
|
+*/
|
|
+void sqlite3rbu_destroy_vfs(const char *zName){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
|
|
+ if( pVfs && pVfs->xOpen==rbuVfsOpen ){
|
|
+ sqlite3_mutex_free(((rbu_vfs*)pVfs)->mutex);
|
|
+ sqlite3_vfs_unregister(pVfs);
|
|
+ sqlite3_free(pVfs);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create an RBU VFS named zName that accesses the underlying file-system
|
|
+** via existing VFS zParent. The new object is registered as a non-default
|
|
+** VFS with SQLite before returning.
|
|
+*/
|
|
+int sqlite3rbu_create_vfs(const char *zName, const char *zParent){
|
|
+
|
|
+ /* Template for VFS */
|
|
+ static sqlite3_vfs vfs_template = {
|
|
+ 1, /* iVersion */
|
|
+ 0, /* szOsFile */
|
|
+ 0, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ 0, /* zName */
|
|
+ 0, /* pAppData */
|
|
+ rbuVfsOpen, /* xOpen */
|
|
+ rbuVfsDelete, /* xDelete */
|
|
+ rbuVfsAccess, /* xAccess */
|
|
+ rbuVfsFullPathname, /* xFullPathname */
|
|
+
|
|
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
+ rbuVfsDlOpen, /* xDlOpen */
|
|
+ rbuVfsDlError, /* xDlError */
|
|
+ rbuVfsDlSym, /* xDlSym */
|
|
+ rbuVfsDlClose, /* xDlClose */
|
|
+#else
|
|
+ 0, 0, 0, 0,
|
|
+#endif
|
|
+
|
|
+ rbuVfsRandomness, /* xRandomness */
|
|
+ rbuVfsSleep, /* xSleep */
|
|
+ rbuVfsCurrentTime, /* xCurrentTime */
|
|
+ rbuVfsGetLastError, /* xGetLastError */
|
|
+ 0, /* xCurrentTimeInt64 (version 2) */
|
|
+ 0, 0, 0 /* Unimplemented version 3 methods */
|
|
+ };
|
|
+
|
|
+ rbu_vfs *pNew = 0; /* Newly allocated VFS */
|
|
+ int rc = SQLITE_OK;
|
|
+ size_t nName;
|
|
+ size_t nByte;
|
|
+
|
|
+ nName = strlen(zName);
|
|
+ nByte = sizeof(rbu_vfs) + nName + 1;
|
|
+ pNew = (rbu_vfs*)sqlite3_malloc(nByte);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ sqlite3_vfs *pParent; /* Parent VFS */
|
|
+ memset(pNew, 0, nByte);
|
|
+ pParent = sqlite3_vfs_find(zParent);
|
|
+ if( pParent==0 ){
|
|
+ rc = SQLITE_NOTFOUND;
|
|
+ }else{
|
|
+ char *zSpace;
|
|
+ memcpy(&pNew->base, &vfs_template, sizeof(sqlite3_vfs));
|
|
+ pNew->base.mxPathname = pParent->mxPathname;
|
|
+ pNew->base.szOsFile = sizeof(rbu_file) + pParent->szOsFile;
|
|
+ pNew->pRealVfs = pParent;
|
|
+ pNew->base.zName = (const char*)(zSpace = (char*)&pNew[1]);
|
|
+ memcpy(zSpace, zName, nName);
|
|
+
|
|
+ /* Allocate the mutex and register the new VFS (not as the default) */
|
|
+ pNew->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE);
|
|
+ if( pNew->mutex==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_vfs_register(&pNew->base, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_mutex_free(pNew->mutex);
|
|
+ sqlite3_free(pNew);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Configure the aggregate temp file size limit for this RBU handle.
|
|
+*/
|
|
+sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu *pRbu, sqlite3_int64 n){
|
|
+ if( n>=0 ){
|
|
+ pRbu->szTempLimit = n;
|
|
+ }
|
|
+ return pRbu->szTempLimit;
|
|
+}
|
|
+
|
|
+sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){
|
|
+ return pRbu->szTemp;
|
|
+}
|
|
+
|
|
+
|
|
+/**************************************************************************/
|
|
+
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
|
|
--- origsrc/sqlite-autoconf-3280000/sqlite3rbu.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/sqlite3rbu.h 2019-03-05 13:14:41.036361800 +0100
|
|
@@ -0,0 +1,605 @@
|
|
+/*
|
|
+** 2014 August 30
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file contains the public interface for the RBU extension.
|
|
+*/
|
|
+
|
|
+/*
|
|
+** SUMMARY
|
|
+**
|
|
+** Writing a transaction containing a large number of operations on
|
|
+** b-tree indexes that are collectively larger than the available cache
|
|
+** memory can be very inefficient.
|
|
+**
|
|
+** The problem is that in order to update a b-tree, the leaf page (at least)
|
|
+** containing the entry being inserted or deleted must be modified. If the
|
|
+** working set of leaves is larger than the available cache memory, then a
|
|
+** single leaf that is modified more than once as part of the transaction
|
|
+** may be loaded from or written to the persistent media multiple times.
|
|
+** Additionally, because the index updates are likely to be applied in
|
|
+** random order, access to pages within the database is also likely to be in
|
|
+** random order, which is itself quite inefficient.
|
|
+**
|
|
+** One way to improve the situation is to sort the operations on each index
|
|
+** by index key before applying them to the b-tree. This leads to an IO
|
|
+** pattern that resembles a single linear scan through the index b-tree,
|
|
+** and all but guarantees each modified leaf page is loaded and stored
|
|
+** exactly once. SQLite uses this trick to improve the performance of
|
|
+** CREATE INDEX commands. This extension allows it to be used to improve
|
|
+** the performance of large transactions on existing databases.
|
|
+**
|
|
+** Additionally, this extension allows the work involved in writing the
|
|
+** large transaction to be broken down into sub-transactions performed
|
|
+** sequentially by separate processes. This is useful if the system cannot
|
|
+** guarantee that a single update process will run for long enough to apply
|
|
+** the entire update, for example because the update is being applied on a
|
|
+** mobile device that is frequently rebooted. Even after the writer process
|
|
+** has committed one or more sub-transactions, other database clients continue
|
|
+** to read from the original database snapshot. In other words, partially
|
|
+** applied transactions are not visible to other clients.
|
|
+**
|
|
+** "RBU" stands for "Resumable Bulk Update". As in a large database update
|
|
+** transmitted via a wireless network to a mobile device. A transaction
|
|
+** applied using this extension is hence refered to as an "RBU update".
|
|
+**
|
|
+**
|
|
+** LIMITATIONS
|
|
+**
|
|
+** An "RBU update" transaction is subject to the following limitations:
|
|
+**
|
|
+** * The transaction must consist of INSERT, UPDATE and DELETE operations
|
|
+** only.
|
|
+**
|
|
+** * INSERT statements may not use any default values.
|
|
+**
|
|
+** * UPDATE and DELETE statements must identify their target rows by
|
|
+** non-NULL PRIMARY KEY values. Rows with NULL values stored in PRIMARY
|
|
+** KEY fields may not be updated or deleted. If the table being written
|
|
+** has no PRIMARY KEY, affected rows must be identified by rowid.
|
|
+**
|
|
+** * UPDATE statements may not modify PRIMARY KEY columns.
|
|
+**
|
|
+** * No triggers will be fired.
|
|
+**
|
|
+** * No foreign key violations are detected or reported.
|
|
+**
|
|
+** * CHECK constraints are not enforced.
|
|
+**
|
|
+** * No constraint handling mode except for "OR ROLLBACK" is supported.
|
|
+**
|
|
+**
|
|
+** PREPARATION
|
|
+**
|
|
+** An "RBU update" is stored as a separate SQLite database. A database
|
|
+** containing an RBU update is an "RBU database". For each table in the
|
|
+** target database to be updated, the RBU database should contain a table
|
|
+** named "data_<target name>" containing the same set of columns as the
|
|
+** target table, and one more - "rbu_control". The data_% table should
|
|
+** have no PRIMARY KEY or UNIQUE constraints, but each column should have
|
|
+** the same type as the corresponding column in the target database.
|
|
+** The "rbu_control" column should have no type at all. For example, if
|
|
+** the target database contains:
|
|
+**
|
|
+** CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE);
|
|
+**
|
|
+** Then the RBU database should contain:
|
|
+**
|
|
+** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control);
|
|
+**
|
|
+** The order of the columns in the data_% table does not matter.
|
|
+**
|
|
+** Instead of a regular table, the RBU database may also contain virtual
|
|
+** tables or view named using the data_<target> naming scheme.
|
|
+**
|
|
+** Instead of the plain data_<target> naming scheme, RBU database tables
|
|
+** may also be named data<integer>_<target>, where <integer> is any sequence
|
|
+** of zero or more numeric characters (0-9). This can be significant because
|
|
+** tables within the RBU database are always processed in order sorted by
|
|
+** name. By judicious selection of the <integer> portion of the names
|
|
+** of the RBU tables the user can therefore control the order in which they
|
|
+** are processed. This can be useful, for example, to ensure that "external
|
|
+** content" FTS4 tables are updated before their underlying content tables.
|
|
+**
|
|
+** If the target database table is a virtual table or a table that has no
|
|
+** PRIMARY KEY declaration, the data_% table must also contain a column
|
|
+** named "rbu_rowid". This column is mapped to the tables implicit primary
|
|
+** key column - "rowid". Virtual tables for which the "rowid" column does
|
|
+** not function like a primary key value cannot be updated using RBU. For
|
|
+** example, if the target db contains either of the following:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE x1 USING fts3(a, b);
|
|
+** CREATE TABLE x1(a, b)
|
|
+**
|
|
+** then the RBU database should contain:
|
|
+**
|
|
+** CREATE TABLE data_x1(a, b, rbu_rowid, rbu_control);
|
|
+**
|
|
+** All non-hidden columns (i.e. all columns matched by "SELECT *") of the
|
|
+** target table must be present in the input table. For virtual tables,
|
|
+** hidden columns are optional - they are updated by RBU if present in
|
|
+** the input table, or not otherwise. For example, to write to an fts4
|
|
+** table with a hidden languageid column such as:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');
|
|
+**
|
|
+** Either of the following input table schemas may be used:
|
|
+**
|
|
+** CREATE TABLE data_ft1(a, b, langid, rbu_rowid, rbu_control);
|
|
+** CREATE TABLE data_ft1(a, b, rbu_rowid, rbu_control);
|
|
+**
|
|
+** For each row to INSERT into the target database as part of the RBU
|
|
+** update, the corresponding data_% table should contain a single record
|
|
+** with the "rbu_control" column set to contain integer value 0. The
|
|
+** other columns should be set to the values that make up the new record
|
|
+** to insert.
|
|
+**
|
|
+** If the target database table has an INTEGER PRIMARY KEY, it is not
|
|
+** possible to insert a NULL value into the IPK column. Attempting to
|
|
+** do so results in an SQLITE_MISMATCH error.
|
|
+**
|
|
+** For each row to DELETE from the target database as part of the RBU
|
|
+** update, the corresponding data_% table should contain a single record
|
|
+** with the "rbu_control" column set to contain integer value 1. The
|
|
+** real primary key values of the row to delete should be stored in the
|
|
+** corresponding columns of the data_% table. The values stored in the
|
|
+** other columns are not used.
|
|
+**
|
|
+** For each row to UPDATE from the target database as part of the RBU
|
|
+** update, the corresponding data_% table should contain a single record
|
|
+** with the "rbu_control" column set to contain a value of type text.
|
|
+** The real primary key values identifying the row to update should be
|
|
+** stored in the corresponding columns of the data_% table row, as should
|
|
+** the new values of all columns being update. The text value in the
|
|
+** "rbu_control" column must contain the same number of characters as
|
|
+** there are columns in the target database table, and must consist entirely
|
|
+** of 'x' and '.' characters (or in some special cases 'd' - see below). For
|
|
+** each column that is being updated, the corresponding character is set to
|
|
+** 'x'. For those that remain as they are, the corresponding character of the
|
|
+** rbu_control value should be set to '.'. For example, given the tables
|
|
+** above, the update statement:
|
|
+**
|
|
+** UPDATE t1 SET c = 'usa' WHERE a = 4;
|
|
+**
|
|
+** is represented by the data_t1 row created by:
|
|
+**
|
|
+** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..x');
|
|
+**
|
|
+** Instead of an 'x' character, characters of the rbu_control value specified
|
|
+** for UPDATEs may also be set to 'd'. In this case, instead of updating the
|
|
+** target table with the value stored in the corresponding data_% column, the
|
|
+** user-defined SQL function "rbu_delta()" is invoked and the result stored in
|
|
+** the target table column. rbu_delta() is invoked with two arguments - the
|
|
+** original value currently stored in the target table column and the
|
|
+** value specified in the data_xxx table.
|
|
+**
|
|
+** For example, this row:
|
|
+**
|
|
+** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d');
|
|
+**
|
|
+** is similar to an UPDATE statement such as:
|
|
+**
|
|
+** UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4;
|
|
+**
|
|
+** Finally, if an 'f' character appears in place of a 'd' or 's' in an
|
|
+** ota_control string, the contents of the data_xxx table column is assumed
|
|
+** to be a "fossil delta" - a patch to be applied to a blob value in the
|
|
+** format used by the fossil source-code management system. In this case
|
|
+** the existing value within the target database table must be of type BLOB.
|
|
+** It is replaced by the result of applying the specified fossil delta to
|
|
+** itself.
|
|
+**
|
|
+** If the target database table is a virtual table or a table with no PRIMARY
|
|
+** KEY, the rbu_control value should not include a character corresponding
|
|
+** to the rbu_rowid value. For example, this:
|
|
+**
|
|
+** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control)
|
|
+** VALUES(NULL, 'usa', 12, '.x');
|
|
+**
|
|
+** causes a result similar to:
|
|
+**
|
|
+** UPDATE ft1 SET b = 'usa' WHERE rowid = 12;
|
|
+**
|
|
+** The data_xxx tables themselves should have no PRIMARY KEY declarations.
|
|
+** However, RBU is more efficient if reading the rows in from each data_xxx
|
|
+** table in "rowid" order is roughly the same as reading them sorted by
|
|
+** the PRIMARY KEY of the corresponding target database table. In other
|
|
+** words, rows should be sorted using the destination table PRIMARY KEY
|
|
+** fields before they are inserted into the data_xxx tables.
|
|
+**
|
|
+** USAGE
|
|
+**
|
|
+** The API declared below allows an application to apply an RBU update
|
|
+** stored on disk to an existing target database. Essentially, the
|
|
+** application:
|
|
+**
|
|
+** 1) Opens an RBU handle using the sqlite3rbu_open() function.
|
|
+**
|
|
+** 2) Registers any required virtual table modules with the database
|
|
+** handle returned by sqlite3rbu_db(). Also, if required, register
|
|
+** the rbu_delta() implementation.
|
|
+**
|
|
+** 3) Calls the sqlite3rbu_step() function one or more times on
|
|
+** the new handle. Each call to sqlite3rbu_step() performs a single
|
|
+** b-tree operation, so thousands of calls may be required to apply
|
|
+** a complete update.
|
|
+**
|
|
+** 4) Calls sqlite3rbu_close() to close the RBU update handle. If
|
|
+** sqlite3rbu_step() has been called enough times to completely
|
|
+** apply the update to the target database, then the RBU database
|
|
+** is marked as fully applied. Otherwise, the state of the RBU
|
|
+** update application is saved in the RBU database for later
|
|
+** resumption.
|
|
+**
|
|
+** See comments below for more detail on APIs.
|
|
+**
|
|
+** If an update is only partially applied to the target database by the
|
|
+** time sqlite3rbu_close() is called, various state information is saved
|
|
+** within the RBU database. This allows subsequent processes to automatically
|
|
+** resume the RBU update from where it left off.
|
|
+**
|
|
+** To remove all RBU extension state information, returning an RBU database
|
|
+** to its original contents, it is sufficient to drop all tables that begin
|
|
+** with the prefix "rbu_"
|
|
+**
|
|
+** DATABASE LOCKING
|
|
+**
|
|
+** An RBU update may not be applied to a database in WAL mode. Attempting
|
|
+** to do so is an error (SQLITE_ERROR).
|
|
+**
|
|
+** While an RBU handle is open, a SHARED lock may be held on the target
|
|
+** database file. This means it is possible for other clients to read the
|
|
+** database, but not to write it.
|
|
+**
|
|
+** If an RBU update is started and then suspended before it is completed,
|
|
+** then an external client writes to the database, then attempting to resume
|
|
+** the suspended RBU update is also an error (SQLITE_BUSY).
|
|
+*/
|
|
+
|
|
+#ifndef _SQLITE3RBU_H
|
|
+#define _SQLITE3RBU_H
|
|
+
|
|
+#include "sqlite3.h" /* Required for error code definitions */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+typedef struct sqlite3rbu sqlite3rbu;
|
|
+
|
|
+/*
|
|
+** Open an RBU handle.
|
|
+**
|
|
+** Argument zTarget is the path to the target database. Argument zRbu is
|
|
+** the path to the RBU database. Each call to this function must be matched
|
|
+** by a call to sqlite3rbu_close(). When opening the databases, RBU passes
|
|
+** the SQLITE_CONFIG_URI flag to sqlite3_open_v2(). So if either zTarget
|
|
+** or zRbu begin with "file:", it will be interpreted as an SQLite
|
|
+** database URI, not a regular file name.
|
|
+**
|
|
+** If the zState argument is passed a NULL value, the RBU extension stores
|
|
+** the current state of the update (how many rows have been updated, which
|
|
+** indexes are yet to be updated etc.) within the RBU database itself. This
|
|
+** can be convenient, as it means that the RBU application does not need to
|
|
+** organize removing a separate state file after the update is concluded.
|
|
+** Or, if zState is non-NULL, it must be a path to a database file in which
|
|
+** the RBU extension can store the state of the update.
|
|
+**
|
|
+** When resuming an RBU update, the zState argument must be passed the same
|
|
+** value as when the RBU update was started.
|
|
+**
|
|
+** Once the RBU update is finished, the RBU extension does not
|
|
+** automatically remove any zState database file, even if it created it.
|
|
+**
|
|
+** By default, RBU uses the default VFS to access the files on disk. To
|
|
+** use a VFS other than the default, an SQLite "file:" URI containing a
|
|
+** "vfs=..." option may be passed as the zTarget option.
|
|
+**
|
|
+** IMPORTANT NOTE FOR ZIPVFS USERS: The RBU extension works with all of
|
|
+** SQLite's built-in VFSs, including the multiplexor VFS. However it does
|
|
+** not work out of the box with zipvfs. Refer to the comment describing
|
|
+** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
|
|
+*/
|
|
+SQLITE_API sqlite3rbu *sqlite3rbu_open(
|
|
+ const char *zTarget,
|
|
+ const char *zRbu,
|
|
+ const char *zState
|
|
+);
|
|
+
|
|
+/*
|
|
+** Open an RBU handle to perform an RBU vacuum on database file zTarget.
|
|
+** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
|
|
+** that it can be suspended and resumed like an RBU update.
|
|
+**
|
|
+** The second argument to this function identifies a database in which
|
|
+** to store the state of the RBU vacuum operation if it is suspended. The
|
|
+** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
|
|
+** operation, the state database should either not exist or be empty
|
|
+** (contain no tables). If an RBU vacuum is suspended by calling
|
|
+** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
|
|
+** returned SQLITE_DONE, the vacuum state is stored in the state database.
|
|
+** The vacuum can be resumed by calling this function to open a new RBU
|
|
+** handle specifying the same target and state databases.
|
|
+**
|
|
+** If the second argument passed to this function is NULL, then the
|
|
+** name of the state database is "<database>-vacuum", where <database>
|
|
+** is the name of the target database file. In this case, on UNIX, if the
|
|
+** state database is not already present in the file-system, it is created
|
|
+** with the same permissions as the target db is made.
|
|
+**
|
|
+** With an RBU vacuum, it is an SQLITE_MISUSE error if the name of the
|
|
+** state database ends with "-vactmp". This name is reserved for internal
|
|
+** use.
|
|
+**
|
|
+** This function does not delete the state database after an RBU vacuum
|
|
+** is completed, even if it created it. However, if the call to
|
|
+** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
|
|
+** of the state tables within the state database are zeroed. This way,
|
|
+** the next call to sqlite3rbu_vacuum() opens a handle that starts a
|
|
+** new RBU vacuum operation.
|
|
+**
|
|
+** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
|
|
+** describing the sqlite3rbu_create_vfs() API function below for
|
|
+** a description of the complications associated with using RBU with
|
|
+** zipvfs databases.
|
|
+*/
|
|
+SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
|
|
+ const char *zTarget,
|
|
+ const char *zState
|
|
+);
|
|
+
|
|
+/*
|
|
+** Configure a limit for the amount of temp space that may be used by
|
|
+** the RBU handle passed as the first argument. The new limit is specified
|
|
+** in bytes by the second parameter. If it is positive, the limit is updated.
|
|
+** If the second parameter to this function is passed zero, then the limit
|
|
+** is removed entirely. If the second parameter is negative, the limit is
|
|
+** not modified (this is useful for querying the current limit).
|
|
+**
|
|
+** In all cases the returned value is the current limit in bytes (zero
|
|
+** indicates unlimited).
|
|
+**
|
|
+** If the temp space limit is exceeded during operation, an SQLITE_FULL
|
|
+** error is returned.
|
|
+*/
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu*, sqlite3_int64);
|
|
+
|
|
+/*
|
|
+** Return the current amount of temp file space, in bytes, currently used by
|
|
+** the RBU handle passed as the only argument.
|
|
+*/
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu*);
|
|
+
|
|
+/*
|
|
+** Internally, each RBU connection uses a separate SQLite database
|
|
+** connection to access the target and rbu update databases. This
|
|
+** API allows the application direct access to these database handles.
|
|
+**
|
|
+** The first argument passed to this function must be a valid, open, RBU
|
|
+** handle. The second argument should be passed zero to access the target
|
|
+** database handle, or non-zero to access the rbu update database handle.
|
|
+** Accessing the underlying database handles may be useful in the
|
|
+** following scenarios:
|
|
+**
|
|
+** * If any target tables are virtual tables, it may be necessary to
|
|
+** call sqlite3_create_module() on the target database handle to
|
|
+** register the required virtual table implementations.
|
|
+**
|
|
+** * If the data_xxx tables in the RBU source database are virtual
|
|
+** tables, the application may need to call sqlite3_create_module() on
|
|
+** the rbu update db handle to any required virtual table
|
|
+** implementations.
|
|
+**
|
|
+** * If the application uses the "rbu_delta()" feature described above,
|
|
+** it must use sqlite3_create_function() or similar to register the
|
|
+** rbu_delta() implementation with the target database handle.
|
|
+**
|
|
+** If an error has occurred, either while opening or stepping the RBU object,
|
|
+** this function may return NULL. The error code and message may be collected
|
|
+** when sqlite3rbu_close() is called.
|
|
+**
|
|
+** Database handles returned by this function remain valid until the next
|
|
+** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db().
|
|
+*/
|
|
+SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
|
|
+
|
|
+/*
|
|
+** Do some work towards applying the RBU update to the target db.
|
|
+**
|
|
+** Return SQLITE_DONE if the update has been completely applied, or
|
|
+** SQLITE_OK if no error occurs but there remains work to do to apply
|
|
+** the RBU update. If an error does occur, some other error code is
|
|
+** returned.
|
|
+**
|
|
+** Once a call to sqlite3rbu_step() has returned a value other than
|
|
+** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops
|
|
+** that immediately return the same value.
|
|
+*/
|
|
+SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu);
|
|
+
|
|
+/*
|
|
+** Force RBU to save its state to disk.
|
|
+**
|
|
+** If a power failure or application crash occurs during an update, following
|
|
+** system recovery RBU may resume the update from the point at which the state
|
|
+** was last saved. In other words, from the most recent successful call to
|
|
+** sqlite3rbu_close() or this function.
|
|
+**
|
|
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
|
+*/
|
|
+SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
|
|
+
|
|
+/*
|
|
+** Close an RBU handle.
|
|
+**
|
|
+** If the RBU update has been completely applied, mark the RBU database
|
|
+** as fully applied. Otherwise, assuming no error has occurred, save the
|
|
+** current state of the RBU update appliation to the RBU database.
|
|
+**
|
|
+** If an error has already occurred as part of an sqlite3rbu_step()
|
|
+** or sqlite3rbu_open() call, or if one occurs within this function, an
|
|
+** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
|
|
+** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
|
|
+** English language error message. It is the responsibility of the caller to
|
|
+** eventually free any such buffer using sqlite3_free().
|
|
+**
|
|
+** Otherwise, if no error occurs, this function returns SQLITE_OK if the
|
|
+** update has been partially applied, or SQLITE_DONE if it has been
|
|
+** completely applied.
|
|
+*/
|
|
+SQLITE_API int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
|
|
+
|
|
+/*
|
|
+** Return the total number of key-value operations (inserts, deletes or
|
|
+** updates) that have been performed on the target database since the
|
|
+** current RBU update was started.
|
|
+*/
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
|
|
+
|
|
+/*
|
|
+** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
|
|
+** progress indications for the two stages of an RBU update. This API may
|
|
+** be useful for driving GUI progress indicators and similar.
|
|
+**
|
|
+** An RBU update is divided into two stages:
|
|
+**
|
|
+** * Stage 1, in which changes are accumulated in an oal/wal file, and
|
|
+** * Stage 2, in which the contents of the wal file are copied into the
|
|
+** main database.
|
|
+**
|
|
+** The update is visible to non-RBU clients during stage 2. During stage 1
|
|
+** non-RBU reader clients may see the original database.
|
|
+**
|
|
+** If this API is called during stage 2 of the update, output variable
|
|
+** (*pnOne) is set to 10000 to indicate that stage 1 has finished and (*pnTwo)
|
|
+** to a value between 0 and 10000 to indicate the permyriadage progress of
|
|
+** stage 2. A value of 5000 indicates that stage 2 is half finished,
|
|
+** 9000 indicates that it is 90% finished, and so on.
|
|
+**
|
|
+** If this API is called during stage 1 of the update, output variable
|
|
+** (*pnTwo) is set to 0 to indicate that stage 2 has not yet started. The
|
|
+** value to which (*pnOne) is set depends on whether or not the RBU
|
|
+** database contains an "rbu_count" table. The rbu_count table, if it
|
|
+** exists, must contain the same columns as the following:
|
|
+**
|
|
+** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
|
|
+**
|
|
+** There must be one row in the table for each source (data_xxx) table within
|
|
+** the RBU database. The 'tbl' column should contain the name of the source
|
|
+** table. The 'cnt' column should contain the number of rows within the
|
|
+** source table.
|
|
+**
|
|
+** If the rbu_count table is present and populated correctly and this
|
|
+** API is called during stage 1, the *pnOne output variable is set to the
|
|
+** permyriadage progress of the same stage. If the rbu_count table does
|
|
+** not exist, then (*pnOne) is set to -1 during stage 1. If the rbu_count
|
|
+** table exists but is not correctly populated, the value of the *pnOne
|
|
+** output variable during stage 1 is undefined.
|
|
+*/
|
|
+SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
|
|
+
|
|
+/*
|
|
+** Obtain an indication as to the current stage of an RBU update or vacuum.
|
|
+** This function always returns one of the SQLITE_RBU_STATE_XXX constants
|
|
+** defined in this file. Return values should be interpreted as follows:
|
|
+**
|
|
+** SQLITE_RBU_STATE_OAL:
|
|
+** RBU is currently building a *-oal file. The next call to sqlite3rbu_step()
|
|
+** may either add further data to the *-oal file, or compute data that will
|
|
+** be added by a subsequent call.
|
|
+**
|
|
+** SQLITE_RBU_STATE_MOVE:
|
|
+** RBU has finished building the *-oal file. The next call to sqlite3rbu_step()
|
|
+** will move the *-oal file to the equivalent *-wal path. If the current
|
|
+** operation is an RBU update, then the updated version of the database
|
|
+** file will become visible to ordinary SQLite clients following the next
|
|
+** call to sqlite3rbu_step().
|
|
+**
|
|
+** SQLITE_RBU_STATE_CHECKPOINT:
|
|
+** RBU is currently performing an incremental checkpoint. The next call to
|
|
+** sqlite3rbu_step() will copy a page of data from the *-wal file into
|
|
+** the target database file.
|
|
+**
|
|
+** SQLITE_RBU_STATE_DONE:
|
|
+** The RBU operation has finished. Any subsequent calls to sqlite3rbu_step()
|
|
+** will immediately return SQLITE_DONE.
|
|
+**
|
|
+** SQLITE_RBU_STATE_ERROR:
|
|
+** An error has occurred. Any subsequent calls to sqlite3rbu_step() will
|
|
+** immediately return the SQLite error code associated with the error.
|
|
+*/
|
|
+#define SQLITE_RBU_STATE_OAL 1
|
|
+#define SQLITE_RBU_STATE_MOVE 2
|
|
+#define SQLITE_RBU_STATE_CHECKPOINT 3
|
|
+#define SQLITE_RBU_STATE_DONE 4
|
|
+#define SQLITE_RBU_STATE_ERROR 5
|
|
+
|
|
+SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
|
|
+
|
|
+/*
|
|
+** Create an RBU VFS named zName that accesses the underlying file-system
|
|
+** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
|
|
+** then the new RBU VFS uses the default system VFS to access the file-system.
|
|
+** The new object is registered as a non-default VFS with SQLite before
|
|
+** returning.
|
|
+**
|
|
+** Part of the RBU implementation uses a custom VFS object. Usually, this
|
|
+** object is created and deleted automatically by RBU.
|
|
+**
|
|
+** The exception is for applications that also use zipvfs. In this case,
|
|
+** the custom VFS must be explicitly created by the user before the RBU
|
|
+** handle is opened. The RBU VFS should be installed so that the zipvfs
|
|
+** VFS uses the RBU VFS, which in turn uses any other VFS layers in use
|
|
+** (for example multiplexor) to access the file-system. For example,
|
|
+** to assemble an RBU enabled VFS stack that uses both zipvfs and
|
|
+** multiplexor (error checking omitted):
|
|
+**
|
|
+** // Create a VFS named "multiplex" (not the default).
|
|
+** sqlite3_multiplex_initialize(0, 0);
|
|
+**
|
|
+** // Create an rbu VFS named "rbu" that uses multiplexor. If the
|
|
+** // second argument were replaced with NULL, the "rbu" VFS would
|
|
+** // access the file-system via the system default VFS, bypassing the
|
|
+** // multiplexor.
|
|
+** sqlite3rbu_create_vfs("rbu", "multiplex");
|
|
+**
|
|
+** // Create a zipvfs VFS named "zipvfs" that uses rbu.
|
|
+** zipvfs_create_vfs_v3("zipvfs", "rbu", 0, xCompressorAlgorithmDetector);
|
|
+**
|
|
+** // Make zipvfs the default VFS.
|
|
+** sqlite3_vfs_register(sqlite3_vfs_find("zipvfs"), 1);
|
|
+**
|
|
+** Because the default VFS created above includes a RBU functionality, it
|
|
+** may be used by RBU clients. Attempting to use RBU with a zipvfs VFS stack
|
|
+** that does not include the RBU layer results in an error.
|
|
+**
|
|
+** The overhead of adding the "rbu" VFS to the system is negligible for
|
|
+** non-RBU users. There is no harm in an application accessing the
|
|
+** file-system via "rbu" all the time, even if it only uses RBU functionality
|
|
+** occasionally.
|
|
+*/
|
|
+SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
|
|
+
|
|
+/*
|
|
+** Deregister and destroy an RBU vfs created by an earlier call to
|
|
+** sqlite3rbu_create_vfs().
|
|
+**
|
|
+** VFS objects are not reference counted. If a VFS object is destroyed
|
|
+** before all database handles that use it have been closed, the results
|
|
+** are undefined.
|
|
+*/
|
|
+SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+} /* end of the 'extern "C"' block */
|
|
+#endif
|
|
+
|
|
+#endif /* _SQLITE3RBU_H */
|
|
--- origsrc/sqlite-autoconf-3280000/stmt.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/stmt.c 2019-05-14 16:22:31.272262300 +0200
|
|
@@ -0,0 +1,313 @@
|
|
+/*
|
|
+** 2017-05-31
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file demonstrates an eponymous virtual table that returns information
|
|
+** about all prepared statements for the database connection.
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** .load ./stmt
|
|
+** .mode line
|
|
+** .header on
|
|
+** SELECT * FROM stmt;
|
|
+*/
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB)
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/* stmt_vtab is a subclass of sqlite3_vtab which will
|
|
+** serve as the underlying representation of a stmt virtual table
|
|
+*/
|
|
+typedef struct stmt_vtab stmt_vtab;
|
|
+struct stmt_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this stmt vtab */
|
|
+};
|
|
+
|
|
+/* stmt_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct stmt_cursor stmt_cursor;
|
|
+struct stmt_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database connection for this cursor */
|
|
+ sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The stmtConnect() method is invoked to create a new
|
|
+** stmt_vtab that describes the stmt virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for stmt_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the stmt_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against stmt will look like.
|
|
+*/
|
|
+static int stmtConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ stmt_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define STMT_COLUMN_SQL 0 /* SQL for the statement */
|
|
+#define STMT_COLUMN_NCOL 1 /* Number of result columns */
|
|
+#define STMT_COLUMN_RO 2 /* True if read-only */
|
|
+#define STMT_COLUMN_BUSY 3 /* True if currently busy */
|
|
+#define STMT_COLUMN_NSCAN 4 /* SQLITE_STMTSTATUS_FULLSCAN_STEP */
|
|
+#define STMT_COLUMN_NSORT 5 /* SQLITE_STMTSTATUS_SORT */
|
|
+#define STMT_COLUMN_NAIDX 6 /* SQLITE_STMTSTATUS_AUTOINDEX */
|
|
+#define STMT_COLUMN_NSTEP 7 /* SQLITE_STMTSTATUS_VM_STEP */
|
|
+#define STMT_COLUMN_REPREP 8 /* SQLITE_STMTSTATUS_REPREPARE */
|
|
+#define STMT_COLUMN_RUN 9 /* SQLITE_STMTSTATUS_RUN */
|
|
+#define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */
|
|
+
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep,"
|
|
+ "reprep,run,mem)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->db = db;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for stmt_cursor objects.
|
|
+*/
|
|
+static int stmtDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new stmt_cursor object.
|
|
+*/
|
|
+static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ stmt_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->db = ((stmt_vtab*)p)->db;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a stmt_cursor.
|
|
+*/
|
|
+static int stmtClose(sqlite3_vtab_cursor *cur){
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a stmt_cursor to its next row of output.
|
|
+*/
|
|
+static int stmtNext(sqlite3_vtab_cursor *cur){
|
|
+ stmt_cursor *pCur = (stmt_cursor*)cur;
|
|
+ pCur->iRowid++;
|
|
+ pCur->pStmt = sqlite3_next_stmt(pCur->db, pCur->pStmt);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the stmt_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int stmtColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ stmt_cursor *pCur = (stmt_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case STMT_COLUMN_SQL: {
|
|
+ sqlite3_result_text(ctx, sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+ case STMT_COLUMN_NCOL: {
|
|
+ sqlite3_result_int(ctx, sqlite3_column_count(pCur->pStmt));
|
|
+ break;
|
|
+ }
|
|
+ case STMT_COLUMN_RO: {
|
|
+ sqlite3_result_int(ctx, sqlite3_stmt_readonly(pCur->pStmt));
|
|
+ break;
|
|
+ }
|
|
+ case STMT_COLUMN_BUSY: {
|
|
+ sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
|
|
+ break;
|
|
+ }
|
|
+ case STMT_COLUMN_MEM: {
|
|
+ i = SQLITE_STMTSTATUS_MEMUSED +
|
|
+ STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
|
|
+ /* Fall thru */
|
|
+ }
|
|
+ case STMT_COLUMN_NSCAN:
|
|
+ case STMT_COLUMN_NSORT:
|
|
+ case STMT_COLUMN_NAIDX:
|
|
+ case STMT_COLUMN_NSTEP:
|
|
+ case STMT_COLUMN_REPREP:
|
|
+ case STMT_COLUMN_RUN: {
|
|
+ sqlite3_result_int(ctx, sqlite3_stmt_status(pCur->pStmt,
|
|
+ i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ stmt_cursor *pCur = (stmt_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int stmtEof(sqlite3_vtab_cursor *cur){
|
|
+ stmt_cursor *pCur = (stmt_cursor*)cur;
|
|
+ return pCur->pStmt==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the stmt_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to stmtColumn() or stmtRowid() or
|
|
+** stmtEof().
|
|
+*/
|
|
+static int stmtFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ stmt_cursor *pCur = (stmt_cursor *)pVtabCursor;
|
|
+ pCur->pStmt = 0;
|
|
+ pCur->iRowid = 0;
|
|
+ return stmtNext(pVtabCursor);
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the stmt virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int stmtBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ pIdxInfo->estimatedCost = (double)500;
|
|
+ pIdxInfo->estimatedRows = 500;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** stmt virtual table.
|
|
+*/
|
|
+static const sqlite3_module stmtModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ stmtConnect, /* xConnect */
|
|
+ stmtBestIndex, /* xBestIndex */
|
|
+ stmtDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ stmtOpen, /* xOpen - open a cursor */
|
|
+ stmtClose, /* xClose - close a cursor */
|
|
+ stmtFilter, /* xFilter - configure scan constraints */
|
|
+ stmtNext, /* xNext - advance a cursor */
|
|
+ stmtEof, /* xEof - check for end of scan */
|
|
+ stmtColumn, /* xColumn - read data */
|
|
+ stmtRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+int sqlite3StmtVtabInit(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "sqlite_stmt", &stmtModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#ifndef SQLITE_CORE
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_stmt_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3StmtVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3StmtVtabInit(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
+#endif /* SQLITE_CORE */
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
|
|
--- origsrc/sqlite-autoconf-3280000/tea/configure.ac 2019-04-16 22:06:18.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/tea/configure.ac 2019-05-14 15:42:25.867211100 +0200
|
|
@@ -6,7 +6,7 @@ dnl to configure the system for the loca
|
|
# RCS: @(#) $Id: configure.in,v 1.43 2005/07/26 19:17:05 mdejong Exp $
|
|
|
|
#-----------------------------------------------------------------------
|
|
-# Sample configure.in for Tcl Extensions. The only places you should
|
|
+# Sample configure.ac for Tcl Extensions. The only places you should
|
|
# need to modify this file are marked by the string __CHANGE__
|
|
#-----------------------------------------------------------------------
|
|
|
|
@@ -27,7 +27,7 @@ AC_INIT([sqlite], [3.28.0])
|
|
# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
|
|
#--------------------------------------------------------------------
|
|
|
|
-TEA_INIT([3.9])
|
|
+TEA_INIT([3.10])
|
|
|
|
AC_CONFIG_AUX_DIR(tclconfig)
|
|
|
|
@@ -75,9 +75,23 @@ TEA_ADD_SOURCES([tclsqlite3.c])
|
|
TEA_ADD_HEADERS([])
|
|
TEA_ADD_INCLUDES([-I\"`\${CYGPATH} \${srcdir}/generic`\"])
|
|
TEA_ADD_LIBS([])
|
|
-TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DBSTAT_VTAB=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3_PARENTHESIS=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS4=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS5=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_COLUMN_METADATA=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_JSON1=1])
|
|
TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1])
|
|
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_UNTESTABLE=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_OMIT_LOOKASIDE=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_SECURE_DELETE=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_SOUNDEX=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_USE_ALLOCA=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_WIN32_NO_ANSI=1])
|
|
+TEA_ADD_CFLAGS([-DSQLITE_WIN32_GETVERSIONEX=0])
|
|
TEA_ADD_STUB_SOURCES([])
|
|
TEA_ADD_TCL_SOURCES([])
|
|
|
|
@@ -167,8 +181,9 @@ AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stu
|
|
#--------------------------------------------------------------------
|
|
#
|
|
#AC_CHECK_FUNC(fdatasync, , AC_DEFINE(fdatasync, fsync))
|
|
+AC_CHECK_HEADERS([malloc.h])
|
|
# Check for library functions that SQLite can optionally use.
|
|
-AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r])
|
|
+AC_CHECK_FUNCS([fdatasync usleep strchrnul localtime_r gmtime_r localtime_s malloc_usable_size utime flock readlink lstat pread pread64 pwrite pwrite64 rand_s])
|
|
|
|
AC_FUNC_STRERROR_R
|
|
|
|
--- origsrc/sqlite-autoconf-3280000/tea/generic/tclsqlite3.c 2019-04-16 22:06:18.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/tea/generic/tclsqlite3.c 2019-03-05 13:14:41.074439600 +0100
|
|
@@ -1,7 +1,7 @@
|
|
#ifdef USE_SYSTEM_SQLITE
|
|
# include <sqlite3.h>
|
|
#else
|
|
-#include "sqlite3.c"
|
|
+#include "../compat/sqlite3/sqlite3.c"
|
|
#endif
|
|
/*
|
|
** 2001 September 15
|
|
@@ -80,6 +80,30 @@
|
|
# define GETPID (int)GetCurrentProcessId
|
|
#endif
|
|
|
|
+#ifdef USE_TCL_STUBS
|
|
+# undef Tcl_BackgroundException
|
|
+# define Tcl_BackgroundException(interp, result) (DbUseNre()? \
|
|
+ ((void (*)(Tcl_Interp *, int))((&(tclStubsPtr->tcl_PkgProvideEx))[609]))((interp), (result)): \
|
|
+ ((void (*)(Tcl_Interp *))((&(tclStubsPtr->tcl_PkgProvideEx))[76]))(interp))
|
|
+#endif /* USE_TCL_STUBS */
|
|
+
|
|
+#if TCL_MAJOR_VERSION>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6)
|
|
+static int DbUseNre(void);
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * If we are not sure the platform is 32-bit, always use sqlite3_????64()
|
|
+ * in stead of sqlite3_????() for certain functions, in order to prevent overflow.
|
|
+ */
|
|
+#if !defined(i386) && !defined(__i386__) && !defined(_M_IX86) && \
|
|
+ !defined(_M_ARM) && !defined(__arm__) && !defined(__x86) && \
|
|
+ (!defined(__SIZEOF_POINTER__) || (__SIZEOF_POINTER__ != 4))
|
|
+# undef sqlite3_bind_text
|
|
+# undef sqlite3_result_text
|
|
+# define sqlite3_bind_text(pStmt, i, zData, nData, xDel) sqlite3_bind_text64(pStmt, i, zData, nData, xDel, SQLITE_UTF8)
|
|
+# define sqlite3_result_text(pCtx, z, n, xDel) sqlite3_result_text64(pCtx, z, n, xDel, SQLITE_UTF8)
|
|
+#endif
|
|
+
|
|
/*
|
|
* Windows needs to know which symbols to export. Unix does not.
|
|
* BUILD_sqlite should be undefined for Unix.
|
|
@@ -210,6 +234,10 @@ static int strlen30(const char *z){
|
|
return 0x3fffffff & (int)(z2 - z);
|
|
}
|
|
|
|
+#ifdef USE_TCL_STUBS
|
|
+# define tclStubsPtr staticTclStubsPtr
|
|
+static const TclStubs *tclStubsPtr = 0;
|
|
+#endif
|
|
|
|
#ifndef SQLITE_OMIT_INCRBLOB
|
|
/*
|
|
@@ -257,7 +285,7 @@ static int SQLITE_TCLAPI incrblobClose(
|
|
Tcl_Free((char *)p);
|
|
|
|
if( rc!=SQLITE_OK ){
|
|
- Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
|
|
+ Tcl_AppendResult(interp, sqlite3_errmsg(db), (char*)0);
|
|
return TCL_ERROR;
|
|
}
|
|
return TCL_OK;
|
|
@@ -300,7 +328,7 @@ static int SQLITE_TCLAPI incrblobInput(
|
|
*/
|
|
static int SQLITE_TCLAPI incrblobOutput(
|
|
ClientData instanceData,
|
|
- CONST char *buf,
|
|
+ const char *buf,
|
|
int toWrite,
|
|
int *errorCodePtr
|
|
){
|
|
@@ -413,7 +441,7 @@ static int createIncrblobChannel(
|
|
|
|
rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, &pBlob);
|
|
if( rc!=SQLITE_OK ){
|
|
- Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
|
|
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
@@ -434,7 +462,7 @@ static int createIncrblobChannel(
|
|
pDb->pIncrblob = p;
|
|
p->pDb = pDb;
|
|
|
|
- Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE);
|
|
+ Tcl_AppendResult(interp, Tcl_GetChannelName(p->channel), (char*)0);
|
|
return TCL_OK;
|
|
}
|
|
#else /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */
|
|
@@ -589,10 +617,16 @@ static void SQLITE_TCLAPI DbDeleteCmd(vo
|
|
static int DbBusyHandler(void *cd, int nTries){
|
|
SqliteDb *pDb = (SqliteDb*)cd;
|
|
int rc;
|
|
- char zVal[30];
|
|
+ char zVal[32];
|
|
+ Tcl_DString dstring;
|
|
|
|
- sqlite3_snprintf(sizeof(zVal), zVal, "%d", nTries);
|
|
- rc = Tcl_VarEval(pDb->interp, pDb->zBusy, " ", zVal, (char*)0);
|
|
+ sqlite3_snprintf(sizeof(zVal), zVal, " %d", nTries);
|
|
+ Tcl_DStringInit(&dstring);
|
|
+ Tcl_DStringAppend(&dstring, pDb->zBusy, -1);
|
|
+ Tcl_DStringAppend(&dstring, zVal, -1);
|
|
+ rc = Tcl_EvalEx(pDb->interp, Tcl_DStringValue(&dstring),
|
|
+ Tcl_DStringLength(&dstring), TCL_EVAL_GLOBAL);
|
|
+ Tcl_DStringFree(&dstring);
|
|
if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
|
|
return 0;
|
|
}
|
|
@@ -608,7 +642,7 @@ static int DbProgressHandler(void *cd){
|
|
int rc;
|
|
|
|
assert( pDb->zProgress );
|
|
- rc = Tcl_Eval(pDb->interp, pDb->zProgress);
|
|
+ rc = Tcl_EvalEx(pDb->interp, pDb->zProgress, -1, 0);
|
|
if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
|
|
return 1;
|
|
}
|
|
@@ -616,22 +650,27 @@ static int DbProgressHandler(void *cd){
|
|
}
|
|
#endif
|
|
|
|
-#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \
|
|
- !defined(SQLITE_OMIT_DEPRECATED)
|
|
+#ifndef SQLITE_OMIT_TRACE
|
|
/*
|
|
** This routine is called by the SQLite trace handler whenever a new
|
|
** block of SQL is executed. The TCL script in pDb->zTrace is executed.
|
|
*/
|
|
-static void DbTraceHandler(void *cd, const char *zSql){
|
|
+static int DbTraceHandler(
|
|
+ unsigned type, /* One of the SQLITE_TRACE_* event types. */
|
|
+ void *cd, /* The original context data pointer. */
|
|
+ void *pd, /* Primary event data, depends on event type. */
|
|
+ void *xd /* Extra event data, depends on event type. */
|
|
+){
|
|
SqliteDb *pDb = (SqliteDb*)cd;
|
|
Tcl_DString str;
|
|
|
|
Tcl_DStringInit(&str);
|
|
Tcl_DStringAppend(&str, pDb->zTrace, -1);
|
|
- Tcl_DStringAppendElement(&str, zSql);
|
|
- Tcl_Eval(pDb->interp, Tcl_DStringValue(&str));
|
|
+ Tcl_DStringAppendElement(&str, (char *)xd);
|
|
+ Tcl_EvalEx(pDb->interp, Tcl_DStringValue(&str), -1, 0);
|
|
Tcl_DStringFree(&str);
|
|
Tcl_ResetResult(pDb->interp);
|
|
+ return TCL_OK;
|
|
}
|
|
#endif
|
|
|
|
@@ -659,7 +698,7 @@ static int DbTraceV2Handler(
|
|
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
|
Tcl_IncrRefCount(pCmd);
|
|
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
|
- Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
|
|
+ Tcl_NewWideIntObj((size_t)pStmt));
|
|
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
|
Tcl_NewStringObj(zSql, -1));
|
|
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
|
@@ -674,7 +713,7 @@ static int DbTraceV2Handler(
|
|
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
|
Tcl_IncrRefCount(pCmd);
|
|
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
|
- Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
|
|
+ Tcl_NewWideIntObj((size_t)pStmt));
|
|
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
|
Tcl_NewWideIntObj((Tcl_WideInt)ns));
|
|
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
|
@@ -688,7 +727,7 @@ static int DbTraceV2Handler(
|
|
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
|
Tcl_IncrRefCount(pCmd);
|
|
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
|
- Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
|
|
+ Tcl_NewWideIntObj((size_t)pStmt));
|
|
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
|
Tcl_DecrRefCount(pCmd);
|
|
Tcl_ResetResult(pDb->interp);
|
|
@@ -700,7 +739,7 @@ static int DbTraceV2Handler(
|
|
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
|
Tcl_IncrRefCount(pCmd);
|
|
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
|
- Tcl_NewWideIntObj((Tcl_WideInt)db));
|
|
+ Tcl_NewWideIntObj((size_t)db));
|
|
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
|
Tcl_DecrRefCount(pCmd);
|
|
Tcl_ResetResult(pDb->interp);
|
|
@@ -711,25 +750,31 @@ static int DbTraceV2Handler(
|
|
}
|
|
#endif
|
|
|
|
-#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \
|
|
- !defined(SQLITE_OMIT_DEPRECATED)
|
|
+#ifndef SQLITE_OMIT_TRACE
|
|
/*
|
|
** This routine is called by the SQLite profile handler after a statement
|
|
** SQL has executed. The TCL script in pDb->zProfile is evaluated.
|
|
*/
|
|
-static void DbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm){
|
|
+static int DbProfileHandler(
|
|
+ unsigned type, /* One of the SQLITE_TRACE_* event types. */
|
|
+ void *cd, /* The original context data pointer. */
|
|
+ void *pd, /* Primary event data, depends on event type. */
|
|
+ void *xd /* Extra event data, depends on event type. */
|
|
+){
|
|
SqliteDb *pDb = (SqliteDb*)cd;
|
|
Tcl_DString str;
|
|
char zTm[100];
|
|
+ sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
|
|
|
|
- sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", tm);
|
|
+ sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", (Tcl_WideInt)(size_t)xd);
|
|
Tcl_DStringInit(&str);
|
|
Tcl_DStringAppend(&str, pDb->zProfile, -1);
|
|
- Tcl_DStringAppendElement(&str, zSql);
|
|
+ Tcl_DStringAppendElement(&str, sqlite3_sql(pStmt));
|
|
Tcl_DStringAppendElement(&str, zTm);
|
|
- Tcl_Eval(pDb->interp, Tcl_DStringValue(&str));
|
|
+ Tcl_EvalEx(pDb->interp, Tcl_DStringValue(&str), -1, 0);
|
|
Tcl_DStringFree(&str);
|
|
Tcl_ResetResult(pDb->interp);
|
|
+ return SQLITE_OK;
|
|
}
|
|
#endif
|
|
|
|
@@ -743,7 +788,7 @@ static int DbCommitHandler(void *cd){
|
|
SqliteDb *pDb = (SqliteDb*)cd;
|
|
int rc;
|
|
|
|
- rc = Tcl_Eval(pDb->interp, pDb->zCommit);
|
|
+ rc = Tcl_EvalEx(pDb->interp, pDb->zCommit, -1, 0);
|
|
if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
|
|
return 1;
|
|
}
|
|
@@ -751,10 +796,11 @@ static int DbCommitHandler(void *cd){
|
|
}
|
|
|
|
static void DbRollbackHandler(void *clientData){
|
|
+ int rc;
|
|
SqliteDb *pDb = (SqliteDb*)clientData;
|
|
assert(pDb->pRollbackHook);
|
|
- if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
|
|
- Tcl_BackgroundError(pDb->interp);
|
|
+ if( TCL_OK!=(rc=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0)) ){
|
|
+ Tcl_BackgroundException(pDb->interp, rc);
|
|
}
|
|
}
|
|
|
|
@@ -767,7 +813,7 @@ static int DbWalHandler(
|
|
const char *zDb,
|
|
int nEntry
|
|
){
|
|
- int ret = SQLITE_OK;
|
|
+ int ret = SQLITE_OK, rc;
|
|
Tcl_Obj *p;
|
|
SqliteDb *pDb = (SqliteDb*)clientData;
|
|
Tcl_Interp *interp = pDb->interp;
|
|
@@ -777,11 +823,11 @@ static int DbWalHandler(
|
|
p = Tcl_DuplicateObj(pDb->pWalHook);
|
|
Tcl_IncrRefCount(p);
|
|
Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
|
|
- Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
|
|
- if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0)
|
|
- || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
|
|
+ Tcl_ListObjAppendElement(interp, p, Tcl_NewWideIntObj(nEntry));
|
|
+ if( TCL_OK!=(rc=Tcl_EvalObjEx(interp, p, 0))
|
|
+ || TCL_OK!=(rc=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret))
|
|
){
|
|
- Tcl_BackgroundError(interp);
|
|
+ Tcl_BackgroundException(interp, rc);
|
|
}
|
|
Tcl_DecrRefCount(p);
|
|
|
|
@@ -792,9 +838,9 @@ static int DbWalHandler(
|
|
static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){
|
|
char zBuf[64];
|
|
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iArg);
|
|
- Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar2(interp, "sqlite_unlock_notify_arg", NULL, zBuf, TCL_GLOBAL_ONLY);
|
|
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nArg);
|
|
- Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar2(interp, "sqlite_unlock_notify_argcount", NULL, zBuf, TCL_GLOBAL_ONLY);
|
|
}
|
|
#else
|
|
# define setTestUnlockNotifyVars(x,y,z)
|
|
@@ -830,7 +876,7 @@ static void DbPreUpdateHandler(
|
|
){
|
|
SqliteDb *pDb = (SqliteDb *)p;
|
|
Tcl_Obj *pCmd;
|
|
- static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"};
|
|
+ static const char azStr[][8] = {"DELETE", "INSERT", "UPDATE"};
|
|
|
|
assert( (SQLITE_DELETE-1)/9 == 0 );
|
|
assert( (SQLITE_INSERT-1)/9 == 1 );
|
|
@@ -860,7 +906,7 @@ static void DbUpdateHandler(
|
|
){
|
|
SqliteDb *pDb = (SqliteDb *)p;
|
|
Tcl_Obj *pCmd;
|
|
- static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"};
|
|
+ static const char azStr[][8] = {"DELETE", "INSERT", "UPDATE"};
|
|
|
|
assert( (SQLITE_DELETE-1)/9 == 0 );
|
|
assert( (SQLITE_INSERT-1)/9 == 1 );
|
|
@@ -966,11 +1012,7 @@ static void tclSqlFunc(sqlite3_context *
|
|
}
|
|
case SQLITE_INTEGER: {
|
|
sqlite_int64 v = sqlite3_value_int64(pIn);
|
|
- if( v>=-2147483647 && v<=2147483647 ){
|
|
- pVal = Tcl_NewIntObj((int)v);
|
|
- }else{
|
|
- pVal = Tcl_NewWideIntObj(v);
|
|
- }
|
|
+ pVal = Tcl_NewWideIntObj(v);
|
|
break;
|
|
}
|
|
case SQLITE_FLOAT: {
|
|
@@ -1013,6 +1055,7 @@ static void tclSqlFunc(sqlite3_context *
|
|
u8 *data;
|
|
const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
|
|
char c = zType[0];
|
|
+
|
|
int eType = p->eType;
|
|
|
|
if( eType==SQLITE_NULL ){
|
|
@@ -1020,7 +1063,8 @@ static void tclSqlFunc(sqlite3_context *
|
|
/* Only return a BLOB type if the Tcl variable is a bytearray and
|
|
** has no string representation. */
|
|
eType = SQLITE_BLOB;
|
|
- }else if( (c=='b' && strcmp(zType,"boolean")==0)
|
|
+ }else if( (c=='b' && ((strcmp(zType,"boolean")==0)
|
|
+ || (strcmp(zType,"booleanString")==0)))
|
|
|| (c=='w' && strcmp(zType,"wideInt")==0)
|
|
|| (c=='i' && strcmp(zType,"int")==0)
|
|
){
|
|
@@ -1143,7 +1187,7 @@ static int auth_callback(
|
|
#ifdef SQLITE_USER_AUTHENTICATION
|
|
Tcl_DStringAppendElement(&str, zArg5 ? zArg5 : "");
|
|
#endif
|
|
- rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str));
|
|
+ rc = Tcl_EvalEx(pDb->interp, Tcl_DStringValue(&str), -1, TCL_EVAL_GLOBAL);
|
|
Tcl_DStringFree(&str);
|
|
zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY";
|
|
if( strcmp(zReply,"SQLITE_OK")==0 ){
|
|
@@ -1159,6 +1203,7 @@ static int auth_callback(
|
|
}
|
|
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
|
|
|
+#if 0
|
|
/*
|
|
** This routine reads a line of text from FILE in, stores
|
|
** the text in memory obtained from malloc() and returns a pointer
|
|
@@ -1203,6 +1248,7 @@ static char *local_getline(char *zPrompt
|
|
zLine = realloc( zLine, n+1 );
|
|
return zLine;
|
|
}
|
|
+#endif
|
|
|
|
|
|
/*
|
|
@@ -1432,8 +1478,9 @@ static int dbPrepareAndBind(
|
|
sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC);
|
|
Tcl_IncrRefCount(pVar);
|
|
pPreStmt->apParm[iParm++] = pVar;
|
|
- }else if( c=='b' && strcmp(zType,"boolean")==0 ){
|
|
- Tcl_GetIntFromObj(interp, pVar, &n);
|
|
+ }else if( c=='b' && (strcmp(zType,"boolean")==0 ||
|
|
+ strcmp(zType,"booleanString")==0) ){
|
|
+ Tcl_GetBooleanFromObj(interp, pVar, &n);
|
|
sqlite3_bind_int(pStmt, i, n);
|
|
}else if( c=='d' && strcmp(zType,"double")==0 ){
|
|
double r;
|
|
@@ -1445,8 +1492,8 @@ static int dbPrepareAndBind(
|
|
Tcl_GetWideIntFromObj(interp, pVar, &v);
|
|
sqlite3_bind_int64(pStmt, i, v);
|
|
}else{
|
|
- data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
|
|
- sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC);
|
|
+ data = (unsigned char *)Tcl_GetString(pVar);
|
|
+ sqlite3_bind_text(pStmt, i, (char *)data, pVar->length, SQLITE_STATIC);
|
|
Tcl_IncrRefCount(pVar);
|
|
pPreStmt->apParm[iParm++] = pVar;
|
|
}
|
|
@@ -1742,11 +1789,7 @@ static Tcl_Obj *dbEvalColumnValue(DbEval
|
|
}
|
|
case SQLITE_INTEGER: {
|
|
sqlite_int64 v = sqlite3_column_int64(pStmt, iCol);
|
|
- if( v>=-2147483647 && v<=2147483647 ){
|
|
- return Tcl_NewIntObj((int)v);
|
|
- }else{
|
|
- return Tcl_NewWideIntObj(v);
|
|
- }
|
|
+ return Tcl_NewWideIntObj(v);
|
|
}
|
|
case SQLITE_FLOAT: {
|
|
return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol));
|
|
@@ -1768,7 +1811,7 @@ static Tcl_Obj *dbEvalColumnValue(DbEval
|
|
*/
|
|
#if TCL_MAJOR_VERSION>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6)
|
|
# define SQLITE_TCL_NRE 1
|
|
-static int DbUseNre(void){
|
|
+int DbUseNre(void){
|
|
int major, minor;
|
|
Tcl_GetVersion(&major, &minor, 0, 0);
|
|
return( (major==8 && minor>=6) || major>8 );
|
|
@@ -1918,7 +1961,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
SqliteDb *pDb = (SqliteDb*)cd;
|
|
int choice;
|
|
int rc = TCL_OK;
|
|
- static const char *DB_strs[] = {
|
|
+ static const char *const DB_strs[] = {
|
|
"authorizer", "backup", "bind_fallback",
|
|
"busy", "cache", "changes",
|
|
"close", "collate", "collation_needed",
|
|
@@ -1956,7 +1999,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
|
|
return TCL_ERROR;
|
|
}
|
|
- if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
|
|
+ if( Tcl_GetIndexFromObjStruct(interp, objv[1], DB_strs, sizeof(char *), "option", 0, &choice) ){
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
@@ -2163,7 +2206,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
Tcl_WrongNumArgs(interp, 1, objv, "cache option ?arg?");
|
|
return TCL_ERROR;
|
|
}
|
|
- subCmd = Tcl_GetStringFromObj( objv[2], 0 );
|
|
+ subCmd = Tcl_GetString( objv[2] );
|
|
if( *subCmd=='f' && strcmp(subCmd,"flush")==0 ){
|
|
if( objc!=3 ){
|
|
Tcl_WrongNumArgs(interp, 2, objv, "flush");
|
|
@@ -2178,7 +2221,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
}else{
|
|
if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){
|
|
Tcl_AppendResult( interp, "cannot convert \"",
|
|
- Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0);
|
|
+ Tcl_GetString(objv[3]), "\" to integer", (char*)0);
|
|
return TCL_ERROR;
|
|
}else{
|
|
if( n<0 ){
|
|
@@ -2192,7 +2235,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
}
|
|
}else{
|
|
Tcl_AppendResult( interp, "bad option \"",
|
|
- Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size",
|
|
+ Tcl_GetString(objv[2]), "\": must be flush or size",
|
|
(char*)0);
|
|
return TCL_ERROR;
|
|
}
|
|
@@ -2212,7 +2255,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
return TCL_ERROR;
|
|
}
|
|
pResult = Tcl_GetObjResult(interp);
|
|
- Tcl_SetIntObj(pResult, sqlite3_changes(pDb->db));
|
|
+ Tcl_SetWideIntObj(pResult, sqlite3_changes(pDb->db));
|
|
break;
|
|
}
|
|
|
|
@@ -2221,7 +2264,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
** Shutdown the database
|
|
*/
|
|
case DB_CLOSE: {
|
|
- Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0));
|
|
+ Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
|
break;
|
|
}
|
|
|
|
@@ -2240,7 +2283,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
|
|
return TCL_ERROR;
|
|
}
|
|
- zName = Tcl_GetStringFromObj(objv[2], 0);
|
|
+ zName = Tcl_GetString(objv[2]);
|
|
zScript = Tcl_GetStringFromObj(objv[3], &nScript);
|
|
pCollate = (SqlCollate*)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 );
|
|
if( pCollate==0 ) return TCL_ERROR;
|
|
@@ -2249,9 +2292,9 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
pCollate->zScript = (char*)&pCollate[1];
|
|
pDb->pCollate = pCollate;
|
|
memcpy(pCollate->zScript, zScript, nScript+1);
|
|
- if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8,
|
|
- pCollate, tclSqlCollate) ){
|
|
- Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
|
|
+ if( sqlite3_create_collation_v2(pDb->db, zName, SQLITE_UTF8,
|
|
+ pCollate, tclSqlCollate, 0) ){
|
|
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
|
return TCL_ERROR;
|
|
}
|
|
break;
|
|
@@ -2329,9 +2372,9 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
Tcl_WrongNumArgs(interp, 2, objv, "SQL");
|
|
return TCL_ERROR;
|
|
}
|
|
- isComplete = sqlite3_complete( Tcl_GetStringFromObj(objv[2], 0) );
|
|
+ isComplete = sqlite3_complete( Tcl_GetString(objv[2]) );
|
|
pResult = Tcl_GetObjResult(interp);
|
|
- Tcl_SetBooleanObj(pResult, isComplete);
|
|
+ Tcl_SetWideIntObj(pResult, isComplete != 0);
|
|
#endif
|
|
break;
|
|
}
|
|
@@ -2367,9 +2410,10 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
char *zLine; /* A single line of input from the file */
|
|
char **azCol; /* zLine[] broken up into columns */
|
|
const char *zCommit; /* How to commit changes */
|
|
- FILE *in; /* The input file */
|
|
+ Tcl_Channel in; /* The input file */
|
|
int lineno = 0; /* Line number of input file */
|
|
char zLineNum[80]; /* Line number print buffer */
|
|
+ Tcl_DString str;
|
|
Tcl_Obj *pResult; /* interp result */
|
|
|
|
const char *zSep;
|
|
@@ -2380,18 +2424,18 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
return TCL_ERROR;
|
|
}
|
|
if( objc>=6 ){
|
|
- zSep = Tcl_GetStringFromObj(objv[5], 0);
|
|
+ zSep = Tcl_GetString(objv[5]);
|
|
}else{
|
|
zSep = "\t";
|
|
}
|
|
if( objc>=7 ){
|
|
- zNull = Tcl_GetStringFromObj(objv[6], 0);
|
|
+ zNull = Tcl_GetString(objv[6]);
|
|
}else{
|
|
zNull = "";
|
|
}
|
|
- zConflict = Tcl_GetStringFromObj(objv[2], 0);
|
|
- zTable = Tcl_GetStringFromObj(objv[3], 0);
|
|
- zFile = Tcl_GetStringFromObj(objv[4], 0);
|
|
+ zConflict = Tcl_GetString(objv[2]);
|
|
+ zTable = Tcl_GetString(objv[3]);
|
|
+ zFile = Tcl_GetString(objv[4]);
|
|
nSep = strlen30(zSep);
|
|
nNull = strlen30(zNull);
|
|
if( nSep==0 ){
|
|
@@ -2427,7 +2471,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
if( nCol==0 ) {
|
|
return TCL_ERROR;
|
|
}
|
|
- zSql = malloc( nByte + 50 + nCol*2 );
|
|
+ zSql = sqlite3_malloc( nByte + 50 + nCol*2 );
|
|
if( zSql==0 ) {
|
|
Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0);
|
|
return TCL_ERROR;
|
|
@@ -2442,29 +2486,30 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
zSql[j++] = ')';
|
|
zSql[j] = 0;
|
|
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
|
|
- free(zSql);
|
|
+ sqlite3_free(zSql);
|
|
if( rc ){
|
|
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0);
|
|
sqlite3_finalize(pStmt);
|
|
return TCL_ERROR;
|
|
}
|
|
- in = fopen(zFile, "rb");
|
|
+ in = Tcl_OpenFileChannel(interp, zFile, "r", 0666);
|
|
if( in==0 ){
|
|
- Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, (char*)0);
|
|
sqlite3_finalize(pStmt);
|
|
return TCL_ERROR;
|
|
}
|
|
- azCol = malloc( sizeof(azCol[0])*(nCol+1) );
|
|
+ azCol = sqlite3_malloc( sizeof(azCol[0])*(nCol+1) );
|
|
if( azCol==0 ) {
|
|
Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0);
|
|
- fclose(in);
|
|
+ Tcl_Close(interp, in);
|
|
return TCL_ERROR;
|
|
}
|
|
+ Tcl_DStringInit(&str);
|
|
(void)sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0);
|
|
zCommit = "COMMIT";
|
|
- while( (zLine = local_getline(0, in))!=0 ){
|
|
+ while( (size_t)Tcl_Gets(in, &str)!=(size_t)-1 ) {
|
|
char *z;
|
|
lineno++;
|
|
+ zLine = Tcl_DStringValue(&str);
|
|
azCol[0] = zLine;
|
|
for(i=0, z=zLine; *z; z++){
|
|
if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){
|
|
@@ -2479,45 +2524,45 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
if( i+1!=nCol ){
|
|
char *zErr;
|
|
int nErr = strlen30(zFile) + 200;
|
|
- zErr = malloc(nErr);
|
|
+ zErr = sqlite3_malloc(nErr);
|
|
if( zErr ){
|
|
sqlite3_snprintf(nErr, zErr,
|
|
"Error: %s line %d: expected %d columns of data but found %d",
|
|
zFile, lineno, nCol, i+1);
|
|
Tcl_AppendResult(interp, zErr, (char*)0);
|
|
- free(zErr);
|
|
+ sqlite3_free(zErr);
|
|
}
|
|
zCommit = "ROLLBACK";
|
|
break;
|
|
}
|
|
for(i=0; i<nCol; i++){
|
|
/* check for null data, if so, bind as null */
|
|
- if( (nNull>0 && strcmp(azCol[i], zNull)==0)
|
|
- || strlen30(azCol[i])==0
|
|
+ if( (azCol[i][0]==0) || (nNull>0 && strcmp(azCol[i], zNull)==0)
|
|
){
|
|
sqlite3_bind_null(pStmt, i+1);
|
|
}else{
|
|
- sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
|
|
+ sqlite3_bind_text(pStmt, i+1, azCol[i], strlen30(azCol[i]), SQLITE_STATIC);
|
|
}
|
|
}
|
|
sqlite3_step(pStmt);
|
|
rc = sqlite3_reset(pStmt);
|
|
- free(zLine);
|
|
+ Tcl_DStringSetLength(&str, 0);
|
|
if( rc!=SQLITE_OK ){
|
|
Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), (char*)0);
|
|
zCommit = "ROLLBACK";
|
|
break;
|
|
}
|
|
}
|
|
- free(azCol);
|
|
- fclose(in);
|
|
+ Tcl_DStringFree(&str);
|
|
+ sqlite3_free(azCol);
|
|
+ Tcl_Close(interp, in);
|
|
sqlite3_finalize(pStmt);
|
|
(void)sqlite3_exec(pDb->db, zCommit, 0, 0, 0);
|
|
|
|
if( zCommit[0] == 'C' ){
|
|
/* success, set result as number of lines processed */
|
|
pResult = Tcl_GetObjResult(interp);
|
|
- Tcl_SetIntObj(pResult, lineno);
|
|
+ Tcl_SetWideIntObj(pResult, lineno);
|
|
rc = TCL_OK;
|
|
}else{
|
|
/* failure, append lineno where failed */
|
|
@@ -2577,7 +2622,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
|
}
|
|
pValue = objv[objc-1];
|
|
pBA = Tcl_GetByteArrayFromObj(pValue, &len);
|
|
- pData = sqlite3_malloc64( len );
|
|
+ pData = sqlite3_malloc( len );
|
|
if( pData==0 && len>0 ){
|
|
Tcl_AppendResult(interp, "out of memory", (char*)0);
|
|
rc = TCL_ERROR;
|
|
@@ -2635,7 +2680,7 @@ deserialize_error:
|
|
** call to sqlite3_exec().
|
|
*/
|
|
case DB_ERRORCODE: {
|
|
- Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db)));
|
|
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_errcode(pDb->db)));
|
|
break;
|
|
}
|
|
|
|
@@ -2664,7 +2709,7 @@ deserialize_error:
|
|
Tcl_ResetResult(interp);
|
|
}
|
|
}else if( rc==TCL_BREAK || rc==TCL_OK ){
|
|
- pResult = Tcl_NewBooleanObj(rc==TCL_OK);
|
|
+ pResult = Tcl_NewWideIntObj(rc==TCL_OK);
|
|
}
|
|
dbEvalFinalize(&sEval);
|
|
if( pResult ) Tcl_SetObjResult(interp, pResult);
|
|
@@ -2783,7 +2828,7 @@ deserialize_error:
|
|
flags |= SQLITE_DETERMINISTIC;
|
|
}else
|
|
if( n>1 && strncmp(z, "-returntype", n)==0 ){
|
|
- const char *azType[] = {"integer", "real", "text", "blob", "any", 0};
|
|
+ const char *const azType[] = {"integer", "real", "text", "blob", "any", 0};
|
|
assert( SQLITE_INTEGER==1 && SQLITE_FLOAT==2 && SQLITE_TEXT==3 );
|
|
assert( SQLITE_BLOB==4 && SQLITE_NULL==5 );
|
|
if( i==(objc-2) ){
|
|
@@ -2791,7 +2836,7 @@ deserialize_error:
|
|
return TCL_ERROR;
|
|
}
|
|
i++;
|
|
- if( Tcl_GetIndexFromObj(interp, objv[i], azType, "type", 0, &eType) ){
|
|
+ if( Tcl_GetIndexFromObjStruct(interp, objv[i], azType, sizeof(char*), "type", 0, &eType) ){
|
|
return TCL_ERROR;
|
|
}
|
|
eType++;
|
|
@@ -2804,7 +2849,7 @@ deserialize_error:
|
|
}
|
|
|
|
pScript = objv[objc-1];
|
|
- zName = Tcl_GetStringFromObj(objv[2], 0);
|
|
+ zName = Tcl_GetString(objv[2]);
|
|
pFunc = findSqlFunc(pDb, zName);
|
|
if( pFunc==0 ) return TCL_ERROR;
|
|
if( pFunc->pScript ){
|
|
@@ -2814,11 +2859,11 @@ deserialize_error:
|
|
Tcl_IncrRefCount(pScript);
|
|
pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript);
|
|
pFunc->eType = eType;
|
|
- rc = sqlite3_create_function(pDb->db, zName, nArg, flags,
|
|
- pFunc, tclSqlFunc, 0, 0);
|
|
+ rc = sqlite3_create_function_v2(pDb->db, zName, nArg, flags,
|
|
+ pFunc, tclSqlFunc, 0, 0, 0);
|
|
if( rc!=SQLITE_OK ){
|
|
rc = TCL_ERROR;
|
|
- Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
|
|
+ Tcl_AppendResult(interp, (char *)sqlite3_errmsg(pDb->db), (char*)0);
|
|
}
|
|
break;
|
|
}
|
|
@@ -2996,13 +3041,12 @@ deserialize_error:
|
|
}else{
|
|
pDb->zProfile = 0;
|
|
}
|
|
-#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \
|
|
- !defined(SQLITE_OMIT_DEPRECATED)
|
|
+#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
|
if( pDb->zProfile ){
|
|
pDb->interp = interp;
|
|
- sqlite3_profile(pDb->db, DbProfileHandler, pDb);
|
|
+ sqlite3_trace_v2(pDb->db, SQLITE_TRACE_PROFILE, DbProfileHandler, pDb);
|
|
}else{
|
|
- sqlite3_profile(pDb->db, 0, 0);
|
|
+ sqlite3_trace_v2(pDb->db, 0, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
@@ -3155,7 +3199,7 @@ deserialize_error:
|
|
(char*)0);
|
|
return TCL_ERROR;
|
|
}
|
|
- Tcl_SetObjResult(interp, Tcl_NewIntObj(v));
|
|
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(v));
|
|
break;
|
|
}
|
|
|
|
@@ -3188,7 +3232,7 @@ deserialize_error:
|
|
return TCL_ERROR;
|
|
}
|
|
pResult = Tcl_GetObjResult(interp);
|
|
- Tcl_SetIntObj(pResult, sqlite3_total_changes(pDb->db));
|
|
+ Tcl_SetWideIntObj(pResult, sqlite3_total_changes(pDb->db));
|
|
break;
|
|
}
|
|
|
|
@@ -3219,13 +3263,12 @@ deserialize_error:
|
|
}else{
|
|
pDb->zTrace = 0;
|
|
}
|
|
-#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \
|
|
- !defined(SQLITE_OMIT_DEPRECATED)
|
|
+#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
|
if( pDb->zTrace ){
|
|
pDb->interp = interp;
|
|
- sqlite3_trace(pDb->db, DbTraceHandler, pDb);
|
|
+ sqlite3_trace_v2(pDb->db, SQLITE_TRACE_STMT, DbTraceHandler, pDb);
|
|
}else{
|
|
- sqlite3_trace(pDb->db, 0, 0);
|
|
+ sqlite3_trace_v2(pDb->db, 0, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
@@ -3251,7 +3294,7 @@ deserialize_error:
|
|
int len;
|
|
Tcl_WideInt wMask = 0;
|
|
if( objc==4 ){
|
|
- static const char *TTYPE_strs[] = {
|
|
+ static const char *const TTYPE_strs[] = {
|
|
"statement", "profile", "row", "close", 0
|
|
};
|
|
enum TTYPE_enum {
|
|
@@ -3267,7 +3310,7 @@ deserialize_error:
|
|
if( TCL_OK!=Tcl_ListObjIndex(interp, objv[3], i, &pObj) ){
|
|
return TCL_ERROR;
|
|
}
|
|
- if( Tcl_GetIndexFromObj(interp, pObj, TTYPE_strs, "trace type",
|
|
+ if( Tcl_GetIndexFromObjStruct(interp, pObj, TTYPE_strs, sizeof(char *), "trace type",
|
|
0, &ttype)!=TCL_OK ){
|
|
Tcl_WideInt wType;
|
|
Tcl_Obj *pError = Tcl_DuplicateObj(Tcl_GetObjResult(interp));
|
|
@@ -3334,14 +3377,14 @@ deserialize_error:
|
|
}
|
|
|
|
if( pDb->nTransaction==0 && objc==4 ){
|
|
- static const char *TTYPE_strs[] = {
|
|
+ static const char *const TTYPE_strs[] = {
|
|
"deferred", "exclusive", "immediate", 0
|
|
};
|
|
enum TTYPE_enum {
|
|
TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE
|
|
};
|
|
int ttype;
|
|
- if( Tcl_GetIndexFromObj(interp, objv[2], TTYPE_strs, "transaction type",
|
|
+ if( Tcl_GetIndexFromObjStruct(interp, objv[2], TTYPE_strs, sizeof(char *), "transaction type",
|
|
0, &ttype) ){
|
|
return TCL_ERROR;
|
|
}
|
|
@@ -3426,7 +3469,7 @@ deserialize_error:
|
|
(char*)0);
|
|
rc = TCL_ERROR;
|
|
#else
|
|
- static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0};
|
|
+ static const char *const azSub[] = {"count", "depth", "hook", "new", "old", 0};
|
|
enum DbPreupdateSubCmd {
|
|
PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD
|
|
};
|
|
@@ -3435,14 +3478,14 @@ deserialize_error:
|
|
if( objc<3 ){
|
|
Tcl_WrongNumArgs(interp, 2, objv, "SUB-COMMAND ?ARGS?");
|
|
}
|
|
- if( Tcl_GetIndexFromObj(interp, objv[2], azSub, "sub-command", 0, &iSub) ){
|
|
+ if( Tcl_GetIndexFromObjStruct(interp, objv[2], azSub, sizeof(char *), "sub-command", 0, &iSub) ){
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
switch( (enum DbPreupdateSubCmd)iSub ){
|
|
case PRE_COUNT: {
|
|
int nCol = sqlite3_preupdate_count(pDb->db);
|
|
- Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol));
|
|
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nCol));
|
|
break;
|
|
}
|
|
|
|
@@ -3461,7 +3504,7 @@ deserialize_error:
|
|
Tcl_WrongNumArgs(interp, 3, objv, "");
|
|
return TCL_ERROR;
|
|
}
|
|
- pRet = Tcl_NewIntObj(sqlite3_preupdate_depth(pDb->db));
|
|
+ pRet = Tcl_NewWideIntObj(sqlite3_preupdate_depth(pDb->db));
|
|
Tcl_SetObjResult(interp, pRet);
|
|
break;
|
|
}
|
|
@@ -3553,7 +3596,7 @@ deserialize_error:
|
|
char zBuf[100];
|
|
sqlite3_snprintf(sizeof(zBuf), zBuf, "%p",
|
|
pDb->stmtList ? pDb->stmtList->pStmt: 0);
|
|
- Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
|
+ Tcl_AppendResult(interp, zBuf, (char*)0);
|
|
}else
|
|
#endif /* SQLITE_TEST */
|
|
{
|
|
@@ -3562,7 +3605,7 @@ deserialize_error:
|
|
}
|
|
}
|
|
if( i==2 ){
|
|
- Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC);
|
|
+ Tcl_AppendResult(interp, sqlite3_libversion(), (char*)0);
|
|
}
|
|
break;
|
|
}
|
|
@@ -3654,7 +3697,7 @@ static int SQLITE_TCLAPI DbMain(
|
|
|
|
if( objc==1 ) return sqliteCmdUsage(interp, objv);
|
|
if( objc==2 ){
|
|
- zArg = Tcl_GetStringFromObj(objv[1], 0);
|
|
+ zArg = Tcl_GetString(objv[1]);
|
|
if( strcmp(zArg,"-version")==0 ){
|
|
Tcl_AppendResult(interp,sqlite3_libversion(), (char*)0);
|
|
return TCL_OK;
|
|
@@ -3759,7 +3802,7 @@ static int SQLITE_TCLAPI DbMain(
|
|
}
|
|
#endif
|
|
if( p->db==0 ){
|
|
- Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
|
|
+ Tcl_AppendResult(interp, zErrMsg, (char*)0);
|
|
Tcl_Free((char*)p);
|
|
sqlite3_free(zErrMsg);
|
|
return TCL_ERROR;
|
|
@@ -3767,7 +3810,7 @@ static int SQLITE_TCLAPI DbMain(
|
|
p->maxStmt = NUM_PREPARED_STMTS;
|
|
p->openFlags = flags & SQLITE_OPEN_URI;
|
|
p->interp = interp;
|
|
- zArg = Tcl_GetStringFromObj(objv[1], 0);
|
|
+ zArg = Tcl_GetString(objv[1]);
|
|
if( DbUseNre() ){
|
|
Tcl_NRCreateCommand(interp, zArg, DbObjCmdAdaptor, DbObjCmd,
|
|
(char*)p, DbDeleteCmd);
|
|
@@ -3781,9 +3824,26 @@ static int SQLITE_TCLAPI DbMain(
|
|
** Provide a dummy Tcl_InitStubs if we are using this as a static
|
|
** library.
|
|
*/
|
|
+#undef Tcl_InitStubs
|
|
#ifndef USE_TCL_STUBS
|
|
-# undef Tcl_InitStubs
|
|
# define Tcl_InitStubs(a,b,c) TCL_VERSION
|
|
+#else
|
|
+# define Tcl_InitStubs staticTclInitStubs
|
|
+typedef struct {
|
|
+ char *result;
|
|
+ Tcl_FreeProc *freeProc;
|
|
+ int errorLine;
|
|
+ const struct TclStubs *stubTable;
|
|
+} PrivateTclInterp;
|
|
+static const char *Tcl_InitStubs(Tcl_Interp *interp, const char *version, int exact) {
|
|
+ tclStubsPtr = ((PrivateTclInterp *)interp)->stubTable;
|
|
+ if (tclStubsPtr->magic != TCL_STUB_MAGIC) {
|
|
+ ((PrivateTclInterp *)interp)->result = (char *)"interpreter uses an incompatible stubs mechanism";
|
|
+ ((PrivateTclInterp *)interp)->freeProc = 0; /* TCL_STATIC */
|
|
+ return NULL;
|
|
+ }
|
|
+ return Tcl_PkgRequireEx(interp, "Tcl", version, 0, (void *)&tclStubsPtr);
|
|
+}
|
|
#endif
|
|
|
|
/*
|
|
@@ -3804,10 +3864,13 @@ static int SQLITE_TCLAPI DbMain(
|
|
** used to open a new SQLite database. See the DbMain() routine above
|
|
** for additional information.
|
|
**
|
|
-** The EXTERN macros are required by TCL in order to work on windows.
|
|
+** The DLLEXPORT macros are required by TCL in order to work on windows.
|
|
*/
|
|
-EXTERN int Sqlite3_Init(Tcl_Interp *interp){
|
|
- int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR;
|
|
+DLLEXPORT int Sqlite3_Init(Tcl_Interp *interp){
|
|
+ int rc = Tcl_InitStubs(interp, "8.5-", 0) ? TCL_OK : TCL_ERROR;
|
|
+ if( rc!=TCL_OK ){
|
|
+ rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR;
|
|
+ }
|
|
if( rc==TCL_OK ){
|
|
Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
|
|
#ifndef SQLITE_3_SUFFIX_ONLY
|
|
@@ -3816,20 +3879,20 @@ EXTERN int Sqlite3_Init(Tcl_Interp *inte
|
|
** command. */
|
|
Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
|
|
#endif
|
|
- rc = Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
|
|
+ rc = Tcl_PkgProvideEx(interp, "sqlite3", PACKAGE_VERSION, NULL);
|
|
}
|
|
return rc;
|
|
}
|
|
-EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
|
-EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
|
-EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
|
+DLLEXPORT int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
|
+DLLEXPORT int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
|
+DLLEXPORT int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
|
|
|
/* Because it accesses the file-system and uses persistent state, SQLite
|
|
** is not considered appropriate for safe interpreters. Hence, we cause
|
|
** the _SafeInit() interfaces return TCL_ERROR.
|
|
*/
|
|
-EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; }
|
|
-EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;}
|
|
+DLLEXPORT int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; }
|
|
+DLLEXPORT int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;}
|
|
|
|
|
|
|
|
@@ -3914,16 +3977,16 @@ int SQLITE_CDECL TCLSH_MAIN(int argc, ch
|
|
sqlite3_shutdown();
|
|
|
|
Tcl_FindExecutable(argv[0]);
|
|
- Tcl_SetSystemEncoding(NULL, "utf-8");
|
|
+ Tcl_SetSystemEncoding(0, "utf-8");
|
|
interp = Tcl_CreateInterp();
|
|
Sqlite3_Init(interp);
|
|
|
|
sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-1);
|
|
- Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY);
|
|
- Tcl_SetVar(interp,"argv0",argv[0],TCL_GLOBAL_ONLY);
|
|
- Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar2(interp,"argc", NULL, zArgc, TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar2(interp,"argv0", NULL, argv[0],TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar2(interp,"argv", NULL, "", TCL_GLOBAL_ONLY);
|
|
for(i=1; i<argc; i++){
|
|
- Tcl_SetVar(interp, "argv", argv[i],
|
|
+ Tcl_SetVar2(interp, "argv", NULL, argv[i],
|
|
TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
|
|
}
|
|
#if defined(TCLSH_INIT_PROC)
|
|
@@ -3932,8 +3995,8 @@ int SQLITE_CDECL TCLSH_MAIN(int argc, ch
|
|
if( zScript==0 ){
|
|
zScript = tclsh_main_loop();
|
|
}
|
|
- if( Tcl_GlobalEval(interp, zScript)!=TCL_OK ){
|
|
- const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
|
|
+ if( Tcl_EvalEx(interp, zScript, -1, TCL_EVAL_GLOBAL)!=TCL_OK ){
|
|
+ const char *zInfo = Tcl_GetVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
|
|
if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp);
|
|
fprintf(stderr,"%s: %s\n", *argv, zInfo);
|
|
return 1;
|
|
--- origsrc/sqlite-autoconf-3280000/tea/tclconfig/tcl.m4 2019-04-16 22:06:14.000000000 +0200
|
|
+++ src/sqlite-autoconf-3280000/tea/tclconfig/tcl.m4 2019-03-05 13:14:41.089082500 +0100
|
|
@@ -11,14 +11,11 @@
|
|
|
|
AC_PREREQ(2.57)
|
|
|
|
-dnl TEA extensions pass us the version of TEA they think they
|
|
-dnl are compatible with (must be set in TEA_INIT below)
|
|
-dnl TEA_VERSION="3.9"
|
|
-
|
|
# Possible values for key variables defined:
|
|
#
|
|
# TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem')
|
|
# TEA_PLATFORM - windows unix
|
|
+# TEA_TK_EXTENSION - True if this is a Tk extension
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
@@ -108,6 +105,9 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
|
|
`ls -d /Library/Frameworks 2>/dev/null` \
|
|
`ls -d /Network/Library/Frameworks 2>/dev/null` \
|
|
`ls -d /System/Library/Frameworks 2>/dev/null` \
|
|
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks/Tcl.framework 2>/dev/null` \
|
|
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Network/Library/Frameworks/Tcl.framework 2>/dev/null` \
|
|
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tcl.framework 2>/dev/null` \
|
|
; do
|
|
if test -f "$i/Tcl.framework/tclConfig.sh" ; then
|
|
ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`"
|
|
@@ -136,10 +136,15 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
|
|
`ls -d ${prefix}/lib 2>/dev/null` \
|
|
`ls -d /usr/local/lib 2>/dev/null` \
|
|
`ls -d /usr/contrib/lib 2>/dev/null` \
|
|
+ `ls -d /usr/pkg/lib 2>/dev/null` \
|
|
`ls -d /usr/lib 2>/dev/null` \
|
|
`ls -d /usr/lib64 2>/dev/null` \
|
|
`ls -d /usr/lib/tcl8.6 2>/dev/null` \
|
|
`ls -d /usr/lib/tcl8.5 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tcl8.6 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tcl8.5 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tcl/tcl8.6 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tcl/tcl8.5 2>/dev/null` \
|
|
; do
|
|
if test -f "$i/tclConfig.sh" ; then
|
|
ac_cv_c_tclconfig="`(cd $i; pwd)`"
|
|
@@ -263,6 +268,9 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
|
|
`ls -d /Library/Frameworks 2>/dev/null` \
|
|
`ls -d /Network/Library/Frameworks 2>/dev/null` \
|
|
`ls -d /System/Library/Frameworks 2>/dev/null` \
|
|
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks/Tcl.framework 2>/dev/null` \
|
|
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Network/Library/Frameworks/Tcl.framework 2>/dev/null` \
|
|
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tcl.framework 2>/dev/null` \
|
|
; do
|
|
if test -f "$i/Tk.framework/tkConfig.sh" ; then
|
|
ac_cv_c_tkconfig="`(cd $i/Tk.framework; pwd)`"
|
|
@@ -278,8 +286,15 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
|
|
`ls -d ${prefix}/lib 2>/dev/null` \
|
|
`ls -d /usr/local/lib 2>/dev/null` \
|
|
`ls -d /usr/contrib/lib 2>/dev/null` \
|
|
+ `ls -d /usr/pkg/lib 2>/dev/null` \
|
|
`ls -d /usr/lib 2>/dev/null` \
|
|
`ls -d /usr/lib64 2>/dev/null` \
|
|
+ `ls -d /usr/lib/tk8.6 2>/dev/null` \
|
|
+ `ls -d /usr/lib/tk8.5 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tk8.6 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tk8.5 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tcl/tk8.6 2>/dev/null` \
|
|
+ `ls -d /usr/local/lib/tcl/tk8.5 2>/dev/null` \
|
|
; do
|
|
if test -f "$i/tkConfig.sh" ; then
|
|
ac_cv_c_tkconfig="`(cd $i; pwd)`"
|
|
@@ -348,6 +363,8 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
|
|
# TCL_BIN_DIR
|
|
# TCL_SRC_DIR
|
|
# TCL_LIB_FILE
|
|
+# TCL_ZIP_FILE
|
|
+# TCL_ZIPFS_SUPPORT
|
|
#------------------------------------------------------------------------
|
|
|
|
AC_DEFUN([TEA_LOAD_TCLCONFIG], [
|
|
@@ -422,8 +439,13 @@ AC_DEFUN([TEA_LOAD_TCLCONFIG], [
|
|
#ifdef _WIN32
|
|
#error win32
|
|
#endif
|
|
- ], TEA_PLATFORM="unix",
|
|
+ ], [
|
|
+ # first test we've already retrieved platform (cross-compile), fallback to unix otherwise:
|
|
+ TEA_PLATFORM="${TEA_PLATFORM-unix}"
|
|
+ CYGPATH=echo
|
|
+ ], [
|
|
TEA_PLATFORM="windows"
|
|
+ AC_CHECK_PROG(CYGPATH, cygpath, cygpath -m, echo) ]
|
|
)
|
|
CC=$hold_cc
|
|
AC_MSG_RESULT($TEA_PLATFORM)
|
|
@@ -572,7 +594,15 @@ AC_DEFUN([TEA_PROG_TCLSH], [
|
|
if test -f "${TCL_BIN_DIR}/Makefile" ; then
|
|
# tclConfig.sh is in Tcl build directory
|
|
if test "${TEA_PLATFORM}" = "windows"; then
|
|
+ if test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" ; then
|
|
TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
|
|
+ elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}s${EXEEXT}" ; then
|
|
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}s${EXEEXT}"
|
|
+ elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}t${EXEEXT}" ; then
|
|
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}t${EXEEXT}"
|
|
+ elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}st${EXEEXT}" ; then
|
|
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}st${EXEEXT}"
|
|
+ fi
|
|
else
|
|
TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
|
|
fi
|
|
@@ -622,7 +652,15 @@ AC_DEFUN([TEA_PROG_WISH], [
|
|
if test -f "${TK_BIN_DIR}/Makefile" ; then
|
|
# tkConfig.sh is in Tk build directory
|
|
if test "${TEA_PLATFORM}" = "windows"; then
|
|
+ if test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}" ; then
|
|
WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
|
|
+ elif test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}s${EXEEXT}" ; then
|
|
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}$s{EXEEXT}"
|
|
+ elif test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}t${EXEEXT}" ; then
|
|
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}t${EXEEXT}"
|
|
+ elif test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}st${EXEEXT}" ; then
|
|
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}st${EXEEXT}"
|
|
+ fi
|
|
else
|
|
WISH_PROG="${TK_BIN_DIR}/wish"
|
|
fi
|
|
@@ -660,6 +698,7 @@ AC_DEFUN([TEA_PROG_WISH], [
|
|
#
|
|
# Adds the following arguments to configure:
|
|
# --enable-shared=yes|no
|
|
+# --enable-stubs=yes|no
|
|
#
|
|
# Defines the following vars:
|
|
# STATIC_BUILD Used for building import/export libraries
|
|
@@ -667,31 +706,63 @@ AC_DEFUN([TEA_PROG_WISH], [
|
|
#
|
|
# Sets the following vars:
|
|
# SHARED_BUILD Value of 1 or 0
|
|
+# STUBS_BUILD Value if 1 or 0
|
|
+# USE_TCL_STUBS Value true: if SHARED_BUILD or --enable-stubs
|
|
+# USE_TCLOO_STUBS Value true: if SHARED_BUILD or --enable-stubs
|
|
+# USE_TK_STUBS Value true: if SHARED_BUILD or --enable-stubs
|
|
+# AND TEA_WINDOWING_SYSTEM != ""
|
|
#------------------------------------------------------------------------
|
|
-
|
|
AC_DEFUN([TEA_ENABLE_SHARED], [
|
|
AC_MSG_CHECKING([how to build libraries])
|
|
AC_ARG_ENABLE(shared,
|
|
AC_HELP_STRING([--enable-shared],
|
|
[build and link with shared libraries (default: on)]),
|
|
- [tcl_ok=$enableval], [tcl_ok=yes])
|
|
+ [shared_ok=$enableval], [shared_ok=yes])
|
|
|
|
if test "${enable_shared+set}" = set; then
|
|
enableval="$enable_shared"
|
|
- tcl_ok=$enableval
|
|
+ shared_ok=$enableval
|
|
else
|
|
- tcl_ok=yes
|
|
+ shared_ok=yes
|
|
+ fi
|
|
+
|
|
+ AC_ARG_ENABLE(stubs,
|
|
+ AC_HELP_STRING([--enable-stubs],
|
|
+ [build and link with stub libraries. Always true for shared builds (default: on)]),
|
|
+ [stubs_ok=$enableval], [stubs_ok=yes])
|
|
+
|
|
+ if test "${enable_stubs+set}" = set; then
|
|
+ enableval="$enable_stubs"
|
|
+ stubs_ok=$enableval
|
|
+ else
|
|
+ stubs_ok=yes
|
|
fi
|
|
|
|
- if test "$tcl_ok" = "yes" ; then
|
|
+ # Stubs are always enabled for shared builds
|
|
+ if test "$shared_ok" = "yes" ; then
|
|
AC_MSG_RESULT([shared])
|
|
SHARED_BUILD=1
|
|
+ STUBS_BUILD=1
|
|
else
|
|
AC_MSG_RESULT([static])
|
|
SHARED_BUILD=0
|
|
- AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?])
|
|
+ AC_DEFINE(STATIC_BUILD, 1, [This a static build])
|
|
+ if test "$stubs_ok" = "yes" ; then
|
|
+ STUBS_BUILD=1
|
|
+ else
|
|
+ STUBS_BUILD=0
|
|
+ fi
|
|
fi
|
|
+ if test "${STUBS_BUILD}" = "1" ; then
|
|
+ AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs])
|
|
+ AC_DEFINE(USE_TCLOO_STUBS, 1, [Use TclOO stubs])
|
|
+ if test "${TEA_WINDOWINGSYSTEM}" != ""; then
|
|
+ AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs])
|
|
+ fi
|
|
+ fi
|
|
+
|
|
AC_SUBST(SHARED_BUILD)
|
|
+ AC_SUBST(STUBS_BUILD)
|
|
])
|
|
|
|
#------------------------------------------------------------------------
|
|
@@ -729,7 +800,7 @@ AC_DEFUN([TEA_ENABLE_SHARED], [
|
|
AC_DEFUN([TEA_ENABLE_THREADS], [
|
|
AC_ARG_ENABLE(threads,
|
|
AC_HELP_STRING([--enable-threads],
|
|
- [build with threads]),
|
|
+ [build with threads (default: on)]),
|
|
[tcl_ok=$enableval], [tcl_ok=yes])
|
|
|
|
if test "${enable_threads+set}" = set; then
|
|
@@ -813,14 +884,6 @@ AC_DEFUN([TEA_ENABLE_THREADS], [
|
|
that IS thread-enabled. It is recommended to use --enable-threads.])
|
|
fi
|
|
;;
|
|
- *)
|
|
- if test "${TCL_THREADS}" = "1"; then
|
|
- AC_MSG_WARN([
|
|
- --enable-threads requested, but building against a Tcl that is NOT
|
|
- thread-enabled. This is an OK configuration that will also run in
|
|
- a thread-enabled core.])
|
|
- fi
|
|
- ;;
|
|
esac
|
|
AC_SUBST(TCL_THREADS)
|
|
])
|
|
@@ -1086,17 +1149,6 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
[doRpath=$enableval], [doRpath=yes])
|
|
AC_MSG_RESULT([$doRpath])
|
|
|
|
- # TEA specific: Cross-compiling options for Windows/CE builds?
|
|
-
|
|
- AS_IF([test "${TEA_PLATFORM}" = windows], [
|
|
- AC_MSG_CHECKING([if Windows/CE build is requested])
|
|
- AC_ARG_ENABLE(wince,
|
|
- AC_HELP_STRING([--enable-wince],
|
|
- [enable Win/CE support (where applicable)]),
|
|
- [doWince=$enableval], [doWince=no])
|
|
- AC_MSG_RESULT([$doWince])
|
|
- ])
|
|
-
|
|
# Set the variable "system" to hold the name and version number
|
|
# for the system.
|
|
|
|
@@ -1133,7 +1185,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
AC_CHECK_TOOL(AR, ar)
|
|
STLIB_LD='${AR} cr'
|
|
LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
|
|
- AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"])
|
|
+ AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION=""],[SHLIB_VERSION=".$SHLIB_VERSION"])
|
|
case $system in
|
|
# TEA specific:
|
|
windows)
|
|
@@ -1169,113 +1221,36 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
fi
|
|
fi
|
|
|
|
- if test "$doWince" != "no" ; then
|
|
- if test "$do64bit" != "no" ; then
|
|
- AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible])
|
|
- fi
|
|
- if test "$GCC" = "yes" ; then
|
|
- AC_MSG_ERROR([Windows/CE and GCC builds incompatible])
|
|
- fi
|
|
- TEA_PATH_CELIB
|
|
- # Set defaults for common evc4/PPC2003 setup
|
|
- # Currently Tcl requires 300+, possibly 420+ for sockets
|
|
- CEVERSION=420; # could be 211 300 301 400 420 ...
|
|
- TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ...
|
|
- ARCH=ARM; # could be ARM MIPS X86EM ...
|
|
- PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002"
|
|
- if test "$doWince" != "yes"; then
|
|
- # If !yes then the user specified something
|
|
- # Reset ARCH to allow user to skip specifying it
|
|
- ARCH=
|
|
- eval `echo $doWince | awk -F, '{ \
|
|
- if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \
|
|
- if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \
|
|
- if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \
|
|
- if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \
|
|
- if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \
|
|
- }'`
|
|
- if test "x${ARCH}" = "x" ; then
|
|
- ARCH=$TARGETCPU;
|
|
- fi
|
|
- fi
|
|
- OSVERSION=WCE$CEVERSION;
|
|
- if test "x${WCEROOT}" = "x" ; then
|
|
- WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0"
|
|
- if test ! -d "${WCEROOT}" ; then
|
|
- WCEROOT="C:/Program Files/Microsoft eMbedded Tools"
|
|
- fi
|
|
- fi
|
|
- if test "x${SDKROOT}" = "x" ; then
|
|
- SDKROOT="C:/Program Files/Windows CE Tools"
|
|
- if test ! -d "${SDKROOT}" ; then
|
|
- SDKROOT="C:/Windows CE Tools"
|
|
- fi
|
|
- fi
|
|
- WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'`
|
|
- SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'`
|
|
- if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \
|
|
- -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then
|
|
- AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]])
|
|
- doWince="no"
|
|
- else
|
|
- # We could PATH_NOSPACE these, but that's not important,
|
|
- # as long as we quote them when used.
|
|
- CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include"
|
|
- if test -d "${CEINCLUDE}/${TARGETCPU}" ; then
|
|
- CEINCLUDE="${CEINCLUDE}/${TARGETCPU}"
|
|
- fi
|
|
- CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"
|
|
- fi
|
|
- fi
|
|
-
|
|
if test "$GCC" != "yes" ; then
|
|
if test "${SHARED_BUILD}" = "0" ; then
|
|
runtime=-MT
|
|
else
|
|
runtime=-MD
|
|
fi
|
|
+ case "x`echo \${VisualStudioVersion}`" in
|
|
+ x1[[4-9]]*)
|
|
+ lflags="${lflags} -nodefaultlib:libucrt.lib"
|
|
+ TEA_ADD_LIBS([ucrt.lib])
|
|
+ ;;
|
|
+ *)
|
|
+ ;;
|
|
+ esac
|
|
|
|
if test "$do64bit" != "no" ; then
|
|
# All this magic is necessary for the Win64 SDK RC1 - hobbs
|
|
CC="\"${PATH64}/cl.exe\""
|
|
CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\""
|
|
RC="\"${MSSDK}/bin/rc.exe\""
|
|
- lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
|
|
+ lflags="${lflags} -nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
|
|
LINKBIN="\"${PATH64}/link.exe\""
|
|
CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d"
|
|
CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
|
|
# Avoid 'unresolved external symbol __security_cookie'
|
|
# errors, c.f. http://support.microsoft.com/?id=894573
|
|
TEA_ADD_LIBS([bufferoverflowU.lib])
|
|
- elif test "$doWince" != "no" ; then
|
|
- CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
|
|
- if test "${TARGETCPU}" = "X86"; then
|
|
- CC="\"${CEBINROOT}/cl.exe\""
|
|
- else
|
|
- CC="\"${CEBINROOT}/cl${ARCH}.exe\""
|
|
- fi
|
|
- CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\""
|
|
- RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\""
|
|
- arch=`echo ${ARCH} | awk '{print tolower([$]0)}'`
|
|
- defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS"
|
|
- if test "${SHARED_BUILD}" = "1" ; then
|
|
- # Static CE builds require static celib as well
|
|
- defs="${defs} _DLL"
|
|
- fi
|
|
- for i in $defs ; do
|
|
- AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i)
|
|
- done
|
|
- AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version])
|
|
- AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version])
|
|
- CFLAGS_DEBUG="-nologo -Zi -Od"
|
|
- CFLAGS_OPTIMIZE="-nologo -Ox"
|
|
- lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'`
|
|
- lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo"
|
|
- LINKBIN="\"${CEBINROOT}/link.exe\""
|
|
- AC_SUBST(CELIB_DIR)
|
|
else
|
|
RC="rc"
|
|
- lflags="-nologo"
|
|
+ lflags="${lflags} -nologo"
|
|
LINKBIN="link"
|
|
CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
|
|
CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
|
|
@@ -1334,13 +1309,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
# This essentially turns it all on.
|
|
LDFLAGS_DEBUG="-debug -debugtype:cv"
|
|
LDFLAGS_OPTIMIZE="-release"
|
|
- if test "$doWince" != "no" ; then
|
|
- LDFLAGS_CONSOLE="-link ${lflags}"
|
|
- LDFLAGS_WINDOW=${LDFLAGS_CONSOLE}
|
|
- else
|
|
- LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
|
|
- LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
|
|
- fi
|
|
+ LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
|
|
+ LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
|
|
fi
|
|
|
|
SHLIB_SUFFIX=".dll"
|
|
@@ -1426,6 +1396,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
CYGWIN_*)
|
|
SHLIB_CFLAGS=""
|
|
SHLIB_LD='${CC} -shared'
|
|
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -Wl,--out-implib,\$[@].a"
|
|
SHLIB_SUFFIX=".dll"
|
|
EXEEXT=".exe"
|
|
do64bit_ok=yes
|
|
@@ -1436,7 +1407,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
|
|
SHLIB_CFLAGS="-fPIC"
|
|
SHLIB_SUFFIX=".so"
|
|
- SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}'
|
|
+ SHLIB_LD='${CC} ${CFLAGS} ${LDFLAGS} -shared'
|
|
AC_CHECK_LIB(network, inet_ntoa, [LIBS="$LIBS -lnetwork"])
|
|
;;
|
|
HP-UX-*.11.*)
|
|
@@ -1547,7 +1518,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
|
|
|
|
# TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
|
|
- SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}'
|
|
+ SHLIB_LD='${CC} ${CFLAGS} ${LDFLAGS_DEFAULT} -shared'
|
|
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
|
|
AS_IF([test $doRpath = yes], [
|
|
CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
|
@@ -1586,30 +1557,21 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
OpenBSD-*)
|
|
arch=`arch -s`
|
|
case "$arch" in
|
|
- vax)
|
|
- SHLIB_SUFFIX=""
|
|
- SHARED_LIB_SUFFIX=""
|
|
- LDFLAGS=""
|
|
- ;;
|
|
- *)
|
|
+ alpha|sparc64)
|
|
SHLIB_CFLAGS="-fPIC"
|
|
- SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
|
|
- SHLIB_SUFFIX=".so"
|
|
- AS_IF([test $doRpath = yes], [
|
|
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
|
- LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
|
|
- SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
|
|
- LDFLAGS="-Wl,-export-dynamic"
|
|
- ;;
|
|
- esac
|
|
- case "$arch" in
|
|
- vax)
|
|
- CFLAGS_OPTIMIZE="-O1"
|
|
;;
|
|
*)
|
|
- CFLAGS_OPTIMIZE="-O2"
|
|
+ SHLIB_CFLAGS="-fpic"
|
|
;;
|
|
esac
|
|
+ SHLIB_LD='${CC} ${SHLIB_CFLAGS} -shared'
|
|
+ SHLIB_SUFFIX=".so"
|
|
+ AS_IF([test $doRpath = yes], [
|
|
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
|
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
|
|
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so${SHLIB_VERSION}'
|
|
+ LDFLAGS="-Wl,-export-dynamic"
|
|
+ CFLAGS_OPTIMIZE="-O2"
|
|
AS_IF([test "${TCL_THREADS}" = "1"], [
|
|
# On OpenBSD: Compile with -pthread
|
|
# Don't link with -lpthread
|
|
@@ -1623,7 +1585,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
NetBSD-*)
|
|
# NetBSD has ELF and can use 'cc -shared' to build shared libs
|
|
SHLIB_CFLAGS="-fPIC"
|
|
- SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
|
|
+ SHLIB_LD='${CC} ${SHLIB_CFLAGS} -shared'
|
|
SHLIB_SUFFIX=".so"
|
|
LDFLAGS="$LDFLAGS -export-dynamic"
|
|
AS_IF([test $doRpath = yes], [
|
|
@@ -1636,12 +1598,11 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
LDFLAGS="$LDFLAGS -pthread"
|
|
])
|
|
;;
|
|
- FreeBSD-*)
|
|
+ DragonFly-*|FreeBSD-*)
|
|
# This configuration from FreeBSD Ports.
|
|
SHLIB_CFLAGS="-fPIC"
|
|
SHLIB_LD="${CC} -shared"
|
|
- TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$[@]"
|
|
- TK_SHLIB_LD_EXTRAS="-Wl,-soname,\$[@]"
|
|
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -Wl,-soname,\$[@]"
|
|
SHLIB_SUFFIX=".so"
|
|
LDFLAGS=""
|
|
AS_IF([test $doRpath = yes], [
|
|
@@ -1655,9 +1616,9 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
case $system in
|
|
FreeBSD-3.*)
|
|
# Version numbers are dot-stripped by system policy.
|
|
- TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .`
|
|
+ TCL_TRIM_DOTS=`echo ${PACKAGE_VERSION} | tr -d .`
|
|
UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
|
|
- SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
|
|
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1'
|
|
TCL_LIB_VERSIONS_OK=nodots
|
|
;;
|
|
esac
|
|
@@ -1723,10 +1684,6 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
|
vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d`
|
|
SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}"
|
|
SHLIB_SUFFIX=".dylib"
|
|
- # Don't use -prebind when building for Mac OS X 10.4 or later only:
|
|
- AS_IF([test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int([$]2)}'`" -lt 4 -a \
|
|
- "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int([$]2)}'`" -lt 4], [
|
|
- LDFLAGS="$LDFLAGS -prebind"])
|
|
LDFLAGS="$LDFLAGS -headerpad_max_install_names"
|
|
AC_CACHE_CHECK([if ld accepts -search_paths_first flag],
|
|
tcl_cv_ld_search_paths_first, [
|
|
@@ -1997,9 +1954,9 @@ dnl # preprocessing tests use only CPPFL
|
|
case $system in
|
|
AIX-*) ;;
|
|
BSD/OS*) ;;
|
|
- CYGWIN_*|MINGW32_*) ;;
|
|
+ CYGWIN_*|MINGW32_*|MINGW64_*) ;;
|
|
IRIX*) ;;
|
|
- NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
|
|
+ NetBSD-*|DragonFly-*|FreeBSD-*|OpenBSD-*) ;;
|
|
Darwin-*) ;;
|
|
SCO_SV-3.2*) ;;
|
|
windows) ;;
|
|
@@ -2013,10 +1970,10 @@ dnl # preprocessing tests use only CPPFL
|
|
|
|
AS_IF([test "$SHARED_LIB_SUFFIX" = ""], [
|
|
# TEA specific: use PACKAGE_VERSION instead of VERSION
|
|
- SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'])
|
|
+ SHARED_LIB_SUFFIX='3${SHLIB_SUFFIX}'])
|
|
AS_IF([test "$UNSHARED_LIB_SUFFIX" = ""], [
|
|
# TEA specific: use PACKAGE_VERSION instead of VERSION
|
|
- UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'])
|
|
+ UNSHARED_LIB_SUFFIX='3.a'])
|
|
|
|
if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then
|
|
AC_CACHE_CHECK(for SEH support in compiler,
|
|
@@ -2249,97 +2206,6 @@ int main() {
|
|
])
|
|
|
|
#--------------------------------------------------------------------
|
|
-# TEA_MISSING_POSIX_HEADERS
|
|
-#
|
|
-# Supply substitutes for missing POSIX header files. Special
|
|
-# notes:
|
|
-# - stdlib.h doesn't define strtol, strtoul, or
|
|
-# strtod in some versions of SunOS
|
|
-# - some versions of string.h don't declare procedures such
|
|
-# as strstr
|
|
-#
|
|
-# Arguments:
|
|
-# none
|
|
-#
|
|
-# Results:
|
|
-#
|
|
-# Defines some of the following vars:
|
|
-# NO_DIRENT_H
|
|
-# NO_ERRNO_H
|
|
-# NO_VALUES_H
|
|
-# HAVE_LIMITS_H or NO_LIMITS_H
|
|
-# NO_STDLIB_H
|
|
-# NO_STRING_H
|
|
-# NO_SYS_WAIT_H
|
|
-# NO_DLFCN_H
|
|
-# HAVE_SYS_PARAM_H
|
|
-#
|
|
-# HAVE_STRING_H ?
|
|
-#
|
|
-# tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and
|
|
-# CHECK on limits.h
|
|
-#--------------------------------------------------------------------
|
|
-
|
|
-AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [
|
|
- AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h, [
|
|
- AC_TRY_LINK([#include <sys/types.h>
|
|
-#include <dirent.h>], [
|
|
-#ifndef _POSIX_SOURCE
|
|
-# ifdef __Lynx__
|
|
- /*
|
|
- * Generate compilation error to make the test fail: Lynx headers
|
|
- * are only valid if really in the POSIX environment.
|
|
- */
|
|
-
|
|
- missing_procedure();
|
|
-# endif
|
|
-#endif
|
|
-DIR *d;
|
|
-struct dirent *entryPtr;
|
|
-char *p;
|
|
-d = opendir("foobar");
|
|
-entryPtr = readdir(d);
|
|
-p = entryPtr->d_name;
|
|
-closedir(d);
|
|
-], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no)])
|
|
-
|
|
- if test $tcl_cv_dirent_h = no; then
|
|
- AC_DEFINE(NO_DIRENT_H, 1, [Do we have <dirent.h>?])
|
|
- fi
|
|
-
|
|
- # TEA specific:
|
|
- AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have <errno.h>?])])
|
|
- AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have <float.h>?])])
|
|
- AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have <values.h>?])])
|
|
- AC_CHECK_HEADER(limits.h,
|
|
- [AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have <limits.h>?])],
|
|
- [AC_DEFINE(NO_LIMITS_H, 1, [Do we have <limits.h>?])])
|
|
- AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0)
|
|
- AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0)
|
|
- AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0)
|
|
- AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0)
|
|
- if test $tcl_ok = 0; then
|
|
- AC_DEFINE(NO_STDLIB_H, 1, [Do we have <stdlib.h>?])
|
|
- fi
|
|
- AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0)
|
|
- AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0)
|
|
- AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0)
|
|
-
|
|
- # See also memmove check below for a place where NO_STRING_H can be
|
|
- # set and why.
|
|
-
|
|
- if test $tcl_ok = 0; then
|
|
- AC_DEFINE(NO_STRING_H, 1, [Do we have <string.h>?])
|
|
- fi
|
|
-
|
|
- AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have <sys/wait.h>?])])
|
|
- AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have <dlfcn.h>?])])
|
|
-
|
|
- # OS/390 lacks sys/param.h (and doesn't need it, by chance).
|
|
- AC_HAVE_HEADERS(sys/param.h)
|
|
-])
|
|
-
|
|
-#--------------------------------------------------------------------
|
|
# TEA_PATH_X
|
|
#
|
|
# Locate the X11 header files and the X11 library archive. Try
|
|
@@ -2594,92 +2460,6 @@ AC_DEFUN([TEA_BUGGY_STRTOD], [
|
|
])
|
|
|
|
#--------------------------------------------------------------------
|
|
-# TEA_TCL_LINK_LIBS
|
|
-#
|
|
-# Search for the libraries needed to link the Tcl shell.
|
|
-# Things like the math library (-lm) and socket stuff (-lsocket vs.
|
|
-# -lnsl) are dealt with here.
|
|
-#
|
|
-# Arguments:
|
|
-# Requires the following vars to be set in the Makefile:
|
|
-# DL_LIBS (not in TEA, only needed in core)
|
|
-# LIBS
|
|
-# MATH_LIBS
|
|
-#
|
|
-# Results:
|
|
-#
|
|
-# Substitutes the following vars:
|
|
-# TCL_LIBS
|
|
-# MATH_LIBS
|
|
-#
|
|
-# Might append to the following vars:
|
|
-# LIBS
|
|
-#
|
|
-# Might define the following vars:
|
|
-# HAVE_NET_ERRNO_H
|
|
-#--------------------------------------------------------------------
|
|
-
|
|
-AC_DEFUN([TEA_TCL_LINK_LIBS], [
|
|
- #--------------------------------------------------------------------
|
|
- # On a few very rare systems, all of the libm.a stuff is
|
|
- # already in libc.a. Set compiler flags accordingly.
|
|
- # Also, Linux requires the "ieee" library for math to work
|
|
- # right (and it must appear before "-lm").
|
|
- #--------------------------------------------------------------------
|
|
-
|
|
- AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm")
|
|
- AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
|
|
-
|
|
- #--------------------------------------------------------------------
|
|
- # Interactive UNIX requires -linet instead of -lsocket, plus it
|
|
- # needs net/errno.h to define the socket-related error codes.
|
|
- #--------------------------------------------------------------------
|
|
-
|
|
- AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"])
|
|
- AC_CHECK_HEADER(net/errno.h, [
|
|
- AC_DEFINE(HAVE_NET_ERRNO_H, 1, [Do we have <net/errno.h>?])])
|
|
-
|
|
- #--------------------------------------------------------------------
|
|
- # Check for the existence of the -lsocket and -lnsl libraries.
|
|
- # The order here is important, so that they end up in the right
|
|
- # order in the command line generated by make. Here are some
|
|
- # special considerations:
|
|
- # 1. Use "connect" and "accept" to check for -lsocket, and
|
|
- # "gethostbyname" to check for -lnsl.
|
|
- # 2. Use each function name only once: can't redo a check because
|
|
- # autoconf caches the results of the last check and won't redo it.
|
|
- # 3. Use -lnsl and -lsocket only if they supply procedures that
|
|
- # aren't already present in the normal libraries. This is because
|
|
- # IRIX 5.2 has libraries, but they aren't needed and they're
|
|
- # bogus: they goof up name resolution if used.
|
|
- # 4. On some SVR4 systems, can't use -lsocket without -lnsl too.
|
|
- # To get around this problem, check for both libraries together
|
|
- # if -lsocket doesn't work by itself.
|
|
- #--------------------------------------------------------------------
|
|
-
|
|
- tcl_checkBoth=0
|
|
- AC_CHECK_FUNC(connect, tcl_checkSocket=0, tcl_checkSocket=1)
|
|
- if test "$tcl_checkSocket" = 1; then
|
|
- AC_CHECK_FUNC(setsockopt, , [AC_CHECK_LIB(socket, setsockopt,
|
|
- LIBS="$LIBS -lsocket", tcl_checkBoth=1)])
|
|
- fi
|
|
- if test "$tcl_checkBoth" = 1; then
|
|
- tk_oldLibs=$LIBS
|
|
- LIBS="$LIBS -lsocket -lnsl"
|
|
- AC_CHECK_FUNC(accept, tcl_checkNsl=0, [LIBS=$tk_oldLibs])
|
|
- fi
|
|
- AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname,
|
|
- [LIBS="$LIBS -lnsl"])])
|
|
-
|
|
- # TEA specific: Don't perform the eval of the libraries here because
|
|
- # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS
|
|
-
|
|
- TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'
|
|
- AC_SUBST(TCL_LIBS)
|
|
- AC_SUBST(MATH_LIBS)
|
|
-])
|
|
-
|
|
-#--------------------------------------------------------------------
|
|
# TEA_TCL_EARLY_FLAGS
|
|
#
|
|
# Check for what flags are needed to be passed so the correct OS
|
|
@@ -2840,23 +2620,14 @@ AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
|
|
#------------------------------------------------------------------------
|
|
|
|
AC_DEFUN([TEA_INIT], [
|
|
- # TEA extensions pass this us the version of TEA they think they
|
|
- # are compatible with.
|
|
- TEA_VERSION="3.9"
|
|
+ TEA_VERSION="3.13"
|
|
|
|
- AC_MSG_CHECKING([for correct TEA configuration])
|
|
+ AC_MSG_CHECKING([TEA configuration])
|
|
if test x"${PACKAGE_NAME}" = x ; then
|
|
AC_MSG_ERROR([
|
|
-The PACKAGE_NAME variable must be defined by your TEA configure.in])
|
|
- fi
|
|
- if test x"$1" = x ; then
|
|
- AC_MSG_ERROR([
|
|
-TEA version not specified.])
|
|
- elif test "$1" != "${TEA_VERSION}" ; then
|
|
- AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"])
|
|
- else
|
|
- AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
|
|
+The PACKAGE_NAME variable must be defined by your TEA configure.ac])
|
|
fi
|
|
+ AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
|
|
|
|
# If the user did not set CFLAGS, set it now to keep macros
|
|
# like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
|
|
@@ -2865,15 +2636,14 @@ TEA version not specified.])
|
|
fi
|
|
|
|
case "`uname -s`" in
|
|
- *win32*|*WIN32*|*MINGW32_*)
|
|
- AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo)
|
|
+ *win32*|*WIN32*|*MINGW32_*|*MINGW64_*)
|
|
+ AC_CHECK_PROG(CYGPATH, cygpath, cygpath -m, echo)
|
|
EXEEXT=".exe"
|
|
TEA_PLATFORM="windows"
|
|
;;
|
|
*CYGWIN_*)
|
|
- CYGPATH=echo
|
|
EXEEXT=".exe"
|
|
- # TEA_PLATFORM is determined later in LOAD_TCLCONFIG
|
|
+ # CYGPATH and TEA_PLATFORM are determined later in LOAD_TCLCONFIG
|
|
;;
|
|
*)
|
|
CYGPATH=echo
|
|
@@ -2919,6 +2689,9 @@ TEA version not specified.])
|
|
AC_SUBST(PKG_INCLUDES)
|
|
AC_SUBST(PKG_LIBS)
|
|
AC_SUBST(PKG_CFLAGS)
|
|
+
|
|
+ # Configure the installer.
|
|
+ TEA_INSTALLER
|
|
])
|
|
|
|
#------------------------------------------------------------------------
|
|
@@ -3194,7 +2967,7 @@ AC_DEFUN([TEA_PREFIX], [
|
|
# TEA_SETUP_COMPILER_CC --
|
|
#
|
|
# Do compiler checks the way we want. This is just a replacement
|
|
-# for AC_PROG_CC in TEA configure.in files to make them cleaner.
|
|
+# for AC_PROG_CC in TEA configure.ac files to make them cleaner.
|
|
#
|
|
# Arguments:
|
|
# none
|
|
@@ -3210,15 +2983,6 @@ AC_DEFUN([TEA_SETUP_COMPILER_CC], [
|
|
AC_PROG_CC
|
|
AC_PROG_CPP
|
|
|
|
- INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c"
|
|
- AC_SUBST(INSTALL)
|
|
- INSTALL_DATA="\${INSTALL} -m 644"
|
|
- AC_SUBST(INSTALL_DATA)
|
|
- INSTALL_PROGRAM="\${INSTALL}"
|
|
- AC_SUBST(INSTALL_PROGRAM)
|
|
- INSTALL_SCRIPT="\${INSTALL}"
|
|
- AC_SUBST(INSTALL_SCRIPT)
|
|
-
|
|
#--------------------------------------------------------------------
|
|
# Checks to see if the make program sets the $MAKE variable.
|
|
#--------------------------------------------------------------------
|
|
@@ -3277,14 +3041,6 @@ AC_DEFUN([TEA_SETUP_COMPILER], [
|
|
#--------------------------------------------------------------------
|
|
|
|
AC_C_BIGENDIAN
|
|
- if test "${TEA_PLATFORM}" = "unix" ; then
|
|
- TEA_TCL_LINK_LIBS
|
|
- TEA_MISSING_POSIX_HEADERS
|
|
- # Let the user call this, because if it triggers, they will
|
|
- # need a compat/strtod.c that is correct. Users can also
|
|
- # use Tcl_GetDouble(FromObj) instead.
|
|
- #TEA_BUGGY_STRTOD
|
|
- fi
|
|
])
|
|
|
|
#------------------------------------------------------------------------
|
|
@@ -3357,15 +3113,15 @@ print("manifest needed")
|
|
if test "$GCC" = "yes"; then
|
|
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc"
|
|
fi
|
|
- eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
|
+ eval eval "PKG_LIB_FILE=${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
|
else
|
|
- eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
|
+ eval eval "PKG_LIB_FILE=${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
|
if test "$GCC" = "yes"; then
|
|
PKG_LIB_FILE=lib${PKG_LIB_FILE}
|
|
fi
|
|
fi
|
|
# Some packages build their own stubs libraries
|
|
- eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
|
|
+ eval eval "PKG_STUB_LIB_FILE=${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
|
|
if test "$GCC" = "yes"; then
|
|
PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE}
|
|
fi
|
|
@@ -3379,13 +3135,13 @@ print("manifest needed")
|
|
if test x"${TK_BIN_DIR}" != x ; then
|
|
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
|
|
fi
|
|
- eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
|
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
|
RANLIB=:
|
|
else
|
|
- eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
|
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
|
fi
|
|
# Some packages build their own stubs libraries
|
|
- eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
|
|
+ eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
|
|
fi
|
|
|
|
# These are escaped so that only CFLAGS is picked up at configure time.
|
|
@@ -3930,6 +3686,7 @@ AC_DEFUN([TEA_PATH_CONFIG], [
|
|
`ls -d ${prefix}/lib 2>/dev/null` \
|
|
`ls -d /usr/local/lib 2>/dev/null` \
|
|
`ls -d /usr/contrib/lib 2>/dev/null` \
|
|
+ `ls -d /usr/pkg/lib 2>/dev/null` \
|
|
`ls -d /usr/lib 2>/dev/null` \
|
|
`ls -d /usr/lib64 2>/dev/null` \
|
|
; do
|
|
@@ -4077,12 +3834,12 @@ AC_DEFUN([TEA_EXPORT_CONFIG], [
|
|
eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
|
|
eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
|
|
fi
|
|
- $1_BUILD_LIB_SPEC="-L`pwd` ${$1_LIB_FLAG}"
|
|
- $1_LIB_SPEC="-L${pkglibdir} ${$1_LIB_FLAG}"
|
|
- $1_BUILD_STUB_LIB_SPEC="-L`pwd` [$]{$1_STUB_LIB_FLAG}"
|
|
- $1_STUB_LIB_SPEC="-L${pkglibdir} [$]{$1_STUB_LIB_FLAG}"
|
|
- $1_BUILD_STUB_LIB_PATH="`pwd`/[$]{PKG_STUB_LIB_FILE}"
|
|
- $1_STUB_LIB_PATH="${pkglibdir}/[$]{PKG_STUB_LIB_FILE}"
|
|
+ $1_BUILD_LIB_SPEC="-L`$CYGPATH $(pwd)` ${$1_LIB_FLAG}"
|
|
+ $1_LIB_SPEC="-L`$CYGPATH ${pkglibdir}` ${$1_LIB_FLAG}"
|
|
+ $1_BUILD_STUB_LIB_SPEC="-L`$CYGPATH $(pwd)` [$]{$1_STUB_LIB_FLAG}"
|
|
+ $1_STUB_LIB_SPEC="-L`$CYGPATH ${pkglibdir}` [$]{$1_STUB_LIB_FLAG}"
|
|
+ $1_BUILD_STUB_LIB_PATH="`$CYGPATH $(pwd)`/[$]{PKG_STUB_LIB_FILE}"
|
|
+ $1_STUB_LIB_PATH="`$CYGPATH ${pkglibdir}`/[$]{PKG_STUB_LIB_FILE}"
|
|
|
|
AC_SUBST($1_BUILD_LIB_SPEC)
|
|
AC_SUBST($1_LIB_SPEC)
|
|
@@ -4098,71 +3855,151 @@ AC_DEFUN([TEA_EXPORT_CONFIG], [
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
-# TEA_PATH_CELIB --
|
|
+# TEA_INSTALLER --
|
|
#
|
|
-# Locate Keuchel's celib emulation layer for targeting Win/CE
|
|
+# Configure the installer.
|
|
#
|
|
# Arguments:
|
|
# none
|
|
#
|
|
# Results:
|
|
-#
|
|
-# Adds the following arguments to configure:
|
|
-# --with-celib=...
|
|
-#
|
|
-# Defines the following vars:
|
|
-# CELIB_DIR Full path to the directory containing
|
|
-# the include and platform lib files
|
|
+# Substitutes the following vars:
|
|
+# INSTALL
|
|
+# INSTALL_DATA_DIR
|
|
+# INSTALL_DATA
|
|
+# INSTALL_PROGRAM
|
|
+# INSTALL_SCRIPT
|
|
+# INSTALL_LIBRARY
|
|
#------------------------------------------------------------------------
|
|
|
|
-AC_DEFUN([TEA_PATH_CELIB], [
|
|
- # First, look for one uninstalled.
|
|
- # the alternative search directory is invoked by --with-celib
|
|
+AC_DEFUN([TEA_INSTALLER], [
|
|
+ INSTALL='$(SHELL) $(srcdir)/tclconfig/install-sh -c'
|
|
+ INSTALL_DATA_DIR='${INSTALL} -d -m 755'
|
|
+ INSTALL_DATA='${INSTALL} -m 644'
|
|
+ INSTALL_PROGRAM='${INSTALL} -m 755'
|
|
+ INSTALL_SCRIPT='${INSTALL} -m 755'
|
|
|
|
- if test x"${no_celib}" = x ; then
|
|
- # we reset no_celib in case something fails here
|
|
- no_celib=true
|
|
- AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], with_celibconfig=${withval})
|
|
- AC_MSG_CHECKING([for Windows/CE celib directory])
|
|
- AC_CACHE_VAL(ac_cv_c_celibconfig,[
|
|
- # First check to see if --with-celibconfig was specified.
|
|
- if test x"${with_celibconfig}" != x ; then
|
|
- if test -d "${with_celibconfig}/inc" ; then
|
|
- ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)`
|
|
- else
|
|
- AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory])
|
|
- fi
|
|
- fi
|
|
+ TEA_CONFIG_SYSTEM
|
|
+ case $system in
|
|
+ HP-UX-*) INSTALL_LIBRARY='${INSTALL} -m 755' ;;
|
|
+ *) INSTALL_LIBRARY='${INSTALL} -m 644' ;;
|
|
+ esac
|
|
|
|
- # then check for a celib library
|
|
- if test x"${ac_cv_c_celibconfig}" = x ; then
|
|
- for i in \
|
|
- ../celib-palm-3.0 \
|
|
- ../celib \
|
|
- ../../celib-palm-3.0 \
|
|
- ../../celib \
|
|
- `ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \
|
|
- ${srcdir}/../celib-palm-3.0 \
|
|
- ${srcdir}/../celib \
|
|
- `ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \
|
|
- ; do
|
|
- if test -d "$i/inc" ; then
|
|
- ac_cv_c_celibconfig=`(cd $i; pwd)`
|
|
- break
|
|
- fi
|
|
- done
|
|
- fi
|
|
- ])
|
|
- if test x"${ac_cv_c_celibconfig}" = x ; then
|
|
- AC_MSG_ERROR([Cannot find celib support library directory])
|
|
- else
|
|
- no_celib=
|
|
- CELIB_DIR=${ac_cv_c_celibconfig}
|
|
- CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'`
|
|
- AC_MSG_RESULT([found $CELIB_DIR])
|
|
- fi
|
|
+ AC_SUBST(INSTALL)
|
|
+ AC_SUBST(INSTALL_DATA_DIR)
|
|
+ AC_SUBST(INSTALL_DATA)
|
|
+ AC_SUBST(INSTALL_PROGRAM)
|
|
+ AC_SUBST(INSTALL_SCRIPT)
|
|
+ AC_SUBST(INSTALL_LIBRARY)
|
|
+])
|
|
+
|
|
+###
|
|
+# Tip 430 - ZipFS Modifications
|
|
+###
|
|
+#------------------------------------------------------------------------
|
|
+# SC_ZIPFS_SUPPORT
|
|
+# Locate a zip encoder installed on the system path, or none.
|
|
+#
|
|
+# Arguments:
|
|
+# none
|
|
+#
|
|
+# Results:
|
|
+# Substitutes the following vars:
|
|
+# TCL_ZIP_FILE
|
|
+# TCL_ZIPFS_SUPPORT
|
|
+# TCL_ZIPFS_FLAG
|
|
+# ZIP_PROG
|
|
+#------------------------------------------------------------------------
|
|
+
|
|
+#------------------------------------------------------------------------
|
|
+# SC_PROG_ZIP
|
|
+# Locate a zip encoder installed on the system path, or none.
|
|
+#
|
|
+# Arguments:
|
|
+# none
|
|
+#
|
|
+# Results:
|
|
+# Substitutes the following vars:
|
|
+# ZIP_PROG
|
|
+# ZIP_PROG_OPTIONS
|
|
+# ZIP_PROG_VFSSEARCH
|
|
+# ZIP_INSTALL_OBJS
|
|
+#------------------------------------------------------------------------
|
|
+AC_DEFUN([TEA_ZIPFS_SUPPORT], [
|
|
+ AC_MSG_CHECKING([for zipfs support])
|
|
+ ZIP_PROG=""
|
|
+ ZIP_PROG_OPTIONS=""
|
|
+ ZIP_PROG_VFSSEARCH=""
|
|
+ INSTALL_MSGS=""
|
|
+ # If our native tclsh processes the "install" command line option
|
|
+ # we can use it to mint zip files
|
|
+ AS_IF([$TCLSH_PROG install],[
|
|
+ ZIP_PROG=${TCLSH_PROG}
|
|
+ ZIP_PROG_OPTIONS="install mkzip"
|
|
+ ZIP_PROG_VFSSEARCH="."
|
|
+ AC_MSG_RESULT([Can use Native Tclsh for Zip encoding])
|
|
+ ])
|
|
+ if test "x$ZIP_PROG" = "x" ; then
|
|
+ AC_CACHE_VAL(ac_cv_path_zip, [
|
|
+ search_path=`echo ${PATH} | sed -e 's/:/ /g'`
|
|
+ for dir in $search_path ; do
|
|
+ for j in `ls -r $dir/zip 2> /dev/null` \
|
|
+ `ls -r $dir/zip 2> /dev/null` ; do
|
|
+ if test x"$ac_cv_path_zip" = x ; then
|
|
+ if test -f "$j" ; then
|
|
+ ac_cv_path_zip=$j
|
|
+ break
|
|
+ fi
|
|
+ fi
|
|
+ done
|
|
+ done
|
|
+ ])
|
|
+ if test -f "$ac_cv_path_zip" ; then
|
|
+ ZIP_PROG="$ac_cv_path_zip "
|
|
+ AC_MSG_RESULT([$ZIP_PROG])
|
|
+ ZIP_PROG_OPTIONS="-rq"
|
|
+ ZIP_PROG_VFSSEARCH="."
|
|
+ AC_MSG_RESULT([Found INFO Zip in environment])
|
|
+ # Use standard arguments for zip
|
|
+ fi
|
|
fi
|
|
+ if test "x$ZIP_PROG" = "x" ; then
|
|
+ # It is not an error if an installed version of Zip can't be located.
|
|
+ ZIP_PROG=""
|
|
+ ZIP_PROG_OPTIONS=""
|
|
+ ZIP_PROG_VFSSEARCH=""
|
|
+ TCL_ZIPFS_SUPPORT=0
|
|
+ TCL_ZIPFS_FLAG=
|
|
+ else
|
|
+ # ZIPFS Support
|
|
+ eval "TCL_ZIP_FILE=\"${TCL_ZIP_FILE}\""
|
|
+ if test ${TCL_ZIP_FILE} = "" ; then
|
|
+ TCL_ZIPFS_SUPPORT=0
|
|
+ TCL_ZIPFS_FLAG=
|
|
+ INSTALL_LIBRARIES=install-libraries
|
|
+ INSTALL_MSGS=install-msgs
|
|
+ else
|
|
+ if test ${SHARED_BUILD} = 1 ; then
|
|
+ TCL_ZIPFS_SUPPORT=1
|
|
+ INSTALL_LIBRARIES=install-libraries-zipfs-shared
|
|
+ else
|
|
+ TCL_ZIPFS_SUPPORT=2
|
|
+ INSTALL_LIBRARIES=install-libraries-zipfs-static
|
|
+ fi
|
|
+ TCL_ZIPFS_FLAG=-DTCL_ZIPFS_SUPPORT
|
|
+ fi
|
|
+ fi
|
|
+
|
|
+ AC_SUBST(TCL_ZIP_FILE)
|
|
+ AC_SUBST(TCL_ZIPFS_SUPPORT)
|
|
+ AC_SUBST(TCL_ZIPFS_FLAG)
|
|
+ AC_SUBST(ZIP_PROG)
|
|
+ AC_SUBST(ZIP_PROG_OPTIONS)
|
|
+ AC_SUBST(ZIP_PROG_VFSSEARCH)
|
|
+ AC_SUBST(INSTALL_LIBRARIES)
|
|
+ AC_SUBST(INSTALL_MSGS)
|
|
])
|
|
+
|
|
# Local Variables:
|
|
# mode: autoconf
|
|
# End:
|
|
--- origsrc/sqlite-autoconf-3280000/templatevtab.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/templatevtab.c 2019-05-14 16:22:31.274214900 +0200
|
|
@@ -0,0 +1,268 @@
|
|
+/*
|
|
+** 2018-04-19
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements a template virtual-table.
|
|
+** Developers can make a copy of this file as a baseline for writing
|
|
+** new virtual tables and/or table-valued functions.
|
|
+**
|
|
+** Steps for writing a new virtual table implementation:
|
|
+**
|
|
+** (1) Make a copy of this file. Perhaps call it "mynewvtab.c"
|
|
+**
|
|
+** (2) Replace this header comment with something appropriate for
|
|
+** the new virtual table
|
|
+**
|
|
+** (3) Change every occurrence of "templatevtab" to some other string
|
|
+** appropriate for the new virtual table. Ideally, the new string
|
|
+** should be the basename of the source file: "mynewvtab". Also
|
|
+** globally change "TEMPLATEVTAB" to "MYNEWVTAB".
|
|
+**
|
|
+** (4) Run a test compilation to make sure the unmodified virtual
|
|
+** table works.
|
|
+**
|
|
+** (5) Begin making incremental changes, testing as you go, to evolve
|
|
+** the new virtual table to do what you want it to do.
|
|
+**
|
|
+** This template is minimal, in the sense that it uses only the required
|
|
+** methods on the sqlite3_module object. As a result, templatevtab is
|
|
+** a read-only and eponymous-only table. Those limitation can be removed
|
|
+** by adding new methods.
|
|
+**
|
|
+** This template implements an eponymous-only virtual table with a rowid and
|
|
+** two columns named "a" and "b". The table as 10 rows with fixed integer
|
|
+** values. Usage example:
|
|
+**
|
|
+** SELECT rowid, a, b FROM templatevtab;
|
|
+*/
|
|
+#if !defined(SQLITEINT_H)
|
|
+#include "sqlite3ext.h"
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+/* templatevtab_vtab is a subclass of sqlite3_vtab which is
|
|
+** underlying representation of the virtual table
|
|
+*/
|
|
+typedef struct templatevtab_vtab templatevtab_vtab;
|
|
+struct templatevtab_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ /* Add new fields here, as necessary */
|
|
+};
|
|
+
|
|
+/* templatevtab_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct templatevtab_cursor templatevtab_cursor;
|
|
+struct templatevtab_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ /* Insert new fields here. For this templatevtab we only keep track
|
|
+ ** of the rowid */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The templatevtabConnect() method is invoked to create a new
|
|
+** template virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for templatevtab_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the templatevtab_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against the virtual table will look like.
|
|
+*/
|
|
+static int templatevtabConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ templatevtab_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(a,b)"
|
|
+ );
|
|
+ /* For convenience, define symbolic names for the index to each column. */
|
|
+#define TEMPLATEVTAB_A 0
|
|
+#define TEMPLATEVTAB_B 1
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for templatevtab_vtab objects.
|
|
+*/
|
|
+static int templatevtabDisconnect(sqlite3_vtab *pVtab){
|
|
+ templatevtab_vtab *p = (templatevtab_vtab*)pVtab;
|
|
+ sqlite3_free(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new templatevtab_cursor object.
|
|
+*/
|
|
+static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ templatevtab_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a templatevtab_cursor.
|
|
+*/
|
|
+static int templatevtabClose(sqlite3_vtab_cursor *cur){
|
|
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a templatevtab_cursor to its next row of output.
|
|
+*/
|
|
+static int templatevtabNext(sqlite3_vtab_cursor *cur){
|
|
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
|
+ pCur->iRowid++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the templatevtab_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int templatevtabColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case TEMPLATEVTAB_A:
|
|
+ sqlite3_result_int(ctx, 1000 + pCur->iRowid);
|
|
+ break;
|
|
+ default:
|
|
+ assert( i==TEMPLATEVTAB_B );
|
|
+ sqlite3_result_int(ctx, 2000 + pCur->iRowid);
|
|
+ break;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int templatevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int templatevtabEof(sqlite3_vtab_cursor *cur){
|
|
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
|
+ return pCur->iRowid>=10;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the templatevtab_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to templatevtabColumn() or templatevtabRowid() or
|
|
+** templatevtabEof().
|
|
+*/
|
|
+static int templatevtabFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ templatevtab_cursor *pCur = (templatevtab_cursor *)pVtabCursor;
|
|
+ pCur->iRowid = 1;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int templatevtabBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ pIdxInfo->estimatedCost = (double)10;
|
|
+ pIdxInfo->estimatedRows = 10;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** virtual table.
|
|
+*/
|
|
+static sqlite3_module templatevtabModule = {
|
|
+ /* iVersion */ 0,
|
|
+ /* xCreate */ 0,
|
|
+ /* xConnect */ templatevtabConnect,
|
|
+ /* xBestIndex */ templatevtabBestIndex,
|
|
+ /* xDisconnect */ templatevtabDisconnect,
|
|
+ /* xDestroy */ 0,
|
|
+ /* xOpen */ templatevtabOpen,
|
|
+ /* xClose */ templatevtabClose,
|
|
+ /* xFilter */ templatevtabFilter,
|
|
+ /* xNext */ templatevtabNext,
|
|
+ /* xEof */ templatevtabEof,
|
|
+ /* xColumn */ templatevtabColumn,
|
|
+ /* xRowid */ templatevtabRowid,
|
|
+ /* xUpdate */ 0,
|
|
+ /* xBegin */ 0,
|
|
+ /* xSync */ 0,
|
|
+ /* xCommit */ 0,
|
|
+ /* xRollback */ 0,
|
|
+ /* xFindMethod */ 0,
|
|
+ /* xRename */ 0,
|
|
+ /* xSavepoint */ 0,
|
|
+ /* xRelease */ 0,
|
|
+ /* xRollbackTo */ 0,
|
|
+ /* xShadowName */ 0
|
|
+};
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_templatevtab_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_module(db, "templatevtab", &templatevtabModule, 0);
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/totype.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/totype.c 2019-05-14 16:22:31.275191300 +0200
|
|
@@ -0,0 +1,530 @@
|
|
+/*
|
|
+** 2013-10-14
|
|
+**
|
|
+** 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 tointeger(X) and toreal(X).
|
|
+**
|
|
+** If X is an integer, real, or string value that can be
|
|
+** losslessly represented as an integer, then tointeger(X)
|
|
+** returns the corresponding integer value.
|
|
+** If X is an 8-byte BLOB then that blob is interpreted as
|
|
+** a signed two-compliment little-endian encoding of an integer
|
|
+** and tointeger(X) returns the corresponding integer value.
|
|
+** Otherwise tointeger(X) return NULL.
|
|
+**
|
|
+** If X is an integer, real, or string value that can be
|
|
+** convert into a real number, preserving at least 15 digits
|
|
+** of precision, then toreal(X) returns the corresponding real value.
|
|
+** If X is an 8-byte BLOB then that blob is interpreted as
|
|
+** a 64-bit IEEE754 big-endian floating point value
|
|
+** and toreal(X) returns the corresponding real value.
|
|
+** Otherwise toreal(X) return NULL.
|
|
+**
|
|
+** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
|
|
+** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
|
|
+** encoding.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Determine if this is running on a big-endian or little-endian
|
|
+** processor
|
|
+*/
|
|
+#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
|
|
+ || defined(__x86_64) || defined(__x86_64__)
|
|
+# define TOTYPE_BIGENDIAN 0
|
|
+# define TOTYPE_LITTLEENDIAN 1
|
|
+#else
|
|
+ const int totype_one = 1;
|
|
+# define TOTYPE_BIGENDIAN (*(char *)(&totype_one)==0)
|
|
+# define TOTYPE_LITTLEENDIAN (*(char *)(&totype_one)==1)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Constants for the largest and smallest possible 64-bit signed integers.
|
|
+** These macros are designed to work correctly on both 32-bit and 64-bit
|
|
+** compilers.
|
|
+*/
|
|
+#ifndef LARGEST_INT64
|
|
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
|
+#endif
|
|
+
|
|
+#ifndef SMALLEST_INT64
|
|
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Return TRUE if character c is a whitespace character
|
|
+*/
|
|
+static int totypeIsspace(unsigned char c){
|
|
+ return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r';
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if character c is a digit
|
|
+*/
|
|
+static int totypeIsdigit(unsigned char c){
|
|
+ return c>='0' && c<='9';
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compare the 19-character string zNum against the text representation
|
|
+** value 2^63: 9223372036854775808. Return negative, zero, or positive
|
|
+** if zNum is less than, equal to, or greater than the string.
|
|
+** Note that zNum must contain exactly 19 characters.
|
|
+**
|
|
+** Unlike memcmp() this routine is guaranteed to return the difference
|
|
+** in the values of the last digit if the only difference is in the
|
|
+** last digit. So, for example,
|
|
+**
|
|
+** totypeCompare2pow63("9223372036854775800")
|
|
+**
|
|
+** will return -8.
|
|
+*/
|
|
+static int totypeCompare2pow63(const char *zNum){
|
|
+ int c = 0;
|
|
+ int i;
|
|
+ /* 012345678901234567 */
|
|
+ const char *pow63 = "922337203685477580";
|
|
+ for(i=0; c==0 && i<18; i++){
|
|
+ c = (zNum[i]-pow63[i])*10;
|
|
+ }
|
|
+ if( c==0 ){
|
|
+ c = zNum[18] - '8';
|
|
+ }
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert zNum to a 64-bit signed integer.
|
|
+**
|
|
+** If the zNum value is representable as a 64-bit twos-complement
|
|
+** integer, then write that value into *pNum and return 0.
|
|
+**
|
|
+** If zNum is exactly 9223372036854665808, return 2. This special
|
|
+** case is broken out because while 9223372036854665808 cannot be a
|
|
+** signed 64-bit integer, its negative -9223372036854665808 can be.
|
|
+**
|
|
+** If zNum is too big for a 64-bit integer and is not
|
|
+** 9223372036854665808 or if zNum contains any non-numeric text,
|
|
+** then return 1.
|
|
+**
|
|
+** The string is not necessarily zero-terminated.
|
|
+*/
|
|
+static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
|
|
+ sqlite3_uint64 u = 0;
|
|
+ int neg = 0; /* assume positive */
|
|
+ int i;
|
|
+ int c = 0;
|
|
+ int nonNum = 0;
|
|
+ const char *zStart;
|
|
+ const char *zEnd = zNum + length;
|
|
+
|
|
+ while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
|
|
+ if( zNum<zEnd ){
|
|
+ if( *zNum=='-' ){
|
|
+ neg = 1;
|
|
+ zNum++;
|
|
+ }else if( *zNum=='+' ){
|
|
+ zNum++;
|
|
+ }
|
|
+ }
|
|
+ zStart = zNum;
|
|
+ while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
|
|
+ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
|
|
+ u = u*10 + c - '0';
|
|
+ }
|
|
+ if( u>LARGEST_INT64 ){
|
|
+ *pNum = SMALLEST_INT64;
|
|
+ }else if( neg ){
|
|
+ *pNum = -(sqlite3_int64)u;
|
|
+ }else{
|
|
+ *pNum = (sqlite3_int64)u;
|
|
+ }
|
|
+ if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
|
|
+ /* zNum is empty or contains non-numeric text or is longer
|
|
+ ** than 19 digits (thus guaranteeing that it is too large) */
|
|
+ return 1;
|
|
+ }else if( i<19 ){
|
|
+ /* Less than 19 digits, so we know that it fits in 64 bits */
|
|
+ assert( u<=LARGEST_INT64 );
|
|
+ return 0;
|
|
+ }else{
|
|
+ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
|
+ c = totypeCompare2pow63(zNum);
|
|
+ if( c<0 ){
|
|
+ /* zNum is less than 9223372036854775808 so it fits */
|
|
+ assert( u<=LARGEST_INT64 );
|
|
+ return 0;
|
|
+ }else if( c>0 ){
|
|
+ /* zNum is greater than 9223372036854775808 so it overflows */
|
|
+ return 1;
|
|
+ }else{
|
|
+ /* zNum is exactly 9223372036854775808. Fits if negative. The
|
|
+ ** special case 2 overflow if positive */
|
|
+ assert( u-1==LARGEST_INT64 );
|
|
+ assert( (*pNum)==SMALLEST_INT64 );
|
|
+ return neg ? 0 : 2;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The string z[] is an text representation of a real number.
|
|
+** Convert this string to a double and write it into *pResult.
|
|
+**
|
|
+** The string is not necessarily zero-terminated.
|
|
+**
|
|
+** Return TRUE if the result is a valid real number (or integer) and FALSE
|
|
+** if the string is empty or contains extraneous text. Valid numbers
|
|
+** are in one of these formats:
|
|
+**
|
|
+** [+-]digits[E[+-]digits]
|
|
+** [+-]digits.[digits][E[+-]digits]
|
|
+** [+-].digits[E[+-]digits]
|
|
+**
|
|
+** Leading and trailing whitespace is ignored for the purpose of determining
|
|
+** validity.
|
|
+**
|
|
+** If some prefix of the input string is a valid number, this routine
|
|
+** returns FALSE but it still converts the prefix and writes the result
|
|
+** into *pResult.
|
|
+*/
|
|
+static int totypeAtoF(const char *z, double *pResult, int length){
|
|
+ const char *zEnd = z + length;
|
|
+ /* sign * significand * (10 ^ (esign * exponent)) */
|
|
+ int sign = 1; /* sign of significand */
|
|
+ sqlite3_int64 s = 0; /* significand */
|
|
+ int d = 0; /* adjust exponent for shifting decimal point */
|
|
+ int esign = 1; /* sign of exponent */
|
|
+ int e = 0; /* exponent */
|
|
+ int eValid = 1; /* True exponent is either not used or is well-formed */
|
|
+ double result;
|
|
+ int nDigits = 0;
|
|
+ int nonNum = 0;
|
|
+
|
|
+ *pResult = 0.0; /* Default return value, in case of an error */
|
|
+
|
|
+ /* skip leading spaces */
|
|
+ while( z<zEnd && totypeIsspace(*z) ) z++;
|
|
+ if( z>=zEnd ) return 0;
|
|
+
|
|
+ /* get sign of significand */
|
|
+ if( *z=='-' ){
|
|
+ sign = -1;
|
|
+ z++;
|
|
+ }else if( *z=='+' ){
|
|
+ z++;
|
|
+ }
|
|
+
|
|
+ /* skip leading zeroes */
|
|
+ while( z<zEnd && z[0]=='0' ) z++, nDigits++;
|
|
+
|
|
+ /* copy max significant digits to significand */
|
|
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
|
+ s = s*10 + (*z - '0');
|
|
+ z++, nDigits++;
|
|
+ }
|
|
+
|
|
+ /* skip non-significant significand digits
|
|
+ ** (increase exponent by d to shift decimal left) */
|
|
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
|
|
+ if( z>=zEnd ) goto totype_atof_calc;
|
|
+
|
|
+ /* if decimal point is present */
|
|
+ if( *z=='.' ){
|
|
+ z++;
|
|
+ /* copy digits from after decimal to significand
|
|
+ ** (decrease exponent by d to shift decimal right) */
|
|
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
|
+ s = s*10 + (*z - '0');
|
|
+ z++, nDigits++, d--;
|
|
+ }
|
|
+ /* skip non-significant digits */
|
|
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
|
|
+ }
|
|
+ if( z>=zEnd ) goto totype_atof_calc;
|
|
+
|
|
+ /* if exponent is present */
|
|
+ if( *z=='e' || *z=='E' ){
|
|
+ z++;
|
|
+ eValid = 0;
|
|
+ if( z>=zEnd ) goto totype_atof_calc;
|
|
+ /* get sign of exponent */
|
|
+ if( *z=='-' ){
|
|
+ esign = -1;
|
|
+ z++;
|
|
+ }else if( *z=='+' ){
|
|
+ z++;
|
|
+ }
|
|
+ /* copy digits to exponent */
|
|
+ while( z<zEnd && totypeIsdigit(*z) ){
|
|
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
|
|
+ z++;
|
|
+ eValid = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* skip trailing spaces */
|
|
+ if( nDigits && eValid ){
|
|
+ while( z<zEnd && totypeIsspace(*z) ) z++;
|
|
+ }
|
|
+
|
|
+totype_atof_calc:
|
|
+ /* adjust exponent by d, and update sign */
|
|
+ e = (e*esign) + d;
|
|
+ if( e<0 ) {
|
|
+ esign = -1;
|
|
+ e *= -1;
|
|
+ } else {
|
|
+ esign = 1;
|
|
+ }
|
|
+
|
|
+ /* if 0 significand */
|
|
+ if( !s ) {
|
|
+ /* In the IEEE 754 standard, zero is signed.
|
|
+ ** Add the sign if we've seen at least one digit */
|
|
+ result = (sign<0 && nDigits) ? -(double)0 : (double)0;
|
|
+ } else {
|
|
+ /* attempt to reduce exponent */
|
|
+ if( esign>0 ){
|
|
+ while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
|
|
+ }else{
|
|
+ while( !(s%10) && e>0 ) e--,s/=10;
|
|
+ }
|
|
+
|
|
+ /* adjust the sign of significand */
|
|
+ s = sign<0 ? -s : s;
|
|
+
|
|
+ /* if exponent, scale significand as appropriate
|
|
+ ** and store in result. */
|
|
+ if( e ){
|
|
+ double scale = 1.0;
|
|
+ /* attempt to handle extremely small/large numbers better */
|
|
+ if( e>307 && e<342 ){
|
|
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
|
+ if( esign<0 ){
|
|
+ result = s / scale;
|
|
+ result /= 1.0e+308;
|
|
+ }else{
|
|
+ result = s * scale;
|
|
+ result *= 1.0e+308;
|
|
+ }
|
|
+ }else if( e>=342 ){
|
|
+ if( esign<0 ){
|
|
+ result = 0.0*s;
|
|
+ }else{
|
|
+ result = 1e308*1e308*s; /* Infinity */
|
|
+ }
|
|
+ }else{
|
|
+ /* 1.0e+22 is the largest power of 10 than can be
|
|
+ ** represented exactly. */
|
|
+ while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
|
+ while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
|
+ if( esign<0 ){
|
|
+ result = s / scale;
|
|
+ }else{
|
|
+ result = s * scale;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ result = (double)s;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* store the result */
|
|
+ *pResult = result;
|
|
+
|
|
+ /* return true if number and no extra non-whitespace chracters after */
|
|
+ return z>=zEnd && nDigits>0 && eValid && nonNum==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** tointeger(X): If X is any value (integer, double, blob, or string) that
|
|
+** can be losslessly converted into an integer, then make the conversion and
|
|
+** return the result. Otherwise, return NULL.
|
|
+*/
|
|
+static void tointegerFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ assert( argc==1 );
|
|
+ (void)argc;
|
|
+ switch( sqlite3_value_type(argv[0]) ){
|
|
+ case SQLITE_FLOAT: {
|
|
+ double rVal = sqlite3_value_double(argv[0]);
|
|
+ sqlite3_int64 iVal = (sqlite3_int64)rVal;
|
|
+ if( rVal==(double)iVal ){
|
|
+ sqlite3_result_int64(context, iVal);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INTEGER: {
|
|
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_BLOB: {
|
|
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
|
+ if( zBlob ){
|
|
+ int nBlob = sqlite3_value_bytes(argv[0]);
|
|
+ if( nBlob==sizeof(sqlite3_int64) ){
|
|
+ sqlite3_int64 iVal;
|
|
+ if( TOTYPE_BIGENDIAN ){
|
|
+ int i;
|
|
+ unsigned char zBlobRev[sizeof(sqlite3_int64)];
|
|
+ for(i=0; i<sizeof(sqlite3_int64); i++){
|
|
+ zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
|
|
+ }
|
|
+ memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
|
|
+ }else{
|
|
+ memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
|
|
+ }
|
|
+ sqlite3_result_int64(context, iVal);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
|
+ if( zStr ){
|
|
+ int nStr = sqlite3_value_bytes(argv[0]);
|
|
+ if( nStr && !totypeIsspace(zStr[0]) ){
|
|
+ sqlite3_int64 iVal;
|
|
+ if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
|
|
+ sqlite3_result_int64(context, iVal);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** toreal(X): If X is any value (integer, double, blob, or string) that can
|
|
+** be losslessly converted into a real number, then do so and return that
|
|
+** real number. Otherwise return NULL.
|
|
+*/
|
|
+#if defined(_MSC_VER)
|
|
+#pragma warning(disable: 4748)
|
|
+#pragma optimize("", off)
|
|
+#endif
|
|
+static void torealFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ assert( argc==1 );
|
|
+ (void)argc;
|
|
+ switch( sqlite3_value_type(argv[0]) ){
|
|
+ case SQLITE_FLOAT: {
|
|
+ sqlite3_result_double(context, sqlite3_value_double(argv[0]));
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INTEGER: {
|
|
+ sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
|
|
+ double rVal = (double)iVal;
|
|
+ if( iVal==(sqlite3_int64)rVal ){
|
|
+ sqlite3_result_double(context, rVal);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_BLOB: {
|
|
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
|
+ if( zBlob ){
|
|
+ int nBlob = sqlite3_value_bytes(argv[0]);
|
|
+ if( nBlob==sizeof(double) ){
|
|
+ double rVal;
|
|
+ if( TOTYPE_LITTLEENDIAN ){
|
|
+ int i;
|
|
+ unsigned char zBlobRev[sizeof(double)];
|
|
+ for(i=0; i<sizeof(double); i++){
|
|
+ zBlobRev[i] = zBlob[sizeof(double)-1-i];
|
|
+ }
|
|
+ memcpy(&rVal, zBlobRev, sizeof(double));
|
|
+ }else{
|
|
+ memcpy(&rVal, zBlob, sizeof(double));
|
|
+ }
|
|
+ sqlite3_result_double(context, rVal);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
|
+ if( zStr ){
|
|
+ int nStr = sqlite3_value_bytes(argv[0]);
|
|
+ if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
|
|
+ double rVal;
|
|
+ if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
|
|
+ sqlite3_result_double(context, rVal);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#if defined(_MSC_VER)
|
|
+#pragma optimize("", on)
|
|
+#pragma warning(default: 4748)
|
|
+#endif
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_totype_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
|
|
+ tointegerFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
|
|
+ torealFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
|
|
+ tointegerFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
|
|
+ torealFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/unionvtab.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/unionvtab.c 2019-05-14 16:22:31.277143900 +0200
|
|
@@ -0,0 +1,1397 @@
|
|
+/*
|
|
+** 2017 July 15
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file contains the implementation of the "unionvtab" and "swarmvtab"
|
|
+** virtual tables. These modules provide read-only access to multiple tables,
|
|
+** possibly in multiple database files, via a single database object.
|
|
+** The source tables must have the following characteristics:
|
|
+**
|
|
+** * They must all be rowid tables (not VIRTUAL or WITHOUT ROWID
|
|
+** tables or views).
|
|
+**
|
|
+** * Each table must have the same set of columns, declared in
|
|
+** the same order and with the same declared types.
|
|
+**
|
|
+** * The tables must not feature a user-defined column named "_rowid_".
|
|
+**
|
|
+** * Each table must contain a distinct range of rowid values.
|
|
+**
|
|
+** The difference between the two virtual table modules is that for
|
|
+** "unionvtab", all source tables must be located in the main database or
|
|
+** in databases ATTACHed to the main database by the user. For "swarmvtab",
|
|
+** the tables may be located in any database file on disk. The "swarmvtab"
|
|
+** implementation takes care of opening and closing database files
|
|
+** automatically.
|
|
+**
|
|
+** UNIONVTAB
|
|
+**
|
|
+** A "unionvtab" virtual table is created as follows:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql-statement>);
|
|
+**
|
|
+** The implementation evalutes <sql statement> whenever a unionvtab virtual
|
|
+** table is created or opened. It should return one row for each source
|
|
+** database table. The four columns required of each row are:
|
|
+**
|
|
+** 1. The name of the database containing the table ("main" or "temp" or
|
|
+** the name of an attached database). Or NULL to indicate that all
|
|
+** databases should be searched for the table in the usual fashion.
|
|
+**
|
|
+** 2. The name of the database table.
|
|
+**
|
|
+** 3. The smallest rowid in the range of rowids that may be stored in the
|
|
+** database table (an integer).
|
|
+**
|
|
+** 4. The largest rowid in the range of rowids that may be stored in the
|
|
+** database table (an integer).
|
|
+**
|
|
+** SWARMVTAB
|
|
+**
|
|
+** LEGACY SYNTAX:
|
|
+**
|
|
+** A "swarmvtab" virtual table is created similarly to a unionvtab table:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE <name>
|
|
+** USING swarmvtab(<sql-statement>, <callback>);
|
|
+**
|
|
+** The difference is that for a swarmvtab table, the first column returned
|
|
+** by the <sql statement> must return a path or URI that can be used to open
|
|
+** the database file containing the source table. The <callback> option
|
|
+** is optional. If included, it is the name of an application-defined
|
|
+** SQL function that is invoked with the URI of the file, if the file
|
|
+** does not already exist on disk when required by swarmvtab.
|
|
+**
|
|
+** NEW SYNTAX:
|
|
+**
|
|
+** Using the new syntax, a swarmvtab table is created with:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE <name> USING swarmvtab(
|
|
+** <sql-statement> [, <options>]
|
|
+** );
|
|
+**
|
|
+** where valid <options> are:
|
|
+**
|
|
+** missing=<udf-function-name>
|
|
+** openclose=<udf-function-name>
|
|
+** maxopen=<integer>
|
|
+** <sql-parameter>=<text-value>
|
|
+**
|
|
+** The <sql-statement> must return the same 4 columns as for a swarmvtab
|
|
+** table in legacy mode. However, it may also return a 5th column - the
|
|
+** "context" column. The text value returned in this column is not used
|
|
+** at all by the swarmvtab implementation, except that it is passed as
|
|
+** an additional argument to the two UDF functions that may be invoked
|
|
+** (see below).
|
|
+**
|
|
+** The "missing" option, if present, specifies the name of an SQL UDF
|
|
+** function to be invoked if a database file is not already present on
|
|
+** disk when required by swarmvtab. If the <sql-statement> did not provide
|
|
+** a context column, it is invoked as:
|
|
+**
|
|
+** SELECT <missing-udf>(<database filename/uri>);
|
|
+**
|
|
+** Or, if there was a context column:
|
|
+**
|
|
+** SELECT <missing-udf>(<database filename/uri>, <context>);
|
|
+**
|
|
+** The "openclose" option may also specify a UDF function. This function
|
|
+** is invoked right before swarmvtab opens a database, and right after
|
|
+** it closes one. The first argument - or first two arguments, if
|
|
+** <sql-statement> supplied the context column - is the same as for
|
|
+** the "missing" UDF. Following this, the UDF is passed integer value
|
|
+** 0 before a db is opened, and 1 right after it is closed. If both
|
|
+** a missing and openclose UDF is supplied, the application should expect
|
|
+** the following sequence of calls (for a single database):
|
|
+**
|
|
+** SELECT <openclose-udf>(<db filename>, <context>, 0);
|
|
+** if( db not already on disk ){
|
|
+** SELECT <missing-udf>(<db filename>, <context>);
|
|
+** }
|
|
+** ... swarmvtab uses database ...
|
|
+** SELECT <openclose-udf>(<db filename>, <context>, 1);
|
|
+**
|
|
+** The "maxopen" option is used to configure the maximum number of
|
|
+** database files swarmvtab will hold open simultaneously (default 9).
|
|
+**
|
|
+** If an option name begins with a ":" character, then it is assumed
|
|
+** to be an SQL parameter. In this case, the specified text value is
|
|
+** bound to the same variable of the <sql-statement> before it is
|
|
+** executed. It is an error of the named SQL parameter does not exist.
|
|
+** For example:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE swarm USING swarmvtab(
|
|
+** 'SELECT :path || localfile, tbl, min, max FROM swarmdir',
|
|
+** :path='/home/user/databases/'
|
|
+** missing='missing_func'
|
|
+** );
|
|
+*/
|
|
+
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/*
|
|
+** Largest and smallest possible 64-bit signed integers. These macros
|
|
+** copied from sqliteInt.h.
|
|
+*/
|
|
+#ifndef LARGEST_INT64
|
|
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
|
+#endif
|
|
+#ifndef SMALLEST_INT64
|
|
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The following is also copied from sqliteInt.h. To facilitate coverage
|
|
+** testing.
|
|
+*/
|
|
+#ifndef ALWAYS
|
|
+# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
|
+# define ALWAYS(X) (1)
|
|
+# define NEVER(X) (0)
|
|
+# elif !defined(NDEBUG)
|
|
+# define ALWAYS(X) ((X)?1:(assert(0),0))
|
|
+# define NEVER(X) ((X)?(assert(0),1):0)
|
|
+# else
|
|
+# define ALWAYS(X) (X)
|
|
+# define NEVER(X) (X)
|
|
+# endif
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The swarmvtab module attempts to keep the number of open database files
|
|
+** at or below this limit. This may not be possible if there are too many
|
|
+** simultaneous queries.
|
|
+*/
|
|
+#define SWARMVTAB_MAX_OPEN 9
|
|
+
|
|
+typedef struct UnionCsr UnionCsr;
|
|
+typedef struct UnionTab UnionTab;
|
|
+typedef struct UnionSrc UnionSrc;
|
|
+
|
|
+/*
|
|
+** Each source table (row returned by the initialization query) is
|
|
+** represented by an instance of the following structure stored in the
|
|
+** UnionTab.aSrc[] array.
|
|
+*/
|
|
+struct UnionSrc {
|
|
+ char *zDb; /* Database containing source table */
|
|
+ char *zTab; /* Source table name */
|
|
+ sqlite3_int64 iMin; /* Minimum rowid */
|
|
+ sqlite3_int64 iMax; /* Maximum rowid */
|
|
+
|
|
+ /* Fields used by swarmvtab only */
|
|
+ char *zFile; /* Database file containing table zTab */
|
|
+ char *zContext; /* Context string, if any */
|
|
+ int nUser; /* Current number of users */
|
|
+ sqlite3 *db; /* Database handle */
|
|
+ UnionSrc *pNextClosable; /* Next in list of closable sources */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Virtual table type for union vtab.
|
|
+*/
|
|
+struct UnionTab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3 *db; /* Database handle */
|
|
+ int bSwarm; /* 1 for "swarmvtab", 0 for "unionvtab" */
|
|
+ int iPK; /* INTEGER PRIMARY KEY column, or -1 */
|
|
+ int nSrc; /* Number of elements in the aSrc[] array */
|
|
+ UnionSrc *aSrc; /* Array of source tables, sorted by rowid */
|
|
+
|
|
+ /* Used by swarmvtab only */
|
|
+ int bHasContext; /* Has context strings */
|
|
+ char *zSourceStr; /* Expected unionSourceToStr() value */
|
|
+ sqlite3_stmt *pNotFound; /* UDF to invoke if file not found on open */
|
|
+ sqlite3_stmt *pOpenClose; /* UDF to invoke on open and close */
|
|
+
|
|
+ UnionSrc *pClosable; /* First in list of closable sources */
|
|
+ int nOpen; /* Current number of open sources */
|
|
+ int nMaxOpen; /* Maximum number of open sources */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Virtual table cursor type for union vtab.
|
|
+*/
|
|
+struct UnionCsr {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_stmt *pStmt; /* SQL statement to run */
|
|
+
|
|
+ /* Used by swarmvtab only */
|
|
+ sqlite3_int64 iMaxRowid; /* Last rowid to visit */
|
|
+ int iTab; /* Index of table read by pStmt */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Given UnionTab table pTab and UnionSrc object pSrc, return the database
|
|
+** handle that should be used to access the table identified by pSrc. This
|
|
+** is the main db handle for "unionvtab" tables, or the source-specific
|
|
+** handle for "swarmvtab".
|
|
+*/
|
|
+#define unionGetDb(pTab, pSrc) ((pTab)->bSwarm ? (pSrc)->db : (pTab)->db)
|
|
+
|
|
+/*
|
|
+** If *pRc is other than SQLITE_OK when this function is called, it
|
|
+** always returns NULL. Otherwise, it attempts to allocate and return
|
|
+** a pointer to nByte bytes of zeroed memory. If the memory allocation
|
|
+** is attempted but fails, NULL is returned and *pRc is set to
|
|
+** SQLITE_NOMEM.
|
|
+*/
|
|
+static void *unionMalloc(int *pRc, size_t nByte){
|
|
+ void *pRet;
|
|
+ assert( nByte>0 );
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ pRet = sqlite3_malloc(nByte);
|
|
+ if( pRet ){
|
|
+ memset(pRet, 0, (size_t)nByte);
|
|
+ }else{
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }
|
|
+ }else{
|
|
+ pRet = 0;
|
|
+ }
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If *pRc is other than SQLITE_OK when this function is called, it
|
|
+** always returns NULL. Otherwise, it attempts to allocate and return
|
|
+** a copy of the nul-terminated string passed as the second argument.
|
|
+** If the allocation is attempted but fails, NULL is returned and *pRc is
|
|
+** set to SQLITE_NOMEM.
|
|
+*/
|
|
+static char *unionStrdup(int *pRc, const char *zIn){
|
|
+ char *zRet = 0;
|
|
+ if( zIn ){
|
|
+ sqlite3_int64 nByte = strlen(zIn) + 1;
|
|
+ zRet = unionMalloc(pRc, nByte);
|
|
+ if( zRet ){
|
|
+ memcpy(zRet, zIn, (size_t)nByte);
|
|
+ }
|
|
+ }
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If the first character of the string passed as the only argument to this
|
|
+** function is one of the 4 that may be used as an open quote character
|
|
+** in SQL, this function assumes that the input is a well-formed quoted SQL
|
|
+** string. In this case the string is dequoted in place.
|
|
+**
|
|
+** If the first character of the input is not an open quote, then this
|
|
+** function is a no-op.
|
|
+*/
|
|
+static void unionDequote(char *z){
|
|
+ if( z ){
|
|
+ char q = z[0];
|
|
+
|
|
+ /* Set stack variable q to the close-quote character */
|
|
+ if( q=='[' || q=='\'' || q=='"' || q=='`' ){
|
|
+ int iIn = 1;
|
|
+ int iOut = 0;
|
|
+ if( q=='[' ) q = ']';
|
|
+ while( ALWAYS(z[iIn]) ){
|
|
+ if( z[iIn]==q ){
|
|
+ if( z[iIn+1]!=q ){
|
|
+ /* Character iIn was the close quote. */
|
|
+ iIn++;
|
|
+ break;
|
|
+ }else{
|
|
+ /* Character iIn and iIn+1 form an escaped quote character. Skip
|
|
+ ** the input cursor past both and copy a single quote character
|
|
+ ** to the output buffer. */
|
|
+ iIn += 2;
|
|
+ z[iOut++] = q;
|
|
+ }
|
|
+ }else{
|
|
+ z[iOut++] = z[iIn++];
|
|
+ }
|
|
+ }
|
|
+ z[iOut] = '\0';
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is a no-op if *pRc is set to other than SQLITE_OK when it
|
|
+** is called. NULL is returned in this case.
|
|
+**
|
|
+** Otherwise, the SQL statement passed as the third argument is prepared
|
|
+** against the database handle passed as the second. If the statement is
|
|
+** successfully prepared, a pointer to the new statement handle is
|
|
+** returned. It is the responsibility of the caller to eventually free the
|
|
+** statement by calling sqlite3_finalize(). Alternatively, if statement
|
|
+** compilation fails, NULL is returned, *pRc is set to an SQLite error
|
|
+** code and *pzErr may be set to an error message buffer allocated by
|
|
+** sqlite3_malloc().
|
|
+*/
|
|
+static sqlite3_stmt *unionPrepare(
|
|
+ int *pRc, /* IN/OUT: Error code */
|
|
+ sqlite3 *db, /* Database handle */
|
|
+ const char *zSql, /* SQL statement to prepare */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ sqlite3_stmt *pRet = 0;
|
|
+ assert( pzErr );
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ int rc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErr = sqlite3_mprintf("sql error: %s", sqlite3_errmsg(db));
|
|
+ *pRc = rc;
|
|
+ }
|
|
+ }
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Like unionPrepare(), except prepare the results of vprintf(zFmt, ...)
|
|
+** instead of a constant SQL string.
|
|
+*/
|
|
+static sqlite3_stmt *unionPreparePrintf(
|
|
+ int *pRc, /* IN/OUT: Error code */
|
|
+ char **pzErr, /* OUT: Error message */
|
|
+ sqlite3 *db, /* Database handle */
|
|
+ const char *zFmt, /* printf() format string */
|
|
+ ... /* Trailing printf args */
|
|
+){
|
|
+ sqlite3_stmt *pRet = 0;
|
|
+ char *zSql;
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+
|
|
+ zSql = sqlite3_vmprintf(zFmt, ap);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ if( zSql==0 ){
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ pRet = unionPrepare(pRc, db, zSql, pzErr);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+
|
|
+ va_end(ap);
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Call sqlite3_reset() on SQL statement pStmt. If *pRc is set to
|
|
+** SQLITE_OK when this function is called, then it is set to the
|
|
+** value returned by sqlite3_reset() before this function exits.
|
|
+** In this case, *pzErr may be set to point to an error message
|
|
+** buffer allocated by sqlite3_malloc().
|
|
+*/
|
|
+#if 0
|
|
+static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
|
+ int rc = sqlite3_reset(pStmt);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ *pRc = rc;
|
|
+ if( rc ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt)));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Call sqlite3_finalize() on SQL statement pStmt. If *pRc is set to
|
|
+** SQLITE_OK when this function is called, then it is set to the
|
|
+** value returned by sqlite3_finalize() before this function exits.
|
|
+*/
|
|
+static void unionFinalize(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
|
+ sqlite3 *db = sqlite3_db_handle(pStmt);
|
|
+ int rc = sqlite3_finalize(pStmt);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ *pRc = rc;
|
|
+ if( rc ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** If an "openclose" UDF was supplied when this virtual table was created,
|
|
+** invoke it now. The first argument passed is the name of the database
|
|
+** file for source pSrc. The second is integer value bClose.
|
|
+**
|
|
+** If successful, return SQLITE_OK. Otherwise an SQLite error code. In this
|
|
+** case if argument pzErr is not NULL, also set (*pzErr) to an English
|
|
+** language error message. The caller is responsible for eventually freeing
|
|
+** any error message using sqlite3_free().
|
|
+*/
|
|
+static int unionInvokeOpenClose(
|
|
+ UnionTab *pTab,
|
|
+ UnionSrc *pSrc,
|
|
+ int bClose,
|
|
+ char **pzErr
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( pTab->pOpenClose ){
|
|
+ sqlite3_bind_text(pTab->pOpenClose, 1, pSrc->zFile, -1, SQLITE_STATIC);
|
|
+ if( pTab->bHasContext ){
|
|
+ sqlite3_bind_text(pTab->pOpenClose, 2, pSrc->zContext, -1, SQLITE_STATIC);
|
|
+ }
|
|
+ sqlite3_bind_int(pTab->pOpenClose, 2+pTab->bHasContext, bClose);
|
|
+ sqlite3_step(pTab->pOpenClose);
|
|
+ if( SQLITE_OK!=(rc = sqlite3_reset(pTab->pOpenClose)) ){
|
|
+ if( pzErr ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is a no-op for unionvtab. For swarmvtab, it attempts to
|
|
+** close open database files until at most nMax are open. An SQLite error
|
|
+** code is returned if an error occurs, or SQLITE_OK otherwise.
|
|
+*/
|
|
+static void unionCloseSources(UnionTab *pTab, int nMax){
|
|
+ while( pTab->pClosable && pTab->nOpen>nMax ){
|
|
+ UnionSrc *p;
|
|
+ UnionSrc **pp;
|
|
+ for(pp=&pTab->pClosable; (*pp)->pNextClosable; pp=&(*pp)->pNextClosable);
|
|
+ p = *pp;
|
|
+ assert( p->db );
|
|
+ sqlite3_close(p->db);
|
|
+ p->db = 0;
|
|
+ *pp = 0;
|
|
+ pTab->nOpen--;
|
|
+ unionInvokeOpenClose(pTab, p, 1, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xDisconnect method.
|
|
+*/
|
|
+static int unionDisconnect(sqlite3_vtab *pVtab){
|
|
+ if( pVtab ){
|
|
+ UnionTab *pTab = (UnionTab*)pVtab;
|
|
+ int i;
|
|
+ for(i=0; i<pTab->nSrc; i++){
|
|
+ UnionSrc *pSrc = &pTab->aSrc[i];
|
|
+ int bHaveSrcDb = (pSrc->db!=0);
|
|
+ sqlite3_close(pSrc->db);
|
|
+ if( bHaveSrcDb ){
|
|
+ unionInvokeOpenClose(pTab, pSrc, 1, 0);
|
|
+ }
|
|
+ sqlite3_free(pSrc->zDb);
|
|
+ sqlite3_free(pSrc->zTab);
|
|
+ sqlite3_free(pSrc->zFile);
|
|
+ sqlite3_free(pSrc->zContext);
|
|
+ }
|
|
+ sqlite3_finalize(pTab->pNotFound);
|
|
+ sqlite3_finalize(pTab->pOpenClose);
|
|
+ sqlite3_free(pTab->zSourceStr);
|
|
+ sqlite3_free(pTab->aSrc);
|
|
+ sqlite3_free(pTab);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check that the table identified by pSrc is a rowid table. If not,
|
|
+** return SQLITE_ERROR and set (*pzErr) to point to an English language
|
|
+** error message. If the table is a rowid table and no error occurs,
|
|
+** return SQLITE_OK and leave (*pzErr) unmodified.
|
|
+*/
|
|
+static int unionIsIntkeyTable(
|
|
+ sqlite3 *db, /* Database handle */
|
|
+ UnionSrc *pSrc, /* Source table to test */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ int bPk = 0;
|
|
+ const char *zType = 0;
|
|
+ int rc;
|
|
+
|
|
+ sqlite3_table_column_metadata(
|
|
+ db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0
|
|
+ );
|
|
+ rc = sqlite3_errcode(db);
|
|
+ if( rc==SQLITE_ERROR
|
|
+ || (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType)))
|
|
+ ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ *pzErr = sqlite3_mprintf("no such rowid table: %s%s%s",
|
|
+ (pSrc->zDb ? pSrc->zDb : ""),
|
|
+ (pSrc->zDb ? "." : ""),
|
|
+ pSrc->zTab
|
|
+ );
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
|
+** called. In this case it returns NULL.
|
|
+**
|
|
+** Otherwise, this function checks that the source table passed as the
|
|
+** second argument (a) exists, (b) is not a view and (c) has a column
|
|
+** named "_rowid_" of type "integer" that is the primary key.
|
|
+** If this is not the case, *pRc is set to SQLITE_ERROR and NULL is
|
|
+** returned.
|
|
+**
|
|
+** Finally, if the source table passes the checks above, a nul-terminated
|
|
+** string describing the column names and types belonging to the source
|
|
+** table is returned. Tables with the same set of column names and types
|
|
+** cause this function to return identical strings. Is is the responsibility
|
|
+** of the caller to free the returned string using sqlite3_free() when
|
|
+** it is no longer required.
|
|
+*/
|
|
+static char *unionSourceToStr(
|
|
+ int *pRc, /* IN/OUT: Error code */
|
|
+ UnionTab *pTab, /* Virtual table object */
|
|
+ UnionSrc *pSrc, /* Source table to test */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ char *zRet = 0;
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ sqlite3 *db = unionGetDb(pTab, pSrc);
|
|
+ int rc = unionIsIntkeyTable(db, pSrc, pzErr);
|
|
+ sqlite3_stmt *pStmt = unionPrepare(&rc, db,
|
|
+ "SELECT group_concat(quote(name) || '.' || quote(type)) "
|
|
+ "FROM pragma_table_info(?, ?)", pzErr
|
|
+ );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC);
|
|
+ sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC);
|
|
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ const char *z = (const char*)sqlite3_column_text(pStmt, 0);
|
|
+ zRet = unionStrdup(&rc, z);
|
|
+ }
|
|
+ unionFinalize(&rc, pStmt, pzErr);
|
|
+ }
|
|
+ *pRc = rc;
|
|
+ }
|
|
+
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check that all configured source tables exist and have the same column
|
|
+** names and datatypes. If this is not the case, or if some other error
|
|
+** occurs, return an SQLite error code. In this case *pzErr may be set
|
|
+** to point to an error message buffer allocated by sqlite3_mprintf().
|
|
+** Or, if no problems regarding the source tables are detected and no
|
|
+** other error occurs, SQLITE_OK is returned.
|
|
+*/
|
|
+static int unionSourceCheck(UnionTab *pTab, char **pzErr){
|
|
+ int rc = SQLITE_OK;
|
|
+ char *z0 = 0;
|
|
+ int i;
|
|
+
|
|
+ assert( *pzErr==0 );
|
|
+ z0 = unionSourceToStr(&rc, pTab, &pTab->aSrc[0], pzErr);
|
|
+ for(i=1; i<pTab->nSrc; i++){
|
|
+ char *z = unionSourceToStr(&rc, pTab, &pTab->aSrc[i], pzErr);
|
|
+ if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){
|
|
+ *pzErr = sqlite3_mprintf("source table schema mismatch");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ sqlite3_free(z);
|
|
+ }
|
|
+ sqlite3_free(z0);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Try to open the swarmvtab database. If initially unable, invoke the
|
|
+** not-found callback UDF and then try again.
|
|
+*/
|
|
+static int unionOpenDatabaseInner(UnionTab *pTab, UnionSrc *pSrc, char **pzErr){
|
|
+ static const int openFlags = SQLITE_OPEN_READONLY | SQLITE_OPEN_URI;
|
|
+ int rc;
|
|
+
|
|
+ rc = unionInvokeOpenClose(pTab, pSrc, 0, pzErr);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+
|
|
+ rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
|
|
+ if( rc==SQLITE_OK ) return rc;
|
|
+ if( pTab->pNotFound ){
|
|
+ sqlite3_close(pSrc->db);
|
|
+ pSrc->db = 0;
|
|
+ sqlite3_bind_text(pTab->pNotFound, 1, pSrc->zFile, -1, SQLITE_STATIC);
|
|
+ if( pTab->bHasContext ){
|
|
+ sqlite3_bind_text(pTab->pNotFound, 2, pSrc->zContext, -1, SQLITE_STATIC);
|
|
+ }
|
|
+ sqlite3_step(pTab->pNotFound);
|
|
+ if( SQLITE_OK!=(rc = sqlite3_reset(pTab->pNotFound)) ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
|
|
+ return rc;
|
|
+ }
|
|
+ rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pSrc->db));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function may only be called for swarmvtab tables. The results of
|
|
+** calling it on a unionvtab table are undefined.
|
|
+**
|
|
+** For a swarmvtab table, this function ensures that source database iSrc
|
|
+** is open. If the database is opened successfully and the schema is as
|
|
+** expected, or if it is already open when this function is called, SQLITE_OK
|
|
+** is returned.
|
|
+**
|
|
+** Alternatively If an error occurs while opening the databases, or if the
|
|
+** database schema is unsuitable, an SQLite error code is returned and (*pzErr)
|
|
+** may be set to point to an English language error message. In this case it is
|
|
+** the responsibility of the caller to eventually free the error message buffer
|
|
+** using sqlite3_free().
|
|
+*/
|
|
+static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){
|
|
+ int rc = SQLITE_OK;
|
|
+ UnionSrc *pSrc = &pTab->aSrc[iSrc];
|
|
+
|
|
+ assert( pTab->bSwarm && iSrc<pTab->nSrc );
|
|
+ if( pSrc->db==0 ){
|
|
+ unionCloseSources(pTab, pTab->nMaxOpen-1);
|
|
+ rc = unionOpenDatabaseInner(pTab, pSrc, pzErr);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ char *z = unionSourceToStr(&rc, pTab, pSrc, pzErr);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pTab->zSourceStr==0 ){
|
|
+ pTab->zSourceStr = z;
|
|
+ }else{
|
|
+ if( sqlite3_stricmp(z, pTab->zSourceStr) ){
|
|
+ *pzErr = sqlite3_mprintf("source table schema mismatch");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ sqlite3_free(z);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pSrc->pNextClosable = pTab->pClosable;
|
|
+ pTab->pClosable = pSrc;
|
|
+ pTab->nOpen++;
|
|
+ }else{
|
|
+ sqlite3_close(pSrc->db);
|
|
+ pSrc->db = 0;
|
|
+ unionInvokeOpenClose(pTab, pSrc, 1, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** This function is a no-op for unionvtab tables. For swarmvtab, increment
|
|
+** the reference count for source table iTab. If the reference count was
|
|
+** zero before it was incremented, also remove the source from the closable
|
|
+** list.
|
|
+*/
|
|
+static void unionIncrRefcount(UnionTab *pTab, int iTab){
|
|
+ if( pTab->bSwarm ){
|
|
+ UnionSrc *pSrc = &pTab->aSrc[iTab];
|
|
+ assert( pSrc->nUser>=0 && pSrc->db );
|
|
+ if( pSrc->nUser==0 ){
|
|
+ UnionSrc **pp;
|
|
+ for(pp=&pTab->pClosable; *pp!=pSrc; pp=&(*pp)->pNextClosable);
|
|
+ *pp = pSrc->pNextClosable;
|
|
+ pSrc->pNextClosable = 0;
|
|
+ }
|
|
+ pSrc->nUser++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Finalize the SQL statement pCsr->pStmt and return the result.
|
|
+**
|
|
+** If this is a swarmvtab table (not unionvtab) and pCsr->pStmt was not
|
|
+** NULL when this function was called, also decrement the reference
|
|
+** count on the associated source table. If this means the source tables
|
|
+** refcount is now zero, add it to the closable list.
|
|
+*/
|
|
+static int unionFinalizeCsrStmt(UnionCsr *pCsr){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( pCsr->pStmt ){
|
|
+ UnionTab *pTab = (UnionTab*)pCsr->base.pVtab;
|
|
+ UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab];
|
|
+ rc = sqlite3_finalize(pCsr->pStmt);
|
|
+ pCsr->pStmt = 0;
|
|
+ if( pTab->bSwarm ){
|
|
+ pSrc->nUser--;
|
|
+ assert( pSrc->nUser>=0 );
|
|
+ if( pSrc->nUser==0 ){
|
|
+ pSrc->pNextClosable = pTab->pClosable;
|
|
+ pTab->pClosable = pSrc;
|
|
+ }
|
|
+ unionCloseSources(pTab, pTab->nMaxOpen);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true if the argument is a space, tab, CR or LF character.
|
|
+*/
|
|
+static int union_isspace(char c){
|
|
+ return (c==' ' || c=='\n' || c=='\r' || c=='\t');
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true if the argument is an alphanumeric character in the
|
|
+** ASCII range.
|
|
+*/
|
|
+static int union_isidchar(char c){
|
|
+ return ((c>='a' && c<='z') || (c>='A' && c<'Z') || (c>='0' && c<='9'));
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called to handle all arguments following the first
|
|
+** (the SQL statement) passed to a swarmvtab (not unionvtab) CREATE
|
|
+** VIRTUAL TABLE statement. It may bind parameters to the SQL statement
|
|
+** or configure members of the UnionTab object passed as the second
|
|
+** argument.
|
|
+**
|
|
+** Refer to header comments at the top of this file for a description
|
|
+** of the arguments parsed.
|
|
+**
|
|
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
|
+** called. Otherwise, if an error occurs, *pRc is set to an SQLite error
|
|
+** code. In this case *pzErr may be set to point to a buffer containing
|
|
+** an English language error message. It is the responsibility of the
|
|
+** caller to eventually free the buffer using sqlite3_free().
|
|
+*/
|
|
+static void unionConfigureVtab(
|
|
+ int *pRc, /* IN/OUT: Error code */
|
|
+ UnionTab *pTab, /* Table to configure */
|
|
+ sqlite3_stmt *pStmt, /* SQL statement to find sources */
|
|
+ int nArg, /* Number of entries in azArg[] array */
|
|
+ const char * const *azArg, /* Array of arguments to consider */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ int rc = *pRc;
|
|
+ int i;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pTab->bHasContext = (sqlite3_column_count(pStmt)>4);
|
|
+ }
|
|
+ for(i=0; rc==SQLITE_OK && i<nArg; i++){
|
|
+ char *zArg = unionStrdup(&rc, azArg[i]);
|
|
+ if( zArg ){
|
|
+ int nOpt = 0; /* Size of option name in bytes */
|
|
+ char *zOpt; /* Pointer to option name */
|
|
+ char *zVal; /* Pointer to value */
|
|
+
|
|
+ unionDequote(zArg);
|
|
+ zOpt = zArg;
|
|
+ while( union_isspace(*zOpt) ) zOpt++;
|
|
+ zVal = zOpt;
|
|
+ if( *zVal==':' ) zVal++;
|
|
+ while( union_isidchar(*zVal) ) zVal++;
|
|
+ nOpt = (int)(zVal-zOpt);
|
|
+
|
|
+ while( union_isspace(*zVal) ) zVal++;
|
|
+ if( *zVal=='=' ){
|
|
+ zOpt[nOpt] = '\0';
|
|
+ zVal++;
|
|
+ while( union_isspace(*zVal) ) zVal++;
|
|
+ zVal = unionStrdup(&rc, zVal);
|
|
+ if( zVal ){
|
|
+ unionDequote(zVal);
|
|
+ if( zOpt[0]==':' ){
|
|
+ /* A value to bind to the SQL statement */
|
|
+ int iParam = sqlite3_bind_parameter_index(pStmt, zOpt);
|
|
+ if( iParam==0 ){
|
|
+ *pzErr = sqlite3_mprintf(
|
|
+ "swarmvtab: no such SQL parameter: %s", zOpt
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ rc = sqlite3_bind_text(pStmt, iParam, zVal, -1, SQLITE_TRANSIENT);
|
|
+ }
|
|
+ }else if( nOpt==7 && 0==sqlite3_strnicmp(zOpt, "maxopen", 7) ){
|
|
+ pTab->nMaxOpen = atoi(zVal);
|
|
+ if( pTab->nMaxOpen<=0 ){
|
|
+ *pzErr = sqlite3_mprintf("swarmvtab: illegal maxopen value");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }else if( nOpt==7 && 0==sqlite3_strnicmp(zOpt, "missing", 7) ){
|
|
+ if( pTab->pNotFound ){
|
|
+ *pzErr = sqlite3_mprintf(
|
|
+ "swarmvtab: duplicate \"missing\" option");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ pTab->pNotFound = unionPreparePrintf(&rc, pzErr, pTab->db,
|
|
+ "SELECT \"%w\"(?%s)", zVal, pTab->bHasContext ? ",?" : ""
|
|
+ );
|
|
+ }
|
|
+ }else if( nOpt==9 && 0==sqlite3_strnicmp(zOpt, "openclose", 9) ){
|
|
+ if( pTab->pOpenClose ){
|
|
+ *pzErr = sqlite3_mprintf(
|
|
+ "swarmvtab: duplicate \"openclose\" option");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ pTab->pOpenClose = unionPreparePrintf(&rc, pzErr, pTab->db,
|
|
+ "SELECT \"%w\"(?,?%s)", zVal, pTab->bHasContext ? ",?" : ""
|
|
+ );
|
|
+ }
|
|
+ }else{
|
|
+ *pzErr = sqlite3_mprintf("swarmvtab: unrecognized option: %s",zOpt);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ sqlite3_free(zVal);
|
|
+ }
|
|
+ }else{
|
|
+ if( i==0 && nArg==1 ){
|
|
+ pTab->pNotFound = unionPreparePrintf(&rc, pzErr, pTab->db,
|
|
+ "SELECT \"%w\"(?)", zArg
|
|
+ );
|
|
+ }else{
|
|
+ *pzErr = sqlite3_mprintf( "swarmvtab: parse error: %s", azArg[i]);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zArg);
|
|
+ }
|
|
+ }
|
|
+ *pRc = rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xConnect/xCreate method.
|
|
+**
|
|
+** The argv[] array contains the following:
|
|
+**
|
|
+** argv[0] -> module name ("unionvtab" or "swarmvtab")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[3] -> SQL statement
|
|
+** argv[4] -> not-found callback UDF name
|
|
+*/
|
|
+static int unionConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ UnionTab *pTab = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ int bSwarm = (pAux==0 ? 0 : 1);
|
|
+ const char *zVtab = (bSwarm ? "swarmvtab" : "unionvtab");
|
|
+
|
|
+ if( sqlite3_stricmp("temp", argv[1]) ){
|
|
+ /* unionvtab tables may only be created in the temp schema */
|
|
+ *pzErr = sqlite3_mprintf("%s tables must be created in TEMP schema", zVtab);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else if( argc<4 || (argc>4 && bSwarm==0) ){
|
|
+ *pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ int nAlloc = 0; /* Allocated size of pTab->aSrc[] */
|
|
+ sqlite3_stmt *pStmt = 0; /* Argument statement */
|
|
+ char *zArg = unionStrdup(&rc, argv[3]); /* Copy of argument to CVT */
|
|
+
|
|
+ /* Prepare the SQL statement. Instead of executing it directly, sort
|
|
+ ** the results by the "minimum rowid" field. This makes it easier to
|
|
+ ** check that there are no rowid range overlaps between source tables
|
|
+ ** and that the UnionTab.aSrc[] array is always sorted by rowid. */
|
|
+ unionDequote(zArg);
|
|
+ pStmt = unionPreparePrintf(&rc, pzErr, db,
|
|
+ "SELECT * FROM (%z) ORDER BY 3", zArg
|
|
+ );
|
|
+
|
|
+ /* Allocate the UnionTab structure */
|
|
+ pTab = unionMalloc(&rc, sizeof(UnionTab));
|
|
+ if( pTab ){
|
|
+ assert( rc==SQLITE_OK );
|
|
+ pTab->db = db;
|
|
+ pTab->bSwarm = bSwarm;
|
|
+ pTab->nMaxOpen = SWARMVTAB_MAX_OPEN;
|
|
+ }
|
|
+
|
|
+ /* Parse other CVT arguments, if any */
|
|
+ if( bSwarm ){
|
|
+ unionConfigureVtab(&rc, pTab, pStmt, argc-4, &argv[4], pzErr);
|
|
+ }
|
|
+
|
|
+ /* Iterate through the rows returned by the SQL statement specified
|
|
+ ** as an argument to the CREATE VIRTUAL TABLE statement. */
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ const char *zDb = (const char*)sqlite3_column_text(pStmt, 0);
|
|
+ const char *zTab = (const char*)sqlite3_column_text(pStmt, 1);
|
|
+ sqlite3_int64 iMin = sqlite3_column_int64(pStmt, 2);
|
|
+ sqlite3_int64 iMax = sqlite3_column_int64(pStmt, 3);
|
|
+ UnionSrc *pSrc;
|
|
+
|
|
+ /* Grow the pTab->aSrc[] array if required. */
|
|
+ if( nAlloc<=pTab->nSrc ){
|
|
+ int nNew = nAlloc ? nAlloc*2 : 8;
|
|
+ UnionSrc *aNew = (UnionSrc*)sqlite3_realloc64(
|
|
+ pTab->aSrc, nNew*sizeof(UnionSrc)
|
|
+ );
|
|
+ if( aNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }else{
|
|
+ memset(&aNew[pTab->nSrc], 0, (nNew-pTab->nSrc)*sizeof(UnionSrc));
|
|
+ pTab->aSrc = aNew;
|
|
+ nAlloc = nNew;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check for problems with the specified range of rowids */
|
|
+ if( iMax<iMin || (pTab->nSrc>0 && iMin<=pTab->aSrc[pTab->nSrc-1].iMax) ){
|
|
+ *pzErr = sqlite3_mprintf("rowid range mismatch error");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pSrc = &pTab->aSrc[pTab->nSrc++];
|
|
+ pSrc->zTab = unionStrdup(&rc, zTab);
|
|
+ pSrc->iMin = iMin;
|
|
+ pSrc->iMax = iMax;
|
|
+ if( bSwarm ){
|
|
+ pSrc->zFile = unionStrdup(&rc, zDb);
|
|
+ }else{
|
|
+ pSrc->zDb = unionStrdup(&rc, zDb);
|
|
+ }
|
|
+ if( pTab->bHasContext ){
|
|
+ const char *zContext = (const char*)sqlite3_column_text(pStmt, 4);
|
|
+ pSrc->zContext = unionStrdup(&rc, zContext);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ unionFinalize(&rc, pStmt, pzErr);
|
|
+ pStmt = 0;
|
|
+
|
|
+ /* It is an error if the SELECT statement returned zero rows. If only
|
|
+ ** because there is no way to determine the schema of the virtual
|
|
+ ** table in this case. */
|
|
+ if( rc==SQLITE_OK && pTab->nSrc==0 ){
|
|
+ *pzErr = sqlite3_mprintf("no source tables configured");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ /* For unionvtab, verify that all source tables exist and have
|
|
+ ** compatible schemas. For swarmvtab, attach the first database and
|
|
+ ** check that the first table is a rowid table only. */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( bSwarm ){
|
|
+ rc = unionOpenDatabase(pTab, 0, pzErr);
|
|
+ }else{
|
|
+ rc = unionSourceCheck(pTab, pzErr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Compose a CREATE TABLE statement and pass it to declare_vtab() */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ UnionSrc *pSrc = &pTab->aSrc[0];
|
|
+ sqlite3 *tdb = unionGetDb(pTab, pSrc);
|
|
+ pStmt = unionPreparePrintf(&rc, pzErr, tdb, "SELECT "
|
|
+ "'CREATE TABLE xyz('"
|
|
+ " || group_concat(quote(name) || ' ' || type, ', ')"
|
|
+ " || ')',"
|
|
+ "max((cid+1) * (type='INTEGER' COLLATE nocase AND pk=1))-1 "
|
|
+ "FROM pragma_table_info(%Q, ?)",
|
|
+ pSrc->zTab, pSrc->zDb
|
|
+ );
|
|
+ }
|
|
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ const char *zDecl = (const char*)sqlite3_column_text(pStmt, 0);
|
|
+ rc = sqlite3_declare_vtab(db, zDecl);
|
|
+ pTab->iPK = sqlite3_column_int(pStmt, 1);
|
|
+ }
|
|
+
|
|
+ unionFinalize(&rc, pStmt, pzErr);
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ unionDisconnect((sqlite3_vtab*)pTab);
|
|
+ pTab = 0;
|
|
+ }
|
|
+
|
|
+ *ppVtab = (sqlite3_vtab*)pTab;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xOpen
|
|
+*/
|
|
+static int unionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ UnionCsr *pCsr;
|
|
+ int rc = SQLITE_OK;
|
|
+ (void)p; /* Suppress harmless warning */
|
|
+ pCsr = (UnionCsr*)unionMalloc(&rc, sizeof(UnionCsr));
|
|
+ *ppCursor = &pCsr->base;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xClose
|
|
+*/
|
|
+static int unionClose(sqlite3_vtab_cursor *cur){
|
|
+ UnionCsr *pCsr = (UnionCsr*)cur;
|
|
+ unionFinalizeCsrStmt(pCsr);
|
|
+ sqlite3_free(pCsr);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function does the work of the xNext() method. Except that, if it
|
|
+** returns SQLITE_ROW, it should be called again within the same xNext()
|
|
+** method call. See unionNext() for details.
|
|
+*/
|
|
+static int doUnionNext(UnionCsr *pCsr){
|
|
+ int rc = SQLITE_OK;
|
|
+ assert( pCsr->pStmt );
|
|
+ if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
|
|
+ UnionTab *pTab = (UnionTab*)pCsr->base.pVtab;
|
|
+ rc = unionFinalizeCsrStmt(pCsr);
|
|
+ if( rc==SQLITE_OK && pTab->bSwarm ){
|
|
+ pCsr->iTab++;
|
|
+ if( pCsr->iTab<pTab->nSrc ){
|
|
+ UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab];
|
|
+ if( pCsr->iMaxRowid>=pSrc->iMin ){
|
|
+ /* It is necessary to scan the next table. */
|
|
+ rc = unionOpenDatabase(pTab, pCsr->iTab, &pTab->base.zErrMsg);
|
|
+ pCsr->pStmt = unionPreparePrintf(&rc, &pTab->base.zErrMsg, pSrc->db,
|
|
+ "SELECT rowid, * FROM %Q %s %lld",
|
|
+ pSrc->zTab,
|
|
+ (pSrc->iMax>pCsr->iMaxRowid ? "WHERE _rowid_ <=" : "-- "),
|
|
+ pCsr->iMaxRowid
|
|
+ );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ assert( pCsr->pStmt );
|
|
+ unionIncrRefcount(pTab, pCsr->iTab);
|
|
+ rc = SQLITE_ROW;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xNext
|
|
+*/
|
|
+static int unionNext(sqlite3_vtab_cursor *cur){
|
|
+ int rc;
|
|
+ do {
|
|
+ rc = doUnionNext((UnionCsr*)cur);
|
|
+ }while( rc==SQLITE_ROW );
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xColumn
|
|
+*/
|
|
+static int unionColumn(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ sqlite3_context *ctx,
|
|
+ int i
|
|
+){
|
|
+ UnionCsr *pCsr = (UnionCsr*)cur;
|
|
+ sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xRowid
|
|
+*/
|
|
+static int unionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ UnionCsr *pCsr = (UnionCsr*)cur;
|
|
+ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xEof
|
|
+*/
|
|
+static int unionEof(sqlite3_vtab_cursor *cur){
|
|
+ UnionCsr *pCsr = (UnionCsr*)cur;
|
|
+ return pCsr->pStmt==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFilter
|
|
+*/
|
|
+static int unionFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ UnionTab *pTab = (UnionTab*)(pVtabCursor->pVtab);
|
|
+ UnionCsr *pCsr = (UnionCsr*)pVtabCursor;
|
|
+ int rc = SQLITE_OK;
|
|
+ int i;
|
|
+ char *zSql = 0;
|
|
+ int bZero = 0;
|
|
+
|
|
+ sqlite3_int64 iMin = SMALLEST_INT64;
|
|
+ sqlite3_int64 iMax = LARGEST_INT64;
|
|
+
|
|
+ assert( idxNum==0
|
|
+ || idxNum==SQLITE_INDEX_CONSTRAINT_EQ
|
|
+ || idxNum==SQLITE_INDEX_CONSTRAINT_LE
|
|
+ || idxNum==SQLITE_INDEX_CONSTRAINT_GE
|
|
+ || idxNum==SQLITE_INDEX_CONSTRAINT_LT
|
|
+ || idxNum==SQLITE_INDEX_CONSTRAINT_GT
|
|
+ || idxNum==(SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_LE)
|
|
+ );
|
|
+
|
|
+ (void)idxStr; /* Suppress harmless warning */
|
|
+
|
|
+ if( idxNum==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ assert( argc==1 );
|
|
+ iMin = iMax = sqlite3_value_int64(argv[0]);
|
|
+ }else{
|
|
+
|
|
+ if( idxNum & (SQLITE_INDEX_CONSTRAINT_LE|SQLITE_INDEX_CONSTRAINT_LT) ){
|
|
+ assert( argc>=1 );
|
|
+ iMax = sqlite3_value_int64(argv[0]);
|
|
+ if( idxNum & SQLITE_INDEX_CONSTRAINT_LT ){
|
|
+ if( iMax==SMALLEST_INT64 ){
|
|
+ bZero = 1;
|
|
+ }else{
|
|
+ iMax--;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( idxNum & (SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_GT) ){
|
|
+ assert( argc>=1 );
|
|
+ iMin = sqlite3_value_int64(argv[argc-1]);
|
|
+ if( idxNum & SQLITE_INDEX_CONSTRAINT_GT ){
|
|
+ if( iMin==LARGEST_INT64 ){
|
|
+ bZero = 1;
|
|
+ }else{
|
|
+ iMin++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ unionFinalizeCsrStmt(pCsr);
|
|
+ if( bZero ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+
|
|
+ for(i=0; i<pTab->nSrc; i++){
|
|
+ UnionSrc *pSrc = &pTab->aSrc[i];
|
|
+ if( iMin>pSrc->iMax || iMax<pSrc->iMin ){
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ zSql = sqlite3_mprintf("%z%sSELECT rowid, * FROM %s%q%s%Q"
|
|
+ , zSql
|
|
+ , (zSql ? " UNION ALL " : "")
|
|
+ , (pSrc->zDb ? "'" : "")
|
|
+ , (pSrc->zDb ? pSrc->zDb : "")
|
|
+ , (pSrc->zDb ? "'." : "")
|
|
+ , pSrc->zTab
|
|
+ );
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if( iMin==iMax ){
|
|
+ zSql = sqlite3_mprintf("%z WHERE rowid=%lld", zSql, iMin);
|
|
+ }else{
|
|
+ const char *zWhere = "WHERE";
|
|
+ if( iMin!=SMALLEST_INT64 && iMin>pSrc->iMin ){
|
|
+ zSql = sqlite3_mprintf("%z WHERE rowid>=%lld", zSql, iMin);
|
|
+ zWhere = "AND";
|
|
+ }
|
|
+ if( iMax!=LARGEST_INT64 && iMax<pSrc->iMax ){
|
|
+ zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( pTab->bSwarm ){
|
|
+ pCsr->iTab = i;
|
|
+ pCsr->iMaxRowid = iMax;
|
|
+ rc = unionOpenDatabase(pTab, i, &pTab->base.zErrMsg);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( zSql==0 ){
|
|
+ return rc;
|
|
+ }else{
|
|
+ sqlite3 *db = unionGetDb(pTab, &pTab->aSrc[pCsr->iTab]);
|
|
+ pCsr->pStmt = unionPrepare(&rc, db, zSql, &pTab->base.zErrMsg);
|
|
+ if( pCsr->pStmt ){
|
|
+ unionIncrRefcount(pTab, pCsr->iTab);
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ return unionNext(pVtabCursor);
|
|
+}
|
|
+
|
|
+/*
|
|
+** xBestIndex.
|
|
+**
|
|
+** This implementation searches for constraints on the rowid field. EQ,
|
|
+** LE, LT, GE and GT are handled.
|
|
+**
|
|
+** If there is an EQ comparison, then idxNum is set to INDEX_CONSTRAINT_EQ.
|
|
+** In this case the only argument passed to xFilter is the rhs of the ==
|
|
+** operator.
|
|
+**
|
|
+** Otherwise, if an LE or LT constraint is found, then the INDEX_CONSTRAINT_LE
|
|
+** or INDEX_CONSTRAINT_LT (but not both) bit is set in idxNum. The first
|
|
+** argument to xFilter is the rhs of the <= or < operator. Similarly, if
|
|
+** an GE or GT constraint is found, then the INDEX_CONSTRAINT_GE or
|
|
+** INDEX_CONSTRAINT_GT bit is set in idxNum. The rhs of the >= or > operator
|
|
+** is passed as either the first or second argument to xFilter, depending
|
|
+** on whether or not there is also a LT|LE constraint.
|
|
+*/
|
|
+static int unionBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ UnionTab *pTab = (UnionTab*)tab;
|
|
+ int iEq = -1;
|
|
+ int iLt = -1;
|
|
+ int iGt = -1;
|
|
+ int i;
|
|
+
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
|
+ if( p->usable && (p->iColumn<0 || p->iColumn==pTab->iPK) ){
|
|
+ switch( p->op ){
|
|
+ case SQLITE_INDEX_CONSTRAINT_EQ:
|
|
+ iEq = i;
|
|
+ break;
|
|
+ case SQLITE_INDEX_CONSTRAINT_LE:
|
|
+ case SQLITE_INDEX_CONSTRAINT_LT:
|
|
+ iLt = i;
|
|
+ break;
|
|
+ case SQLITE_INDEX_CONSTRAINT_GE:
|
|
+ case SQLITE_INDEX_CONSTRAINT_GT:
|
|
+ iGt = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( iEq>=0 ){
|
|
+ pIdxInfo->estimatedRows = 1;
|
|
+ pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
|
+ pIdxInfo->estimatedCost = 3.0;
|
|
+ pIdxInfo->idxNum = SQLITE_INDEX_CONSTRAINT_EQ;
|
|
+ pIdxInfo->aConstraintUsage[iEq].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[iEq].omit = 1;
|
|
+ }else{
|
|
+ int iCons = 1;
|
|
+ int idxNum = 0;
|
|
+ sqlite3_int64 nRow = 1000000;
|
|
+ if( iLt>=0 ){
|
|
+ nRow = nRow / 2;
|
|
+ pIdxInfo->aConstraintUsage[iLt].argvIndex = iCons++;
|
|
+ pIdxInfo->aConstraintUsage[iLt].omit = 1;
|
|
+ idxNum |= pIdxInfo->aConstraint[iLt].op;
|
|
+ }
|
|
+ if( iGt>=0 ){
|
|
+ nRow = nRow / 2;
|
|
+ pIdxInfo->aConstraintUsage[iGt].argvIndex = iCons++;
|
|
+ pIdxInfo->aConstraintUsage[iGt].omit = 1;
|
|
+ idxNum |= pIdxInfo->aConstraint[iGt].op;
|
|
+ }
|
|
+ pIdxInfo->estimatedRows = nRow;
|
|
+ pIdxInfo->estimatedCost = 3.0 * (double)nRow;
|
|
+ pIdxInfo->idxNum = idxNum;
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register the unionvtab virtual table module with database handle db.
|
|
+*/
|
|
+static int createUnionVtab(sqlite3 *db){
|
|
+ static const sqlite3_module unionModule = {
|
|
+ 0, /* iVersion */
|
|
+ unionConnect,
|
|
+ unionConnect,
|
|
+ unionBestIndex, /* xBestIndex - query planner */
|
|
+ unionDisconnect,
|
|
+ unionDisconnect,
|
|
+ unionOpen, /* xOpen - open a cursor */
|
|
+ unionClose, /* xClose - close a cursor */
|
|
+ unionFilter, /* xFilter - configure scan constraints */
|
|
+ unionNext, /* xNext - advance a cursor */
|
|
+ unionEof, /* xEof - check for end of scan */
|
|
+ unionColumn, /* xColumn - read data */
|
|
+ unionRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+ };
|
|
+ int rc;
|
|
+
|
|
+ rc = sqlite3_create_module(db, "unionvtab", &unionModule, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_module(db, "swarmvtab", &unionModule, (void*)db);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_unionvtab_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Suppress harmless warning */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = createUnionVtab(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Suppress harmless warning */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = createUnionVtab(db);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/vfslog.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/vfslog.c 2019-05-14 16:22:31.279096600 +0200
|
|
@@ -0,0 +1,854 @@
|
|
+/*
|
|
+** 2013-10-09
|
|
+**
|
|
+** 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 the implementation of an SQLite vfs wrapper for
|
|
+** unix that generates per-database log files of all disk activity.
|
|
+*/
|
|
+
|
|
+/*
|
|
+** This module contains code for a wrapper VFS that causes a log of
|
|
+** most VFS calls to be written into a file on disk.
|
|
+**
|
|
+** Each database connection creates a separate log file in the same
|
|
+** directory as the original database and named after the original
|
|
+** database. A unique suffix is added to avoid name collisions.
|
|
+** Separate log files are used so that concurrent processes do not
|
|
+** try to write log operations to the same file at the same instant,
|
|
+** resulting in overwritten or comingled log text.
|
|
+**
|
|
+** Each individual log file records operations by a single database
|
|
+** connection on both the original database and its associated rollback
|
|
+** journal.
|
|
+**
|
|
+** The log files are in the comma-separated-value (CSV) format. The
|
|
+** log files can be imported into an SQLite database using the ".import"
|
|
+** command of the SQLite command-line shell for analysis.
|
|
+**
|
|
+** One technique for using this module is to append the text of this
|
|
+** module to the end of a standard "sqlite3.c" amalgamation file then
|
|
+** add the following compile-time options:
|
|
+**
|
|
+** -DSQLITE_EXTRA_INIT=sqlite3_register_vfslog
|
|
+** -DSQLITE_USE_FCNTL_TRACE
|
|
+**
|
|
+** The first compile-time option causes the sqlite3_register_vfslog()
|
|
+** function, defined below, to be invoked when SQLite is initialized.
|
|
+** That causes this custom VFS to become the default VFS for all
|
|
+** subsequent connections. The SQLITE_USE_FCNTL_TRACE option causes
|
|
+** the SQLite core to issue extra sqlite3_file_control() operations
|
|
+** with SQLITE_FCNTL_TRACE to give some indication of what is going
|
|
+** on in the core.
|
|
+*/
|
|
+
|
|
+#include "sqlite3.h"
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include <stdio.h>
|
|
+#if SQLITE_OS_UNIX
|
|
+# include <unistd.h>
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this utility
|
|
+*/
|
|
+typedef struct VLogLog VLogLog;
|
|
+typedef struct VLogVfs VLogVfs;
|
|
+typedef struct VLogFile VLogFile;
|
|
+
|
|
+/* There is a pair (an array of size 2) of the following objects for
|
|
+** each database file being logged. The first contains the filename
|
|
+** and is used to log I/O with the main database. The second has
|
|
+** a NULL filename and is used to log I/O for the journal. Both
|
|
+** out pointers are the same.
|
|
+*/
|
|
+struct VLogLog {
|
|
+ VLogLog *pNext; /* Next in a list of all active logs */
|
|
+ VLogLog **ppPrev; /* Pointer to this in the list */
|
|
+ int nRef; /* Number of references to this object */
|
|
+ int nFilename; /* Length of zFilename in bytes */
|
|
+ char *zFilename; /* Name of database file. NULL for journal */
|
|
+ FILE *out; /* Write information here */
|
|
+};
|
|
+
|
|
+struct VLogVfs {
|
|
+ sqlite3_vfs base; /* VFS methods */
|
|
+ sqlite3_vfs *pVfs; /* Parent VFS */
|
|
+};
|
|
+
|
|
+struct VLogFile {
|
|
+ sqlite3_file base; /* IO methods */
|
|
+ sqlite3_file *pReal; /* Underlying file handle */
|
|
+ VLogLog *pLog; /* The log file for this file */
|
|
+};
|
|
+
|
|
+#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
|
|
+
|
|
+/*
|
|
+** Methods for VLogFile
|
|
+*/
|
|
+static int vlogClose(sqlite3_file*);
|
|
+static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
+static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
+static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
+static int vlogSync(sqlite3_file*, int flags);
|
|
+static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
+static int vlogLock(sqlite3_file*, int);
|
|
+static int vlogUnlock(sqlite3_file*, int);
|
|
+static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
|
|
+static int vlogFileControl(sqlite3_file*, int op, void *pArg);
|
|
+static int vlogSectorSize(sqlite3_file*);
|
|
+static int vlogDeviceCharacteristics(sqlite3_file*);
|
|
+static int vlogShmMap(sqlite3_file*,int,int,int, void volatile **);
|
|
+static int vlogShmLock(sqlite3_file*, int , int, int);
|
|
+static void vlogShmBarrier(sqlite3_file*);
|
|
+static int vlogShmUnmap(sqlite3_file*, int);
|
|
+static int vlogFetch(sqlite3_file*, sqlite3_int64, int, void**);
|
|
+static int vlogUnfetch(sqlite3_file*, sqlite3_int64, void*);
|
|
+
|
|
+/*
|
|
+** Methods for VLogVfs
|
|
+*/
|
|
+static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
+static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
+static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
+static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
+static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
+static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
+static void vlogDlClose(sqlite3_vfs*, void*);
|
|
+static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
+static int vlogSleep(sqlite3_vfs*, int microseconds);
|
|
+static int vlogCurrentTime(sqlite3_vfs*, double*);
|
|
+static int vlogGetLastError(sqlite3_vfs*, int, char *);
|
|
+static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
+static int vlogSetSystemCall(sqlite3_vfs*, const char *, sqlite3_syscall_ptr);
|
|
+static sqlite3_syscall_ptr vlogGetSystemCall(sqlite3_vfs *, const char *);
|
|
+static const char *vlogNextSystemCall(sqlite3_vfs *, const char *);
|
|
+
|
|
+
|
|
+static VLogVfs vlog_vfs = {
|
|
+ {
|
|
+ 0, /* iVersion (set by register_vlog()) */
|
|
+ 0, /* szOsFile (set by register_vlog()) */
|
|
+ 0, /* mxPathname (set by register_vlog()) */
|
|
+ 0, /* pNext */
|
|
+ "vfslog", /* zName */
|
|
+ 0, /* pAppData */
|
|
+ vlogOpen, /* xOpen */
|
|
+ vlogDelete, /* xDelete */
|
|
+ vlogAccess, /* xAccess */
|
|
+ vlogFullPathname, /* xFullPathname */
|
|
+ vlogDlOpen, /* xDlOpen */
|
|
+ vlogDlError, /* xDlError */
|
|
+ vlogDlSym, /* xDlSym */
|
|
+ vlogDlClose, /* xDlClose */
|
|
+ vlogRandomness, /* xRandomness */
|
|
+ vlogSleep, /* xSleep */
|
|
+ vlogCurrentTime, /* xCurrentTime */
|
|
+ vlogGetLastError, /* xGetLastError */
|
|
+ vlogCurrentTimeInt64, /* xCurrentTimeInt64 */
|
|
+ vlogSetSystemCall, /* xSetSystemCall */
|
|
+ vlogGetSystemCall, /* xGetSystemCall */
|
|
+ vlogNextSystemCall /* xNextSystemCall */
|
|
+ },
|
|
+ 0
|
|
+};
|
|
+
|
|
+static sqlite3_io_methods vlog_io_methods = {
|
|
+ 3, /* iVersion */
|
|
+ vlogClose, /* xClose */
|
|
+ vlogRead, /* xRead */
|
|
+ vlogWrite, /* xWrite */
|
|
+ vlogTruncate, /* xTruncate */
|
|
+ vlogSync, /* xSync */
|
|
+ vlogFileSize, /* xFileSize */
|
|
+ vlogLock, /* xLock */
|
|
+ vlogUnlock, /* xUnlock */
|
|
+ vlogCheckReservedLock, /* xCheckReservedLock */
|
|
+ vlogFileControl, /* xFileControl */
|
|
+ vlogSectorSize, /* xSectorSize */
|
|
+ vlogDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ vlogShmMap, /* xShmMap */
|
|
+ vlogShmLock, /* xShmLock */
|
|
+ vlogShmBarrier, /* xShmBarrier */
|
|
+ vlogShmUnmap, /* xShmUnmap */
|
|
+ vlogFetch, /* xFetch */
|
|
+ vlogUnfetch /* xUnfecth */
|
|
+};
|
|
+
|
|
+#ifdef _WIN32
|
|
+#include <windows.h>
|
|
+#include <time.h>
|
|
+static sqlite3_uint64 vlog_time(){
|
|
+ FILETIME ft;
|
|
+ sqlite3_uint64 u64time = 0;
|
|
+
|
|
+ GetSystemTimeAsFileTime(&ft);
|
|
+
|
|
+ u64time |= ft.dwHighDateTime;
|
|
+ u64time <<= 32;
|
|
+ u64time |= ft.dwLowDateTime;
|
|
+
|
|
+ /* ft is 100-nanosecond intervals, we want microseconds */
|
|
+ return u64time /(sqlite3_uint64)10;
|
|
+}
|
|
+#elif SQLITE_OS_UNIX && !defined(NO_GETTOD)
|
|
+#include <sys/time.h>
|
|
+static sqlite3_uint64 vlog_time(){
|
|
+ struct timeval sTime;
|
|
+ gettimeofday(&sTime, 0);
|
|
+ return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
|
|
+}
|
|
+#else
|
|
+static sqlite3_uint64 vlog_time(){
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+** Write a message to the log file
|
|
+*/
|
|
+static void vlogLogPrint(
|
|
+ VLogLog *pLog, /* The log file to write into */
|
|
+ sqlite3_int64 tStart, /* Start time of system call */
|
|
+ sqlite3_int64 tElapse, /* Elapse time of system call */
|
|
+ const char *zOp, /* Type of system call */
|
|
+ sqlite3_int64 iArg1, /* First argument */
|
|
+ sqlite3_int64 iArg2, /* Second argument */
|
|
+ const char *zArg3, /* Third argument */
|
|
+ int iRes /* Result */
|
|
+){
|
|
+ char z1[40], z2[40], z3[2000], zStart[40], zElapse[40];
|
|
+ if( pLog==0 ) return;
|
|
+ if( iArg1>=0 ){
|
|
+ sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
|
|
+ }else{
|
|
+ z1[0] = 0;
|
|
+ }
|
|
+ if( iArg2>=0 ){
|
|
+ sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
|
|
+ }else{
|
|
+ z2[0] = 0;
|
|
+ }
|
|
+ if( zArg3 ){
|
|
+ sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3);
|
|
+ }else{
|
|
+ z3[0] = 0;
|
|
+ }
|
|
+ sqlite3_snprintf(sizeof(zStart), zStart, "%lld", tStart);
|
|
+ sqlite3_snprintf(sizeof(zElapse), zElapse, "%lld", tElapse);
|
|
+ fprintf(pLog->out,"%s,%s,%s,%d,%s,%s,%s,%d\n",
|
|
+ zStart, zElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
|
|
+}
|
|
+
|
|
+/*
|
|
+** List of all active log connections. Protected by the master mutex.
|
|
+*/
|
|
+static VLogLog *allLogs = 0;
|
|
+
|
|
+/*
|
|
+** Close a VLogLog object
|
|
+*/
|
|
+static void vlogLogClose(VLogLog *p){
|
|
+ if( p ){
|
|
+ sqlite3_mutex *pMutex;
|
|
+ p->nRef--;
|
|
+ if( p->nRef>0 || p->zFilename==0 ) return;
|
|
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
|
|
+ sqlite3_mutex_enter(pMutex);
|
|
+ *p->ppPrev = p->pNext;
|
|
+ if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
|
|
+ sqlite3_mutex_leave(pMutex);
|
|
+ fclose(p->out);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a VLogLog object on the given file
|
|
+*/
|
|
+static VLogLog *vlogLogOpen(const char *zFilename){
|
|
+ int nName = (int)strlen(zFilename);
|
|
+ int isJournal = 0;
|
|
+ sqlite3_mutex *pMutex;
|
|
+ VLogLog *pLog, *pTemp;
|
|
+ sqlite3_int64 tNow = 0;
|
|
+ if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
|
|
+ return 0; /* Do not log wal files */
|
|
+ }else
|
|
+ if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
|
|
+ nName -= 8;
|
|
+ isJournal = 1;
|
|
+ }else if( nName>12
|
|
+ && sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
|
|
+ return 0; /* Do not log master journal files */
|
|
+ }
|
|
+ pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 );
|
|
+ if( pTemp==0 ) return 0;
|
|
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
|
|
+ sqlite3_mutex_enter(pMutex);
|
|
+ for(pLog=allLogs; pLog; pLog=pLog->pNext){
|
|
+ if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( pLog==0 ){
|
|
+ pLog = pTemp;
|
|
+ pTemp = 0;
|
|
+ memset(pLog, 0, sizeof(*pLog)*2);
|
|
+ pLog->zFilename = (char*)&pLog[2];
|
|
+ tNow = vlog_time();
|
|
+ sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
|
|
+ nName, zFilename, tNow);
|
|
+ pLog->out = fopen(pLog->zFilename, "a");
|
|
+ if( pLog->out==0 ){
|
|
+ sqlite3_mutex_leave(pMutex);
|
|
+ sqlite3_free(pLog);
|
|
+ return 0;
|
|
+ }
|
|
+ pLog->nFilename = nName;
|
|
+ pLog[1].out = pLog[0].out;
|
|
+ pLog->ppPrev = &allLogs;
|
|
+ if( allLogs ) allLogs->ppPrev = &pLog->pNext;
|
|
+ pLog->pNext = allLogs;
|
|
+ allLogs = pLog;
|
|
+ }
|
|
+ sqlite3_mutex_leave(pMutex);
|
|
+ if( pTemp ){
|
|
+ sqlite3_free(pTemp);
|
|
+ }else{
|
|
+#if SQLITE_OS_UNIX || defined(__CYGWIN__)
|
|
+ char zHost[200];
|
|
+ zHost[0] = 0;
|
|
+ gethostname(zHost, sizeof(zHost)-1);
|
|
+ zHost[sizeof(zHost)-1] = 0;
|
|
+ vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
|
|
+#endif
|
|
+ }
|
|
+ if( pLog && isJournal ) pLog++;
|
|
+ pLog->nRef++;
|
|
+ return pLog;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Close an vlog-file.
|
|
+*/
|
|
+static int vlogClose(sqlite3_file *pFile){
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ int rc = SQLITE_OK;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+
|
|
+ tStart = vlog_time();
|
|
+ if( p->pReal->pMethods ){
|
|
+ rc = p->pReal->pMethods->xClose(p->pReal);
|
|
+ }
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
|
|
+ vlogLogClose(p->pLog);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute signature for a block of content.
|
|
+**
|
|
+** For blocks of 16 or fewer bytes, the signature is just a hex dump of
|
|
+** the entire block.
|
|
+**
|
|
+** For blocks of more than 16 bytes, the signature is a hex dump of the
|
|
+** first 8 bytes followed by a 64-bit has of the entire block.
|
|
+*/
|
|
+static void vlogSignature(unsigned char *p, int n, char *zCksum){
|
|
+ unsigned int s0 = 0, s1 = 0;
|
|
+ unsigned int *pI;
|
|
+ int i;
|
|
+ if( n<=16 ){
|
|
+ for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
|
|
+ }else{
|
|
+ pI = (unsigned int*)p;
|
|
+ for(i=0; i<n-7; i+=8){
|
|
+ s0 += pI[0] + s1;
|
|
+ s1 += pI[1] + s0;
|
|
+ pI += 2;
|
|
+ }
|
|
+ for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
|
|
+ sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert a big-endian 32-bit integer into a native integer
|
|
+*/
|
|
+static int bigToNative(const unsigned char *x){
|
|
+ return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read data from an vlog-file.
|
|
+*/
|
|
+static int vlogRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ char zSig[40];
|
|
+
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ vlogSignature(zBuf, iAmt, zSig);
|
|
+ }else{
|
|
+ zSig[0] = 0;
|
|
+ }
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
|
|
+ if( rc==SQLITE_OK
|
|
+ && p->pLog
|
|
+ && p->pLog->zFilename
|
|
+ && iOfst<=24
|
|
+ && iOfst+iAmt>=28
|
|
+ ){
|
|
+ unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
|
|
+ unsigned iCtr, nFree = -1;
|
|
+ char *zFree = 0;
|
|
+ char zStr[12];
|
|
+ iCtr = bigToNative(x);
|
|
+ if( iOfst+iAmt>=40 ){
|
|
+ zFree = zStr;
|
|
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
|
|
+ nFree = bigToNative(x+12);
|
|
+ }
|
|
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an vlog-file.
|
|
+*/
|
|
+static int vlogWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *z,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ char zSig[40];
|
|
+
|
|
+ tStart = vlog_time();
|
|
+ vlogSignature((unsigned char*)z, iAmt, zSig);
|
|
+ rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
|
|
+ if( rc==SQLITE_OK
|
|
+ && p->pLog
|
|
+ && p->pLog->zFilename
|
|
+ && iOfst<=24
|
|
+ && iOfst+iAmt>=28
|
|
+ ){
|
|
+ unsigned char *x = ((unsigned char*)z)+(24-iOfst);
|
|
+ unsigned iCtr, nFree = -1;
|
|
+ char *zFree = 0;
|
|
+ char zStr[12];
|
|
+ iCtr = bigToNative(x);
|
|
+ if( iOfst+iAmt>=40 ){
|
|
+ zFree = zStr;
|
|
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
|
|
+ nFree = bigToNative(x+12);
|
|
+ }
|
|
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an vlog-file.
|
|
+*/
|
|
+static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xTruncate(p->pReal, size);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an vlog-file.
|
|
+*/
|
|
+static int vlogSync(sqlite3_file *pFile, int flags){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xSync(p->pReal, flags);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an vlog-file.
|
|
+*/
|
|
+static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an vlog-file.
|
|
+*/
|
|
+static int vlogLock(sqlite3_file *pFile, int eLock){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unlock an vlog-file.
|
|
+*/
|
|
+static int vlogUnlock(sqlite3_file *pFile, int eLock){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0);
|
|
+ rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an vlog-file.
|
|
+*/
|
|
+static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
|
|
+ *pResOut, -1, "", rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an vlog-file.
|
|
+*/
|
|
+static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ int rc;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
|
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
|
+ *(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
|
|
+ }
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ if( op==SQLITE_FCNTL_TRACE ){
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc);
|
|
+ }else if( op==SQLITE_FCNTL_PRAGMA ){
|
|
+ const char **azArg = (const char **)pArg;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc);
|
|
+ }else if( op==SQLITE_FCNTL_SIZE_HINT ){
|
|
+ sqlite3_int64 sz = *(sqlite3_int64*)pArg;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc);
|
|
+ }else{
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the sector-size in bytes for an vlog-file.
|
|
+*/
|
|
+static int vlogSectorSize(sqlite3_file *pFile){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xSectorSize(p->pReal);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an vlog-file.
|
|
+*/
|
|
+static int vlogDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vlogShmMap(sqlite3_file*pFile,int a,int b,int c, void volatile **d){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xShmMap(p->pReal, a, b, c, d);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMMAP", a, b, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vlogShmLock(sqlite3_file*pFile, int a, int b, int c){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xShmLock(p->pReal, a, b, c);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, b, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void vlogShmBarrier(sqlite3_file*pFile){
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ p->pReal->pMethods->xShmBarrier(p->pReal);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", -1, -1, 0, 0);
|
|
+}
|
|
+
|
|
+static int vlogShmUnmap(sqlite3_file*pFile, int a){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xShmUnmap(p->pReal, a);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vlogFetch(sqlite3_file*pFile, sqlite3_int64 a, int b, void** c){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xFetch(p->pReal, a, b, c);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, b, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vlogUnfetch(sqlite3_file*pFile, sqlite3_int64 a, void* b){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogFile *p = (VLogFile *)pFile;
|
|
+ tStart = vlog_time();
|
|
+ rc = p->pReal->pMethods->xUnfetch(p->pReal, a, b);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, -1, 0, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an vlog file handle.
|
|
+*/
|
|
+static int vlogOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ sqlite3_int64 iArg2;
|
|
+ VLogFile *p = (VLogFile*)pFile;
|
|
+
|
|
+ p->pReal = (sqlite3_file*)&p[1];
|
|
+ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
|
|
+ p->pLog = vlogLogOpen(zName);
|
|
+ }else{
|
|
+ p->pLog = 0;
|
|
+ }
|
|
+ tStart = vlog_time();
|
|
+ rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ iArg2 = pOutFlags ? *pOutFlags : -1;
|
|
+ vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pFile->pMethods = &vlog_io_methods;
|
|
+ }else{
|
|
+ if( p->pLog ) vlogLogClose(p->pLog);
|
|
+ p->pLog = 0;
|
|
+ }
|
|
+ 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 vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogLog *pLog;
|
|
+ tStart = vlog_time();
|
|
+ rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ pLog = vlogLogOpen(zPath);
|
|
+ vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
|
|
+ vlogLogClose(pLog);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Test for access permissions. Return true if the requested permission
|
|
+** is available, or false otherwise.
|
|
+*/
|
|
+static int vlogAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ int rc;
|
|
+ sqlite3_uint64 tStart, tElapse;
|
|
+ VLogLog *pLog;
|
|
+ tStart = vlog_time();
|
|
+ rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
|
|
+ tElapse = vlog_time() - tStart;
|
|
+ pLog = vlogLogOpen(zPath);
|
|
+ vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
|
|
+ vlogLogClose(pLog);
|
|
+ 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 (INST_MAX_PATHNAME+1) bytes.
|
|
+*/
|
|
+static int vlogFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open the dynamic library located at zPath and return a handle.
|
|
+*/
|
|
+static void *vlogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), 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 vlogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
|
+*/
|
|
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close the dynamic library handle pHandle.
|
|
+*/
|
|
+static void vlogDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
+** random data.
|
|
+*/
|
|
+static int vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sleep for nMicro microseconds. Return the number of microseconds
|
|
+** actually slept.
|
|
+*/
|
|
+static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current time as a Julian Day number in *pTimeOut.
|
|
+*/
|
|
+static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
|
|
+}
|
|
+
|
|
+static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
+ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
|
|
+}
|
|
+static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
+ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
|
|
+}
|
|
+static int vlogSetSystemCall(sqlite3_vfs*pVfs, const char *zName, sqlite3_syscall_ptr p){
|
|
+ return REALVFS(pVfs)->xSetSystemCall(REALVFS(pVfs), zName, p);
|
|
+}
|
|
+static sqlite3_syscall_ptr vlogGetSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
|
+ return REALVFS(pVfs)->xGetSystemCall(REALVFS(pVfs), zName);
|
|
+}
|
|
+static const char *vlogNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
|
+ return REALVFS(pVfs)->xNextSystemCall(REALVFS(pVfs), zName);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register vfslog as the default VFS for this process.
|
|
+*/
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_register_vfslog(const char *zArg){
|
|
+ vlog_vfs.pVfs = sqlite3_vfs_find(0);
|
|
+ if( vlog_vfs.pVfs==&vlog_vfs.base ) return 0; /* Already registered. */
|
|
+ vlog_vfs.base.iVersion = vlog_vfs.pVfs->iVersion;
|
|
+ vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
|
|
+ vlog_vfs.base.mxPathname = vlog_vfs.pVfs->mxPathname;
|
|
+ return sqlite3_vfs_register(&vlog_vfs.base, 1);
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/vfsstat.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/vfsstat.c 2019-05-14 16:22:31.280072900 +0200
|
|
@@ -0,0 +1,836 @@
|
|
+/*
|
|
+** 2016-05-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 the implementation of an SQLite vfs shim that
|
|
+** tracks I/O. Access to the accumulated status counts is provided using
|
|
+** an eponymous virtual table.
|
|
+*/
|
|
+#include <sqlite3ext.h>
|
|
+SQLITE_EXTENSION_INIT1
|
|
+
|
|
+/*
|
|
+** This module contains code for a wrapper VFS that cause stats for
|
|
+** most VFS calls to be recorded.
|
|
+**
|
|
+** To use this module, first compile it as a loadable extension. See
|
|
+** https://www.sqlite.org/loadext.html#build for compilations instructions.
|
|
+**
|
|
+** After compliing, load this extension, then open database connections to be
|
|
+** measured. Query usages status using the vfsstat virtual table:
|
|
+**
|
|
+** SELECT * FROM vfsstat;
|
|
+**
|
|
+** Reset counters using UPDATE statements against vfsstat:
|
|
+**
|
|
+** UPDATE vfsstat SET count=0;
|
|
+**
|
|
+** EXAMPLE SCRIPT:
|
|
+**
|
|
+** .load ./vfsstat
|
|
+** .open test.db
|
|
+** DROP TABLE IF EXISTS t1;
|
|
+** CREATE TABLE t1(x,y);
|
|
+** INSERT INTO t1 VALUES(123, randomblob(5000));
|
|
+** CREATE INDEX t1x ON t1(x);
|
|
+** DROP TABLE t1;
|
|
+** VACUUM;
|
|
+** SELECT * FROM vfsstat WHERE count>0;
|
|
+**
|
|
+** LIMITATIONS:
|
|
+**
|
|
+** This module increments counters without using mutex protection. So if
|
|
+** two or more threads try to use this module at the same time, race conditions
|
|
+** may occur which mess up the counts. This is harmless, other than giving
|
|
+** incorrect statistics.
|
|
+*/
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <assert.h>
|
|
+
|
|
+/*
|
|
+** File types
|
|
+*/
|
|
+#define VFSSTAT_MAIN 0 /* Main database file */
|
|
+#define VFSSTAT_JOURNAL 1 /* Rollback journal */
|
|
+#define VFSSTAT_WAL 2 /* Write-ahead log file */
|
|
+#define VFSSTAT_MASTERJRNL 3 /* Master journal */
|
|
+#define VFSSTAT_SUBJRNL 4 /* Subjournal */
|
|
+#define VFSSTAT_TEMPDB 5 /* TEMP database */
|
|
+#define VFSSTAT_TEMPJRNL 6 /* Journal for TEMP database */
|
|
+#define VFSSTAT_TRANSIENT 7 /* Transient database */
|
|
+#define VFSSTAT_ANY 8 /* Unspecified file type */
|
|
+#define VFSSTAT_nFile 9 /* This many file types */
|
|
+
|
|
+/* Names of the file types. These are allowed values for the
|
|
+** first column of the vfsstat virtual table.
|
|
+*/
|
|
+static const char *const azFile[] = {
|
|
+ "database", "journal", "wal", "master-journal", "sub-journal",
|
|
+ "temp-database", "temp-journal", "transient-db", "*"
|
|
+};
|
|
+
|
|
+/*
|
|
+** Stat types
|
|
+*/
|
|
+#define VFSSTAT_BYTESIN 0 /* Bytes read in */
|
|
+#define VFSSTAT_BYTESOUT 1 /* Bytes written out */
|
|
+#define VFSSTAT_READ 2 /* Read requests */
|
|
+#define VFSSTAT_WRITE 3 /* Write requests */
|
|
+#define VFSSTAT_SYNC 4 /* Syncs */
|
|
+#define VFSSTAT_OPEN 5 /* File opens */
|
|
+#define VFSSTAT_LOCK 6 /* Lock requests */
|
|
+#define VFSSTAT_ACCESS 0 /* xAccess calls. filetype==ANY only */
|
|
+#define VFSSTAT_DELETE 1 /* xDelete calls. filetype==ANY only */
|
|
+#define VFSSTAT_FULLPATH 2 /* xFullPathname calls. ANY only */
|
|
+#define VFSSTAT_RANDOM 3 /* xRandomness calls. ANY only */
|
|
+#define VFSSTAT_SLEEP 4 /* xSleep calls. ANY only */
|
|
+#define VFSSTAT_CURTIME 5 /* xCurrentTime calls. ANY only */
|
|
+#define VFSSTAT_nStat 7 /* This many stat types */
|
|
+
|
|
+
|
|
+/* Names for the second column of the vfsstat virtual table for all
|
|
+** cases except when the first column is "*" or VFSSTAT_ANY. */
|
|
+static const char *const azStat[] = {
|
|
+ "bytes-in", "bytes-out", "read", "write", "sync", "open", "lock",
|
|
+};
|
|
+static const char *const azStatAny[] = {
|
|
+ "access", "delete", "fullpathname", "randomness", "sleep", "currenttimestamp",
|
|
+ "not-used"
|
|
+};
|
|
+
|
|
+/* Total number of counters */
|
|
+#define VFSSTAT_MXCNT (VFSSTAT_nStat*VFSSTAT_nFile)
|
|
+
|
|
+/*
|
|
+** Performance stats are collected in an instance of the following
|
|
+** global array.
|
|
+*/
|
|
+static sqlite3_uint64 aVfsCnt[VFSSTAT_MXCNT];
|
|
+
|
|
+/*
|
|
+** Access to a specific counter
|
|
+*/
|
|
+#define STATCNT(filetype,stat) (aVfsCnt[(filetype)*VFSSTAT_nStat+(stat)])
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this utility
|
|
+*/
|
|
+typedef struct VStatVfs VStatVfs;
|
|
+typedef struct VStatFile VStatFile;
|
|
+
|
|
+/* An instance of the VFS */
|
|
+struct VStatVfs {
|
|
+ sqlite3_vfs base; /* VFS methods */
|
|
+ sqlite3_vfs *pVfs; /* Parent VFS */
|
|
+};
|
|
+
|
|
+/* An open file */
|
|
+struct VStatFile {
|
|
+ sqlite3_file base; /* IO methods */
|
|
+ sqlite3_file *pReal; /* Underlying file handle */
|
|
+ unsigned char eFiletype; /* What type of file is this */
|
|
+};
|
|
+
|
|
+#define REALVFS(p) (((VStatVfs*)(p))->pVfs)
|
|
+
|
|
+/*
|
|
+** Methods for VStatFile
|
|
+*/
|
|
+static int vstatClose(sqlite3_file*);
|
|
+static int vstatRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
+static int vstatWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
+static int vstatTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
+static int vstatSync(sqlite3_file*, int flags);
|
|
+static int vstatFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
+static int vstatLock(sqlite3_file*, int);
|
|
+static int vstatUnlock(sqlite3_file*, int);
|
|
+static int vstatCheckReservedLock(sqlite3_file*, int *pResOut);
|
|
+static int vstatFileControl(sqlite3_file*, int op, void *pArg);
|
|
+static int vstatSectorSize(sqlite3_file*);
|
|
+static int vstatDeviceCharacteristics(sqlite3_file*);
|
|
+static int vstatShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
|
+static int vstatShmLock(sqlite3_file*, int offset, int n, int flags);
|
|
+static void vstatShmBarrier(sqlite3_file*);
|
|
+static int vstatShmUnmap(sqlite3_file*, int deleteFlag);
|
|
+static int vstatFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
|
+static int vstatUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
|
+
|
|
+/*
|
|
+** Methods for VStatVfs
|
|
+*/
|
|
+static int vstatOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
+static int vstatDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
+static int vstatAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
+static int vstatFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
+static void *vstatDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
+static void vstatDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
+static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
+static void vstatDlClose(sqlite3_vfs*, void*);
|
|
+static int vstatRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
+static int vstatSleep(sqlite3_vfs*, int microseconds);
|
|
+static int vstatCurrentTime(sqlite3_vfs*, double*);
|
|
+static int vstatGetLastError(sqlite3_vfs*, int, char *);
|
|
+static int vstatCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
+
|
|
+static VStatVfs vstat_vfs = {
|
|
+ {
|
|
+ 2, /* iVersion */
|
|
+ 0, /* szOsFile (set by register_vstat()) */
|
|
+ 1024, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ "vfslog", /* zName */
|
|
+ 0, /* pAppData */
|
|
+ vstatOpen, /* xOpen */
|
|
+ vstatDelete, /* xDelete */
|
|
+ vstatAccess, /* xAccess */
|
|
+ vstatFullPathname, /* xFullPathname */
|
|
+ vstatDlOpen, /* xDlOpen */
|
|
+ vstatDlError, /* xDlError */
|
|
+ vstatDlSym, /* xDlSym */
|
|
+ vstatDlClose, /* xDlClose */
|
|
+ vstatRandomness, /* xRandomness */
|
|
+ vstatSleep, /* xSleep */
|
|
+ vstatCurrentTime, /* xCurrentTime */
|
|
+ vstatGetLastError, /* xGetLastError */
|
|
+ vstatCurrentTimeInt64 /* xCurrentTimeInt64 */
|
|
+ },
|
|
+ 0
|
|
+};
|
|
+
|
|
+static const sqlite3_io_methods vstat_io_methods = {
|
|
+ 3, /* iVersion */
|
|
+ vstatClose, /* xClose */
|
|
+ vstatRead, /* xRead */
|
|
+ vstatWrite, /* xWrite */
|
|
+ vstatTruncate, /* xTruncate */
|
|
+ vstatSync, /* xSync */
|
|
+ vstatFileSize, /* xFileSize */
|
|
+ vstatLock, /* xLock */
|
|
+ vstatUnlock, /* xUnlock */
|
|
+ vstatCheckReservedLock, /* xCheckReservedLock */
|
|
+ vstatFileControl, /* xFileControl */
|
|
+ vstatSectorSize, /* xSectorSize */
|
|
+ vstatDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ vstatShmMap, /* xShmMap */
|
|
+ vstatShmLock, /* xShmLock */
|
|
+ vstatShmBarrier, /* xShmBarrier */
|
|
+ vstatShmUnmap, /* xShmUnmap */
|
|
+ vstatFetch, /* xFetch */
|
|
+ vstatUnfetch /* xUnfetch */
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Close an vstat-file.
|
|
+*/
|
|
+static int vstatClose(sqlite3_file *pFile){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( p->pReal->pMethods ){
|
|
+ rc = p->pReal->pMethods->xClose(p->pReal);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Read data from an vstat-file.
|
|
+*/
|
|
+static int vstatRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+
|
|
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
|
|
+ STATCNT(p->eFiletype,VFSSTAT_READ)++;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ STATCNT(p->eFiletype,VFSSTAT_BYTESIN) += iAmt;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an vstat-file.
|
|
+*/
|
|
+static int vstatWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *z,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+
|
|
+ rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
|
|
+ STATCNT(p->eFiletype,VFSSTAT_WRITE)++;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ STATCNT(p->eFiletype,VFSSTAT_BYTESOUT) += iAmt;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an vstat-file.
|
|
+*/
|
|
+static int vstatTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xTruncate(p->pReal, size);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an vstat-file.
|
|
+*/
|
|
+static int vstatSync(sqlite3_file *pFile, int flags){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xSync(p->pReal, flags);
|
|
+ STATCNT(p->eFiletype,VFSSTAT_SYNC)++;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an vstat-file.
|
|
+*/
|
|
+static int vstatFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an vstat-file.
|
|
+*/
|
|
+static int vstatLock(sqlite3_file *pFile, int eLock){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
|
|
+ STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unlock an vstat-file.
|
|
+*/
|
|
+static int vstatUnlock(sqlite3_file *pFile, int eLock){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
|
|
+ STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an vstat-file.
|
|
+*/
|
|
+static int vstatCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
|
|
+ STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an vstat-file.
|
|
+*/
|
|
+static int vstatFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ int rc;
|
|
+ rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
|
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
|
+ *(char**)pArg = sqlite3_mprintf("vstat/%z", *(char**)pArg);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the sector-size in bytes for an vstat-file.
|
|
+*/
|
|
+static int vstatSectorSize(sqlite3_file *pFile){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xSectorSize(p->pReal);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an vstat-file.
|
|
+*/
|
|
+static int vstatDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Create a shared memory file mapping */
|
|
+static int vstatShmMap(
|
|
+ sqlite3_file *pFile,
|
|
+ int iPg,
|
|
+ int pgsz,
|
|
+ int bExtend,
|
|
+ void volatile **pp
|
|
+){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ return p->pReal->pMethods->xShmMap(p->pReal, iPg, pgsz, bExtend, pp);
|
|
+}
|
|
+
|
|
+/* Perform locking on a shared-memory segment */
|
|
+static int vstatShmLock(sqlite3_file *pFile, int offset, int n, int flags){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ return p->pReal->pMethods->xShmLock(p->pReal, offset, n, flags);
|
|
+}
|
|
+
|
|
+/* Memory barrier operation on shared memory */
|
|
+static void vstatShmBarrier(sqlite3_file *pFile){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ p->pReal->pMethods->xShmBarrier(p->pReal);
|
|
+}
|
|
+
|
|
+/* Unmap a shared memory segment */
|
|
+static int vstatShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ return p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
|
|
+}
|
|
+
|
|
+/* Fetch a page of a memory-mapped file */
|
|
+static int vstatFetch(
|
|
+ sqlite3_file *pFile,
|
|
+ sqlite3_int64 iOfst,
|
|
+ int iAmt,
|
|
+ void **pp
|
|
+){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ return p->pReal->pMethods->xFetch(p->pReal, iOfst, iAmt, pp);
|
|
+}
|
|
+
|
|
+/* Release a memory-mapped page */
|
|
+static int vstatUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
|
+ VStatFile *p = (VStatFile *)pFile;
|
|
+ return p->pReal->pMethods->xUnfetch(p->pReal, iOfst, pPage);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an vstat file handle.
|
|
+*/
|
|
+static int vstatOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ int rc;
|
|
+ VStatFile *p = (VStatFile*)pFile;
|
|
+
|
|
+ p->pReal = (sqlite3_file*)&p[1];
|
|
+ rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
|
|
+ if( flags & SQLITE_OPEN_MAIN_DB ){
|
|
+ p->eFiletype = VFSSTAT_MAIN;
|
|
+ }else if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
|
|
+ p->eFiletype = VFSSTAT_JOURNAL;
|
|
+ }else if( flags & SQLITE_OPEN_WAL ){
|
|
+ p->eFiletype = VFSSTAT_WAL;
|
|
+ }else if( flags & SQLITE_OPEN_MASTER_JOURNAL ){
|
|
+ p->eFiletype = VFSSTAT_MASTERJRNL;
|
|
+ }else if( flags & SQLITE_OPEN_SUBJOURNAL ){
|
|
+ p->eFiletype = VFSSTAT_SUBJRNL;
|
|
+ }else if( flags & SQLITE_OPEN_TEMP_DB ){
|
|
+ p->eFiletype = VFSSTAT_TEMPDB;
|
|
+ }else if( flags & SQLITE_OPEN_TEMP_JOURNAL ){
|
|
+ p->eFiletype = VFSSTAT_TEMPJRNL;
|
|
+ }else{
|
|
+ p->eFiletype = VFSSTAT_TRANSIENT;
|
|
+ }
|
|
+ STATCNT(p->eFiletype,VFSSTAT_OPEN)++;
|
|
+ pFile->pMethods = rc ? 0 : &vstat_io_methods;
|
|
+ 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 vstatDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ int rc;
|
|
+ rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_DELETE)++;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Test for access permissions. Return true if the requested permission
|
|
+** is available, or false otherwise.
|
|
+*/
|
|
+static int vstatAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ int rc;
|
|
+ rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_ACCESS)++;
|
|
+ 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 (INST_MAX_PATHNAME+1) bytes.
|
|
+*/
|
|
+static int vstatFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_FULLPATH)++;
|
|
+ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open the dynamic library located at zPath and return a handle.
|
|
+*/
|
|
+static void *vstatDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), 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 vstatDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
|
+*/
|
|
+static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close the dynamic library handle pHandle.
|
|
+*/
|
|
+static void vstatDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
+** random data.
|
|
+*/
|
|
+static int vstatRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_RANDOM)++;
|
|
+ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sleep for nMicro microseconds. Return the number of microseconds
|
|
+** actually slept.
|
|
+*/
|
|
+static int vstatSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_SLEEP)++;
|
|
+ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current time as a Julian Day number in *pTimeOut.
|
|
+*/
|
|
+static int vstatCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
|
|
+ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
|
|
+}
|
|
+
|
|
+static int vstatGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
+ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
|
|
+}
|
|
+static int vstatCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
+ STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
|
|
+ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** A virtual table for accessing the stats collected by this VFS shim
|
|
+*/
|
|
+static int vstattabConnect(sqlite3*, void*, int, const char*const*,
|
|
+ sqlite3_vtab**,char**);
|
|
+static int vstattabBestIndex(sqlite3_vtab*,sqlite3_index_info*);
|
|
+static int vstattabDisconnect(sqlite3_vtab*);
|
|
+static int vstattabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
|
|
+static int vstattabClose(sqlite3_vtab_cursor*);
|
|
+static int vstattabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv);
|
|
+static int vstattabNext(sqlite3_vtab_cursor*);
|
|
+static int vstattabEof(sqlite3_vtab_cursor*);
|
|
+static int vstattabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int);
|
|
+static int vstattabRowid(sqlite3_vtab_cursor*,sqlite3_int64*);
|
|
+static int vstattabUpdate(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
|
|
+
|
|
+/* A cursor for the vfsstat virtual table */
|
|
+typedef struct VfsStatCursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
|
|
+ int i; /* Pointing to this aVfsCnt[] value */
|
|
+} VfsStatCursor;
|
|
+
|
|
+
|
|
+static int vstattabConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define VSTAT_COLUMN_FILE 0
|
|
+#define VSTAT_COLUMN_STAT 1
|
|
+#define VSTAT_COLUMN_COUNT 2
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for vstat table object.
|
|
+*/
|
|
+static int vstattabDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new vstat table cursor object.
|
|
+*/
|
|
+static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ VfsStatCursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Destructor for a VfsStatCursor.
|
|
+*/
|
|
+static int vstattabClose(sqlite3_vtab_cursor *cur){
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a VfsStatCursor to its next row of output.
|
|
+*/
|
|
+static int vstattabNext(sqlite3_vtab_cursor *cur){
|
|
+ ((VfsStatCursor*)cur)->i++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the VfsStatCursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int vstattabColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ VfsStatCursor *pCur = (VfsStatCursor*)cur;
|
|
+ switch( i ){
|
|
+ case VSTAT_COLUMN_FILE: {
|
|
+ sqlite3_result_text(ctx, azFile[pCur->i/VFSSTAT_nStat], -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case VSTAT_COLUMN_STAT: {
|
|
+ const char *const *az;
|
|
+ az = (pCur->i/VFSSTAT_nStat)==VFSSTAT_ANY ? azStatAny : azStat;
|
|
+ sqlite3_result_text(ctx, az[pCur->i%VFSSTAT_nStat], -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case VSTAT_COLUMN_COUNT: {
|
|
+ sqlite3_result_int64(ctx, aVfsCnt[pCur->i]);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row.
|
|
+*/
|
|
+static int vstattabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ VfsStatCursor *pCur = (VfsStatCursor*)cur;
|
|
+ *pRowid = pCur->i;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int vstattabEof(sqlite3_vtab_cursor *cur){
|
|
+ VfsStatCursor *pCur = (VfsStatCursor*)cur;
|
|
+ return pCur->i >= VFSSTAT_MXCNT;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only a full table scan is supported. So xFilter simply rewinds to
|
|
+** the beginning.
|
|
+*/
|
|
+static int vstattabFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ VfsStatCursor *pCur = (VfsStatCursor*)pVtabCursor;
|
|
+ pCur->i = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Only a forwards full table scan is supported. xBestIndex is a no-op.
|
|
+*/
|
|
+static int vstattabBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Any VSTAT_COLUMN_COUNT can be changed to a positive integer.
|
|
+** No deletions or insertions are allowed. No changes to other
|
|
+** columns are allowed.
|
|
+*/
|
|
+static int vstattabUpdate(
|
|
+ sqlite3_vtab *tab,
|
|
+ int argc, sqlite3_value **argv,
|
|
+ sqlite3_int64 *pRowid
|
|
+){
|
|
+ sqlite3_int64 iRowid, x;
|
|
+ if( argc==1 ) return SQLITE_ERROR;
|
|
+ if( sqlite3_value_type(argv[0])!=SQLITE_INTEGER ) return SQLITE_ERROR;
|
|
+ iRowid = sqlite3_value_int64(argv[0]);
|
|
+ if( iRowid!=sqlite3_value_int64(argv[1]) ) return SQLITE_ERROR;
|
|
+ if( iRowid<0 || iRowid>=VFSSTAT_MXCNT ) return SQLITE_ERROR;
|
|
+ if( sqlite3_value_type(argv[VSTAT_COLUMN_COUNT+2])!=SQLITE_INTEGER ){
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ x = sqlite3_value_int64(argv[VSTAT_COLUMN_COUNT+2]);
|
|
+ if( x<0 ) return SQLITE_ERROR;
|
|
+ aVfsCnt[iRowid] = x;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static const sqlite3_module VfsStatModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ vstattabConnect, /* xConnect */
|
|
+ vstattabBestIndex, /* xBestIndex */
|
|
+ vstattabDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ vstattabOpen, /* xOpen - open a cursor */
|
|
+ vstattabClose, /* xClose - close a cursor */
|
|
+ vstattabFilter, /* xFilter - configure scan constraints */
|
|
+ vstattabNext, /* xNext - advance a cursor */
|
|
+ vstattabEof, /* xEof - check for end of scan */
|
|
+ vstattabColumn, /* xColumn - read data */
|
|
+ vstattabRowid, /* xRowid - read data */
|
|
+ vstattabUpdate, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+
|
|
+/*
|
|
+** This routine is an sqlite3_auto_extension() callback, invoked to register
|
|
+** the vfsstat virtual table for all new database connections.
|
|
+*/
|
|
+static int vstatRegister(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pThunk
|
|
+){
|
|
+ return sqlite3_create_module(db, "vfsstat", &VfsStatModule, 0);
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+/*
|
|
+** This routine is called when the extension is loaded.
|
|
+**
|
|
+** Register the new VFS. Make arrangement to register the virtual table
|
|
+** for each new database connection.
|
|
+*/
|
|
+int sqlite3_vfsstat_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ vstat_vfs.pVfs = sqlite3_vfs_find(0);
|
|
+ vstat_vfs.base.szOsFile = sizeof(VStatFile) + vstat_vfs.pVfs->szOsFile;
|
|
+ rc = sqlite3_vfs_register(&vstat_vfs.base, 1);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = vstatRegister(db, pzErrMsg, pApi);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
|
|
+ }
|
|
+ }
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ vstat_vfs.pVfs = sqlite3_vfs_find(0);
|
|
+ vstat_vfs.base.szOsFile = sizeof(VStatFile) + vstat_vfs.pVfs->szOsFile;
|
|
+ rc = sqlite3_vfs_register(&vstat_vfs.base, 1);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/vtablog.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/vtablog.c 2019-05-14 16:22:31.282025500 +0200
|
|
@@ -0,0 +1,510 @@
|
|
+/*
|
|
+** 2017-08-10
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements a virtual table that prints diagnostic information
|
|
+** on stdout when its key interfaces are called. This is intended for
|
|
+** interactive analysis and debugging of virtual table interfaces.
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** .load ./vtablog
|
|
+** CREATE VIRTUAL TABLE temp.log USING vtablog(
|
|
+** schema='CREATE TABLE x(a,b,c)',
|
|
+** rows=25
|
|
+** );
|
|
+** SELECT * FROM log;
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <ctype.h>
|
|
+
|
|
+
|
|
+/* vtablog_vtab is a subclass of sqlite3_vtab which will
|
|
+** serve as the underlying representation of a vtablog virtual table
|
|
+*/
|
|
+typedef struct vtablog_vtab vtablog_vtab;
|
|
+struct vtablog_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ int nRow; /* Number of rows in the table */
|
|
+ int iInst; /* Instance number for this vtablog table */
|
|
+ int nCursor; /* Number of cursors created */
|
|
+};
|
|
+
|
|
+/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
+** serve as the underlying representation of a cursor that scans
|
|
+** over rows of the result
|
|
+*/
|
|
+typedef struct vtablog_cursor vtablog_cursor;
|
|
+struct vtablog_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ int iCursor; /* Cursor number */
|
|
+ sqlite3_int64 iRowid; /* The rowid */
|
|
+};
|
|
+
|
|
+/* Skip leading whitespace. Return a pointer to the first non-whitespace
|
|
+** character, or to the zero terminator if the string has only whitespace */
|
|
+static const char *vtablog_skip_whitespace(const char *z){
|
|
+ while( isspace((unsigned char)z[0]) ) z++;
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/* Remove trailing whitespace from the end of string z[] */
|
|
+static void vtablog_trim_whitespace(char *z){
|
|
+ size_t n = strlen(z);
|
|
+ while( n>0 && isspace((unsigned char)z[n]) ) n--;
|
|
+ z[n] = 0;
|
|
+}
|
|
+
|
|
+/* Dequote the string */
|
|
+static void vtablog_dequote(char *z){
|
|
+ int j;
|
|
+ char cQuote = z[0];
|
|
+ size_t i, n;
|
|
+
|
|
+ if( cQuote!='\'' && cQuote!='"' ) return;
|
|
+ n = strlen(z);
|
|
+ if( n<2 || z[n-1]!=z[0] ) return;
|
|
+ for(i=1, j=0; i<n-1; i++){
|
|
+ if( z[i]==cQuote && z[i+1]==cQuote ) i++;
|
|
+ z[j++] = z[i];
|
|
+ }
|
|
+ z[j] = 0;
|
|
+}
|
|
+
|
|
+/* Check to see if the string is of the form: "TAG = VALUE" with optional
|
|
+** whitespace before and around tokens. If it is, return a pointer to the
|
|
+** first character of VALUE. If it is not, return NULL.
|
|
+*/
|
|
+static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
|
|
+ z = vtablog_skip_whitespace(z);
|
|
+ if( strncmp(zTag, z, nTag)!=0 ) return 0;
|
|
+ z = vtablog_skip_whitespace(z+nTag);
|
|
+ if( z[0]!='=' ) return 0;
|
|
+ return vtablog_skip_whitespace(z+1);
|
|
+}
|
|
+
|
|
+/* Decode a parameter that requires a dequoted string.
|
|
+**
|
|
+** Return non-zero on an error.
|
|
+*/
|
|
+static int vtablog_string_parameter(
|
|
+ char **pzErr, /* Leave the error message here, if there is one */
|
|
+ const char *zParam, /* Parameter we are checking for */
|
|
+ const char *zArg, /* Raw text of the virtual table argment */
|
|
+ char **pzVal /* Write the dequoted string value here */
|
|
+){
|
|
+ const char *zValue;
|
|
+ zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
|
|
+ if( zValue==0 ) return 0;
|
|
+ if( *pzVal ){
|
|
+ *pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
|
|
+ return 1;
|
|
+ }
|
|
+ *pzVal = sqlite3_mprintf("%s", zValue);
|
|
+ if( *pzVal==0 ){
|
|
+ *pzErr = sqlite3_mprintf("out of memory");
|
|
+ return 1;
|
|
+ }
|
|
+ vtablog_trim_whitespace(*pzVal);
|
|
+ vtablog_dequote(*pzVal);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#if 0 /* not used - yet */
|
|
+/* Return 0 if the argument is false and 1 if it is true. Return -1 if
|
|
+** we cannot really tell.
|
|
+*/
|
|
+static int vtablog_boolean(const char *z){
|
|
+ if( sqlite3_stricmp("yes",z)==0
|
|
+ || sqlite3_stricmp("on",z)==0
|
|
+ || sqlite3_stricmp("true",z)==0
|
|
+ || (z[0]=='1' && z[1]==0)
|
|
+ ){
|
|
+ return 1;
|
|
+ }
|
|
+ if( sqlite3_stricmp("no",z)==0
|
|
+ || sqlite3_stricmp("off",z)==0
|
|
+ || sqlite3_stricmp("false",z)==0
|
|
+ || (z[0]=='0' && z[1]==0)
|
|
+ ){
|
|
+ return 0;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The vtablogConnect() method is invoked to create a new
|
|
+** vtablog_vtab that describes the vtablog virtual table.
|
|
+**
|
|
+** Think of this routine as the constructor for vtablog_vtab objects.
|
|
+**
|
|
+** All this routine needs to do is:
|
|
+**
|
|
+** (1) Allocate the vtablog_vtab object and initialize all fields.
|
|
+**
|
|
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
+** result set of queries against vtablog will look like.
|
|
+*/
|
|
+static int vtablogConnectCreate(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr,
|
|
+ int isCreate
|
|
+){
|
|
+ static int nInst = 0;
|
|
+ vtablog_vtab *pNew;
|
|
+ int i;
|
|
+ int rc;
|
|
+ int iInst = ++nInst;
|
|
+ char *zSchema = 0;
|
|
+ char *zNRow = 0;
|
|
+
|
|
+ printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst);
|
|
+ printf(" argc=%d\n", argc);
|
|
+ for(i=0; i<argc; i++){
|
|
+ printf(" argv[%d] = ", i);
|
|
+ if( argv[i] ){
|
|
+ printf("[%s]\n", argv[i]);
|
|
+ }else{
|
|
+ printf("NULL\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for(i=3; i<argc; i++){
|
|
+ const char *z = argv[i];
|
|
+ if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( zSchema==0 ){
|
|
+ *pzErr = sqlite3_mprintf("no schema defined");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ rc = sqlite3_declare_vtab(db, zSchema);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ pNew->nRow = 10;
|
|
+ if( zNRow ) pNew->nRow = atoi(zNRow);
|
|
+ pNew->iInst = iInst;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+static int vtablogCreate(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
|
|
+}
|
|
+static int vtablogConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** This method is the destructor for vtablog_cursor objects.
|
|
+*/
|
|
+static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
|
+ printf("vtablogDisconnect(%d)\n", pTab->iInst);
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for vtablog_cursor objects.
|
|
+*/
|
|
+static int vtablogDestroy(sqlite3_vtab *pVtab){
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
|
+ printf("vtablogDestroy(%d)\n", pTab->iInst);
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new vtablog_cursor object.
|
|
+*/
|
|
+static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)p;
|
|
+ vtablog_cursor *pCur;
|
|
+ printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor);
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->iCursor = pTab->nCursor;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for a vtablog_cursor.
|
|
+*/
|
|
+static int vtablogClose(sqlite3_vtab_cursor *cur){
|
|
+ vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
|
+ printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor);
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a vtablog_cursor to its next row of output.
|
|
+*/
|
|
+static int vtablogNext(sqlite3_vtab_cursor *cur){
|
|
+ vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
|
+ printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n",
|
|
+ pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1);
|
|
+ pCur->iRowid++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the vtablog_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int vtablogColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
|
+ char zVal[50];
|
|
+
|
|
+ if( i<26 ){
|
|
+ sqlite3_snprintf(sizeof(zVal),zVal,"%c%d",
|
|
+ "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
|
|
+ }else{
|
|
+ sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
|
|
+ }
|
|
+ printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n",
|
|
+ pTab->iInst, pCur->iCursor, i, zVal);
|
|
+ sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** rowid is the same as the output value.
|
|
+*/
|
|
+static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
|
+ printf("vtablogRowid(tab=%d, cursor=%d): %d\n",
|
|
+ pTab->iInst, pCur->iCursor, (int)pCur->iRowid);
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int vtablogEof(sqlite3_vtab_cursor *cur){
|
|
+ vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
|
+ int rc = pCur->iRowid >= pTab->nRow;
|
|
+ printf("vtablogEof(tab=%d, cursor=%d): %d\n",
|
|
+ pTab->iInst, pCur->iCursor, rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Output an sqlite3_value object's value as an SQL literal.
|
|
+*/
|
|
+static void vtablogQuote(sqlite3_value *p){
|
|
+ char z[50];
|
|
+ switch( sqlite3_value_type(p) ){
|
|
+ case SQLITE_NULL: {
|
|
+ printf("NULL");
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INTEGER: {
|
|
+ sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
|
|
+ printf("%s", z);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_FLOAT: {
|
|
+ sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
|
|
+ printf("%s", z);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_BLOB: {
|
|
+ int n = sqlite3_value_bytes(p);
|
|
+ const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
|
|
+ int i;
|
|
+ printf("x'");
|
|
+ for(i=0; i<n; i++) printf("%02x", z[i]);
|
|
+ printf("'");
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ const char *z = (const char*)sqlite3_value_text(p);
|
|
+ int i;
|
|
+ char c;
|
|
+ for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
|
+ if( c==0 ){
|
|
+ printf("'%s'",z);
|
|
+ }else{
|
|
+ printf("'");
|
|
+ while( *z ){
|
|
+ for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
|
+ if( c=='\'' ) i++;
|
|
+ if( i ){
|
|
+ printf("%.*s", i, z);
|
|
+ z += i;
|
|
+ }
|
|
+ if( c=='\'' ){
|
|
+ printf("'");
|
|
+ continue;
|
|
+ }
|
|
+ if( c==0 ){
|
|
+ break;
|
|
+ }
|
|
+ z++;
|
|
+ }
|
|
+ printf("'");
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** This method is called to "rewind" the vtablog_cursor object back
|
|
+** to the first row of output. This method is always called at least
|
|
+** once prior to any call to vtablogColumn() or vtablogRowid() or
|
|
+** vtablogEof().
|
|
+*/
|
|
+static int vtablogFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ vtablog_cursor *pCur = (vtablog_cursor *)cur;
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
|
+ printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor);
|
|
+ pCur->iRowid = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the vtablog virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+*/
|
|
+static int vtablogBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
|
+ printf("vtablogBestIndex(tab=%d):\n", pTab->iInst);
|
|
+ pIdxInfo->estimatedCost = (double)500;
|
|
+ pIdxInfo->estimatedRows = 500;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
|
|
+** the table.
|
|
+**
|
|
+** This implementation does not actually make any changes to the table
|
|
+** content. It merely logs the fact that the method was invoked
|
|
+*/
|
|
+static int vtablogUpdate(
|
|
+ sqlite3_vtab *tab,
|
|
+ int argc,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
|
+ int i;
|
|
+ printf("vtablogUpdate(tab=%d):\n", pTab->iInst);
|
|
+ printf(" argc=%d\n", argc);
|
|
+ for(i=0; i<argc; i++){
|
|
+ printf(" argv[%d]=", i);
|
|
+ vtablogQuote(argv[i]);
|
|
+ printf("\n");
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This following structure defines all the methods for the
|
|
+** vtablog virtual table.
|
|
+*/
|
|
+static const sqlite3_module vtablogModule = {
|
|
+ 0, /* iVersion */
|
|
+ vtablogCreate, /* xCreate */
|
|
+ vtablogConnect, /* xConnect */
|
|
+ vtablogBestIndex, /* xBestIndex */
|
|
+ vtablogDisconnect, /* xDisconnect */
|
|
+ vtablogDestroy, /* xDestroy */
|
|
+ vtablogOpen, /* xOpen - open a cursor */
|
|
+ vtablogClose, /* xClose - close a cursor */
|
|
+ vtablogFilter, /* xFilter - configure scan constraints */
|
|
+ vtablogNext, /* xNext - advance a cursor */
|
|
+ vtablogEof, /* xEof - check for end of scan */
|
|
+ vtablogColumn, /* xColumn - read data */
|
|
+ vtablogRowid, /* xRowid - read data */
|
|
+ vtablogUpdate, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+};
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_vtablog_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
|
|
+ return rc;
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/vtshim.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/vtshim.c 2019-05-14 16:22:31.283001900 +0200
|
|
@@ -0,0 +1,563 @@
|
|
+/*
|
|
+** 2013-06-12
|
|
+**
|
|
+** 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.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** A shim that sits between the SQLite virtual table interface and
|
|
+** runtimes with garbage collector based memory management.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/* Forward references */
|
|
+typedef struct vtshim_aux vtshim_aux;
|
|
+typedef struct vtshim_vtab vtshim_vtab;
|
|
+typedef struct vtshim_cursor vtshim_cursor;
|
|
+
|
|
+
|
|
+/* The vtshim_aux argument is the auxiliary parameter that is passed
|
|
+** into sqlite3_create_module_v2().
|
|
+*/
|
|
+struct vtshim_aux {
|
|
+ void *pChildAux; /* pAux for child virtual tables */
|
|
+ void (*xChildDestroy)(void*); /* Destructor for pChildAux */
|
|
+ sqlite3_module *pMod; /* Methods for child virtual tables */
|
|
+ sqlite3 *db; /* The database to which we are attached */
|
|
+ char *zName; /* Name of the module */
|
|
+ int bDisposed; /* True if disposed */
|
|
+ vtshim_vtab *pAllVtab; /* List of all vtshim_vtab objects */
|
|
+ sqlite3_module sSelf; /* Methods used by this shim */
|
|
+};
|
|
+
|
|
+/* A vtshim virtual table object */
|
|
+struct vtshim_vtab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ sqlite3_vtab *pChild; /* Child virtual table */
|
|
+ vtshim_aux *pAux; /* Pointer to vtshim_aux object */
|
|
+ vtshim_cursor *pAllCur; /* List of all cursors */
|
|
+ vtshim_vtab **ppPrev; /* Previous on list */
|
|
+ vtshim_vtab *pNext; /* Next on list */
|
|
+};
|
|
+
|
|
+/* A vtshim cursor object */
|
|
+struct vtshim_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_vtab_cursor *pChild; /* Cursor generated by the managed subclass */
|
|
+ vtshim_cursor **ppPrev; /* Previous on list of all cursors */
|
|
+ vtshim_cursor *pNext; /* Next on list of all cursors */
|
|
+};
|
|
+
|
|
+/* Macro used to copy the child vtable error message to outer vtable */
|
|
+#define VTSHIM_COPY_ERRMSG() \
|
|
+ do { \
|
|
+ sqlite3_free(pVtab->base.zErrMsg); \
|
|
+ pVtab->base.zErrMsg = sqlite3_mprintf("%s", pVtab->pChild->zErrMsg); \
|
|
+ } while (0)
|
|
+
|
|
+/* Methods for the vtshim module */
|
|
+static int vtshimCreate(
|
|
+ sqlite3 *db,
|
|
+ void *ppAux,
|
|
+ int argc,
|
|
+ const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
|
|
+ vtshim_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+ assert( db==pAux->db );
|
|
+ if( pAux->bDisposed ){
|
|
+ if( pzErr ){
|
|
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
|
|
+ pAux->zName);
|
|
+ }
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv,
|
|
+ &pNew->pChild, pzErr);
|
|
+ if( rc ){
|
|
+ sqlite3_free(pNew);
|
|
+ *ppVtab = 0;
|
|
+ return rc;
|
|
+ }
|
|
+ pNew->pAux = pAux;
|
|
+ pNew->ppPrev = &pAux->pAllVtab;
|
|
+ pNew->pNext = pAux->pAllVtab;
|
|
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
|
|
+ pAux->pAllVtab = pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimConnect(
|
|
+ sqlite3 *db,
|
|
+ void *ppAux,
|
|
+ int argc,
|
|
+ const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
|
|
+ vtshim_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+ assert( db==pAux->db );
|
|
+ if( pAux->bDisposed ){
|
|
+ if( pzErr ){
|
|
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
|
|
+ pAux->zName);
|
|
+ }
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv,
|
|
+ &pNew->pChild, pzErr);
|
|
+ if( rc ){
|
|
+ sqlite3_free(pNew);
|
|
+ *ppVtab = 0;
|
|
+ return rc;
|
|
+ }
|
|
+ pNew->pAux = pAux;
|
|
+ pNew->ppPrev = &pAux->pAllVtab;
|
|
+ pNew->pNext = pAux->pAllVtab;
|
|
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
|
|
+ pAux->pAllVtab = pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimBestIndex(
|
|
+ sqlite3_vtab *pBase,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xBestIndex(pVtab->pChild, pIdxInfo);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimDisconnect(sqlite3_vtab *pBase){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( !pAux->bDisposed ){
|
|
+ rc = pAux->pMod->xDisconnect(pVtab->pChild);
|
|
+ }
|
|
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
|
|
+ *pVtab->ppPrev = pVtab->pNext;
|
|
+ sqlite3_free(pVtab);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimDestroy(sqlite3_vtab *pBase){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( !pAux->bDisposed ){
|
|
+ rc = pAux->pMod->xDestroy(pVtab->pChild);
|
|
+ }
|
|
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
|
|
+ *pVtab->ppPrev = pVtab->pNext;
|
|
+ sqlite3_free(pVtab);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ vtshim_cursor *pCur;
|
|
+ int rc;
|
|
+ *ppCursor = 0;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
|
|
+ if( rc ){
|
|
+ sqlite3_free(pCur);
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ return rc;
|
|
+ }
|
|
+ pCur->pChild->pVtab = pVtab->pChild;
|
|
+ *ppCursor = &pCur->base;
|
|
+ pCur->ppPrev = &pVtab->pAllCur;
|
|
+ if( pVtab->pAllCur ) pVtab->pAllCur->ppPrev = &pCur->pNext;
|
|
+ pCur->pNext = pVtab->pAllCur;
|
|
+ pVtab->pAllCur = pCur;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static int vtshimClose(sqlite3_vtab_cursor *pX){
|
|
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( !pAux->bDisposed ){
|
|
+ rc = pAux->pMod->xClose(pCur->pChild);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ }
|
|
+ if( pCur->pNext ) pCur->pNext->ppPrev = pCur->ppPrev;
|
|
+ *pCur->ppPrev = pCur->pNext;
|
|
+ sqlite3_free(pCur);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimFilter(
|
|
+ sqlite3_vtab_cursor *pX,
|
|
+ int idxNum,
|
|
+ const char *idxStr,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xFilter(pCur->pChild, idxNum, idxStr, argc, argv);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimNext(sqlite3_vtab_cursor *pX){
|
|
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xNext(pCur->pChild);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimEof(sqlite3_vtab_cursor *pX){
|
|
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return 1;
|
|
+ rc = pAux->pMod->xEof(pCur->pChild);
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimColumn(sqlite3_vtab_cursor *pX, sqlite3_context *ctx, int i){
|
|
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xColumn(pCur->pChild, ctx, i);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimRowid(sqlite3_vtab_cursor *pX, sqlite3_int64 *pRowid){
|
|
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xRowid(pCur->pChild, pRowid);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimUpdate(
|
|
+ sqlite3_vtab *pBase,
|
|
+ int argc,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite3_int64 *pRowid
|
|
+){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xUpdate(pVtab->pChild, argc, argv, pRowid);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimBegin(sqlite3_vtab *pBase){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xBegin(pVtab->pChild);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimSync(sqlite3_vtab *pBase){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xSync(pVtab->pChild);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimCommit(sqlite3_vtab *pBase){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xCommit(pVtab->pChild);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimRollback(sqlite3_vtab *pBase){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xRollback(pVtab->pChild);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimFindFunction(
|
|
+ sqlite3_vtab *pBase,
|
|
+ int nArg,
|
|
+ const char *zName,
|
|
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void **ppArg
|
|
+){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return 0;
|
|
+ rc = pAux->pMod->xFindFunction(pVtab->pChild, nArg, zName, pxFunc, ppArg);
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimRename(sqlite3_vtab *pBase, const char *zNewName){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xRename(pVtab->pChild, zNewName);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimSavepoint(sqlite3_vtab *pBase, int n){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xSavepoint(pVtab->pChild, n);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimRelease(sqlite3_vtab *pBase, int n){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xRelease(pVtab->pChild, n);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vtshimRollbackTo(sqlite3_vtab *pBase, int n){
|
|
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
|
+ vtshim_aux *pAux = pVtab->pAux;
|
|
+ int rc;
|
|
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
|
+ rc = pAux->pMod->xRollbackTo(pVtab->pChild, n);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ VTSHIM_COPY_ERRMSG();
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* The destructor function for a disposible module */
|
|
+static void vtshimAuxDestructor(void *pXAux){
|
|
+ vtshim_aux *pAux = (vtshim_aux*)pXAux;
|
|
+ assert( pAux->pAllVtab==0 );
|
|
+ if( !pAux->bDisposed && pAux->xChildDestroy ){
|
|
+ pAux->xChildDestroy(pAux->pChildAux);
|
|
+ pAux->xChildDestroy = 0;
|
|
+ }
|
|
+ sqlite3_free(pAux->zName);
|
|
+ sqlite3_free(pAux->pMod);
|
|
+ sqlite3_free(pAux);
|
|
+}
|
|
+
|
|
+static int vtshimCopyModule(
|
|
+ const sqlite3_module *pMod, /* Source module to be copied */
|
|
+ sqlite3_module **ppMod /* Destination for copied module */
|
|
+){
|
|
+ sqlite3_module *p;
|
|
+ if( !pMod || !ppMod ) return SQLITE_ERROR;
|
|
+ p = sqlite3_malloc( sizeof(*p) );
|
|
+ if( p==0 ) return SQLITE_NOMEM;
|
|
+ memcpy(p, pMod, sizeof(*p));
|
|
+ *ppMod = p;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+void *sqlite3_create_disposable_module(
|
|
+ sqlite3 *db, /* SQLite connection to register module with */
|
|
+ const char *zName, /* Name of the module */
|
|
+ const sqlite3_module *p, /* Methods for the module */
|
|
+ void *pClientData, /* Client data for xCreate/xConnect */
|
|
+ void(*xDestroy)(void*) /* Module destructor function */
|
|
+){
|
|
+ vtshim_aux *pAux;
|
|
+ sqlite3_module *pMod;
|
|
+ int rc;
|
|
+ pAux = sqlite3_malloc( sizeof(*pAux) );
|
|
+ if( pAux==0 ){
|
|
+ if( xDestroy ) xDestroy(pClientData);
|
|
+ return 0;
|
|
+ }
|
|
+ rc = vtshimCopyModule(p, &pMod);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_free(pAux);
|
|
+ return 0;
|
|
+ }
|
|
+ pAux->pChildAux = pClientData;
|
|
+ pAux->xChildDestroy = xDestroy;
|
|
+ pAux->pMod = pMod;
|
|
+ pAux->db = db;
|
|
+ pAux->zName = sqlite3_mprintf("%s", zName);
|
|
+ pAux->bDisposed = 0;
|
|
+ pAux->pAllVtab = 0;
|
|
+ pAux->sSelf.iVersion = p->iVersion<=2 ? p->iVersion : 2;
|
|
+ pAux->sSelf.xCreate = p->xCreate ? vtshimCreate : 0;
|
|
+ pAux->sSelf.xConnect = p->xConnect ? vtshimConnect : 0;
|
|
+ pAux->sSelf.xBestIndex = p->xBestIndex ? vtshimBestIndex : 0;
|
|
+ pAux->sSelf.xDisconnect = p->xDisconnect ? vtshimDisconnect : 0;
|
|
+ pAux->sSelf.xDestroy = p->xDestroy ? vtshimDestroy : 0;
|
|
+ pAux->sSelf.xOpen = p->xOpen ? vtshimOpen : 0;
|
|
+ pAux->sSelf.xClose = p->xClose ? vtshimClose : 0;
|
|
+ pAux->sSelf.xFilter = p->xFilter ? vtshimFilter : 0;
|
|
+ pAux->sSelf.xNext = p->xNext ? vtshimNext : 0;
|
|
+ pAux->sSelf.xEof = p->xEof ? vtshimEof : 0;
|
|
+ pAux->sSelf.xColumn = p->xColumn ? vtshimColumn : 0;
|
|
+ pAux->sSelf.xRowid = p->xRowid ? vtshimRowid : 0;
|
|
+ pAux->sSelf.xUpdate = p->xUpdate ? vtshimUpdate : 0;
|
|
+ pAux->sSelf.xBegin = p->xBegin ? vtshimBegin : 0;
|
|
+ pAux->sSelf.xSync = p->xSync ? vtshimSync : 0;
|
|
+ pAux->sSelf.xCommit = p->xCommit ? vtshimCommit : 0;
|
|
+ pAux->sSelf.xRollback = p->xRollback ? vtshimRollback : 0;
|
|
+ pAux->sSelf.xFindFunction = p->xFindFunction ? vtshimFindFunction : 0;
|
|
+ pAux->sSelf.xRename = p->xRename ? vtshimRename : 0;
|
|
+ if( p->iVersion>=2 ){
|
|
+ pAux->sSelf.xSavepoint = p->xSavepoint ? vtshimSavepoint : 0;
|
|
+ pAux->sSelf.xRelease = p->xRelease ? vtshimRelease : 0;
|
|
+ pAux->sSelf.xRollbackTo = p->xRollbackTo ? vtshimRollbackTo : 0;
|
|
+ }else{
|
|
+ pAux->sSelf.xSavepoint = 0;
|
|
+ pAux->sSelf.xRelease = 0;
|
|
+ pAux->sSelf.xRollbackTo = 0;
|
|
+ }
|
|
+ rc = sqlite3_create_module_v2(db, zName, &pAux->sSelf,
|
|
+ pAux, vtshimAuxDestructor);
|
|
+ return rc==SQLITE_OK ? (void*)pAux : 0;
|
|
+}
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+void sqlite3_dispose_module(void *pX){
|
|
+ vtshim_aux *pAux = (vtshim_aux*)pX;
|
|
+ if( !pAux->bDisposed ){
|
|
+ vtshim_vtab *pVtab;
|
|
+ vtshim_cursor *pCur;
|
|
+ for(pVtab=pAux->pAllVtab; pVtab; pVtab=pVtab->pNext){
|
|
+ for(pCur=pVtab->pAllCur; pCur; pCur=pCur->pNext){
|
|
+ pAux->pMod->xClose(pCur->pChild);
|
|
+ }
|
|
+ pAux->pMod->xDisconnect(pVtab->pChild);
|
|
+ }
|
|
+ pAux->bDisposed = 1;
|
|
+ if( pAux->xChildDestroy ){
|
|
+ pAux->xChildDestroy(pAux->pChildAux);
|
|
+ pAux->xChildDestroy = 0;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_vtshim_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/wholenumber.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/wholenumber.c 2019-05-14 16:22:31.284954600 +0200
|
|
@@ -0,0 +1,288 @@
|
|
+/*
|
|
+** 2011 April 02
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+**
|
|
+** This file implements a virtual table that returns the whole numbers
|
|
+** between 1 and 4294967295, inclusive.
|
|
+**
|
|
+** Example:
|
|
+**
|
|
+** CREATE VIRTUAL TABLE nums USING wholenumber;
|
|
+** SELECT value FROM nums WHERE value<10;
|
|
+**
|
|
+** Results in:
|
|
+**
|
|
+** 1 2 3 4 5 6 7 8 9
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+
|
|
+/* A wholenumber cursor object */
|
|
+typedef struct wholenumber_cursor wholenumber_cursor;
|
|
+struct wholenumber_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ sqlite3_int64 iValue; /* Current value */
|
|
+ sqlite3_int64 mxValue; /* Maximum value */
|
|
+};
|
|
+
|
|
+/* Methods for the wholenumber module */
|
|
+static int wholenumberConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3_vtab *pNew;
|
|
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+/* Note that for this virtual table, the xCreate and xConnect
|
|
+** methods are identical. */
|
|
+
|
|
+static int wholenumberDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+/* The xDisconnect and xDestroy methods are also the same */
|
|
+
|
|
+
|
|
+/*
|
|
+** Open a new wholenumber cursor.
|
|
+*/
|
|
+static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ wholenumber_cursor *pCur;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close a wholenumber cursor.
|
|
+*/
|
|
+static int wholenumberClose(sqlite3_vtab_cursor *cur){
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance a cursor to its next row of output
|
|
+*/
|
|
+static int wholenumberNext(sqlite3_vtab_cursor *cur){
|
|
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
|
+ pCur->iValue++;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the value associated with a wholenumber.
|
|
+*/
|
|
+static int wholenumberColumn(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ sqlite3_context *ctx,
|
|
+ int i
|
|
+){
|
|
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
|
+ sqlite3_result_int64(ctx, pCur->iValue);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The rowid.
|
|
+*/
|
|
+static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
|
+ *pRowid = pCur->iValue;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal
|
|
+** that the cursor has nothing more to output.
|
|
+*/
|
|
+static int wholenumberEof(sqlite3_vtab_cursor *cur){
|
|
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
|
+ return pCur->iValue>pCur->mxValue || pCur->iValue==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Called to "rewind" a cursor back to the beginning so that
|
|
+** it starts its output over again. Always called at least once
|
|
+** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call.
|
|
+**
|
|
+** idxNum Constraints
|
|
+** ------ ---------------------
|
|
+** 0 (none)
|
|
+** 1 value > $argv0
|
|
+** 2 value >= $argv0
|
|
+** 4 value < $argv0
|
|
+** 8 value <= $argv0
|
|
+**
|
|
+** 5 value > $argv0 AND value < $argv1
|
|
+** 6 value >= $argv0 AND value < $argv1
|
|
+** 9 value > $argv0 AND value <= $argv1
|
|
+** 10 value >= $argv0 AND value <= $argv1
|
|
+*/
|
|
+static int wholenumberFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor;
|
|
+ sqlite3_int64 v;
|
|
+ int i = 0;
|
|
+ pCur->iValue = 1;
|
|
+ pCur->mxValue = 0xffffffff; /* 4294967295 */
|
|
+ if( idxNum & 3 ){
|
|
+ v = sqlite3_value_int64(argv[0]) + (idxNum&1);
|
|
+ if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
|
|
+ i++;
|
|
+ }
|
|
+ if( idxNum & 12 ){
|
|
+ v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
|
|
+ if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Search for terms of these forms:
|
|
+**
|
|
+** (1) value > $value
|
|
+** (2) value >= $value
|
|
+** (4) value < $value
|
|
+** (8) value <= $value
|
|
+**
|
|
+** idxNum is an ORed combination of 1 or 2 with 4 or 8.
|
|
+*/
|
|
+static int wholenumberBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i;
|
|
+ int idxNum = 0;
|
|
+ int argvIdx = 1;
|
|
+ int ltIdx = -1;
|
|
+ int gtIdx = -1;
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->usable==0 ) continue;
|
|
+ if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){
|
|
+ idxNum |= 1;
|
|
+ ltIdx = i;
|
|
+ }
|
|
+ if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){
|
|
+ idxNum |= 2;
|
|
+ ltIdx = i;
|
|
+ }
|
|
+ if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
|
+ idxNum |= 4;
|
|
+ gtIdx = i;
|
|
+ }
|
|
+ if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
|
|
+ idxNum |= 8;
|
|
+ gtIdx = i;
|
|
+ }
|
|
+ }
|
|
+ pIdxInfo->idxNum = idxNum;
|
|
+ if( ltIdx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++;
|
|
+ pIdxInfo->aConstraintUsage[ltIdx].omit = 1;
|
|
+ }
|
|
+ if( gtIdx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx;
|
|
+ pIdxInfo->aConstraintUsage[gtIdx].omit = 1;
|
|
+ }
|
|
+ if( pIdxInfo->nOrderBy==1
|
|
+ && pIdxInfo->aOrderBy[0].desc==0
|
|
+ ){
|
|
+ pIdxInfo->orderByConsumed = 1;
|
|
+ }
|
|
+ if( (idxNum & 12)==0 ){
|
|
+ pIdxInfo->estimatedCost = (double)100000000;
|
|
+ }else if( (idxNum & 3)==0 ){
|
|
+ pIdxInfo->estimatedCost = (double)5;
|
|
+ }else{
|
|
+ pIdxInfo->estimatedCost = (double)1;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** A virtual table module that provides read-only access to a
|
|
+** Tcl global variable namespace.
|
|
+*/
|
|
+static const sqlite3_module wholenumberModule = {
|
|
+ 0, /* iVersion */
|
|
+ wholenumberConnect,
|
|
+ wholenumberConnect,
|
|
+ wholenumberBestIndex,
|
|
+ wholenumberDisconnect,
|
|
+ wholenumberDisconnect,
|
|
+ wholenumberOpen, /* xOpen - open a cursor */
|
|
+ wholenumberClose, /* xClose - close a cursor */
|
|
+ wholenumberFilter, /* xFilter - configure scan constraints */
|
|
+ wholenumberNext, /* xNext - advance a cursor */
|
|
+ wholenumberEof, /* xEof - check for end of scan */
|
|
+ wholenumberColumn, /* xColumn - read data */
|
|
+ wholenumberRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+};
|
|
+
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_wholenumber_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
|
+int sqlite3_extension_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
--- origsrc/sqlite-autoconf-3280000/zipfile.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/zipfile.c 2019-05-14 16:22:31.286907200 +0200
|
|
@@ -0,0 +1,2183 @@
|
|
+/*
|
|
+** 2017-12-26
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements a virtual table for reading and writing ZIP archive
|
|
+** files.
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** SELECT name, sz, datetime(mtime,'unixepoch') FROM zipfile($filename);
|
|
+**
|
|
+** Current limitations:
|
|
+**
|
|
+** * No support for encryption
|
|
+** * No support for ZIP archives spanning multiple files
|
|
+** * No support for zip64 extensions
|
|
+** * Only the "inflate/deflate" (zlib) compression method is supported
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+#include <zlib.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+#ifndef SQLITE_AMALGAMATION
|
|
+
|
|
+typedef sqlite3_int64 i64;
|
|
+typedef unsigned char u8;
|
|
+typedef unsigned short u16;
|
|
+typedef unsigned int u32;
|
|
+#ifndef MIN
|
|
+# define MIN(A,B) ((A)<(B)?(A):(B))
|
|
+#endif
|
|
+#ifndef MAX
|
|
+# define MAX(A,B) ((A)>(B)?(A):(B))
|
|
+#endif
|
|
+
|
|
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
|
+# define ALWAYS(X) (1)
|
|
+# define NEVER(X) (0)
|
|
+#elif !defined(NDEBUG)
|
|
+# define ALWAYS(X) ((X)?1:(assert(0),0))
|
|
+# define NEVER(X) ((X)?(assert(0),1):0)
|
|
+#else
|
|
+# define ALWAYS(X) (X)
|
|
+# define NEVER(X) (X)
|
|
+#endif
|
|
+
|
|
+#endif /* SQLITE_AMALGAMATION */
|
|
+
|
|
+/*
|
|
+** Definitions for mode bitmasks S_IFDIR, S_IFREG and S_IFLNK.
|
|
+**
|
|
+** In some ways it would be better to obtain these values from system
|
|
+** header files. But, the dependency is undesirable and (a) these
|
|
+** have been stable for decades, (b) the values are part of POSIX and
|
|
+** are also made explicit in [man stat], and (c) are part of the
|
|
+** file format for zip archives.
|
|
+*/
|
|
+#ifndef S_IFDIR
|
|
+# define S_IFDIR 0040000
|
|
+#endif
|
|
+#ifndef S_IFREG
|
|
+# define S_IFREG 0100000
|
|
+#endif
|
|
+#ifndef S_IFLNK
|
|
+# define S_IFLNK 0120000
|
|
+#endif
|
|
+
|
|
+static const char ZIPFILE_SCHEMA[] =
|
|
+ "CREATE TABLE y("
|
|
+ "name PRIMARY KEY," /* 0: Name of file in zip archive */
|
|
+ "mode," /* 1: POSIX mode for file */
|
|
+ "mtime," /* 2: Last modification time (secs since 1970)*/
|
|
+ "sz," /* 3: Size of object */
|
|
+ "rawdata," /* 4: Raw data */
|
|
+ "data," /* 5: Uncompressed data */
|
|
+ "method," /* 6: Compression method (integer) */
|
|
+ "z HIDDEN" /* 7: Name of zip file */
|
|
+ ") WITHOUT ROWID;";
|
|
+
|
|
+#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
|
|
+#define ZIPFILE_BUFFER_SIZE (64*1024)
|
|
+
|
|
+
|
|
+/*
|
|
+** Magic numbers used to read and write zip files.
|
|
+**
|
|
+** ZIPFILE_NEWENTRY_MADEBY:
|
|
+** Use this value for the "version-made-by" field in new zip file
|
|
+** entries. The upper byte indicates "unix", and the lower byte
|
|
+** indicates that the zip file matches pkzip specification 3.0.
|
|
+** This is what info-zip seems to do.
|
|
+**
|
|
+** ZIPFILE_NEWENTRY_REQUIRED:
|
|
+** Value for "version-required-to-extract" field of new entries.
|
|
+** Version 2.0 is required to support folders and deflate compression.
|
|
+**
|
|
+** ZIPFILE_NEWENTRY_FLAGS:
|
|
+** Value for "general-purpose-bit-flags" field of new entries. Bit
|
|
+** 11 means "utf-8 filename and comment".
|
|
+**
|
|
+** ZIPFILE_SIGNATURE_CDS:
|
|
+** First 4 bytes of a valid CDS record.
|
|
+**
|
|
+** ZIPFILE_SIGNATURE_LFH:
|
|
+** First 4 bytes of a valid LFH record.
|
|
+**
|
|
+** ZIPFILE_SIGNATURE_EOCD
|
|
+** First 4 bytes of a valid EOCD record.
|
|
+*/
|
|
+#define ZIPFILE_EXTRA_TIMESTAMP 0x5455
|
|
+#define ZIPFILE_NEWENTRY_MADEBY ((3<<8) + 30)
|
|
+#define ZIPFILE_NEWENTRY_REQUIRED 20
|
|
+#define ZIPFILE_NEWENTRY_FLAGS 0x800
|
|
+#define ZIPFILE_SIGNATURE_CDS 0x02014b50
|
|
+#define ZIPFILE_SIGNATURE_LFH 0x04034b50
|
|
+#define ZIPFILE_SIGNATURE_EOCD 0x06054b50
|
|
+
|
|
+/*
|
|
+** The sizes of the fixed-size part of each of the three main data
|
|
+** structures in a zip archive.
|
|
+*/
|
|
+#define ZIPFILE_LFH_FIXED_SZ 30
|
|
+#define ZIPFILE_EOCD_FIXED_SZ 22
|
|
+#define ZIPFILE_CDS_FIXED_SZ 46
|
|
+
|
|
+/*
|
|
+*** 4.3.16 End of central directory record:
|
|
+***
|
|
+*** end of central dir signature 4 bytes (0x06054b50)
|
|
+*** number of this disk 2 bytes
|
|
+*** number of the disk with the
|
|
+*** start of the central directory 2 bytes
|
|
+*** total number of entries in the
|
|
+*** central directory on this disk 2 bytes
|
|
+*** total number of entries in
|
|
+*** the central directory 2 bytes
|
|
+*** size of the central directory 4 bytes
|
|
+*** offset of start of central
|
|
+*** directory with respect to
|
|
+*** the starting disk number 4 bytes
|
|
+*** .ZIP file comment length 2 bytes
|
|
+*** .ZIP file comment (variable size)
|
|
+*/
|
|
+typedef struct ZipfileEOCD ZipfileEOCD;
|
|
+struct ZipfileEOCD {
|
|
+ u16 iDisk;
|
|
+ u16 iFirstDisk;
|
|
+ u16 nEntry;
|
|
+ u16 nEntryTotal;
|
|
+ u32 nSize;
|
|
+ u32 iOffset;
|
|
+};
|
|
+
|
|
+/*
|
|
+*** 4.3.12 Central directory structure:
|
|
+***
|
|
+*** ...
|
|
+***
|
|
+*** central file header signature 4 bytes (0x02014b50)
|
|
+*** version made by 2 bytes
|
|
+*** version needed to extract 2 bytes
|
|
+*** general purpose bit flag 2 bytes
|
|
+*** compression method 2 bytes
|
|
+*** last mod file time 2 bytes
|
|
+*** last mod file date 2 bytes
|
|
+*** crc-32 4 bytes
|
|
+*** compressed size 4 bytes
|
|
+*** uncompressed size 4 bytes
|
|
+*** file name length 2 bytes
|
|
+*** extra field length 2 bytes
|
|
+*** file comment length 2 bytes
|
|
+*** disk number start 2 bytes
|
|
+*** internal file attributes 2 bytes
|
|
+*** external file attributes 4 bytes
|
|
+*** relative offset of local header 4 bytes
|
|
+*/
|
|
+typedef struct ZipfileCDS ZipfileCDS;
|
|
+struct ZipfileCDS {
|
|
+ u16 iVersionMadeBy;
|
|
+ u16 iVersionExtract;
|
|
+ u16 flags;
|
|
+ u16 iCompression;
|
|
+ u16 mTime;
|
|
+ u16 mDate;
|
|
+ u32 crc32;
|
|
+ u32 szCompressed;
|
|
+ u32 szUncompressed;
|
|
+ u16 nFile;
|
|
+ u16 nExtra;
|
|
+ u16 nComment;
|
|
+ u16 iDiskStart;
|
|
+ u16 iInternalAttr;
|
|
+ u32 iExternalAttr;
|
|
+ u32 iOffset;
|
|
+ char *zFile; /* Filename (sqlite3_malloc()) */
|
|
+};
|
|
+
|
|
+/*
|
|
+*** 4.3.7 Local file header:
|
|
+***
|
|
+*** local file header signature 4 bytes (0x04034b50)
|
|
+*** version needed to extract 2 bytes
|
|
+*** general purpose bit flag 2 bytes
|
|
+*** compression method 2 bytes
|
|
+*** last mod file time 2 bytes
|
|
+*** last mod file date 2 bytes
|
|
+*** crc-32 4 bytes
|
|
+*** compressed size 4 bytes
|
|
+*** uncompressed size 4 bytes
|
|
+*** file name length 2 bytes
|
|
+*** extra field length 2 bytes
|
|
+***
|
|
+*/
|
|
+typedef struct ZipfileLFH ZipfileLFH;
|
|
+struct ZipfileLFH {
|
|
+ u16 iVersionExtract;
|
|
+ u16 flags;
|
|
+ u16 iCompression;
|
|
+ u16 mTime;
|
|
+ u16 mDate;
|
|
+ u32 crc32;
|
|
+ u32 szCompressed;
|
|
+ u32 szUncompressed;
|
|
+ u16 nFile;
|
|
+ u16 nExtra;
|
|
+};
|
|
+
|
|
+typedef struct ZipfileEntry ZipfileEntry;
|
|
+struct ZipfileEntry {
|
|
+ ZipfileCDS cds; /* Parsed CDS record */
|
|
+ u32 mUnixTime; /* Modification time, in UNIX format */
|
|
+ u8 *aExtra; /* cds.nExtra+cds.nComment bytes of extra data */
|
|
+ i64 iDataOff; /* Offset to data in file (if aData==0) */
|
|
+ u8 *aData; /* cds.szCompressed bytes of compressed data */
|
|
+ ZipfileEntry *pNext; /* Next element in in-memory CDS */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Cursor type for zipfile tables.
|
|
+*/
|
|
+typedef struct ZipfileCsr ZipfileCsr;
|
|
+struct ZipfileCsr {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ i64 iId; /* Cursor ID */
|
|
+ u8 bEof; /* True when at EOF */
|
|
+ u8 bNoop; /* If next xNext() call is no-op */
|
|
+
|
|
+ /* Used outside of write transactions */
|
|
+ FILE *pFile; /* Zip file */
|
|
+ i64 iNextOff; /* Offset of next record in central directory */
|
|
+ ZipfileEOCD eocd; /* Parse of central directory record */
|
|
+
|
|
+ ZipfileEntry *pFreeEntry; /* Free this list when cursor is closed or reset */
|
|
+ ZipfileEntry *pCurrent; /* Current entry */
|
|
+ ZipfileCsr *pCsrNext; /* Next cursor on same virtual table */
|
|
+};
|
|
+
|
|
+typedef struct ZipfileTab ZipfileTab;
|
|
+struct ZipfileTab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ char *zFile; /* Zip file this table accesses (may be NULL) */
|
|
+ sqlite3 *db; /* Host database connection */
|
|
+ u8 *aBuffer; /* Temporary buffer used for various tasks */
|
|
+
|
|
+ ZipfileCsr *pCsrList; /* List of cursors */
|
|
+ i64 iNextCsrid;
|
|
+
|
|
+ /* The following are used by write transactions only */
|
|
+ ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */
|
|
+ ZipfileEntry *pLastEntry; /* Last element in pFirstEntry list */
|
|
+ FILE *pWriteFd; /* File handle open on zip archive */
|
|
+ i64 szCurrent; /* Current size of zip archive */
|
|
+ i64 szOrig; /* Size of archive at start of transaction */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Set the error message contained in context ctx to the results of
|
|
+** vprintf(zFmt, ...).
|
|
+*/
|
|
+static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
|
|
+ char *zMsg = 0;
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ zMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ sqlite3_result_error(ctx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+/*
|
|
+** If string zIn is quoted, dequote it in place. Otherwise, if the string
|
|
+** is not quoted, do nothing.
|
|
+*/
|
|
+static void zipfileDequote(char *zIn){
|
|
+ char q = zIn[0];
|
|
+ if( q=='"' || q=='\'' || q=='`' || q=='[' ){
|
|
+ int iIn = 1;
|
|
+ int iOut = 0;
|
|
+ if( q=='[' ) q = ']';
|
|
+ while( ALWAYS(zIn[iIn]) ){
|
|
+ char c = zIn[iIn++];
|
|
+ if( c==q && zIn[iIn++]!=q ) break;
|
|
+ zIn[iOut++] = c;
|
|
+ }
|
|
+ zIn[iOut] = '\0';
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Construct a new ZipfileTab virtual table object.
|
|
+**
|
|
+** argv[0] -> module name ("zipfile")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[...] -> "column name" and other module argument fields.
|
|
+*/
|
|
+static int zipfileConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ size_t nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE;
|
|
+ size_t nFile = 0;
|
|
+ const char *zFile = 0;
|
|
+ ZipfileTab *pNew = 0;
|
|
+ int rc;
|
|
+
|
|
+ /* If the table name is not "zipfile", require that the argument be
|
|
+ ** specified. This stops zipfile tables from being created as:
|
|
+ **
|
|
+ ** CREATE VIRTUAL TABLE zzz USING zipfile();
|
|
+ **
|
|
+ ** It does not prevent:
|
|
+ **
|
|
+ ** CREATE VIRTUAL TABLE zipfile USING zipfile();
|
|
+ */
|
|
+ assert( 0==sqlite3_stricmp(argv[0], "zipfile") );
|
|
+ if( (0!=sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){
|
|
+ *pzErr = sqlite3_mprintf("zipfile constructor requires one argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ if( argc>3 ){
|
|
+ zFile = argv[3];
|
|
+ nFile = strlen(zFile)+1;
|
|
+ }
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = (ZipfileTab*)sqlite3_malloc(nByte+nFile);
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, nByte+nFile);
|
|
+ pNew->db = db;
|
|
+ pNew->aBuffer = (u8*)&pNew[1];
|
|
+ if( zFile ){
|
|
+ pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE];
|
|
+ memcpy(pNew->zFile, zFile, nFile);
|
|
+ zipfileDequote(pNew->zFile);
|
|
+ }
|
|
+ }
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the ZipfileEntry structure indicated by the only argument.
|
|
+*/
|
|
+static void zipfileEntryFree(ZipfileEntry *p){
|
|
+ if( p ){
|
|
+ sqlite3_free(p->cds.zFile);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Release resources that should be freed at the end of a write
|
|
+** transaction.
|
|
+*/
|
|
+static void zipfileCleanupTransaction(ZipfileTab *pTab){
|
|
+ ZipfileEntry *pEntry;
|
|
+ ZipfileEntry *pNext;
|
|
+
|
|
+ if( pTab->pWriteFd ){
|
|
+ fclose(pTab->pWriteFd);
|
|
+ pTab->pWriteFd = 0;
|
|
+ }
|
|
+ for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){
|
|
+ pNext = pEntry->pNext;
|
|
+ zipfileEntryFree(pEntry);
|
|
+ }
|
|
+ pTab->pFirstEntry = 0;
|
|
+ pTab->pLastEntry = 0;
|
|
+ pTab->szCurrent = 0;
|
|
+ pTab->szOrig = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for zipfile vtab objects.
|
|
+*/
|
|
+static int zipfileDisconnect(sqlite3_vtab *pVtab){
|
|
+ zipfileCleanupTransaction((ZipfileTab*)pVtab);
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new ZipfileCsr object.
|
|
+*/
|
|
+static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)p;
|
|
+ ZipfileCsr *pCsr;
|
|
+ pCsr = sqlite3_malloc(sizeof(*pCsr));
|
|
+ *ppCsr = (sqlite3_vtab_cursor*)pCsr;
|
|
+ if( pCsr==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ memset(pCsr, 0, sizeof(*pCsr));
|
|
+ pCsr->iId = ++pTab->iNextCsrid;
|
|
+ pCsr->pCsrNext = pTab->pCsrList;
|
|
+ pTab->pCsrList = pCsr;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset a cursor back to the state it was in when first returned
|
|
+** by zipfileOpen().
|
|
+*/
|
|
+static void zipfileResetCursor(ZipfileCsr *pCsr){
|
|
+ ZipfileEntry *p;
|
|
+ ZipfileEntry *pNext;
|
|
+
|
|
+ pCsr->bEof = 0;
|
|
+ if( pCsr->pFile ){
|
|
+ fclose(pCsr->pFile);
|
|
+ pCsr->pFile = 0;
|
|
+ zipfileEntryFree(pCsr->pCurrent);
|
|
+ pCsr->pCurrent = 0;
|
|
+ }
|
|
+
|
|
+ for(p=pCsr->pFreeEntry; p; p=pNext){
|
|
+ pNext = p->pNext;
|
|
+ zipfileEntryFree(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for an ZipfileCsr.
|
|
+*/
|
|
+static int zipfileClose(sqlite3_vtab_cursor *cur){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab);
|
|
+ ZipfileCsr **pp;
|
|
+ zipfileResetCursor(pCsr);
|
|
+
|
|
+ /* Remove this cursor from the ZipfileTab.pCsrList list. */
|
|
+ for(pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext));
|
|
+ *pp = pCsr->pCsrNext;
|
|
+
|
|
+ sqlite3_free(pCsr);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set the error message for the virtual table associated with cursor
|
|
+** pCsr to the results of vprintf(zFmt, ...).
|
|
+*/
|
|
+static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ sqlite3_free(pTab->base.zErrMsg);
|
|
+ pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ sqlite3_free(pCsr->base.pVtab->zErrMsg);
|
|
+ pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read nRead bytes of data from offset iOff of file pFile into buffer
|
|
+** aRead[]. Return SQLITE_OK if successful, or an SQLite error code
|
|
+** otherwise.
|
|
+**
|
|
+** If an error does occur, output variable (*pzErrmsg) may be set to point
|
|
+** to an English language error message. It is the responsibility of the
|
|
+** caller to eventually free this buffer using
|
|
+** sqlite3_free().
|
|
+*/
|
|
+static int zipfileReadData(
|
|
+ FILE *pFile, /* Read from this file */
|
|
+ u8 *aRead, /* Read into this buffer */
|
|
+ int nRead, /* Number of bytes to read */
|
|
+ i64 iOff, /* Offset to read from */
|
|
+ char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
|
|
+){
|
|
+ size_t n;
|
|
+ fseek(pFile, (long)iOff, SEEK_SET);
|
|
+ n = fread(aRead, 1, nRead, pFile);
|
|
+ if( (int)n!=nRead ){
|
|
+ *pzErrmsg = sqlite3_mprintf("error in fread()");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static int zipfileAppendData(
|
|
+ ZipfileTab *pTab,
|
|
+ const u8 *aWrite,
|
|
+ int nWrite
|
|
+){
|
|
+ size_t n;
|
|
+ fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
|
|
+ n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
|
|
+ if( (int)n!=nWrite ){
|
|
+ pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ pTab->szCurrent += nWrite;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read and return a 16-bit little-endian unsigned integer from buffer aBuf.
|
|
+*/
|
|
+static u16 zipfileGetU16(const u8 *aBuf){
|
|
+ return (aBuf[1] << 8) + aBuf[0];
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
|
|
+*/
|
|
+static u32 zipfileGetU32(const u8 *aBuf){
|
|
+ return ((u32)(aBuf[3]) << 24)
|
|
+ + ((u32)(aBuf[2]) << 16)
|
|
+ + ((u32)(aBuf[1]) << 8)
|
|
+ + ((u32)(aBuf[0]) << 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write a 16-bit little endiate integer into buffer aBuf.
|
|
+*/
|
|
+static void zipfilePutU16(u8 *aBuf, u16 val){
|
|
+ aBuf[0] = val & 0xFF;
|
|
+ aBuf[1] = (val>>8) & 0xFF;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write a 32-bit little endiate integer into buffer aBuf.
|
|
+*/
|
|
+static void zipfilePutU32(u8 *aBuf, u32 val){
|
|
+ aBuf[0] = val & 0xFF;
|
|
+ aBuf[1] = (val>>8) & 0xFF;
|
|
+ aBuf[2] = (val>>16) & 0xFF;
|
|
+ aBuf[3] = (val>>24) & 0xFF;
|
|
+}
|
|
+
|
|
+#define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) )
|
|
+#define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) )
|
|
+
|
|
+#define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; }
|
|
+#define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; }
|
|
+
|
|
+/*
|
|
+** Magic numbers used to read CDS records.
|
|
+*/
|
|
+#define ZIPFILE_CDS_NFILE_OFF 28
|
|
+#define ZIPFILE_CDS_SZCOMPRESSED_OFF 20
|
|
+
|
|
+/*
|
|
+** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR
|
|
+** if the record is not well-formed, or SQLITE_OK otherwise.
|
|
+*/
|
|
+static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){
|
|
+ u8 *aRead = aBuf;
|
|
+ u32 sig = zipfileRead32(aRead);
|
|
+ int rc = SQLITE_OK;
|
|
+ if( sig!=ZIPFILE_SIGNATURE_CDS ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ pCDS->iVersionMadeBy = zipfileRead16(aRead);
|
|
+ pCDS->iVersionExtract = zipfileRead16(aRead);
|
|
+ pCDS->flags = zipfileRead16(aRead);
|
|
+ pCDS->iCompression = zipfileRead16(aRead);
|
|
+ pCDS->mTime = zipfileRead16(aRead);
|
|
+ pCDS->mDate = zipfileRead16(aRead);
|
|
+ pCDS->crc32 = zipfileRead32(aRead);
|
|
+ pCDS->szCompressed = zipfileRead32(aRead);
|
|
+ pCDS->szUncompressed = zipfileRead32(aRead);
|
|
+ assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
|
|
+ pCDS->nFile = zipfileRead16(aRead);
|
|
+ pCDS->nExtra = zipfileRead16(aRead);
|
|
+ pCDS->nComment = zipfileRead16(aRead);
|
|
+ pCDS->iDiskStart = zipfileRead16(aRead);
|
|
+ pCDS->iInternalAttr = zipfileRead16(aRead);
|
|
+ pCDS->iExternalAttr = zipfileRead32(aRead);
|
|
+ pCDS->iOffset = zipfileRead32(aRead);
|
|
+ assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] );
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Decode the LFH record in buffer aBuf into (*pLFH). Return SQLITE_ERROR
|
|
+** if the record is not well-formed, or SQLITE_OK otherwise.
|
|
+*/
|
|
+static int zipfileReadLFH(
|
|
+ u8 *aBuffer,
|
|
+ ZipfileLFH *pLFH
|
|
+){
|
|
+ u8 *aRead = aBuffer;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ u32 sig = zipfileRead32(aRead);
|
|
+ if( sig!=ZIPFILE_SIGNATURE_LFH ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ pLFH->iVersionExtract = zipfileRead16(aRead);
|
|
+ pLFH->flags = zipfileRead16(aRead);
|
|
+ pLFH->iCompression = zipfileRead16(aRead);
|
|
+ pLFH->mTime = zipfileRead16(aRead);
|
|
+ pLFH->mDate = zipfileRead16(aRead);
|
|
+ pLFH->crc32 = zipfileRead32(aRead);
|
|
+ pLFH->szCompressed = zipfileRead32(aRead);
|
|
+ pLFH->szUncompressed = zipfileRead32(aRead);
|
|
+ pLFH->nFile = zipfileRead16(aRead);
|
|
+ pLFH->nExtra = zipfileRead16(aRead);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields.
|
|
+** Scan through this buffer to find an "extra-timestamp" field. If one
|
|
+** exists, extract the 32-bit modification-timestamp from it and store
|
|
+** the value in output parameter *pmTime.
|
|
+**
|
|
+** Zero is returned if no extra-timestamp record could be found (and so
|
|
+** *pmTime is left unchanged), or non-zero otherwise.
|
|
+**
|
|
+** The general format of an extra field is:
|
|
+**
|
|
+** Header ID 2 bytes
|
|
+** Data Size 2 bytes
|
|
+** Data N bytes
|
|
+*/
|
|
+static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
|
|
+ int ret = 0;
|
|
+ u8 *p = aExtra;
|
|
+ u8 *pEnd = &aExtra[nExtra];
|
|
+
|
|
+ while( p<pEnd ){
|
|
+ u16 id = zipfileRead16(p);
|
|
+ u16 nByte = zipfileRead16(p);
|
|
+
|
|
+ switch( id ){
|
|
+ case ZIPFILE_EXTRA_TIMESTAMP: {
|
|
+ u8 b = p[0];
|
|
+ if( b & 0x01 ){ /* 0x01 -> modtime is present */
|
|
+ *pmTime = zipfileGetU32(&p[1]);
|
|
+ ret = 1;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ p += nByte;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert the standard MS-DOS timestamp stored in the mTime and mDate
|
|
+** fields of the CDS structure passed as the only argument to a 32-bit
|
|
+** UNIX seconds-since-the-epoch timestamp. Return the result.
|
|
+**
|
|
+** "Standard" MS-DOS time format:
|
|
+**
|
|
+** File modification time:
|
|
+** Bits 00-04: seconds divided by 2
|
|
+** Bits 05-10: minute
|
|
+** Bits 11-15: hour
|
|
+** File modification date:
|
|
+** Bits 00-04: day
|
|
+** Bits 05-08: month (1-12)
|
|
+** Bits 09-15: years from 1980
|
|
+**
|
|
+** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx
|
|
+*/
|
|
+static u32 zipfileMtime(ZipfileCDS *pCDS){
|
|
+ int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F));
|
|
+ int M = ((pCDS->mDate >> 5) & 0x0F);
|
|
+ int D = (pCDS->mDate & 0x1F);
|
|
+ int B = -13;
|
|
+
|
|
+ int sec = (pCDS->mTime & 0x1F)*2;
|
|
+ int min = (pCDS->mTime >> 5) & 0x3F;
|
|
+ int hr = (pCDS->mTime >> 11) & 0x1F;
|
|
+ i64 JD;
|
|
+
|
|
+ /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */
|
|
+
|
|
+ /* Calculate the JD in seconds for noon on the day in question */
|
|
+ if( M<3 ){
|
|
+ Y = Y-1;
|
|
+ M = M+12;
|
|
+ }
|
|
+ JD = (i64)(24*60*60) * (
|
|
+ (int)(365.25 * (Y + 4716))
|
|
+ + (int)(30.6001 * (M + 1))
|
|
+ + D + B - 1524
|
|
+ );
|
|
+
|
|
+ /* Correct the JD for the time within the day */
|
|
+ JD += (hr-12) * 3600 + min * 60 + sec;
|
|
+
|
|
+ /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */
|
|
+ return (u32)(JD - (i64)(24405875) * 24*60*6);
|
|
+}
|
|
+
|
|
+/*
|
|
+** The opposite of zipfileMtime(). This function populates the mTime and
|
|
+** mDate fields of the CDS structure passed as the first argument according
|
|
+** to the UNIX timestamp value passed as the second.
|
|
+*/
|
|
+static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
|
|
+ /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */
|
|
+ i64 JD = (i64)2440588 + mUnixTime / (24*60*60);
|
|
+
|
|
+ int A, B, C, D, E;
|
|
+ int yr, mon, day;
|
|
+ int hr, min, sec;
|
|
+
|
|
+ A = (int)((JD - 1867216.25)/36524.25);
|
|
+ A = (int)(JD + 1 + A - (A/4));
|
|
+ B = A + 1524;
|
|
+ C = (int)((B - 122.1)/365.25);
|
|
+ D = (36525*(C&32767))/100;
|
|
+ E = (int)((B-D)/30.6001);
|
|
+
|
|
+ day = B - D - (int)(30.6001*E);
|
|
+ mon = (E<14 ? E-1 : E-13);
|
|
+ yr = mon>2 ? C-4716 : C-4715;
|
|
+
|
|
+ hr = (mUnixTime % (24*60*60)) / (60*60);
|
|
+ min = (mUnixTime % (60*60)) / 60;
|
|
+ sec = (mUnixTime % 60);
|
|
+
|
|
+ if( yr>=1980 ){
|
|
+ pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9));
|
|
+ pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11));
|
|
+ }else{
|
|
+ pCds->mDate = pCds->mTime = 0;
|
|
+ }
|
|
+
|
|
+ assert( mUnixTime<315507600
|
|
+ || mUnixTime==zipfileMtime(pCds)
|
|
+ || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
|
|
+ /* || (mUnixTime % 2) */
|
|
+ );
|
|
+}
|
|
+
|
|
+/*
|
|
+** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
|
|
+** size) containing an entire zip archive image. Or, if aBlob is NULL,
|
|
+** then pFile is a file-handle open on a zip file. In either case, this
|
|
+** function creates a ZipfileEntry object based on the zip archive entry
|
|
+** for which the CDS record is at offset iOff.
|
|
+**
|
|
+** If successful, SQLITE_OK is returned and (*ppEntry) set to point to
|
|
+** the new object. Otherwise, an SQLite error code is returned and the
|
|
+** final value of (*ppEntry) undefined.
|
|
+*/
|
|
+static int zipfileGetEntry(
|
|
+ ZipfileTab *pTab, /* Store any error message here */
|
|
+ const u8 *aBlob, /* Pointer to in-memory file image */
|
|
+ int nBlob, /* Size of aBlob[] in bytes */
|
|
+ FILE *pFile, /* If aBlob==0, read from this file */
|
|
+ i64 iOff, /* Offset of CDS record */
|
|
+ ZipfileEntry **ppEntry /* OUT: Pointer to new object */
|
|
+){
|
|
+ u8 *aRead;
|
|
+ char **pzErr = &pTab->base.zErrMsg;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( aBlob==0 ){
|
|
+ aRead = pTab->aBuffer;
|
|
+ rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
|
|
+ }else{
|
|
+ aRead = (u8*)&aBlob[iOff];
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ size_t nAlloc;
|
|
+ ZipfileEntry *pNew;
|
|
+
|
|
+ int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]);
|
|
+ int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]);
|
|
+ nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]);
|
|
+
|
|
+ nAlloc = sizeof(ZipfileEntry) + nExtra;
|
|
+ if( aBlob ){
|
|
+ nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]);
|
|
+ }
|
|
+
|
|
+ pNew = (ZipfileEntry*)sqlite3_malloc(nAlloc);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pNew, 0, sizeof(ZipfileEntry));
|
|
+ rc = zipfileReadCDS(aRead, &pNew->cds);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
|
|
+ }else if( aBlob==0 ){
|
|
+ rc = zipfileReadData(
|
|
+ pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
|
|
+ );
|
|
+ }else{
|
|
+ aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ u32 *pt = &pNew->mUnixTime;
|
|
+ pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead);
|
|
+ pNew->aExtra = (u8*)&pNew[1];
|
|
+ memcpy(pNew->aExtra, &aRead[nFile], nExtra);
|
|
+ if( pNew->cds.zFile==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else if( 0==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){
|
|
+ pNew->mUnixTime = zipfileMtime(&pNew->cds);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ static const int szFix = ZIPFILE_LFH_FIXED_SZ;
|
|
+ ZipfileLFH lfh;
|
|
+ if( pFile ){
|
|
+ rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
|
|
+ }else{
|
|
+ aRead = (u8*)&aBlob[pNew->cds.iOffset];
|
|
+ }
|
|
+
|
|
+ rc = zipfileReadLFH(aRead, &lfh);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
|
|
+ pNew->iDataOff += lfh.nFile + lfh.nExtra;
|
|
+ if( aBlob && pNew->cds.szCompressed ){
|
|
+ pNew->aData = &pNew->aExtra[nExtra];
|
|
+ memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
|
|
+ }
|
|
+ }else{
|
|
+ *pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
|
|
+ (int)pNew->cds.iOffset
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ zipfileEntryFree(pNew);
|
|
+ }else{
|
|
+ *ppEntry = pNew;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance an ZipfileCsr to its next row of output.
|
|
+*/
|
|
+static int zipfileNext(sqlite3_vtab_cursor *cur){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( pCsr->pFile ){
|
|
+ i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
|
|
+ zipfileEntryFree(pCsr->pCurrent);
|
|
+ pCsr->pCurrent = 0;
|
|
+ if( pCsr->iNextOff>=iEof ){
|
|
+ pCsr->bEof = 1;
|
|
+ }else{
|
|
+ ZipfileEntry *p = 0;
|
|
+ ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab);
|
|
+ rc = zipfileGetEntry(pTab, 0, 0, pCsr->pFile, pCsr->iNextOff, &p);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
|
|
+ pCsr->iNextOff += (int)p->cds.nExtra + p->cds.nFile + p->cds.nComment;
|
|
+ }
|
|
+ pCsr->pCurrent = p;
|
|
+ }
|
|
+ }else{
|
|
+ if( !pCsr->bNoop ){
|
|
+ pCsr->pCurrent = pCsr->pCurrent->pNext;
|
|
+ }
|
|
+ if( pCsr->pCurrent==0 ){
|
|
+ pCsr->bEof = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pCsr->bNoop = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void zipfileFree(void *p) {
|
|
+ sqlite3_free(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Buffer aIn (size nIn bytes) contains compressed data. Uncompressed, the
|
|
+** size is nOut bytes. This function uncompresses the data and sets the
|
|
+** return value in context pCtx to the result (a blob).
|
|
+**
|
|
+** If an error occurs, an error code is left in pCtx instead.
|
|
+*/
|
|
+static void zipfileInflate(
|
|
+ sqlite3_context *pCtx, /* Store result here */
|
|
+ const u8 *aIn, /* Compressed data */
|
|
+ int nIn, /* Size of buffer aIn[] in bytes */
|
|
+ int nOut /* Expected output size */
|
|
+){
|
|
+ u8 *aRes = sqlite3_malloc(nOut);
|
|
+ if( aRes==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ int err;
|
|
+ z_stream str;
|
|
+ memset(&str, 0, sizeof(str));
|
|
+
|
|
+ str.next_in = (Byte*)aIn;
|
|
+ str.avail_in = nIn;
|
|
+ str.next_out = (Byte*)aRes;
|
|
+ str.avail_out = nOut;
|
|
+
|
|
+ err = inflateInit2(&str, -15);
|
|
+ if( err!=Z_OK ){
|
|
+ zipfileCtxErrorMsg(pCtx, "inflateInit2() failed (%d)", err);
|
|
+ }else{
|
|
+ err = inflate(&str, Z_NO_FLUSH);
|
|
+ if( err!=Z_STREAM_END ){
|
|
+ zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
|
|
+ }else{
|
|
+ sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
|
|
+ aRes = 0;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(aRes);
|
|
+ inflateEnd(&str);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Buffer aIn (size nIn bytes) contains uncompressed data. This function
|
|
+** compresses it and sets (*ppOut) to point to a buffer containing the
|
|
+** compressed data. The caller is responsible for eventually calling
|
|
+** sqlite3_free() to release buffer (*ppOut). Before returning, (*pnOut)
|
|
+** is set to the size of buffer (*ppOut) in bytes.
|
|
+**
|
|
+** If no error occurs, SQLITE_OK is returned. Otherwise, an SQLite error
|
|
+** code is returned and an error message left in virtual-table handle
|
|
+** pTab. The values of (*ppOut) and (*pnOut) are left unchanged in this
|
|
+** case.
|
|
+*/
|
|
+static int zipfileDeflate(
|
|
+ const u8 *aIn, size_t nIn, /* Input */
|
|
+ u8 **ppOut, size_t *pnOut, /* Output */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ size_t nAlloc = compressBound(nIn);
|
|
+ u8 *aOut;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ aOut = (u8*)sqlite3_malloc(nAlloc);
|
|
+ if( aOut==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int res;
|
|
+ z_stream str;
|
|
+ memset(&str, 0, sizeof(str));
|
|
+ str.next_in = (Bytef*)aIn;
|
|
+ str.avail_in = nIn;
|
|
+ str.next_out = aOut;
|
|
+ str.avail_out = nAlloc;
|
|
+
|
|
+ deflateInit2(&str, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
|
|
+ res = deflate(&str, Z_FINISH);
|
|
+
|
|
+ if( res==Z_STREAM_END ){
|
|
+ *ppOut = aOut;
|
|
+ *pnOut = str.total_out;
|
|
+ }else{
|
|
+ sqlite3_free(aOut);
|
|
+ *pzErr = sqlite3_mprintf("zipfile: deflate() error");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ deflateEnd(&str);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the series_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int zipfileColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ ZipfileCDS *pCDS = &pCsr->pCurrent->cds;
|
|
+ int rc = SQLITE_OK;
|
|
+ switch( i ){
|
|
+ case 0: /* name */
|
|
+ sqlite3_result_text(ctx, pCDS->zFile, -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ case 1: /* mode */
|
|
+ /* TODO: Whether or not the following is correct surely depends on
|
|
+ ** the platform on which the archive was created. */
|
|
+ sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16);
|
|
+ break;
|
|
+ case 2: { /* mtime */
|
|
+ sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime);
|
|
+ break;
|
|
+ }
|
|
+ case 3: { /* sz */
|
|
+ if( sqlite3_vtab_nochange(ctx)==0 ){
|
|
+ sqlite3_result_int64(ctx, pCDS->szUncompressed);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case 4: /* rawdata */
|
|
+ if( sqlite3_vtab_nochange(ctx) ) break;
|
|
+ case 5: { /* data */
|
|
+ if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){
|
|
+ size_t sz = pCDS->szCompressed;
|
|
+ size_t szFinal = pCDS->szUncompressed;
|
|
+ if( szFinal>0 ){
|
|
+ u8 *aBuf;
|
|
+ u8 *aFree = 0;
|
|
+ if( pCsr->pCurrent->aData ){
|
|
+ aBuf = pCsr->pCurrent->aData;
|
|
+ }else{
|
|
+ aBuf = aFree = sqlite3_malloc(sz);
|
|
+ if( aBuf==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ FILE *pFile = pCsr->pFile;
|
|
+ if( pFile==0 ){
|
|
+ pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
|
|
+ }
|
|
+ rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff,
|
|
+ &pCsr->base.pVtab->zErrMsg
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( i==5 && pCDS->iCompression ){
|
|
+ zipfileInflate(ctx, aBuf, sz, szFinal);
|
|
+ }else{
|
|
+ sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(aFree);
|
|
+ }else{
|
|
+ /* Figure out if this is a directory or a zero-sized file. Consider
|
|
+ ** it to be a directory either if the mode suggests so, or if
|
|
+ ** the final character in the name is '/'. */
|
|
+ u32 mode = pCDS->iExternalAttr >> 16;
|
|
+ if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){
|
|
+ sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case 6: /* method */
|
|
+ sqlite3_result_int(ctx, pCDS->iCompression);
|
|
+ break;
|
|
+ default: /* z */
|
|
+ assert( i==7 );
|
|
+ sqlite3_result_int64(ctx, pCsr->iId);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor is at EOF.
|
|
+*/
|
|
+static int zipfileEof(sqlite3_vtab_cursor *cur){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ return pCsr->bEof;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If aBlob is not NULL, then it points to a buffer nBlob bytes in size
|
|
+** containing an entire zip archive image. Or, if aBlob is NULL, then pFile
|
|
+** is guaranteed to be a file-handle open on a zip file.
|
|
+**
|
|
+** This function attempts to locate the EOCD record within the zip archive
|
|
+** and populate *pEOCD with the results of decoding it. SQLITE_OK is
|
|
+** returned if successful. Otherwise, an SQLite error code is returned and
|
|
+** an English language error message may be left in virtual-table pTab.
|
|
+*/
|
|
+static int zipfileReadEOCD(
|
|
+ ZipfileTab *pTab, /* Return errors here */
|
|
+ const u8 *aBlob, /* Pointer to in-memory file image */
|
|
+ int nBlob, /* Size of aBlob[] in bytes */
|
|
+ FILE *pFile, /* Read from this file if aBlob==0 */
|
|
+ ZipfileEOCD *pEOCD /* Object to populate */
|
|
+){
|
|
+ u8 *aRead = pTab->aBuffer; /* Temporary buffer */
|
|
+ int nRead; /* Bytes to read from file */
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( aBlob==0 ){
|
|
+ i64 iOff; /* Offset to read from */
|
|
+ i64 szFile; /* Total size of file in bytes */
|
|
+ fseek(pFile, 0, SEEK_END);
|
|
+ szFile = (i64)ftell(pFile);
|
|
+ if( szFile==0 ){
|
|
+ memset(pEOCD, 0, sizeof(ZipfileEOCD));
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
|
|
+ iOff = szFile - nRead;
|
|
+ rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg);
|
|
+ }else{
|
|
+ nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE));
|
|
+ aRead = (u8*)&aBlob[nBlob-nRead];
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int i;
|
|
+
|
|
+ /* Scan backwards looking for the signature bytes */
|
|
+ for(i=nRead-20; i>=0; i--){
|
|
+ if( aRead[i]==0x50 && aRead[i+1]==0x4b
|
|
+ && aRead[i+2]==0x05 && aRead[i+3]==0x06
|
|
+ ){
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( i<0 ){
|
|
+ pTab->base.zErrMsg = sqlite3_mprintf(
|
|
+ "cannot find end of central directory record"
|
|
+ );
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ aRead += i+4;
|
|
+ pEOCD->iDisk = zipfileRead16(aRead);
|
|
+ pEOCD->iFirstDisk = zipfileRead16(aRead);
|
|
+ pEOCD->nEntry = zipfileRead16(aRead);
|
|
+ pEOCD->nEntryTotal = zipfileRead16(aRead);
|
|
+ pEOCD->nSize = zipfileRead32(aRead);
|
|
+ pEOCD->iOffset = zipfileRead32(aRead);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add object pNew to the linked list that begins at ZipfileTab.pFirstEntry
|
|
+** and ends with pLastEntry. If argument pBefore is NULL, then pNew is added
|
|
+** to the end of the list. Otherwise, it is added to the list immediately
|
|
+** before pBefore (which is guaranteed to be a part of said list).
|
|
+*/
|
|
+static void zipfileAddEntry(
|
|
+ ZipfileTab *pTab,
|
|
+ ZipfileEntry *pBefore,
|
|
+ ZipfileEntry *pNew
|
|
+){
|
|
+ assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) );
|
|
+ assert( pNew->pNext==0 );
|
|
+ if( pBefore==0 ){
|
|
+ if( pTab->pFirstEntry==0 ){
|
|
+ pTab->pFirstEntry = pTab->pLastEntry = pNew;
|
|
+ }else{
|
|
+ assert( pTab->pLastEntry->pNext==0 );
|
|
+ pTab->pLastEntry->pNext = pNew;
|
|
+ pTab->pLastEntry = pNew;
|
|
+ }
|
|
+ }else{
|
|
+ ZipfileEntry **pp;
|
|
+ for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext));
|
|
+ pNew->pNext = pBefore;
|
|
+ *pp = pNew;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
|
|
+ ZipfileEOCD eocd;
|
|
+ int rc;
|
|
+ int i;
|
|
+ i64 iOff;
|
|
+
|
|
+ rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd);
|
|
+ iOff = eocd.iOffset;
|
|
+ for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){
|
|
+ ZipfileEntry *pNew = 0;
|
|
+ rc = zipfileGetEntry(pTab, aBlob, nBlob, pTab->pWriteFd, iOff, &pNew);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ zipfileAddEntry(pTab, 0, pNew);
|
|
+ iOff += ZIPFILE_CDS_FIXED_SZ;
|
|
+ iOff += (int)pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment;
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFilter callback.
|
|
+*/
|
|
+static int zipfileFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ const char *zFile = 0; /* Zip file to scan */
|
|
+ int rc = SQLITE_OK; /* Return Code */
|
|
+ int bInMemory = 0; /* True for an in-memory zipfile */
|
|
+
|
|
+ zipfileResetCursor(pCsr);
|
|
+
|
|
+ if( pTab->zFile ){
|
|
+ zFile = pTab->zFile;
|
|
+ }else if( idxNum==0 ){
|
|
+ zipfileCursorErr(pCsr, "zipfile() function requires an argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
|
+ const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
|
|
+ int nBlob = sqlite3_value_bytes(argv[0]);
|
|
+ assert( pTab->pFirstEntry==0 );
|
|
+ rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
|
|
+ pCsr->pFreeEntry = pTab->pFirstEntry;
|
|
+ pTab->pFirstEntry = pTab->pLastEntry = 0;
|
|
+ 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 = fopen(zFile, "rb");
|
|
+ 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 ){
|
|
+ pCsr->bEof = 1;
|
|
+ }else{
|
|
+ pCsr->iNextOff = pCsr->eocd.iOffset;
|
|
+ rc = zipfileNext(cur);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ pCsr->bNoop = 1;
|
|
+ pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry;
|
|
+ rc = zipfileNext(cur);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xBestIndex callback.
|
|
+*/
|
|
+static int zipfileBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i;
|
|
+ int idx = -1;
|
|
+ int unusable = 0;
|
|
+
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
|
|
+ if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue;
|
|
+ if( pCons->usable==0 ){
|
|
+ unusable = 1;
|
|
+ }else if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ idx = i;
|
|
+ }
|
|
+ }
|
|
+ if( idx>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[idx].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[idx].omit = 1;
|
|
+ pIdxInfo->estimatedCost = 1000.0;
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ }else if( unusable ){
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static ZipfileEntry *zipfileNewEntry(const char *zPath){
|
|
+ ZipfileEntry *pNew;
|
|
+ pNew = sqlite3_malloc(sizeof(ZipfileEntry));
|
|
+ if( pNew ){
|
|
+ memset(pNew, 0, sizeof(ZipfileEntry));
|
|
+ pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
|
|
+ if( pNew->cds.zFile==0 ){
|
|
+ sqlite3_free(pNew);
|
|
+ pNew = 0;
|
|
+ }
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){
|
|
+ ZipfileCDS *pCds = &pEntry->cds;
|
|
+ u8 *a = aBuf;
|
|
+
|
|
+ pCds->nExtra = 9;
|
|
+
|
|
+ /* Write the LFH itself */
|
|
+ zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH);
|
|
+ zipfileWrite16(a, pCds->iVersionExtract);
|
|
+ zipfileWrite16(a, pCds->flags);
|
|
+ zipfileWrite16(a, pCds->iCompression);
|
|
+ zipfileWrite16(a, pCds->mTime);
|
|
+ zipfileWrite16(a, pCds->mDate);
|
|
+ zipfileWrite32(a, pCds->crc32);
|
|
+ zipfileWrite32(a, pCds->szCompressed);
|
|
+ zipfileWrite32(a, pCds->szUncompressed);
|
|
+ zipfileWrite16(a, (u16)pCds->nFile);
|
|
+ zipfileWrite16(a, pCds->nExtra);
|
|
+ assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] );
|
|
+
|
|
+ /* Add the file name */
|
|
+ memcpy(a, pCds->zFile, (int)pCds->nFile);
|
|
+ a += (int)pCds->nFile;
|
|
+
|
|
+ /* The "extra" data */
|
|
+ zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
|
|
+ zipfileWrite16(a, 5);
|
|
+ *a++ = 0x01;
|
|
+ zipfileWrite32(a, pEntry->mUnixTime);
|
|
+
|
|
+ return a-aBuf;
|
|
+}
|
|
+
|
|
+static int zipfileAppendEntry(
|
|
+ ZipfileTab *pTab,
|
|
+ ZipfileEntry *pEntry,
|
|
+ const u8 *pData,
|
|
+ int nData
|
|
+){
|
|
+ u8 *aBuf = pTab->aBuffer;
|
|
+ int nBuf;
|
|
+ int rc;
|
|
+
|
|
+ nBuf = zipfileSerializeLFH(pEntry, aBuf);
|
|
+ rc = zipfileAppendData(pTab, aBuf, nBuf);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pEntry->iDataOff = pTab->szCurrent;
|
|
+ rc = zipfileAppendData(pTab, pData, nData);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int zipfileGetMode(
|
|
+ sqlite3_value *pVal,
|
|
+ int bIsDir, /* If true, default to directory */
|
|
+ u32 *pMode, /* OUT: Mode value */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ const char *z = (const char*)sqlite3_value_text(pVal);
|
|
+ u32 mode = 0;
|
|
+ if( z==0 ){
|
|
+ mode = (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644));
|
|
+ }else if( z[0]>='0' && z[0]<='9' ){
|
|
+ mode = (unsigned int)sqlite3_value_int(pVal);
|
|
+ }else{
|
|
+ const char zTemplate[11] = "-rwxrwxrwx";
|
|
+ int i;
|
|
+ if( strlen(z)!=10 ) goto parse_error;
|
|
+ switch( z[0] ){
|
|
+ case '-': mode |= S_IFREG; break;
|
|
+ case 'd': mode |= S_IFDIR; break;
|
|
+ case 'l': mode |= S_IFLNK; break;
|
|
+ default: goto parse_error;
|
|
+ }
|
|
+ for(i=1; i<10; i++){
|
|
+ if( z[i]==zTemplate[i] ) mode |= 1 << (9-i);
|
|
+ else if( z[i]!='-' ) goto parse_error;
|
|
+ }
|
|
+ }
|
|
+ if( ((mode & S_IFDIR)==0)==bIsDir ){
|
|
+ /* The "mode" attribute is a directory, but data has been specified.
|
|
+ ** Or vice-versa - no data but "mode" is a file or symlink. */
|
|
+ *pzErr = sqlite3_mprintf("zipfile: mode does not match data");
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ *pMode = mode;
|
|
+ return SQLITE_OK;
|
|
+
|
|
+ parse_error:
|
|
+ *pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s", z);
|
|
+ return SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Both (const char*) arguments point to nul-terminated strings. Argument
|
|
+** nB is the value of strlen(zB). This function returns 0 if the strings are
|
|
+** identical, ignoring any trailing '/' character in either path. */
|
|
+static int zipfileComparePath(const char *zA, const char *zB, int nB){
|
|
+ int nA = (int)strlen(zA);
|
|
+ if( zA[nA-1]=='/' ) nA--;
|
|
+ if( zB[nB-1]=='/' ) nB--;
|
|
+ if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int zipfileBegin(sqlite3_vtab *pVtab){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)pVtab;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ assert( pTab->pWriteFd==0 );
|
|
+
|
|
+ /* 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 = 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);
|
|
+ pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
|
|
+ rc = zipfileLoadDirectory(pTab, 0, 0);
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ zipfileCleanupTransaction(pTab);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current time as a 32-bit timestamp in UNIX epoch format (like
|
|
+** time(2)).
|
|
+*/
|
|
+static u32 zipfileTime(void){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
|
|
+ u32 ret;
|
|
+ if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
|
|
+ i64 ms;
|
|
+ pVfs->xCurrentTimeInt64(pVfs, &ms);
|
|
+ ret = (u32)((ms/1000) - ((i64)24405875 * 8640));
|
|
+ }else{
|
|
+ double day;
|
|
+ pVfs->xCurrentTime(pVfs, &day);
|
|
+ ret = (u32)((day - 2440587.5) * 86400);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a 32-bit timestamp in UNIX epoch format.
|
|
+**
|
|
+** If the value passed as the only argument is either NULL or an SQL NULL,
|
|
+** return the current time. Otherwise, return the value stored in (*pVal)
|
|
+** cast to a 32-bit unsigned integer.
|
|
+*/
|
|
+static u32 zipfileGetTime(sqlite3_value *pVal){
|
|
+ if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
|
|
+ return zipfileTime();
|
|
+ }
|
|
+ return (u32)sqlite3_value_int64(pVal);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
|
|
+** linked list. Remove it from the list and free the object.
|
|
+*/
|
|
+static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
|
|
+ if( pOld ){
|
|
+ ZipfileEntry **pp;
|
|
+ for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext));
|
|
+ *pp = (*pp)->pNext;
|
|
+ zipfileEntryFree(pOld);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xUpdate method.
|
|
+*/
|
|
+static int zipfileUpdate(
|
|
+ sqlite3_vtab *pVtab,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)pVtab;
|
|
+ int rc = SQLITE_OK; /* Return Code */
|
|
+ ZipfileEntry *pNew = 0; /* New in-memory CDS entry */
|
|
+
|
|
+ u32 mode = 0; /* Mode for new entry */
|
|
+ u32 mTime = 0; /* Modification time for new entry */
|
|
+ i64 sz = 0; /* Uncompressed size */
|
|
+ const char *zPath = 0; /* Path for new entry */
|
|
+ int nPath = 0; /* strlen(zPath) */
|
|
+ const u8 *pData = 0; /* Pointer to buffer containing content */
|
|
+ size_t nData = 0; /* Size of pData buffer in bytes */
|
|
+ int iMethod = 0; /* Compression method for new entry */
|
|
+ u8 *pFree = 0; /* Free this */
|
|
+ char *zFree = 0; /* Also free this */
|
|
+ ZipfileEntry *pOld = 0;
|
|
+ ZipfileEntry *pOld2 = 0;
|
|
+ int bUpdate = 0; /* True for an update that modifies "name" */
|
|
+ int bIsDir = 0;
|
|
+ u32 iCrc32 = 0;
|
|
+
|
|
+ if( pTab->pWriteFd==0 ){
|
|
+ rc = zipfileBegin(pVtab);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ }
|
|
+
|
|
+ /* If this is a DELETE or UPDATE, find the archive entry to delete. */
|
|
+ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
|
|
+ const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
|
|
+ int nDelete = (int)strlen(zDelete);
|
|
+ if( nVal>1 ){
|
|
+ const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]);
|
|
+ if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){
|
|
+ bUpdate = 1;
|
|
+ }
|
|
+ }
|
|
+ for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){
|
|
+ if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){
|
|
+ break;
|
|
+ }
|
|
+ assert( pOld->pNext );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( nVal>1 ){
|
|
+ /* Check that "sz" and "rawdata" are both NULL: */
|
|
+ if( sqlite3_value_type(apVal[5])!=SQLITE_NULL ){
|
|
+ zipfileTableErr(pTab, "sz must be NULL");
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ if( sqlite3_value_type(apVal[6])!=SQLITE_NULL ){
|
|
+ zipfileTableErr(pTab, "rawdata must be NULL");
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){
|
|
+ /* data=NULL. A directory */
|
|
+ bIsDir = 1;
|
|
+ }else{
|
|
+ /* Value specified for "data", and possibly "method". This must be
|
|
+ ** a regular file or a symlink. */
|
|
+ const u8 *aIn = sqlite3_value_blob(apVal[7]);
|
|
+ size_t nIn = sqlite3_value_bytes(apVal[7]);
|
|
+ int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL;
|
|
+
|
|
+ iMethod = sqlite3_value_int(apVal[8]);
|
|
+ sz = nIn;
|
|
+ pData = aIn;
|
|
+ nData = nIn;
|
|
+ if( iMethod!=0 && iMethod!=8 ){
|
|
+ zipfileTableErr(pTab, "unknown compression method: %d", iMethod);
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ }else{
|
|
+ if( bAuto || iMethod ){
|
|
+ size_t nCmp;
|
|
+ rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( iMethod || nCmp<nIn ){
|
|
+ iMethod = 8;
|
|
+ pData = pFree;
|
|
+ nData = nCmp;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ iCrc32 = crc32(0, aIn, nIn);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ zPath = (const char*)sqlite3_value_text(apVal[2]);
|
|
+ nPath = (int)strlen(zPath);
|
|
+ mTime = zipfileGetTime(apVal[4]);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK && bIsDir ){
|
|
+ /* For a directory, check that the last character in the path is a
|
|
+ ** '/'. This appears to be required for compatibility with info-zip
|
|
+ ** (the unzip command on unix). It does not create directories
|
|
+ ** otherwise. */
|
|
+ if( zPath[nPath-1]!='/' ){
|
|
+ zFree = sqlite3_mprintf("%s/", zPath);
|
|
+ if( zFree==0 ){ rc = SQLITE_NOMEM; }
|
|
+ zPath = (const char*)zFree;
|
|
+ nPath++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check that we're not inserting a duplicate entry -OR- updating an
|
|
+ ** entry with a path, thereby making it into a duplicate. */
|
|
+ if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){
|
|
+ ZipfileEntry *p;
|
|
+ for(p=pTab->pFirstEntry; p; p=p->pNext){
|
|
+ if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){
|
|
+ switch( sqlite3_vtab_on_conflict(pTab->db) ){
|
|
+ case SQLITE_IGNORE: {
|
|
+ goto zipfile_update_done;
|
|
+ }
|
|
+ case SQLITE_REPLACE: {
|
|
+ pOld2 = p;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ zipfileTableErr(pTab, "duplicate name: \"%s\"", zPath);
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ /* Create the new CDS record. */
|
|
+ pNew = zipfileNewEntry(zPath);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
|
|
+ pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
|
|
+ pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS;
|
|
+ pNew->cds.iCompression = (u16)iMethod;
|
|
+ zipfileMtimeToDos(&pNew->cds, mTime);
|
|
+ pNew->cds.crc32 = iCrc32;
|
|
+ pNew->cds.szCompressed = nData;
|
|
+ pNew->cds.szUncompressed = (u32)sz;
|
|
+ pNew->cds.iExternalAttr = (mode<<16);
|
|
+ pNew->cds.iOffset = (u32)pTab->szCurrent;
|
|
+ pNew->cds.nFile = (u16)nPath;
|
|
+ pNew->mUnixTime = (u32)mTime;
|
|
+ rc = zipfileAppendEntry(pTab, pNew, pData, nData);
|
|
+ zipfileAddEntry(pTab, pOld, pNew);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK && (pOld || pOld2) ){
|
|
+ ZipfileCsr *pCsr;
|
|
+ for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
|
|
+ if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){
|
|
+ pCsr->pCurrent = pCsr->pCurrent->pNext;
|
|
+ pCsr->bNoop = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ zipfileRemoveEntryFromList(pTab, pOld);
|
|
+ zipfileRemoveEntryFromList(pTab, pOld2);
|
|
+ }
|
|
+
|
|
+zipfile_update_done:
|
|
+ sqlite3_free(pFree);
|
|
+ sqlite3_free(zFree);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){
|
|
+ u8 *a = aBuf;
|
|
+ zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD);
|
|
+ zipfileWrite16(a, p->iDisk);
|
|
+ zipfileWrite16(a, p->iFirstDisk);
|
|
+ zipfileWrite16(a, p->nEntry);
|
|
+ zipfileWrite16(a, p->nEntryTotal);
|
|
+ zipfileWrite32(a, p->nSize);
|
|
+ zipfileWrite32(a, p->iOffset);
|
|
+ zipfileWrite16(a, 0); /* Size of trailing comment in bytes*/
|
|
+
|
|
+ return a-aBuf;
|
|
+}
|
|
+
|
|
+static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
|
|
+ int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer);
|
|
+ assert( nBuf==ZIPFILE_EOCD_FIXED_SZ );
|
|
+ return zipfileAppendData(pTab, pTab->aBuffer, nBuf);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Serialize the CDS structure into buffer aBuf[]. Return the number
|
|
+** of bytes written.
|
|
+*/
|
|
+static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){
|
|
+ u8 *a = aBuf;
|
|
+ ZipfileCDS *pCDS = &pEntry->cds;
|
|
+
|
|
+ if( pEntry->aExtra==0 ){
|
|
+ pCDS->nExtra = 9;
|
|
+ }
|
|
+
|
|
+ zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS);
|
|
+ zipfileWrite16(a, pCDS->iVersionMadeBy);
|
|
+ zipfileWrite16(a, pCDS->iVersionExtract);
|
|
+ zipfileWrite16(a, pCDS->flags);
|
|
+ zipfileWrite16(a, pCDS->iCompression);
|
|
+ zipfileWrite16(a, pCDS->mTime);
|
|
+ zipfileWrite16(a, pCDS->mDate);
|
|
+ zipfileWrite32(a, pCDS->crc32);
|
|
+ zipfileWrite32(a, pCDS->szCompressed);
|
|
+ zipfileWrite32(a, pCDS->szUncompressed);
|
|
+ assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
|
|
+ zipfileWrite16(a, pCDS->nFile);
|
|
+ zipfileWrite16(a, pCDS->nExtra);
|
|
+ zipfileWrite16(a, pCDS->nComment);
|
|
+ zipfileWrite16(a, pCDS->iDiskStart);
|
|
+ zipfileWrite16(a, pCDS->iInternalAttr);
|
|
+ zipfileWrite32(a, pCDS->iExternalAttr);
|
|
+ zipfileWrite32(a, pCDS->iOffset);
|
|
+
|
|
+ memcpy(a, pCDS->zFile, pCDS->nFile);
|
|
+ a += pCDS->nFile;
|
|
+
|
|
+ if( pEntry->aExtra ){
|
|
+ int n = (int)pCDS->nExtra + (int)pCDS->nComment;
|
|
+ memcpy(a, pEntry->aExtra, n);
|
|
+ a += n;
|
|
+ }else{
|
|
+ assert( pCDS->nExtra==9 );
|
|
+ zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
|
|
+ zipfileWrite16(a, 5);
|
|
+ *a++ = 0x01;
|
|
+ zipfileWrite32(a, pEntry->mUnixTime);
|
|
+ }
|
|
+
|
|
+ return a-aBuf;
|
|
+}
|
|
+
|
|
+static int zipfileCommit(sqlite3_vtab *pVtab){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)pVtab;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( pTab->pWriteFd ){
|
|
+ i64 iOffset = pTab->szCurrent;
|
|
+ ZipfileEntry *p;
|
|
+ ZipfileEOCD eocd;
|
|
+ int nEntry = 0;
|
|
+
|
|
+ /* Write out all entries */
|
|
+ for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){
|
|
+ int n = zipfileSerializeCDS(p, pTab->aBuffer);
|
|
+ rc = zipfileAppendData(pTab, pTab->aBuffer, n);
|
|
+ nEntry++;
|
|
+ }
|
|
+
|
|
+ /* Write out the EOCD record */
|
|
+ eocd.iDisk = 0;
|
|
+ eocd.iFirstDisk = 0;
|
|
+ eocd.nEntry = (u16)nEntry;
|
|
+ eocd.nEntryTotal = (u16)nEntry;
|
|
+ eocd.nSize = (u32)(pTab->szCurrent - iOffset);
|
|
+ eocd.iOffset = (u32)iOffset;
|
|
+ rc = zipfileAppendEOCD(pTab, &eocd);
|
|
+
|
|
+ zipfileCleanupTransaction(pTab);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int zipfileRollback(sqlite3_vtab *pVtab){
|
|
+ return zipfileCommit(pVtab);
|
|
+}
|
|
+
|
|
+static ZipfileCsr *zipfileFindCursor(ZipfileTab *pTab, i64 iId){
|
|
+ ZipfileCsr *pCsr;
|
|
+ for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
|
|
+ if( iId==pCsr->iId ) break;
|
|
+ }
|
|
+ return pCsr;
|
|
+}
|
|
+
|
|
+static void zipfileFunctionCds(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ ZipfileCsr *pCsr;
|
|
+ ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context);
|
|
+ assert( argc>0 );
|
|
+
|
|
+ pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0]));
|
|
+ if( pCsr ){
|
|
+ ZipfileCDS *p = &pCsr->pCurrent->cds;
|
|
+ char *zRes = sqlite3_mprintf("{"
|
|
+ "\"version-made-by\" : %u, "
|
|
+ "\"version-to-extract\" : %u, "
|
|
+ "\"flags\" : %u, "
|
|
+ "\"compression\" : %u, "
|
|
+ "\"time\" : %u, "
|
|
+ "\"date\" : %u, "
|
|
+ "\"crc32\" : %u, "
|
|
+ "\"compressed-size\" : %u, "
|
|
+ "\"uncompressed-size\" : %u, "
|
|
+ "\"file-name-length\" : %u, "
|
|
+ "\"extra-field-length\" : %u, "
|
|
+ "\"file-comment-length\" : %u, "
|
|
+ "\"disk-number-start\" : %u, "
|
|
+ "\"internal-attr\" : %u, "
|
|
+ "\"external-attr\" : %u, "
|
|
+ "\"offset\" : %u }",
|
|
+ (u32)p->iVersionMadeBy, (u32)p->iVersionExtract,
|
|
+ (u32)p->flags, (u32)p->iCompression,
|
|
+ (u32)p->mTime, (u32)p->mDate,
|
|
+ (u32)p->crc32, (u32)p->szCompressed,
|
|
+ (u32)p->szUncompressed, (u32)p->nFile,
|
|
+ (u32)p->nExtra, (u32)p->nComment,
|
|
+ (u32)p->iDiskStart, (u32)p->iInternalAttr,
|
|
+ (u32)p->iExternalAttr, (u32)p->iOffset
|
|
+ );
|
|
+
|
|
+ if( zRes==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(zRes);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFindFunction method.
|
|
+*/
|
|
+static int zipfileFindFunction(
|
|
+ sqlite3_vtab *pVtab, /* Virtual table handle */
|
|
+ int nArg, /* Number of SQL function arguments */
|
|
+ const char *zName, /* Name of SQL function */
|
|
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
|
|
+ void **ppArg /* OUT: User data for *pxFunc */
|
|
+){
|
|
+ if( sqlite3_stricmp("zipfile_cds", zName)==0 ){
|
|
+ *pxFunc = zipfileFunctionCds;
|
|
+ *ppArg = (void*)pVtab;
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+typedef struct ZipfileBuffer ZipfileBuffer;
|
|
+struct ZipfileBuffer {
|
|
+ u8 *a; /* Pointer to buffer */
|
|
+ size_t n; /* Size of buffer in bytes */
|
|
+ size_t nAlloc; /* Byte allocated at a[] */
|
|
+};
|
|
+
|
|
+typedef struct ZipfileCtx ZipfileCtx;
|
|
+struct ZipfileCtx {
|
|
+ int nEntry;
|
|
+ ZipfileBuffer body;
|
|
+ ZipfileBuffer cds;
|
|
+};
|
|
+
|
|
+static int zipfileBufferGrow(ZipfileBuffer *pBuf, size_t nByte){
|
|
+ if( pBuf->n+nByte>pBuf->nAlloc ){
|
|
+ u8 *aNew;
|
|
+ size_t nNew = pBuf->n ? pBuf->n*2 : 512;
|
|
+ size_t nReq = pBuf->n + nByte;
|
|
+
|
|
+ while( nNew<nReq ) nNew = nNew*2;
|
|
+ aNew = sqlite3_realloc(pBuf->a, nNew);
|
|
+ if( aNew==0 ) return SQLITE_NOMEM;
|
|
+ pBuf->a = aNew;
|
|
+ pBuf->nAlloc = (int)nNew;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xStep() callback for the zipfile() aggregate. This can be called in
|
|
+** any of the following ways:
|
|
+**
|
|
+** SELECT zipfile(name,data) ...
|
|
+** SELECT zipfile(name,mode,mtime,data) ...
|
|
+** SELECT zipfile(name,mode,mtime,data,method) ...
|
|
+*/
|
|
+void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
|
|
+ ZipfileCtx *p; /* Aggregate function context */
|
|
+ ZipfileEntry e; /* New entry to add to zip archive */
|
|
+
|
|
+ sqlite3_value *pName = 0;
|
|
+ sqlite3_value *pMode = 0;
|
|
+ sqlite3_value *pMtime = 0;
|
|
+ sqlite3_value *pData = 0;
|
|
+ sqlite3_value *pMethod = 0;
|
|
+
|
|
+ int bIsDir = 0;
|
|
+ u32 mode;
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zErr = 0;
|
|
+
|
|
+ int iMethod = -1; /* Compression method to use (0 or 8) */
|
|
+
|
|
+ const u8 *aData = 0; /* Possibly compressed data for new entry */
|
|
+ size_t nData = 0; /* Size of aData[] in bytes */
|
|
+ int szUncompressed = 0; /* Size of data before compression */
|
|
+ u8 *aFree = 0; /* Free this before returning */
|
|
+ u32 iCrc32 = 0; /* crc32 of uncompressed data */
|
|
+
|
|
+ char *zName = 0; /* Path (name) of new entry */
|
|
+ int nName = 0; /* Size of zName in bytes */
|
|
+ char *zFree = 0; /* Free this before returning */
|
|
+ size_t nByte;
|
|
+
|
|
+ memset(&e, 0, sizeof(e));
|
|
+ p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
|
+ if( p==0 ) return;
|
|
+
|
|
+ /* Martial the arguments into stack variables */
|
|
+ if( nVal!=2 && nVal!=4 && nVal!=5 ){
|
|
+ zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ pName = apVal[0];
|
|
+ if( nVal==2 ){
|
|
+ pData = apVal[1];
|
|
+ }else{
|
|
+ pMode = apVal[1];
|
|
+ pMtime = apVal[2];
|
|
+ pData = apVal[3];
|
|
+ if( nVal==5 ){
|
|
+ pMethod = apVal[4];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check that the 'name' parameter looks ok. */
|
|
+ zName = (char*)sqlite3_value_text(pName);
|
|
+ nName = sqlite3_value_bytes(pName);
|
|
+ if( zName==0 ){
|
|
+ zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+
|
|
+ /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
|
|
+ ** deflate compression) or NULL (choose automatically). */
|
|
+ if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
|
|
+ iMethod = (int)sqlite3_value_int64(pMethod);
|
|
+ if( iMethod!=0 && iMethod!=8 ){
|
|
+ zErr = sqlite3_mprintf("illegal method value: %d", iMethod);
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Now inspect the data. If this is NULL, then the new entry must be a
|
|
+ ** directory. Otherwise, figure out whether or not the data should
|
|
+ ** be deflated or simply stored in the zip archive. */
|
|
+ if( sqlite3_value_type(pData)==SQLITE_NULL ){
|
|
+ bIsDir = 1;
|
|
+ iMethod = 0;
|
|
+ }else{
|
|
+ aData = sqlite3_value_blob(pData);
|
|
+ szUncompressed = nData = sqlite3_value_bytes(pData);
|
|
+ iCrc32 = crc32(0, aData, nData);
|
|
+ if( iMethod<0 || iMethod==8 ){
|
|
+ size_t nOut = 0;
|
|
+ rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ if( iMethod==8 || nOut<nData ){
|
|
+ aData = aFree;
|
|
+ nData = nOut;
|
|
+ iMethod = 8;
|
|
+ }else{
|
|
+ iMethod = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Decode the "mode" argument. */
|
|
+ rc = zipfileGetMode(pMode, bIsDir, &mode, &zErr);
|
|
+ if( rc ) goto zipfile_step_out;
|
|
+
|
|
+ /* Decode the "mtime" argument. */
|
|
+ e.mUnixTime = zipfileGetTime(pMtime);
|
|
+
|
|
+ /* If this is a directory entry, ensure that there is exactly one '/'
|
|
+ ** at the end of the path. Or, if this is not a directory and the path
|
|
+ ** ends in '/' it is an error. */
|
|
+ if( bIsDir==0 ){
|
|
+ if( zName[nName-1]=='/' ){
|
|
+ zErr = sqlite3_mprintf("non-directory name must not end with /");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ }else{
|
|
+ if( zName[nName-1]!='/' ){
|
|
+ zName = zFree = sqlite3_mprintf("%s/", zName);
|
|
+ nName++;
|
|
+ if( zName==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ }else{
|
|
+ while( nName>1 && zName[nName-2]=='/' ) nName--;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Assemble the ZipfileEntry object for the new zip archive entry */
|
|
+ e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
|
|
+ e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
|
|
+ e.cds.flags = ZIPFILE_NEWENTRY_FLAGS;
|
|
+ e.cds.iCompression = (u16)iMethod;
|
|
+ zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime);
|
|
+ e.cds.crc32 = iCrc32;
|
|
+ e.cds.szCompressed = nData;
|
|
+ e.cds.szUncompressed = szUncompressed;
|
|
+ e.cds.iExternalAttr = (mode<<16);
|
|
+ e.cds.iOffset = p->body.n;
|
|
+ e.cds.nFile = (u16)nName;
|
|
+ e.cds.zFile = zName;
|
|
+
|
|
+ /* Append the LFH to the body of the new archive */
|
|
+ nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9;
|
|
+ if( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out;
|
|
+ p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]);
|
|
+
|
|
+ /* Append the data to the body of the new archive */
|
|
+ if( nData>0 ){
|
|
+ if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out;
|
|
+ memcpy(&p->body.a[p->body.n], aData, nData);
|
|
+ p->body.n += nData;
|
|
+ }
|
|
+
|
|
+ /* Append the CDS record to the directory of the new archive */
|
|
+ nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9;
|
|
+ if( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out;
|
|
+ p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]);
|
|
+
|
|
+ /* Increment the count of entries in the archive */
|
|
+ p->nEntry++;
|
|
+
|
|
+ zipfile_step_out:
|
|
+ sqlite3_free(aFree);
|
|
+ sqlite3_free(zFree);
|
|
+ if( rc ){
|
|
+ if( zErr ){
|
|
+ sqlite3_result_error(pCtx, zErr, -1);
|
|
+ }else{
|
|
+ sqlite3_result_error_code(pCtx, rc);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFinalize() callback for zipfile aggregate function.
|
|
+*/
|
|
+void zipfileFinal(sqlite3_context *pCtx){
|
|
+ ZipfileCtx *p;
|
|
+ ZipfileEOCD eocd;
|
|
+ size_t nZip;
|
|
+ u8 *aZip;
|
|
+
|
|
+ p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
|
+ if( p==0 ) return;
|
|
+ if( p->nEntry>0 ){
|
|
+ memset(&eocd, 0, sizeof(eocd));
|
|
+ eocd.nEntry = (u16)p->nEntry;
|
|
+ eocd.nEntryTotal = (u16)p->nEntry;
|
|
+ eocd.nSize = p->cds.n;
|
|
+ eocd.iOffset = p->body.n;
|
|
+
|
|
+ nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ;
|
|
+ aZip = (u8*)sqlite3_malloc(nZip);
|
|
+ if( aZip==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ memcpy(aZip, p->body.a, p->body.n);
|
|
+ memcpy(&aZip[p->body.n], p->cds.a, p->cds.n);
|
|
+ zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]);
|
|
+ sqlite3_result_blob(pCtx, aZip, nZip, zipfileFree);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_free(p->body.a);
|
|
+ sqlite3_free(p->cds.a);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Register the "zipfile" virtual table.
|
|
+*/
|
|
+static int zipfileRegister(sqlite3 *db){
|
|
+ static const sqlite3_module zipfileModule = {
|
|
+ 1, /* iVersion */
|
|
+ zipfileConnect, /* xCreate */
|
|
+ zipfileConnect, /* xConnect */
|
|
+ zipfileBestIndex, /* xBestIndex */
|
|
+ zipfileDisconnect, /* xDisconnect */
|
|
+ zipfileDisconnect, /* xDestroy */
|
|
+ zipfileOpen, /* xOpen - open a cursor */
|
|
+ zipfileClose, /* xClose - close a cursor */
|
|
+ zipfileFilter, /* xFilter - configure scan constraints */
|
|
+ zipfileNext, /* xNext - advance a cursor */
|
|
+ zipfileEof, /* xEof - check for end of scan */
|
|
+ zipfileColumn, /* xColumn - read data */
|
|
+ 0, /* xRowid - read data */
|
|
+ zipfileUpdate, /* xUpdate */
|
|
+ zipfileBegin, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ zipfileCommit, /* xCommit */
|
|
+ zipfileRollback, /* xRollback */
|
|
+ zipfileFindFunction, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ };
|
|
+
|
|
+ int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0);
|
|
+ if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0,
|
|
+ zipfileStep, zipfileFinal
|
|
+ );
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#else /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+# define zipfileRegister(x) SQLITE_OK
|
|
+#endif
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_zipfile_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ return zipfileRegister(db);
|
|
+}
|
|
--- origsrc/sqlite-autoconf-3280000/zorder.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ src/sqlite-autoconf-3280000/zorder.c 2019-05-14 16:22:31.287883600 +0200
|
|
@@ -0,0 +1,102 @@
|
|
+/*
|
|
+** 2018-02-09
|
|
+**
|
|
+** 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.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** SQL functions for z-order (Morton code) transformations.
|
|
+**
|
|
+** zorder(X0,X0,..,xN) Generate an N+1 dimension Morton code
|
|
+**
|
|
+** unzorder(Z,N,I) Extract the I-th dimension from N-dimensional
|
|
+** Morton code Z.
|
|
+*/
|
|
+#include "sqlite3ext.h"
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+
|
|
+/*
|
|
+** Functions: zorder(X0,X1,....)
|
|
+**
|
|
+** Convert integers X0, X1, ... into morton code.
|
|
+**
|
|
+** The output is a signed 64-bit integer. If any argument is too large,
|
|
+** an error is thrown.
|
|
+*/
|
|
+static void zorderFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3_int64 z, x[63];
|
|
+ int i, j;
|
|
+ z = 0;
|
|
+ for(i=0; i<argc; i++){
|
|
+ x[i] = sqlite3_value_int64(argv[i]);
|
|
+ }
|
|
+ if( argc>0 ){
|
|
+ for(i=0; i<63; i++){
|
|
+ j = i%argc;
|
|
+ z |= (x[j]&1)<<i;
|
|
+ x[j] >>= 1;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_int64(context, z);
|
|
+ for(i=0; i<argc; i++){
|
|
+ if( x[i] ){
|
|
+ sqlite3_result_error(context, "parameter too large", -1);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Functions: unzorder(Z,N,I)
|
|
+**
|
|
+** Assuming that Z is an N-dimensional Morton code, extract the I-th
|
|
+** dimension.
|
|
+*/
|
|
+static void unzorderFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3_int64 z, n, i, x;
|
|
+ int j, k;
|
|
+ z = sqlite3_value_int64(argv[0]);
|
|
+ n = sqlite3_value_int64(argv[1]);
|
|
+ i = sqlite3_value_int64(argv[2]);
|
|
+ x = 0;
|
|
+ for(k=0, j=i; j<63; j+=n, k++){
|
|
+ x |= ((z>>j)&1)<<k;
|
|
+ }
|
|
+ sqlite3_result_int64(context, x);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+int sqlite3_zorder_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ rc = sqlite3_create_function(db, "zorder", -1, SQLITE_UTF8, 0,
|
|
+ zorderFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "unzorder", 3, SQLITE_UTF8, 0,
|
|
+ unzorderFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|