Index: Doc/Extra/dbfactorysupport.html ================================================================== --- Doc/Extra/dbfactorysupport.html +++ Doc/Extra/dbfactorysupport.html @@ -83,11 +83,11 @@ <DbProviderFactories> <remove invariant="System.Data.SQLite"/> <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, - Version=1.0.76.0, Culture=neutral, + Version=1.0.78.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139"/> </DbProviderFactories> </system.data> </configuration> Index: Doc/Extra/designer.html ================================================================== --- Doc/Extra/designer.html +++ Doc/Extra/designer.html @@ -48,22 +48,22 @@ SQLite databases from within Visual Studio is a great time-saver.  Though the support is not yet fully-implemented, there's certainly enough there to keep you busy.  You can create databases, design and execute queries, create typed datasets and lots more all from Visual Studio.

Installation Instructions

-

- In Windows Explorer, navigate to SQLite.Net\bin\Designer - and execute the INSTALL.EXE program.  It will automatically - detect what eligible Visual Studio products are installed, and allow you to check - and uncheck which environments to install the designer for.

+

Download and run one of the setup packages and then select the "Install + the designer components for Visual Studio 20XX." option when prompted.

Express Edition Limitations

-

All Express Editions (except Visual Web Developer) are hard-coded to only allow you to design for Jet and Sql Server Database Files.  The only way for SQLite - to install its designer is to temporarily replace one of the existing "approved" - designers.  Therefore, when you install the SQLite designer for one of these - express editions, it will temporarily replace the Microsoft Access designer.  - You can revert back to the Access designer simply by re-running the install.exe - program and un-checking the boxes.

+

Visual Studio design-time Support, works with all versions of Visual Studio + 2005/2008/2010. You can add a SQLite database to the Servers list, design + queries with the Query Designer, drag-and-drop tables onto a Typed DataSet, etc. +
+ + Due to Visual Studio licensing restrictions, the Express Editions can no + longer be supported. + +


Version History

+

1.0.78.0 - January 27, 2012

+ +

1.0.77.0 - November 28, 2011

+

1.0.76.0 - October 4, 2011

1.0.75.0 - October 3, 2011

1.0.74.0 - July 4, 2011

1.0.73.0 - June 2, 2011

1.0.69.0 - April 12, 2011

1.0.68.0 - February 2011

1.0.67.0 - January 3, 2011

1.0.66.1 - August 1, 2010

Index: Doc/SQLite.NET.chm ================================================================== --- Doc/SQLite.NET.chm +++ Doc/SQLite.NET.chm cannot compute difference between binary files Index: Externals/Eagle/bin/Eagle.dll ================================================================== --- Externals/Eagle/bin/Eagle.dll +++ Externals/Eagle/bin/Eagle.dll cannot compute difference between binary files Index: Externals/Eagle/bin/EagleShell.exe ================================================================== --- Externals/Eagle/bin/EagleShell.exe +++ Externals/Eagle/bin/EagleShell.exe cannot compute difference between binary files ADDED Externals/Eagle/bin/EagleShell.exe.mda.config Index: Externals/Eagle/bin/EagleShell.exe.mda.config ================================================================== --- /dev/null +++ Externals/Eagle/bin/EagleShell.exe.mda.config @@ -0,0 +1,82 @@ + + + + + + + Index: Externals/Eagle/lib/Eagle1.0/embed.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/embed.eagle +++ Externals/Eagle/lib/Eagle1.0/embed.eagle @@ -3,11 +3,11 @@ # embed.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Application Embedding Initialization File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ Index: Externals/Eagle/lib/Eagle1.0/init.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/init.eagle +++ Externals/Eagle/lib/Eagle1.0/init.eagle @@ -3,11 +3,11 @@ # init.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Interpreter Initialization File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -75,10 +75,29 @@ # # NOTE: This should work properly in both Tcl and Eagle. # return [expr {[info exists ::env($name)] ? $::env($name) : ""}] } + + proc combineFlags { flags1 flags2 } { + # + # NOTE: This should work properly in both Tcl and Eagle. + # + set result [list] + + foreach flags [list $flags1 $flags2] { + foreach flag [split $flags ", "] { + set flag [string trim $flag] + + if {[string length $flag] > 0} then { + lappend result $flag + } + } + } + + return [join $result ,] + } proc getCompileInfo {} { # # NOTE: Return the important compile-time information for use by the # setup or other callers. @@ -748,11 +767,12 @@ } # # NOTE: This proc can be used to dynamically compile C# code in a script. # - proc compileCSharp { string resultsVarName errorsVarName args } { + proc compileCSharp { + string memory symbols strict resultsVarName errorsVarName args } { # # NOTE: Create the C# code provider object (i.e. the compiler). # set provider [object create -alias Microsoft.CSharp.CSharpCodeProvider] @@ -762,14 +782,52 @@ # set parameters [object create -alias \ System.CodeDom.Compiler.CompilerParameters] # - # NOTE: By default, we do not want to persist the generated assembly - # to disk. + # NOTE: Do we not want to persist the generated assembly to disk? # - $parameters GenerateInMemory true + if {$memory} then { + $parameters GenerateInMemory true + } + + # + # NOTE: Do we want symbols to be generated for the generated assembly? + # + if {$symbols} then { + $parameters IncludeDebugInformation true + } + + # + # NOTE: Make sure that the "standard" preprocessor defines match those + # for the platform (i.e. the ones used to compile the Eagle core + # library assembly). + # + set platformOptions [expr { \ + [info exists ::eagle_platform(compileOptions)] ? \ + $::eagle_platform(compileOptions) : [list]}] + + if {[llength $platformOptions] > 0} then { + # + # NOTE: Grab the existing compiler options, if any. + # + set compilerOptions [$parameters CompilerOptions] + + if {"DEBUG" in $platformOptions} then { + append compilerOptions " /define:DEBUG" + } + + if {"TRACE" in $platformOptions} then { + append compilerOptions " /define:TRACE" + } + + # + # NOTE: Reset the compiler options to the pre-existing ones plus the + # extra defines we may have added (above). + # + $parameters CompilerOptions $compilerOptions + } # # NOTE: Process any extra compiler settings the caller may have # provided. # @@ -800,20 +858,16 @@ # # NOTE: Fetch the collection of compiler errors (which may be empty). # set errors [$results -alias Errors] - # - # NOTE: How many compile errors? - # - set count [$errors Count] - # # NOTE: It is assumed that no assembly was generated if there were - # any compile errors. + # any compiler errors. Ignore all compiler warnings unless + # we are in strict mode. # - if {$count > 0} then { + if {[$errors HasErrors] || ($strict && [$errors HasWarnings])} then { # # NOTE: Compilation of the assembly failed. # set code Error @@ -820,10 +874,19 @@ # # NOTE: Prepare to transfer the error messages to the caller. # upvar 1 $errorsVarName local_errors + # + # NOTE: How many compile errors? + # + set count [$errors Count] + + # + # NOTE: Grab each error object and append the string itself to + # the overall list of errors. + # for {set index 0} {$index < $count} {incr index} { # # NOTE: Get the compiler error object at this index. # set error [$errors -alias Item $index] @@ -1215,10 +1278,60 @@ } } return [list "cannot determine if your build is the latest"] } + + proc getReturnType { object member } { + if {[string length $object] == 0 || [string length $member] == 0} then { + return "" + } + + set code [catch { + object foreach -alias memberInfo \ + [object invoke -noinvoke $object $member] { + # + # NOTE: Use the member type to determine which property contains + # the type information we want to return. + # + switch -exact -- [$memberInfo MemberType] { + Field { + return [$memberInfo FieldType.AssemblyQualifiedName] + } + Method { + return [$memberInfo ReturnType.AssemblyQualifiedName] + } + Property { + return [$memberInfo PropertyType.AssemblyQualifiedName] + } + default { + return "" + } + } + } + } result] + + # + # NOTE: If no error was raised above, return the result; otherwise, + # return an empty string to indicate a general failure. + # + return [expr {$code == 2 ? $result : ""}] + } + + proc getDefaultValue { typeName } { + if {[string length $typeName] == 0} then { + return "" + } + + set type [object invoke -alias Type GetType $typeName] + + if {[string length $type] == 0} then { + return "" + } + + return [expr {[$type IsValueType] ? 0 : "null"}] + } proc parray { a {pattern *} } { upvar 1 $a array if {![array exists array]} { @@ -1535,48 +1648,55 @@ # # NOTE: Forget any previous commands that were imported from this # namespace into the global namespace? # if {$forget} then { - namespace forget ${namespace}::* + namespace eval :: [list namespace forget [appendArgs $namespace ::*]] } # - # NOTE: Process each of the commands to be exported from this + # NOTE: Process each command to be exported from the specified # namespace and import it into the global namespace, if # necessary. # foreach export $exports { # - # NOTE: Force importing of our exported commands into the - # global namespace? Otherwise, see if the command is - # already present in the global namespace before trying - # to import it. + # NOTE: Force importing of our exported commands into the global + # namespace? Otherwise, see if the command is already + # present in the global namespace before trying to import + # it. # - if {$force || [llength [info commands ::$export]] == 0} then { - namespace export $export + if {$force || \ + [llength [info commands [appendArgs :: $export]]] == 0} then { + # + # NOTE: Export the specified command from the specified namespace. + # + namespace eval $namespace [list namespace export $export] + + # + # NOTE: Import the specified command into the global namespace. + # + set namespaceExport [appendArgs $namespace :: $export] if {$force} then { - namespace eval :: [list namespace import -force \ - ${namespace}::$export] + namespace eval :: [list namespace import -force $namespaceExport] } else { - namespace eval :: [list namespace import \ - ${namespace}::$export] + namespace eval :: [list namespace import $namespaceExport] } } } } # - # NOTE: Exports the necessary commands from this package and import - # them into the global namespace. + # NOTE: Exports the necessary commands from this package and import them + # into the global namespace. # exportAndImportPackageCommands [namespace current] [list \ exportAndImportPackageCommands isEagle isMono getEnvironmentVariable \ getPluginPath getDictionaryValue getColumnValue getRowColumnValue \ appendArgs haveGaruda lappendArgs readFile filter map reduce \ - getPlatformInfo execShell] false false + getPlatformInfo execShell combineFlags] false false ########################################################################### ############################## END Tcl ONLY ############################### ########################################################################### } @@ -1583,8 +1703,8 @@ # # NOTE: Provide the Eagle library package to the interpreter. # package provide EagleLibrary \ - [expr {[isEagle] ? [info engine PatchLevel] : 1.0}] + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] } Index: Externals/Eagle/lib/Eagle1.0/pkgIndex.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/pkgIndex.eagle +++ Externals/Eagle/lib/Eagle1.0/pkgIndex.eagle @@ -3,11 +3,11 @@ # pkgIndex.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Package Index File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ Index: Externals/Eagle/lib/Eagle1.0/pkgIndex.tcl ================================================================== --- Externals/Eagle/lib/Eagle1.0/pkgIndex.tcl +++ Externals/Eagle/lib/Eagle1.0/pkgIndex.tcl @@ -3,11 +3,11 @@ # pkgIndex.tcl -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Package Index File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ Index: Externals/Eagle/lib/Eagle1.0/safe.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/safe.eagle +++ Externals/Eagle/lib/Eagle1.0/safe.eagle @@ -3,11 +3,11 @@ # safe.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Safe Interpreter Initialization File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -78,8 +78,8 @@ # # NOTE: Provide the Eagle "safe" package to the interpreter. # package provide EagleSafe \ - [expr {[isEagle] ? [info engine PatchLevel] : 1.0}] + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] } Index: Externals/Eagle/lib/Eagle1.0/shell.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/shell.eagle +++ Externals/Eagle/lib/Eagle1.0/shell.eagle @@ -3,11 +3,11 @@ # shell.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Shell Initialization File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -50,8 +50,8 @@ # # NOTE: Provide the Eagle shell package to the interpreter. # package provide EagleShell \ - [expr {[isEagle] ? [info engine PatchLevel] : 1.0}] + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] } Index: Externals/Eagle/lib/Eagle1.0/test.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/test.eagle +++ Externals/Eagle/lib/Eagle1.0/test.eagle @@ -3,11 +3,11 @@ # test.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Test Initialization File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -170,11 +170,11 @@ if {[string length $fileName] > 0} then { if {[file exists $fileName]} then { tputs $::test_channel [appendArgs \ "---- evaluating $type file: \"" $fileName \"\n] - if {[catch {uplevel 1 [list source $fileName]} error] != 0} then { + if {[catch {uplevel 1 [list source $fileName]} error]} then { tputs $::test_channel [appendArgs \ "---- error during $type file: " $error \n] # # NOTE: The error has been logged, now re-throw it. @@ -428,10 +428,14 @@ proc isExitOnComplete {} { return [expr {[info exists ::test_exit_on_complete] && \ [string is boolean -strict $::test_exit_on_complete] && \ $::test_exit_on_complete}] } + + proc returnInfoScript {} { + return [info script] + } proc runTestPrologue {} { # # HACK: We do not want to force every third-party test suite # to come up with a half-baked solution to finding its @@ -546,12 +550,11 @@ } # # NOTE: Display and log the result of the test we just completed. # - host result $code $result - tlog $result + tresult $code $result # # NOTE: If the test failed with an actual error (i.e. not just a # test failure), make sure we do not obscure the error # message with test suite output. @@ -565,12 +568,11 @@ # raise an error now. If we are being run from inside # runAllTests, this will also serve to signal it to stop # processing further test files. # if {$code != 0 && [isStopOnFailure]} then { - host result Error "OVERALL RESULT: STOP-ON-FAILURE\n" - tlog "OVERALL RESULT: STOP-ON-FAILURE\n" + tresult Error "OVERALL RESULT: STOP-ON-FAILURE\n" error ""; # no message } } else { if {$error} then { @@ -695,11 +697,11 @@ catch {set array(tclthreads,$index) [llength [tcl threads]]} catch {set array(tclcommands,$index) [llength [tcl command list]]} } } - proc reportTestStatistics { channel fileName varName } { + proc reportTestStatistics { channel fileName statsVarName filesVarName } { set statistics [list afters variables commands procedures files \ temporaryFiles channels aliases interpreters environment] if {[isEagle]} then { # @@ -713,14 +715,16 @@ } # # NOTE: Show what leaked, if anything. # - upvar 1 $varName array + set count 0; upvar 1 $statsVarName array foreach statistic $statistics { if {$array($statistic,after) > $array($statistic,before)} then { + incr count + tputs $channel [appendArgs "==== \"" $fileName "\" LEAKED " \ $statistic \n] if {[info exists array($statistic,before,list)]} then { tputs $channel [appendArgs "---- " $statistic " BEFORE: " \ @@ -731,10 +735,21 @@ tputs $channel [appendArgs "---- " $statistic " AFTER: " \ $array($statistic,after,list) \n] } } } + + # + # NOTE: Make sure this file name is recorded in the list of file names with + # leaking tests. + # + upvar 1 $filesVarName fileNames + + if {$count > 0 && \ + [lsearch -exact $fileNames [file tail $fileName]] == -1} then { + lappend fileNames [file tail $fileName] + } } proc formatList { list {default ""} {columns 1} } { set count 1 set result "" @@ -840,13 +855,13 @@ if {[isEagle]} then { host title "" } } - proc reportTestPercent { channel percent } { + proc reportTestPercent { channel percent failed leaked } { set status [appendArgs "---- test suite running, about " $percent \ - "% complete..."] + "% complete (" $failed " failed, " $leaked " leaked)..."] tputs $channel [appendArgs $status \n] if {[isEagle]} then { host title $status @@ -876,13 +891,14 @@ # NOTE: So far, we have run no tests. # set count 0 # - # NOTE: So far, no files have had no files with failing tests. + # NOTE: So far, no files have had failing or leaking tests. # set failed [list] + set leaked [list] # # NOTE: Process each file name we have been given by the caller... # set total [llength $fileNames]; set lastPercent -1 @@ -890,14 +906,16 @@ foreach fileName $fileNames { # # NOTE: In terms of files, not tests, what percent done are we now? # set percent [formatDecimal \ - [expr {100.0 * ($count / double($total))}]] + [expr {$total != 0 ? 100.0 * ($count / double($total)) : 100}]] if {$percent != $lastPercent} then { - reportTestPercent $channel $percent + reportTestPercent $channel $percent \ + [llength $failed] [llength $leaked] + set lastPercent $percent } # # NOTE: Skipping over any file name that matches a pattern in the @@ -1011,14 +1029,16 @@ # # NOTE: In terms of files, not tests, what percent done are we now? # set percent [formatDecimal \ - [expr {100.0 * ($count / double($total))}]] + [expr {$total != 0 ? 100.0 * ($count / double($total)) : 100}]] if {$percent != $lastPercent} then { - reportTestPercent $channel $percent + reportTestPercent $channel $percent \ + [llength $failed] [llength $leaked] + set lastPercent $percent } # # NOTE: Record failed test count after this file. @@ -1053,11 +1073,11 @@ # # NOTE: Determine if any resource leaks have occurred and # output diagnostics as necessary if they have. # - reportTestStatistics $channel $fileName leaks + reportTestStatistics $channel $fileName leaks leaked } } else { # # NOTE: This file does not actually count towards the total (i.e. # it contains no actual tests). @@ -1067,14 +1087,16 @@ # # NOTE: In terms of files, not tests, what percent done are we now? # set percent [formatDecimal \ - [expr {100.0 * ($count / double($total))}]] + [expr {$total != 0 ? 100.0 * ($count / double($total)) : 100}]] if {$percent != $lastPercent} then { - reportTestPercent $channel $percent + reportTestPercent $channel $percent \ + [llength $failed] [llength $leaked] + set lastPercent $percent } # # NOTE: If the test file raised an error (i.e. to indicate a @@ -1094,14 +1116,16 @@ # # NOTE: In terms of files, not tests, what percent done are we now? # set percent [formatDecimal \ - [expr {100.0 * ($count / double($total))}]] + [expr {$total != 0 ? 100.0 * ($count / double($total)) : 100}]] if {$percent != $lastPercent} then { - reportTestPercent $channel $percent + reportTestPercent $channel $percent \ + [llength $failed] [llength $leaked] + set lastPercent $percent } } # @@ -1112,23 +1136,33 @@ tputs $channel [appendArgs "---- sourced " $count " test " \ [expr {$count > 1 ? "files" : "file"}] \n] # - # NOTE: Show the files that had failing tests. + # NOTE: Show the files that had failing and/or leaking tests. # if {[llength $failed] > 0} then { tputs $channel [appendArgs "---- files with failing tests: " $failed \n] } + + if {[llength $leaked] > 0} then { + tputs $channel [appendArgs "---- files with leaking tests: " $leaked \n] + } } proc configureTcltest { imports force } { if {[isEagle]} then { # - # NOTE: Fake having the tcltest package. + # HACK: Flag the "test" and "runTest" script library procedures so + # that they use the script location of their caller and not + # their own. + # + # BUGBUG: This does not yet fix the script location issues in the + # test suite. # - package provide tcltest 2.2.10; # Tcl 8.4 + # debug procedureflags test +ScriptLocation + # debug procedureflags runTest +ScriptLocation # # HACK: Compatibility shim(s) for use with various tests in the Tcl # test suite. # @@ -1137,10 +1171,15 @@ # # NOTE: This is needed by most tests in the Tcl test suite. # proc ::tcltest::cleanupTests { args } {} + + # + # NOTE: Fake having the tcltest package. + # + package provide tcltest 2.2.10; # Tcl 8.4 } else { # # NOTE: Load the tcltest package. # package require tcltest @@ -1222,10 +1261,14 @@ if {[info exists test_flags(-constraints)]} then { eval lappend eagle_tests(constraints) $test_flags(-constraints) } } } + + proc tresult { code result } { + host result $code $result; tlog $result + } proc getPassPercentage {} { if {$::eagle_tests(total) > 0} then { return [expr \ {100.0 * (($::eagle_tests(passed) + \ @@ -1244,35 +1287,54 @@ } return 0; # no tests were run, etc. } - proc cleanupThread { thread } { - if {[$thread IsAlive]} then { - if {[catch {$thread Interrupt} error]} then { - tputs $::test_channel [appendArgs \ - "---- failed to interrupt test thread \"" \ - $thread "\": " $error \n] - } - - if {[$thread IsAlive]} then { - if {[catch {$thread Abort} error]} then { - tputs $::test_channel [appendArgs \ - "---- failed to abort test thread \"" \ - $thread "\": " $error \n] - } - - if {![$thread IsAlive]} then { - tputs $::test_channel [appendArgs \ - "---- test thread \"" $thread "\" aborted\n"] - - return true; # aborted? - } - } else { - tputs $::test_channel [appendArgs \ - "---- test thread \"" $thread "\" interrupted\n"] - + proc cleanupThread { thread {timeout 2000} } { + if {[$thread IsAlive]} then { + if {[catch {$thread Interrupt} error]} then { + tputs $::test_channel [appendArgs \ + "---- failed to interrupt test thread \"" $thread "\": " $error \ + \n] + } else { + tputs $::test_channel [appendArgs "---- test thread \"" $thread \ + "\" interrupted\n"] + } + + if {[$thread IsAlive]} then { + if {[catch {$thread Join $timeout} error]} then { + tputs $::test_channel [appendArgs \ + "---- failed to join test thread \"" $thread "\": " $error \n] + } elseif {$error} then { + tputs $::test_channel [appendArgs "---- joined test thread \"" \ + $thread \"\n] + } else { + tputs $::test_channel [appendArgs \ + "---- timeout joining test thread \"" $thread " (" $timeout \ + " milliseconds)\"\n"] + } + + if {[$thread IsAlive]} then { + if {[catch {$thread Abort} error]} then { + tputs $::test_channel [appendArgs \ + "---- failed to abort test thread \"" $thread "\": " $error \ + \n] + } else { + tputs $::test_channel [appendArgs "---- test thread \"" $thread \ + "\" aborted\n"] + } + + if {[$thread IsAlive]} then { + tputs $::test_channel [appendArgs "---- test thread \"" $thread \ + "\" appears to be a zombie\n"] + } else { + return true; # aborted? + } + } else { + return true; # joined? + } + } else { return true; # interrupted? } } else { return true; # already dead? } @@ -1279,34 +1341,116 @@ return false; # still alive (or error). } proc calculateBogoCops { {milliseconds 2000} } { + # + # NOTE: Save the current background error handler for later restoration + # and then reset the current background error handler to nothing. + # + set bgerror [interp bgerror {}] + interp bgerror {} "" + + try { + # + # NOTE: Save the current [after] flags for later restoration and then + # reset them to process events immediately. + # + set flags [after flags] + after flags =Immediate + + try { + set code [catch { + # + # NOTE: Schedule the event to cancel the script we are about to + # evaluate, capturing the name so we can cancel it later, if + # necessary. + # + set event [after $milliseconds [list interp cancel]] + + # + # HACK: There is the potential for a "race condition" here. If the + # specified number of milliseconds elapses before (or after) + # entering the [catch] script block (below) then the resulting + # script cancellation error will not be caught and we will be + # unable to return the correct result to the caller. + # + set before [info cmdcount] + catch {time {nop} -1}; # uses the [time] internal busy loop. + set after [info cmdcount] + + # + # HACK: Mono has a bug that results in excessive trailing zeros + # here (Mono bug #655780). + # + if {[isMono]} then { + expr {double(($after - $before) / ($milliseconds / 1000.0))} + } else { + expr {($after - $before) / ($milliseconds / 1000.0)} + } + } result] + + # + # NOTE: If we failed to calculate the number of commands-per-second + # due to some subtle race condition [as explained above], return + # an obviously invalid result instead. + # + if {$code == 0} then { + return $result + } else { + return 0 + } + } finally { + if {[info exists event]} then { + catch {after cancel $event} + } + + after flags =$flags + } + } finally { + interp bgerror {} $bgerror + } + } + + proc evalWithTimeout { script {milliseconds 2000} {resultVarName ""} } { + # + # NOTE: Save the current background error handler for later restoration + # and then reset the current background error handler to nothing. + # set bgerror [interp bgerror {}] interp bgerror {} "" try { + # + # NOTE: Save the current [after] flags for later restoration and then + # reset them to process events immediately. + # set flags [after flags] after flags =Immediate try { - set event [after $milliseconds [list interp cancel]] - - set before [info cmdcount] - catch {time {nop} -1}; # internal busy loop. - set after [info cmdcount] - - # - # HACK: Mono has a bug that results in excessive trailing zeros - # here (Mono bug #655780). - # - if {[isMono]} then { - return [expr \ - {double(($after - $before) / ($milliseconds / 1000.0))}] - } else { - return [expr {($after - $before) / ($milliseconds / 1000.0)}] - } + # + # NOTE: Evaluate the specified script in the context of the caller, + # returning the result to the caller. + # + if {[string length $resultVarName] > 0} then { + upvar 1 $resultVarName result + } + + return [catch { + # + # NOTE: Schedule the event to cancel the script we are about to + # evaluate, capturing the name so we can cancel it later, if + # necessary. + # + set event [after $milliseconds [list interp cancel]] + + # + # NOTE: Evaluate the script in the context of the caller. + # + uplevel 1 $script + } result] } finally { if {[info exists event]} then { catch {after cancel $event} } @@ -1315,11 +1459,11 @@ } finally { interp bgerror {} $bgerror } } - proc getMachineForTclShell {} { + proc testExecTclScript { script } { try { # # NOTE: Get a temporary file name for the script we are going to # use to query the machine type for the native Tcl shell. # @@ -1328,19 +1472,19 @@ # # NOTE: Since the native Tcl shell cannot simply evaluate a string # supplied via the command line, write the script to be # evaluated to the temporary file. # - writeFile $fileName {puts -nonewline stdout $tcl_platform(machine)} + writeFile $fileName $script # # NOTE: Evaluate the script using the native Tcl shell, trim the # excess whitespace from the output, and return it to the # caller. # if {[catch {string trim \ - [testExec $::test_tclsh [list] \ + [testExec $::test_tclsh [list -success Success] \ [appendArgs \" $fileName \"]]} result] == 0} then { # # NOTE: Success, return the result to the caller. # return $result @@ -1364,10 +1508,22 @@ # catch {file delete $fileName} } } } + + proc getMachineForTclShell {} { + return [testExecTclScript { + puts -nonewline stdout $tcl_platform(machine) + }] + } + + proc getTkVersion {} { + return [testExecTclScript { + puts -nonewline stdout [package require Tk]; exit + }] + } proc machineToPlatform { machine } { switch -exact -nocase -- $machine { amd64 { return x64 @@ -1475,20 +1631,21 @@ # if {![interp issafe] && ![info exists ::test_path]} then { # # NOTE: Try the source release directory structure. # - set ::test_path [file join [file normalize [file dirname [file dirname \ - [info library]]]] Library Tests] + set ::test_path [file join [file normalize [file dirname \ + [file dirname [info library]]]] Library Tests] if {![file exists $::test_path] || \ ![file isdirectory $::test_path]} then { # # NOTE: Try for the test package directory. # set ::test_path [file join [file normalize [file dirname \ - [file dirname [info script]]]] Test1.0] + [file dirname [info script]]]] [appendArgs Test \ + [info engine Version]]] } if {![file exists $::test_path] || \ ![file isdirectory $::test_path]} then { # @@ -1495,12 +1652,12 @@ # NOTE: This must be a binary release, no "Library" directory then. # Also, binary releases have an upper-case "Tests" directory # name that originates from the "update.bat" tool. This must # match the casing used in "update.bat". # - set ::test_path [file join [file normalize [file dirname [file dirname \ - [info library]]]] Tests] + set ::test_path [file join [file normalize [file dirname \ + [file dirname [info library]]]] Tests] } } # # NOTE: Fake having the tcltest package unless we are prevented. @@ -1539,11 +1696,11 @@ } # # NOTE: Setup the test path relative to the path of this file. # - if {![info exists ::test_path]} then { + if {![interp issafe] && ![info exists ::test_path]} then { # # NOTE: Try the source release directory structure. # set ::test_path [file join [file normalize [file dirname \ [file dirname [file dirname [info script]]]]] Library Tests] @@ -1571,11 +1728,11 @@ } # # NOTE: Load and configure the tcltest package unless we are prevented. # - if {![info exists ::no(configureTcltest)]} then { + if {![interp issafe] && ![info exists ::no(configureTcltest)]} then { configureTcltest [list test testConstraint] false } # # NOTE: We need several of our test related commands in the global @@ -1585,12 +1742,12 @@ calculateRelativePerformance haveConstraint haveOrAddConstraint \ processTestArguments getTemporaryPath getTestLog getTestLogId getFiles \ getConstraints getTestFiles getTestRunId execTestShell runTestPrologue \ runTestEpilogue runTest runAllTests fixConstraints sourceIfValid \ isExitOnComplete getPassPercentage getSkipPercentage testExec tlog \ - tputs formatDecimal formatList configureTcltest tsource testShim] \ - false false + returnInfoScript tputs formatDecimal formatList configureTcltest \ + tsource testShim] false false ########################################################################### ############################## END Tcl ONLY ############################### ########################################################################### } @@ -1597,8 +1754,8 @@ # # NOTE: Provide the Eagle test package to the interpreter. # package provide EagleTest \ - [expr {[isEagle] ? [info engine PatchLevel] : 1.0}] + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] } Index: Externals/Eagle/lib/Eagle1.0/vendor.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/vendor.eagle +++ Externals/Eagle/lib/Eagle1.0/vendor.eagle @@ -3,11 +3,11 @@ # vendor.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Vendor Initialization File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -21,22 +21,38 @@ # initialization and/or customizations in here. Additionally, this file # may contain per-interpreter customizations required when porting to # new platforms, operating systems, etc. # +############################################################################### +############################## BEGIN VENDOR CODE ############################## +############################################################################### # # NOTE: Use our own namespace here because even though we do not directly # support namespaces ourselves, we do not want to pollute the global # namespace if this script actually ends up being evaluated in Tcl. # namespace eval ::Eagle { if {[isEagle]} then { - # - # NOTE: This script library helper procedure helps to establish the link - # between the System.Data.SQLite test package and Eagle. - # - proc addTestSuiteToAutoPath { channel quiet } { + proc checkForTestOverrides { channel varNames quiet } { + set result 0 + + foreach varName $varNames { + if {[uplevel 1 [list info exists $varName]]} then { + incr result + + if {!$quiet} then { + puts -nonewline $channel [appendArgs \ + "Found vendor-specific test override \"" $varName "\".\n"] + } + } + } + + return $result + } + + proc addTestSuiteToAutoPath { channel varName quiet } { # # NOTE: Start with the directory containing this file. # set dir [file normalize [file dirname [info script]]] @@ -52,14 +68,22 @@ # if {[file exists [file join $dir Tests]] && \ [file isdirectory [file join $dir Tests]] && \ [file exists [file join $dir Tests pkgIndex.eagle]] && \ [file isfile [file join $dir Tests pkgIndex.eagle]]} then { + # + # NOTE: If requested, give the caller access to the name of the + # directory we just found. + # + if {[string length $varName] > 0} then { + upvar 1 $varName dir2 + } + # # NOTE: Ok, show the directory we found. # - set dir [file join $dir Tests] + set dir2 [file join $dir Tests] # # NOTE: We found the necessary directory to add to the auto-path; # However, we cannot simply add it to the auto-path directly # because the auto-path is dynamically constructed after this @@ -66,21 +90,31 @@ # script is evaluated; therefore, set the Eagle library path # environment variable and force the appropriate internal path # list to be refreshed. # if {![info exists ::env(EAGLELIBPATH)] || \ - [lsearch -exact $::env(EAGLELIBPATH) $dir] == -1} then { + [lsearch -exact $::env(EAGLELIBPATH) $dir2] == -1} then { # # NOTE: If we have NOT been instructed to be quiet, report now. # if {!$quiet} then { puts -nonewline $channel [appendArgs \ - "Found vendor-specific test package directory \"" $dir \ + "Found vendor-specific test package directory \"" $dir2 \ "\", adding...\n"] } - lappend ::env(EAGLELIBPATH) $dir + # + # NOTE: Append the directory to the necessary environment variable + # so that it will get picked up when Eagle actually rebuilds + # the auto-path list (below). + # + lappend ::env(EAGLELIBPATH) $dir2 + + # + # NOTE: Force Eagle to rebuild the auto-path list for the current + # interpreter right now. + # object invoke Utility RefreshAutoPathList } # # NOTE: We are done, return success. @@ -105,9 +139,52 @@ # # NOTE: Directory not found, return failure. # return false } + + proc setupInterpreterTestPath { channel dir quiet } { + set testPath [object invoke -flags +NonPublic Interpreter.GetActive \ + TestPath] + + if {$dir ne $testPath} then { + object invoke -flags +NonPublic Interpreter.GetActive TestPath $dir + + if {!$quiet} then { + puts -nonewline $channel [appendArgs \ + "Set interpreter test path to \"" $dir \".\n] + } + } + } + + checkForTestOverrides stdout \ + [list binary_directory build_base_directory build_directory \ + common_directory connection_flags database_directory \ + datetime_format test_configuration test_year] false + + # + # NOTE: This variable will contain the name of the directory containing the + # vendor-specific testing infrastructure. + # + set ::vendor_directory "" + + # + # NOTE: This procedure will attempt to find the vendor-specific testing + # infrastructure directory and add it to the auto-path for the + # current interpreter. + # + addTestSuiteToAutoPath stdout ::vendor_directory false - addTestSuiteToAutoPath stdout false + # + # NOTE: If we actually found a vendor-specific testing infrastructure + # directory then modify the TestPath property of the current + # interpreter to point directly to it. + # + if {[string length $::vendor_directory] > 0} then { + setupInterpreterTestPath stdout $::vendor_directory false + } } } + +############################################################################### +############################### END VENDOR CODE ############################### +############################################################################### Index: Externals/Eagle/lib/Test1.0/constraints.eagle ================================================================== --- Externals/Eagle/lib/Test1.0/constraints.eagle +++ Externals/Eagle/lib/Test1.0/constraints.eagle @@ -3,11 +3,11 @@ # constraints.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Test Constraints File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -123,10 +123,32 @@ } tputs $channel no\n } } + + proc checkForSymbols { channel name {constraint ""} } { + set fileName [file normalize [appendArgs [file rootname $name] .pdb]] + + tputs $channel [appendArgs "---- checking for symbols \"" $fileName \ + "\"... "] + + if {[file exists $fileName]} then { + # + # NOTE: The file appears to have associated symbols available. + # + if {[string length $constraint] > 0} then { + addConstraint [appendArgs symbols_ $constraint] + } else { + addConstraint [appendArgs symbols_ [file tail $name]] + } + + tputs $channel yes\n + } else { + tputs $channel no\n + } + } proc checkForLogFile { channel } { tputs $channel "---- checking for log file... " if {[info exists ::test_log] && \ @@ -533,9 +555,14 @@ } else { tputs $channel no\n } } - proc checkForTiming { channel threshold } { - tputs $channel "---- checking for precision timing... " + proc checkForTiming { channel threshold {constraint ""} {tries 1} } { + tputs $channel [appendArgs \ + "---- checking for precision timing (" $threshold " milliseconds)... "] # + # HACK: Sometimes the first try takes quite a bit longer than subsequent + # tries. We attempt to bypass this problem by retrying a set number + # of times (which can be overridden by the caller) before giving up. + # @@ -542,8 +569,9 @@ - # NOTE: Are we allowed to do precision timing tests? - # - if {![info exists ::no(timing)]} then { + set try 0 + set difference unknown + + for {} {$try < $tries} {incr try} { # # NOTE: Attempt to block for exactly one second. # set start [expr {[clock clicks -milliseconds] & 0x7fffffff}] after 1000; # wait for "exactly" one second. @@ -557,21 +585,33 @@ # # NOTE: Are we within the threshold specified by the caller? # if {$difference >= 0 && $difference <= $threshold} then { - addConstraint timing + # + # NOTE: We appear to be capable of fairly precise timing. + # + if {[string length $constraint] > 0} then { + addConstraint $constraint + } else { + addConstraint timing + } tputs $channel [appendArgs "yes (0 <= " $difference " <= " \ - $threshold " milliseconds)\n"] - } else { - tputs $channel [appendArgs "no (0 <= " $difference " > " \ - $threshold " milliseconds)\n"] - } - } else { - tputs $channel no\n - } + $threshold " milliseconds, tried " [expr {$try + 1}] \ + " " [expr {$try + 1 > 1 ? "times" : "time"}] ")\n"] + + # + # NOTE: We are done here, return now. + # + return + } + } + + tputs $channel [appendArgs "no (0 <= " $difference " > " \ + $threshold " milliseconds, tried " $try " " \ + [expr {$try > 1 ? "times" : "time"}] ")\n"] } proc checkForPerformance { channel } { tputs $channel "---- checking for performance testing... " @@ -728,10 +768,47 @@ tputs $channel trusted\n } else { tputs $channel untrusted\n } } + + proc checkForStrongName { channel } { + tputs $channel "---- checking for strong name... " + + set strongName [object invoke Interpreter.GetActive GetStrongName] + + if {[string length $strongName] > 0} then { + # + # NOTE: Yes, it appears that the core library was signed with a + # strong name key. + # + addConstraint strongName + + tputs $channel yes\n + } else { + tputs $channel no\n + } + } + + proc checkForCertificate { channel } { + tputs $channel "---- checking for certificate... " + + set certificate [object invoke Interpreter.GetActive GetCertificate] + + if {[string length $certificate] > 0} then { + # + # NOTE: Yes, it appears that the core library was signed with a + # code-signing certificate. + # + addConstraint certificate + + tputs $channel [appendArgs "yes (" \ + [object invoke $certificate Subject] ")\n"] + } else { + tputs $channel no\n + } + } proc checkForAdministrator { channel } { tputs $channel "---- checking for administrator... " if {[isAdministrator]} then { @@ -768,10 +845,30 @@ tputs $channel [appendArgs "yes (" $threadId ")\n"] } else { tputs $channel [appendArgs "no (" $threadId ")\n"] } } + + proc checkForDefaultAppDomain { channel } { + tputs $channel "---- checking for default application domain... " + + set appDomain [object invoke AppDomain CurrentDomain] + + if {[string length $appDomain] > 0} then { + if {[object invoke $appDomain IsDefaultAppDomain]} then { + addConstraint defaultAppDomain + + tputs $channel [appendArgs "yes (" [object invoke $appDomain Id] \ + ")\n"] + } else { + tputs $channel [appendArgs "no (" [object invoke $appDomain Id] \ + ")\n"] + } + } else { + tputs $channel [appendArgs "no (null)\n"] + } + } proc checkForRuntime { channel } { tputs $channel "---- checking for runtime... " # @@ -1015,10 +1112,35 @@ if {[string length $culture] > 0} then { # # NOTE: The culture information is present, use it and show it. # addConstraint [appendArgs culture. [string map [list - _] $culture]] + + tputs $channel [appendArgs $culture \n] + } else { + tputs $channel [appendArgs unknown \n] + } + } + + proc checkForThreadCulture { channel } { + tputs $channel "---- checking for thread culture... " + + # + # NOTE: Grab the current thread culture. + # + set culture [object invoke System.Threading.Thread.CurrentThread \ + CurrentCulture] + + set culture [object invoke Eagle._Components.Private.FormatOps \ + CultureName $culture false] + + if {[string length $culture] > 0} then { + # + # NOTE: The culture information is present, use it and show it. + # + addConstraint [appendArgs threadCulture. [string map [list - _] \ + $culture]] tputs $channel [appendArgs $culture \n] } else { tputs $channel [appendArgs unknown \n] } @@ -1329,10 +1451,31 @@ $result ")\n"] } else { tputs $channel "---- checking for Tcl shell... no\n" } } + + proc checkForTkPackage { channel } { + # + # HACK: We do not care about the Tk version returned from this + # procedure, we only care if it returns "error" because that + # would indicate an error was caught during [exec] (i.e. the + # native Tcl shell could not be executed). + # + if {[catch {getTkVersion} result] == 0 && \ + $result ne "error"} then { + # + # NOTE: Yes, a native Tk package appears to be available. + # + addConstraint tkPackage + + tputs $channel [appendArgs "---- checking for Tk package... yes (" \ + $result ")\n"] + } else { + tputs $channel "---- checking for Tk package... no\n" + } + } proc checkForPowerShell { channel } { tputs $channel "---- checking for PowerShell... " # @@ -1430,11 +1573,11 @@ if {[string length $directory] == 0} then { # # NOTE: Registry hive where WiX install information # is stored. # - set key {HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML} + set key {HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Installer XML} # # NOTE: The versions of WiX that we support. # set versions [list 3.5 3.0] @@ -1455,49 +1598,40 @@ # # NOTE: Does the directory name look valid and # does it actually exist? # - if {[string length $directory] > 0} then { - # - # NOTE: Does the directory actually exist? - # - if {[file isdirectory $directory]} then { - # - # NOTE: The file name of the primary WiX assembly. - # - set fileName [file join $directory wix.dll] - - # - # NOTE: We do not know the file version yet. - # - set version "" - - # - # NOTE: Attempt to query the version of the file. - # - if {[catch {file version $fileName} version] == 0 && \ - [string length $version] > 0} then { - # - # NOTE: Indicate where we found the file. - # - set where registry - - # - # NOTE: We found it, bail out now. - # - break - } else { - # - # NOTE: The file does not exist or is not properly - # versioned. - # - set directory "" - } - } else { - # - # NOTE: The directory does not exist. + if {[string length $directory] > 0 && \ + [file isdirectory $directory]} then { + # + # NOTE: The file name of the primary WiX assembly. + # + set fileName [file join $directory wix.dll] + + # + # NOTE: We do not know the file version yet. + # + set version "" + + # + # NOTE: Attempt to query the version of the file. + # + if {[catch {file version $fileName} version] == 0 && \ + [string length $version] > 0} then { + # + # NOTE: Indicate where we found the file. + # + set where registry + + # + # NOTE: We found it, bail out now. + # + break + } else { + # + # NOTE: The file does not exist or is not properly + # versioned. # set directory "" } } } @@ -1532,10 +1666,79 @@ } } tputs $channel no\n } + + proc checkForVisualStudio { channel } { + tputs $channel "---- checking for Visual Studio... " + + # + # NOTE: Platform must be Windows for this constraint to even be + # checked (i.e. we require the registry). + # + set visualStudioVersions [list] + + if {$::tcl_platform(platform) eq "windows"} then { + # + # NOTE: Registry hive where Visual Studio install information + # is stored. + # + set key {HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio} + + # + # NOTE: The versions of Visual Studio that we support. + # + set versions [list [list 8.0 2005] [list 9.0 2008] [list 10.0 2010]] + + # + # NOTE: Check each version and keep track of the ones we find. + # + foreach version $versions { + # + # NOTE: Attempt to fetch the Visual Studio install directory + # value from the registry, removing the trailing backslash, + # if any. + # + set fileName [file normalize [file join [string trimright [object \ + invoke Microsoft.Win32.Registry GetValue [appendArgs $key \\ \ + [lindex $version 0]] InstallDir null] \\] msenv.dll]] + + # + # NOTE: Does the directory name look valid and does it actually + # exist? + # + if {[string length $fileName] > 0 && [file isfile $fileName]} then { + # + # NOTE: Yes, it appears that it is available. + # + addConstraint [appendArgs visualStudio [lindex $version 1]] + + # + # NOTE: Keep track of all the versions that we find. + # + lappend visualStudioVersions [lindex $version 1] + + # + # NOTE: Save the directory for later usage by + # the test itself. + # + set ::test_visual_studio [file dirname $fileName] + } + } + } + + if {[llength $visualStudioVersions] > 0} then { + # + # NOTE: Show where we found the latest version. + # + tputs $channel [appendArgs "yes (" $visualStudioVersions ", \"" \ + $::test_visual_studio "\")\n"] + } else { + tputs $channel no\n + } + } proc checkForManagedDebugger { channel } { tputs $channel "---- checking for managed debugger... " # @@ -1643,13 +1846,13 @@ # exportAndImportPackageCommands [namespace current] [list checkForPlatform \ checkForEagle checkForGaruda checkForShell checkForDebug checkForTk \ checkForVersion checkForCommand checkForFile checkForNativeCode \ checkForTip127 checkForTip194 checkForTip241 checkForTip285 \ - checkForPerformance checkForTiming checkForInteractive checkForLogFile \ - checkForNetwork checkForCompileOption checkForUserInteraction] false \ - false + checkForPerformance checkForTiming checkForInteractive checkForSymbols \ + checkForLogFile checkForNetwork checkForCompileOption \ + checkForUserInteraction] false false ########################################################################### ############################## END Tcl ONLY ############################### ########################################################################### } @@ -1656,8 +1859,8 @@ # # NOTE: Provide the Eagle test constraints package to the interpreter. # package provide EagleTestConstraints \ - [expr {[isEagle] ? [info engine PatchLevel] : 1.0}] + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] } Index: Externals/Eagle/lib/Test1.0/epilogue.eagle ================================================================== --- Externals/Eagle/lib/Test1.0/epilogue.eagle +++ Externals/Eagle/lib/Test1.0/epilogue.eagle @@ -3,11 +3,11 @@ # epilogue.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Test Epilogue File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -17,10 +17,16 @@ if {![info exists no([file tail [info script]])]} then { if {[info level] > 0} then { error "cannot run, current level is not global" } + # + # NOTE: Show when the tests actually ended (now). + # + tputs $test_channel [appendArgs "---- tests ended at " \ + [clock format [clock seconds]] \n] + if {[isEagle]} then { # # NOTE: Show the current state of the memory. # catch {debug memory} memory @@ -37,19 +43,11 @@ tputs $test_channel [appendArgs "---- ending stack: " \ [formatListAsDict $stack] \n] unset stack - } - - # - # NOTE: Show when the tests actually ended (now). - # - tputs $test_channel [appendArgs "---- tests ended at " \ - [clock format [clock seconds]] \n] - - if {[isEagle]} then { + # # NOTE: Check for and display any duplicate test names that we found. In # theory, this checking may produce false positives if a test file # (or the entire test suite) is run multiple times without resetting # the test statistics and/or restarting Eagle; however, duplicate @@ -68,51 +66,43 @@ unset -nocomplain name count tputs $test_channel \n; # NOTE: Blank line. if {$eagle_tests(passed) > 0} then { - host result Ok [appendArgs "PASSED: " $eagle_tests(passed) \n] - tlog [appendArgs "PASSED: " $eagle_tests(passed) \n] + tresult Ok [appendArgs "PASSED: " $eagle_tests(passed) \n] } if {$eagle_tests(failed) > 0} then { - host result Error [appendArgs "FAILED: " $eagle_tests(failed) \n] - tlog [appendArgs "FAILED: " $eagle_tests(failed) \n] + tresult Error [appendArgs "FAILED: " $eagle_tests(failed) \n] if {[llength $eagle_tests(failedNames)] > 0} then { - host result Error [appendArgs "FAILED: " $eagle_tests(failedNames) \n] - tlog [appendArgs "FAILED: " $eagle_tests(failedNames) \n] + tresult Error [appendArgs "FAILED: " $eagle_tests(failedNames) \n] } } if {$eagle_tests(skipped) > 0} then { - host result Break [appendArgs "SKIPPED: " $eagle_tests(skipped) \n] - tlog [appendArgs "SKIPPED: " $eagle_tests(skipped) \n] + tresult Break [appendArgs "SKIPPED: " $eagle_tests(skipped) \n] if {[llength $eagle_tests(skippedNames)] > 0} then { - host result Break [appendArgs "SKIPPED: " $eagle_tests(skippedNames) \n] - tlog [appendArgs "SKIPPED: " $eagle_tests(skippedNames) \n] + tresult Break [appendArgs "SKIPPED: " $eagle_tests(skippedNames) \n] } } if {$eagle_tests(total) > 0} then { - host result Return [appendArgs "TOTAL: " $eagle_tests(total) \n] - tlog [appendArgs "TOTAL: " $eagle_tests(total) \n] + tresult Return [appendArgs "TOTAL: " $eagle_tests(total) \n] if {$eagle_tests(skipped) > 0} then { set percent [getSkipPercentage] - host result Break [appendArgs "SKIP PERCENTAGE: " \ - [formatDecimal $percent] %\n] - tlog [appendArgs "SKIP PERCENTAGE: " [formatDecimal $percent] %\n] + tresult Break [appendArgs \ + "SKIP PERCENTAGE: " [formatDecimal $percent] %\n] } set percent [getPassPercentage] - host result Return [appendArgs "PASS PERCENTAGE: " \ - [formatDecimal $percent] %\n] - tlog [appendArgs "PASS PERCENTAGE: " [formatDecimal $percent] %\n] + tresult Return [appendArgs \ + "PASS PERCENTAGE: " [formatDecimal $percent] %\n] } else { # # NOTE: No tests. # set percent 0 @@ -132,17 +122,15 @@ $eagle_tests(skipped)}] if {$passedOrSkipped == $eagle_tests(total)} then { set exitCode Success - host result Ok "OVERALL RESULT: SUCCESS\n" - tlog "OVERALL RESULT: SUCCESS\n" + tresult Ok "OVERALL RESULT: SUCCESS\n" } else { set exitCode Failure - host result Error "OVERALL RESULT: FAILURE\n" - tlog "OVERALL RESULT: FAILURE\n" + tresult Error "OVERALL RESULT: FAILURE\n" } unset passedOrSkipped } else { # @@ -151,22 +139,16 @@ # set the exit code to success; otherwise, set it to failure. # if {$percent >= $test_threshold} then { set exitCode Success - host result Ok [appendArgs "OVERALL RESULT: SUCCESS (" \ - $percent "% >= " $test_threshold %)\n] - - tlog [appendArgs "OVERALL RESULT: SUCCESS (" \ - $percent "% >= " $test_threshold %)\n] + tresult Ok [appendArgs \ + "OVERALL RESULT: SUCCESS (" $percent "% >= " $test_threshold %)\n] } else { set exitCode Failure - host result Error [appendArgs \ - "OVERALL RESULT: FAILURE (" $percent "% < " $test_threshold %)\n] - - tlog [appendArgs \ + tresult Error [appendArgs \ "OVERALL RESULT: FAILURE (" $percent "% < " $test_threshold %)\n] } } unset percent @@ -174,42 +156,44 @@ tputs $test_channel \n; # NOTE: Blank line. } else { tputs $test_channel \n; # NOTE: Blank line. if {$::tcltest::numTests(Passed) > 0} then { - tputs $test_channel \ - [appendArgs "PASSED: " $::tcltest::numTests(Passed) \n] - } - - if {$::tcltest::numTests(Failed) > 0} then { - tputs $test_channel \ - [appendArgs "FAILED: " $::tcltest::numTests(Failed) \n] - - if {[llength $::tcltest::failFiles] > 0} then { - tputs $test_channel \ - [appendArgs "FAILED: " $::tcltest::failFiles \n] - } - } - - if {$::tcltest::numTests(Skipped) > 0} then { - tputs $test_channel \ - [appendArgs "SKIPPED: " $::tcltest::numTests(Skipped) \n] - } - - if {$::tcltest::numTests(Total) > 0} then { - tputs $test_channel \ - [appendArgs "TOTAL: " $::tcltest::numTests(Total) \n] - - if {$::tcltest::numTests(Skipped) > 0} then { - set percent [getSkipPercentage] - tputs $test_channel [appendArgs "SKIP PERCENTAGE: " \ - [formatDecimal $percent] %\n] - } - - set percent [getPassPercentage] - tputs $test_channel [appendArgs "PASS PERCENTAGE: " \ - [formatDecimal $percent] %\n] + tputs $test_channel [appendArgs \ + "PASSED: " $::tcltest::numTests(Passed) \n] + } + + if {$::tcltest::numTests(Failed) > 0} then { + tputs $test_channel [appendArgs \ + "FAILED: " $::tcltest::numTests(Failed) \n] + + if {[llength $::tcltest::failFiles] > 0} then { + tputs $test_channel [appendArgs \ + "FAILED: " $::tcltest::failFiles \n] + } + } + + if {$::tcltest::numTests(Skipped) > 0} then { + tputs $test_channel [appendArgs \ + "SKIPPED: " $::tcltest::numTests(Skipped) \n] + } + + if {$::tcltest::numTests(Total) > 0} then { + tputs $test_channel [appendArgs \ + "TOTAL: " $::tcltest::numTests(Total) \n] + + if {$::tcltest::numTests(Skipped) > 0} then { + set percent [getSkipPercentage] + + tputs $test_channel [appendArgs \ + "SKIP PERCENTAGE: " [formatDecimal $percent] %\n] + } + + set percent [getPassPercentage] + + tputs $test_channel [appendArgs \ + "PASS PERCENTAGE: " [formatDecimal $percent] %\n] } else { # # NOTE: No tests. # set percent 0 @@ -246,17 +230,17 @@ # set the exit code to success; otherwise, set it to failure. # if {$percent >= $test_threshold} then { set exitCode 0; # Success. - tputs $test_channel [appendArgs "OVERALL RESULT: SUCCESS (" \ - $percent "% >= " $test_threshold %)\n] + tputs $test_channel [appendArgs \ + "OVERALL RESULT: SUCCESS (" $percent "% >= " $test_threshold %)\n] } else { set exitCode 1; # Failure. - tputs $test_channel [appendArgs "OVERALL RESULT: FAILURE (" \ - $percent "% < " $test_threshold %)\n] + tputs $test_channel [appendArgs \ + "OVERALL RESULT: FAILURE (" $percent "% < " $test_threshold %)\n] } } unset percent Index: Externals/Eagle/lib/Test1.0/pkgIndex.eagle ================================================================== --- Externals/Eagle/lib/Test1.0/pkgIndex.eagle +++ Externals/Eagle/lib/Test1.0/pkgIndex.eagle @@ -3,11 +3,11 @@ # pkgIndex.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Package Index File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ Index: Externals/Eagle/lib/Test1.0/pkgIndex.tcl ================================================================== --- Externals/Eagle/lib/Test1.0/pkgIndex.tcl +++ Externals/Eagle/lib/Test1.0/pkgIndex.tcl @@ -3,11 +3,11 @@ # pkgIndex.tcl -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Package Index File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ Index: Externals/Eagle/lib/Test1.0/prologue.eagle ================================================================== --- Externals/Eagle/lib/Test1.0/prologue.eagle +++ Externals/Eagle/lib/Test1.0/prologue.eagle @@ -3,11 +3,11 @@ # prologue.eagle -- # # Extensible Adaptable Generalized Logic Engine (Eagle) # Test Prologue File # -# Copyright (c) 2007-2010 by Joe Mistachkin. All rights reserved. +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: $ @@ -48,24 +48,28 @@ # actually point to the "lib\Eagle1.0" sub-directory under the # solution directory. # # WARNING: The Eagle package name and version are hard-coded here. # + set pkg_dir Eagle1.0; # TODO: Change me. + if {![file exists [file join $base_path lib]] || \ ![file isdirectory [file join $base_path lib]] || \ - ![file exists [file join $base_path lib Eagle1.0]] || \ - ![file isdirectory [file join $base_path lib Eagle1.0]] || \ - ![file exists [file join $base_path lib Eagle1.0 init.eagle]] || \ - ![file isfile [file join $base_path lib Eagle1.0 init.eagle]]} then { - # - # NOTE: We do not bother to check if the "lib" sub-directory - # actually exists as a child of this one. This is the - # previous (legacy) behavior (i.e. where we always went - # up two levels to the base directory). + ![file exists [file join $base_path lib $pkg_dir]] || \ + ![file isdirectory [file join $base_path lib $pkg_dir]] || \ + ![file exists [file join $base_path lib $pkg_dir init.eagle]] || \ + ![file isfile [file join $base_path lib $pkg_dir init.eagle]]} then { + # + # NOTE: We do not bother to check if the "lib" sub-directory actually + # exists as a child of this one. This is the previous (legacy) + # behavior (i.e. where we always went up two levels to the base + # directory). # set base_path [file dirname $base_path] } + + unset pkg_dir } # # NOTE: Set the local root directory of the source checkout (i.e. of # Eagle or whatever project the Eagle binaries are being used by). @@ -91,11 +95,11 @@ # trailing slashes. # set root_path [file normalize $directory] } - unset -nocomplain directory exec pattern + unset -nocomplain directory dummy exec pattern } # # NOTE: Set the executable file name for the process, if # necessary. @@ -523,10 +527,17 @@ # # if {![info exists no(mono)] && [isMono]} then { # set no(mono) true # } + ########################################################################### + ######################### BEGIN Eagle Constraints ######################### + ########################################################################### + + tputs $test_channel [appendArgs \ + "---- start of Eagle specific test constraints...\n"] + # # NOTE: Has administrator detection support been disabled? We do # this check [nearly] first as it may [eventually] be used # to help determine if other constraints should be skipped. # @@ -547,10 +558,20 @@ # to help determine if other constraints should be skipped. # if {![info exists no(primaryThread)]} then { checkForPrimaryThread $test_channel } + + # + # NOTE: Has default application domain detection support been + # disabled? We do this check [nearly] first as it may + # [eventually] be used to help determine if other + # constraints should be skipped. + # + if {![info exists no(defaultAppDomain)]} then { + checkForDefaultAppDomain $test_channel + } # # NOTE: Has runtime detection support been disabled? We do this # checking [nearly] first as it may skip other constraints. # @@ -587,16 +608,37 @@ # if {![info exists no(culture)]} then { checkForCulture $test_channel } + # + # NOTE: Has thread culture detection support been disabled? + # + if {![info exists no(threadCulture)]} then { + checkForThreadCulture $test_channel + } + # # NOTE: Has software update trust detection support been disabled? # if {![info exists no(softwareUpdate)]} then { checkForSoftwareUpdateTrust $test_channel } + + # + # NOTE: Has strong name detection support been disabled? + # + if {![info exists no(strongName)]} then { + checkForStrongName $test_channel + } + + # + # NOTE: Has certificate detection support been disabled? + # + if {![info exists no(certificate)]} then { + checkForCertificate $test_channel + } # # NOTE: Has database testing support been disabled? # if {![info exists no(sql)]} then { @@ -653,12 +695,20 @@ # # NOTE: Can we access the local database? # checkForDatabase $test_channel $test_database + unset password user timeout database server } + + # + # NOTE: Has symbol testing support been disabled? + # + if {![info exists no(assemblySymbols)]} then { + checkForSymbols $test_channel [lindex [info assembly] end] + } # # NOTE: Has quiet testing support been disabled? # if {![info exists no(quiet)]} then { @@ -770,11 +820,11 @@ # # NOTE: Has Windows support been enabled (at compile-time)? # if {![info exists no(compileWindows)]} then { # - # NOTE: For test "garuda-1.0". + # NOTE: For test "garuda-1.1". # checkForCompileOption $test_channel WINDOWS } # @@ -790,11 +840,11 @@ # # NOTE: Has native package support been enabled (at compile-time)? # if {![info exists no(compileNativePackage)]} then { # - # NOTE: For test "garuda-1.0". + # NOTE: For test "garuda-1.1". # checkForCompileOption $test_channel NATIVE_PACKAGE } # @@ -829,10 +879,20 @@ # # NOTE: For tests "commands-1.4", "object-7.3" and "xml-1.1.*". # checkForCompileOption $test_channel XML } + + # + # NOTE: Has serialization support been enabled (at compile-time)? + # + if {![info exists no(compileSerialization)]} then { + # + # NOTE: For test "interp-1.10". + # + checkForCompileOption $test_channel SERIALIZATION + } # # NOTE: Has dedicated test support been enabled (at compile-time)? # if {![info exists no(compileTest)]} then { @@ -902,20 +962,38 @@ checkForTclReady $test_channel } if {![info exists no(tclShell)]} then { # - # NOTE: For test "garuda-1.0". + # NOTE: For test "garuda-1.1". # checkForTclShell $test_channel } + + if {![info exists no(tkPackage)]} then { + # + # NOTE: For test "tclLoad-1.1". + # + checkForTkPackage $test_channel + } } # # NOTE: Has custom test method support been disabled? # if {![info exists no(test)]} then { + # + # NOTE: Has DateTime testing support been disabled? + # + if {![info exists no(testDateTime)]} then { + # + # NOTE: For test "vwait-1.11". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestSetDateTimeNowCallback* + } + # # NOTE: Has remoting testing support been disabled? # if {![info exists no(testRemoting)]} then { # @@ -996,14 +1074,77 @@ # checkForObjectMember $test_channel Eagle._Tests.Default+Disposable \ *ToString* Eagle._Tests.Default.Disposable.ToString } + # + # NOTE: Has linked variable testing support been disabled? + # + if {![info exists no(testLinkedVariables)]} then { + # + # NOTE: For tests "basic-1.39", "basic-1.40", "basic-1.41", + # "basic-1.42", and "basic-1.43". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestSetVariableLinks* + + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestUnsetVariableLinks* + } + + # + # NOTE: Has field testing support been disabled? + # + if {![info exists no(testFields)]} then { + # + # NOTE: For tests "basic-1.39", "basic-1.40", "basic-1.41", + # "basic-1.42", and "basic-1.43". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *privateField* + + checkForObjectMember $test_channel Eagle._Tests.Default \ + *objectField* + + checkForObjectMember $test_channel Eagle._Tests.Default \ + *intField* + } + + # + # NOTE: Has property testing support been disabled? + # + if {![info exists no(testProperties)]} then { + # + # NOTE: For tests "basic-1.39", "basic-1.40", "basic-1.41", + # "basic-1.42", and "basic-1.43". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *get_SimpleProperty* + + # + # NOTE: For test "object-3.1". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *get_Item* + + checkForObjectMember $test_channel Eagle._Tests.Default \ + *set_Item* + } + # # NOTE: Has core marshaller testing support been disabled? # if {![info exists no(testMarshaller)]} then { + # + # NOTE: These are not currently used by any tests. + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestSaveObjects* + + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestRestoreObjects* + # # NOTE: For test "basic-1.29". # checkForObjectMember $test_channel Eagle._Tests.Default \ *TestExecuteStaticDelegates* @@ -1021,12 +1162,12 @@ *TestComplexMethod* # # NOTE: For test "object-2.3". # - checkForObjectMember $test_channel Eagle._Components.Private.ArrayOps \ - *ToHexadecimalString* + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestToHexadecimalString* checkForObjectMember $test_channel Eagle._Tests.Default \ *TestMulti2Array* checkForObjectMember $test_channel Eagle._Tests.Default \ @@ -1034,27 +1175,48 @@ checkForObjectMember $test_channel Eagle._Tests.Default \ *TestNestedArray* # - # NOTE: For test "object-3.1". - # - checkForObjectMember $test_channel Eagle._Tests.Default \ - *get_Item* - - checkForObjectMember $test_channel Eagle._Tests.Default \ - *set_Item* - - # - # NOTE: For test "object-3.6.*". + # NOTE: For tests "object-3.6" and "object-3.7". # checkForObjectMember $test_channel Eagle._Tests.Default \ *TestStringIListReturnValue* checkForObjectMember $test_channel Eagle._Tests.Default \ *TestStringIListIListIListReturnValue* + # + # NOTE: For test "object-3.8". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestByteList* + + # + # NOTE: For test "object-3.9". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestIntList* + + # + # NOTE: For test "object-3.10". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestLongList* + + # + # NOTE: For test "object-3.11". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestDerivedList* + + # + # NOTE: For tests "object-3.12" and "object-3.13". + # + checkForObjectMember $test_channel Eagle._Tests.Default \ + *TestStringIDictionaryReturnValue* + # # NOTE: For test "object-4.1". # checkForObjectMember $test_channel Eagle._Tests.Default \ *TestExpr* @@ -1165,21 +1327,21 @@ # # NOTE: Are we running in an STA thread? # if {![info exists no(staThread)]} then { # - # NOTE: For test "xaml-1.*". + # NOTE: For tests "xaml-1.*". # checkForStaThread $test_channel } # # NOTE: Has WPF testing support been disabled? # if {![info exists no(wpf)]} then { # - # NOTE: For test "xaml-1.*". + # NOTE: For tests "xaml-1.*". # checkForWindowsPresentationFoundation $test_channel } # @@ -1189,10 +1351,17 @@ # # NOTE: For tests "object-4.7", "object-4.8", and "object-4.9". # checkForPowerShell $test_channel } + + # + # NOTE: Has Visual Studio testing support been disabled? + # + if {![info exists no(visualStudio)]} then { + checkForVisualStudio $test_channel + } # # NOTE: Has WiX testing support been disabled? # if {![info exists no(wix)]} then { @@ -1255,14 +1424,18 @@ # # NOTE: Has Garuda testing support been disabled? # if {![info exists no(garudaDll)]} then { # - # NOTE: For test "garuda-1.0". + # NOTE: For test "garuda-1.1". # checkForGarudaDll $test_channel } + + ########################################################################### + ########################## END Eagle Constraints ########################## + ########################################################################### } else { # # HACK: Reset the test counts for tcltest. # set ::tcltest::numTests(Total) 0 @@ -1272,10 +1445,17 @@ # # HACK: Reset the list of failed files. # set ::tcltest::failFiles [list] + + ########################################################################### + ########################## BEGIN Tcl Constraints ########################## + ########################################################################### + + tputs $test_channel [appendArgs \ + "---- start of Tcl specific test constraints...\n"] # # NOTE: Has compile/runtime option testing support been disabled? # if {![info exists no(compileOptions)]} then { @@ -1287,173 +1467,197 @@ # NOTE: For test "tclLoad-1.16.1". # checkForCompileOption $test_channel TEST } } - } - - # - # NOTE: For test "package-1.0". - # - if {![info exists no(pkgAll.tcl)]} then { - checkForFile $test_channel [file join $base_path Package Tests all.tcl] \ - pkgAll.tcl - } - - # - # NOTE: For test "subst-1.*". - # - if {![info exists no(bad_subst.txt)]} then { - checkForFile $test_channel [file join $test_path bad_subst.txt] - } - - # - # NOTE: For test "fileIO-1.*". - # - if {![info exists no(file.dat)]} then { - checkForFile $test_channel [file join $test_path file.dat] - } - - # - # NOTE: For test "garbage-1.1". - # - if {![info exists no(garbage.txt)]} then { - checkForFile $test_channel [file join $test_path garbage.txt] - } - - # - # NOTE: For test "xaml-1.*". - # - if {![info exists no(test.png)]} then { - checkForFile $test_channel [file join $test_path test.png] - } - - # - # NOTE: For test "socket-1.2". - # - if {![info exists no(client.tcl)]} then { - checkForFile $test_channel [file join $test_path client.tcl] - } - - # - # NOTE: For test "tclLoad-1.2". - # - if {![info exists no(tcl_unload.tcl)]} then { - checkForFile $test_channel [file join $test_path tcl_unload.tcl] - } - - # - # NOTE: For test "read.eagle". - # - if {![info exists no(read.eagle)]} then { - checkForFile $test_channel [file join $test_path read.eagle] - } - - # - # NOTE: For test "read2.eagle". - # - if {![info exists no(read2.eagle)]} then { - checkForFile $test_channel [file join $test_path read2.eagle] - } - - # - # NOTE: For test "read3.eagle". - # - if {![info exists no(read3.eagle)]} then { - checkForFile $test_channel [file join $test_path read3.eagle] - } - - # - # NOTE: For test "read4.eagle". - # - if {![info exists no(read4.eagle)]} then { - checkForFile $test_channel [file join $test_path read4.eagle] - } - - # - # NOTE: For test "source.eagle". - # - if {![info exists no(source.eagle)]} then { - checkForFile $test_channel [file join $test_path source.eagle] - } - - # - # NOTE: For test "unbalanced_brace.eagle". - # - if {![info exists no(unbalanced_brace.eagle)]} then { - checkForFile $test_channel [file join $test_path unbalanced_brace.eagle] - } - - # - # NOTE: For test "unbalanced_brace2.eagle". - # - if {![info exists no(unbalanced_brace2.eagle)]} then { - checkForFile $test_channel [file join $test_path unbalanced_brace2.eagle] - } - - # - # NOTE: For test "excel-2.*". - # - if {![info exists no(test.xls)]} then { - checkForFile $test_channel [file join $test_path test.xls] - } - - # - # NOTE: For test "interp-1.10". - # - if {![info exists no(settings.xml)]} then { - checkForFile $test_channel [file join $test_path settings.xml] - } - - # - # NOTE: For test "load-1.1.*". - # - if {![info exists no(Plugin.dll)]} then { - checkForFile $test_channel [file join $lib_path Plugin1.0 Plugin.dll] - } - - # - # NOTE: For test "object-6.1". - # - if {![info exists no(Sample.exe)]} then { - checkForFile $test_channel [file join $bin_path Sample.exe] - } - - # - # NOTE: For test "object-4.8". - # - if {![info exists no(EagleCmdlets.dll)]} then { - checkForFile $test_channel [file join $bin_path EagleCmdlets.dll] - } - - # - # NOTE: For test "object-4.10". - # - if {![info exists no(EagleExtensions.dll)]} then { - checkForFile $test_channel [file join $bin_path EagleExtensions.dll] - } - - # - # NOTE: For test "object-4.10". - # - if {![info exists no(test.wxs)]} then { - checkForFile $test_channel [file join $base_path Installer Tests test.wxs] - } - - # - # NOTE: For test "sql-1.2". - # - if {![info exists no(sqlite3.dll)]} then { - checkForFile $test_channel [file join $bin_path sqlite3.dll] - } - - if {![info exists no(System.Data.SQLite.dll)]} then { - checkForFile $test_channel [file join $bin_path System.Data.SQLite.dll] - } - - if {![info exists no(test.sqlite3)]} then { - checkForFile $test_channel [file join $test_path test.sqlite3] + + ########################################################################### + ########################### END Tcl Constraints ########################### + ########################################################################### + } + + ############################################################################# + ####################### BEGIN Tcl & Eagle Constraints ####################### + ############################################################################# + + tputs $test_channel [appendArgs \ + "---- start of common (Tcl & Eagle) test constraints...\n"] + + # + # NOTE: Has checking for the extra files needed by various tests been + # disabled? + # + if {![info exists no(checkForFile)]} then { + # + # NOTE: For test "package-1.0". + # + if {![info exists no(pkgAll.tcl)]} then { + checkForFile $test_channel [file join $base_path Package Tests all.tcl] \ + pkgAll.tcl + } + + # + # NOTE: For tests "subst-1.*". + # + if {![info exists no(bad_subst.txt)]} then { + checkForFile $test_channel [file join $test_path bad_subst.txt] + } + + # + # NOTE: For tests "fileIO-1.*". + # + if {![info exists no(file.dat)]} then { + checkForFile $test_channel [file join $test_path file.dat] + } + + # + # NOTE: For test "garbage-1.1". + # + if {![info exists no(garbage.txt)]} then { + checkForFile $test_channel [file join $test_path garbage.txt] + } + + # + # NOTE: For tests "xaml-1.*". + # + if {![info exists no(test.png)]} then { + checkForFile $test_channel [file join $test_path test.png] + } + + # + # NOTE: For test "socket-1.2". + # + if {![info exists no(client.tcl)]} then { + checkForFile $test_channel [file join $test_path client.tcl] + } + + # + # NOTE: For test "tclLoad-1.2". + # + if {![info exists no(tcl_unload.tcl)]} then { + checkForFile $test_channel [file join $test_path tcl_unload.tcl] + } + + # + # NOTE: For test "basic-1.4". + # + if {![info exists no(read.eagle)]} then { + checkForFile $test_channel [file join $test_path read.eagle] + } + + # + # NOTE: For test "basic-1.5". + # + if {![info exists no(read2.eagle)]} then { + checkForFile $test_channel [file join $test_path read2.eagle] + } + + # + # NOTE: For test "basic-1.6". + # + if {![info exists no(read3.eagle)]} then { + checkForFile $test_channel [file join $test_path read3.eagle] + } + + # + # NOTE: For test "basic-1.7". + # + if {![info exists no(read4.eagle)]} then { + checkForFile $test_channel [file join $test_path read4.eagle] + } + + # + # NOTE: For test "infoScript-1.1". + # + if {![info exists no(script.eagle)]} then { + checkForFile $test_channel [file join $test_path script.eagle] + } + + # + # NOTE: For test "basic-1.1". + # + if {![info exists no(source.eagle)]} then { + checkForFile $test_channel [file join $test_path source.eagle] + } + + # + # NOTE: For test "basic-1.2". + # + if {![info exists no(unbalanced_brace.eagle)]} then { + checkForFile $test_channel [file join $test_path unbalanced_brace.eagle] + } + + # + # NOTE: For test "basic-1.3". + # + if {![info exists no(unbalanced_brace2.eagle)]} then { + checkForFile $test_channel [file join $test_path unbalanced_brace2.eagle] + } + + # + # NOTE: For tests "excel-2.*". + # + if {![info exists no(test.xls)]} then { + checkForFile $test_channel [file join $test_path test.xls] + } + + # + # NOTE: For test "interp-1.10". + # + if {![info exists no(settings.xml)]} then { + checkForFile $test_channel [file join $test_path settings.xml] + } + + # + # NOTE: For tests "load-1.1.*". + # + if {![info exists no(Plugin.dll)]} then { + checkForFile $test_channel [file join $lib_path Plugin1.0 Plugin.dll] + } + + # + # NOTE: For test "object-6.1". + # + if {![info exists no(Sample.exe)]} then { + checkForFile $test_channel [file join $bin_path Sample.exe] + } + + # + # NOTE: For test "object-4.8". + # + if {![info exists no(EagleCmdlets.dll)]} then { + checkForFile $test_channel [file join $bin_path EagleCmdlets.dll] + } + + # + # NOTE: For test "object-4.10". + # + if {![info exists no(EagleExtensions.dll)]} then { + checkForFile $test_channel [file join $bin_path EagleExtensions.dll] + } + + # + # NOTE: For test "object-4.10". + # + if {![info exists no(test.wxs)]} then { + checkForFile $test_channel [file join $base_path Installer Tests test.wxs] + } + + # + # NOTE: For test "sql-1.2". + # + if {![info exists no(sqlite3.dll)]} then { + checkForFile $test_channel [file join $bin_path sqlite3.dll] + } + + if {![info exists no(System.Data.SQLite.dll)]} then { + checkForFile $test_channel [file join $bin_path System.Data.SQLite.dll] + } + + if {![info exists no(test.sqlite3)]} then { + checkForFile $test_channel [file join $test_path test.sqlite3] + } } # # NOTE: Check the core test constraints unless they have been # explicitly disabled. @@ -1471,10 +1675,14 @@ } if {![info exists no(noLogFile)]} then { checkForLogFile $test_channel } + + if {![info exists no(symbols)]} then { + checkForSymbols $test_channel [info nameofexecutable] + } if {![info exists no(garuda)]} then { checkForGaruda $test_channel } @@ -1553,15 +1761,22 @@ if {![info exists no(performance)]} then { checkForPerformance $test_channel } # - # NOTE: Have precision timing tests been disabled? + # NOTE: Have precise timing tests been disabled? # if {![info exists no(timing)]} then { checkForTiming $test_channel 50; # 1/20th second. } + + # + # NOTE: Have very precise timing tests been disabled? + # + if {![info exists no(preciseTiming)]} then { + checkForTiming $test_channel 25 preciseTiming; # 1/40th second. + } # # NOTE: Has interactive testing been disabled? # if {![info exists no(interactive)]} then { @@ -1577,10 +1792,14 @@ # the Eagle distribution site). # if {![info exists no(network)]} then { checkForNetwork $test_channel $test_host $test_timeout } + + ############################################################################# + ######################## END Eagle & Tcl Constraints ######################## + ############################################################################# # # NOTE: For Eagle, dump the platform information, including # the compile options. # @@ -1611,11 +1830,11 @@ if {![info exists test_cops]} then { set test_cops [calculateBogoCops] } - tputs $test_channel [appendArgs $test_cops \n] + tputs $test_channel [appendArgs [formatDecimal $test_cops] \n] set percent [expr {[calculateRelativePerformance iterations 1] * 100}] tputs $test_channel [appendArgs \ "---- current BogoCops (commands-per-second) is " [formatDecimal \ @@ -1639,11 +1858,11 @@ # # HACK: We need something to go into the log file. # set timeStamp [lindex $timeStamp 0] } else { - set timeStamp [clock format [clock scan $timeStamp] -iso] + set timeStamp [clock format [clock scan $timeStamp] -iso -isotimezone] } } else { set timeStamp } @@ -1654,10 +1873,12 @@ [list [getPlatformInfo release ]] " " \ [list [getPlatformInfo text ]] " " \ [list [getPlatformInfo configuration ]] " " \ [list [getPlatformInfo suffix ]] " " \ [list $timeStamp] \n] + + unset timeStamp tputs $test_channel [appendArgs "---- os: " \ [getPlatformInfo os ] \n] tputs $test_channel [appendArgs "---- globalAssemblyCache: " \ @@ -1672,12 +1893,10 @@ tputs $test_channel [appendArgs "---- strongName: " \ [getPlatformInfo strongName ] \n] tputs $test_channel [appendArgs "---- certificate: " \ [getPlatformInfo certificate ] \n] - - unset timeStamp } # # NOTE: Show the current test file name, if any. # ADDED Externals/HtmlHelp/HelpDocs.zip Index: Externals/HtmlHelp/HelpDocs.zip ================================================================== --- /dev/null +++ Externals/HtmlHelp/HelpDocs.zip cannot compute difference between binary files ADDED Externals/HtmlHelp/htmlhelp.exe Index: Externals/HtmlHelp/htmlhelp.exe ================================================================== --- /dev/null +++ Externals/HtmlHelp/htmlhelp.exe cannot compute difference between binary files ADDED Externals/HtmlHelp/htmlhelpj.exe Index: Externals/HtmlHelp/htmlhelpj.exe ================================================================== --- /dev/null +++ Externals/HtmlHelp/htmlhelpj.exe cannot compute difference between binary files Index: Externals/MSVCPP/vcredist_x64_2008_SP1.exe ================================================================== --- Externals/MSVCPP/vcredist_x64_2008_SP1.exe +++ Externals/MSVCPP/vcredist_x64_2008_SP1.exe cannot compute difference between binary files Index: Externals/MSVCPP/vcredist_x64_2010_SP1.exe ================================================================== --- Externals/MSVCPP/vcredist_x64_2010_SP1.exe +++ Externals/MSVCPP/vcredist_x64_2010_SP1.exe cannot compute difference between binary files Index: Externals/MSVCPP/vcredist_x86_2008_SP1.exe ================================================================== --- Externals/MSVCPP/vcredist_x86_2008_SP1.exe +++ Externals/MSVCPP/vcredist_x86_2008_SP1.exe cannot compute difference between binary files Index: Externals/MSVCPP/vcredist_x86_2010_SP1.exe ================================================================== --- Externals/MSVCPP/vcredist_x86_2010_SP1.exe +++ Externals/MSVCPP/vcredist_x86_2010_SP1.exe cannot compute difference between binary files ADDED Externals/NDoc3/NDoc3 Beta 4.msi Index: Externals/NDoc3/NDoc3 Beta 4.msi ================================================================== --- /dev/null +++ Externals/NDoc3/NDoc3 Beta 4.msi cannot compute difference between binary files Index: Membership/Properties/AssemblyInfo.cs ================================================================== --- Membership/Properties/AssemblyInfo.cs +++ Membership/Properties/AssemblyInfo.cs @@ -31,7 +31,7 @@ // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] Index: SQLite.Designer/AssemblyInfo.cs ================================================================== --- SQLite.Designer/AssemblyInfo.cs +++ SQLite.Designer/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.ConstrainedExecution; using System.Resources; @@ -34,7 +41,7 @@ // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] Index: SQLite.Designer/ChangePasswordDialog.Designer.cs ================================================================== --- SQLite.Designer/ChangePasswordDialog.Designer.cs +++ SQLite.Designer/ChangePasswordDialog.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace SQLite.Designer { partial class ChangePasswordDialog { /// Index: SQLite.Designer/Design/ForeignKey.cs ================================================================== --- SQLite.Designer/Design/ForeignKey.cs +++ SQLite.Designer/Design/ForeignKey.cs @@ -281,11 +281,13 @@ } [DefaultProperty("From")] internal class ForeignKey : IHaveConnection, ICloneable { - internal Table _table; + internal Table _table; + internal int _id; + internal int _ordinal; internal ForeignKeyFromItem _from; internal ForeignKeyToItem _to; internal string _name; internal string _onUpdate; internal string _onDelete; @@ -292,11 +294,13 @@ internal string _match; private bool _dirty; private ForeignKey(ForeignKey source) { - _table = source._table; + _table = source._table; + _id = source._id; + _ordinal = source._ordinal; _from = new ForeignKeyFromItem(this, source._from.Column); _to = new ForeignKeyToItem(this, source._to.Catalog, source._to.Table, source._to.Column); _name = source._name; _onUpdate = source._onUpdate; _onDelete = source._onDelete; @@ -322,20 +326,24 @@ internal ForeignKey(DbConnection cnn, Table table, DataRow row) { _table = table; if (row != null) - { + { + _id = (int)row["FKEY_ID"]; + _ordinal = (int)row["FKEY_FROM_ORDINAL_POSITION"]; _from = new ForeignKeyFromItem(this, row["FKEY_FROM_COLUMN"].ToString()); _to = new ForeignKeyToItem(this, row["FKEY_TO_CATALOG"].ToString(), row["FKEY_TO_TABLE"].ToString(), row["FKEY_TO_COLUMN"].ToString()); _name = row["CONSTRAINT_NAME"].ToString(); _onUpdate = row["FKEY_ON_UPDATE"].ToString(); _onDelete = row["FKEY_ON_DELETE"].ToString(); _match = row["FKEY_MATCH"].ToString(); } else - { + { + _id = -1; + _ordinal = -1; _from = new ForeignKeyFromItem(this, ""); _to = new ForeignKeyToItem(this, _table.Catalog, "", ""); } } @@ -355,11 +363,11 @@ { get { if (String.IsNullOrEmpty(_name) == false) return _name; - return String.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}_{2}_{3}", _from.Table, _from.Column, _to.Table, _to.Column); + return String.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}_{2}", _from.Table, _id, _ordinal); } set { if (_name != value) { @@ -380,11 +388,27 @@ public DbConnection GetConnection() { return ((IHaveConnection)_table).GetConnection(); } - #endregion + #endregion + + [DisplayName("Id")] + [Category("Id")] + [Description("The identifier of this foreign key.")] + public int Id + { + get { return _id; } + } + + [DisplayName("Ordinal")] + [Category("Ordinal")] + [Description("The column ordinal of this foreign key.")] + public int Ordinal + { + get { return _ordinal; } + } [DisplayName("From Key")] [Category("From")] [Description("The source column in the current table that refers to the foreign key.")] public ForeignKeyFromItem From Index: SQLite.Designer/Design/Table.cs ================================================================== --- SQLite.Designer/Design/Table.cs +++ SQLite.Designer/Design/Table.cs @@ -406,39 +406,15 @@ } } builder.Append(separator); builder.AppendFormat("CONSTRAINT [CK_{0}_{1}] CHECK {2}", Name, n + 1, check); } - - List keys = new List(); - - for (int x = 0; x < ForeignKeys.Count; x++) - { - ForeignKey key = ForeignKeys[x]; - - if (String.IsNullOrEmpty(key.From.Column) == true || String.IsNullOrEmpty(key.From.Catalog) == true || - String.IsNullOrEmpty(key.To.Table) == true || String.IsNullOrEmpty(key.To.Column) == true) - continue; - - if (keys.Count > 0) - { - if (keys[0].Name == key.Name && keys[0].To.Catalog == key.To.Catalog && keys[0].To.Table == key.To.Table) - { - keys.Add(key); - continue; - } - builder.Append(separator); - WriteFKeys(keys, builder); - keys.Clear(); - } - keys.Add(key); - } - - if (keys.Count > 0) - { - builder.Append(separator); - WriteFKeys(keys, builder); + + if (ForeignKeys.Count > 0) + { + builder.Append(separator); + WriteFKeys(ForeignKeys, builder); } builder.Append("\r\n);\r\n"); // Rebuilding an existing table @@ -494,41 +470,66 @@ } builder.AppendLine(); } return builder.ToString(); - } - - private void WriteFKeys(List keys, StringBuilder builder) - { - builder.AppendFormat("CONSTRAINT [{0}] FOREIGN KEY (", keys[0].Name); - string separator = ""; - - foreach (ForeignKey key in keys) - { - builder.AppendFormat("{0}[{1}]", separator, key.From.Column); - separator = ", "; - } - - builder.AppendFormat(") REFERENCES [{0}] (", keys[0].To.Table); - - separator = ""; - foreach (ForeignKey key in keys) - { - builder.AppendFormat("{0}[{1}]", separator, key.To.Column); - separator = ", "; - } - builder.Append(")"); - - if (!String.IsNullOrEmpty(keys[0].Match)) - builder.AppendFormat(" MATCH {0}", keys[0].Match); - - if (!String.IsNullOrEmpty(keys[0].OnUpdate)) - builder.AppendFormat(" ON UPDATE {0}", keys[0].OnUpdate); - - if (!String.IsNullOrEmpty(keys[0].OnDelete)) - builder.AppendFormat(" ON DELETE {0}", keys[0].OnDelete); + } + + private void WriteFKeys(List keys, StringBuilder builder) + { + for (int index = 0; index < keys.Count; ) + { + ForeignKey key = keys[index]; + + if (index > 0) + builder.Append(",\r\n "); + + builder.AppendFormat( + "CONSTRAINT [{0}] FOREIGN KEY (", key.Name); + + int startIndex = index; + + do + { + builder.AppendFormat("{0}[{1}]", + index > startIndex ? ", " : String.Empty, + keys[index].From.Column); + + index++; + } while (index < keys.Count && keys[index].Id == key.Id); + + builder.AppendFormat(") REFERENCES [{0}]", key.To.Table); + + if (!String.IsNullOrEmpty(key.To.Column)) + { + builder.Append(" ("); + index = startIndex; + + do + { + builder.AppendFormat("{0}[{1}]", + index > startIndex ? ", " : String.Empty, + keys[index].To.Column); + + index++; + } while (index < keys.Count && keys[index].Id == key.Id); + + builder.Append(')'); + } + + if (!String.IsNullOrEmpty(key.Match)) + builder.AppendFormat(" MATCH {0}", key.Match); + + if (!String.IsNullOrEmpty(key.OnUpdate)) + builder.AppendFormat(" ON UPDATE {0}", key.OnUpdate); + + if (!String.IsNullOrEmpty(key.OnDelete)) + builder.AppendFormat(" ON DELETE {0}", key.OnDelete); + + if (index == startIndex) + index++; + } } [Browsable(false)] public override ViewTableBase DesignTable { Index: SQLite.Designer/Editors/TableDesignerDoc.Designer.cs ================================================================== --- SQLite.Designer/Editors/TableDesignerDoc.Designer.cs +++ SQLite.Designer/Editors/TableDesignerDoc.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace SQLite.Designer.Editors { partial class TableDesignerDoc { /// Index: SQLite.Designer/Editors/ViewDesignerDoc.Designer.cs ================================================================== --- SQLite.Designer/Editors/ViewDesignerDoc.Designer.cs +++ SQLite.Designer/Editors/ViewDesignerDoc.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace SQLite.Designer.Editors { partial class ViewDesignerDoc { /// Index: SQLite.Designer/SQLite.Designer.2008.csproj ================================================================== --- SQLite.Designer/SQLite.Designer.2008.csproj +++ SQLite.Designer/SQLite.Designer.2008.csproj @@ -46,35 +46,37 @@ - - False - - - False - - - False - - - False - - - False - - - False - - - False - - - False - - + + False + + + False + False + + + False + + + False + False + + + False + + + False + + + False + + + False + + False @@ -150,13 +152,11 @@ 1000 - - ResXFileCodeGenerator VSPackage.Designer.cs true Designer @@ -200,6 +200,6 @@ --> - + Index: SQLite.Designer/SQLite.Designer.2010.csproj ================================================================== --- SQLite.Designer/SQLite.Designer.2010.csproj +++ SQLite.Designer/SQLite.Designer.2010.csproj @@ -45,35 +45,37 @@ - - False - - - False - - - False - - - False - - - False - - - False - - - False - - - False - - + + False + + + False + False + + + False + + + False + False + + + False + + + False + + + False + + + False + + False @@ -149,12 +151,10 @@ 1000 - - ResXFileCodeGenerator VSPackage.Designer.cs true Index: SQLite.Designer/SQLiteConnectionUIControl.Designer.cs ================================================================== --- SQLite.Designer/SQLiteConnectionUIControl.Designer.cs +++ SQLite.Designer/SQLiteConnectionUIControl.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace SQLite.Designer { partial class SQLiteConnectionUIControl { /// DELETED SQLite.Designer/SQLiteDataViewSupport2005.xml Index: SQLite.Designer/SQLiteDataViewSupport2005.xml ================================================================== --- SQLite.Designer/SQLiteDataViewSupport2005.xml +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - - - - SQLite - - - - SQLite [{Root.Server}] - - - - - - - - - - Tables - - - - - - - - - - - - - - - - - - System Tables - - - - - - - - - - - - - - - - - Views - - - - - - - - {View.Name} - - - - - - - - - - - - - - - - - Columns - - - - - - - - - - - - - - - - - - - Indexes - - - - - - - - - - - - - - - - Foreign Keys - - - - - - - - - - - - - - Triggers - - - - - - - - - - - - Columns - - - - - - - - - - - - - - Triggers - - - - - - - - - - - - - - - - - - - Catalog - - - - - - - - - - - - - - - - - - - - - - - - Catalog - - - - - - Updatable - - - - - - - - - - - - - - - - - - - - - - - Catalog - - - - - - - - - - - Data Type - - - Allow Nulls - - - Default Value - - - - - - - - - - - - - - Catalog - - - - - - - - - Is Unique - - - Primary Key - - - - - - - - - - - - - - - - - Catalog - - - - - - - - - - - - - - - Catalog - - - - - - - - - - - Data Type - - - Allow Nulls - - - Default Value - - - Primary Key - - - - - - - - - - - - - - Catalog - - - - - - - - - - Referenced Database - - - - Referenced Table - - - - - - - - - - - - (Identity) - (Location) - (Source) - References - - Index: SQLite.Designer/TableNameDialog.Designer.cs ================================================================== --- SQLite.Designer/TableNameDialog.Designer.cs +++ SQLite.Designer/TableNameDialog.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace SQLite.Designer { partial class TableNameDialog { /// DELETED SQLite.Designer/VSDesign/Microsoft.Data.ConnectionUI.dll Index: SQLite.Designer/VSDesign/Microsoft.Data.ConnectionUI.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.Data.ConnectionUI.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/Microsoft.VisualStudio.CommandBars.dll Index: SQLite.Designer/VSDesign/Microsoft.VisualStudio.CommandBars.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.VisualStudio.CommandBars.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/Microsoft.VisualStudio.Data.dll Index: SQLite.Designer/VSDesign/Microsoft.VisualStudio.Data.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.VisualStudio.Data.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/Microsoft.VisualStudio.OLE.Interop.dll Index: SQLite.Designer/VSDesign/Microsoft.VisualStudio.OLE.Interop.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.VisualStudio.OLE.Interop.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.Interop.8.0.dll Index: SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.Interop.8.0.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.Interop.8.0.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.Interop.dll Index: SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.Interop.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.Interop.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.dll Index: SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.dll ================================================================== --- SQLite.Designer/VSDesign/Microsoft.VisualStudio.Shell.dll +++ /dev/null cannot compute difference between binary files DELETED SQLite.Designer/VSDesign/envdte.dll Index: SQLite.Designer/VSDesign/envdte.dll ================================================================== --- SQLite.Designer/VSDesign/envdte.dll +++ /dev/null cannot compute difference between binary files Index: SQLite.Designer/VSPackage.Designer.cs ================================================================== --- SQLite.Designer/VSPackage.Designer.cs +++ SQLite.Designer/VSPackage.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // Index: SQLite.Designer/source.extension.vsixmanifest ================================================================== --- SQLite.Designer/source.extension.vsixmanifest +++ SQLite.Designer/source.extension.vsixmanifest @@ -1,11 +1,11 @@ SQLite Designer Robert Simpson - 1.0.38.1 + 1.0.78.0 ADO.NET Data Designer for SQLite 1033 false Index: SQLite.Interop/SQLite.Interop.2008.vcproj ================================================================== --- SQLite.Interop/SQLite.Interop.2008.vcproj +++ SQLite.Interop/SQLite.Interop.2008.vcproj @@ -305,11 +305,11 @@ Index: SQLite.Interop/SQLite.Interop.2010.vcxproj ================================================================== --- SQLite.Interop/SQLite.Interop.2010.vcxproj +++ SQLite.Interop/SQLite.Interop.2010.vcxproj @@ -52,14 +52,15 @@ false $(INTEROP_MIXED_NAME) Index: SQLite.Interop/SQLite.Interop.CE.2008.vcproj ================================================================== --- SQLite.Interop/SQLite.Interop.CE.2008.vcproj +++ SQLite.Interop/SQLite.Interop.CE.2008.vcproj @@ -73,11 +73,11 @@ Index: SQLite.Interop/SQLite.Interop.Static.2010.vcxproj ================================================================== --- SQLite.Interop/SQLite.Interop.Static.2010.vcxproj +++ SQLite.Interop/SQLite.Interop.Static.2010.vcxproj @@ -52,14 +52,15 @@ false $(INTEROP_MIXED_NAME) Index: SQLite.Interop/props/SQLite.Interop.props ================================================================== --- SQLite.Interop/props/SQLite.Interop.props +++ SQLite.Interop/props/SQLite.Interop.props @@ -8,13 +8,13 @@ * --> 2010 - 076 - 1.0.76.0 - 1,0,76,0 + 078 + 1.0.78.0 + 1,0,78,0 /ASSEMBLYRESOURCE:..\System.Data.SQLite\SQLiteCommand.bmp,System.Data.SQLite.SQLiteCommand.bmp /ASSEMBLYRESOURCE:..\System.Data.SQLite\SQLiteConnection.bmp,System.Data.SQLite.SQLiteConnection.bmp /ASSEMBLYRESOURCE:..\System.Data.SQLite\SQLiteDataAdapter.bmp,System.Data.SQLite.SQLiteDataAdapter.bmp $(ProjectDir)..\System.Data.SQLite\System.Data.SQLite.snk SQLite.Interop System.Data.SQLite Index: SQLite.Interop/props/SQLite.Interop.vsprops ================================================================== --- SQLite.Interop/props/SQLite.Interop.vsprops +++ SQLite.Interop/props/SQLite.Interop.vsprops @@ -17,21 +17,21 @@ Value="2008" PerformEnvironmentSet="true" /> - 3.7.8 - 3,7,8 - SQLITE_THREADSAFE=1;SQLITE_ENABLE_COLUMN_METADATA=1;SQLITE_ENABLE_STAT2=1;SQLITE_ENABLE_FTS3=1;SQLITE_ENABLE_LOAD_EXTENSION=1;SQLITE_ENABLE_RTREE=1;SQLITE_SOUNDEX=1 + 3.7.10.0 + 3,7,10,0 + SQLITE_THREADSAFE=1;SQLITE_ENABLE_COLUMN_METADATA=1;SQLITE_ENABLE_STAT3=1;SQLITE_ENABLE_FTS3=1;SQLITE_ENABLE_LOAD_EXTENSION=1;SQLITE_ENABLE_RTREE=1;SQLITE_SOUNDEX=1 SQLITE_HAS_CODEC=1 SQLITE_OMIT_WAL=1 SQLITE_DEBUG=1;SQLITE_MEMDEBUG=1 SQLITE_WIN32_MALLOC=1 - 4018;4055;4057;4090;4100;4127;4132;4146;4152;4210;4244;4245;4389;4701;4706;4996 - 4267;4306 + 4018;4055;4057;4090;4100;4127;4132;4146;4152;4210;4232;4244;4245;4389;4701;4706;4996 + 4232;4267;4306 <_ProjectFileVersion>10.0.30319.1 Index: SQLite.Interop/props/sqlite3.vsprops ================================================================== --- SQLite.Interop/props/sqlite3.vsprops +++ SQLite.Interop/props/sqlite3.vsprops @@ -12,21 +12,21 @@ Version="8.00" Name="sqlite3" > Index: SQLite.Interop/src/core/sqlite3.c ================================================================== --- SQLite.Interop/src/core/sqlite3.c +++ SQLite.Interop/src/core/sqlite3.c @@ -1,8 +1,8 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.7.8. By combining all the individual C code files into this +** version 3.7.10. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. @@ -314,17 +314,10 @@ #endif #ifdef HAVE_INTTYPES_H #include #endif -/* -** The number of samples of an index that SQLite takes in order to -** construct a histogram of the table content when running ANALYZE -** and with SQLITE_ENABLE_STAT2 -*/ -#define SQLITE_INDEX_SAMPLES 10 - /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. @@ -370,10 +363,18 @@ #else # define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ #endif #endif +/* +** Powersafe overwrite is on by default. But can be turned off using +** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option. +*/ +#ifndef SQLITE_POWERSAFE_OVERWRITE +# define SQLITE_POWERSAFE_OVERWRITE 1 +#endif + /* ** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. ** It determines whether or not the features related to ** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can ** be overridden at runtime using the sqlite3_config() API. @@ -394,11 +395,11 @@ ** assert() macro is enabled, each call into the Win32 native heap subsystem ** will cause HeapValidate to be called. If heap validation should fail, an ** assertion will be triggered. ** ** (Historical note: There used to be several other options, but we've -** pared it down to just these two.) +** pared it down to just these three.) ** ** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as ** the default. */ #if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)>1 @@ -654,13 +655,13 @@ ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.7.8" -#define SQLITE_VERSION_NUMBER 3007008 -#define SQLITE_SOURCE_ID "2011-09-19 14:49:19 3e0da808d2f5b4d12046e05980ca04578f581177" +#define SQLITE_VERSION "3.7.10" +#define SQLITE_VERSION_NUMBER 3007010 +#define SQLITE_SOURCE_ID "2012-01-16 13:28:40 ebd01a8deffb5024a5d7494eef800d2366d97204" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** @@ -724,11 +725,11 @@ /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if -** SQLite was compiled mutexing code omitted due to the +** SQLite was compiled with mutexing code omitted due to the ** [SQLITE_THREADSAFE] compile-time option being set to 0. ** ** SQLite can be compiled with or without mutexes. When ** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes ** are enabled and SQLite is threadsafe. When the @@ -918,11 +919,11 @@ ** CAPI3REF: Result Codes ** KEYWORDS: SQLITE_OK {error code} {error codes} ** KEYWORDS: {result code} {result codes} ** ** Many SQLite functions return an integer result code from the set shown -** here in order to indicates success or failure. +** here in order to indicate success or failure. ** ** New error codes may be added in future versions of SQLite. ** ** See also: [SQLITE_IOERR_READ | extended result codes], ** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. @@ -1056,11 +1057,15 @@ ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means ** that when data is appended to a file, the data is appended ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls -** to xWrite(). +** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that +** after reboot following a crash or power loss, the only bytes in a +** file that were written at the application level might have changed +** and that adjacent bytes, even bytes within the same sector are +** guaranteed to be unchanged. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 @@ -1070,10 +1075,11 @@ #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 +#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second @@ -1291,16 +1297,16 @@ ** opcode as doing so may disrupt the operation of the specialized VFSes ** that do require it. ** ** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic ** retry counts and intervals for certain disk I/O operations for the -** windows [VFS] in order to work to provide robustness against +** windows [VFS] in order to provide robustness in the presence of ** anti-virus programs. By default, the windows VFS will retry file read, -** file write, and file delete opertions up to 10 times, with a delay +** file write, and file delete operations up to 10 times, with a delay ** of 25 milliseconds before the first retry and with the delay increasing ** by an additional 25 milliseconds with each subsequent retry. This -** opcode allows those to values (10 retries and 25 milliseconds of delay) +** opcode allows these two values (10 retries and 25 milliseconds of delay) ** to be adjusted. The values are changed for all database connections ** within the same process. The argument is a pointer to an array of two ** integers where the first integer i the new retry count and the second ** integer is the delay. If either integer is negative, then the setting ** is not changed but instead the prior value of that setting is written @@ -1318,22 +1324,49 @@ ** in order for the database to be readable. The fourth parameter to ** [sqlite3_file_control()] for this opcode should be a pointer to an integer. ** That integer is 0 to disable persistent WAL mode or 1 to enable persistent ** WAL mode. If the integer is -1, then it is overwritten with the current ** WAL persistence setting. -** +** +** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the +** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting +** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the +** xDeviceCharacteristics methods. The fourth parameter to +** [sqlite3_file_control()] for this opcode should be a pointer to an integer. +** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage +** mode. If the integer is -1, then it is overwritten with the current +** zero-damage mode setting. +** +** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening +** a write transaction to indicate that, unless it is rolled back for some +** reason, the entire database file will be overwritten by the current +** transaction. This is used by VACUUM operations. +** +** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of +** all [VFSes] in the VFS stack. The names are of all VFS shims and the +** final bottom-level VFS are written into memory obtained from +** [sqlite3_malloc()] and the result is stored in the char* variable +** that the fourth parameter of [sqlite3_file_control()] points to. +** The caller is responsible for freeing the memory when done. As with +** all file-control actions, there is no guarantee that this will actually +** do anything. Callers should initialize the char* variable to a NULL +** pointer in case this file-control is not implemented. This file-control +** is intended for diagnostic use only. */ -#define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 -#define SQLITE_FCNTL_SIZE_HINT 5 -#define SQLITE_FCNTL_CHUNK_SIZE 6 -#define SQLITE_FCNTL_FILE_POINTER 7 -#define SQLITE_FCNTL_SYNC_OMITTED 8 -#define SQLITE_FCNTL_WIN32_AV_RETRY 9 -#define SQLITE_FCNTL_PERSIST_WAL 10 +#define SQLITE_FCNTL_LOCKSTATE 1 +#define SQLITE_GET_LOCKPROXYFILE 2 +#define SQLITE_SET_LOCKPROXYFILE 3 +#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_SIZE_HINT 5 +#define SQLITE_FCNTL_CHUNK_SIZE 6 +#define SQLITE_FCNTL_FILE_POINTER 7 +#define SQLITE_FCNTL_SYNC_OMITTED 8 +#define SQLITE_FCNTL_WIN32_AV_RETRY 9 +#define SQLITE_FCNTL_PERSIST_WAL 10 +#define SQLITE_FCNTL_OVERWRITE 11 +#define SQLITE_FCNTL_VFSNAME 12 +#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an @@ -1384,11 +1417,11 @@ ** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname() with an optional suffix added. ** ^If a suffix is added to the zFilename parameter, it will ** consist of a single "-" character followed by no more than -** 10 alphanumeric and/or "-" characters. +** 11 alphanumeric and/or "-" characters. ** ^SQLite further guarantees that ** the string will be valid and unchanged until xClose() is ** called. Because of the previous sentence, ** the [sqlite3_file] can safely store a pointer to the ** filename if it needs to remember the filename for some reason. @@ -1915,11 +1948,11 @@ ** ** [[SQLITE_CONFIG_PAGECACHE]]
SQLITE_CONFIG_PAGECACHE
**
^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implementation. ** This configuration should not be used if an application-define page -** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. +** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each ** page header. ^The page header size is 20 to 40 bytes depending on @@ -1946,12 +1979,12 @@ ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte ** boundary or subsequent behavior of SQLite will be undefined. -** The minimum allocation size is capped at 2^12. Reasonable values -** for the minimum allocation size are 2^5 through 2^8.
+** The minimum allocation size is capped at 2**12. Reasonable values +** for the minimum allocation size are 2**5 through 2**8. ** ** [[SQLITE_CONFIG_MUTEX]]
SQLITE_CONFIG_MUTEX
**
^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place @@ -1984,19 +2017,19 @@ ** slots allocated to each database connection.)^ ^(This option sets the ** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] ** verb to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^
** -** [[SQLITE_CONFIG_PCACHE]]
SQLITE_CONFIG_PCACHE
+** [[SQLITE_CONFIG_PCACHE2]]
SQLITE_CONFIG_PCACHE2
**
^(This option takes a single argument which is a pointer to -** an [sqlite3_pcache_methods] object. This object specifies the interface +** an [sqlite3_pcache_methods2] object. This object specifies the interface ** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.
** -** [[SQLITE_CONFIG_GETPCACHE]]
SQLITE_CONFIG_GETPCACHE
+** [[SQLITE_CONFIG_GETPCACHE2]]
SQLITE_CONFIG_GETPCACHE2
**
^(This option takes a single argument which is a pointer to an -** [sqlite3_pcache_methods] object. SQLite copies of the current +** [sqlite3_pcache_methods2] object. SQLite copies of the current ** page cache implementation into that object.)^
** ** [[SQLITE_CONFIG_LOG]]
SQLITE_CONFIG_LOG
**
^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), @@ -2025,10 +2058,15 @@ ** connection is opened. If it is globally disabled, filenames are ** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the ** database connection is opened. By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** [SQLITE_USE_URI] symbol defined. +** +** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] +**
SQLITE_CONFIG_PCACHE and SQLITE_CONFNIG_GETPCACHE +**
These options are obsolete and should not be used by new code. +** They are retained for backwards compatibility but are now no-ops. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ @@ -2040,14 +2078,16 @@ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ -#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that @@ -2528,11 +2568,11 @@ ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", and "%z" options. ** -** ^(The %q option works like %s in that it substitutes a null-terminated +** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. ** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into ** the string. ** @@ -3136,25 +3176,44 @@ ); /* ** CAPI3REF: Obtain Values For URI Parameters ** -** This is a utility routine, useful to VFS implementations, that checks +** These are utility routines, useful to VFS implementations, that check ** to see if a database file was a URI that contained a specific query -** parameter, and if so obtains the value of the query parameter. -** -** The zFilename argument is the filename pointer passed into the xOpen() -** method of a VFS implementation. The zParam argument is the name of the -** query parameter we seek. This routine returns the value of the zParam -** parameter if it exists. If the parameter does not exist, this routine -** returns a NULL pointer. -** -** If the zFilename argument to this function is not a pointer that SQLite -** passed into the xOpen VFS method, then the behavior of this routine -** is undefined and probably undesirable. +** parameter, and if so obtains the value of that query parameter. +** +** If F is the database filename pointer passed into the xOpen() method of +** a VFS implementation when the flags parameter to xOpen() has one or +** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and +** P is the name of the query parameter, then +** sqlite3_uri_parameter(F,P) returns the value of the P +** parameter if it exists or a NULL pointer if P does not appear as a +** query parameter on F. If P is a query parameter of F +** has no explicit value, then sqlite3_uri_parameter(F,P) returns +** a pointer to an empty string. +** +** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean +** parameter and returns true (1) or false (0) according to the value +** of P. The value of P is true if it is "yes" or "true" or "on" or +** a non-zero number and is false otherwise. If P is not a query parameter +** on F then sqlite3_uri_boolean(F,P,B) returns (B!=0). +** +** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a +** 64-bit signed integer and returns that integer, or D if P does not +** exist. If the value of P is something other than an integer, then +** zero is returned. +** +** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and +** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and +** is not a database file pathname pointer that SQLite passed into the xOpen +** VFS method, then the behavior of this routine is undefined and probably +** undesirable. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages ** @@ -3346,11 +3405,12 @@ ** zSql string ends at either the first '\000' or '\u0000' character or ** the nByte-th byte, whichever comes first. If the caller knows ** that the supplied string is nul-terminated, then there is a small ** performance advantage to be gained by passing an nByte parameter that ** is equal to the number of bytes in the input string including -** the nul-terminator bytes. +** the nul-terminator bytes as this saves SQLite from having to +** make a copy of the input string. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. @@ -3397,11 +3457,11 @@ ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column -** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled. +** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** the ** ** */ SQLITE_API int sqlite3_prepare( @@ -3471,10 +3531,29 @@ ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** +** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the +** [prepared statement] S has been stepped at least once using +** [sqlite3_step(S)] but has not run to completion and/or has not +** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) +** interface returns false if S is a NULL pointer. If S is not a +** NULL pointer and is not a pointer to a valid [prepared statement] +** object, then the behavior is undefined and probably undesirable. +** +** This interface can be used in combination [sqlite3_next_stmt()] +** to locate all prepared statements associated with a database +** connection that are in need of being reset. This can be used, +** for example, in diagnostic routines to search for prepared +** statements that are holding a transaction open. +*/ +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values @@ -3567,10 +3646,17 @@ ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the ** number of bytes in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. +** If a non-negative fourth parameter is provided to sqlite3_bind_text() +** or sqlite3_bind_text16() then that parameter must be the byte offset +** where the NUL terminator would occur assuming the string were NUL +** terminated. If any NUL characters occur at byte offsets less than +** the value of the fourth parameter then the resulting string value will +** contain embedded NULs. The result of expressions involving strings +** with embedded NULs is undefined. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called ** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), @@ -3900,10 +3986,16 @@ ** current row of the result set of [prepared statement] P. ** ^If prepared statement P does not have results ready to return ** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of ** interfaces) then sqlite3_data_count(P) returns 0. ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. +** ^The sqlite3_data_count(P) routine returns 0 if the previous call to +** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) +** will return non-zero if previous call to [sqlite3_step](P) returned +** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] +** where it always returns zero since each step of that multi-step +** pragma returns 0 columns of data. ** ** See also: [sqlite3_column_count()] */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); @@ -3999,11 +4091,11 @@ ** of the string. ^For clarity: the values returned by ** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero terminated. ^The return +** even empty strings, are always zero-terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. @@ -4579,11 +4671,16 @@ ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined -** function result. +** function result. If the 3rd parameter is non-negative, then it +** must be the byte offset into the string where the NUL terminator would +** appear if the string where NUL terminated. If any NUL characters occur +** in the string at a byte offset that is less than the value of the 3rd +** parameter, then the resulting string will contain embedded NULs and the +** result of expressions operating on strings with embedded NULs is undefined. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to @@ -4894,10 +4991,26 @@ ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +/* +** CAPI3REF: Return The Filename For A Database Connection +** +** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename +** associated with database N of connection D. ^The main database file +** has the name "main". If there is no attached database N on the database +** connection D, or if database N is a temporary or in-memory database, then +** a NULL pointer is returned. +** +** ^The filename returned by this function is the output of the +** xFullPathname method of the [VFS]. ^In other words, the filename +** will be an absolute pathname, even if the filename used +** to open the database originally was a URI or relative pathname. +*/ +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + /* ** CAPI3REF: Find the next prepared statement ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL @@ -4929,17 +5042,19 @@ ** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions ** return the P argument from the previous call of the same function ** on the same [database connection] D, or NULL for ** the first call for each function on D. ** +** The commit and rollback hook callbacks are not reentrant. ** The callback implementation must not do anything that will modify ** the database connection that invoked the callback. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the commit ** or rollback hook in the first place. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. +** Note that running any other SQL statements, including SELECT statements, +** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify +** the database connections for the meaning of "modify" in this paragraph. ** ** ^Registering a NULL function disables the callback. ** ** ^When the commit hook callback routine returns zero, the [COMMIT] ** operation is allowed to continue normally. ^If the commit hook @@ -5048,13 +5163,28 @@ ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** ^The sqlite3_release_memory() routine is a no-op returning zero ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** See also: [sqlite3_db_release_memory()] */ SQLITE_API int sqlite3_release_memory(int); +/* +** CAPI3REF: Free Memory Used By A Database Connection +** +** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap +** memory as possible from database connection D. Unlike the +** [sqlite3_release_memory()] interface, this interface is effect even +** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** omitted. +** +** See also: [sqlite3_release_memory()] +*/ +SQLITE_API int sqlite3_db_release_memory(sqlite3*); + /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. @@ -5065,11 +5195,12 @@ ** below the limit, it will exceed the limit rather than generate ** an [SQLITE_NOMEM] error. In other words, the soft heap limit ** is advisory only. ** ** ^The return value from sqlite3_soft_heap_limit64() is the size of -** the soft heap limit prior to the call. ^If the argument N is negative +** the soft heap limit prior to the call, or negative in the case of an +** error. ^If the argument N is negative ** then no change is made to the soft heap limit. Hence, the current ** size of the soft heap limit can be determined by invoking ** sqlite3_soft_heap_limit64() with a negative argument. ** ** ^If the argument N is zero then the soft heap limit is disabled. @@ -5081,11 +5212,11 @@ **
  • The soft heap limit is set to zero. **
  • Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. **
  • An alternative page cache implementation is specified using -** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). +** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). **
  • The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** )^ ** @@ -5823,19 +5954,19 @@ ** is selected automatically at compile-time. ^(The following ** implementations are available in the SQLite core: ** **
      **
    • SQLITE_MUTEX_OS2 -**
    • SQLITE_MUTEX_PTHREAD +**
    • SQLITE_MUTEX_PTHREADS **
    • SQLITE_MUTEX_W32 **
    • SQLITE_MUTEX_NOOP **
    )^ ** ** ^The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in ** a single-threaded application. ^The SQLITE_MUTEX_OS2, -** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations +** SQLITE_MUTEX_PTHREADS, and SQLITE_MUTEX_W32 implementations ** are appropriate for use on OS/2, Unix, and Windows. ** ** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex ** implementation is included with the library. In this case the @@ -6021,11 +6152,11 @@ ** defined and if NDEBUG is not defined. ** ** ^These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** -** ^The implementation is not required to provided versions of these +** ^The implementation is not required to provide versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then @@ -6149,13 +6280,13 @@ #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 -#define SQLITE_TESTCTRL_PGHDRSZ 17 -#define SQLITE_TESTCTRL_SCRATCHMALLOC 18 -#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19 +#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 #define SQLITE_TESTCTRL_LAST 19 /* ** CAPI3REF: SQLite Runtime Status ** @@ -6362,20 +6493,34 @@ **
    This parameter returns the approximate number of of bytes of heap ** and lookaside memory used by all prepared statements associated with ** the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. **
    +** +** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
    SQLITE_DBSTATUS_CACHE_HIT
    +**
    This parameter returns the number of pager cache hits that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT +** is always 0. +**
    +** +** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
    SQLITE_DBSTATUS_CACHE_MISS
    +**
    This parameter returns the number of pager cache misses that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS +** is always 0. +**
    ** */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_SCHEMA_USED 2 #define SQLITE_DBSTATUS_STMT_USED 3 #define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 -#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_CACHE_HIT 7 +#define SQLITE_DBSTATUS_CACHE_MISS 8 +#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** @@ -6425,11 +6570,10 @@ **
    ^This is the number of rows inserted into transient indices that ** were created automatically in order to help joins run faster. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance by adding permanent indices that do not ** need to be reinitialized each time the statement is run.
    -** ** */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 #define SQLITE_STMTSTATUS_AUTOINDEX 3 @@ -6441,21 +6585,37 @@ ** the pluggable module. The SQLite core has no knowledge of ** its size or internal structure and never deals with the ** sqlite3_pcache object except by holding and passing pointers ** to the object. ** -** See [sqlite3_pcache_methods] for additional information. +** See [sqlite3_pcache_methods2] for additional information. */ typedef struct sqlite3_pcache sqlite3_pcache; + +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache_page object represents a single page in the +** page cache. The page cache will allocate instances of this +** object. Various methods of the page cache use pointers to instances +** of this object as parameters or as their return value. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache_page sqlite3_pcache_page; +struct sqlite3_pcache_page { + void *pBuf; /* The content of the page */ + void *pExtra; /* Extra information associated with the page */ +}; /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can ** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods structure.)^ +** instance of the sqlite3_pcache_methods2 structure.)^ ** In many applications, most of the heap memory allocated by ** SQLite is used for the page cache. ** By implementing a ** custom page cache using this API, an application can better control ** the amount of memory consumed by SQLite, the way in which @@ -6465,20 +6625,20 @@ ** ** The alternative page cache mechanism is an ** extreme measure that is only needed by the most demanding applications. ** The built-in page cache is recommended for most uses. ** -** ^(The contents of the sqlite3_pcache_methods structure are copied to an +** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** ** [[the xInit() page cache method]] ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() -** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ +** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ ** The intent of the xInit() method is to set up global data structures ** required by the custom page cache implementation. ** ^(If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache.)^ @@ -6501,21 +6661,19 @@ ** [[the xCreate() page cache methods]] ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. ^szPage will not be a power of two. ^szPage -** will the page size of the database file that is to be cached plus an -** increment (here called "R") of less than 250. SQLite will use the -** extra R bytes on each page to store metadata about the underlying -** database page on disk. The value of R depends +** be allocated by the cache. ^szPage will always a power of two. ^The +** second parameter szExtra is a number of bytes of extra storage +** associated with each page cache entry. ^The szExtra parameter will +** a number less than 250. SQLite will use the +** extra szExtra bytes on each page to store metadata about the underlying +** database page on disk. The value passed into szExtra depends ** on the SQLite version, the target platform, and how SQLite was compiled. -** ^(R is constant for a particular build of SQLite. Except, there are two -** distinct values of R when SQLite is compiled with the proprietary -** ZIPVFS extension.)^ ^The second argument to -** xCreate(), bPurgeable, is true if the cache being created will -** be used to cache database pages of a file stored on disk, or +** ^The third argument to xCreate(), bPurgeable, is true if the cache being +** created will be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to @@ -6535,15 +6693,20 @@ ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** ** [[the xFetch() page cache methods]] ** The xFetch() method locates a page in the cache and returns a pointer to -** the page, or a NULL pointer. -** A "page", in this context, means a buffer of szPage bytes aligned at an -** 8-byte boundary. The page to be fetched is determined by the key. ^The -** minimum key value is 1. After it has been retrieved using xFetch, the page -** is considered to be "pinned". +** an sqlite3_pcache_page object associated with that page, or a NULL pointer. +** The pBuf element of the returned sqlite3_pcache_page object will be a +** pointer to a buffer of szPage bytes used to store the content of a +** single database page. The pExtra element of sqlite3_pcache_page will be +** a pointer to the szExtra bytes of extra storage that SQLite has requested +** for each entry in the page cache. +** +** The page to be fetched is determined by the key. ^The minimum key value +** is 1. After it has been retrieved using xFetch, the page is considered +** to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** cache implementation should use the value of the createFlag @@ -6592,12 +6755,41 @@ ** ** [[the xDestroy() page cache method]] ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] -** handle invalid, and will not use it with any other sqlite3_pcache_methods +** handle invalid, and will not use it with any other sqlite3_pcache_methods2 ** functions. +** +** [[the xShrink() page cache method]] +** ^SQLite invokes the xShrink() method when it wants the page cache to +** free up as much of heap memory as possible. The page cache implementation +** is not obligated to free any memory, but well-behaved implementations should +** do their best. +*/ +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; +struct sqlite3_pcache_methods2 { + int iVersion; + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); + void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, + unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + void (*xShrink)(sqlite3_pcache*); +}; + +/* +** This is the obsolete pcache_methods object that has now been replaced +** by sqlite3_pcache_methods2. This object is not used by SQLite. It is +** retained in the header file for backwards compatibility only. */ typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; struct sqlite3_pcache_methods { void *pArg; int (*xInit)(void*); @@ -6609,10 +6801,11 @@ void (*xUnpin)(sqlite3_pcache*, void*, int discard); void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); void (*xDestroy)(sqlite3_pcache*); }; + /* ** CAPI3REF: Online Backup Object ** ** The sqlite3_backup object records state information about an ongoing @@ -7613,11 +7806,11 @@ ** the default file format for new databases and the maximum file format ** that the library can read. */ #define SQLITE_MAX_FILE_FORMAT 4 #ifndef SQLITE_DEFAULT_FILE_FORMAT -# define SQLITE_DEFAULT_FILE_FORMAT 1 +# define SQLITE_DEFAULT_FILE_FORMAT 4 #endif /* ** Determine whether triggers are recursive by default. This can be ** changed at run-time using a pragma. @@ -7711,10 +7904,22 @@ ** is 0x00000000ffffffff. But because of quirks of some compilers, we ** have to specify the value in the less intuitive manner shown: */ #define SQLITE_MAX_U32 ((((u64)1)<<32)-1) +/* +** The datatype used to store estimates of the number of rows in a +** table or index. This is an unsigned integer type. For 99.9% of +** the world, a 32-bit integer is sufficient. But a 64-bit integer +** can be used at compile-time if desired. +*/ +#ifdef SQLITE_64BIT_STATS + typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ +#else + typedef u32 tRowcnt; /* 32-bit is the default */ +#endif + /* ** Macros to determine whether the machine is big or little endian, ** evaluated at runtime. */ #ifdef SQLITE_AMALGAMATION @@ -8241,10 +8446,11 @@ struct SubProgram { VdbeOp *aOp; /* Array of opcodes for sub-program */ int nOp; /* Elements in aOp[] */ int nMem; /* Number of memory cells required */ int nCsr; /* Number of cursors required */ + int nOnce; /* Number of OP_Once instructions */ void *token; /* id that may be used to recursive triggers */ SubProgram *pNext; /* Next sub-program already visited */ }; /* @@ -8487,14 +8693,14 @@ #define OPFLG_IN2 0x0008 /* in2: P2 is an input */ #define OPFLG_IN3 0x0010 /* in3: P3 is an input */ #define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ #define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,\ +/* 0 */ 0x00, 0x01, 0x01, 0x04, 0x04, 0x10, 0x00, 0x02,\ /* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x24, 0x24,\ /* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ -/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00,\ +/* 24 */ 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00,\ /* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\ /* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ /* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\ /* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\ @@ -8690,10 +8896,11 @@ /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); @@ -8742,10 +8949,12 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) @@ -8796,11 +9005,12 @@ /* ** Every page in the cache is controlled by an instance of the following ** structure. */ struct PgHdr { - void *pData; /* Content of this page */ + sqlite3_pcache_page *pPage; /* Pcache object page handle */ + void *pData; /* Page data */ void *pExtra; /* Extra content */ PgHdr *pDirty; /* Transient list of dirty pages */ Pgno pgno; /* Page number for this page */ Pager *pPager; /* The pager this page is part of */ #ifdef SQLITE_CHECK_PAGES @@ -8914,10 +9124,13 @@ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); #endif +/* Free up as much memory as possible from the page cache */ +SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*); + #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* Try to return memory used by the pcache module to the main memory heap */ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); #endif @@ -8998,21 +9211,10 @@ #else # ifndef SQLITE_OS_WIN # define SQLITE_OS_WIN 0 # endif #endif - -/* -** Determine if we are dealing with WindowsCE - which has a much -** reduced API. -*/ -#if defined(_WIN32_WCE) -# define SQLITE_OS_WINCE 1 -#else -# define SQLITE_OS_WINCE 0 -#endif - /* ** Define the maximum size of a temporary filename */ #if SQLITE_OS_WIN @@ -9034,10 +9236,29 @@ # define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP) #else # define SQLITE_TEMPNAME_SIZE 200 #endif +/* +** Determine if we are dealing with Windows NT. +*/ +#if defined(_WIN32_WINNT) +# define SQLITE_OS_WINNT 1 +#else +# define SQLITE_OS_WINNT 0 +#endif + +/* +** Determine if we are dealing with WindowsCE - which has a much +** reduced API. +*/ +#if defined(_WIN32_WCE) +# define SQLITE_OS_WINCE 1 +#else +# define SQLITE_OS_WINCE 0 +#endif + /* If the SET_FULLSYNC macro is not defined above, then make it ** a no-op */ #ifndef SET_FULLSYNC # define SET_FULLSYNC(x,y) @@ -9045,11 +9266,11 @@ /* ** The default size of a disk sector */ #ifndef SQLITE_DEFAULT_SECTOR_SIZE -# define SQLITE_DEFAULT_SECTOR_SIZE 512 +# define SQLITE_DEFAULT_SECTOR_SIZE 4096 #endif /* ** Temporary files are named starting with this prefix followed by 16 random ** alphanumeric characters, and no file extension. They are stored in the @@ -9178,17 +9399,19 @@ SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); + /* ** Functions for accessing sqlite3_vfs methods */ SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); @@ -9278,18 +9501,21 @@ /* ** If this is a no-op implementation, implement everything as macros. */ #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) #define sqlite3_mutex_free(X) -#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_enter(X) #define sqlite3_mutex_try(X) SQLITE_OK -#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_leave(X) #define sqlite3_mutex_held(X) ((void)(X),1) #define sqlite3_mutex_notheld(X) ((void)(X),1) #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() +#define MUTEX_LOGIC(X) +#else +#define MUTEX_LOGIC(X) X #endif /* defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -9773,24 +9999,15 @@ ** collating sequence may not be read or written. */ struct CollSeq { char *zName; /* Name of the collating sequence, UTF-8 encoded */ u8 enc; /* Text encoding handled by xCmp() */ - u8 type; /* One of the SQLITE_COLL_... values below */ void *pUser; /* First argument to xCmp() */ int (*xCmp)(void*,int, const void*, int, const void*); void (*xDel)(void*); /* Destructor for pUser */ }; -/* -** Allowed values of CollSeq.type: -*/ -#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */ -#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */ -#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */ -#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */ - /* ** A sort order can be either ASC or DESC. */ #define SQLITE_SO_ASC 0 /* Sort in ascending order */ #define SQLITE_SO_DESC 1 /* Sort in ascending order */ @@ -9918,11 +10135,11 @@ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ - unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ + tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ @@ -10072,24 +10289,21 @@ ** into its constituent fields. */ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ u16 nField; /* Number of entries in apMem[] */ - u16 flags; /* Boolean settings. UNPACKED_... below */ + u8 flags; /* Boolean settings. UNPACKED_... below */ i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ Mem *aMem; /* Values */ }; /* ** Allowed values of UnpackedRecord.flags */ -#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */ -#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */ -#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */ -#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */ -#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */ -#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */ +#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ +#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ +#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */ /* ** Each SQL index is represented in memory by an ** instance of the following structure. ** @@ -10117,11 +10331,11 @@ */ struct Index { char *zName; /* Name of this index */ int nColumn; /* Number of columns in the table used by this index */ int *aiColumn; /* Which columns are used by this index. 1st is 0 */ - unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ + tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ u8 bUnordered; /* Use this index for == or IN queries only */ @@ -10128,24 +10342,33 @@ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ - IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */ +#ifdef SQLITE_ENABLE_STAT3 + int nSample; /* Number of elements in aSample[] */ + tRowcnt avgEq; /* Average nEq value for key values not in aSample */ + IndexSample *aSample; /* Samples of the left-most key */ +#endif }; /* -** Each sample stored in the sqlite_stat2 table is represented in memory -** using a structure of this type. +** Each sample stored in the sqlite_stat3 table is represented in memory +** using a structure of this type. See documentation at the top of the +** analyze.c source file for additional information. */ struct IndexSample { union { char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ - double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */ + double r; /* Value if eType is SQLITE_FLOAT */ + i64 i; /* Value if eType is SQLITE_INTEGER */ } u; u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ - u8 nByte; /* Size in byte of text or blob. */ + int nByte; /* Size in byte of text or blob. */ + tRowcnt nEq; /* Est. number of rows where the key equals this sample */ + tRowcnt nLt; /* Est. number of rows where key is less than this sample */ + tRowcnt nDLt; /* Est. number of distinct keys less than this sample */ }; /* ** Each token coming out of the lexer is an instance of ** this structure. Tokens are also used as part of an expression. @@ -10339,14 +10562,14 @@ #define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */ #define EP_FixedDest 0x0200 /* Result needed in a specific register */ #define EP_IntValue 0x0400 /* Integer value contained in u.iValue */ #define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */ - -#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ -#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ -#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */ +#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */ +#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ +#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ +#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */ /* ** The following are the meanings of bits in the Expr.flags2 field. */ #define EP2_MallocedToken 0x0001 /* Need to sqlite3DbFree() Expr.zToken */ @@ -10404,11 +10627,11 @@ Expr *pExpr; /* The list of expressions */ char *zName; /* Token associated with this expression */ char *zSpan; /* Original text of the expression */ u8 sortOrder; /* 1 for DESC or 0 for ASC */ u8 done; /* A flag to indicate when processing is finished */ - u16 iCol; /* For ORDER BY, column number in result set */ + u16 iOrderByCol; /* For ORDER BY, column number in result set */ u16 iAlias; /* Index into Parse.aAlias[] for zName */ } *a; /* One entry for each expression */ }; /* @@ -10593,14 +10816,14 @@ #define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */ #define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */ #define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ #define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ #define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ -#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */ -#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */ -#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */ -#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */ +#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */ +#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ +#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ +#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */ /* ** The WHERE clause processing routine has two halves. The ** first part does the start of the WHERE loop and the second ** half does the tail of the WHERE loop. An instance of @@ -10704,17 +10927,17 @@ /* ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ -#define SF_Distinct 0x0001 /* Output should be DISTINCT */ -#define SF_Resolved 0x0002 /* Identifiers have been resolved */ -#define SF_Aggregate 0x0004 /* Contains aggregate functions */ -#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ -#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ -#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ -#define SF_UseSorter 0x0040 /* Sort using a sorter */ +#define SF_Distinct 0x01 /* Output should be DISTINCT */ +#define SF_Resolved 0x02 /* Identifiers have been resolved */ +#define SF_Aggregate 0x04 /* Contains aggregate functions */ +#define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */ +#define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */ +#define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */ +#define SF_UseSorter 0x40 /* Sort using a sorter */ /* ** The results of a select can be distributed in several ways. The ** "SRT" prefix means "SELECT Result Type". @@ -10825,28 +11048,27 @@ sqlite3 *db; /* The main database structure */ int rc; /* Return code from execution */ char *zErrMsg; /* An error message */ Vdbe *pVdbe; /* An engine for executing database bytecode */ u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ - u8 nameClash; /* A permanent table name clashes with temp table name */ u8 checkSchema; /* Causes schema cookie check after an error */ u8 nested; /* Number of nested calls to the parser/code generator */ - u8 parseError; /* True after a parsing error. Ticket #1794 */ u8 nTempReg; /* Number of temporary registers in aTempReg[] */ u8 nTempInUse; /* Number of aTempReg[] currently checked out */ int aTempReg[8]; /* Holding area for temporary registers */ int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ + int nOnce; /* Number of OP_Once instructions so far */ int ckBase; /* Base register of data during check constraints */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ int iCacheCnt; /* Counter used to generate aColCache[].lru values */ - u8 nColCache; /* Number of entries in the column cache */ - u8 iColCache; /* Next entry of the cache to replace */ + u8 nColCache; /* Number of entries in aColCache[] */ + u8 iColCache; /* Next entry in aColCache[] to replace */ struct yColCache { int iTable; /* Table cursor number */ int iColumn; /* Table column number */ u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ @@ -10884,11 +11106,10 @@ int nVar; /* Number of '?' variables seen in the SQL so far */ int nzVar; /* Number of available slots in azVar[] */ char **azVar; /* Pointers to names of parameters */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ int nAlias; /* Number of aliased result set columns */ - int nAliasAlloc; /* Number of allocated slots for aAlias[] */ int *aAlias; /* Register used to hold aliased result */ u8 explain; /* True if the EXPLAIN flag is found on the query */ Token sNameToken; /* Token with unqualified schema object name */ Token sLastToken; /* The last token parsed */ const char *zTail; /* All SQL text past the last semicolon parsed */ @@ -11079,11 +11300,11 @@ int mxStrlen; /* Maximum string length */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ - sqlite3_pcache_methods pcache; /* Low-level page-cache interface */ + sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */ void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ void *pScratch; /* Scratch memory */ int szScratch; /* Size of each scratch buffer */ @@ -11285,10 +11506,33 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...); #endif #if defined(SQLITE_TEST) SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*); #endif + +/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */ +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) +SQLITE_PRIVATE void sqlite3ExplainBegin(Vdbe*); +SQLITE_PRIVATE void sqlite3ExplainPrintf(Vdbe*, const char*, ...); +SQLITE_PRIVATE void sqlite3ExplainNL(Vdbe*); +SQLITE_PRIVATE void sqlite3ExplainPush(Vdbe*); +SQLITE_PRIVATE void sqlite3ExplainPop(Vdbe*); +SQLITE_PRIVATE void sqlite3ExplainFinish(Vdbe*); +SQLITE_PRIVATE void sqlite3ExplainSelect(Vdbe*, Select*); +SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe*, Expr*); +SQLITE_PRIVATE void sqlite3ExplainExprList(Vdbe*, ExprList*); +SQLITE_PRIVATE const char *sqlite3VdbeExplanation(Vdbe*); +#else +# define sqlite3ExplainBegin(X) +# define sqlite3ExplainSelect(A,B) +# define sqlite3ExplainExpr(A,B) +# define sqlite3ExplainExprList(A,B) +# define sqlite3ExplainFinish(X) +# define sqlite3VdbeExplanation(X) 0 +#endif + + SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*, ...); SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); SQLITE_PRIVATE int sqlite3Dequote(char*); SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **); @@ -11295,10 +11539,11 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse*); SQLITE_PRIVATE int sqlite3GetTempReg(Parse*); SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); +SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*, const Token*); SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*); @@ -11326,10 +11571,11 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*); SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*); SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); +SQLITE_PRIVATE int sqlite3CodeOnce(Parse *); SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32); SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32); SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec*, u32); SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec*, u32, void*); @@ -11350,10 +11596,11 @@ #else # define sqlite3ViewGetColumnNames(A,B) 0 #endif SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); +SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); #ifndef SQLITE_OMIT_AUTOINCREMENT SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse); #else @@ -11606,11 +11853,11 @@ SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*); SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *); #endif SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION @@ -11663,10 +11910,11 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int); SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int); +SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum*,int); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*); SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); @@ -11708,19 +11956,21 @@ # define sqlite3VtabInSync(db) 0 # define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabUnlockList(X) # define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK +# define sqlite3GetVTable(X,Y) ((VTable*)0) #else SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*); SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **); SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db); SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db); SQLITE_PRIVATE void sqlite3VtabLock(VTable *); SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *); SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*); SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int); +SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*); SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse*, Token*); @@ -11736,11 +11986,10 @@ SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*); -SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*); SQLITE_PRIVATE const char *sqlite3JournalModename(int); SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); /* Declarations for functions in fkey.c. All of these are replaced by @@ -12041,11 +12290,11 @@ 0x7ffffffe, /* mxStrlen */ 128, /* szLookaside */ 500, /* nLookaside */ {0,0,0,0,0,0,0,0}, /* m */ {0,0,0,0,0,0,0,0,0}, /* mutex */ - {0,0,0,0,0,0,0,0,0,0,0}, /* pcache */ + {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ (void*)0, /* pHeap */ 0, /* nHeap */ 0, 0, /* mnHeap, mxHeap */ (void*)0, /* pScratch */ 0, /* szScratch */ @@ -12230,12 +12479,12 @@ "ENABLE_OVERSIZE_CELL_CHECK", #endif #ifdef SQLITE_ENABLE_RTREE "ENABLE_RTREE", #endif -#ifdef SQLITE_ENABLE_STAT2 - "ENABLE_STAT2", +#ifdef SQLITE_ENABLE_STAT3 + "ENABLE_STAT3", #endif #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY "ENABLE_UNLOCK_NOTIFY", #endif #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT @@ -12445,13 +12694,10 @@ "OMIT_WSD", #endif #ifdef SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif -#ifdef SQLITE_PAGECACHE_BLOCKALLOC - "PAGECACHE_BLOCKALLOC", -#endif #ifdef SQLITE_PERFORMANCE_TRACE "PERFORMANCE_TRACE", #endif #ifdef SQLITE_PROXY_DEBUG "PROXY_DEBUG", @@ -12571,10 +12817,13 @@ typedef unsigned char Bool; /* Opaque type used by code in vdbesort.c */ typedef struct VdbeSorter VdbeSorter; +/* Opaque type used by the explainer */ +typedef struct Explain Explain; + /* ** A cursor is a pointer into a single BTree within a database file. ** The cursor can seek to a BTree entry with a particular key, or ** loop over all entries of the Btree. You can also insert new BTree ** entries or retrieve the key or data from the entry that the cursor @@ -12655,10 +12904,12 @@ int pc; /* Program Counter in parent (calling) frame */ Op *aOp; /* Program instructions for parent frame */ int nOp; /* Size of aOp array */ Mem *aMem; /* Array of memory cells for parent frame */ int nMem; /* Number of entries in aMem */ + u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */ + int nOnceFlag; /* Number of entries in aOnceFlag */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u16 nCursor; /* Number of entries in apCsr */ void *token; /* Copy of SubProgram.token */ int nChildMem; /* Number of memory cells for child frame */ int nChildCsr; /* Number of cursors for child frame */ @@ -12793,10 +13044,22 @@ Mem *pMem; /* Memory cell used to store aggregate context */ int isError; /* Error code returned by the function. */ CollSeq *pColl; /* Collating sequence */ }; +/* +** An Explain object accumulates indented output which is helpful +** in describing recursive data structures. +*/ +struct Explain { + Vdbe *pVdbe; /* Attach the explanation to this Vdbe */ + StrAccum str; /* The string being accumulated */ + int nIndent; /* Number of elements in aIndent */ + u16 aIndent[100]; /* Levels of indentation */ + char zBase[100]; /* Initial space */ +}; + /* ** An instance of the virtual machine. This structure contains the complete ** state of the virtual machine. ** ** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() @@ -12859,15 +13122,21 @@ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ #ifdef SQLITE_DEBUG FILE *trace; /* Write an execution trace here, if not NULL */ #endif +#ifdef SQLITE_ENABLE_TREE_EXPLAIN + Explain *pExplain; /* The explainer */ + char *zExplain; /* Explanation of data structures */ +#endif VdbeFrame *pFrame; /* Parent frame */ VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ int nFrame; /* Number of frames in pFrame list */ u32 expmask; /* Binding to these vars invalidates VM */ SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ + int nOnceFlag; /* Size of array aOnceFlag[] */ + u8 *aOnceFlag; /* Flags for OP_Once */ }; /* ** The following are allowed values for Vdbe.magic */ @@ -12923,20 +13192,21 @@ SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p); -#define MemReleaseExt(X) \ +#define VdbeMemRelease(X) \ if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \ sqlite3VdbeMemReleaseExternal(X); SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); SQLITE_PRIVATE const char *sqlite3OpcodeName(int); SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); +SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); #ifdef SQLITE_OMIT_MERGE_SORT # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK # define sqlite3VdbeSorterClose(Y,Z) @@ -12961,11 +13231,11 @@ # define sqlite3VdbeEnter(X) # define sqlite3VdbeLeave(X) #endif #ifdef SQLITE_DEBUG -SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); +SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int); #else @@ -12979,12 +13249,14 @@ #endif SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem); #ifndef SQLITE_OMIT_INCRBLOB SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *); + #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0) #else #define sqlite3VdbeMemExpandBlob(x) SQLITE_OK + #define ExpandBlob(P) SQLITE_OK #endif #endif /* !defined(_VDBEINT_H_) */ /************** End of vdbeInt.h *********************************************/ @@ -13189,10 +13461,32 @@ *pHighwater = 0; *pCurrent = nByte; break; } + + /* + ** Set *pCurrent to the total cache hits or misses encountered by all + ** pagers the database handle is connected to. *pHighwater is always set + ** to zero. + */ + case SQLITE_DBSTATUS_CACHE_HIT: + case SQLITE_DBSTATUS_CACHE_MISS: { + int i; + int nRet = 0; + assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); + + for(i=0; inDb; i++){ + if( db->aDb[i].pBt ){ + Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt); + sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); + } + } + *pHighwater = 0; + *pCurrent = nRet; + break; + } default: { rc = SQLITE_ERROR; } } @@ -13490,16 +13784,22 @@ } return 0; } /* -** Set the time to the current time reported by the VFS +** Set the time to the current time reported by the VFS. +** +** Return the number of errors. */ -static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ +static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ sqlite3 *db = sqlite3_context_db_handle(context); - sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD); - p->validJD = 1; + if( sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD)==SQLITE_OK ){ + p->validJD = 1; + return 0; + }else{ + return 1; + } } /* ** Attempt to parse the given string into a Julian Day Number. Return ** the number of errors. @@ -13525,12 +13825,11 @@ if( parseYyyyMmDd(zDate,p)==0 ){ return 0; }else if( parseHhMmSs(zDate, p)==0 ){ return 0; }else if( sqlite3StrICmp(zDate,"now")==0){ - setDateTimeToCurrent(context, p); - return 0; + return setDateTimeToCurrent(context, p); }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); p->validJD = 1; return 0; } @@ -13953,12 +14252,13 @@ int i; const unsigned char *z; int eType; memset(p, 0, sizeof(*p)); if( argc==0 ){ - setDateTimeToCurrent(context, p); - }else if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT + return setDateTimeToCurrent(context, p); + } + if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT || eType==SQLITE_INTEGER ){ p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5); p->validJD = 1; }else{ z = sqlite3_value_text(argv[0]); @@ -14266,35 +14566,32 @@ ){ time_t t; char *zFormat = (char *)sqlite3_user_data(context); sqlite3 *db; sqlite3_int64 iT; + struct tm *pTm; + struct tm sNow; char zBuf[20]; UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); db = sqlite3_context_db_handle(context); - sqlite3OsCurrentTimeInt64(db->pVfs, &iT); + if( sqlite3OsCurrentTimeInt64(db->pVfs, &iT) ) return; t = iT/1000 - 10000*(sqlite3_int64)21086676; #ifdef HAVE_GMTIME_R - { - struct tm sNow; - gmtime_r(&t, &sNow); + pTm = gmtime_r(&t, &sNow); +#else + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); + pTm = gmtime(&t); + if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +#endif + if( pTm ){ strftime(zBuf, 20, zFormat, &sNow); - } -#else - { - struct tm *pTm; - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - pTm = gmtime(&t); - strftime(zBuf, 20, zFormat, pTm); - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - } -#endif - - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + } } #endif /* ** This function registered all of the above C functions as SQL @@ -14355,15 +14652,22 @@ ** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro. ** ** The following functions are instrumented for malloc() failure ** testing: ** -** sqlite3OsOpen() ** sqlite3OsRead() ** sqlite3OsWrite() ** sqlite3OsSync() +** sqlite3OsFileSize() ** sqlite3OsLock() +** sqlite3OsCheckReservedLock() +** sqlite3OsFileControl() +** sqlite3OsShmMap() +** sqlite3OsOpen() +** sqlite3OsDelete() +** sqlite3OsAccess() +** sqlite3OsFullPathname() ** */ #if defined(SQLITE_TEST) SQLITE_API int sqlite3_memdebug_vfs_oom_test = 1; #define DO_OS_MALLOC_TEST(x) \ @@ -14418,13 +14722,27 @@ } SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ DO_OS_MALLOC_TEST(id); return id->pMethods->xCheckReservedLock(id, pResOut); } + +/* +** Use sqlite3OsFileControl() when we are doing something that might fail +** and we need to know about the failures. Use sqlite3OsFileControlHint() +** when simply tossing information over the wall to the VFS and we do not +** really care if the VFS receives and understands the information since it +** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() +** routine has no return value since the return value would be meaningless. +*/ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ + DO_OS_MALLOC_TEST(id); return id->pMethods->xFileControl(id, op, pArg); } +SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ + (void)id->pMethods->xFileControl(id, op, pArg); +} + SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){ int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize; return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ @@ -14444,10 +14762,11 @@ int iPage, int pgsz, int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Pointer to mapping */ ){ + DO_OS_MALLOC_TEST(id); return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); } /* ** The next group of routines are convenience wrappers around the @@ -14460,19 +14779,21 @@ int flags, int *pFlagsOut ){ int rc; DO_OS_MALLOC_TEST(0); - /* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed + /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before ** reaching the VFS. */ rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut); assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; } SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + DO_OS_MALLOC_TEST(0); + assert( dirSync==0 || dirSync==1 ); return pVfs->xDelete(pVfs, zPath, dirSync); } SQLITE_PRIVATE int sqlite3OsAccess( sqlite3_vfs *pVfs, const char *zPath, @@ -14486,10 +14807,11 @@ sqlite3_vfs *pVfs, const char *zPath, int nPathOut, char *zPathOut ){ + DO_OS_MALLOC_TEST(0); zPathOut[0] = 0; return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); } #ifndef SQLITE_OMIT_LOAD_EXTENSION SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ @@ -14625,16 +14947,16 @@ ** Register a VFS with the system. It is harmless to register the same ** VFS multiple times. The new VFS becomes the default if makeDflt is ** true. */ SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ - sqlite3_mutex *mutex = 0; + MUTEX_LOGIC(sqlite3_mutex *mutex;) #ifndef SQLITE_OMIT_AUTOINIT int rc = sqlite3_initialize(); if( rc ) return rc; #endif - mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) sqlite3_mutex_enter(mutex); vfsUnlink(pVfs); if( makeDflt || vfsList==0 ){ pVfs->pNext = vfsList; vfsList = pVfs; @@ -14837,31 +15159,81 @@ ** used when no other memory allocator is specified using compile-time ** macros. */ #ifdef SQLITE_SYSTEM_MALLOC +/* +** Windows systems have malloc_usable_size() but it is called _msize() +*/ +#if !defined(HAVE_MALLOC_USABLE_SIZE) && SQLITE_OS_WIN +# define HAVE_MALLOC_USABLE_SIZE 1 +# define malloc_usable_size _msize +#endif + +#if defined(__APPLE__) + +/* +** Use the zone allocator available on apple products +*/ +#include +#include +#include +static malloc_zone_t* _sqliteZone_; +#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x)) +#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x)); +#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y)) +#define SQLITE_MALLOCSIZE(x) \ + (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x)) + +#else /* if not __APPLE__ */ + +/* +** Use standard C library malloc and free on non-Apple systems. +*/ +#define SQLITE_MALLOC(x) malloc(x) +#define SQLITE_FREE(x) free(x) +#define SQLITE_REALLOC(x,y) realloc((x),(y)) + +#ifdef HAVE_MALLOC_USABLE_SIZE +#include +#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) +#else +#undef SQLITE_MALLOCSIZE +#endif + +#endif /* __APPLE__ or not __APPLE__ */ + /* ** Like malloc(), but remember the size of the allocation ** so that we can find it later using sqlite3MemSize(). ** ** For this low-level routine, we are guaranteed that nByte>0 because ** cases of nByte<=0 will be intercepted and dealt with by higher level ** routines. */ static void *sqlite3MemMalloc(int nByte){ +#ifdef SQLITE_MALLOCSIZE + void *p = SQLITE_MALLOC( nByte ); + if( p==0 ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); + } + return p; +#else sqlite3_int64 *p; assert( nByte>0 ); nByte = ROUND8(nByte); - p = malloc( nByte+8 ); + p = SQLITE_MALLOC( nByte+8 ); if( p ){ p[0] = nByte; p++; }else{ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); } return (void *)p; +#endif } /* ** Like free() but works for allocations obtained from sqlite3MemMalloc() ** or sqlite3MemRealloc(). @@ -14869,26 +15241,34 @@ ** For this low-level routine, we already know that pPrior!=0 since ** cases where pPrior==0 will have been intecepted and dealt with ** by higher-level routines. */ static void sqlite3MemFree(void *pPrior){ +#ifdef SQLITE_MALLOCSIZE + SQLITE_FREE(pPrior); +#else sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 ); p--; - free(p); + SQLITE_FREE(p); +#endif } /* ** Report the allocated size of a prior return from xMalloc() ** or xRealloc(). */ static int sqlite3MemSize(void *pPrior){ +#ifdef SQLITE_MALLOCSIZE + return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0; +#else sqlite3_int64 *p; if( pPrior==0 ) return 0; p = (sqlite3_int64*)pPrior; p--; return (int)p[0]; +#endif } /* ** Like realloc(). Resize an allocation previously obtained from ** sqlite3MemMalloc(). @@ -14898,15 +15278,25 @@ ** redirected to xMalloc. Similarly, we know that nByte>0 becauses ** cases where nByte<=0 will have been intercepted by higher-level ** routines and redirected to xFree. */ static void *sqlite3MemRealloc(void *pPrior, int 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); + } + return p; +#else sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */ p--; - p = realloc(p, nByte+8 ); + p = SQLITE_REALLOC(p, nByte+8 ); if( p ){ p[0] = nByte; p++; }else{ testcase( sqlite3GlobalConfig.xLog!=0 ); @@ -14913,10 +15303,11 @@ sqlite3_log(SQLITE_NOMEM, "failed memory resize %u to %u bytes", sqlite3MemSize(pPrior), nByte); } return (void*)p; +#endif } /* ** Round up a request size to the next valid allocation size. */ @@ -14926,10 +15317,38 @@ /* ** Initialize this module. */ static int sqlite3MemInit(void *NotUsed){ +#if defined(__APPLE__) + int cpuCount; + size_t len; + if( _sqliteZone_ ){ + return SQLITE_OK; + } + len = sizeof(cpuCount); + /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ + sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); + if( cpuCount>1 ){ + /* defer MT decisions to system malloc */ + _sqliteZone_ = malloc_default_zone(); + }else{ + /* only 1 core, use our own zone to contention over global locks, + ** e.g. we have our own dedicated locks */ + bool success; + malloc_zone_t* newzone = malloc_create_zone(4096, 0); + malloc_set_zone_name(newzone, "Sqlite_Heap"); + do{ + success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone, + (void * volatile *)&_sqliteZone_); + }while(!_sqliteZone_); + if( !success ){ + /* somebody registered a zone first */ + malloc_destroy_zone(newzone); + } + } +#endif UNUSED_PARAMETER(NotUsed); return SQLITE_OK; } /* @@ -16915,11 +17334,11 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); } #endif -#endif /* SQLITE_MUTEX_OMIT */ +#endif /* !defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex.c ***********************************************/ /************** Begin file mutex_noop.c **************************************/ /* ** 2008 October 07 @@ -17122,12 +17541,12 @@ */ #ifdef SQLITE_MUTEX_NOOP SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ return sqlite3NoopMutex(); } -#endif /* SQLITE_MUTEX_NOOP */ -#endif /* SQLITE_MUTEX_OMIT */ +#endif /* defined(SQLITE_MUTEX_NOOP) */ +#endif /* !defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex_noop.c ******************************************/ /************** Begin file mutex_os2.c ***************************************/ /* ** 2007 August 28 @@ -17752,11 +18171,11 @@ }; return &sMutex; } -#endif /* SQLITE_MUTEX_PTHREAD */ +#endif /* SQLITE_MUTEX_PTHREADS */ /************** End of mutex_unix.c ******************************************/ /************** Begin file mutex_w32.c ***************************************/ /* ** 2007 August 14 @@ -18221,11 +18640,12 @@ */ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ sqlite3_int64 priorLimit; sqlite3_int64 excess; #ifndef SQLITE_OMIT_AUTOINIT - sqlite3_initialize(); + int rc = sqlite3_initialize(); + if( rc ) return -1; #endif sqlite3_mutex_enter(mem0.mutex); priorLimit = mem0.alarmThreshold; sqlite3_mutex_leave(mem0.mutex); if( n<0 ) return priorLimit; @@ -18878,52 +19298,14 @@ ** an historical reference. Most of the "enhancements" have been backed ** out so that the functionality is now the same as standard printf(). ** ************************************************************************** ** -** The following modules is an enhanced replacement for the "printf" subroutines -** found in the standard C library. The following enhancements are -** supported: -** -** + Additional functions. The standard set of "printf" functions -** includes printf, fprintf, sprintf, vprintf, vfprintf, and -** vsprintf. This module adds the following: -** -** * snprintf -- Works like sprintf, but has an extra argument -** which is the size of the buffer written to. -** -** * mprintf -- Similar to sprintf. Writes output to memory -** obtained from malloc. -** -** * xprintf -- Calls a function to dispose of output. -** -** * nprintf -- No output, but returns the number of characters -** that would have been output by printf. -** -** * A v- version (ex: vsnprintf) of every function is also -** supplied. -** -** + A few extensions to the formatting notation are supported: -** -** * The "=" flag (similar to "-") causes the output to be -** be centered in the appropriately sized field. -** -** * The %b field outputs an integer in binary notation. -** -** * The %c field now accepts a precision. The character output -** is repeated by the number of times the precision specifies. -** -** * The %' field works like %c, but takes as its character the -** next character of the format string, instead of the next -** argument. For example, printf("%.78'-") prints 78 minus -** signs, the same as printf("%.78c",'-'). -** -** + When compiled using GCC on a SPARC, this version of printf is -** faster than the library printf for SUN OS 4.1. -** -** + All functions are fully reentrant. -** +** This file contains code for a set of "printf"-like routines. These +** routines format strings much like the printf() from the standard C +** library, though the implementation here has enhancements to support +** SQLlite. */ /* ** Conversion types fall into various categories as defined by the ** following enumeration. @@ -19044,11 +19426,11 @@ #endif /* SQLITE_OMIT_FLOATING_POINT */ /* ** Append N space characters to the given string buffer. */ -static void appendSpace(StrAccum *pAccum, int N){ +SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *pAccum, int N){ static const char zSpaces[] = " "; while( N>=(int)sizeof(zSpaces)-1 ){ sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1); N -= sizeof(zSpaces)-1; } @@ -19057,47 +19439,19 @@ } } /* ** On machines with a small stack size, you can redefine the -** SQLITE_PRINT_BUF_SIZE to be less than 350. +** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. */ #ifndef SQLITE_PRINT_BUF_SIZE -# if defined(SQLITE_SMALL_STACK) -# define SQLITE_PRINT_BUF_SIZE 50 -# else -# define SQLITE_PRINT_BUF_SIZE 350 -# endif +# define SQLITE_PRINT_BUF_SIZE 70 #endif #define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */ /* -** The root program. All variations call this core. -** -** INPUTS: -** func This is a pointer to a function taking three arguments -** 1. A pointer to anything. Same as the "arg" parameter. -** 2. A pointer to the list of characters to be output -** (Note, this list is NOT null terminated.) -** 3. An integer number of characters to be output. -** (Note: This number might be zero.) -** -** arg This is the pointer to anything which will be passed as the -** first argument to "func". Use it for whatever you like. -** -** fmt This is the format string, as in the usual print. -** -** ap This is a pointer to a list of arguments. Same as in -** vfprint. -** -** OUTPUTS: -** The return value is the total number of characters sent to -** the function "func". Returns -1 on a error. -** -** Note that the order in which automatic variables are declared below -** seems to make a big difference in determining how fast this beast -** will run. +** Render a string given by "fmt" into the StrAccum object. */ SQLITE_PRIVATE void sqlite3VXPrintf( StrAccum *pAccum, /* Accumulate results here */ int useExtended, /* Allow extended %-conversions */ const char *fmt, /* Format string */ @@ -19116,27 +19470,27 @@ etByte flag_altform2; /* True if "!" flag is present */ etByte flag_zeropad; /* True if field width constant starts with zero */ etByte flag_long; /* True if "l" flag is present */ etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ + etByte xtype = 0; /* Conversion paradigm */ + char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ - char buf[etBUFSIZE]; /* Conversion buffer */ - char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ - etByte xtype = 0; /* Conversion paradigm */ - char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ + char *zOut; /* Rendering buffer */ + int nOut; /* Size of the rendering buffer */ + char *zExtra; /* Malloced memory used by some conversion */ #ifndef SQLITE_OMIT_FLOATING_POINT int exp, e2; /* exponent of real numbers */ + int nsd; /* Number of significant digits returned */ double rounder; /* Used for rounding floating point values */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ - etByte flag_exp; /* True to force display of the exponent */ - int nsd; /* Number of significant digits returned */ #endif + char buf[etBUFSIZE]; /* Conversion buffer */ - length = 0; bufpt = 0; for(; (c=(*fmt))!=0; ++fmt){ if( c!='%' ){ int amt; bufpt = (char *)fmt; @@ -19177,13 +19531,10 @@ while( c>='0' && c<='9' ){ width = width*10 + c - '0'; c = *++fmt; } } - if( width > etBUFSIZE-10 ){ - width = etBUFSIZE-10; - } /* Get the precision */ if( c=='.' ){ precision = 0; c = *++fmt; if( c=='*' ){ @@ -19226,16 +19577,10 @@ break; } } zExtra = 0; - - /* Limit the precision to prevent overflowing buf[] during conversion */ - if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ - precision = etBUFSIZE-40; - } - /* ** At this point, variables are initialized as follows: ** ** flag_alternateform TRUE if a '#' is present. ** flag_altform2 TRUE if a '!' is present. @@ -19296,20 +19641,30 @@ } if( longvalue==0 ) flag_alternateform = 0; if( flag_zeropad && precisionmallocFailed = 1; + return; + } + } + bufpt = &zOut[nOut-1]; if( xtype==etORDINAL ){ static const char zOrd[] = "thstndrd"; int x = (int)(longvalue % 10); if( x>=4 || (longvalue/10)%10==1 ){ x = 0; } - buf[etBUFSIZE-3] = zOrd[x*2]; - buf[etBUFSIZE-2] = zOrd[x*2+1]; - bufpt -= 2; + *(--bufpt) = zOrd[x*2+1]; + *(--bufpt) = zOrd[x*2]; } { register const char *cset; /* Use registers for speed */ register int base; cset = &aDigits[infop->charset]; @@ -19317,11 +19672,11 @@ do{ /* Convert to ascii */ *(--bufpt) = cset[longvalue%base]; longvalue = longvalue/base; }while( longvalue>0 ); } - length = (int)(&buf[etBUFSIZE-1]-bufpt); + length = (int)(&zOut[nOut-1]-bufpt); for(idx=precision-length; idx>0; idx--){ *(--bufpt) = '0'; /* Zero pad */ } if( prefix ) *(--bufpt) = prefix; /* Add sign */ if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ @@ -19328,21 +19683,20 @@ const char *pre; char x; pre = &aPrefix[infop->prefix]; for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; } - length = (int)(&buf[etBUFSIZE-1]-bufpt); + length = (int)(&zOut[nOut-1]-bufpt); break; case etFLOAT: case etEXP: case etGENERIC: realvalue = va_arg(ap,double); #ifdef SQLITE_OMIT_FLOATING_POINT length = 0; #else if( precision<0 ) precision = 6; /* Set default precision */ - if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10; if( realvalue<0.0 ){ realvalue = -realvalue; prefix = '-'; }else{ if( flag_plussign ) prefix = '+'; @@ -19386,11 +19740,10 @@ bufpt = buf; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ - flag_exp = xtype==etEXP; if( xtype!=etFLOAT ){ realvalue += rounder; if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } } if( xtype==etGENERIC ){ @@ -19407,10 +19760,18 @@ if( xtype==etEXP ){ e2 = 0; }else{ e2 = exp; } + if( e2+precision+width > etBUFSIZE - 15 ){ + bufpt = zExtra = sqlite3Malloc( e2+precision+width+15 ); + if( bufpt==0 ){ + pAccum->mallocFailed = 1; + return; + } + } + zOut = bufpt; nsd = 0; flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; /* The sign in front of the number */ if( prefix ){ *(bufpt++) = prefix; @@ -19438,21 +19799,21 @@ *(bufpt++) = et_getdigit(&realvalue,&nsd); } /* Remove trailing zeros and the "." if no digits follow the "." */ if( flag_rtz && flag_dp ){ while( bufpt[-1]=='0' ) *(--bufpt) = 0; - assert( bufpt>buf ); + assert( bufpt>zOut ); if( bufpt[-1]=='.' ){ if( flag_altform2 ){ *(bufpt++) = '0'; }else{ *(--bufpt) = 0; } } } /* Add the "eNNN" suffix */ - if( flag_exp || xtype==etEXP ){ + if( xtype==etEXP ){ *(bufpt++) = aDigits[infop->charset]; if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; }else{ *(bufpt++) = '+'; @@ -19467,12 +19828,12 @@ *bufpt = 0; /* The converted number is in buf[] and zero terminated. Output it. ** Note that the number is in the usual order, not reversed as with ** integer conversions. */ - length = (int)(bufpt-buf); - bufpt = buf; + length = (int)(bufpt-zOut); + bufpt = zOut; /* Special case: Add leading zeros if the flag_zeropad flag is ** set and we are not left justified */ if( flag_zeropad && !flag_leftjustify && length < width){ int i; @@ -19593,26 +19954,24 @@ */ if( !flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ - appendSpace(pAccum, nspace); + sqlite3AppendSpace(pAccum, nspace); } } if( length>0 ){ sqlite3StrAccumAppend(pAccum, bufpt, length); } if( flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ - appendSpace(pAccum, nspace); + sqlite3AppendSpace(pAccum, nspace); } } - if( zExtra ){ - sqlite3_free(zExtra); - } + sqlite3_free(zExtra); }/* End for loop over the format string */ } /* End of function */ /* ** Append N bytes of text from z to the StrAccum object. @@ -19622,10 +19981,11 @@ if( p->tooBig | p->mallocFailed ){ testcase(p->tooBig); testcase(p->mallocFailed); return; } + assert( p->zText!=0 || p->nChar==0 ); if( N<0 ){ N = sqlite3Strlen30(z); } if( N==0 || NEVER(z==0) ){ return; @@ -19653,19 +20013,20 @@ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); }else{ zNew = sqlite3_realloc(zOld, p->nAlloc); } if( zNew ){ - if( zOld==0 ) memcpy(zNew, p->zText, p->nChar); + if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); p->zText = zNew; }else{ p->mallocFailed = 1; sqlite3StrAccumReset(p); return; } } } + assert( p->zText ); memcpy(&p->zText[p->nChar], z, N); p->nChar += N; } /* @@ -20511,11 +20872,11 @@ ** no longer required. ** ** If a malloc failure occurs, NULL is returned and the db.mallocFailed ** flag set. */ -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){ Mem m; memset(&m, 0, sizeof(m)); m.db = db; sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC); @@ -20940,11 +21301,11 @@ }else if( *z=='+' ){ z+=incr; } /* copy digits to exponent */ while( z=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; } @@ -21772,22 +22139,21 @@ ** Examples: ** ** test.db-journal => test.nal ** test.db-wal => test.wal ** test.db-shm => test.shm +** test.db-mj7f3319fa => test.9fa */ SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){ #if SQLITE_ENABLE_8_3_NAMES<2 - const char *zOk; - zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names"); - if( zOk && sqlite3GetBoolean(zOk) ) + if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) ) #endif { int i, sz; sz = sqlite3Strlen30(z); for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} - if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4); + if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); } } #endif /************** End of util.c ************************************************/ @@ -24488,10 +24854,11 @@ #include #include #ifndef SQLITE_OMIT_WAL #include #endif + #if SQLITE_ENABLE_LOCKING_STYLE # include # if OS_VXWORKS # include @@ -24572,10 +24939,11 @@ ** VFS implementations. */ typedef struct unixFile unixFile; struct unixFile { sqlite3_io_methods const *pMethod; /* Always the first entry */ + sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ unixInodeInfo *pInode; /* Info about locks on this inode */ int h; /* The file descriptor */ unsigned char eFileLock; /* The type of lock held on this fd */ unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ @@ -24589,11 +24957,10 @@ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS - int isDelete; /* Delete on close if true */ struct vxworksFileId *pId; /* Unique file ID */ #endif #ifndef NDEBUG /* The next group of variables are used to track whether or not the ** transaction counter in bytes 24-27 of database files are updated @@ -24623,10 +24990,14 @@ #ifndef SQLITE_DISABLE_DIRSYNC # define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ #else # define UNIXFILE_DIRSYNC 0x00 #endif +#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ +#define UNIXFILE_DELETE 0x20 /* Delete on close */ +#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ +#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ /* ** Include code that is common to all os_*.c files */ /************** Include os_common.h in the middle of os_unix.c ***************/ @@ -24981,10 +25352,16 @@ #define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent) { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 }, #define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent) + { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 }, +#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) + + { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, +#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) + }; /* End of the overrideable system calls */ /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the ** "unix" VFSes. Return SQLITE_OK opon successfully updating the @@ -25102,11 +25479,11 @@ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } #endif -#ifdef SQLITE_DEBUG +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* ** Helper function for printing out trace information from debugging ** binaries. This returns the string represetation of the supplied ** integer lock-type. */ @@ -25937,18 +26314,18 @@ ** locking a random byte from a range, concurrent SHARED locks may exist ** even if the locking primitive used is always a write-lock. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode = pFile->pInode; + unixInodeInfo *pInode; struct flock lock; int tErrno = 0; assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pInode->eFileLock), pInode->nShared , getpid())); + azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid())); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the end_lock: exit path, as ** unixEnterMutex() hasn't been called yet. */ @@ -26148,11 +26525,10 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; int rc = SQLITE_OK; - int h; assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, getpid())); @@ -26160,18 +26536,14 @@ assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ return SQLITE_OK; } unixEnterMutex(); - h = pFile->h; pInode = pFile->pInode; assert( pInode->nShared!=0 ); if( pFile->eFileLock>SHARED_LOCK ){ assert( pInode->eFileLock==pFile->eFileLock ); - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); #ifndef NDEBUG /* When reducing a lock such that other processes can start ** reading the database file again, make sure that the ** transaction counter was updated if any part of the database @@ -26178,15 +26550,10 @@ ** file changed. If the transaction counter is not updated, ** other connections to the same file might not realize that ** the file has changed and hence might not know to flush their ** cache. The use of a stale cache can lead to database corruption. */ -#if 0 - assert( pFile->inNormalWrite==0 - || pFile->dbUpdate==0 - || pFile->transCntrChng==1 ); -#endif pFile->inNormalWrite = 0; #endif /* downgrading to a shared lock on NFS involves clearing the write lock ** before establishing the readlock - to avoid a race condition we downgrade @@ -26284,13 +26651,10 @@ pInode->nShared--; if( pInode->nShared==0 ){ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = NO_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; pFile->lastErrno = errno; @@ -26343,11 +26707,11 @@ robust_close(pFile, pFile->h, __LINE__); pFile->h = -1; } #if OS_VXWORKS if( pFile->pId ){ - if( pFile->isDelete ){ + if( pFile->ctrlFlags & UNIXFILE_DELETE ){ osUnlink(pFile->pId->zCanonicalName); } vxworksReleaseFileId(pFile->pId); pFile->pId = 0; } @@ -26432,12 +26796,12 @@ /****************************************************************************** ************************* Begin dot-file Locking ****************************** ** ** The dotfile locking implementation uses the existance of separate lock -** files in order to control access to the database. This works on just -** about every filesystem imaginable. But there are serious downsides: +** files (really a directory) to control access to the database. This works +** on just about every filesystem imaginable. But there are serious downsides: ** ** (1) There is zero concurrency. A single reader blocks all other ** connections from reading or writing the database. ** ** (2) An application crash or power loss can leave stale lock files @@ -26444,19 +26808,19 @@ ** sitting around that need to be cleared manually. ** ** Nevertheless, a dotlock is an appropriate locking mode for use if no ** other locking strategy is available. ** -** Dotfile locking works by creating a file in the same directory as the -** database and with the same name but with a ".lock" extension added. -** The existance of a lock file implies an EXCLUSIVE lock. All other lock -** types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. +** Dotfile locking works by creating a subdirectory in the same directory as +** the database and with the same name but with a ".lock" extension added. +** The existance of a lock directory implies an EXCLUSIVE lock. All other +** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. */ /* ** The file suffix added to the data base filename in order to create the -** lock file. +** lock directory. */ #define DOTLOCK_SUFFIX ".lock" /* ** This routine checks if there is a RESERVED lock held on the specified @@ -26519,11 +26883,10 @@ ** With dotfile locking, we really only support state (4): EXCLUSIVE. ** But we track the other locking levels internally. */ static int dotlockLock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; - int fd; char *zLockFile = (char *)pFile->lockingContext; int rc = SQLITE_OK; /* If we have any lock, then the lock file already exists. All we have @@ -26539,13 +26902,13 @@ #endif return SQLITE_OK; } /* grab an exclusive lock */ - fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); - if( fd<0 ){ - /* failed to open/create the file, someone else may have stolen the lock */ + rc = osMkdir(zLockFile, 0777); + if( rc<0 ){ + /* failed to open/create the lock directory */ int tErrno = errno; if( EEXIST == tErrno ){ rc = SQLITE_BUSY; } else { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); @@ -26553,11 +26916,10 @@ pFile->lastErrno = tErrno; } } return rc; } - robust_close(pFile, fd, __LINE__); /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; return rc; } @@ -26572,10 +26934,11 @@ ** When the locking level reaches NO_LOCK, delete the lock file. */ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; char *zLockFile = (char *)pFile->lockingContext; + int rc; assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, pFile->eFileLock, getpid())); assert( eFileLock<=SHARED_LOCK ); @@ -26593,13 +26956,15 @@ return SQLITE_OK; } /* To fully unlock the database, delete the lock file */ assert( eFileLock==NO_LOCK ); - if( osUnlink(zLockFile) ){ - int rc = 0; + rc = osRmdir(zLockFile); + if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile); + if( rc<0 ){ int tErrno = errno; + rc = 0; if( ENOENT != tErrno ){ rc = SQLITE_IOERR_UNLOCK; } if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -27531,39 +27896,52 @@ ** To avoid stomping the errno value on a failed read the lastErrno value ** is set before returning. */ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ int got; + int prior = 0; #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) i64 newOffset; #endif TIMER_START; + do{ #if defined(USE_PREAD) - do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); - SimulateIOError( got = -1 ); + got = osPread(id->h, pBuf, cnt, offset); + SimulateIOError( got = -1 ); #elif defined(USE_PREAD64) - do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR); - SimulateIOError( got = -1 ); -#else - newOffset = lseek(id->h, offset, SEEK_SET); - SimulateIOError( newOffset-- ); - if( newOffset!=offset ){ - if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; - }else{ - ((unixFile*)id)->lastErrno = 0; - } - return -1; - } - do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); -#endif - TIMER_END; - if( got<0 ){ - ((unixFile*)id)->lastErrno = errno; - } - OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); - return got; + got = osPread64(id->h, pBuf, cnt, offset); + SimulateIOError( got = -1 ); +#else + newOffset = lseek(id->h, offset, SEEK_SET); + SimulateIOError( newOffset-- ); + if( newOffset!=offset ){ + if( newOffset == -1 ){ + ((unixFile*)id)->lastErrno = errno; + }else{ + ((unixFile*)id)->lastErrno = 0; + } + return -1; + } + got = osRead(id->h, pBuf, cnt); +#endif + if( got==cnt ) break; + if( got<0 ){ + if( errno==EINTR ){ got = 1; continue; } + prior = 0; + ((unixFile*)id)->lastErrno = errno; + break; + }else if( got>0 ){ + cnt -= got; + offset += got; + prior += got; + pBuf = (void*)(got + (char*)pBuf); + } + }while( got>0 ); + TIMER_END; + OSTRACE(("READ %-3d %5d %7lld %llu\n", + id->h, got+prior, offset-prior, TIMER_ELAPSED)); + return got+prior; } /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes @@ -28063,10 +28441,26 @@ } } return SQLITE_OK; } + +/* +** If *pArg is inititially negative then this is a query. Set *pArg to +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. +** +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. +*/ +static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ + if( *pArg<0 ){ + *pArg = (pFile->ctrlFlags & mask)!=0; + }else if( (*pArg)==0 ){ + pFile->ctrlFlags &= ~mask; + }else{ + pFile->ctrlFlags |= mask; + } +} /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ @@ -28090,18 +28484,19 @@ rc = fcntlSizeHint(pFile, *(i64 *)pArg); SimulateIOErrorBenign(0); return rc; } case SQLITE_FCNTL_PERSIST_WAL: { - int bPersist = *(int*)pArg; - if( bPersist<0 ){ - *(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0; - }else if( bPersist==0 ){ - pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL; - }else{ - pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL; - } + unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg); + return SQLITE_OK; + } + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { + unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg); + return SQLITE_OK; + } + case SQLITE_FCNTL_VFSNAME: { + *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName); return SQLITE_OK; } #ifndef NDEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and @@ -28117,13 +28512,10 @@ case SQLITE_SET_LOCKPROXYFILE: case SQLITE_GET_LOCKPROXYFILE: { return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ - case SQLITE_FCNTL_SYNC_OMITTED: { - return SQLITE_OK; /* A no-op */ - } } return SQLITE_NOTFOUND; } /* @@ -28134,21 +28526,35 @@ ** SQLite code assumes this function cannot fail. It also assumes that ** if two files are created in the same file-system directory (i.e. ** a database and its journal file) that the sector size will be the ** same for both. */ -static int unixSectorSize(sqlite3_file *NotUsed){ - UNUSED_PARAMETER(NotUsed); +static int unixSectorSize(sqlite3_file *pFile){ + (void)pFile; return SQLITE_DEFAULT_SECTOR_SIZE; } /* -** Return the device characteristics for the file. This is always 0 for unix. +** Return the device characteristics for the file. +** +** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. +** However, that choice is contraversial since technically the underlying +** file system does not always provide powersafe overwrites. (In other +** words, after a power-loss event, parts of the file that were never +** written might end up being altered.) However, non-PSOW behavior is very, +** very rare. And asserting PSOW makes a large reduction in the amount +** of required I/O for journaling, since a lot of padding is eliminated. +** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control +** available to turn it off and URI query parameter available to turn it off. */ -static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ - UNUSED_PARAMETER(NotUsed); - return 0; +static int unixDeviceCharacteristics(sqlite3_file *id){ + unixFile *p = (unixFile*)id; + if( p->ctrlFlags & UNIXFILE_PSOW ){ + return SQLITE_IOCAP_POWERSAFE_OVERWRITE; + }else{ + return 0; + } } #ifndef SQLITE_OMIT_WAL @@ -28399,20 +28805,20 @@ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } #ifdef SQLITE_SHM_DIRECTORY - nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30; + nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; #else - nShmFilename = 5 + (int)strlen(pDbFd->zPath); + nShmFilename = 6 + (int)strlen(pDbFd->zPath); #endif pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename ); if( pShmNode==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } - memset(pShmNode, 0, sizeof(*pShmNode)); + memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1]; #ifdef SQLITE_SHM_DIRECTORY sqlite3_snprintf(nShmFilename, zShmFilename, SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", (u32)sStat.st_ino, (u32)sStat.st_dev); @@ -28428,20 +28834,17 @@ rc = SQLITE_NOMEM; goto shm_open_err; } if( pInode->bProcessLock==0 ){ - pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, - (sStat.st_mode & 0777)); + int openFlags = O_RDWR | O_CREAT; + if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ + openFlags = O_RDONLY; + pShmNode->isReadonly = 1; + } + pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777)); if( pShmNode->h<0 ){ - const char *zRO; - zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm"); - if( zRO && sqlite3GetBoolean(zRO) ){ - pShmNode->h = robust_open(zShmFilename, O_RDONLY, - (sStat.st_mode & 0777)); - pShmNode->isReadonly = 1; - } if( pShmNode->h<0 ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); goto shm_open_err; } } @@ -29094,28 +29497,20 @@ ** Initialize the contents of the unixFile structure pointed to by pId. */ static int fillInUnixFile( sqlite3_vfs *pVfs, /* Pointer to vfs object */ int h, /* Open file descriptor of file being opened */ - int syncDir, /* True to sync directory on first sync */ sqlite3_file *pId, /* Write to the unixFile structure here */ const char *zFilename, /* Name of the file being opened */ - int noLock, /* Omit locking if true */ - int isDelete, /* Delete on close if true */ - int isReadOnly /* True if the file is opened read-only */ + int ctrlFlags /* Zero or more UNIXFILE_* values */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; assert( pNew->pInode==NULL ); - /* Parameter isDelete is only used on vxworks. Express this explicitly - ** here to prevent compiler warnings about unused parameters. - */ - UNUSED_PARAMETER(isDelete); - /* Usually the path zFilename should not be a relative pathname. The ** exception is when opening the proxy "conch" file in builds that ** include the special Apple locking styles. */ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE @@ -29123,34 +29518,35 @@ || pVfs->pAppData==(void*)&autolockIoFinder ); #else assert( zFilename==0 || zFilename[0]=='/' ); #endif + /* No locking occurs in temporary files */ + assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); + OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; + pNew->pVfs = pVfs; pNew->zPath = zFilename; + pNew->ctrlFlags = (u8)ctrlFlags; + if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), + "psow", SQLITE_POWERSAFE_OVERWRITE) ){ + pNew->ctrlFlags |= UNIXFILE_PSOW; + } if( memcmp(pVfs->zName,"unix-excl",10)==0 ){ - pNew->ctrlFlags = UNIXFILE_EXCL; - }else{ - pNew->ctrlFlags = 0; - } - if( isReadOnly ){ - pNew->ctrlFlags |= UNIXFILE_RDONLY; - } - if( syncDir ){ - pNew->ctrlFlags |= UNIXFILE_DIRSYNC; + pNew->ctrlFlags |= UNIXFILE_EXCL; } #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ - noLock = 1; + ctrlFlags |= UNIXFILE_NOLOCK; rc = SQLITE_NOMEM; } #endif - if( noLock ){ + if( ctrlFlags & UNIXFILE_NOLOCK ){ pLockingStyle = &nolockIoMethods; }else{ pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); #if SQLITE_ENABLE_LOCKING_STYLE /* Cache zFilename in the locking context (AFP and dotlock override) for @@ -29224,10 +29620,11 @@ /* Dotfile locking uses the file path so it needs to be included in ** the dotlockLockingContext */ char *zLockFile; int nFilename; + assert( zFilename!=0 ); nFilename = (int)strlen(zFilename) + 6; zLockFile = (char *)sqlite3_malloc(nFilename); if( zLockFile==0 ){ rc = SQLITE_NOMEM; }else{ @@ -29266,11 +29663,11 @@ if( h>=0 ) robust_close(pNew, h, __LINE__); h = -1; osUnlink(zFilename); isDelete = 0; } - pNew->isDelete = isDelete; + if( isDelete ) pNew->ctrlFlags |= UNIXFILE_DELETE; #endif if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pNew->pMethod = pLockingStyle; @@ -29331,22 +29728,23 @@ if( zDir==0 ) zDir = "."; /* Check that the output buffer is large enough for the temporary file ** name. If it is not, return SQLITE_ERROR. */ - if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){ + if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){ return SQLITE_ERROR; } do{ - sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); + sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); j = (int)strlen(zBuf); sqlite3_randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; + zBuf[j+1] = 0; }while( osAccess(zBuf,0)==0 ); return SQLITE_OK; } #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) @@ -29458,16 +29856,24 @@ ** "-journal" ** "-wal" ** "-journalNN" ** "-walNN" ** - ** where NN is a 4 digit decimal number. The NN naming schemes are + ** where NN is a decimal number. The NN naming schemes are ** used by the test_multiplex.c module. */ nDb = sqlite3Strlen30(zPath) - 1; - while( nDb>0 && zPath[nDb]!='-' ) nDb--; - if( nDb==0 ) return SQLITE_OK; +#ifdef SQLITE_ENABLE_8_3_NAMES + while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--; + if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK; +#else + while( zPath[nDb]!='-' ){ + assert( nDb>0 ); + assert( zPath[nDb]!='\n' ); + nDb--; + } +#endif memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; if( 0==osStat(zDb, &sStat) ){ *pMode = sStat.st_mode & 0777; @@ -29513,10 +29919,11 @@ int fd = -1; /* File descriptor returned by open() */ int openFlags = 0; /* Flags to pass to open() */ int eType = flags&0xFFFFFF00; /* Type of file to open */ int noLock; /* True to omit locking primitives */ int rc = SQLITE_OK; /* Function Return Code */ + int ctrlFlags = 0; /* UNIXFILE_* flags */ int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); int isReadonly = (flags & SQLITE_OPEN_READONLY); @@ -29539,11 +29946,11 @@ )); /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ - char zTmpname[MAX_PATHNAME+1]; + char zTmpname[MAX_PATHNAME+2]; const char *zName = zPath; /* Check the following statements are true: ** ** (a) Exactly one of the READWRITE and READONLY flags must be set, and @@ -29582,18 +29989,28 @@ if( !pUnused ){ return SQLITE_NOMEM; } } p->pUnused = pUnused; + + /* Database filenames are double-zero terminated if they are not + ** URIs with parameters. Hence, they can always be passed into + ** sqlite3_uri_parameter(). */ + assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); + }else if( !zName ){ /* If zName is NULL, the upper layer is requesting a temp file. */ assert(isDelete && !syncDir); - rc = unixGetTempname(MAX_PATHNAME+1, zTmpname); + rc = unixGetTempname(MAX_PATHNAME+2, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zName = zTmpname; + + /* Generated temporary filenames are always double-zero terminated + ** for use by sqlite3_uri_parameter(). */ + assert( zName[strlen(zName)+1]==0 ); } /* Determine the value of the flags parameter passed to POSIX function ** open(). These must be calculated even if open() is not called, as ** they may be stored as part of the file handle and used by the @@ -29666,11 +30083,18 @@ } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; } #endif - + + /* Set up appropriate ctrlFlags */ + if( isDelete ) ctrlFlags |= UNIXFILE_DELETE; + if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY; + if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK; + if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC; + if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI; + #if SQLITE_ENABLE_LOCKING_STYLE #if SQLITE_PREFER_PROXY_LOCKING isAutoProxy = 1; #endif if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ @@ -29696,12 +30120,11 @@ goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock, - isDelete, isReadonly); + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); if( rc==SQLITE_OK ){ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); if( rc!=SQLITE_OK ){ /* Use unixClose to clean up the resources added in fillInUnixFile ** and clear all the structure's references. Specifically, @@ -29714,12 +30137,12 @@ goto open_finished; } } #endif - rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock, - isDelete, isReadonly); + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); + open_finished: if( rc!=SQLITE_OK ){ sqlite3_free(p->pUnused); } return rc; @@ -29740,11 +30163,11 @@ SimulateIOError(return SQLITE_IOERR_DELETE); if( osUnlink(zPath)==(-1) && errno!=ENOENT ){ return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); } #ifndef SQLITE_DISABLE_DIRSYNC - if( dirSync ){ + if( (dirSync & 1)!=0 ){ int fd; rc = osOpenDirectory(zPath, &fd); if( rc==SQLITE_OK ){ #if OS_VXWORKS if( fsync(fd)==-1 ) @@ -29995,14 +30418,16 @@ ** the current time and date as a Julian Day number times 86_400_000. In ** other words, write into *piNow the number of milliseconds since the Julian ** epoch of noon in Greenwich on November 24, 4714 B.C according to the ** proleptic Gregorian calendar. ** -** On success, return 0. Return 1 if the time and date cannot be found. +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date +** cannot be found. */ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; + int rc = SQLITE_OK; #if defined(NO_GETTOD) time_t t; time(&t); *piNow = ((sqlite3_int64)t)*1000 + unixEpoch; #elif OS_VXWORKS @@ -30009,34 +30434,38 @@ struct timespec sNow; clock_gettime(CLOCK_REALTIME, &sNow); *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; #else struct timeval sNow; - gettimeofday(&sNow, 0); - *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; + if( gettimeofday(&sNow, 0)==0 ){ + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; + }else{ + rc = SQLITE_ERROR; + } #endif #ifdef SQLITE_TEST if( sqlite3_current_time ){ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } #endif UNUSED_PARAMETER(NotUsed); - return 0; + return rc; } /* ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ - sqlite3_int64 i; + sqlite3_int64 i = 0; + int rc; UNUSED_PARAMETER(NotUsed); - unixCurrentTimeInt64(0, &i); + rc = unixCurrentTimeInt64(0, &i); *prNow = i/86400000.0; - return 0; + return rc; } /* ** We added the xGetLastError() method with the intention of providing ** better low-level error messages when operating-system problems come up @@ -30285,11 +30714,11 @@ if( lockPath[i] == '/' && (i - start > 0) ){ /* only mkdir if leaf dir != "." or "/" or ".." */ if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ buf[i]='\0'; - if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ + if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ int err=errno; if( err!=EEXIST ) { OSTRACE(("CREATELOCKPATH FAILED creating %s, " "'%s' proxy lock path=%s pid=%d\n", buf, strerror(err), lockPath, getpid())); @@ -30380,11 +30809,11 @@ dummyVfs.zName = "dummy"; pUnused->fd = fd; pUnused->flags = openFlags; pNew->pUnused = pUnused; - rc = fillInUnixFile(&dummyVfs, fd, 0, (sqlite3_file*)pNew, path, 0, 0, 0); + rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0); if( rc==SQLITE_OK ){ *ppFile = pNew; return SQLITE_OK; } end_create_proxy: @@ -31321,11 +31750,11 @@ }; unsigned int i; /* Loop counter */ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==18 ); + assert( ArraySize(aSyscall)==20 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); } @@ -31357,54 +31786,18 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** -** This file contains code that is specific to windows. -*/ -#if SQLITE_OS_WIN /* This file is used for windows only */ - - -/* -** A Note About Memory Allocation: -** -** This driver uses malloc()/free() directly rather than going through -** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers -** are designed for use on embedded systems where memory is scarce and -** malloc failures happen frequently. Win32 does not typically run on -** embedded systems, and when it does the developers normally have bigger -** problems to worry about than running out of memory. So there is not -** a compelling need to use the wrappers. -** -** But there is a good reason to not use the wrappers. If we use the -** wrappers then we will get simulated malloc() failures within this -** driver. And that causes all kinds of problems for our tests. We -** could enhance SQLite to deal with simulated malloc failures within -** the OS driver, but the code to deal with those failure would not -** be exercised on Linux (which does not need to malloc() in the driver) -** and so we would have difficulty writing coverage tests for that -** code. Better to leave the code out, we think. -** -** The point of this discussion is as follows: When creating a new -** OS layer for an embedded system, if you use this file as an example, -** avoid the use of malloc()/free(). Those routines work ok on windows -** desktops but not so well in embedded systems. -*/ - -#include +** This file contains code that is specific to Windows. +*/ +#if SQLITE_OS_WIN /* This file is used for Windows only */ #ifdef __CYGWIN__ # include #endif -/* -** Macros used to determine whether or not to use threads. -*/ -#if defined(THREADSAFE) && THREADSAFE -# define SQLITE_W32_THREADS 1 -#endif - /* ** Include code that is common to all os_*.c files */ /************** Include os_common.h in the middle of os_win.c ****************/ /************** Begin file os_common.h ***************************************/ @@ -31615,25 +32008,16 @@ /************** End of os_common.h *******************************************/ /************** Continuing where we left off in os_win.c *********************/ /* -** Some microsoft compilers lack this definition. +** Some Microsoft compilers lack this definition. */ #ifndef INVALID_FILE_ATTRIBUTES # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif -/* -** Determine if we are dealing with WindowsCE - which has a much -** reduced API. -*/ -#if SQLITE_OS_WINCE -# define AreFileApisANSI() 1 -# define FormatMessageW(a,b,c,d,e,f,g) 0 -#endif - /* Forward references */ typedef struct winShm winShm; /* A connection to shared-memory */ typedef struct winShmNode winShmNode; /* A region of shared-memory */ /* @@ -31658,25 +32042,30 @@ const sqlite3_io_methods *pMethod; /*** Must be first ***/ sqlite3_vfs *pVfs; /* The VFS used to open this file */ HANDLE h; /* Handle for accessing the file */ u8 locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ - u8 bPersistWal; /* True to persist WAL files */ + u8 ctrlFlags; /* Flags. See WINFILE_* below */ DWORD lastErrno; /* The Windows errno from the last I/O error */ - DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ #if SQLITE_OS_WINCE - WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ + LPWSTR zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif }; +/* +** Allowed values for winFile.ctrlFlags +*/ +#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ +#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ + /* * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the * various Win32 API heap functions instead of our own. */ #ifdef SQLITE_WIN32_MALLOC @@ -31744,25 +32133,17 @@ static void winMemShutdown(void *pAppData); SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void); #endif /* SQLITE_WIN32_MALLOC */ -/* -** Forward prototypes. -*/ -static int getSectorSize( - sqlite3_vfs *pVfs, - const char *zRelative /* UTF-8 file name */ -); - /* ** The following variable is (normally) set once and never changes -** thereafter. It records whether the operating system is Win95 +** thereafter. It records whether the operating system is Win9x ** or WinNT. ** ** 0: Operating system unknown. -** 1: Operating system is Win95. +** 1: Operating system is Win9x. ** 2: Operating system is WinNT. ** ** In order to facilitate testing on a WinNT system, the test fixture ** can manually set this value to 1 to emulate Win98 behavior. */ @@ -31769,10 +32150,540 @@ #ifdef SQLITE_TEST SQLITE_API int sqlite3_os_type = 0; #else static int sqlite3_os_type = 0; #endif + +/* +** Many system calls are accessed through pointer-to-functions so that +** they may be overridden at runtime to facilitate fault injection during +** testing and sandboxing. The following array holds the names and pointers +** to all overrideable system calls. +*/ +#if !SQLITE_OS_WINCE +# define SQLITE_WIN32_HAS_ANSI +#endif + +#if SQLITE_OS_WINCE || SQLITE_OS_WINNT +# define SQLITE_WIN32_HAS_WIDE +#endif + +#ifndef SYSCALL +# define SYSCALL sqlite3_syscall_ptr +#endif + +#if SQLITE_OS_WINCE +/* +** These macros are necessary because Windows CE does not natively support the +** Win32 APIs LockFile, UnlockFile, and LockFileEx. + */ + +# define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e) +# define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e) +# define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f) + +/* +** These are the special syscall hacks for Windows CE. The locking related +** defines here refer to the macros defined just above. + */ + +# define osAreFileApisANSI() 1 +# define osLockFile LockFile +# define osUnlockFile UnlockFile +# define osLockFileEx LockFileEx +#endif + +static struct win_syscall { + const char *zName; /* Name of the sytem call */ + sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ + sqlite3_syscall_ptr pDefault; /* Default value */ +} aSyscall[] = { +#if !SQLITE_OS_WINCE + { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, + +#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent) +#else + { "AreFileApisANSI", (SYSCALL)0, 0 }, +#endif + +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "CharLowerW", (SYSCALL)CharLowerW, 0 }, +#else + { "CharLowerW", (SYSCALL)0, 0 }, +#endif + +#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent) + +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "CharUpperW", (SYSCALL)CharUpperW, 0 }, +#else + { "CharUpperW", (SYSCALL)0, 0 }, +#endif + +#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent) + + { "CloseHandle", (SYSCALL)CloseHandle, 0 }, + +#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "CreateFileA", (SYSCALL)CreateFileA, 0 }, +#else + { "CreateFileA", (SYSCALL)0, 0 }, +#endif + +#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "CreateFileW", (SYSCALL)CreateFileW, 0 }, +#else + { "CreateFileW", (SYSCALL)0, 0 }, +#endif + +#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) + + { "CreateFileMapping", (SYSCALL)CreateFileMapping, 0 }, + +#define osCreateFileMapping ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ + DWORD,DWORD,DWORD,LPCTSTR))aSyscall[6].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, +#else + { "CreateFileMappingW", (SYSCALL)0, 0 }, +#endif + +#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ + DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, +#else + { "CreateMutexW", (SYSCALL)0, 0 }, +#endif + +#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ + LPCWSTR))aSyscall[8].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "DeleteFileA", (SYSCALL)DeleteFileA, 0 }, +#else + { "DeleteFileA", (SYSCALL)0, 0 }, +#endif + +#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "DeleteFileW", (SYSCALL)DeleteFileW, 0 }, +#else + { "DeleteFileW", (SYSCALL)0, 0 }, +#endif + +#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent) + +#if SQLITE_OS_WINCE + { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 }, +#else + { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, +#endif + +#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ + LPFILETIME))aSyscall[11].pCurrent) + +#if SQLITE_OS_WINCE + { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 }, +#else + { "FileTimeToSystemTime", (SYSCALL)0, 0 }, +#endif + +#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ + LPSYSTEMTIME))aSyscall[12].pCurrent) + + { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, + +#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "FormatMessageA", (SYSCALL)FormatMessageA, 0 }, +#else + { "FormatMessageA", (SYSCALL)0, 0 }, +#endif + +#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \ + DWORD,va_list*))aSyscall[14].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "FormatMessageW", (SYSCALL)FormatMessageW, 0 }, +#else + { "FormatMessageW", (SYSCALL)0, 0 }, +#endif + +#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ + DWORD,va_list*))aSyscall[15].pCurrent) + + { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, + +#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent) + + { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 }, + +#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) + { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 }, +#else + { "GetDiskFreeSpaceA", (SYSCALL)0, 0 }, +#endif + +#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ + LPDWORD))aSyscall[18].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, +#else + { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, +#endif + +#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \ + LPDWORD))aSyscall[19].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 }, +#else + { "GetFileAttributesA", (SYSCALL)0, 0 }, +#endif + +#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, +#else + { "GetFileAttributesW", (SYSCALL)0, 0 }, +#endif + +#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 }, +#else + { "GetFileAttributesExW", (SYSCALL)0, 0 }, +#endif + +#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ + LPVOID))aSyscall[22].pCurrent) + + { "GetFileSize", (SYSCALL)GetFileSize, 0 }, + +#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) + { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 }, +#else + { "GetFullPathNameA", (SYSCALL)0, 0 }, +#endif + +#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ + LPSTR*))aSyscall[24].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, +#else + { "GetFullPathNameW", (SYSCALL)0, 0 }, +#endif + +#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ + LPWSTR*))aSyscall[25].pCurrent) + + { "GetLastError", (SYSCALL)GetLastError, 0 }, + +#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) + +#if SQLITE_OS_WINCE + /* The GetProcAddressA() routine is only available on Windows CE. */ + { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, +#else + /* All other Windows platforms expect GetProcAddress() to take + ** an ANSI string regardless of the _UNICODE setting */ + { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, +#endif + +#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ + LPCSTR))aSyscall[27].pCurrent) + + { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, + +#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent) + + { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, + +#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent) + +#if !SQLITE_OS_WINCE + { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, +#else + { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, +#endif + +#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \ + LPFILETIME))aSyscall[30].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetTempPathA", (SYSCALL)GetTempPathA, 0 }, +#else + { "GetTempPathA", (SYSCALL)0, 0 }, +#endif + +#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, +#else + { "GetTempPathW", (SYSCALL)0, 0 }, +#endif + +#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent) + + { "GetTickCount", (SYSCALL)GetTickCount, 0 }, + +#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, +#else + { "GetVersionExA", (SYSCALL)0, 0 }, +#endif + +#define osGetVersionExA ((BOOL(WINAPI*)( \ + LPOSVERSIONINFOA))aSyscall[34].pCurrent) + + { "HeapAlloc", (SYSCALL)HeapAlloc, 0 }, + +#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ + SIZE_T))aSyscall[35].pCurrent) + + { "HeapCreate", (SYSCALL)HeapCreate, 0 }, + +#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ + SIZE_T))aSyscall[36].pCurrent) + + { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, + +#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[37].pCurrent) + + { "HeapFree", (SYSCALL)HeapFree, 0 }, + +#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[38].pCurrent) + + { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 }, + +#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \ + SIZE_T))aSyscall[39].pCurrent) + + { "HeapSize", (SYSCALL)HeapSize, 0 }, + +#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ + LPCVOID))aSyscall[40].pCurrent) + + { "HeapValidate", (SYSCALL)HeapValidate, 0 }, + +#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ + LPCVOID))aSyscall[41].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, +#else + { "LoadLibraryA", (SYSCALL)0, 0 }, +#endif + +#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, +#else + { "LoadLibraryW", (SYSCALL)0, 0 }, +#endif + +#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[43].pCurrent) + + { "LocalFree", (SYSCALL)LocalFree, 0 }, + +#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[44].pCurrent) + +#if !SQLITE_OS_WINCE + { "LockFile", (SYSCALL)LockFile, 0 }, + +#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[45].pCurrent) +#else + { "LockFile", (SYSCALL)0, 0 }, +#endif + +#if !SQLITE_OS_WINCE + { "LockFileEx", (SYSCALL)LockFileEx, 0 }, + +#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \ + LPOVERLAPPED))aSyscall[46].pCurrent) +#else + { "LockFileEx", (SYSCALL)0, 0 }, +#endif + + { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, + +#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + SIZE_T))aSyscall[47].pCurrent) + + { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 }, + +#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \ + int))aSyscall[48].pCurrent) + + { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, + +#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ + LARGE_INTEGER*))aSyscall[49].pCurrent) + + { "ReadFile", (SYSCALL)ReadFile, 0 }, + +#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ + LPOVERLAPPED))aSyscall[50].pCurrent) + + { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, + +#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[51].pCurrent) + + { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, + +#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ + DWORD))aSyscall[52].pCurrent) + + { "Sleep", (SYSCALL)Sleep, 0 }, + +#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[53].pCurrent) + + { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, + +#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ + LPFILETIME))aSyscall[54].pCurrent) + +#if !SQLITE_OS_WINCE + { "UnlockFile", (SYSCALL)UnlockFile, 0 }, + +#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[55].pCurrent) +#else + { "UnlockFile", (SYSCALL)0, 0 }, +#endif + +#if !SQLITE_OS_WINCE + { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 }, + +#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + LPOVERLAPPED))aSyscall[56].pCurrent) +#else + { "UnlockFileEx", (SYSCALL)0, 0 }, +#endif + + { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, + +#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[57].pCurrent) + + { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, + +#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ + LPCSTR,LPBOOL))aSyscall[58].pCurrent) + + { "WriteFile", (SYSCALL)WriteFile, 0 }, + +#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ + LPOVERLAPPED))aSyscall[59].pCurrent) + +}; /* End of the overrideable system calls */ + +/* +** This is the xSetSystemCall() method of sqlite3_vfs for all of the +** "win32" VFSes. Return SQLITE_OK opon successfully updating the +** system call pointer, or SQLITE_NOTFOUND if there is no configurable +** system call named zName. +*/ +static int winSetSystemCall( + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ + const char *zName, /* Name of system call to override */ + sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ +){ + unsigned int i; + int rc = SQLITE_NOTFOUND; + + UNUSED_PARAMETER(pNotUsed); + if( zName==0 ){ + /* If no zName is given, restore all system calls to their default + ** settings and return NULL + */ + rc = SQLITE_OK; + for(i=0; i=0 ); - p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); + p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); if( !p ){ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p", - nBytes, GetLastError(), (void*)hHeap); + nBytes, osGetLastError(), (void*)hHeap); } return p; } /* @@ -31830,16 +32741,16 @@ winMemAssertMagic(); hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #ifdef SQLITE_WIN32_MALLOC_VALIDATE - assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); + assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ - if( !HeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ + if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p", - pPrior, GetLastError(), (void*)hHeap); + pPrior, osGetLastError(), (void*)hHeap); } } /* ** Change the size of an existing memory allocation @@ -31851,22 +32762,22 @@ winMemAssertMagic(); hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #ifdef SQLITE_WIN32_MALLOC_VALIDATE - assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); + assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif assert( nBytes>=0 ); if( !pPrior ){ - p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); + p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); }else{ - p = HeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); + p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); } if( !p ){ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p", - pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, GetLastError(), - (void*)hHeap); + pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(), + (void*)hHeap); } return p; } /* @@ -31879,17 +32790,17 @@ winMemAssertMagic(); hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #ifdef SQLITE_WIN32_MALLOC_VALIDATE - assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); + assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif if( !p ) return 0; - n = HeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); + n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); if( n==(SIZE_T)-1 ){ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p", - p, GetLastError(), (void*)hHeap); + p, osGetLastError(), (void*)hHeap); return 0; } return (int)n; } @@ -31907,26 +32818,26 @@ winMemData *pWinMemData = (winMemData *)pAppData; if( !pWinMemData ) return SQLITE_ERROR; assert( pWinMemData->magic==WINMEM_MAGIC ); if( !pWinMemData->hHeap ){ - pWinMemData->hHeap = HeapCreate(SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, - SQLITE_WIN32_HEAP_MAX_SIZE); + pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, + SQLITE_WIN32_HEAP_INIT_SIZE, + SQLITE_WIN32_HEAP_MAX_SIZE); if( !pWinMemData->hHeap ){ sqlite3_log(SQLITE_NOMEM, "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u", - GetLastError(), SQLITE_WIN32_HEAP_FLAGS, SQLITE_WIN32_HEAP_INIT_SIZE, - SQLITE_WIN32_HEAP_MAX_SIZE); + osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, + SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE); return SQLITE_NOMEM; } pWinMemData->bOwned = TRUE; } assert( pWinMemData->hHeap!=0 ); assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); #ifdef SQLITE_WIN32_MALLOC_VALIDATE - assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); + assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif return SQLITE_OK; } /* @@ -31937,16 +32848,16 @@ if( !pWinMemData ) return; if( pWinMemData->hHeap ){ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); #ifdef SQLITE_WIN32_MALLOC_VALIDATE - assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); + assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif if( pWinMemData->bOwned ){ - if( !HeapDestroy(pWinMemData->hHeap) ){ + if( !osHeapDestroy(pWinMemData->hHeap) ){ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p", - GetLastError(), (void*)pWinMemData->hHeap); + osGetLastError(), (void*)pWinMemData->hHeap); } pWinMemData->bOwned = FALSE; } pWinMemData->hHeap = NULL; } @@ -31978,197 +32889,219 @@ sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32()); } #endif /* SQLITE_WIN32_MALLOC */ /* -** Convert a UTF-8 string to microsoft unicode (UTF-16?). +** Convert a UTF-8 string to Microsoft Unicode (UTF-16?). ** ** Space to hold the returned string is obtained from malloc. */ -static WCHAR *utf8ToUnicode(const char *zFilename){ +static LPWSTR utf8ToUnicode(const char *zFilename){ int nChar; - WCHAR *zWideFilename; + LPWSTR zWideFilename; - nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); - zWideFilename = malloc( nChar*sizeof(zWideFilename[0]) ); + nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); + if( nChar==0 ){ + return 0; + } + zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) ); if( zWideFilename==0 ){ return 0; } - nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); + nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, + nChar); if( nChar==0 ){ - free(zWideFilename); + sqlite3_free(zWideFilename); zWideFilename = 0; } return zWideFilename; } /* -** Convert microsoft unicode to UTF-8. Space to hold the returned string is -** obtained from malloc(). +** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is +** obtained from sqlite3_malloc(). */ -static char *unicodeToUtf8(const WCHAR *zWideFilename){ +static char *unicodeToUtf8(LPCWSTR zWideFilename){ int nByte; char *zFilename; - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); - zFilename = malloc( nByte ); + nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); + if( nByte == 0 ){ + return 0; + } + zFilename = sqlite3_malloc( nByte ); if( zFilename==0 ){ return 0; } - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, - 0, 0); + nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, + 0, 0); if( nByte == 0 ){ - free(zFilename); + sqlite3_free(zFilename); zFilename = 0; } return zFilename; } /* -** Convert an ansi string to microsoft unicode, based on the +** Convert an ANSI string to Microsoft Unicode, based on the ** current codepage settings for file apis. ** ** Space to hold the returned string is obtained -** from malloc. +** from sqlite3_malloc. */ -static WCHAR *mbcsToUnicode(const char *zFilename){ +static LPWSTR mbcsToUnicode(const char *zFilename){ int nByte; - WCHAR *zMbcsFilename; - int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; + LPWSTR zMbcsFilename; + int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); - zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); + nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL, + 0)*sizeof(WCHAR); + if( nByte==0 ){ + return 0; + } + zMbcsFilename = sqlite3_malloc( nByte*sizeof(zMbcsFilename[0]) ); if( zMbcsFilename==0 ){ return 0; } - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); + nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, + nByte); if( nByte==0 ){ - free(zMbcsFilename); + sqlite3_free(zMbcsFilename); zMbcsFilename = 0; } return zMbcsFilename; } /* -** Convert microsoft unicode to multibyte character string, based on the -** user's Ansi codepage. +** Convert Microsoft Unicode to multi-byte character string, based on the +** user's ANSI codepage. ** ** Space to hold the returned string is obtained from -** malloc(). +** sqlite3_malloc(). */ -static char *unicodeToMbcs(const WCHAR *zWideFilename){ +static char *unicodeToMbcs(LPCWSTR zWideFilename){ int nByte; char *zFilename; - int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; + int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; - nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); - zFilename = malloc( nByte ); + nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); + if( nByte == 0 ){ + return 0; + } + zFilename = sqlite3_malloc( nByte ); if( zFilename==0 ){ return 0; } - nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte, - 0, 0); + nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, + nByte, 0, 0); if( nByte == 0 ){ - free(zFilename); + sqlite3_free(zFilename); zFilename = 0; } return zFilename; } /* ** Convert multibyte character string to UTF-8. Space to hold the -** returned string is obtained from malloc(). +** returned string is obtained from sqlite3_malloc(). */ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ char *zFilenameUtf8; - WCHAR *zTmpWide; + LPWSTR zTmpWide; zTmpWide = mbcsToUnicode(zFilename); if( zTmpWide==0 ){ return 0; } zFilenameUtf8 = unicodeToUtf8(zTmpWide); - free(zTmpWide); + sqlite3_free(zTmpWide); return zFilenameUtf8; } /* ** Convert UTF-8 to multibyte character string. Space to hold the -** returned string is obtained from malloc(). +** returned string is obtained from sqlite3_malloc(). */ SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ char *zFilenameMbcs; - WCHAR *zTmpWide; + LPWSTR zTmpWide; zTmpWide = utf8ToUnicode(zFilename); if( zTmpWide==0 ){ return 0; } zFilenameMbcs = unicodeToMbcs(zTmpWide); - free(zTmpWide); + sqlite3_free(zTmpWide); return zFilenameMbcs; } /* ** The return value of getLastErrorMsg ** is zero if the error message fits in the buffer, or non-zero ** otherwise (if the message was truncated). */ -static int getLastErrorMsg(int nBuf, char *zBuf){ +static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ /* FormatMessage returns 0 on failure. Otherwise it ** returns the number of TCHARs written to the output ** buffer, excluding the terminating null char. */ - DWORD error = GetLastError(); DWORD dwLen = 0; char *zOut = 0; if( isNT() ){ - WCHAR *zTempWide = NULL; - dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, - (LPWSTR) &zTempWide, - 0, - 0); + LPWSTR zTempWide = NULL; + dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + (LPWSTR) &zTempWide, + 0, + 0); if( dwLen > 0 ){ /* allocate a buffer and convert to UTF8 */ + sqlite3BeginBenignMalloc(); zOut = unicodeToUtf8(zTempWide); + sqlite3EndBenignMalloc(); /* free the system buffer allocated by FormatMessage */ - LocalFree(zTempWide); + osLocalFree(zTempWide); } /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ char *zTemp = NULL; - dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, - (LPSTR) &zTemp, - 0, - 0); + dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + (LPSTR) &zTemp, + 0, + 0); if( dwLen > 0 ){ /* allocate a buffer and convert to UTF8 */ + sqlite3BeginBenignMalloc(); zOut = sqlite3_win32_mbcs_to_utf8(zTemp); + sqlite3EndBenignMalloc(); /* free the system buffer allocated by FormatMessage */ - LocalFree(zTemp); + osLocalFree(zTemp); } #endif } if( 0 == dwLen ){ - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); + 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); /* free the UTF8 buffer */ - free(zOut); + sqlite3_free(zOut); } return 0; } /* @@ -32184,30 +33117,30 @@ ** The first argument passed to the macro should be the error code that ** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). ** The two subsequent arguments should be the name of the OS function that ** failed and the the associated file-system path, if any. */ -#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__) +#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__) static int winLogErrorAtLine( int errcode, /* SQLite error code */ + DWORD lastErrno, /* Win32 last error */ const char *zFunc, /* Name of OS function that failed */ const char *zPath, /* File path associated with error */ int iLine /* Source line number where error occurred */ ){ char zMsg[500]; /* Human readable error text */ int i; /* Loop counter */ - DWORD iErrno = GetLastError(); /* Error code */ zMsg[0] = 0; - getLastErrorMsg(sizeof(zMsg), zMsg); + getLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); assert( errcode!=SQLITE_OK ); if( zPath==0 ) zPath = ""; for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} zMsg[i] = 0; sqlite3_log(errcode, "os_win.c:%d: (%d) %s(%s) - %s", - iLine, iErrno, zFunc, zPath, zMsg + iLine, lastErrno, zFunc, zPath, zMsg ); return errcode; } @@ -32229,23 +33162,28 @@ /* ** If a ReadFile() or WriteFile() error occurs, invoke this routine ** to see if it should be retried. Return TRUE to retry. Return FALSE ** to give up with an error. */ -static int retryIoerr(int *pnRetry){ - DWORD e; +static int retryIoerr(int *pnRetry, DWORD *pError){ + DWORD e = osGetLastError(); if( *pnRetry>=win32IoerrRetry ){ + if( pError ){ + *pError = e; + } return 0; } - e = GetLastError(); if( e==ERROR_ACCESS_DENIED || e==ERROR_LOCK_VIOLATION || e==ERROR_SHARING_VIOLATION ){ - Sleep(win32IoerrRetryDelay*(1+*pnRetry)); + osSleep(win32IoerrRetryDelay*(1+*pnRetry)); ++*pnRetry; return 1; } + if( pError ){ + *pError = e; + } return 0; } /* ** Log a I/O error retry episode. @@ -32262,11 +33200,11 @@ #if SQLITE_OS_WINCE /************************************************************************* ** This section contains code for WinCE only. */ /* -** WindowsCE does not have a localtime() function. So create a +** Windows CE does not have a localtime() function. So create a ** substitute. */ /* #include */ struct tm *__cdecl localtime(const time_t *t) { @@ -32276,12 +33214,12 @@ sqlite3_int64 t64; t64 = *t; t64 = (t64 + 11644473600)*10000000; uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); uTm.dwHighDateTime= (DWORD)(t64 >> 32); - FileTimeToLocalFileTime(&uTm,&lTm); - FileTimeToSystemTime(&lTm,&pTm); + osFileTimeToLocalFileTime(&uTm,&lTm); + osFileTimeToSystemTime(&lTm,&pTm); y.tm_year = pTm.wYear - 1900; y.tm_mon = pTm.wMonth - 1; y.tm_wday = pTm.wDayOfWeek; y.tm_mday = pTm.wDay; y.tm_hour = pTm.wHour; @@ -32288,17 +33226,10 @@ y.tm_min = pTm.wMinute; y.tm_sec = pTm.wSecond; return &y; } -/* This will never be called, but defined to make the code compile */ -#define GetTempPathA(a,b) - -#define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e) -#define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e) -#define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f) - #define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] /* ** Acquire a lock on the handle h */ @@ -32316,30 +33247,36 @@ /* ** Create the mutex and shared memory used for locking in the file ** descriptor pFile */ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ - WCHAR *zTok; - WCHAR *zName = utf8ToUnicode(zFilename); + LPWSTR zTok; + LPWSTR zName; BOOL bInit = TRUE; + + zName = utf8ToUnicode(zFilename); + if( zName==0 ){ + /* out of memory */ + return FALSE; + } /* Initialize the local lockdata */ - ZeroMemory(&pFile->local, sizeof(pFile->local)); + memset(&pFile->local, 0, sizeof(pFile->local)); /* Replace the backslashes from the filename and lowercase it ** to derive a mutex name. */ - zTok = CharLowerW(zName); + zTok = osCharLowerW(zName); for (;*zTok;zTok++){ if (*zTok == '\\') *zTok = '_'; } /* Create/open the named mutex */ - pFile->hMutex = CreateMutexW(NULL, FALSE, zName); + pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); if (!pFile->hMutex){ - pFile->lastErrno = GetLastError(); - winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename); - free(zName); + pFile->lastErrno = osGetLastError(); + winLogError(SQLITE_ERROR, pFile->lastErrno, "winceCreateLock1", zFilename); + sqlite3_free(zName); return FALSE; } /* Acquire the mutex before continuing */ winceMutexAcquire(pFile->hMutex); @@ -32346,47 +33283,48 @@ /* Since the names of named mutexes, semaphores, file mappings etc are ** case-sensitive, take advantage of that by uppercasing the mutex name ** and using that as the shared filemapping name. */ - CharUpperW(zName); - pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, - PAGE_READWRITE, 0, sizeof(winceLock), - zName); + osCharUpperW(zName); + pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, sizeof(winceLock), + zName); /* Set a flag that indicates we're the first to create the memory so it ** must be zero-initialized */ - if (GetLastError() == ERROR_ALREADY_EXISTS){ + if (osGetLastError() == ERROR_ALREADY_EXISTS){ bInit = FALSE; } - free(zName); + sqlite3_free(zName); /* If we succeeded in making the shared memory handle, map it. */ if (pFile->hShared){ - pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared, + pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); /* If mapping failed, close the shared memory handle and erase it */ if (!pFile->shared){ - pFile->lastErrno = GetLastError(); - winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename); - CloseHandle(pFile->hShared); + pFile->lastErrno = osGetLastError(); + winLogError(SQLITE_ERROR, pFile->lastErrno, + "winceCreateLock2", zFilename); + osCloseHandle(pFile->hShared); pFile->hShared = NULL; } } /* If shared memory could not be created, then close the mutex and fail */ if (pFile->hShared == NULL){ winceMutexRelease(pFile->hMutex); - CloseHandle(pFile->hMutex); + osCloseHandle(pFile->hMutex); pFile->hMutex = NULL; return FALSE; } /* Initialize the shared memory if we're supposed to */ if (bInit) { - ZeroMemory(pFile->shared, sizeof(winceLock)); + memset(pFile->shared, 0, sizeof(winceLock)); } winceMutexRelease(pFile->hMutex); return TRUE; } @@ -32413,22 +33351,22 @@ if (pFile->local.bExclusive){ pFile->shared->bExclusive = FALSE; } /* De-reference and close our copy of the shared memory handle */ - UnmapViewOfFile(pFile->shared); - CloseHandle(pFile->hShared); + osUnmapViewOfFile(pFile->shared); + osCloseHandle(pFile->hShared); /* Done with the mutex */ winceMutexRelease(pFile->hMutex); - CloseHandle(pFile->hMutex); + osCloseHandle(pFile->hMutex); pFile->hMutex = NULL; } } /* -** An implementation of the LockFile() API of windows for wince +** An implementation of the LockFile() API of Windows for CE */ static BOOL winceLockFile( HANDLE *phFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, @@ -32488,11 +33426,11 @@ winceMutexRelease(pFile->hMutex); return bReturn; } /* -** An implementation of the UnlockFile API of windows for wince +** An implementation of the UnlockFile API of Windows for CE */ static BOOL winceUnlockFile( HANDLE *phFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, @@ -32550,11 +33488,11 @@ winceMutexRelease(pFile->hMutex); return bReturn; } /* -** An implementation of the LockFileEx() API of windows for wince +** An implementation of the LockFileEx() API of Windows for CE */ static BOOL winceLockFileEx( HANDLE *phFile, DWORD dwFlags, DWORD dwReserved, @@ -32583,11 +33521,11 @@ ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. ******************************************************************************/ /* -** Some microsoft compilers lack this definition. +** Some Microsoft compilers lack this definition. */ #ifndef INVALID_SET_FILE_POINTER # define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif @@ -32598,10 +33536,11 @@ */ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ LONG upperBits; /* Most sig. 32 bits of new offset */ LONG lowerBits; /* Least sig. 32 bits of new offset */ DWORD dwRet; /* Value returned by SetFilePointer() */ + DWORD lastErrno; /* Value returned by GetLastError() */ upperBits = (LONG)((iOffset>>32) & 0x7fffffff); lowerBits = (LONG)(iOffset & 0xffffffff); /* API oddity: If successful, SetFilePointer() returns a dword @@ -32609,14 +33548,17 @@ ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine ** whether an error has actually occured, it is also necessary to call ** GetLastError(). */ - dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ - pFile->lastErrno = GetLastError(); - winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath); + dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); + + if( (dwRet==INVALID_SET_FILE_POINTER + && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, + "seekWinFile", pFile->zPath); return 1; } return 0; } @@ -32623,11 +33565,11 @@ /* ** Close a file. ** ** It is reported that an attempt to close a handle might sometimes -** fail. This is a very unreasonable result, but windows is notorious +** fail. This is a very unreasonable result, but Windows is notorious ** for being unreasonable so I do not doubt that it might happen. If ** the close fails, we pause for 100 milliseconds and try again. As ** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before ** giving up and returning an error. */ @@ -32638,32 +33580,33 @@ assert( id!=0 ); assert( pFile->pShm==0 ); OSTRACE(("CLOSE %d\n", pFile->h)); do{ - rc = CloseHandle(pFile->h); + rc = osCloseHandle(pFile->h); /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ - }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); + }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (osSleep(100), 1) ); #if SQLITE_OS_WINCE #define WINCE_DELETION_ATTEMPTS 3 winceDestroyLock(pFile); if( pFile->zDeleteOnClose ){ int cnt = 0; while( - DeleteFileW(pFile->zDeleteOnClose)==0 - && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff + osDeleteFileW(pFile->zDeleteOnClose)==0 + && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff && cnt++ < WINCE_DELETION_ATTEMPTS ){ - Sleep(100); /* Wait a little before trying again */ + osSleep(100); /* Wait a little before trying again */ } - free(pFile->zDeleteOnClose); + sqlite3_free(pFile->zDeleteOnClose); } #endif OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OpenCounter(-1); return rc ? SQLITE_OK - : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath); + : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), + "winClose", pFile->zPath); } /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes @@ -32684,14 +33627,16 @@ OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); if( seekWinFile(pFile, offset) ){ return SQLITE_FULL; } - while( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ - if( retryIoerr(&nRetry) ) continue; - pFile->lastErrno = GetLastError(); - return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath); + while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ + DWORD lastErrno; + if( retryIoerr(&nRetry, &lastErrno) ) continue; + pFile->lastErrno = lastErrno; + return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, + "winRead", pFile->zPath); } logIoerr(nRetry); if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[nRead], 0, amt-nRead); @@ -32725,32 +33670,34 @@ rc = seekWinFile(pFile, offset); if( rc==0 ){ u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ int nRem = amt; /* Number of bytes yet to be written */ DWORD nWrite; /* Bytes written by each WriteFile() call */ + DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ while( nRem>0 ){ - if( !WriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ - if( retryIoerr(&nRetry) ) continue; + if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ + if( retryIoerr(&nRetry, &lastErrno) ) continue; break; } if( nWrite<=0 ) break; aRem += nWrite; nRem -= nWrite; } if( nRem>0 ){ - pFile->lastErrno = GetLastError(); + pFile->lastErrno = lastErrno; rc = 1; } } if( rc ){ if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) || ( pFile->lastErrno==ERROR_DISK_FULL )){ return SQLITE_FULL; } - return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath); + return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, + "winWrite", pFile->zPath); }else{ logIoerr(nRetry); } return SQLITE_OK; } @@ -32776,14 +33723,16 @@ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ if( seekWinFile(pFile, nByte) ){ - rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath); - }else if( 0==SetEndOfFile(pFile->h) ){ - pFile->lastErrno = GetLastError(); - rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath); + rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, + "winTruncate1", pFile->zPath); + }else if( 0==osSetEndOfFile(pFile->h) ){ + pFile->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, + "winTruncate2", pFile->zPath); } OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); return rc; } @@ -32844,17 +33793,18 @@ ** no-op */ #ifdef SQLITE_NO_SYNC return SQLITE_OK; #else - rc = FlushFileBuffers(pFile->h); + rc = osFlushFileBuffers(pFile->h); SimulateIOError( rc=FALSE ); if( rc ){ return SQLITE_OK; }else{ - pFile->lastErrno = GetLastError(); - return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath); + pFile->lastErrno = osGetLastError(); + return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, + "winSync", pFile->zPath); } #endif } /* @@ -32862,20 +33812,21 @@ */ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ DWORD upperBits; DWORD lowerBits; winFile *pFile = (winFile*)id; - DWORD error; + DWORD lastErrno; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_FSTAT); - lowerBits = GetFileSize(pFile->h, &upperBits); + lowerBits = osGetFileSize(pFile->h, &upperBits); if( (lowerBits == INVALID_FILE_SIZE) - && ((error = GetLastError()) != NO_ERROR) ) + && ((lastErrno = osGetLastError())!=NO_ERROR) ) { - pFile->lastErrno = error; - return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath); + pFile->lastErrno = lastErrno; + return winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, + "winFileSize", pFile->zPath); } *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; return SQLITE_OK; } @@ -32887,33 +33838,33 @@ #endif /* ** Acquire a reader lock. ** Different API routines are called depending on whether or not this -** is Win95 or WinNT. +** is Win9x or WinNT. */ static int getReadLock(winFile *pFile){ int res; if( isNT() ){ OVERLAPPED ovlp; ovlp.Offset = SHARED_FIRST; ovlp.OffsetHigh = 0; ovlp.hEvent = 0; - res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY, - 0, SHARED_SIZE, 0, &ovlp); + res = osLockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY, + 0, SHARED_SIZE, 0, &ovlp); /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. */ #if SQLITE_OS_WINCE==0 }else{ int lk; sqlite3_randomness(sizeof(lk), &lk); pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); - res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); + res = osLockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); #endif } if( res == 0 ){ - pFile->lastErrno = GetLastError(); + pFile->lastErrno = osGetLastError(); /* No need to log a failure to lock */ } return res; } @@ -32920,22 +33871,24 @@ /* ** Undo a readlock */ static int unlockReadLock(winFile *pFile){ int res; + DWORD lastErrno; if( isNT() ){ - res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + res = osUnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. */ #if SQLITE_OS_WINCE==0 }else{ - res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); + res = osUnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); #endif } - if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){ - pFile->lastErrno = GetLastError(); - winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath); + if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, + "unlockReadLock", pFile->zPath); } return res; } /* @@ -32964,15 +33917,15 @@ ** It is not possible to lower the locking level one step at a time. You ** must go straight to locking level 0. */ static int winLock(sqlite3_file *id, int locktype){ int rc = SQLITE_OK; /* Return code from subroutines */ - int res = 1; /* Result of a windows lock call */ + int res = 1; /* Result of a Windows lock call */ int newLocktype; /* Set pFile->locktype to this value before exiting */ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ winFile *pFile = (winFile*)id; - DWORD error = NO_ERROR; + DWORD lastErrno = NO_ERROR; assert( id!=0 ); OSTRACE(("LOCK %d %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); @@ -32998,20 +33951,23 @@ if( (pFile->locktype==NO_LOCK) || ( (locktype==EXCLUSIVE_LOCK) && (pFile->locktype==RESERVED_LOCK)) ){ int cnt = 3; - while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ - /* Try 3 times to get the pending lock. The pending lock might be - ** held by another reader process who will release it momentarily. + while( cnt-->0 && (res = osLockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ + /* Try 3 times to get the pending lock. This is needed to work + ** around problems caused by indexing and/or anti-virus software on + ** Windows systems. + ** If you are using this code as a model for alternative VFSes, do not + ** copy this retry logic. It is a hack intended for Windows only. */ OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt)); - Sleep(1); + if( cnt ) osSleep(1); } gotPendingLock = res; if( !res ){ - error = GetLastError(); + lastErrno = osGetLastError(); } } /* Acquire a shared lock */ @@ -33019,23 +33975,23 @@ assert( pFile->locktype==NO_LOCK ); res = getReadLock(pFile); if( res ){ newLocktype = SHARED_LOCK; }else{ - error = GetLastError(); + lastErrno = osGetLastError(); } } /* Acquire a RESERVED lock */ if( locktype==RESERVED_LOCK && res ){ assert( pFile->locktype==SHARED_LOCK ); - res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); + res = osLockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( res ){ newLocktype = RESERVED_LOCK; }else{ - error = GetLastError(); + lastErrno = osGetLastError(); } } /* Acquire a PENDING lock */ @@ -33048,25 +34004,25 @@ */ if( locktype==EXCLUSIVE_LOCK && res ){ assert( pFile->locktype>=SHARED_LOCK ); res = unlockReadLock(pFile); OSTRACE(("unreadlock = %d\n", res)); - res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + res = osLockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ newLocktype = EXCLUSIVE_LOCK; }else{ - error = GetLastError(); - OSTRACE(("error-code = %d\n", error)); + lastErrno = osGetLastError(); + OSTRACE(("error-code = %d\n", lastErrno)); getReadLock(pFile); } } /* If we are holding a PENDING lock that ought to be released, then ** release it now. */ if( gotPendingLock && locktype==SHARED_LOCK ){ - UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); + osUnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } /* Update the state of the lock has held in the file descriptor then ** return the appropriate result code. */ @@ -33073,11 +34029,11 @@ if( res ){ rc = SQLITE_OK; }else{ OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h, locktype, newLocktype)); - pFile->lastErrno = error; + pFile->lastErrno = lastErrno; rc = SQLITE_BUSY; } pFile->locktype = (u8)newLocktype; return rc; } @@ -33096,13 +34052,13 @@ assert( id!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc)); }else{ - rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); + rc = osLockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( rc ){ - UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); + osUnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } rc = !rc; OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc)); } *pResOut = rc; @@ -33128,29 +34084,46 @@ assert( locktype<=SHARED_LOCK ); OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ - UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + osUnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ - rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath); + rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), + "winUnlock", pFile->zPath); } } if( type>=RESERVED_LOCK ){ - UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); + osUnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } if( locktype==NO_LOCK && type>=SHARED_LOCK ){ unlockReadLock(pFile); } if( type>=PENDING_LOCK ){ - UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); + osUnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } pFile->locktype = (u8)locktype; return rc; } + +/* +** If *pArg is inititially negative then this is a query. Set *pArg to +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. +** +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. +*/ +static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ + if( *pArg<0 ){ + *pArg = (pFile->ctrlFlags & mask)!=0; + }else if( (*pArg)==0 ){ + pFile->ctrlFlags &= ~mask; + }else{ + pFile->ctrlFlags |= mask; + } +} /* ** Control and query of the open file handle. */ static int winFileControl(sqlite3_file *id, int op, void *pArg){ @@ -33183,19 +34156,19 @@ return rc; } return SQLITE_OK; } case SQLITE_FCNTL_PERSIST_WAL: { - int bPersist = *(int*)pArg; - if( bPersist<0 ){ - *(int*)pArg = pFile->bPersistWal; - }else{ - pFile->bPersistWal = bPersist!=0; - } + winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg); + return SQLITE_OK; + } + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { + winModeBit(pFile, WINFILE_PSOW, (int*)pArg); return SQLITE_OK; } - case SQLITE_FCNTL_SYNC_OMITTED: { + case SQLITE_FCNTL_VFSNAME: { + *(char**)pArg = sqlite3_mprintf("win32"); return SQLITE_OK; } case SQLITE_FCNTL_WIN32_AV_RETRY: { int *a = (int*)pArg; if( a[0]>0 ){ @@ -33223,20 +34196,21 @@ ** if two files are created in the same file-system directory (i.e. ** a database and its journal file) that the sector size will be the ** same for both. */ static int winSectorSize(sqlite3_file *id){ - assert( id!=0 ); - return (int)(((winFile*)id)->sectorSize); + (void)id; + return SQLITE_DEFAULT_SECTOR_SIZE; } /* ** Return a vector of device characteristics. */ static int winDeviceCharacteristics(sqlite3_file *id){ - UNUSED_PARAMETER(id); - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + winFile *p = (winFile*)id; + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | + ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); } #ifndef SQLITE_OMIT_WAL /* @@ -33379,19 +34353,19 @@ memset(&ovlp, 0, sizeof(OVERLAPPED)); ovlp.Offset = ofst; /* Release/Acquire the system-level lock */ if( lockType==_SHM_UNLCK ){ - rc = UnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp); + rc = osUnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp); }else{ - rc = LockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp); + rc = osLockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp); } if( rc!= 0 ){ rc = SQLITE_OK; }else{ - pFile->lastErrno = GetLastError(); + pFile->lastErrno = osGetLastError(); rc = SQLITE_BUSY; } OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", pFile->hFile.h, @@ -33421,27 +34395,29 @@ while( (p = *pp)!=0 ){ if( p->nRef==0 ){ int i; if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; inRegion; i++){ - bRc = UnmapViewOfFile(p->aRegion[i].pMap); + bRc = osUnmapViewOfFile(p->aRegion[i].pMap); OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", - (int)GetCurrentProcessId(), i, + (int)osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); - bRc = CloseHandle(p->aRegion[i].hMap); + bRc = osCloseHandle(p->aRegion[i].hMap); OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", - (int)GetCurrentProcessId(), i, + (int)osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); } if( p->hFile.h != INVALID_HANDLE_VALUE ){ SimulateIOErrorBenign(1); winClose((sqlite3_file *)&p->hFile); SimulateIOErrorBenign(0); } if( deleteFlag ){ SimulateIOErrorBenign(1); + sqlite3BeginBenignMalloc(); winDelete(pVfs, p->zFilename, 0); + sqlite3EndBenignMalloc(); SimulateIOErrorBenign(0); } *pp = p->pNext; sqlite3_free(p->aRegion); sqlite3_free(p); @@ -33469,19 +34445,19 @@ /* Allocate space for the new sqlite3_shm object. Also speculatively ** allocate space for a new winShmNode and filename. */ p = sqlite3_malloc( sizeof(*p) ); - if( p==0 ) return SQLITE_NOMEM; + if( p==0 ) return SQLITE_IOERR_NOMEM; memset(p, 0, sizeof(*p)); nName = sqlite3Strlen30(pDbFd->zPath); - pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 15 ); + pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 17 ); if( pNew==0 ){ sqlite3_free(p); - return SQLITE_NOMEM; + return SQLITE_IOERR_NOMEM; } - memset(pNew, 0, sizeof(*pNew)); + memset(pNew, 0, sizeof(*pNew) + nName + 17); pNew->zFilename = (char*)&pNew[1]; sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); /* Look to see if there is an existing winShmNode that can be used. @@ -33503,31 +34479,31 @@ pShmNode->pNext = winShmNodeList; winShmNodeList = pShmNode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_IOERR_NOMEM; goto shm_open_err; } rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */ 0); if( SQLITE_OK!=rc ){ - rc = SQLITE_CANTOPEN_BKPT; goto shm_open_err; } /* Check to see if another process is holding the dead-man switch. ** If not, truncate the file to zero length. */ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath); + rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), + "winOpenShm", pDbFd->zPath); } } if( rc==SQLITE_OK ){ winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1); @@ -33708,11 +34684,11 @@ } } } sqlite3_mutex_leave(pShmNode->mutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", - p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, + p->id, (int)osGetCurrentProcessId(), p->sharedMask, p->exclMask, rc ? "failed" : "ok")); return rc; } /* @@ -33782,11 +34758,12 @@ ** Check to see if it has been allocated (i.e. if the wal-index file is ** large enough to contain the requested region). */ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath); + rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), + "winShmMap1", pDbFd->zPath); goto shmpage_out; } if( szhFile, nByte); if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath); + rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), + "winShmMap2", pDbFd->zPath); goto shmpage_out; } } /* Map the requested memory region into this processes address space. */ @@ -33815,30 +34793,31 @@ while( pShmNode->nRegion<=iRegion ){ HANDLE hMap; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ - hMap = CreateFileMapping(pShmNode->hFile.h, + hMap = osCreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL ); OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", - (int)GetCurrentProcessId(), pShmNode->nRegion, nByte, + (int)osGetCurrentProcessId(), pShmNode->nRegion, nByte, hMap ? "ok" : "failed")); if( hMap ){ int iOffset = pShmNode->nRegion*szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; - pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, + pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, iOffset - iOffsetShift, szRegion + iOffsetShift ); OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", - (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, - pMap ? "ok" : "failed")); + (int)osGetCurrentProcessId(), pShmNode->nRegion, iOffset, + szRegion, pMap ? "ok" : "failed")); } if( !pMap ){ - pShmNode->lastErrno = GetLastError(); - rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath); - if( hMap ) CloseHandle(hMap); + pShmNode->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno, + "winShmMap3", pDbFd->zPath); + if( hMap ) osCloseHandle(hMap); goto shmpage_out; } pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; @@ -33932,11 +34911,11 @@ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; size_t i, j; - char zTempPath[MAX_PATH+1]; + char zTempPath[MAX_PATH+2]; /* 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. */ @@ -33945,55 +34924,56 @@ if( sqlite3_temp_directory ){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); }else if( isNT() ){ char *zMulti; WCHAR zWidePath[MAX_PATH]; - GetTempPathW(MAX_PATH-30, zWidePath); + osGetTempPathW(MAX_PATH-30, zWidePath); zMulti = unicodeToUtf8(zWidePath); if( zMulti ){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti); - free(zMulti); + sqlite3_free(zMulti); }else{ - return SQLITE_NOMEM; + return SQLITE_IOERR_NOMEM; } /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ char *zUtf8; char zMbcsPath[MAX_PATH]; - GetTempPathA(MAX_PATH-30, zMbcsPath); + osGetTempPathA(MAX_PATH-30, zMbcsPath); zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); if( zUtf8 ){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); - free(zUtf8); + sqlite3_free(zUtf8); }else{ - return SQLITE_NOMEM; + return SQLITE_IOERR_NOMEM; } #endif } /* Check that the output buffer is large enough for the temporary file ** name. If it is not, return SQLITE_ERROR. */ - if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 17) >= nBuf ){ + if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){ return SQLITE_ERROR; } for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} zTempPath[i] = 0; - sqlite3_snprintf(nBuf-17, zBuf, + sqlite3_snprintf(nBuf-18, zBuf, "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); j = sqlite3Strlen30(zBuf); sqlite3_randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; + zBuf[j+1] = 0; OSTRACE(("TEMP FILENAME: %s\n", zBuf)); return SQLITE_OK; } @@ -34006,10 +34986,11 @@ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */ ){ HANDLE h; + DWORD lastErrno; DWORD dwDesiredAccess; DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; #if SQLITE_OS_WINCE @@ -34021,11 +35002,11 @@ int cnt = 0; /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ - char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ + char zTmpname[MAX_PATH+2]; /* Buffer used to create temp filename */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE int eType = flags&0xFFFFFF00; /* Type of file to open */ #endif @@ -34080,21 +35061,28 @@ /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ assert(isDelete && !isOpenJournal); - rc = getTempname(MAX_PATH+1, zTmpname); + rc = getTempname(MAX_PATH+2, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zUtf8Name = zTmpname; } + + /* Database filenames are double-zero terminated if they are not + ** URIs with parameters. Hence, they can always be passed into + ** sqlite3_uri_parameter(). + */ + assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || + zUtf8Name[strlen(zUtf8Name)+1]==0 ); /* Convert the filename to the system encoding. */ zConverted = convertUtf8Filename(zUtf8Name); if( zConverted==0 ){ - return SQLITE_NOMEM; + return SQLITE_IOERR_NOMEM; } if( isReadWrite ){ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; }else{ @@ -34136,30 +35124,30 @@ #if SQLITE_OS_WINCE dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; #endif if( isNT() ){ - while( (h = CreateFileW((WCHAR*)zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt) ){} + while( (h = osCreateFileW((LPCWSTR)zConverted, + dwDesiredAccess, + dwShareMode, NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL))==INVALID_HANDLE_VALUE && + retryIoerr(&cnt, &lastErrno) ){} /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ - while( (h = CreateFileA((char*)zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt) ){} + while( (h = osCreateFileA((LPCSTR)zConverted, + dwDesiredAccess, + dwShareMode, NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL))==INVALID_HANDLE_VALUE && + retryIoerr(&cnt, &lastErrno) ){} #endif } logIoerr(cnt); @@ -34166,14 +35154,14 @@ OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, h==INVALID_HANDLE_VALUE ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ - pFile->lastErrno = GetLastError(); - winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name); - free(zConverted); - if( isReadWrite ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); + sqlite3_free(zConverted); + if( isReadWrite && !isExclusive ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags); }else{ return SQLITE_CANTOPEN_BKPT; } @@ -34192,36 +35180,38 @@ pFile->h = h; pFile->lastErrno = NO_ERROR; pFile->pVfs = pVfs; pFile->pShm = 0; pFile->zPath = zName; - pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); + if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ + pFile->ctrlFlags |= WINFILE_PSOW; + } #if SQLITE_OS_WINCE if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB && !winceCreateLock(zName, pFile) ){ - CloseHandle(h); - free(zConverted); + osCloseHandle(h); + sqlite3_free(zConverted); return SQLITE_CANTOPEN_BKPT; } if( isTemp ){ pFile->zDeleteOnClose = zConverted; }else #endif { - free(zConverted); + sqlite3_free(zConverted); } OpenCounter(+1); return rc; } /* ** Delete the named file. ** -** Note that windows does not allow a file to be deleted if some other +** Note that Windows does not allow a file to be deleted if some other ** process has it open. Sometimes a virus scanner or indexing program ** will open a journal file shortly after it is created in order to do ** whatever it does. While this other process is holding the ** file open, we will be unable to delete it. To work around this ** problem, we delay 100 milliseconds and try to delete again. Up @@ -34233,42 +35223,44 @@ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on win32 */ ){ int cnt = 0; int rc; + DWORD lastErrno; void *zConverted; UNUSED_PARAMETER(pVfs); UNUSED_PARAMETER(syncDir); SimulateIOError(return SQLITE_IOERR_DELETE); zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ - return SQLITE_NOMEM; + return SQLITE_IOERR_NOMEM; } if( isNT() ){ rc = 1; - while( GetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES && - (rc = DeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){} + while( osGetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES && + (rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){} rc = rc ? SQLITE_OK : SQLITE_ERROR; /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ rc = 1; - while( GetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES && - (rc = DeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){} + while( osGetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES && + (rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){} rc = rc ? SQLITE_OK : SQLITE_ERROR; #endif } if( rc ){ - rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename); + rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, + "winDelete", zFilename); }else{ logIoerr(cnt); } - free(zConverted); + sqlite3_free(zConverted); OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" ))); return rc; } /* @@ -34280,25 +35272,26 @@ int flags, /* Type of test to make on this file */ int *pResOut /* OUT: Result */ ){ DWORD attr; int rc = 0; + DWORD lastErrno; void *zConverted; UNUSED_PARAMETER(pVfs); SimulateIOError( return SQLITE_IOERR_ACCESS; ); zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ - return SQLITE_NOMEM; + return SQLITE_IOERR_NOMEM; } if( isNT() ){ int cnt = 0; WIN32_FILE_ATTRIBUTE_DATA sAttrData; memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = GetFileAttributesExW((WCHAR*)zConverted, + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, GetFileExInfoStandard, - &sAttrData)) && retryIoerr(&cnt) ){} + &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){} if( rc ){ /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file ** as if it does not exist. */ if( flags==SQLITE_ACCESS_EXISTS @@ -34308,28 +35301,28 @@ }else{ attr = sAttrData.dwFileAttributes; } }else{ logIoerr(cnt); - if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ - winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename); - free(zConverted); + if( lastErrno!=ERROR_FILE_NOT_FOUND ){ + winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename); + sqlite3_free(zConverted); return SQLITE_IOERR_ACCESS; }else{ attr = INVALID_FILE_ATTRIBUTES; } } /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ - attr = GetFileAttributesA((char*)zConverted); + attr = osGetFileAttributesA((char*)zConverted); #endif } - free(zConverted); + sqlite3_free(zConverted); switch( flags ){ case SQLITE_ACCESS_READ: case SQLITE_ACCESS_EXISTS: rc = attr!=INVALID_FILE_ATTRIBUTES; break; @@ -34390,119 +35383,52 @@ ** current working directory has been unlinked. */ SimulateIOError( return SQLITE_ERROR ); UNUSED_PARAMETER(nFull); zConverted = convertUtf8Filename(zRelative); + if( zConverted==0 ){ + return SQLITE_IOERR_NOMEM; + } if( isNT() ){ - WCHAR *zTemp; - nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; - zTemp = malloc( nByte*sizeof(zTemp[0]) ); + LPWSTR zTemp; + nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0) + 3; + zTemp = sqlite3_malloc( nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ - free(zConverted); - return SQLITE_NOMEM; + sqlite3_free(zConverted); + return SQLITE_IOERR_NOMEM; } - GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0); - free(zConverted); + osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); + sqlite3_free(zConverted); zOut = unicodeToUtf8(zTemp); - free(zTemp); + sqlite3_free(zTemp); /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ char *zTemp; - nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3; - zTemp = malloc( nByte*sizeof(zTemp[0]) ); + nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0) + 3; + zTemp = sqlite3_malloc( nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ - free(zConverted); - return SQLITE_NOMEM; + sqlite3_free(zConverted); + return SQLITE_IOERR_NOMEM; } - GetFullPathNameA((char*)zConverted, nByte, zTemp, 0); - free(zConverted); + osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); + sqlite3_free(zConverted); zOut = sqlite3_win32_mbcs_to_utf8(zTemp); - free(zTemp); + sqlite3_free(zTemp); #endif } if( zOut ){ sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut); - free(zOut); + sqlite3_free(zOut); return SQLITE_OK; }else{ - return SQLITE_NOMEM; - } -#endif -} - -/* -** Get the sector size of the device used to store -** file. -*/ -static int getSectorSize( - sqlite3_vfs *pVfs, - const char *zRelative /* UTF-8 file name */ -){ - DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; - /* GetDiskFreeSpace is not supported under WINCE */ -#if SQLITE_OS_WINCE - UNUSED_PARAMETER(pVfs); - UNUSED_PARAMETER(zRelative); -#else - char zFullpath[MAX_PATH+1]; - int rc; - DWORD dwRet = 0; - DWORD dwDummy; - - /* - ** We need to get the full path name of the file - ** to get the drive letter to look up the sector - ** size. - */ - SimulateIOErrorBenign(1); - rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); - SimulateIOErrorBenign(0); - if( rc == SQLITE_OK ) - { - void *zConverted = convertUtf8Filename(zFullpath); - if( zConverted ){ - if( isNT() ){ - /* trim path to just drive reference */ - WCHAR *p = zConverted; - for(;*p;p++){ - if( *p == '\\' ){ - *p = '\0'; - break; - } - } - dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, - &dwDummy, - &bytesPerSector, - &dwDummy, - &dwDummy); - }else{ - /* trim path to just drive reference */ - char *p = (char *)zConverted; - for(;*p;p++){ - if( *p == '\\' ){ - *p = '\0'; - break; - } - } - dwRet = GetDiskFreeSpaceA((char*)zConverted, - &dwDummy, - &bytesPerSector, - &dwDummy, - &dwDummy); - } - free(zConverted); - } - if( !dwRet ){ - bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; - } - } -#endif - return (int) bytesPerSector; + return SQLITE_IOERR_NOMEM; + } +#endif } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Interfaces for opening a shared library, finding entry points @@ -34518,41 +35444,34 @@ UNUSED_PARAMETER(pVfs); if( zConverted==0 ){ return 0; } if( isNT() ){ - h = LoadLibraryW((WCHAR*)zConverted); + h = osLoadLibraryW((LPCWSTR)zConverted); /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, +** Since the ANSI version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ - h = LoadLibraryA((char*)zConverted); + h = osLoadLibraryA((char*)zConverted); #endif } - free(zConverted); + sqlite3_free(zConverted); return (void*)h; } static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ UNUSED_PARAMETER(pVfs); - getLastErrorMsg(nBuf, zBufOut); -} -void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ - UNUSED_PARAMETER(pVfs); -#if SQLITE_OS_WINCE - /* The GetProcAddressA() routine is only available on wince. */ - return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol); -#else - /* All other windows platforms expect GetProcAddress() to take - ** an Ansi string regardless of the _UNICODE setting */ - return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol); -#endif -} -void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ - UNUSED_PARAMETER(pVfs); - FreeLibrary((HANDLE)pHandle); + getLastErrorMsg(osGetLastError(), nBuf, zBufOut); +} +static void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ + UNUSED_PARAMETER(pVfs); + return (void(*)(void))osGetProcAddressA((HANDLE)pHandle, zSymbol); +} +static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ + UNUSED_PARAMETER(pVfs); + osFreeLibrary((HANDLE)pHandle); } #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ #define winDlOpen 0 #define winDlError 0 #define winDlSym 0 @@ -34570,27 +35489,27 @@ n = nBuf; memset(zBuf, 0, nBuf); #else if( sizeof(SYSTEMTIME)<=nBuf-n ){ SYSTEMTIME x; - GetSystemTime(&x); + osGetSystemTime(&x); memcpy(&zBuf[n], &x, sizeof(x)); n += sizeof(x); } if( sizeof(DWORD)<=nBuf-n ){ - DWORD pid = GetCurrentProcessId(); + DWORD pid = osGetCurrentProcessId(); memcpy(&zBuf[n], &pid, sizeof(pid)); n += sizeof(pid); } if( sizeof(DWORD)<=nBuf-n ){ - DWORD cnt = GetTickCount(); + DWORD cnt = osGetTickCount(); memcpy(&zBuf[n], &cnt, sizeof(cnt)); n += sizeof(cnt); } if( sizeof(LARGE_INTEGER)<=nBuf-n ){ LARGE_INTEGER i; - QueryPerformanceCounter(&i); + osQueryPerformanceCounter(&i); memcpy(&zBuf[n], &i, sizeof(i)); n += sizeof(i); } #endif return n; @@ -34599,11 +35518,11 @@ /* ** Sleep for a little while. Return the amount of time slept. */ static int winSleep(sqlite3_vfs *pVfs, int microsec){ - Sleep((microsec+999)/1000); + osSleep((microsec+999)/1000); UNUSED_PARAMETER(pVfs); return ((microsec+999)/1000)*1000; } /* @@ -34620,11 +35539,12 @@ ** the current time and date as a Julian Day number times 86_400_000. In ** other words, write into *piNow the number of milliseconds since the Julian ** epoch of noon in Greenwich on November 24, 4714 B.C according to the ** proleptic Gregorian calendar. ** -** On success, return 0. Return 1 if the time and date cannot be found. +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date +** cannot be found. */ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ /* FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). */ @@ -34637,17 +35557,17 @@ static const sqlite3_int64 max32BitValue = (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; #if SQLITE_OS_WINCE SYSTEMTIME time; - GetSystemTime(&time); + osGetSystemTime(&time); /* if SystemTimeToFileTime() fails, it returns zero. */ - if (!SystemTimeToFileTime(&time,&ft)){ - return 1; + if (!osSystemTimeToFileTime(&time,&ft)){ + return SQLITE_ERROR; } #else - GetSystemTimeAsFileTime( &ft ); + osGetSystemTimeAsFileTime( &ft ); #endif *piNow = winFiletimeEpoch + ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000; @@ -34656,19 +35576,19 @@ if( sqlite3_current_time ){ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } #endif UNUSED_PARAMETER(pVfs); - return 0; + return SQLITE_OK; } /* ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ -int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ +static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ int rc; sqlite3_int64 i; rc = winCurrentTimeInt64(pVfs, &i); if( !rc ){ *prNow = i/86400000.0; @@ -34676,12 +35596,12 @@ return rc; } /* ** The idea is that this function works like a combination of -** GetLastError() and FormatMessage() on windows (or errno and -** strerror_r() on unix). After an error is returned by an OS +** GetLastError() and FormatMessage() on Windows (or errno and +** strerror_r() on Unix). After an error is returned by an OS ** function, SQLite calls this function with zBuf pointing to ** a buffer of nBuf bytes. The OS layer should populate the ** buffer with a nul-terminated UTF-8 encoded error message ** describing the last IO error to have occurred within the calling ** thread. @@ -34706,14 +35626,12 @@ ** by sqlite into the error message available to the user using ** sqlite3_errmsg(), possibly making IO errors easier to debug. */ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ UNUSED_PARAMETER(pVfs); - return getLastErrorMsg(nBuf, zBuf); + return getLastErrorMsg(osGetLastError(), nBuf, zBuf); } - - /* ** Initialize and deinitialize the operating system interface. */ SQLITE_API int sqlite3_os_init(void){ @@ -34735,25 +35653,30 @@ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ - 0, /* xSetSystemCall */ - 0, /* xGetSystemCall */ - 0, /* xNextSystemCall */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ }; + + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ + assert( ArraySize(aSyscall)==60 ); #ifndef SQLITE_OMIT_WAL /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); - GetSystemInfo(&winSysInfo); + osGetSystemInfo(&winSysInfo); assert(winSysInfo.dwAllocationGranularity > 0); #endif sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } + SQLITE_API int sqlite3_os_end(void){ return SQLITE_OK; } #endif /* SQLITE_OS_WIN */ @@ -35189,11 +36112,11 @@ */ struct PCache { PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ PgHdr *pSynced; /* Last synced page in dirty page list */ int nRef; /* Number of referenced pages */ - int nMax; /* Configured cache size */ + int szCache; /* Configured cache size */ int szPage; /* Size of every page in this cache */ int szExtra; /* Size of extra space for each page */ int bPurgeable; /* True if pages are on backing store */ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ void *pStress; /* Argument to xStress */ @@ -35300,32 +36223,32 @@ PCache *pCache = p->pCache; if( pCache->bPurgeable ){ if( p->pgno==1 ){ pCache->pPage1 = 0; } - sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0); + sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 0); } } /*************************************************** General Interfaces ****** ** ** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe. */ SQLITE_PRIVATE int sqlite3PcacheInitialize(void){ - if( sqlite3GlobalConfig.pcache.xInit==0 ){ + if( sqlite3GlobalConfig.pcache2.xInit==0 ){ /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache. */ sqlite3PCacheSetDefault(); } - return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg); + return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg); } SQLITE_PRIVATE void sqlite3PcacheShutdown(void){ - if( sqlite3GlobalConfig.pcache.xShutdown ){ + if( sqlite3GlobalConfig.pcache2.xShutdown ){ /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ - sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); + sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg); } } /* ** Return the size in bytes of a PCache object. @@ -35350,26 +36273,37 @@ p->szPage = szPage; p->szExtra = szExtra; p->bPurgeable = bPurgeable; p->xStress = xStress; p->pStress = pStress; - p->nMax = 100; + p->szCache = 100; } /* ** Change the page size for PCache object. The caller must ensure that there ** are no outstanding page references when this function is called. */ SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ assert( pCache->nRef==0 && pCache->pDirty==0 ); if( pCache->pCache ){ - sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache); + sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); pCache->pCache = 0; pCache->pPage1 = 0; } pCache->szPage = szPage; } + +/* +** Compute the number of pages of cache requested. +*/ +static int numberOfCachePages(PCache *p){ + if( p->szCache>=0 ){ + return p->szCache; + }else{ + return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); + } +} /* ** Try to obtain a page from the cache. */ SQLITE_PRIVATE int sqlite3PcacheFetch( @@ -35376,11 +36310,12 @@ PCache *pCache, /* Obtain the page from this cache */ Pgno pgno, /* Page number to obtain */ int createFlag, /* If true, create page if it does not exist already */ PgHdr **ppPage /* Write the page here */ ){ - PgHdr *pPage = 0; + sqlite3_pcache_page *pPage = 0; + PgHdr *pPgHdr = 0; int eCreate; assert( pCache!=0 ); assert( createFlag==1 || createFlag==0 ); assert( pgno>0 ); @@ -35388,23 +36323,23 @@ /* If the pluggable cache (sqlite3_pcache*) has not been allocated, ** allocate it now. */ if( !pCache->pCache && createFlag ){ sqlite3_pcache *p; - int nByte; - nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr); - p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable); + p = sqlite3GlobalConfig.pcache2.xCreate( + pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable + ); if( !p ){ return SQLITE_NOMEM; } - sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax); + sqlite3GlobalConfig.pcache2.xCachesize(p, numberOfCachePages(pCache)); pCache->pCache = p; } eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); if( pCache->pCache ){ - pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate); + pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); } if( !pPage && eCreate==1 ){ PgHdr *pPg; @@ -35427,45 +36362,48 @@ #ifdef SQLITE_LOG_CACHE_SPILL sqlite3_log(SQLITE_FULL, "spill page %d making room for %d - cache used: %d/%d", pPg->pgno, pgno, sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), - pCache->nMax); + numberOfCachePages(pCache)); #endif rc = pCache->xStress(pCache->pStress, pPg); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ return rc; } } - pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2); + pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); } if( pPage ){ - if( !pPage->pData ){ - memset(pPage, 0, sizeof(PgHdr)); - pPage->pData = (void *)&pPage[1]; - pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage]; - memset(pPage->pExtra, 0, pCache->szExtra); - pPage->pCache = pCache; - pPage->pgno = pgno; - } - assert( pPage->pCache==pCache ); - assert( pPage->pgno==pgno ); - assert( pPage->pData==(void *)&pPage[1] ); - assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] ); - - if( 0==pPage->nRef ){ + pPgHdr = (PgHdr *)pPage->pExtra; + + if( !pPgHdr->pPage ){ + memset(pPgHdr, 0, sizeof(PgHdr)); + pPgHdr->pPage = pPage; + pPgHdr->pData = pPage->pBuf; + pPgHdr->pExtra = (void *)&pPgHdr[1]; + memset(pPgHdr->pExtra, 0, pCache->szExtra); + pPgHdr->pCache = pCache; + pPgHdr->pgno = pgno; + } + assert( pPgHdr->pCache==pCache ); + assert( pPgHdr->pgno==pgno ); + assert( pPgHdr->pData==pPage->pBuf ); + assert( pPgHdr->pExtra==(void *)&pPgHdr[1] ); + + if( 0==pPgHdr->nRef ){ pCache->nRef++; } - pPage->nRef++; + pPgHdr->nRef++; if( pgno==1 ){ - pCache->pPage1 = pPage; + pCache->pPage1 = pPgHdr; } } - *ppPage = pPage; - return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK; + *ppPage = pPgHdr; + return (pPgHdr==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK; } /* ** Decrement the reference count on a page. If the page is clean and the ** reference count drops to 0, then it is made elible for recycling. @@ -35508,11 +36446,11 @@ pCache = p->pCache; pCache->nRef--; if( p->pgno==1 ){ pCache->pPage1 = 0; } - sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1); + sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 1); } /* ** Make sure the page is marked as dirty. If it isn't dirty already, ** make it so. @@ -35566,11 +36504,11 @@ */ SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ PCache *pCache = p->pCache; assert( p->nRef>0 ); assert( newPgno>0 ); - sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno); + sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); p->pgno = newPgno; if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ pcacheRemoveFromDirtyList(p); pcacheAddToDirtyList(p); } @@ -35603,20 +36541,20 @@ } if( pgno==0 && pCache->pPage1 ){ memset(pCache->pPage1->pData, 0, pCache->szPage); pgno = 1; } - sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1); + sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); } } /* ** Close a cache. */ SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){ if( pCache->pCache ){ - sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache); + sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); } } /* ** Discard the contents of the cache. @@ -35724,31 +36662,41 @@ ** Return the total number of pages in the cache. */ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){ int nPage = 0; if( pCache->pCache ){ - nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache); + nPage = sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache); } return nPage; } #ifdef SQLITE_TEST /* ** Get the suggested cache-size value. */ SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){ - return pCache->nMax; + return numberOfCachePages(pCache); } #endif /* ** Set the suggested cache-size value. */ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ - pCache->nMax = mxPage; + pCache->szCache = mxPage; + if( pCache->pCache ){ + sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, + numberOfCachePages(pCache)); + } +} + +/* +** Free up as much memory as possible from the page cache. +*/ +SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){ if( pCache->pCache ){ - sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage); + sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache); } } #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* @@ -35789,13 +36737,10 @@ typedef struct PCache1 PCache1; typedef struct PgHdr1 PgHdr1; typedef struct PgFreeslot PgFreeslot; typedef struct PGroup PGroup; -typedef struct PGroupBlock PGroupBlock; -typedef struct PGroupBlockList PGroupBlockList; - /* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set ** of one or more PCaches that are able to recycle each others unpinned ** pages when they are under memory pressure. A PGroup is an instance of ** the following object. ** @@ -35808,82 +36753,24 @@ ** of. ** ** Mode 1 uses more memory (since PCache instances are not able to rob ** unused pages from other PCaches) but it also operates without a mutex, ** and is therefore often faster. Mode 2 requires a mutex in order to be -** threadsafe, but is able recycle pages more efficient. +** threadsafe, but recycles pages more efficiently. ** ** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single ** PGroup which is the pcache1.grp global variable and its mutex is ** SQLITE_MUTEX_STATIC_LRU. */ struct PGroup { sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ - int nMaxPage; /* Sum of nMax for purgeable caches */ - int nMinPage; /* Sum of nMin for purgeable caches */ - int mxPinned; /* nMaxpage + 10 - nMinPage */ - int nCurrentPage; /* Number of purgeable pages allocated */ + unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ + unsigned int nMinPage; /* Sum of nMin for purgeable caches */ + unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ + unsigned int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ -#ifdef SQLITE_PAGECACHE_BLOCKALLOC - int isBusy; /* Do not run ReleaseMemory() if true */ - PGroupBlockList *pBlockList; /* List of block-lists for this group */ -#endif -}; - -/* -** If SQLITE_PAGECACHE_BLOCKALLOC is defined when the library is built, -** each PGroup structure has a linked list of the the following starting -** at PGroup.pBlockList. There is one entry for each distinct page-size -** currently used by members of the PGroup (i.e. 1024 bytes, 4096 bytes -** etc.). Variable PGroupBlockList.nByte is set to the actual allocation -** size requested by each pcache, which is the database page-size plus -** the various header structures used by the pcache, pager and btree layers. -** Usually around (pgsz+200) bytes. -** -** This size (pgsz+200) bytes is not allocated efficiently by some -** implementations of malloc. In particular, some implementations are only -** able to allocate blocks of memory chunks of 2^N bytes, where N is some -** integer value. Since the page-size is a power of 2, this means we -** end up wasting (pgsz-200) bytes in each allocation. -** -** If SQLITE_PAGECACHE_BLOCKALLOC is defined, the (pgsz+200) byte blocks -** are not allocated directly. Instead, blocks of roughly M*(pgsz+200) bytes -** are requested from malloc allocator. After a block is returned, -** sqlite3MallocSize() is used to determine how many (pgsz+200) byte -** allocations can fit in the space returned by malloc(). This value may -** be more than M. -** -** The blocks are stored in a doubly-linked list. Variable PGroupBlock.nEntry -** contains the number of allocations that will fit in the aData[] space. -** nEntry is limited to the number of bits in bitmask mUsed. If a slot -** within aData is in use, the corresponding bit in mUsed is set. Thus -** when (mUsed+1==(1 << nEntry)) the block is completely full. -** -** Each time a slot within a block is freed, the block is moved to the start -** of the linked-list. And if a block becomes completely full, then it is -** moved to the end of the list. As a result, when searching for a free -** slot, only the first block in the list need be examined. If it is full, -** then it is guaranteed that all blocks are full. -*/ -struct PGroupBlockList { - int nByte; /* Size of each allocation in bytes */ - PGroupBlock *pFirst; /* First PGroupBlock in list */ - PGroupBlock *pLast; /* Last PGroupBlock in list */ - PGroupBlockList *pNext; /* Next block-list attached to group */ -}; - -struct PGroupBlock { - Bitmask mUsed; /* Mask of used slots */ - int nEntry; /* Maximum number of allocations in aData[] */ - u8 *aData; /* Pointer to data block */ - PGroupBlock *pNext; /* Next PGroupBlock in list */ - PGroupBlock *pPrev; /* Previous PGroupBlock in list */ - PGroupBlockList *pList; /* Owner list */ -}; - -/* Minimum value for PGroupBlock.nEntry */ -#define PAGECACHE_BLOCKALLOC_MINENTRY 15 +}; /* Each page cache is an instance of the following object. Every ** open database file (including each in-memory database and each ** temporary or transient database) has a single page cache which ** is an instance of this object. @@ -35892,15 +36779,16 @@ ** opaque sqlite3_pcache* handles. */ struct PCache1 { /* Cache configuration parameters. Page size (szPage) and the purgeable ** flag (bPurgeable) are set when the cache is created. nMax may be - ** modified at any time by a call to the pcache1CacheSize() method. + ** modified at any time by a call to the pcache1Cachesize() method. ** The PGroup mutex must be held when accessing nMax. */ PGroup *pGroup; /* PGroup this cache belongs to */ int szPage; /* Size of allocated pages in bytes */ + int szExtra; /* Size of extra space in bytes */ int bPurgeable; /* True if cache is purgeable */ unsigned int nMin; /* Minimum number of pages reserved */ unsigned int nMax; /* Configured "cache_size" value */ unsigned int n90pct; /* nMax*9/10 */ @@ -35915,15 +36803,16 @@ unsigned int iMaxKey; /* Largest key seen since xTruncate() */ }; /* ** Each cache entry is represented by an instance of the following -** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated -** directly before this structure in memory (see the PGHDR1_TO_PAGE() -** macro below). +** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of +** PgHdr1.pCache->szPage bytes is allocated directly before this structure +** in memory. */ struct PgHdr1 { + sqlite3_pcache_page page; unsigned int iKey; /* Key value (page number) */ PgHdr1 *pNext; /* Next in hash table chain */ PCache1 *pCache; /* Cache that currently owns this page */ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ @@ -35968,36 +36857,10 @@ ** All code in this file should access the global structure above via the ** alias "pcache1". This ensures that the WSD emulation is used when ** compiling for systems that do not support real WSD. */ #define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g)) - -/* -** When a PgHdr1 structure is allocated, the associated PCache1.szPage -** bytes of data are located directly before it in memory (i.e. the total -** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The -** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as -** an argument and returns a pointer to the associated block of szPage -** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is -** a pointer to a block of szPage bytes of data and the return value is -** a pointer to the associated PgHdr1 structure. -** -** assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X ); -*/ -#define PGHDR1_TO_PAGE(p) (void*)(((char*)p) - p->pCache->szPage) -#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage) - -/* -** Blocks used by the SQLITE_PAGECACHE_BLOCKALLOC blocks to store/retrieve -** a PGroupBlock pointer based on a pointer to a page buffer. -*/ -#define PAGE_SET_BLOCKPTR(pCache, pPg, pBlock) \ - ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) = pBlock ) - -#define PAGE_GET_BLOCKPTR(pCache, pPg) \ - ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) ) - /* ** Macros to enter and leave the PCache LRU mutex. */ #define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex) @@ -36077,12 +36940,13 @@ } /* ** Free an allocated buffer obtained from pcache1Alloc(). */ -static void pcache1Free(void *p){ - if( p==0 ) return; +static int pcache1Free(void *p){ + int nFreed = 0; + if( p==0 ) return 0; if( p>=pcache1.pStart && ppPrev = 0; - pBlock->pNext = pList->pFirst; - pList->pFirst = pBlock; - if( pBlock->pNext ){ - pBlock->pNext->pPrev = pBlock; - }else{ - assert( pList->pLast==0 ); - pList->pLast = pBlock; - } -} - -/* -** If there are no blocks in the list headed by pList, remove pList -** from the pGroup->pBlockList list and free it with sqlite3_free(). -*/ -static void freeListIfEmpty(PGroup *pGroup, PGroupBlockList *pList){ - assert( sqlite3_mutex_held(pGroup->mutex) ); - if( pList->pFirst==0 ){ - PGroupBlockList **pp; - for(pp=&pGroup->pBlockList; *pp!=pList; pp=&(*pp)->pNext); - *pp = (*pp)->pNext; - sqlite3_free(pList); - } -} -#endif /* SQLITE_PAGECACHE_BLOCKALLOC */ - /* ** Allocate a new page object initially associated with cache pCache. */ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ - int nByte = sizeof(PgHdr1) + pCache->szPage; - void *pPg = 0; - PgHdr1 *p; - -#ifdef SQLITE_PAGECACHE_BLOCKALLOC - PGroup *pGroup = pCache->pGroup; - PGroupBlockList *pList; - PGroupBlock *pBlock; - int i; - - nByte += sizeof(PGroupBlockList *); - nByte = ROUND8(nByte); - - for(pList=pGroup->pBlockList; pList; pList=pList->pNext){ - if( pList->nByte==nByte ) break; - } - if( pList==0 ){ - PGroupBlockList *pNew; - assert( pGroup->isBusy==0 ); - assert( sqlite3_mutex_held(pGroup->mutex) ); - pGroup->isBusy = 1; /* Disable sqlite3PcacheReleaseMemory() */ - pNew = (PGroupBlockList *)sqlite3MallocZero(sizeof(PGroupBlockList)); - pGroup->isBusy = 0; /* Reenable sqlite3PcacheReleaseMemory() */ - if( pNew==0 ){ - /* malloc() failure. Return early. */ - return 0; - } -#ifdef SQLITE_DEBUG - for(pList=pGroup->pBlockList; pList; pList=pList->pNext){ - assert( pList->nByte!=nByte ); - } -#endif - pNew->nByte = nByte; - pNew->pNext = pGroup->pBlockList; - pGroup->pBlockList = pNew; - pList = pNew; - } - - pBlock = pList->pFirst; - if( pBlock==0 || pBlock->mUsed==(((Bitmask)1<nEntry)-1) ){ - int sz; - - /* Allocate a new block. Try to allocate enough space for the PGroupBlock - ** structure and MINENTRY allocations of nByte bytes each. If the - ** allocator returns more memory than requested, then more than MINENTRY - ** allocations may fit in it. */ - assert( sqlite3_mutex_held(pGroup->mutex) ); - pcache1LeaveMutex(pCache->pGroup); - sz = sizeof(PGroupBlock) + PAGECACHE_BLOCKALLOC_MINENTRY * nByte; - pBlock = (PGroupBlock *)sqlite3Malloc(sz); - pcache1EnterMutex(pCache->pGroup); - - if( !pBlock ){ - freeListIfEmpty(pGroup, pList); - return 0; - } - pBlock->nEntry = (sqlite3MallocSize(pBlock) - sizeof(PGroupBlock)) / nByte; - if( pBlock->nEntry>=BMS ){ - pBlock->nEntry = BMS-1; - } - pBlock->pList = pList; - pBlock->mUsed = 0; - pBlock->aData = (u8 *)&pBlock[1]; - addBlockToList(pList, pBlock); - - sz = sqlite3MallocSize(pBlock); - sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); - sqlite3_mutex_leave(pcache1.mutex); - } - - for(i=0; pPg==0 && ALWAYS(inEntry); i++){ - if( 0==(pBlock->mUsed & ((Bitmask)1<mUsed |= ((Bitmask)1<aData[pList->nByte * i]; - } - } - assert( pPg ); - PAGE_SET_BLOCKPTR(pCache, pPg, pBlock); - - /* If the block is now full, shift it to the end of the list */ - if( pBlock->mUsed==(((Bitmask)1<nEntry)-1) && pList->pLast!=pBlock ){ - assert( pList->pFirst==pBlock ); - assert( pBlock->pPrev==0 ); - assert( pList->pLast->pNext==0 ); - pList->pFirst = pBlock->pNext; - pList->pFirst->pPrev = 0; - pBlock->pPrev = pList->pLast; - pBlock->pNext = 0; - pList->pLast->pNext = pBlock; - pList->pLast = pBlock; - } - p = PAGE_TO_PGHDR1(pCache, pPg); - if( pCache->bPurgeable ){ - pCache->pGroup->nCurrentPage++; - } -#else + PgHdr1 *p = 0; + void *pPg; + /* The group mutex must be released before pcache1Alloc() is called. This ** is because it may call sqlite3_release_memory(), which assumes that ** this mutex is not held. */ assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); pcache1LeaveMutex(pCache->pGroup); - pPg = pcache1Alloc(nByte); +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + pPg = pcache1Alloc(pCache->szPage); + p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); + if( !pPg || !p ){ + pcache1Free(pPg); + sqlite3_free(p); + pPg = 0; + } +#else + pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra); + p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; +#endif pcache1EnterMutex(pCache->pGroup); + if( pPg ){ - p = PAGE_TO_PGHDR1(pCache, pPg); + p->page.pBuf = pPg; + p->page.pExtra = &p[1]; if( pCache->bPurgeable ){ pCache->pGroup->nCurrentPage++; } - }else{ - p = 0; + return p; } -#endif - return p; + return 0; } /* ** Free a page object allocated by pcache1AllocPage(). ** @@ -36282,52 +37031,14 @@ ** with a NULL pointer, so we mark the NULL test with ALWAYS(). */ static void pcache1FreePage(PgHdr1 *p){ if( ALWAYS(p) ){ PCache1 *pCache = p->pCache; - void *pPg = PGHDR1_TO_PAGE(p); - -#ifdef SQLITE_PAGECACHE_BLOCKALLOC - PGroupBlock *pBlock = PAGE_GET_BLOCKPTR(pCache, pPg); - PGroupBlockList *pList = pBlock->pList; - int i = ((u8 *)pPg - pBlock->aData) / pList->nByte; - - assert( pPg==(void *)&pBlock->aData[i*pList->nByte] ); - assert( pBlock->mUsed & ((Bitmask)1<mUsed &= ~((Bitmask)1<pFirst==pBlock ){ - pList->pFirst = pBlock->pNext; - if( pList->pFirst ) pList->pFirst->pPrev = 0; - }else{ - pBlock->pPrev->pNext = pBlock->pNext; - } - if( pList->pLast==pBlock ){ - pList->pLast = pBlock->pPrev; - if( pList->pLast ) pList->pLast->pNext = 0; - }else{ - pBlock->pNext->pPrev = pBlock->pPrev; - } - - if( pBlock->mUsed==0 ){ - PGroup *pGroup = p->pCache->pGroup; - - int sz = sqlite3MallocSize(pBlock); - sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -sz); - sqlite3_mutex_leave(pcache1.mutex); - freeListIfEmpty(pGroup, pList); - sqlite3_free(pBlock); - }else{ - addBlockToList(pList, pBlock); - } -#else assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); - pcache1Free(pPg); + pcache1Free(p->page.pBuf); +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + sqlite3_free(p); #endif if( pCache->bPurgeable ){ pCache->pGroup->nCurrentPage--; } } @@ -36359,17 +37070,17 @@ ** it is desirable to avoid allocating a new page cache entry because ** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient ** for all page cache needs and we should not need to spill the ** allocation onto the heap. ** -** Or, the heap is used for all page cache memory put the heap is +** Or, the heap is used for all page cache memory but the heap is ** under memory pressure, then again it is desirable to avoid ** allocating a new page cache entry in order to avoid stressing ** the heap even further. */ static int pcache1UnderMemoryPressure(PCache1 *pCache){ - if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){ + if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ return pcache1.bUnderPressure; }else{ return sqlite3HeapNearlyFull(); } } @@ -36556,11 +37267,11 @@ /* ** Implementation of the sqlite3_pcache.xCreate method. ** ** Allocate a new cache. */ -static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){ +static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ PCache1 *pCache; /* The newly created page cache */ PGroup *pGroup; /* The group the new page cache will belong to */ int sz; /* Bytes of memory required to allocate the new cache */ /* @@ -36578,10 +37289,13 @@ #if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 const int separateCache = 0; #else int separateCache = sqlite3GlobalConfig.bCoreMutex>0; #endif + + assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); + assert( szExtra < 300 ); sz = sizeof(PCache1) + sizeof(PGroup)*separateCache; pCache = (PCache1 *)sqlite3_malloc(sz); if( pCache ){ memset(pCache, 0, sz); @@ -36591,10 +37305,11 @@ }else{ pGroup = &pcache1.grp; } pCache->pGroup = pGroup; pCache->szPage = szPage; + pCache->szExtra = szExtra; pCache->bPurgeable = (bPurgeable ? 1 : 0); if( bPurgeable ){ pCache->nMin = 10; pcache1EnterMutex(pGroup); pGroup->nMinPage += pCache->nMin; @@ -36621,10 +37336,29 @@ pCache->n90pct = pCache->nMax*9/10; pcache1EnforceMaxPage(pGroup); pcache1LeaveMutex(pGroup); } } + +/* +** Implementation of the sqlite3_pcache.xShrink method. +** +** Free up as much memory as possible. +*/ +static void pcache1Shrink(sqlite3_pcache *p){ + PCache1 *pCache = (PCache1*)p; + if( pCache->bPurgeable ){ + PGroup *pGroup = pCache->pGroup; + int savedMaxPage; + pcache1EnterMutex(pGroup); + savedMaxPage = pGroup->nMaxPage; + pGroup->nMaxPage = 0; + pcache1EnforceMaxPage(pGroup); + pGroup->nMaxPage = savedMaxPage; + pcache1LeaveMutex(pGroup); + } +} /* ** Implementation of the sqlite3_pcache.xPagecount method. */ static int pcache1Pagecount(sqlite3_pcache *p){ @@ -36647,11 +37381,11 @@ ** means to try really hard to allocate a new page. ** ** For a non-purgeable cache (a cache used as the storage for an in-memory ** database) there is really no difference between createFlag 1 and 2. So ** the calling function (pcache.c) will never have a createFlag of 1 on -** a non-purgable cache. +** a non-purgeable cache. ** ** There are three different approaches to obtaining space for a page, ** depending on the value of parameter createFlag (which may be 0, 1 or 2). ** ** 1. Regardless of the value of createFlag, the cache is searched for a @@ -36688,12 +37422,16 @@ ** size, return the recycled buffer. Otherwise, free the buffer and ** proceed to step 5. ** ** 5. Otherwise, allocate and return a new page buffer. */ -static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ - int nPinned; +static sqlite3_pcache_page *pcache1Fetch( + sqlite3_pcache *p, + unsigned int iKey, + int createFlag +){ + unsigned int nPinned; PCache1 *pCache = (PCache1 *)p; PGroup *pGroup; PgHdr1 *pPage = 0; assert( pCache->bPurgeable || createFlag!=1 ); @@ -36723,19 +37461,18 @@ */ #ifdef SQLITE_MUTEX_OMIT pGroup = pCache->pGroup; #endif - /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ + assert( pCache->nPage >= pCache->nRecyclable ); nPinned = pCache->nPage - pCache->nRecyclable; - assert( nPinned>=0 ); assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); assert( pCache->n90pct == pCache->nMax*9/10 ); if( createFlag==1 && ( nPinned>=pGroup->mxPinned - || nPinned>=(int)pCache->n90pct + || nPinned>=pCache->n90pct || pcache1UnderMemoryPressure(pCache) )){ goto fetch_out; } @@ -36747,20 +37484,28 @@ if( pCache->bPurgeable && pGroup->pLruTail && ( (pCache->nPage+1>=pCache->nMax) || pGroup->nCurrentPage>=pGroup->nMaxPage || pcache1UnderMemoryPressure(pCache) )){ - PCache1 *pOtherCache; + PCache1 *pOther; pPage = pGroup->pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); - if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){ + pOther = pPage->pCache; + + /* We want to verify that szPage and szExtra are the same for pOther + ** and pCache. Assert that we can verify this by comparing sums. */ + assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 ); + assert( pCache->szExtra<512 ); + assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 ); + assert( pOther->szExtra<512 ); + + if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){ pcache1FreePage(pPage); pPage = 0; }else{ - pGroup->nCurrentPage -= - (pOtherCache->bPurgeable - pCache->bPurgeable); + pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); } } /* Step 5. If a usable page buffer has still not been found, ** attempt to allocate a new one. @@ -36777,31 +37522,35 @@ pPage->iKey = iKey; pPage->pNext = pCache->apHash[h]; pPage->pCache = pCache; pPage->pLruPrev = 0; pPage->pLruNext = 0; - *(void **)(PGHDR1_TO_PAGE(pPage)) = 0; + *(void **)pPage->page.pExtra = 0; pCache->apHash[h] = pPage; } fetch_out: if( pPage && iKey>pCache->iMaxKey ){ pCache->iMaxKey = iKey; } pcache1LeaveMutex(pGroup); - return (pPage ? PGHDR1_TO_PAGE(pPage) : 0); + return &pPage->page; } /* ** Implementation of the sqlite3_pcache.xUnpin method. ** ** Mark a page as unpinned (eligible for asynchronous recycling). */ -static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){ +static void pcache1Unpin( + sqlite3_pcache *p, + sqlite3_pcache_page *pPg, + int reuseUnlikely +){ PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg); + PgHdr1 *pPage = (PgHdr1 *)pPg; PGroup *pGroup = pCache->pGroup; assert( pPage->pCache==pCache ); pcache1EnterMutex(pGroup); @@ -36833,16 +37582,16 @@ /* ** Implementation of the sqlite3_pcache.xRekey method. */ static void pcache1Rekey( sqlite3_pcache *p, - void *pPg, + sqlite3_pcache_page *pPg, unsigned int iOld, unsigned int iNew ){ PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg); + PgHdr1 *pPage = (PgHdr1 *)pPg; PgHdr1 **pp; unsigned int h; assert( pPage->iKey==iOld ); assert( pPage->pCache==pCache ); @@ -36892,11 +37641,13 @@ PCache1 *pCache = (PCache1 *)p; PGroup *pGroup = pCache->pGroup; assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); pcache1EnterMutex(pGroup); pcache1TruncateUnsafe(pCache, 0); + assert( pGroup->nMaxPage >= pCache->nMax ); pGroup->nMaxPage -= pCache->nMax; + assert( pGroup->nMinPage >= pCache->nMin ); pGroup->nMinPage -= pCache->nMin; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pcache1EnforceMaxPage(pGroup); pcache1LeaveMutex(pGroup); sqlite3_free(pCache->apHash); @@ -36907,11 +37658,12 @@ ** This function is called during initialization (sqlite3_initialize()) to ** install the default pluggable cache module, assuming the user has not ** already provided an alternative. */ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){ - static const sqlite3_pcache_methods defaultMethods = { + static const sqlite3_pcache_methods2 defaultMethods = { + 1, /* iVersion */ 0, /* pArg */ pcache1Init, /* xInit */ pcache1Shutdown, /* xShutdown */ pcache1Create, /* xCreate */ pcache1Cachesize, /* xCachesize */ @@ -36918,13 +37670,14 @@ pcache1Pagecount, /* xPagecount */ pcache1Fetch, /* xFetch */ pcache1Unpin, /* xUnpin */ pcache1Rekey, /* xRekey */ pcache1Truncate, /* xTruncate */ - pcache1Destroy /* xDestroy */ + pcache1Destroy, /* xDestroy */ + pcache1Shrink /* xShrink */ }; - sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods); + sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods); } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* ** This function is called to free superfluous dynamically allocated memory @@ -36935,20 +37688,20 @@ ** 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; -#ifdef SQLITE_PAGECACHE_BLOCKALLOC - if( pcache1.grp.isBusy ) return 0; -#endif assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); assert( sqlite3_mutex_notheld(pcache1.mutex) ); if( pcache1.pStart==0 ){ PgHdr1 *p; pcache1EnterMutex(&pcache1.grp); while( (nReq<0 || nFreepage.pBuf); +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + nFree += sqlite3MemSize(p); +#endif pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } pcache1LeaveMutex(&pcache1.grp); @@ -36972,12 +37725,12 @@ int nRecyclable = 0; for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){ nRecyclable++; } *pnCurrent = pcache1.grp.nCurrentPage; - *pnMax = pcache1.grp.nMaxPage; - *pnMin = pcache1.grp.nMinPage; + *pnMax = (int)pcache1.grp.nMaxPage; + *pnMin = (int)pcache1.grp.nMinPage; *pnRecyclable = nRecyclable; } #endif /************** End of pcache1.c *********************************************/ @@ -37447,10 +38200,16 @@ #ifndef _WAL_H_ #define _WAL_H_ +/* Additional values that can be added to the sync_flags argument of +** sqlite3WalFrames(): +*/ +#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */ +#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */ + #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalLimit(x,y) # define sqlite3WalClose(w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 @@ -38146,10 +38905,11 @@ u8 useJournal; /* Use a rollback journal on this file */ u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */ + u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */ u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ @@ -38200,12 +38960,12 @@ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ + int nHit, nMiss; /* Total cache hits and misses */ #ifdef SQLITE_TEST - int nHit, nMiss; /* Cache hits and missing */ int nRead, nWrite; /* Database pages read/written */ #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ @@ -38316,11 +39076,11 @@ return (pPager->pWal!=0); } #else # define pagerUseWal(x) 0 # define pagerRollbackWal(x) 0 -# define pagerWalFrames(v,w,x,y,z) 0 +# define pagerWalFrames(v,w,x,y) 0 # define pagerOpenWalIfPresent(z) SQLITE_OK # define pagerBeginReadTransaction(z) SQLITE_OK #endif #ifndef NDEBUG @@ -40015,14 +40775,13 @@ rc = sqlite3OsFileSize(pPager->fd, ¤tSize); newSize = szPage*(i64)nPage; if( rc==SQLITE_OK && currentSize!=newSize ){ if( currentSize>newSize ){ rc = sqlite3OsTruncate(pPager->fd, newSize); - }else{ + }else if( (currentSize+szPage)<=newSize ){ char *pTmp = pPager->pTmpSpace; memset(pTmp, 0, szPage); - testcase( (newSize-szPage) < currentSize ); testcase( (newSize-szPage) == currentSize ); testcase( (newSize-szPage) > currentSize ); rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); } if( rc==SQLITE_OK ){ @@ -40044,27 +40803,40 @@ ** ** Otherwise, for non-temporary files, the effective sector size is ** the value returned by the xSectorSize() method rounded up to 32 if ** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it ** is greater than MAX_SECTOR_SIZE. +** +** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set +** the effective sector size to its minimum value (512). The purpose of +** pPager->sectorSize is to define the "blast radius" of bytes that +** might change if a crash occurs while writing to a single byte in +** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero +** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector +** size. For backwards compatibility of the rollback journal file format, +** we cannot reduce the effective sector size below 512. */ static void setSectorSize(Pager *pPager){ assert( isOpen(pPager->fd) || pPager->tempFile ); - if( !pPager->tempFile ){ + if( pPager->tempFile + || (sqlite3OsDeviceCharacteristics(pPager->fd) & + SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0 + ){ /* Sector size doesn't matter for temporary files. Also, the file ** may not have been opened yet, in which case the OsSectorSize() - ** call will segfault. - */ + ** call will segfault. */ + pPager->sectorSize = 512; + }else{ pPager->sectorSize = sqlite3OsSectorSize(pPager->fd); - } - if( pPager->sectorSize<32 ){ - pPager->sectorSize = 512; - } - if( pPager->sectorSize>MAX_SECTOR_SIZE ){ - assert( MAX_SECTOR_SIZE>=512 ); - pPager->sectorSize = MAX_SECTOR_SIZE; + if( pPager->sectorSize<32 ){ + pPager->sectorSize = 512; + } + if( pPager->sectorSize>MAX_SECTOR_SIZE ){ + assert( MAX_SECTOR_SIZE>=512 ); + pPager->sectorSize = MAX_SECTOR_SIZE; + } } } /* ** Playback the journal and thus restore the database file to @@ -40233,11 +41005,10 @@ needPagerReset = 0; } rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; pPager->journalOff = szJ; break; }else if( rc==SQLITE_IOERR_SHORT_READ ){ /* If the journal has been truncated, simply stop reading and ** processing the journal. This might happen if the journal was @@ -40264,14 +41035,15 @@ /* Following a rollback, the database file should be back in its original ** state prior to the start of the transaction, so invoke the ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the ** assertion that the transaction counter was modified. */ - assert( - pPager->fd->pMethods==0 || - sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK - ); +#ifdef SQLITE_DEBUG + if( pPager->fd->pMethods ){ + sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); + } +#endif /* If this playback is happening automatically as a result of an IO or ** malloc error that occurred after the change-counter was updated but ** before the transaction was committed, then the change-counter ** modification may just have been reverted. If this happens in exclusive @@ -40486,19 +41258,19 @@ */ static int pagerWalFrames( Pager *pPager, /* Pager object */ PgHdr *pList, /* List of frames to log */ Pgno nTruncate, /* Database size after this commit */ - int isCommit, /* True if this is a commit */ - int syncFlags /* Flags to pass to OsSync() (or 0) */ + int isCommit /* True if this is a commit */ ){ int rc; /* Return code */ #if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) PgHdr *p; /* For looping over pages */ #endif assert( pPager->pWal ); + assert( pList ); #ifdef SQLITE_DEBUG /* Verify that the page list is in accending order */ for(p=pList; p && p->pDirty; p=p->pDirty){ assert( p->pgno < p->pDirty->pgno ); } @@ -40517,11 +41289,11 @@ assert( pList ); } if( pList->pgno==1 ) pager_write_changecounter(pList); rc = sqlite3WalFrames(pPager->pWal, - pPager->pageSize, pList, nTruncate, isCommit, syncFlags + pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags ); if( rc==SQLITE_OK && pPager->pBackup ){ PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); @@ -40604,14 +41376,11 @@ int rc = sqlite3OsFileSize(pPager->fd, &n); if( rc!=SQLITE_OK ){ return rc; } } - nPage = (Pgno)(n / pPager->pageSize); - if( nPage==0 && n>0 ){ - nPage = 1; - } + nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); } /* If the current number of pages in the file is greater than the ** configured maximum pager number, increase the allowed limit so ** that the file can be read. @@ -40797,17 +41566,17 @@ ** previously rolled back out of the main journal (and are hence in pDone) ** will be skipped. Out-of-range pages are also skipped. */ if( pSavepoint ){ u32 ii; /* Loop counter */ - i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize); + i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize); if( pagerUseWal(pPager) ){ rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData); } for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && iinSubRec; ii++){ - assert( offset==ii*(4+pPager->pageSize) ); + assert( offset==(i64)ii*(4+pPager->pageSize) ); rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); } assert( rc!=SQLITE_DONE ); } @@ -40823,10 +41592,17 @@ ** Change the maximum number of in-memory pages that are allowed. */ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ sqlite3PcacheSetCachesize(pPager->pPCache, mxPage); } + +/* +** Free as much memory as possible from the pager. +*/ +SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){ + sqlite3PcacheShrink(pPager->pPCache); +} /* ** Adjust the robustness of the database to damage due to OS crashes ** or power failures by changing the number of syncs()s when writing ** the rollback journal. There are three levels: @@ -40890,10 +41666,14 @@ pPager->ckptSyncFlags = SQLITE_SYNC_FULL; }else{ pPager->syncFlags = SQLITE_SYNC_NORMAL; pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; } + pPager->walSyncFlags = pPager->syncFlags; + if( pPager->fullSync ){ + pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS; + } } #endif /* ** The following global variable is incremented whenever the library @@ -41027,11 +41807,11 @@ if( !pNew ) rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ pager_reset(pPager); - pPager->dbSize = (Pgno)(nByte/pageSize); + pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); pPager->pageSize = pageSize; sqlite3PageFree(pPager->pTmpSpace); pPager->pTmpSpace = pNew; sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); } @@ -41535,11 +42315,11 @@ ** file size will be. */ assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){ sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; - sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); + sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); pPager->dbHintSize = pPager->dbSize; } while( rc==SQLITE_OK && pList ){ Pgno pgno = pList->pgno; @@ -41644,11 +42424,11 @@ /* If the sub-journal was opened successfully (or was already open), ** write the journal record into the file. */ if( rc==SQLITE_OK ){ void *pData = pPg->pData; - i64 offset = pPager->nSubRec*(4+pPager->pageSize); + i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); @@ -41699,11 +42479,11 @@ ** The doNotSpill flag inhibits all cache spilling regardless of whether ** or not a sync is required. This is set during a rollback. ** ** Spilling is also prohibited when in an error state since that could ** lead to database corruption. In the current implementaton it - ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1 + ** is impossible for sqlite3PcacheFetch() to be called with createFlag==1 ** while in the error state, hence it is impossible for this routine to ** be called in the error state. Nevertheless, we include a NEVER() ** test for the error state as a safeguard against future changes. */ if( NEVER(pPager->errCode) ) return SQLITE_OK; @@ -41717,11 +42497,11 @@ /* Write a single frame for this page to the log. */ if( subjRequiresPage(pPg) ){ rc = subjournalPage(pPg); } if( rc==SQLITE_OK ){ - rc = pagerWalFrames(pPager, pPg, 0, 0, 0); + rc = pagerWalFrames(pPager, pPg, 0, 0); } }else{ /* Sync the journal file if required. */ if( pPg->flags&PGHDR_NEED_SYNC @@ -41876,11 +42656,12 @@ z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1]; while( *z ){ z += sqlite3Strlen30(z)+1; z += sqlite3Strlen30(z)+1; } - nUri = &z[1] - zUri; + nUri = (int)(&z[1] - zUri); + assert( nUri>=0 ); if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ /* This branch is taken when the journal path required by ** the database being opened will be more than pVfs->mxPathname ** bytes in length. This means the database cannot be opened, ** as it will not be possible to open the journal file or even @@ -41910,13 +42691,13 @@ ROUND8(sizeof(*pPager)) + /* Pager structure */ ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ nPathname + 1 + nUri + /* zFilename */ - nPathname + 8 + 1 /* zJournal */ + nPathname + 8 + 2 /* zJournal */ #ifndef SQLITE_OMIT_WAL - + nPathname + 4 + 1 /* zWal */ + + nPathname + 4 + 2 /* zWal */ #endif ); assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); if( !pPtr ){ sqlite3_free(zPathname); @@ -41935,16 +42716,16 @@ assert( nPathname>0 ); pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(&pPager->zFilename[nPathname+1], zUri, nUri); memcpy(pPager->zJournal, zPathname, nPathname); - memcpy(&pPager->zJournal[nPathname], "-journal", 8); + memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+1); sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal); #ifndef SQLITE_OMIT_WAL pPager->zWal = &pPager->zJournal[nPathname+8+1]; memcpy(pPager->zWal, zPathname, nPathname); - memcpy(&pPager->zWal[nPathname], "-wal", 4); + memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1); sqlite3FileSuffix3(pPager->zFilename, pPager->zWal); #endif sqlite3_free(zPathname); } pPager->pVfs = pVfs; @@ -42056,13 +42837,21 @@ pPager->changeCountDone = pPager->tempFile; pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; assert( useJournal || pPager->tempFile ); pPager->noSync = pPager->tempFile; - pPager->fullSync = pPager->noSync ?0:1; - pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL; - pPager->ckptSyncFlags = pPager->syncFlags; + if( pPager->noSync ){ + assert( pPager->fullSync==0 ); + assert( pPager->syncFlags==0 ); + assert( pPager->walSyncFlags==0 ); + assert( pPager->ckptSyncFlags==0 ); + }else{ + pPager->fullSync = 1; + pPager->syncFlags = SQLITE_SYNC_NORMAL; + pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS; + pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; + } /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ pPager->nExtra = (u16)nExtra; pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; @@ -42535,18 +43324,17 @@ if( (*ppPage)->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); - PAGER_INCR(pPager->nHit); + pPager->nHit++; return SQLITE_OK; }else{ /* The pager cache has created a new page. Its content needs to ** be initialized. */ - PAGER_INCR(pPager->nMiss); pPg = *ppPage; pPg->pPager = pPager; /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page ** number greater than this, or the unused locking-page, is requested. */ @@ -42578,10 +43366,11 @@ } memset(pPg->pData, 0, pPager->pageSize); IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ assert( pPg->pPager==pPager ); + pPager->nMiss++; rc = readDbPage(pPg); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } } @@ -43191,11 +43980,14 @@ if( !pPager->noSync ){ assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); }else if( isOpen(pPager->fd) ){ assert( !MEMDB ); - sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc); + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0); + if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_OK; + } } return rc; } /* @@ -43288,13 +44080,11 @@ pList = pPageOne; pList->pDirty = 0; } assert( rc==SQLITE_OK ); if( ALWAYS(pList) ){ - rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, - (pPager->fullSync ? pPager->syncFlags : 0) - ); + rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); } sqlite3PagerUnref(pPageOne); if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } @@ -43549,11 +44339,12 @@ }else{ rc = pager_playback(pPager, 0); } assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); - assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR ); + assert( rc==SQLITE_OK || rc==SQLITE_FULL + || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR ); /* If an error occurs during a ROLLBACK, we can no longer trust the pager ** cache. So call pager_error() on the way out to make any error persistent. */ return pager_error(pPager, rc); @@ -43611,10 +44402,35 @@ a[9] = pPager->nRead; a[10] = pPager->nWrite; return a; } #endif + +/* +** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or +** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the +** current cache hit or miss count, according to the value of eStat. If the +** reset parameter is non-zero, the cache hit or miss count is zeroed before +** returning. +*/ +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ + int *piStat; + + assert( eStat==SQLITE_DBSTATUS_CACHE_HIT + || eStat==SQLITE_DBSTATUS_CACHE_MISS + ); + if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){ + piStat = &pPager->nHit; + }else{ + piStat = &pPager->nMiss; + } + + *pnVal += *piStat; + if( reset ){ + *piStat = 0; + } +} /* ** Return true if this is an in-memory pager. */ SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){ @@ -44164,10 +44980,19 @@ ** sqlite3BackupUpdate() only. */ SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ return &pPager->pBackup; } + +#ifndef SQLITE_OMIT_VACUUM +/* +** Unless this is an in-memory or temporary database, clear the pager cache. +*/ +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){ + if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager); +} +#endif #ifndef SQLITE_OMIT_WAL /* ** This function is called when the user invokes "PRAGMA wal_checkpoint", ** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() @@ -44777,17 +45602,22 @@ sqlite3_file *pDbFd; /* File handle for the database file */ sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ i64 mxWalSize; /* Truncate WAL to this size upon reset */ int nWiData; /* Size of array apWiData */ + int szFirstBlock; /* Size of first block written to WAL file */ volatile u32 **apWiData; /* Pointer to wal-index content in memory */ u32 szPage; /* Database page size */ i16 readLock; /* Which read lock is being held. -1 for none */ + u8 syncFlags; /* Flags to use to sync header writes */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ + u8 truncateOnCommit; /* True to truncate WAL file on commit */ + u8 syncHeader; /* Fsync the WAL header if true */ + u8 padToSectorBoundary; /* Pad transactions out to the next sector */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ @@ -45456,10 +46286,11 @@ int iFrame; /* Index of last frame read */ i64 iOffset; /* Next offset to read from log file */ int szPage; /* Page size according to the log */ u32 magic; /* Magic value read from WAL header */ u32 version; /* Magic value read from WAL header */ + int isValid; /* True if this frame is valid */ /* Read in the WAL header. */ rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ goto recovery_error; @@ -45514,18 +46345,18 @@ /* Read all frames from the log file. */ iFrame = 0; for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){ u32 pgno; /* Database page number for frame */ u32 nTruncate; /* dbsize field from frame header */ - int isValid; /* True if this frame is valid */ /* Read and decode the next log frame. */ + iFrame++; rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); if( rc!=SQLITE_OK ) break; isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); if( !isValid ) break; - rc = walIndexAppend(pWal, ++iFrame, pgno); + rc = walIndexAppend(pWal, iFrame, pgno); if( rc!=SQLITE_OK ) break; /* If nTruncate is non-zero, this is a commit record. */ if( nTruncate ){ pWal->hdr.mxFrame = iFrame; @@ -45644,10 +46475,12 @@ pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->mxWalSize = mxWalSize; pRet->zWalName = zWalName; + pRet->syncHeader = 1; + pRet->padToSectorBoundary = 1; pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); @@ -45658,10 +46491,15 @@ if( rc!=SQLITE_OK ){ walIndexClose(pRet, 0); sqlite3OsClose(pRet->pWalFd); sqlite3_free(pRet); }else{ + int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd); + if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; } + if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){ + pRet->padToSectorBoundary = 0; + } *ppWal = pRet; WALTRACE(("WAL%d: opened\n", pRet)); } return rc; } @@ -46077,11 +46915,11 @@ */ if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); } } /* Iterate through the contents of the WAL, copying data to the db file. */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ @@ -46143,10 +46981,28 @@ walcheckpoint_out: walIteratorFree(pIter); return rc; } + +/* +** If the WAL file is currently larger than nMax bytes in size, truncate +** it to exactly nMax bytes. If an error occurs while doing so, ignore it. +*/ +static void walLimitSize(Wal *pWal, i64 nMax){ + i64 sz; + int rx; + sqlite3BeginBenignMalloc(); + rx = sqlite3OsFileSize(pWal->pWalFd, &sz); + if( rx==SQLITE_OK && (sz > nMax ) ){ + rx = sqlite3OsTruncate(pWal->pWalFd, nMax); + } + sqlite3EndBenignMalloc(); + if( rx ){ + sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); + } +} /* ** Close a connection to a log file. */ SQLITE_PRIVATE int sqlite3WalClose( @@ -46167,27 +47023,44 @@ ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ - int bPersistWal = -1; if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } rc = sqlite3WalCheckpoint( pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 ); - sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal); - if( rc==SQLITE_OK && bPersistWal!=1 ){ - isDelete = 1; + if( rc==SQLITE_OK ){ + int bPersist = -1; + sqlite3OsFileControlHint( + pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist + ); + if( bPersist!=1 ){ + /* Try to delete the WAL file if the checkpoint completed and + ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal + ** mode (!bPersist) */ + isDelete = 1; + }else if( pWal->mxWalSize>=0 ){ + /* Try to truncate the WAL file to zero bytes if the checkpoint + ** completed and fsynced (rc==SQLITE_OK) and we are in persistent + ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a + ** non-negative value (pWal->mxWalSize>=0). Note that we truncate + ** to zero bytes as truncating to the journal_size_limit might + ** leave a corrupt WAL file on disk. */ + walLimitSize(pWal, 0); + } } } walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); if( isDelete ){ + sqlite3BeginBenignMalloc(); sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); + sqlite3EndBenignMalloc(); } WALTRACE(("WAL%p: closed\n", pWal)); sqlite3_free((void *)pWal->apWiData); sqlite3_free(pWal); } @@ -46673,11 +47546,11 @@ } nCollide = HASHTABLE_NSLOT; for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ u32 iFrame = aHash[iKey] + iZero; if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ - assert( iFrame>iRead ); + /* assert( iFrame>iRead ); -- not true if there is corruption */ iRead = iFrame; } if( (nCollide--)==0 ){ return SQLITE_CORRUPT_BKPT; } @@ -46706,11 +47579,11 @@ */ if( iRead ){ int sz; i64 iOffset; sz = pWal->hdr.szPage; - sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); + sz = (sz&0xfe00) + ((sz&0x0001)<<16); testcase( sz<=32768 ); testcase( sz>=65536 ); iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; *pInWal = 1; /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ @@ -46785,10 +47658,11 @@ */ SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){ if( pWal->writeLock ){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); pWal->writeLock = 0; + pWal->truncateOnCommit = 0; } return SQLITE_OK; } /* @@ -46880,10 +47754,11 @@ walCleanupHash(pWal); } return rc; } + /* ** This function is called just before writing a set of frames to the log ** file (see sqlite3WalFrames()). It checks to see if, instead of appending ** to the current log file, it is possible to overwrite the start of the @@ -46918,27 +47793,10 @@ ** to handle if this transaction is rolled back. */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ - /* Limit the size of WAL file if the journal_size_limit PRAGMA is - ** set to a non-negative value. Log errors encountered - ** during the truncation attempt. */ - if( pWal->mxWalSize>=0 ){ - i64 sz; - int rx; - sqlite3BeginBenignMalloc(); - rx = sqlite3OsFileSize(pWal->pWalFd, &sz); - if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){ - rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize); - } - sqlite3EndBenignMalloc(); - if( rx ){ - sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); - } - } - pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); aSalt[1] = salt1; walIndexWriteHdr(pWal); @@ -46962,10 +47820,78 @@ testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); } return rc; } + +/* +** Information about the current state of the WAL file and where +** the next fsync should occur - passed from sqlite3WalFrames() into +** walWriteToLog(). +*/ +typedef struct WalWriter { + Wal *pWal; /* The complete WAL information */ + sqlite3_file *pFd; /* The WAL file to which we write */ + sqlite3_int64 iSyncPoint; /* Fsync at this offset */ + int syncFlags; /* Flags for the fsync */ + int szPage; /* Size of one page */ +} WalWriter; + +/* +** Write iAmt bytes of content into the WAL file beginning at iOffset. +** Do a sync when crossing the p->iSyncPoint boundary. +** +** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt, +** first write the part before iSyncPoint, then sync, then write the +** rest. +*/ +static int walWriteToLog( + WalWriter *p, /* WAL to write to */ + void *pContent, /* Content to be written */ + int iAmt, /* Number of bytes to write */ + sqlite3_int64 iOffset /* Start writing at this offset */ +){ + int rc; + if( iOffsetiSyncPoint && iOffset+iAmt>=p->iSyncPoint ){ + int iFirstAmt = (int)(p->iSyncPoint - iOffset); + rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset); + if( rc ) return rc; + iOffset += iFirstAmt; + iAmt -= iFirstAmt; + pContent = (void*)(iFirstAmt + (char*)pContent); + assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) ); + rc = sqlite3OsSync(p->pFd, p->syncFlags); + if( iAmt==0 || rc ) return rc; + } + rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset); + return rc; +} + +/* +** Write out a single frame of the WAL +*/ +static int walWriteOneFrame( + WalWriter *p, /* Where to write the frame */ + PgHdr *pPage, /* The page of the frame to be written */ + int nTruncate, /* The commit flag. Usually 0. >0 for commit */ + sqlite3_int64 iOffset /* Byte offset at which to write */ +){ + int rc; /* Result code from subfunctions */ + void *pData; /* Data actually written */ + u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ +#if defined(SQLITE_HAS_CODEC) + if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM; +#else + pData = pPage->pData; +#endif + walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); + rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); + if( rc ) return rc; + /* Write the page data */ + rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); + return rc; +} /* ** Write a set of frames to the log. The caller must hold the write-lock ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). */ @@ -46977,17 +47903,23 @@ int isCommit, /* True if this is a commit */ int sync_flags /* Flags to pass to OsSync() (or 0) */ ){ int rc; /* Used to catch return codes */ u32 iFrame; /* Next frame address */ - u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ PgHdr *p; /* Iterator to run through pList with. */ PgHdr *pLast = 0; /* Last frame in list */ - int nLast = 0; /* Number of extra copies of last page */ + int nExtra = 0; /* Number of extra copies of last page */ + int szFrame; /* The size of a single frame */ + i64 iOffset; /* Next byte to write in WAL file */ + WalWriter w; /* The writer */ assert( pList ); assert( pWal->writeLock ); + + /* If this frame set completes a transaction, then nTruncate>0. If + ** nTruncate==0 then this frame set does not complete the transaction. */ + assert( (isCommit!=0)==(nTruncate!=0) ); #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); @@ -47012,91 +47944,103 @@ sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN)); sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION); sqlite3Put4byte(&aWalHdr[8], szPage); sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); - sqlite3_randomness(8, pWal->hdr.aSalt); + if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt); memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); sqlite3Put4byte(&aWalHdr[24], aCksum[0]); sqlite3Put4byte(&aWalHdr[28], aCksum[1]); pWal->szPage = szPage; pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; pWal->hdr.aFrameCksum[0] = aCksum[0]; pWal->hdr.aFrameCksum[1] = aCksum[1]; + pWal->truncateOnCommit = 1; rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } + + /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless + ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise + ** an out-of-order write following a WAL restart could result in + ** database corruption. See the ticket: + ** + ** http://localhost:591/sqlite/info/ff5be73dee + */ + if( pWal->syncHeader && sync_flags ){ + rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK); + if( rc ) return rc; + } } assert( (int)pWal->szPage==szPage ); - /* Write the log file. */ + /* Setup information needed to write frames into the WAL */ + w.pWal = pWal; + w.pFd = pWal->pWalFd; + w.iSyncPoint = 0; + w.syncFlags = sync_flags; + w.szPage = szPage; + iOffset = walFrameOffset(iFrame+1, szPage); + szFrame = szPage + WAL_FRAME_HDRSIZE; + + /* Write all frames into the log file exactly once */ for(p=pList; p; p=p->pDirty){ - u32 nDbsize; /* Db-size field for frame header */ - i64 iOffset; /* Write offset in log file */ - void *pData; - - iOffset = walFrameOffset(++iFrame, szPage); - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ - - /* Populate and write the frame header */ - nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0; -#if defined(SQLITE_HAS_CODEC) - if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM; -#else - pData = p->pData; -#endif - walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame); - rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* Write the page data */ - rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset+sizeof(aFrame)); - if( rc!=SQLITE_OK ){ - return rc; - } + int nDbSize; /* 0 normally. Positive == commit flag */ + iFrame++; + assert( iOffset==walFrameOffset(iFrame, szPage) ); + nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; + rc = walWriteOneFrame(&w, p, nDbSize, iOffset); + if( rc ) return rc; pLast = p; - } - - /* Sync the log file if the 'isSync' flag was specified. */ - if( sync_flags ){ - i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd); - i64 iOffset = walFrameOffset(iFrame+1, szPage); - - assert( isCommit ); - assert( iSegment>0 ); - - iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment); - while( iOffsetpData; -#endif - walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame); - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ - rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); - if( rc!=SQLITE_OK ){ - return rc; - } - iOffset += WAL_FRAME_HDRSIZE; - rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset); - if( rc!=SQLITE_OK ){ - return rc; - } - nLast++; - iOffset += szPage; - } - - rc = sqlite3OsSync(pWal->pWalFd, sync_flags); + iOffset += szFrame; + } + + /* If this is the end of a transaction, then we might need to pad + ** the transaction and/or sync the WAL file. + ** + ** Padding and syncing only occur if this set of frames complete a + ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL + ** or synchonous==OFF, then no padding or syncing are needed. + ** + ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not + ** needed and only the sync is done. If padding is needed, then the + ** final frame is repeated (with its commit mark) until the next sector + ** boundary is crossed. Only the part of the WAL prior to the last + ** sector boundary is synced; the part of the last frame that extends + ** past the sector boundary is written after the sync. + */ + if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){ + if( pWal->padToSectorBoundary ){ + int sectorSize = sqlite3OsSectorSize(pWal->pWalFd); + w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; + while( iOffsettruncateOnCommit && pWal->mxWalSize>=0 ){ + i64 sz = pWal->mxWalSize; + if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){ + sz = walFrameOffset(iFrame+nExtra+1, szPage); + } + walLimitSize(pWal, sz); + pWal->truncateOnCommit = 0; } /* Append data to the wal-index. It is not necessary to lock the ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index ** guarantees that there are no other writers, and no data that may @@ -47105,13 +48049,13 @@ iFrame = pWal->hdr.mxFrame; for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ iFrame++; rc = walIndexAppend(pWal, iFrame, p->pgno); } - while( nLast>0 && rc==SQLITE_OK ){ + while( rc==SQLITE_OK && nExtra>0 ){ iFrame++; - nLast--; + nExtra--; rc = walIndexAppend(pWal, iFrame, pLast->pgno); } if( rc==SQLITE_OK ){ /* Update the private copy of the header. */ @@ -47613,10 +48557,11 @@ u8 intKey; /* True if intkey flag is set */ u8 leaf; /* True if leaf flag is set */ u8 hasData; /* True if this page stores data */ u8 hdrOffset; /* 100 for page 1. 0 otherwise */ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ + u8 max1bytePayload; /* min(maxLocal,127) */ u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ u16 nFree; /* Number of free bytes on the page */ u16 nCell; /* Number of cells on this page, local and ovfl */ @@ -47625,10 +48570,12 @@ u8 *pCell; /* Pointers to the body of the overflow cell */ u16 idx; /* Insert this cell before idx-th non-overflow cell */ } aOvfl[5]; BtShared *pBt; /* Pointer to BtShared that this page is part of */ u8 *aData; /* Pointer to disk image of the page data */ + u8 *aDataEnd; /* One byte past the end of usable data */ + u8 *aCellIdx; /* The cell index area */ DbPage *pDbPage; /* Pager page handle */ Pgno pgno; /* Page number for this page */ }; /* @@ -47704,11 +48651,11 @@ #define TRANS_WRITE 2 /* ** An instance of this object represents a single database file. ** -** A single database file can be in use as the same time by two +** A single database file can be in use at the same time by two ** or more database connections. When two or more connections are ** sharing the same database file, each connection has it own ** private Btree object for the file and each of those Btrees points ** to this one BtShared object. BtShared.nRef is the number of ** connections currently sharing this database file. @@ -47741,21 +48688,18 @@ struct BtShared { Pager *pPager; /* The page cache */ sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ - u8 readOnly; /* True if the underlying file is readonly */ - u8 pageSizeFixed; /* True if the page size can no longer be changed */ - u8 secureDelete; /* True if secure_delete is enabled */ - u8 initiallyEmpty; /* Database is empty at start of transaction */ u8 openFlags; /* Flags to sqlite3BtreeOpen() */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u8 inTransaction; /* Transaction state */ - u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ + u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ + u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u32 pageSize; /* Total number of bytes on a page */ @@ -47769,16 +48713,25 @@ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtLock *pLock; /* List of locks held on this shared-btree struct */ Btree *pWriter; /* Btree with currently open write transaction */ - u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */ - u8 isPending; /* If waiting for read-locks to clear */ #endif u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ }; +/* +** Allowed values for BtShared.btsFlags +*/ +#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ +#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ +#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ +#define BTS_INITIALLY_EMPTY 0x0008 /* Database was empty at trans start */ +#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */ +#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */ +#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */ + /* ** An instance of the following structure is used to hold information ** about a cell. The parseCellPtr() function fills in this structure ** based on information extract from the raw disk page. */ @@ -47810,11 +48763,11 @@ ** b-tree within a database file. ** ** The entry is identified by its MemPage and the index in ** MemPage.aCell[] of the entry. ** -** A single database file can shared by two more database connections, +** A single database file can be shared by two more database connections, ** but cursors cannot be shared. Each cursor is associated with a ** particular database connection identified BtCursor.pBtree.db. ** ** Fields in this structure are accessed under the BtShared.mutex ** found at self->pBt->mutex. @@ -47971,11 +48924,11 @@ int mallocFailed; /* A memory allocation error has occurred */ StrAccum errMsg; /* Accumulate the error message text here */ }; /* -** Read or write a two- and four-byte big-endian integer values. +** Routines to read or write a two- and four-byte big-endian integer values. */ #define get2byte(x) ((x)[0]<<8 | (x)[1]) #define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) #define get4byte sqlite3Get4byte #define put4byte sqlite3Put4byte @@ -48496,11 +49449,11 @@ } /* If some other connection is holding an exclusive lock, the ** requested lock may not be obtained. */ - if( pBt->pWriter!=p && pBt->isExclusive ){ + if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){ sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); return SQLITE_LOCKED_SHAREDCACHE; } for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ @@ -48517,11 +49470,11 @@ assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); if( eLock==WRITE_LOCK ){ assert( p==pBt->pWriter ); - pBt->isPending = 1; + pBt->btsFlags |= BTS_PENDING; } return SQLITE_LOCKED_SHAREDCACHE; } } return SQLITE_OK; @@ -48605,11 +49558,11 @@ /* ** Release all the table locks (locks obtained via calls to ** the setSharedCacheTableLock() procedure) held by Btree object p. ** ** This function assumes that Btree p has an open read or write -** transaction. If it does not, then the BtShared.isPending variable +** transaction. If it does not, then the BTS_PENDING flag ** may be incorrectly cleared. */ static void clearAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; @@ -48618,11 +49571,11 @@ assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); while( *ppIter ){ BtLock *pLock = *ppIter; - assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); + assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; assert( pLock->iTable!=1 || pLock==&p->lock ); if( pLock->iTable!=1 ){ @@ -48631,26 +49584,25 @@ }else{ ppIter = &pLock->pNext; } } - assert( pBt->isPending==0 || pBt->pWriter ); + assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter ); if( pBt->pWriter==p ){ pBt->pWriter = 0; - pBt->isExclusive = 0; - pBt->isPending = 0; + pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); }else if( pBt->nTransaction==2 ){ /* This function is called when Btree p is concluding its ** transaction. If there currently exists a writer, and p is not ** that writer, then the number of locks held by connections other ** than the writer must be about to drop to zero. In this case - ** set the isPending flag to 0. + ** set the BTS_PENDING flag to 0. ** - ** If there is not currently a writer, then BtShared.isPending must + ** If there is not currently a writer, then BTS_PENDING must ** be zero already. So this next line is harmless in that case. */ - pBt->isPending = 0; + pBt->btsFlags &= ~BTS_PENDING; } } /* ** This function changes all write-locks held by Btree p into read-locks. @@ -48658,12 +49610,11 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; if( pBt->pWriter==p ){ BtLock *pLock; pBt->pWriter = 0; - pBt->isExclusive = 0; - pBt->isPending = 0; + pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); pLock->eLock = READ_LOCK; } } @@ -49112,11 +50063,11 @@ ** to the cell content. ** ** This routine works only for pages that do not contain overflow cells. */ #define findCell(P,I) \ - ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)]))) + ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)]))) #define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I))))) /* ** This a more complex version of findCell() that works for @@ -49517,11 +50468,11 @@ assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); assert( (start + size) <= (int)pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( size>=0 ); /* Minimum cell size is 4 */ - if( pPage->pBt->secureDelete ){ + if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){ /* Overwrite deleted information with zeros when the secure_delete ** option is enabled */ memset(&data[start], 0, size); } @@ -49620,10 +50571,11 @@ pPage->maxLocal = pBt->maxLocal; pPage->minLocal = pBt->minLocal; }else{ return SQLITE_CORRUPT_BKPT; } + pPage->max1bytePayload = pBt->max1bytePayload; return SQLITE_OK; } /* ** Initialize the auxiliary information for a disk block. @@ -49662,10 +50614,12 @@ assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); pPage->nOverflow = 0; usableSize = pBt->usableSize; pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; + pPage->aDataEnd = &data[usableSize]; + pPage->aCellIdx = &data[cellOffset]; top = get2byteNotZero(&data[hdr+5]); pPage->nCell = get2byte(&data[hdr+3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ return SQLITE_CORRUPT_BKPT; @@ -49753,11 +50707,11 @@ assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); assert( sqlite3PagerGetData(pPage->pDbPage) == data ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->secureDelete ){ + if( pBt->btsFlags & BTS_SECURE_DELETE ){ memset(&data[hdr], 0, pBt->usableSize - hdr); } data[hdr] = (char)flags; first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); memset(&data[hdr+1], 0, 4); @@ -49765,10 +50719,12 @@ put2byte(&data[hdr+5], pBt->usableSize); pPage->nFree = (u16)(pBt->usableSize - first); decodeFlags(pPage, flags); pPage->hdrOffset = hdr; pPage->cellOffset = first; + pPage->aDataEnd = &data[pBt->usableSize]; + pPage->aCellIdx = &data[first]; pPage->nOverflow = 0; assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); pPage->nCell = 0; pPage->isInit = 1; @@ -50019,21 +50975,28 @@ */ if( isMemdb==0 && isTempDb==0 ){ if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); - sqlite3_mutex *mutexShared; + MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; } - sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); + rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); + if( rc ){ + sqlite3_free(zFullPathname); + sqlite3_free(p); + return rc; + } +#if SQLITE_THREADSAFE mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN); sqlite3_mutex_enter(mutexOpen); mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); sqlite3_mutex_enter(mutexShared); +#endif for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){ assert( pBt->nRef>0 ); if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager)) && sqlite3PagerVfs(pBt->pPager)==pVfs ){ int iDb; @@ -50097,13 +51060,13 @@ sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; pBt->pPage1 = 0; - pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); + if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY; #ifdef SQLITE_SECURE_DELETE - pBt->secureDelete = 1; + pBt->btsFlags |= BTS_SECURE_DELETE; #endif pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ pBt->pageSize = 0; @@ -50120,11 +51083,11 @@ } #endif nReserve = 0; }else{ nReserve = zDbHeader[20]; - pBt->pageSizeFixed = 1; + pBt->btsFlags |= BTS_PAGESIZE_FIXED; #ifndef SQLITE_OMIT_AUTOVACUUM pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); #endif } @@ -50135,13 +51098,13 @@ #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* Add the new BtShared object to the linked list sharable BtShareds. */ if( p->sharable ){ - sqlite3_mutex *mutexShared; + MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) pBt->nRef = 1; - mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);) if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){ pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST); if( pBt->mutex==0 ){ rc = SQLITE_NOMEM; db->mallocFailed = 0; @@ -50219,16 +51182,16 @@ ** true if the BtShared.nRef counter reaches zero and return ** false if it is still positive. */ static int removeFromSharingList(BtShared *pBt){ #ifndef SQLITE_OMIT_SHARED_CACHE - sqlite3_mutex *pMaster; + MUTEX_LOGIC( sqlite3_mutex *pMaster; ) BtShared *pList; int removed = 0; assert( sqlite3_mutex_notheld(pBt->mutex) ); - pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) sqlite3_mutex_enter(pMaster); pBt->nRef--; if( pBt->nRef<=0 ){ if( GLOBAL(BtShared*,sqlite3SharedCacheList)==pBt ){ GLOBAL(BtShared*,sqlite3SharedCacheList) = pBt->pNext; @@ -50408,19 +51371,19 @@ ** at the beginning of a page. ** ** If parameter nReserve is less than zero, then the number of reserved ** bytes per page is left unchanged. ** -** If the iFix!=0 then the pageSizeFixed flag is set so that the page size +** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size ** and autovacuum mode can no longer be changed. */ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ int rc = SQLITE_OK; BtShared *pBt = p->pBt; assert( nReserve>=-1 && nReserve<=255 ); sqlite3BtreeEnter(p); - if( pBt->pageSizeFixed ){ + if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ sqlite3BtreeLeave(p); return SQLITE_READONLY; } if( nReserve<0 ){ nReserve = pBt->pageSize - pBt->usableSize; @@ -50433,11 +51396,11 @@ pBt->pageSize = (u32)pageSize; freeTempSpace(pBt); } rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); pBt->usableSize = pBt->pageSize - (u16)nReserve; - if( iFix ) pBt->pageSizeFixed = 1; + if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED; sqlite3BtreeLeave(p); return rc; } /* @@ -50473,22 +51436,23 @@ sqlite3BtreeLeave(p); return n; } /* -** Set the secureDelete flag if newFlag is 0 or 1. If newFlag is -1, -** then make no changes. Always return the value of the secureDelete +** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1. If newFlag is -1, +** then make no changes. Always return the value of the BTS_SECURE_DELETE ** setting after the change. */ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ int b; if( p==0 ) return 0; sqlite3BtreeEnter(p); if( newFlag>=0 ){ - p->pBt->secureDelete = (newFlag!=0) ? 1 : 0; + p->pBt->btsFlags &= ~BTS_SECURE_DELETE; + if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE; } - b = p->pBt->secureDelete; + b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0; sqlite3BtreeLeave(p); return b; } #endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */ @@ -50505,11 +51469,11 @@ BtShared *pBt = p->pBt; int rc = SQLITE_OK; u8 av = (u8)autoVacuum; sqlite3BtreeEnter(p); - if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){ + if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){ rc = SQLITE_READONLY; }else{ pBt->autoVacuum = av ?1:0; pBt->incrVacuum = av==2 ?1:0; } @@ -50579,18 +51543,18 @@ goto page1_init_failed; } #ifdef SQLITE_OMIT_WAL if( page1[18]>1 ){ - pBt->readOnly = 1; + pBt->btsFlags |= BTS_READ_ONLY; } if( page1[19]>1 ){ goto page1_init_failed; } #else if( page1[18]>2 ){ - pBt->readOnly = 1; + pBt->btsFlags |= BTS_READ_ONLY; } if( page1[19]>2 ){ goto page1_init_failed; } @@ -50600,11 +51564,11 @@ ** The caller detects this and calls this function again. This is ** required as the version of page 1 currently in the page1 buffer ** may not be the latest version - there may be a newer one in the log ** file. */ - if( page1[19]==2 && pBt->doNotUseWAL==0 ){ + if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ int isOpen = 0; rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( isOpen==0 ){ @@ -50677,10 +51641,15 @@ */ pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23); pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23); pBt->maxLeaf = (u16)(pBt->usableSize - 35); pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); + if( pBt->maxLocal>127 ){ + pBt->max1bytePayload = 127; + }else{ + pBt->max1bytePayload = (u8)pBt->maxLocal; + } assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; pBt->nPage = nPage; return SQLITE_OK; @@ -50740,11 +51709,11 @@ data[21] = 64; data[22] = 32; data[23] = 32; memset(&data[24], 0, 100-24); zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); - pBt->pageSizeFixed = 1; + pBt->btsFlags |= BTS_PAGESIZE_FIXED; #ifndef SQLITE_OMIT_AUTOVACUUM assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); put4byte(&data[36 + 4*4], pBt->autoVacuum); put4byte(&data[36 + 7*4], pBt->incrVacuum); @@ -50804,21 +51773,23 @@ if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ goto trans_begun; } /* Write transactions are not possible on a read-only database */ - if( pBt->readOnly && wrflag ){ + if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ rc = SQLITE_READONLY; goto trans_begun; } #ifndef SQLITE_OMIT_SHARED_CACHE /* If another database handle has already opened a write transaction ** on this shared-btree structure and a second write transaction is ** requested, return SQLITE_LOCKED. */ - if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){ + if( (wrflag && pBt->inTransaction==TRANS_WRITE) + || (pBt->btsFlags & BTS_PENDING)!=0 + ){ pBlock = pBt->pWriter->db; }else if( wrflag>1 ){ BtLock *pIter; for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->pBtree!=p ){ @@ -50838,11 +51809,12 @@ ** page 1. So if some other shared-cache client already has a write-lock ** on page 1, the transaction cannot be opened. */ rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); if( SQLITE_OK!=rc ) goto trans_begun; - pBt->initiallyEmpty = (u8)(pBt->nPage==0); + pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; + if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; do { /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after ** reading page 1 it discovers that the page-size of the database @@ -50850,11 +51822,11 @@ ** pBt->pageSize to the page-size of the file on disk. */ while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); if( rc==SQLITE_OK && wrflag ){ - if( pBt->readOnly ){ + if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ rc = SQLITE_READONLY; }else{ rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db)); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); @@ -50887,11 +51859,12 @@ if( wrflag ){ MemPage *pPage1 = pBt->pPage1; #ifndef SQLITE_OMIT_SHARED_CACHE assert( !pBt->pWriter ); pBt->pWriter = p; - pBt->isExclusive = (u8)(wrflag>1); + pBt->btsFlags &= ~BTS_EXCLUSIVE; + if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE; #endif /* If the db-size header field is incorrect (as it may be if an old ** client has been writing the database file), update it now. Doing ** this sooner rather than later means the database size can safely @@ -51616,11 +52589,11 @@ SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree *p, int iStatement){ int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); assert( p->inTrans==TRANS_WRITE ); - assert( pBt->readOnly==0 ); + assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( iStatement>0 ); assert( iStatement>p->db->nSavepoint ); assert( pBt->inTransaction==TRANS_WRITE ); /* At the pager level, a statement transaction is a savepoint with ** an index greater than all savepoints created explicitly using @@ -51651,11 +52624,13 @@ assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); if( rc==SQLITE_OK ){ - if( iSavepoint<0 && pBt->initiallyEmpty ) pBt->nPage = 0; + if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ + pBt->nPage = 0; + } rc = newDatabase(pBt); pBt->nPage = get4byte(28 + pBt->pPage1->aData); /* The database size was written into the offset 28 of the header ** when the transaction started, so we know that the value at offset @@ -51721,11 +52696,11 @@ /* Assert that the caller has opened the required transaction. */ assert( p->inTrans>TRANS_NONE ); assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); assert( pBt->pPage1 && pBt->pPage1->aData ); - if( NEVER(wrFlag && pBt->readOnly) ){ + if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){ return SQLITE_READONLY; } if( iTable==1 && btreePagecount(pBt)==0 ){ assert( wrFlag==0 ); iTable = 0; @@ -52191,25 +53166,59 @@ offset -= ovflSize; }else{ /* Need to read this page properly. It contains some of the ** range of data that is being read (eOp==0) or written (eOp!=0). */ - DbPage *pDbPage; +#ifdef SQLITE_DIRECT_OVERFLOW_READ + sqlite3_file *fd; +#endif int a = amt; - rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage); - if( rc==SQLITE_OK ){ - aPayload = sqlite3PagerGetData(pDbPage); - nextPage = get4byte(aPayload); - if( a + offset > ovflSize ){ - a = ovflSize - offset; - } - rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); - sqlite3PagerUnref(pDbPage); - offset = 0; - amt -= a; - pBuf += a; - } + if( a + offset > ovflSize ){ + a = ovflSize - offset; + } + +#ifdef SQLITE_DIRECT_OVERFLOW_READ + /* If all the following are true: + ** + ** 1) this is a read operation, and + ** 2) data is required from the start of this overflow page, and + ** 3) the database is file-backed, and + ** 4) there is no open write-transaction, and + ** 5) the database is not a WAL database, + ** + ** then data can be read directly from the database file into the + ** output buffer, bypassing the page-cache altogether. This speeds + ** up loading large records that span many overflow pages. + */ + if( eOp==0 /* (1) */ + && offset==0 /* (2) */ + && pBt->inTransaction==TRANS_READ /* (4) */ + && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */ + && pBt->pPage1->aData[19]==0x01 /* (5) */ + ){ + u8 aSave[4]; + u8 *aWrite = &pBuf[-4]; + memcpy(aSave, aWrite, 4); + rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); + nextPage = get4byte(aWrite); + memcpy(aWrite, aSave, 4); + }else +#endif + + { + DbPage *pDbPage; + rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage); + if( rc==SQLITE_OK ){ + aPayload = sqlite3PagerGetData(pDbPage); + nextPage = get4byte(aPayload); + rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); + sqlite3PagerUnref(pDbPage); + offset = 0; + } + } + amt -= a; + pBuf += a; } } } if( rc==SQLITE_OK && amt>0 ){ @@ -52391,11 +53400,11 @@ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; } -#ifndef NDEBUG +#if 0 /* ** Page pParent is an internal (non-leaf) tree page. This function ** asserts that page number iChild is the left-child if the iIdx'th ** cell in page pParent. Or, if iIdx is equal to the total number of ** cells in pParent, that page number iChild is the right-child of @@ -52424,15 +53433,25 @@ static void moveToParent(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); assert( pCur->apPage[pCur->iPage] ); + + /* UPDATE: It is actually possible for the condition tested by the assert + ** below to be untrue if the database file is corrupt. This can occur if + ** one cursor has modified page pParent while a reference to it is held + ** by a second cursor. Which can only happen if a single page is linked + ** into more than one b-tree structure in a corrupt database. */ +#if 0 assertParentIndex( pCur->apPage[pCur->iPage-1], pCur->aiIdx[pCur->iPage-1], pCur->apPage[pCur->iPage]->pgno ); +#endif + testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); + releasePage(pCur->apPage[pCur->iPage]); pCur->iPage--; pCur->info.nSize = 0; pCur->validNKey = 0; } @@ -52767,20 +53786,25 @@ ** the entire cell by checking for the cases where the record is ** stored entirely within the b-tree page by inspecting the first ** 2 bytes of the cell. */ int nCell = pCell[0]; - if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){ + if( nCell<=pPage->max1bytePayload + /* && (pCell+nCell)aDataEnd */ + ){ /* This branch runs if the record-size field of the cell is a ** single byte varint and the record fits entirely on the main ** b-tree page. */ + testcase( pCell+nCell+1==pPage->aDataEnd ); c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + /* && (pCell+nCell+2)<=pPage->aDataEnd */ ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ + testcase( pCell+nCell+2==pPage->aDataEnd ); c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey); }else{ /* The record flows over onto one or more overflow pages. In ** this case the whole cell needs to be parsed, a buffer allocated ** and accessPayload() used to retrieve the record into the @@ -52804,11 +53828,10 @@ } } if( c==0 ){ if( pPage->intKey && !pPage->leaf ){ lwr = idx; - upr = lwr - 1; break; }else{ *pRes = 0; rc = SQLITE_OK; goto moveto_finish; @@ -52822,11 +53845,11 @@ if( lwr>upr ){ break; } pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2); } - assert( lwr==upr+1 ); + assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); assert( pPage->isInit ); if( pPage->leaf ){ chldPg = 0; }else if( lwr>=pPage->nCell ){ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); @@ -52894,11 +53917,17 @@ pCur->skipNext = 0; pPage = pCur->apPage[pCur->iPage]; idx = ++pCur->aiIdx[pCur->iPage]; assert( pPage->isInit ); - assert( idx<=pPage->nCell ); + + /* If the database file is corrupt, it is possible for the value of idx + ** to be invalid here. This can only occur if a second cursor modifies + ** the page while cursor pCur is holding a reference to it. Which can + ** only happen if the database is corrupt in such a way as to link the + ** page into more than one b-tree structure. */ + testcase( idx>pPage->nCell ); pCur->info.nSize = 0; pCur->validNKey = 0; if( idx>=pPage->nCell ){ if( !pPage->leaf ){ @@ -53087,10 +54116,12 @@ } if( rc ){ pTrunk = 0; goto end_allocate_page; } + assert( pTrunk!=0 ); + assert( pTrunk->aData!=0 ); k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */ if( k==0 && !searchList ){ /* The trunk has no leaves and the list is not being searched. ** So extract the trunk page itself and use it as the newly @@ -53317,11 +54348,11 @@ rc = sqlite3PagerWrite(pPage1->pDbPage); if( rc ) goto freepage_out; nFree = get4byte(&pPage1->aData[36]); put4byte(&pPage1->aData[36], nFree+1); - if( pBt->secureDelete ){ + if( pBt->btsFlags & BTS_SECURE_DELETE ){ /* If the secure_delete option is enabled, then ** always fully overwrite deleted information with zeros. */ if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) || ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0) @@ -53378,11 +54409,11 @@ */ rc = sqlite3PagerWrite(pTrunk->pDbPage); if( rc==SQLITE_OK ){ put4byte(&pTrunk->aData[4], nLeaf+1); put4byte(&pTrunk->aData[8+nLeaf*4], iPage); - if( pPage && !pBt->secureDelete ){ + if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){ sqlite3PagerDontWrite(pPage->pDbPage); } rc = btreeSetHasContent(pBt, iPage); } TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); @@ -53670,11 +54701,11 @@ assert( idx>=0 && idxnCell ); assert( sz==cellSize(pPage, idx) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); data = pPage->aData; - ptr = &data[pPage->cellOffset + 2*idx]; + ptr = &pPage->aCellIdx[2*idx]; pc = get2byte(ptr); hdr = pPage->hdrOffset; testcase( pc==get2byte(&data[hdr+5]) ); testcase( pc+sz==pPage->pBt->usableSize ); if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){ @@ -53684,11 +54715,11 @@ rc = freeSpace(pPage, pc, sz); if( rc ){ *pRC = rc; return; } - endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2]; + endPtr = &pPage->aCellIdx[2*pPage->nCell - 2]; assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ while( ptrnCell==0 ); assert( get2byteNotZero(&data[hdr+5])==nUsable ); - pCellptr = &data[pPage->cellOffset + nCell*2]; + pCellptr = &pPage->aCellIdx[nCell*2]; cellbody = nUsable; for(i=nCell-1; i>=0; i--){ u16 sz = aSize[i]; pCellptr -= 2; cellbody -= sz; @@ -54214,17 +55245,19 @@ ** This is safe because dropping a cell only overwrites the first ** four bytes of it, and this function does not need the first ** four bytes of the divider cell. So the pointer is safe to use ** later on. ** - ** Unless SQLite is compiled in secure-delete mode. In this case, + ** But not if we are in secure-delete mode. In secure-delete mode, ** the dropCell() routine will overwrite the entire cell with zeroes. ** In this case, temporarily copy the cell into the aOvflSpace[] ** buffer. It will be copied out again as soon as the aSpace[] buffer ** is allocated. */ - if( pBt->secureDelete ){ - int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); + if( pBt->btsFlags & BTS_SECURE_DELETE ){ + int iOff; + + iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); if( (iOff+szNew[i])>(int)pBt->usableSize ){ rc = SQLITE_CORRUPT_BKPT; memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; }else{ @@ -54402,12 +55435,18 @@ } /* Either we found one or more cells (cntnew[0])>0) or pPage is ** a virtual root page. A virtual root page is when the real root ** page is page 1 and we are the only child of that page. + ** + ** UPDATE: The assert() below is not necessarily true if the database + ** file is corrupt. The corruption will be detected and reported later + ** in this procedure so there is no need to act upon it now. */ +#if 0 assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) ); +#endif TRACE(("BALANCE: old: %d %d %d ", apOld[0]->pgno, nOld>=2 ? apOld[1]->pgno : 0, nOld>=3 ? apOld[2]->pgno : 0 @@ -54640,10 +55679,11 @@ int isDivider = 0; while( i==iNextOld ){ /* Cell i is the cell immediately following the last cell on old ** sibling page j. If the siblings are not leaf pages of an ** intkey b-tree, then cell i was a divider cell. */ + assert( j+1 < ArraySize(apCopy) ); pOld = apCopy[++j]; iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow; if( pOld->nOverflow ){ nOverflow = pOld->nOverflow; iOverflow = i + !leafData + pOld->aOvfl[0].idx; @@ -54952,11 +55992,12 @@ assert( pCur->skipNext!=SQLITE_OK ); return pCur->skipNext; } assert( cursorHoldsMutex(pCur) ); - assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly ); + assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE + && (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); /* Assert that the caller has been consistent. If this cursor was opened ** expecting an index b-tree, then the caller should be inserting blob ** keys with no associated data. If the cursor was opened expecting an @@ -55081,11 +56122,11 @@ int iCellIdx; /* Index of cell to delete */ int iCellDepth; /* Depth of node containing pCell */ assert( cursorHoldsMutex(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); - assert( !pBt->readOnly ); + assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( pCur->wrFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) @@ -55202,11 +56243,11 @@ int rc; int ptfFlags; /* Page-type flage for the root page of new table */ assert( sqlite3BtreeHoldsMutex(p) ); assert( pBt->inTransaction==TRANS_WRITE ); - assert( !pBt->readOnly ); + assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); #ifdef SQLITE_OMIT_AUTOVACUUM rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ){ return rc; @@ -55576,11 +56617,13 @@ *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); /* If auto-vacuum is disabled in this build and this is an auto-vacuum ** database, mark the database as read-only. */ #ifdef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1; + if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ + pBt->btsFlags |= BTS_READ_ONLY; + } #endif sqlite3BtreeLeave(p); } @@ -56376,11 +57419,12 @@ ** (e) the cursor points at a valid row of an intKey table. */ if( !pCsr->wrFlag ){ return SQLITE_READONLY; } - assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE ); + assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 + && pCsr->pBt->inTransaction==TRANS_WRITE ); assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); assert( pCsr->apPage[pCsr->iPage]->intKey ); return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); @@ -56416,11 +57460,12 @@ assert( iVersion==1 || iVersion==2 ); /* If setting the version fields to 1, do not automatically open the ** WAL connection, even if the version fields are currently set to 2. */ - pBt->doNotUseWAL = (u8)(iVersion==1); + pBt->btsFlags &= ~BTS_NO_WAL; + if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL; rc = sqlite3BtreeBeginTrans(pBtree, 0); if( rc==SQLITE_OK ){ u8 *aData = pBt->pPage1->aData; if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ @@ -56433,11 +57478,11 @@ } } } } - pBt->doNotUseWAL = 0; + pBt->btsFlags &= ~BTS_NO_WAL; return rc; } /************** End of btree.c ***********************************************/ /************** Begin file backup.c ******************************************/ @@ -56982,18 +58027,18 @@ /* ** Release all resources associated with an sqlite3_backup* handle. */ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ sqlite3_backup **pp; /* Ptr to head of pagers backup list */ - sqlite3_mutex *mutex; /* Mutex to protect source database */ + MUTEX_LOGIC( sqlite3_mutex *mutex; ) /* Mutex to protect source database */ int rc; /* Value to return */ /* Enter the mutexes */ if( p==0 ) return SQLITE_OK; sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3BtreeEnter(p->pSrc); - mutex = p->pSrcDb->mutex; + MUTEX_LOGIC( mutex = p->pSrcDb->mutex; ) if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } /* Detach this backup from the source pager. */ @@ -57108,13 +58153,23 @@ ** goes wrong, the transaction on pTo is rolled back. If successful, the ** transaction is committed before returning. */ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ int rc; + sqlite3_file *pFd; /* File descriptor for database pTo */ sqlite3_backup b; sqlite3BtreeEnter(pTo); sqlite3BtreeEnter(pFrom); + + assert( sqlite3BtreeIsInTrans(pTo) ); + pFd = sqlite3PagerFile(sqlite3BtreePager(pTo)); + if( pFd->pMethods ){ + i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom); + rc = sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; + if( rc ) goto copy_finished; + } /* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set ** to 0. This is used by the implementations of sqlite3_backup_step() ** and sqlite3_backup_finish() to detect that they are being called ** from this function, not directly by the user. @@ -57134,13 +58189,17 @@ */ sqlite3_backup_step(&b, 0x7FFFFFFF); assert( b.rc!=SQLITE_OK ); rc = sqlite3_backup_finish(&b); if( rc==SQLITE_OK ){ - pTo->pBt->pageSizeFixed = 0; + pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; + }else{ + sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); } + assert( sqlite3BtreeIsInTrans(pTo)==0 ); +copy_finished: sqlite3BtreeLeave(pFrom); sqlite3BtreeLeave(pTo); return rc; } #endif /* SQLITE_OMIT_VACUUM */ @@ -57163,16 +58222,10 @@ ** stores a single value in the VDBE. Mem is an opaque structure visible ** only within the VDBE. Interface routines refer to a Mem using the ** name sqlite_value */ -/* -** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*) -** P if required. -*/ -#define expandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0) - /* ** If pMem is an object with a valid string representation, this routine ** ensures the internal encoding for the string representation is ** 'desiredEnc', one of SQLITE_UTF8, SQLITE_UTF16LE or SQLITE_UTF16BE. ** @@ -57268,11 +58321,11 @@ */ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ int f; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( (pMem->flags&MEM_RowSet)==0 ); - expandBlob(pMem); + ExpandBlob(pMem); f = pMem->flags; if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE_NOMEM; } @@ -57437,11 +58490,11 @@ ** Release any memory held by the Mem. This may leave the Mem in an ** inconsistent state, for example with (Mem.z==0) and ** (Mem.type==SQLITE_TEXT). */ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ - MemReleaseExt(p); + VdbeMemRelease(p); sqlite3DbFree(p->db, p->zMalloc); p->z = 0; p->zMalloc = 0; p->xDel = 0; } @@ -57733,11 +58786,11 @@ ** copies of this cell as invalid. ** ** This is used for testing and debugging only - to make sure shallow ** copies are not misused. */ -SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){ +SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ int i; Mem *pX; for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ if( pX->pScopyFrom==pMem ){ pX->flags |= MEM_Invalid; @@ -57759,11 +58812,11 @@ ** pFrom->z is used, then pTo->z points to the same thing as pFrom->z ** and flags gets srcType (either MEM_Ephem or MEM_Static). */ SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){ assert( (pFrom->flags & MEM_RowSet)==0 ); - MemReleaseExt(pTo); + VdbeMemRelease(pTo); memcpy(pTo, pFrom, MEMCELLSIZE); pTo->xDel = 0; if( (pFrom->flags&MEM_Static)==0 ){ pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem); assert( srcType==MEM_Ephem || srcType==MEM_Static ); @@ -57777,11 +58830,11 @@ */ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ int rc = SQLITE_OK; assert( (pFrom->flags & MEM_RowSet)==0 ); - MemReleaseExt(pTo); + VdbeMemRelease(pTo); memcpy(pTo, pFrom, MEMCELLSIZE); pTo->flags &= ~MEM_Dyn; if( pTo->flags&(MEM_Str|MEM_Blob) ){ if( 0==(pFrom->flags&MEM_Static) ){ @@ -58105,20 +59158,20 @@ if( pVal->flags&MEM_Null ){ return 0; } assert( (MEM_Blob>>3) == MEM_Str ); pVal->flags |= (pVal->flags & MEM_Blob)>>3; - expandBlob(pVal); + ExpandBlob(pVal); if( pVal->flags&MEM_Str ){ sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ return 0; } } - sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */ + sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */ }else{ assert( (pVal->flags&MEM_Blob)==0 ); sqlite3VdbeMemStringify(pVal, enc); assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) ); } @@ -58171,15 +59224,15 @@ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; - /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2. + /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3. ** The ifdef here is to enable us to achieve 100% branch test coverage even - ** when SQLITE_ENABLE_STAT2 is omitted. + ** when SQLITE_ENABLE_STAT3 is omitted. */ -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; #endif @@ -58495,11 +59548,12 @@ return addr; } /* ** Add an OP_ParseSchema opcode. This routine is broken out from -** sqlite3VdbeAddOp4() since it needs to also local all btrees. +** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees +** as having been used. ** ** The zWhere string must have been obtained from sqlite3_malloc(). ** This routine will take ownership of the allocated memory. */ SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ @@ -58874,12 +59928,12 @@ /* ** Change the P2 operand of instruction addr so that it points to ** the address of the next instruction to be coded. */ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){ - assert( addr>=0 ); - sqlite3VdbeChangeP2(p, addr, p->nOp); + assert( addr>=0 || p->db->mallocFailed ); + if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp); } /* ** If the input FuncDef structure is ephemeral, then free it. If @@ -59080,34 +60134,33 @@ ** Change the comment on the the most recently coded instruction. Or ** insert a No-op and add the comment to that new instruction. This ** makes the code easier to read during debugging. None of this happens ** in a production build. */ -SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ - va_list ap; - if( !p ) return; +static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ assert( p->nOp>0 || p->aOp==0 ); assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); if( p->nOp ){ - char **pz = &p->aOp[p->nOp-1].zComment; + assert( p->aOp ); + sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); + p->aOp[p->nOp-1].zComment = sqlite3VMPrintf(p->db, zFormat, ap); + } +} +SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ + va_list ap; + if( p ){ va_start(ap, zFormat); - sqlite3DbFree(p->db, *pz); - *pz = sqlite3VMPrintf(p->db, zFormat, ap); + vdbeVComment(p, zFormat, ap); va_end(ap); } } SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ va_list ap; - if( !p ) return; - sqlite3VdbeAddOp0(p, OP_Noop); - assert( p->nOp>0 || p->aOp==0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); - if( p->nOp ){ - char **pz = &p->aOp[p->nOp-1].zComment; + if( p ){ + sqlite3VdbeAddOp0(p, OP_Noop); va_start(ap, zFormat); - sqlite3DbFree(p->db, *pz); - *pz = sqlite3VMPrintf(p->db, zFormat, ap); + vdbeVComment(p, zFormat, ap); va_end(ap); } } #endif /* NDEBUG */ @@ -59213,17 +60266,18 @@ sqlite3_snprintf(nTemp, zTemp, "%.16g", *pOp->p4.pReal); break; } case P4_MEM: { Mem *pMem = pOp->p4.pMem; - assert( (pMem->flags & MEM_Null)==0 ); if( pMem->flags & MEM_Str ){ zP4 = pMem->z; }else if( pMem->flags & MEM_Int ){ sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i); }else if( pMem->flags & MEM_Real ){ sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r); + }else if( pMem->flags & MEM_Null ){ + sqlite3_snprintf(nTemp, zTemp, "NULL"); }else{ assert( pMem->flags & MEM_Blob ); zP4 = "(blob)"; } break; @@ -59262,12 +60316,13 @@ /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** ** The prepared statements need to know in advance the complete set of -** attached databases that they will be using. A mask of these databases -** is maintained in p->btreeMask and is used for locking and other purposes. +** attached databases that will be use. A mask of these databases +** is maintained in p->btreeMask. The p->lockMask value is the subset of +** p->btreeMask of databases that will require a lock. */ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){ assert( i>=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); p->btreeMask |= ((yDbMask)1)<zMalloc ){ sqlite3DbFree(db, p->zMalloc); p->zMalloc = 0; } - p->flags = MEM_Null; + p->flags = MEM_Invalid; } db->mallocFailed = malloc_failed; } } @@ -59441,11 +60496,11 @@ SubProgram **apSub = 0; /* Array of sub-vdbes */ Mem *pSub = 0; /* Memory cell hold array of subprogs */ sqlite3 *db = p->db; /* The database connection */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ - Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */ + Mem *pMem = &p->aMem[1]; /* First Mem of result set */ assert( p->explain ); assert( p->magic==VDBE_MAGIC_RUN ); assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); @@ -59452,10 +60507,11 @@ /* Even though this opcode does not use dynamic strings for ** the result, result columns may become dynamic if the user calls ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. */ releaseMemArray(pMem, 8); + p->pResultSet = 0; if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ db->mallocFailed = 1; @@ -59606,10 +60662,11 @@ pMem->type = SQLITE_NULL; } } p->nResColumn = 8 - 4*(p->explain-1); + p->pResultSet = &p->aMem[1]; p->rc = SQLITE_OK; rc = SQLITE_ROW; } return rc; } @@ -59767,10 +60824,11 @@ sqlite3 *db; /* The database connection */ int nVar; /* Number of parameters */ int nMem; /* Number of VM memory registers */ int nCursor; /* Number of cursors required */ int nArg; /* Number of arguments in subprograms */ + int nOnce; /* Number of OP_Once instructions */ int n; /* Loop counter */ u8 *zCsr; /* Memory available for allocation */ u8 *zEnd; /* First byte past allocated memory */ int nByte; /* How much extra memory is needed */ @@ -59782,10 +60840,12 @@ assert( db->mallocFailed==0 ); nVar = pParse->nVar; nMem = pParse->nMem; nCursor = pParse->nTab; nArg = pParse->nMaxArg; + nOnce = pParse->nOnce; + if( nOnce==0 ) nOnce = 1; /* Ensure at least one byte in p->aOnceFlag[] */ /* For each cursor required, also allocate a memory cell. Memory ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by ** the vdbe program. Instead they are used to allocate space for ** VdbeCursor/BtCursor structures. The blob of memory associated with @@ -59828,18 +60888,20 @@ p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte); + p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte); if( nByte ){ p->pFree = sqlite3DbMallocZero(db, nByte); } zCsr = p->pFree; zEnd = &zCsr[nByte]; }while( nByte && !db->mallocFailed ); p->nCursor = (u16)nCursor; + p->nOnceFlag = nOnce; if( p->aVar ){ p->nVar = (ynVar)nVar; for(n=0; naVar[n].flags = MEM_Null; p->aVar[n].db = db; @@ -59852,11 +60914,11 @@ } if( p->aMem ){ p->aMem--; /* aMem[] goes from 1..nMem */ p->nMem = nMem; /* not from 0..nMem-1 */ for(n=1; n<=nMem; n++){ - p->aMem[n].flags = MEM_Null; + p->aMem[n].flags = MEM_Invalid; p->aMem[n].db = db; } } p->explain = pParse->explain; sqlite3VdbeRewind(p); @@ -59894,10 +60956,12 @@ ** is used, for example, when a trigger sub-program is halted to restore ** control to the main program. */ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ Vdbe *v = pFrame->v; + v->aOnceFlag = pFrame->aOnceFlag; + v->nOnceFlag = pFrame->nOnceFlag; v->aOp = pFrame->aOp; v->nOp = pFrame->nOp; v->aMem = pFrame->aMem; v->nMem = pFrame->nMem; v->apCsr = pFrame->apCsr; @@ -59956,12 +61020,14 @@ #ifdef SQLITE_DEBUG /* Execute assert() statements to ensure that the Vdbe.apCsr[] and ** Vdbe.aMem[] arrays have already been cleaned up. */ int i; - for(i=0; inCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 ); - for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null ); + if( p->apCsr ) for(i=0; inCursor; i++) assert( p->apCsr[i]==0 ); + if( p->aMem ){ + for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid ); + } #endif sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; p->pResultSet = 0; @@ -60122,20 +61188,35 @@ char *zMaster = 0; /* File-name for the master journal */ char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); sqlite3_file *pMaster = 0; i64 offset = 0; int res; + int retryCount = 0; + int nMainFile; /* Select a master journal file name */ + nMainFile = sqlite3Strlen30(zMainFile); + zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile); + if( zMaster==0 ) return SQLITE_NOMEM; do { u32 iRandom; - sqlite3DbFree(db, zMaster); + if( retryCount ){ + if( retryCount>100 ){ + sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster); + sqlite3OsDelete(pVfs, zMaster, 0); + break; + }else if( retryCount==1 ){ + sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster); + } + } + retryCount++; sqlite3_randomness(sizeof(iRandom), &iRandom); - zMaster = sqlite3MPrintf(db, "%s-mj%08X", zMainFile, iRandom&0x7fffffff); - if( !zMaster ){ - return SQLITE_NOMEM; - } + sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X", + (iRandom>>8)&0xffffff, iRandom&0xff); + /* The antipenultimate character of the master journal name must + ** be "9" to avoid name collisions when using 8+3 filenames. */ + assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' ); sqlite3FileSuffix3(zMainFile, zMaster); rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); }while( rc==SQLITE_OK && res ); if( rc==SQLITE_OK ){ /* Open the master journal. */ @@ -60425,10 +61506,11 @@ */ if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } + if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag); closeAllCursors(p); if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } checkActiveVdbeCnt(db); @@ -60607,10 +61689,34 @@ ** in p->rc. This routine sets that result back to SQLITE_OK. */ SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){ p->rc = SQLITE_OK; } + +/* +** Copy the error code and error message belonging to the VDBE passed +** as the first argument to its database handle (so that they will be +** returned by calls to sqlite3_errcode() and sqlite3_errmsg()). +** +** This function does not clear the VDBE error code or message, just +** copies them to the database handle. +*/ +SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ + sqlite3 *db = p->db; + int rc = p->rc; + if( p->zErrMsg ){ + u8 mallocFailed = db->mallocFailed; + sqlite3BeginBenignMalloc(); + sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); + sqlite3EndBenignMalloc(); + db->mallocFailed = mallocFailed; + db->errCode = rc; + }else{ + sqlite3Error(db, rc, 0); + } + return rc; +} /* ** Clean up a VDBE after execution but do not delete the VDBE just yet. ** Write any error messages into *pzErrMsg. Return the result code. ** @@ -60635,22 +61741,13 @@ ** and error message from the VDBE into the main database structure. But ** if the VDBE has just been set to run but has not actually executed any ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ - if( p->zErrMsg ){ - sqlite3BeginBenignMalloc(); - sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT); - sqlite3EndBenignMalloc(); - db->errCode = p->rc; - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - }else if( p->rc ){ - sqlite3Error(db, p->rc, 0); - }else{ - sqlite3Error(db, SQLITE_OK, 0); - } + sqlite3VdbeTransferError(p); + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call ** to sqlite3_step(). For consistency (since sqlite3_step() was ** called), set the database error in this case as well. @@ -60747,10 +61844,14 @@ vdbeFreeOpArray(db, p->aOp, p->nOp); sqlite3DbFree(db, p->aLabel); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); sqlite3DbFree(db, p->pFree); +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) + sqlite3DbFree(db, p->zExplain); + sqlite3DbFree(db, p->pExplain); +#endif sqlite3DbFree(db, p); } /* ** Delete an entire VDBE. @@ -61226,19 +62327,10 @@ ** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set ** and the common prefixes are equal, then key1 is less than key2. ** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are ** equal, then the keys are considered to be equal and ** the parts beyond the common prefix are ignored. -** -** If the UNPACKED_IGNORE_ROWID flag is set, then the last byte of -** the header of pKey1 is ignored. It is assumed that pKey1 is -** an index key, and thus ends with a rowid value. The last byte -** of the header will therefore be the serial type of the rowid: -** one of 1, 2, 3, 4, 5, 6, 8, or 9 - the integer serial types. -** The serial type of the final rowid will always be a single byte. -** By ignoring this last byte of the header, we force the comparison -** to ignore the rowid at the end of key1. */ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( int nKey1, const void *pKey1, /* Left key */ UnpackedRecord *pPKey2 /* Right key */ ){ @@ -61267,13 +62359,10 @@ */ /* mem1.u.i = 0; // not needed, here to silence compiler warning */ idx1 = getVarint32(aKey1, szHdr1); d1 = szHdr1; - if( pPKey2->flags & UNPACKED_IGNORE_ROWID ){ - szHdr1--; - } nField = pKeyInfo->nField; while( idx1nField ){ u32 serial_type1; /* Read the serial types for the next element in each key. */ @@ -61361,11 +62450,11 @@ ** than 2GiB are support - anything large must be database corruption. ** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so ** this code can safely assume that nCellKey is 32-bits */ assert( sqlite3BtreeCursorIsValid(pCur) ); - rc = sqlite3BtreeKeySize(pCur, &nCellKey); + VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); /* Read in the complete content of the index entry */ memset(&m, 0, sizeof(m)); @@ -61436,11 +62525,11 @@ int rc; BtCursor *pCur = pC->pCursor; Mem m; assert( sqlite3BtreeCursorIsValid(pCur) ); - rc = sqlite3BtreeKeySize(pCur, &nCellKey); + VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ /* nCellKey will always be between 0 and 0xffffffff because of the say ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; @@ -61449,11 +62538,11 @@ memset(&m, 0, sizeof(m)); rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); if( rc ){ return rc; } - assert( pUnpacked->flags & UNPACKED_IGNORE_ROWID ); + assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH ); *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked); sqlite3VdbeMemRelease(&m); return SQLITE_OK; } @@ -61892,11 +62981,11 @@ ** since any application that receives an SQLITE_MISUSE is broken by ** definition. ** ** Nevertheless, some published applications that were originally written ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE - ** returns, and the so were broken by the automatic-reset change. As a + ** returns, and those were broken by the automatic-reset change. As a ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the ** legacy behavior of returning SQLITE_MISUSE for cases where the ** previous sqlite3_step() returned something other than a SQLITE_LOCKED ** or SQLITE_BUSY error. */ @@ -61992,11 +63081,11 @@ if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ /* If this statement was prepared using sqlite3_prepare_v2(), and an ** error has occured, then return the error code in p->rc to the ** caller. Set the error code in the database handle to the same value. */ - rc = db->errCode = p->rc; + rc = sqlite3VdbeTransferError(p); } return (rc&db->errMask); } /* @@ -62238,17 +63327,17 @@ pOut = &pVm->pResultSet[i]; }else{ /* If the value passed as the second argument is out of range, return ** a pointer to the following static Mem object which contains the ** value SQL NULL. Even though the Mem structure contains an element - ** of type i64, on certain architecture (x86) with certain compiler + ** of type i64, on certain architectures (x86) with certain compiler ** switches (-Os), gcc may align this Mem object on a 4-byte boundary ** instead of an 8-byte one. This all works fine, except that when ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s ** that a Mem structure is located on an 8-byte boundary. To prevent - ** this assert() from failing, when building with SQLITE_DEBUG defined - ** using gcc, force nullMem to be 8-byte aligned using the magical + ** these assert()s from failing, when building with SQLITE_DEBUG defined + ** using gcc, we force nullMem to be 8-byte aligned using the magical ** __attribute__((aligned(8))) macro. */ static const Mem nullMem #if defined(SQLITE_DEBUG) && defined(__GNUC__) __attribute__((aligned(8))) #endif @@ -62814,10 +63903,18 @@ ** database. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } + +/* +** Return true if the prepared statement is in need of being reset. +*/ +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ + Vdbe *v = (Vdbe*)pStmt; + return v!=0 && v->pc>0 && v->magic==VDBE_MAGIC_RUN; +} /* ** Return a pointer to the next prepared statement after pStmt associated ** with database connection pDb. If pStmt is NULL, return the first ** prepared statement for the database connection. Return NULL if there @@ -62859,10 +63956,12 @@ ** ************************************************************************* ** ** This file contains code used to insert the values of host parameters ** (aka "wildcards") into the SQL text output by sqlite3_trace(). +** +** The Vdbe parse-tree explainer is also found here. */ #ifndef SQLITE_OMIT_TRACE /* @@ -62997,10 +64096,125 @@ } return sqlite3StrAccumFinish(&out); } #endif /* #ifndef SQLITE_OMIT_TRACE */ + +/***************************************************************************** +** The following code implements the data-structure explaining logic +** for the Vdbe. +*/ + +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) + +/* +** Allocate a new Explain object +*/ +SQLITE_PRIVATE void sqlite3ExplainBegin(Vdbe *pVdbe){ + if( pVdbe ){ + sqlite3BeginBenignMalloc(); + Explain *p = sqlite3_malloc( sizeof(Explain) ); + if( p ){ + memset(p, 0, sizeof(*p)); + p->pVdbe = pVdbe; + sqlite3_free(pVdbe->pExplain); + pVdbe->pExplain = p; + sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase), + SQLITE_MAX_LENGTH); + p->str.useMalloc = 2; + }else{ + sqlite3EndBenignMalloc(); + } + } +} + +/* +** Return true if the Explain ends with a new-line. +*/ +static int endsWithNL(Explain *p){ + return p && p->str.zText && p->str.nChar + && p->str.zText[p->str.nChar-1]=='\n'; +} + +/* +** Append text to the indentation +*/ +SQLITE_PRIVATE void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){ + Explain *p; + if( pVdbe && (p = pVdbe->pExplain)!=0 ){ + va_list ap; + if( p->nIndent && endsWithNL(p) ){ + int n = p->nIndent; + if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent); + sqlite3AppendSpace(&p->str, p->aIndent[n-1]); + } + va_start(ap, zFormat); + sqlite3VXPrintf(&p->str, 1, zFormat, ap); + va_end(ap); + } +} + +/* +** Append a '\n' if there is not already one. +*/ +SQLITE_PRIVATE void sqlite3ExplainNL(Vdbe *pVdbe){ + Explain *p; + if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){ + sqlite3StrAccumAppend(&p->str, "\n", 1); + } +} + +/* +** Push a new indentation level. Subsequent lines will be indented +** so that they begin at the current cursor position. +*/ +SQLITE_PRIVATE void sqlite3ExplainPush(Vdbe *pVdbe){ + Explain *p; + if( pVdbe && (p = pVdbe->pExplain)!=0 ){ + if( p->str.zText && p->nIndentaIndent) ){ + const char *z = p->str.zText; + int i = p->str.nChar-1; + int x; + while( i>=0 && z[i]!='\n' ){ i--; } + x = (p->str.nChar - 1) - i; + if( p->nIndent && xaIndent[p->nIndent-1] ){ + x = p->aIndent[p->nIndent-1]; + } + p->aIndent[p->nIndent] = x; + } + p->nIndent++; + } +} + +/* +** Pop the indentation stack by one level. +*/ +SQLITE_PRIVATE void sqlite3ExplainPop(Vdbe *p){ + if( p && p->pExplain ) p->pExplain->nIndent--; +} + +/* +** Free the indentation structure +*/ +SQLITE_PRIVATE void sqlite3ExplainFinish(Vdbe *pVdbe){ + if( pVdbe && pVdbe->pExplain ){ + sqlite3_free(pVdbe->zExplain); + sqlite3ExplainNL(pVdbe); + pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str); + sqlite3_free(pVdbe->pExplain); + pVdbe->pExplain = 0; + sqlite3EndBenignMalloc(); + } +} + +/* +** Return the explanation of a virtual machine. +*/ +SQLITE_PRIVATE const char *sqlite3VdbeExplanation(Vdbe *pVdbe){ + return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0; +} +#endif /* defined(SQLITE_DEBUG) */ /************** End of vdbetrace.c *******************************************/ /************** Begin file vdbe.c ********************************************/ /* ** 2001 September 15 @@ -63052,11 +64266,11 @@ ** Invoke this macro on memory cells just prior to changing the ** value of the cell. This macro verifies that shallow copies are ** not misused. */ #ifdef SQLITE_DEBUG -# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M) +# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M) #else # define memAboutToChange(P,M) #endif /* @@ -63070,12 +64284,12 @@ SQLITE_API int sqlite3_search_count = 0; #endif /* ** When this global variable is positive, it gets decremented once before -** each instruction in the VDBE. When reaches zero, the u1.isInterrupted -** field of the sqlite3 structure is set in order to simulate and interrupt. +** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted +** field of the sqlite3 structure is set in order to simulate an interrupt. ** ** This facility is used for testing purposes only. It does not function ** in an ordinary build. */ #ifdef SQLITE_TEST @@ -63151,16 +64365,10 @@ */ #define Deephemeralize(P) \ if( ((P)->flags&MEM_Ephem)!=0 \ && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} -/* -** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*) -** P if required. -*/ -#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0) - /* Return true if the cursor was opened using the OP_OpenSorter opcode. */ #ifdef SQLITE_OMIT_MERGE_SORT # define isSorter(x) 0 #else # define isSorter(x) ((x)->pSorter!=0) @@ -63196,11 +64404,11 @@ */ static VdbeCursor *allocateCursor( Vdbe *p, /* The virtual machine */ int iCur, /* Index of the new VdbeCursor */ int nField, /* Number of fields in the table or index */ - int iDb, /* When database the cursor belongs to, or -1 */ + int iDb, /* Database the cursor belongs to, or -1 */ int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */ ){ /* Find the memory cell that will be used to store the blob of memory ** required for this VdbeCursor structure. It is convenient to use a ** vdbe memory cell to manage the memory allocation required for a @@ -63567,11 +64775,11 @@ ** sqlite3_interrupt() routine has been called. If it has been, then ** processing of the VDBE program is interrupted. ** ** This macro added to every instruction that does a jump in order to ** implement a loop. This test used to be on every single instruction, -** but that meant we more testing that we needed. By only testing the +** but that meant we more testing than we needed. By only testing the ** flag on jump instructions, we get a (small) speed improvement. */ #define CHECK_FOR_INTERRUPT \ if( db->u1.isInterrupted ) goto abort_due_to_interrupt; @@ -63677,69 +64885,72 @@ */ union vdbeExecUnion { struct OP_Yield_stack_vars { int pcDest; } aa; + struct OP_Null_stack_vars { + int cnt; + } ab; struct OP_Variable_stack_vars { Mem *pVar; /* Value being transferred */ - } ab; + } ac; struct OP_Move_stack_vars { char *zMalloc; /* Holding variable for allocated memory */ int n; /* Number of registers left to copy */ int p1; /* Register to copy from */ int p2; /* Register to copy to */ - } ac; + } ad; struct OP_ResultRow_stack_vars { Mem *pMem; int i; - } ad; + } ae; struct OP_Concat_stack_vars { i64 nByte; - } ae; + } af; struct OP_Remainder_stack_vars { int flags; /* Combined MEM_* flags from both inputs */ i64 iA; /* Integer value of left operand */ i64 iB; /* Integer value of right operand */ double rA; /* Real value of left operand */ double rB; /* Real value of right operand */ - } af; + } ag; struct OP_Function_stack_vars { int i; Mem *pArg; sqlite3_context ctx; sqlite3_value **apVal; int n; - } ag; + } ah; struct OP_ShiftRight_stack_vars { i64 iA; u64 uA; i64 iB; u8 op; - } ah; + } ai; struct OP_Ge_stack_vars { int res; /* Result of the comparison of pIn1 against pIn3 */ char affinity; /* Affinity to use for comparison */ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ - } ai; + } aj; struct OP_Compare_stack_vars { int n; int i; int p1; int p2; const KeyInfo *pKeyInfo; int idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ - } aj; + } ak; struct OP_Or_stack_vars { int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - } ak; + } al; struct OP_IfNot_stack_vars { int c; - } al; + } am; struct OP_Column_stack_vars { u32 payloadSize; /* Number of bytes in the record */ i64 payloadSize64; /* Number of bytes in the record */ int p1; /* P1 value of the opcode */ int p2; /* column number to retrieve */ @@ -63760,15 +64971,15 @@ u32 szField; /* Number of bytes in the content of a field */ int szHdr; /* Size of the header size field at start of record */ int avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ - } am; + } an; struct OP_Affinity_stack_vars { const char *zAffinity; /* The affinity to be applied */ char cAff; /* A single character of affinity */ - } an; + } ao; struct OP_MakeRecord_stack_vars { u8 *zNewRecord; /* A buffer to hold the data for the new record */ Mem *pRec; /* The new record */ u64 nData; /* Number of bytes of data space */ int nHdr; /* Number of bytes of header space */ @@ -63781,108 +64992,108 @@ int nField; /* Number of fields in the record */ char *zAffinity; /* The affinity string for the record */ int file_format; /* File format to use for encoding */ int i; /* Space used in zNewRecord[] */ int len; /* Length of a field */ - } ao; + } ap; struct OP_Count_stack_vars { i64 nEntry; BtCursor *pCrsr; - } ap; + } aq; struct OP_Savepoint_stack_vars { int p1; /* Value of P1 operand */ char *zName; /* Name of savepoint */ int nName; Savepoint *pNew; Savepoint *pSavepoint; Savepoint *pTmp; int iSavepoint; int ii; - } aq; + } ar; struct OP_AutoCommit_stack_vars { int desiredAutoCommit; int iRollback; int turnOnAC; - } ar; + } as; struct OP_Transaction_stack_vars { Btree *pBt; - } as; + } at; struct OP_ReadCookie_stack_vars { int iMeta; int iDb; int iCookie; - } at; + } au; struct OP_SetCookie_stack_vars { Db *pDb; - } au; + } av; struct OP_VerifyCookie_stack_vars { int iMeta; int iGen; Btree *pBt; - } av; + } aw; struct OP_OpenWrite_stack_vars { int nField; KeyInfo *pKeyInfo; int p2; int iDb; int wrFlag; Btree *pX; VdbeCursor *pCur; Db *pDb; - } aw; + } ax; struct OP_OpenEphemeral_stack_vars { VdbeCursor *pCx; - } ax; + } ay; struct OP_SorterOpen_stack_vars { VdbeCursor *pCx; - } ay; + } az; struct OP_OpenPseudo_stack_vars { VdbeCursor *pCx; - } az; + } ba; struct OP_SeekGt_stack_vars { int res; int oc; VdbeCursor *pC; UnpackedRecord r; int nField; i64 iKey; /* The rowid we are to seek to */ - } ba; + } bb; struct OP_Seek_stack_vars { VdbeCursor *pC; - } bb; + } bc; struct OP_Found_stack_vars { int alreadyExists; VdbeCursor *pC; int res; char *pFree; UnpackedRecord *pIdxKey; UnpackedRecord r; char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; - } bc; + } bd; struct OP_IsUnique_stack_vars { u16 ii; VdbeCursor *pCx; BtCursor *pCrsr; u16 nField; Mem *aMx; UnpackedRecord r; /* B-Tree index search key */ i64 R; /* Rowid stored in register P3 */ - } bd; + } be; struct OP_NotExists_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; - } be; + } bf; struct OP_NewRowid_stack_vars { i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ int cnt; /* Counter to limit the number of searches */ Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ VdbeFrame *pFrame; /* Root frame of VDBE */ - } bf; + } bg; struct OP_InsertInt_stack_vars { Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ i64 iKey; /* The integer ROWID or key for the record to be inserted */ VdbeCursor *pC; /* Cursor to table into which insert is written */ @@ -63889,161 +65100,161 @@ int nZero; /* Number of zero-bytes to append */ int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ - } bg; + } bh; struct OP_Delete_stack_vars { i64 iKey; VdbeCursor *pC; - } bh; + } bi; struct OP_SorterCompare_stack_vars { VdbeCursor *pC; int res; - } bi; + } bj; struct OP_SorterData_stack_vars { VdbeCursor *pC; - } bj; + } bk; struct OP_RowData_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; - } bk; + } bl; struct OP_Rowid_stack_vars { VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; - } bl; + } bm; struct OP_NullRow_stack_vars { VdbeCursor *pC; - } bm; + } bn; struct OP_Last_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; - } bn; + } bo; struct OP_Rewind_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; - } bo; + } bp; struct OP_Next_stack_vars { VdbeCursor *pC; int res; - } bp; + } bq; struct OP_IdxInsert_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int nKey; const char *zKey; - } bq; + } br; struct OP_IdxDelete_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; UnpackedRecord r; - } br; + } bs; struct OP_IdxRowid_stack_vars { BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; - } bs; + } bt; struct OP_IdxGE_stack_vars { VdbeCursor *pC; int res; UnpackedRecord r; - } bt; + } bu; struct OP_Destroy_stack_vars { int iMoved; int iCnt; Vdbe *pVdbe; int iDb; - } bu; + } bv; struct OP_Clear_stack_vars { int nChange; - } bv; + } bw; struct OP_CreateTable_stack_vars { int pgno; int flags; Db *pDb; - } bw; + } bx; struct OP_ParseSchema_stack_vars { int iDb; const char *zMaster; char *zSql; InitData initData; - } bx; + } by; struct OP_IntegrityCk_stack_vars { int nRoot; /* Number of tables to check. (Number of root pages.) */ int *aRoot; /* Array of rootpage numbers for tables to be checked */ int j; /* Loop counter */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ - } by; + } bz; struct OP_RowSetRead_stack_vars { i64 val; - } bz; + } ca; struct OP_RowSetTest_stack_vars { int iSet; int exists; - } ca; + } cb; struct OP_Program_stack_vars { int nMem; /* Number of memory registers for sub-program */ int nByte; /* Bytes of runtime space required for sub-program */ Mem *pRt; /* Register to allocate runtime space */ Mem *pMem; /* Used to iterate through memory cells */ Mem *pEnd; /* Last memory cell in new array */ VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ - } cb; + } cc; struct OP_Param_stack_vars { VdbeFrame *pFrame; Mem *pIn; - } cc; + } cd; struct OP_MemMax_stack_vars { Mem *pIn1; VdbeFrame *pFrame; - } cd; + } ce; struct OP_AggStep_stack_vars { int n; int i; Mem *pMem; Mem *pRec; sqlite3_context ctx; sqlite3_value **apVal; - } ce; + } cf; struct OP_AggFinal_stack_vars { Mem *pMem; - } cf; + } cg; struct OP_Checkpoint_stack_vars { int i; /* Loop counter */ int aRes[3]; /* Results */ Mem *pMem; /* Write results here */ - } cg; + } ch; struct OP_JournalMode_stack_vars { Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ int eOld; /* The old journal mode */ const char *zFilename; /* Name of database file for pPager */ - } ch; + } ci; struct OP_IncrVacuum_stack_vars { Btree *pBt; - } ci; + } cj; struct OP_VBegin_stack_vars { VTable *pVTab; - } cj; + } ck; struct OP_VOpen_stack_vars { VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; - } ck; + } cl; struct OP_VFilter_stack_vars { int nArg; int iQuery; const sqlite3_module *pModule; Mem *pQuery; @@ -64052,40 +65263,40 @@ sqlite3_vtab *pVtab; VdbeCursor *pCur; int res; int i; Mem **apArg; - } cl; + } cm; struct OP_VColumn_stack_vars { sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; - } cm; + } cn; struct OP_VNext_stack_vars { sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; - } cn; + } co; struct OP_VRename_stack_vars { sqlite3_vtab *pVtab; Mem *pName; - } co; + } cp; struct OP_VUpdate_stack_vars { sqlite3_vtab *pVtab; sqlite3_module *pModule; int nArg; int i; sqlite_int64 rowid; Mem **apArg; Mem *pX; - } cp; + } cq; struct OP_Trace_stack_vars { char *zTrace; char *z; - } cq; + } cr; } u; /* End automatically generated code ********************************************************************/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ @@ -64181,11 +65392,11 @@ if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); - MemReleaseExt(pOut); + VdbeMemRelease(pOut); pOut->flags = MEM_Int; } /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG @@ -64272,11 +65483,12 @@ /* Opcode: Gosub P1 P2 * * * ** ** Write the current address onto register P1 ** and then jump to address P2. */ -case OP_Gosub: { /* jump, in1 */ +case OP_Gosub: { /* jump */ + assert( pOp->p1>0 && pOp->p1<=p->nMem ); pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; pIn1->u.i = pc; @@ -64471,16 +65683,31 @@ pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } -/* Opcode: Null * P2 * * * +/* Opcode: Null * P2 P3 * * ** -** Write a NULL into register P2. +** Write a NULL into registers P2. If P3 greater than P2, then also write +** NULL into register P3 and ever register in between P2 and P3. If P3 +** is less than P2 (typically P3 is zero) then only register P2 is +** set to NULL */ case OP_Null: { /* out2-prerelease */ +#if 0 /* local variables moved into u.ab */ + int cnt; +#endif /* local variables moved into u.ab */ + u.ab.cnt = pOp->p3-pOp->p2; + assert( pOp->p3<=p->nMem ); pOut->flags = MEM_Null; + while( u.ab.cnt>0 ){ + pOut++; + memAboutToChange(p, pOut); + VdbeMemRelease(pOut); + pOut->flags = MEM_Null; + u.ab.cnt--; + } break; } /* Opcode: Blob P1 P2 * P4 @@ -64502,21 +65729,21 @@ ** ** If the parameter is named, then its name appears in P4 and P3==1. ** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ab */ +#if 0 /* local variables moved into u.ac */ Mem *pVar; /* Value being transferred */ -#endif /* local variables moved into u.ab */ +#endif /* local variables moved into u.ac */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] ); - u.ab.pVar = &p->aVar[pOp->p1 - 1]; - if( sqlite3VdbeMemTooBig(u.ab.pVar) ){ + u.ac.pVar = &p->aVar[pOp->p1 - 1]; + if( sqlite3VdbeMemTooBig(u.ac.pVar) ){ goto too_big; } - sqlite3VdbeMemShallowCopy(pOut, u.ab.pVar, MEM_Static); + sqlite3VdbeMemShallowCopy(pOut, u.ac.pVar, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Move P1 P2 P3 * * @@ -64525,40 +65752,40 @@ ** registers P2..P2+P3-1. Registers P1..P1+P1-1 are ** left holding a NULL. It is an error for register ranges ** P1..P1+P3-1 and P2..P2+P3-1 to overlap. */ case OP_Move: { -#if 0 /* local variables moved into u.ac */ +#if 0 /* local variables moved into u.ad */ char *zMalloc; /* Holding variable for allocated memory */ int n; /* Number of registers left to copy */ int p1; /* Register to copy from */ int p2; /* Register to copy to */ -#endif /* local variables moved into u.ac */ - - u.ac.n = pOp->p3; - u.ac.p1 = pOp->p1; - u.ac.p2 = pOp->p2; - assert( u.ac.n>0 && u.ac.p1>0 && u.ac.p2>0 ); - assert( u.ac.p1+u.ac.n<=u.ac.p2 || u.ac.p2+u.ac.n<=u.ac.p1 ); - - pIn1 = &aMem[u.ac.p1]; - pOut = &aMem[u.ac.p2]; - while( u.ac.n-- ){ +#endif /* local variables moved into u.ad */ + + u.ad.n = pOp->p3; + u.ad.p1 = pOp->p1; + u.ad.p2 = pOp->p2; + assert( u.ad.n>0 && u.ad.p1>0 && u.ad.p2>0 ); + assert( u.ad.p1+u.ad.n<=u.ad.p2 || u.ad.p2+u.ad.n<=u.ad.p1 ); + + pIn1 = &aMem[u.ad.p1]; + pOut = &aMem[u.ad.p2]; + while( u.ad.n-- ){ assert( pOut<=&aMem[p->nMem] ); assert( pIn1<=&aMem[p->nMem] ); assert( memIsValid(pIn1) ); memAboutToChange(p, pOut); - u.ac.zMalloc = pOut->zMalloc; + u.ad.zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); #ifdef SQLITE_DEBUG - if( pOut->pScopyFrom>=&aMem[u.ac.p1] && pOut->pScopyFrom<&aMem[u.ac.p1+pOp->p3] ){ - pOut->pScopyFrom += u.ac.p1 - pOp->p2; + if( pOut->pScopyFrom>=&aMem[u.ad.p1] && pOut->pScopyFrom<&aMem[u.ad.p1+pOp->p3] ){ + pOut->pScopyFrom += u.ad.p1 - pOp->p2; } #endif - pIn1->zMalloc = u.ac.zMalloc; - REGISTER_TRACE(u.ac.p2++, pOut); + pIn1->zMalloc = u.ad.zMalloc; + REGISTER_TRACE(u.ad.p2++, pOut); pIn1++; pOut++; } break; } @@ -64611,14 +65838,14 @@ ** with an SQLITE_ROW return code and it sets up the sqlite3_stmt ** structure to provide access to the top P1 values as the result ** row. */ case OP_ResultRow: { -#if 0 /* local variables moved into u.ad */ +#if 0 /* local variables moved into u.ae */ Mem *pMem; int i; -#endif /* local variables moved into u.ad */ +#endif /* local variables moved into u.ae */ assert( p->nResColumn==pOp->p2 ); assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=p->nMem+1 ); /* If this statement has violated immediate foreign key constraints, do @@ -64654,21 +65881,21 @@ /* Invalidate all ephemeral cursor row caches */ p->cacheCtr = (p->cacheCtr + 2)|1; /* Make sure the results of the current row are \000 terminated ** and have an assigned type. The results are de-ephemeralized as - ** as side effect. + ** a side effect. */ - u.ad.pMem = p->pResultSet = &aMem[pOp->p1]; - for(u.ad.i=0; u.ad.ip2; u.ad.i++){ - assert( memIsValid(&u.ad.pMem[u.ad.i]) ); - Deephemeralize(&u.ad.pMem[u.ad.i]); - assert( (u.ad.pMem[u.ad.i].flags & MEM_Ephem)==0 - || (u.ad.pMem[u.ad.i].flags & (MEM_Str|MEM_Blob))==0 ); - sqlite3VdbeMemNulTerminate(&u.ad.pMem[u.ad.i]); - sqlite3VdbeMemStoreType(&u.ad.pMem[u.ad.i]); - REGISTER_TRACE(pOp->p1+u.ad.i, &u.ad.pMem[u.ad.i]); + u.ae.pMem = p->pResultSet = &aMem[pOp->p1]; + for(u.ae.i=0; u.ae.ip2; u.ae.i++){ + assert( memIsValid(&u.ae.pMem[u.ae.i]) ); + Deephemeralize(&u.ae.pMem[u.ae.i]); + assert( (u.ae.pMem[u.ae.i].flags & MEM_Ephem)==0 + || (u.ae.pMem[u.ae.i].flags & (MEM_Str|MEM_Blob))==0 ); + sqlite3VdbeMemNulTerminate(&u.ae.pMem[u.ae.i]); + sqlite3VdbeMemStoreType(&u.ae.pMem[u.ae.i]); + REGISTER_TRACE(pOp->p1+u.ae.i, &u.ae.pMem[u.ae.i]); } if( db->mallocFailed ) goto no_mem; /* Return SQLITE_ROW */ @@ -64688,13 +65915,13 @@ ** It is illegal for P1 and P3 to be the same register. Sometimes, ** if P3 is the same register as P2, the implementation is able ** to avoid a memcpy(). */ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ -#if 0 /* local variables moved into u.ae */ +#if 0 /* local variables moved into u.af */ i64 nByte; -#endif /* local variables moved into u.ae */ +#endif /* local variables moved into u.af */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; assert( pIn1!=pOut ); @@ -64703,26 +65930,26 @@ break; } if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem; Stringify(pIn1, encoding); Stringify(pIn2, encoding); - u.ae.nByte = pIn1->n + pIn2->n; - if( u.ae.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + u.af.nByte = pIn1->n + pIn2->n; + if( u.af.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } MemSetTypeFlag(pOut, MEM_Str); - if( sqlite3VdbeMemGrow(pOut, (int)u.ae.nByte+2, pOut==pIn2) ){ + if( sqlite3VdbeMemGrow(pOut, (int)u.af.nByte+2, pOut==pIn2) ){ goto no_mem; } if( pOut!=pIn2 ){ memcpy(pOut->z, pIn2->z, pIn2->n); } memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); - pOut->z[u.ae.nByte] = 0; - pOut->z[u.ae.nByte+1] = 0; + pOut->z[u.af.nByte] = 0; + pOut->z[u.af.nByte+1] = 0; pOut->flags |= MEM_Term; - pOut->n = (int)u.ae.nByte; + pOut->n = (int)u.af.nByte; pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -64762,80 +65989,80 @@ case OP_Add: /* same as TK_PLUS, in1, in2, out3 */ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ -#if 0 /* local variables moved into u.af */ +#if 0 /* local variables moved into u.ag */ int flags; /* Combined MEM_* flags from both inputs */ i64 iA; /* Integer value of left operand */ i64 iB; /* Integer value of right operand */ double rA; /* Real value of left operand */ double rB; /* Real value of right operand */ -#endif /* local variables moved into u.af */ +#endif /* local variables moved into u.ag */ pIn1 = &aMem[pOp->p1]; applyNumericAffinity(pIn1); pIn2 = &aMem[pOp->p2]; applyNumericAffinity(pIn2); pOut = &aMem[pOp->p3]; - u.af.flags = pIn1->flags | pIn2->flags; - if( (u.af.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; + u.ag.flags = pIn1->flags | pIn2->flags; + if( (u.ag.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ - u.af.iA = pIn1->u.i; - u.af.iB = pIn2->u.i; + u.ag.iA = pIn1->u.i; + u.ag.iB = pIn2->u.i; switch( pOp->opcode ){ - case OP_Add: if( sqlite3AddInt64(&u.af.iB,u.af.iA) ) goto fp_math; break; - case OP_Subtract: if( sqlite3SubInt64(&u.af.iB,u.af.iA) ) goto fp_math; break; - case OP_Multiply: if( sqlite3MulInt64(&u.af.iB,u.af.iA) ) goto fp_math; break; + case OP_Add: if( sqlite3AddInt64(&u.ag.iB,u.ag.iA) ) goto fp_math; break; + case OP_Subtract: if( sqlite3SubInt64(&u.ag.iB,u.ag.iA) ) goto fp_math; break; + case OP_Multiply: if( sqlite3MulInt64(&u.ag.iB,u.ag.iA) ) goto fp_math; break; case OP_Divide: { - if( u.af.iA==0 ) goto arithmetic_result_is_null; - if( u.af.iA==-1 && u.af.iB==SMALLEST_INT64 ) goto fp_math; - u.af.iB /= u.af.iA; + if( u.ag.iA==0 ) goto arithmetic_result_is_null; + if( u.ag.iA==-1 && u.ag.iB==SMALLEST_INT64 ) goto fp_math; + u.ag.iB /= u.ag.iA; break; } default: { - if( u.af.iA==0 ) goto arithmetic_result_is_null; - if( u.af.iA==-1 ) u.af.iA = 1; - u.af.iB %= u.af.iA; + if( u.ag.iA==0 ) goto arithmetic_result_is_null; + if( u.ag.iA==-1 ) u.ag.iA = 1; + u.ag.iB %= u.ag.iA; break; } } - pOut->u.i = u.af.iB; + pOut->u.i = u.ag.iB; MemSetTypeFlag(pOut, MEM_Int); }else{ fp_math: - u.af.rA = sqlite3VdbeRealValue(pIn1); - u.af.rB = sqlite3VdbeRealValue(pIn2); + u.ag.rA = sqlite3VdbeRealValue(pIn1); + u.ag.rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ - case OP_Add: u.af.rB += u.af.rA; break; - case OP_Subtract: u.af.rB -= u.af.rA; break; - case OP_Multiply: u.af.rB *= u.af.rA; break; + case OP_Add: u.ag.rB += u.ag.rA; break; + case OP_Subtract: u.ag.rB -= u.ag.rA; break; + case OP_Multiply: u.ag.rB *= u.ag.rA; break; case OP_Divide: { /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - if( u.af.rA==(double)0 ) goto arithmetic_result_is_null; - u.af.rB /= u.af.rA; + if( u.ag.rA==(double)0 ) goto arithmetic_result_is_null; + u.ag.rB /= u.ag.rA; break; } default: { - u.af.iA = (i64)u.af.rA; - u.af.iB = (i64)u.af.rB; - if( u.af.iA==0 ) goto arithmetic_result_is_null; - if( u.af.iA==-1 ) u.af.iA = 1; - u.af.rB = (double)(u.af.iB % u.af.iA); + u.ag.iA = (i64)u.ag.rA; + u.ag.iB = (i64)u.ag.rB; + if( u.ag.iA==0 ) goto arithmetic_result_is_null; + if( u.ag.iA==-1 ) u.ag.iA = 1; + u.ag.rB = (double)(u.ag.iB % u.ag.iA); break; } } #ifdef SQLITE_OMIT_FLOATING_POINT - pOut->u.i = u.af.rB; + pOut->u.i = u.ag.rB; MemSetTypeFlag(pOut, MEM_Int); #else - if( sqlite3IsNaN(u.af.rB) ){ + if( sqlite3IsNaN(u.ag.rB) ){ goto arithmetic_result_is_null; } - pOut->r = u.af.rB; + pOut->r = u.ag.rB; MemSetTypeFlag(pOut, MEM_Real); - if( (u.af.flags & MEM_Real)==0 ){ + if( (u.ag.flags & MEM_Real)==0 ){ sqlite3VdbeIntegerAffinity(pOut); } #endif } break; @@ -64876,96 +66103,96 @@ ** invocation of this opcode. ** ** See also: AggStep and AggFinal */ case OP_Function: { -#if 0 /* local variables moved into u.ag */ +#if 0 /* local variables moved into u.ah */ int i; Mem *pArg; sqlite3_context ctx; sqlite3_value **apVal; int n; -#endif /* local variables moved into u.ag */ +#endif /* local variables moved into u.ah */ - u.ag.n = pOp->p5; - u.ag.apVal = p->apArg; - assert( u.ag.apVal || u.ag.n==0 ); + u.ah.n = pOp->p5; + u.ah.apVal = p->apArg; + assert( u.ah.apVal || u.ah.n==0 ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); - assert( u.ag.n==0 || (pOp->p2>0 && pOp->p2+u.ag.n<=p->nMem+1) ); - assert( pOp->p3p2 || pOp->p3>=pOp->p2+u.ag.n ); - u.ag.pArg = &aMem[pOp->p2]; - for(u.ag.i=0; u.ag.ip2+u.ag.i, u.ag.pArg); + assert( u.ah.n==0 || (pOp->p2>0 && pOp->p2+u.ah.n<=p->nMem+1) ); + assert( pOp->p3p2 || pOp->p3>=pOp->p2+u.ah.n ); + u.ah.pArg = &aMem[pOp->p2]; + for(u.ah.i=0; u.ah.ip2+u.ah.i, u.ah.pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ - u.ag.ctx.pFunc = pOp->p4.pFunc; - u.ag.ctx.pVdbeFunc = 0; + u.ah.ctx.pFunc = pOp->p4.pFunc; + u.ah.ctx.pVdbeFunc = 0; }else{ - u.ag.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; - u.ag.ctx.pFunc = u.ag.ctx.pVdbeFunc->pFunc; + u.ah.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; + u.ah.ctx.pFunc = u.ah.ctx.pVdbeFunc->pFunc; } - u.ag.ctx.s.flags = MEM_Null; - u.ag.ctx.s.db = db; - u.ag.ctx.s.xDel = 0; - u.ag.ctx.s.zMalloc = 0; + u.ah.ctx.s.flags = MEM_Null; + u.ah.ctx.s.db = db; + u.ah.ctx.s.xDel = 0; + u.ah.ctx.s.zMalloc = 0; /* The output cell may already have a buffer allocated. Move - ** the pointer to u.ag.ctx.s so in case the user-function can use + ** the pointer to u.ah.ctx.s so in case the user-function can use ** the already allocated buffer instead of allocating a new one. */ - sqlite3VdbeMemMove(&u.ag.ctx.s, pOut); - MemSetTypeFlag(&u.ag.ctx.s, MEM_Null); + sqlite3VdbeMemMove(&u.ah.ctx.s, pOut); + MemSetTypeFlag(&u.ah.ctx.s, MEM_Null); - u.ag.ctx.isError = 0; - if( u.ag.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ + u.ah.ctx.isError = 0; + if( u.ah.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); - u.ag.ctx.pColl = pOp[-1].p4.pColl; + u.ah.ctx.pColl = pOp[-1].p4.pColl; } db->lastRowid = lastRowid; - (*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */ + (*u.ah.ctx.pFunc->xFunc)(&u.ah.ctx, u.ah.n, u.ah.apVal); /* IMP: R-24505-23230 */ lastRowid = db->lastRowid; /* If any auxiliary data functions have been called by this user function, ** immediately call the destructor for any non-static values. */ - if( u.ag.ctx.pVdbeFunc ){ - sqlite3VdbeDeleteAuxData(u.ag.ctx.pVdbeFunc, pOp->p1); - pOp->p4.pVdbeFunc = u.ag.ctx.pVdbeFunc; + if( u.ah.ctx.pVdbeFunc ){ + sqlite3VdbeDeleteAuxData(u.ah.ctx.pVdbeFunc, pOp->p1); + pOp->p4.pVdbeFunc = u.ah.ctx.pVdbeFunc; pOp->p4type = P4_VDBEFUNC; } if( db->mallocFailed ){ /* Even though a malloc() has failed, the implementation of the ** user function may have called an sqlite3_result_XXX() function ** to return a value. The following call releases any resources ** associated with such a value. */ - sqlite3VdbeMemRelease(&u.ag.ctx.s); + sqlite3VdbeMemRelease(&u.ah.ctx.s); goto no_mem; } /* If the function returned an error, throw an exception */ - if( u.ag.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ag.ctx.s)); - rc = u.ag.ctx.isError; + if( u.ah.ctx.isError ){ + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ah.ctx.s)); + rc = u.ah.ctx.isError; } /* Copy the result of the function into register P3 */ - sqlite3VdbeChangeEncoding(&u.ag.ctx.s, encoding); - sqlite3VdbeMemMove(pOut, &u.ag.ctx.s); + sqlite3VdbeChangeEncoding(&u.ah.ctx.s, encoding); + sqlite3VdbeMemMove(pOut, &u.ah.ctx.s); if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } #if 0 @@ -65009,56 +66236,56 @@ */ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ -#if 0 /* local variables moved into u.ah */ +#if 0 /* local variables moved into u.ai */ i64 iA; u64 uA; i64 iB; u8 op; -#endif /* local variables moved into u.ah */ +#endif /* local variables moved into u.ai */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; if( (pIn1->flags | pIn2->flags) & MEM_Null ){ sqlite3VdbeMemSetNull(pOut); break; } - u.ah.iA = sqlite3VdbeIntValue(pIn2); - u.ah.iB = sqlite3VdbeIntValue(pIn1); - u.ah.op = pOp->opcode; - if( u.ah.op==OP_BitAnd ){ - u.ah.iA &= u.ah.iB; - }else if( u.ah.op==OP_BitOr ){ - u.ah.iA |= u.ah.iB; - }else if( u.ah.iB!=0 ){ - assert( u.ah.op==OP_ShiftRight || u.ah.op==OP_ShiftLeft ); + u.ai.iA = sqlite3VdbeIntValue(pIn2); + u.ai.iB = sqlite3VdbeIntValue(pIn1); + u.ai.op = pOp->opcode; + if( u.ai.op==OP_BitAnd ){ + u.ai.iA &= u.ai.iB; + }else if( u.ai.op==OP_BitOr ){ + u.ai.iA |= u.ai.iB; + }else if( u.ai.iB!=0 ){ + assert( u.ai.op==OP_ShiftRight || u.ai.op==OP_ShiftLeft ); /* If shifting by a negative amount, shift in the other direction */ - if( u.ah.iB<0 ){ + if( u.ai.iB<0 ){ assert( OP_ShiftRight==OP_ShiftLeft+1 ); - u.ah.op = 2*OP_ShiftLeft + 1 - u.ah.op; - u.ah.iB = u.ah.iB>(-64) ? -u.ah.iB : 64; + u.ai.op = 2*OP_ShiftLeft + 1 - u.ai.op; + u.ai.iB = u.ai.iB>(-64) ? -u.ai.iB : 64; } - if( u.ah.iB>=64 ){ - u.ah.iA = (u.ah.iA>=0 || u.ah.op==OP_ShiftLeft) ? 0 : -1; - }else{ - memcpy(&u.ah.uA, &u.ah.iA, sizeof(u.ah.uA)); - if( u.ah.op==OP_ShiftLeft ){ - u.ah.uA <<= u.ah.iB; - }else{ - u.ah.uA >>= u.ah.iB; + if( u.ai.iB>=64 ){ + u.ai.iA = (u.ai.iA>=0 || u.ai.op==OP_ShiftLeft) ? 0 : -1; + }else{ + memcpy(&u.ai.uA, &u.ai.iA, sizeof(u.ai.uA)); + if( u.ai.op==OP_ShiftLeft ){ + u.ai.uA <<= u.ai.iB; + }else{ + u.ai.uA >>= u.ai.iB; /* Sign-extend on a right shift of a negative number */ - if( u.ah.iA<0 ) u.ah.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.ah.iB); + if( u.ai.iA<0 ) u.ai.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.ai.iB); } - memcpy(&u.ah.iA, &u.ah.uA, sizeof(u.ah.iA)); + memcpy(&u.ai.iA, &u.ai.uA, sizeof(u.ai.iA)); } } - pOut->u.i = u.ah.iA; + pOut->u.i = u.ai.iA; MemSetTypeFlag(pOut, MEM_Int); break; } /* Opcode: AddImm P1 P2 * * * @@ -65295,30 +66522,30 @@ case OP_Ne: /* same as TK_NE, jump, in1, in3 */ case OP_Lt: /* same as TK_LT, jump, in1, in3 */ case OP_Le: /* same as TK_LE, jump, in1, in3 */ case OP_Gt: /* same as TK_GT, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ -#if 0 /* local variables moved into u.ai */ +#if 0 /* local variables moved into u.aj */ int res; /* Result of the comparison of pIn1 against pIn3 */ char affinity; /* Affinity to use for comparison */ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ -#endif /* local variables moved into u.ai */ +#endif /* local variables moved into u.aj */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; - u.ai.flags1 = pIn1->flags; - u.ai.flags3 = pIn3->flags; - if( (u.ai.flags1 | u.ai.flags3)&MEM_Null ){ + u.aj.flags1 = pIn1->flags; + u.aj.flags3 = pIn3->flags; + if( (u.aj.flags1 | u.aj.flags3)&MEM_Null ){ /* One or both operands are NULL */ if( pOp->p5 & SQLITE_NULLEQ ){ /* If SQLITE_NULLEQ is set (which will only happen if the operator is ** OP_Eq or OP_Ne) then take the jump or not depending on whether ** or not both operands are null. */ assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); - u.ai.res = (u.ai.flags1 & u.ai.flags3 & MEM_Null)==0; + u.aj.res = (u.aj.flags1 & u.aj.flags3 & MEM_Null)==0; }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ @@ -65331,44 +66558,44 @@ } break; } }else{ /* Neither operand is NULL. Do a comparison. */ - u.ai.affinity = pOp->p5 & SQLITE_AFF_MASK; - if( u.ai.affinity ){ - applyAffinity(pIn1, u.ai.affinity, encoding); - applyAffinity(pIn3, u.ai.affinity, encoding); + u.aj.affinity = pOp->p5 & SQLITE_AFF_MASK; + if( u.aj.affinity ){ + applyAffinity(pIn1, u.aj.affinity, encoding); + applyAffinity(pIn3, u.aj.affinity, encoding); if( db->mallocFailed ) goto no_mem; } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); ExpandBlob(pIn1); ExpandBlob(pIn3); - u.ai.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + u.aj.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } switch( pOp->opcode ){ - case OP_Eq: u.ai.res = u.ai.res==0; break; - case OP_Ne: u.ai.res = u.ai.res!=0; break; - case OP_Lt: u.ai.res = u.ai.res<0; break; - case OP_Le: u.ai.res = u.ai.res<=0; break; - case OP_Gt: u.ai.res = u.ai.res>0; break; - default: u.ai.res = u.ai.res>=0; break; + case OP_Eq: u.aj.res = u.aj.res==0; break; + case OP_Ne: u.aj.res = u.aj.res!=0; break; + case OP_Lt: u.aj.res = u.aj.res<0; break; + case OP_Le: u.aj.res = u.aj.res<=0; break; + case OP_Gt: u.aj.res = u.aj.res>0; break; + default: u.aj.res = u.aj.res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = u.ai.res; + pOut->u.i = u.aj.res; REGISTER_TRACE(pOp->p2, pOut); - }else if( u.ai.res ){ + }else if( u.aj.res ){ pc = pOp->p2-1; } /* Undo any changes made by applyAffinity() to the input registers. */ - pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (u.ai.flags1&MEM_TypeMask); - pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (u.ai.flags3&MEM_TypeMask); + pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (u.aj.flags1&MEM_TypeMask); + pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (u.aj.flags3&MEM_TypeMask); break; } /* Opcode: Permutation * * * P4 * ** @@ -65399,50 +66626,50 @@ ** The comparison is a sort comparison, so NULLs compare equal, ** NULLs are less than numbers, numbers are less than strings, ** and strings are less than blobs. */ case OP_Compare: { -#if 0 /* local variables moved into u.aj */ +#if 0 /* local variables moved into u.ak */ int n; int i; int p1; int p2; const KeyInfo *pKeyInfo; int idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ -#endif /* local variables moved into u.aj */ - - u.aj.n = pOp->p3; - u.aj.pKeyInfo = pOp->p4.pKeyInfo; - assert( u.aj.n>0 ); - assert( u.aj.pKeyInfo!=0 ); - u.aj.p1 = pOp->p1; - u.aj.p2 = pOp->p2; +#endif /* local variables moved into u.ak */ + + u.ak.n = pOp->p3; + u.ak.pKeyInfo = pOp->p4.pKeyInfo; + assert( u.ak.n>0 ); + assert( u.ak.pKeyInfo!=0 ); + u.ak.p1 = pOp->p1; + u.ak.p2 = pOp->p2; #if SQLITE_DEBUG if( aPermute ){ int k, mx = 0; - for(k=0; kmx ) mx = aPermute[k]; - assert( u.aj.p1>0 && u.aj.p1+mx<=p->nMem+1 ); - assert( u.aj.p2>0 && u.aj.p2+mx<=p->nMem+1 ); + for(k=0; kmx ) mx = aPermute[k]; + assert( u.ak.p1>0 && u.ak.p1+mx<=p->nMem+1 ); + assert( u.ak.p2>0 && u.ak.p2+mx<=p->nMem+1 ); }else{ - assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 ); - assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 ); + assert( u.ak.p1>0 && u.ak.p1+u.ak.n<=p->nMem+1 ); + assert( u.ak.p2>0 && u.ak.p2+u.ak.n<=p->nMem+1 ); } #endif /* SQLITE_DEBUG */ - for(u.aj.i=0; u.aj.inField ); - u.aj.pColl = u.aj.pKeyInfo->aColl[u.aj.i]; - u.aj.bRev = u.aj.pKeyInfo->aSortOrder[u.aj.i]; - iCompare = sqlite3MemCompare(&aMem[u.aj.p1+u.aj.idx], &aMem[u.aj.p2+u.aj.idx], u.aj.pColl); + for(u.ak.i=0; u.ak.inField ); + u.ak.pColl = u.ak.pKeyInfo->aColl[u.ak.i]; + u.ak.bRev = u.ak.pKeyInfo->aSortOrder[u.ak.i]; + iCompare = sqlite3MemCompare(&aMem[u.ak.p1+u.ak.idx], &aMem[u.ak.p2+u.ak.idx], u.ak.pColl); if( iCompare ){ - if( u.aj.bRev ) iCompare = -iCompare; + if( u.ak.bRev ) iCompare = -iCompare; break; } } aPermute = 0; break; @@ -65483,39 +66710,39 @@ ** even if the other input is NULL. A NULL and false or two NULLs ** give a NULL output. */ case OP_And: /* same as TK_AND, in1, in2, out3 */ case OP_Or: { /* same as TK_OR, in1, in2, out3 */ -#if 0 /* local variables moved into u.ak */ +#if 0 /* local variables moved into u.al */ int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ -#endif /* local variables moved into u.ak */ +#endif /* local variables moved into u.al */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ - u.ak.v1 = 2; + u.al.v1 = 2; }else{ - u.ak.v1 = sqlite3VdbeIntValue(pIn1)!=0; + u.al.v1 = sqlite3VdbeIntValue(pIn1)!=0; } pIn2 = &aMem[pOp->p2]; if( pIn2->flags & MEM_Null ){ - u.ak.v2 = 2; + u.al.v2 = 2; }else{ - u.ak.v2 = sqlite3VdbeIntValue(pIn2)!=0; + u.al.v2 = sqlite3VdbeIntValue(pIn2)!=0; } if( pOp->opcode==OP_And ){ static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - u.ak.v1 = and_logic[u.ak.v1*3+u.ak.v2]; + u.al.v1 = and_logic[u.al.v1*3+u.al.v2]; }else{ static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - u.ak.v1 = or_logic[u.ak.v1*3+u.ak.v2]; + u.al.v1 = or_logic[u.al.v1*3+u.al.v2]; } pOut = &aMem[pOp->p3]; - if( u.ak.v1==2 ){ + if( u.al.v1==2 ){ MemSetTypeFlag(pOut, MEM_Null); }else{ - pOut->u.i = u.ak.v1; + pOut->u.i = u.al.v1; MemSetTypeFlag(pOut, MEM_Int); } break; } @@ -65553,55 +66780,55 @@ break; } /* Opcode: Once P1 P2 * * * ** -** Jump to P2 if the value in register P1 is a not null or zero. If -** the value is NULL or zero, fall through and change the P1 register -** to an integer 1. +** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise, +** set the flag and fall through to the next instruction. ** -** When P1 is not used otherwise in a program, this opcode falls through -** once and jumps on all subsequent invocations. It is the equivalent -** of "OP_If P1 P2", followed by "OP_Integer 1 P1". +** See also: JumpOnce */ +case OP_Once: { /* jump */ + assert( pOp->p1nOnceFlag ); + if( p->aOnceFlag[pOp->p1] ){ + pc = pOp->p2-1; + }else{ + p->aOnceFlag[pOp->p1] = 1; + } + break; +} + /* Opcode: If P1 P2 P3 * * ** ** Jump to P2 if the value in register P1 is true. The value ** is considered true if it is numeric and non-zero. If the value -** in P1 is NULL then take the jump if P3 is true. +** in P1 is NULL then take the jump if P3 is non-zero. */ /* Opcode: IfNot P1 P2 P3 * * ** ** Jump to P2 if the value in register P1 is False. The value -** is considered true if it has a numeric value of zero. If the value -** in P1 is NULL then take the jump if P3 is true. +** is considered false if it has a numeric value of zero. If the value +** in P1 is NULL then take the jump if P3 is zero. */ -case OP_Once: /* jump, in1 */ case OP_If: /* jump, in1 */ case OP_IfNot: { /* jump, in1 */ -#if 0 /* local variables moved into u.al */ +#if 0 /* local variables moved into u.am */ int c; -#endif /* local variables moved into u.al */ +#endif /* local variables moved into u.am */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ - u.al.c = pOp->p3; + u.am.c = pOp->p3; }else{ #ifdef SQLITE_OMIT_FLOATING_POINT - u.al.c = sqlite3VdbeIntValue(pIn1)!=0; + u.am.c = sqlite3VdbeIntValue(pIn1)!=0; #else - u.al.c = sqlite3VdbeRealValue(pIn1)!=0.0; + u.am.c = sqlite3VdbeRealValue(pIn1)!=0.0; #endif - if( pOp->opcode==OP_IfNot ) u.al.c = !u.al.c; - } - if( u.al.c ){ - pc = pOp->p2-1; - }else if( pOp->opcode==OP_Once ){ - assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 ); - memAboutToChange(p, pIn1); - pIn1->flags = MEM_Int; - pIn1->u.i = 1; - REGISTER_TRACE(pOp->p1, pIn1); + if( pOp->opcode==OP_IfNot ) u.am.c = !u.am.c; + } + if( u.am.c ){ + pc = pOp->p2-1; } break; } /* Opcode: IsNull P1 P2 * * * @@ -65646,11 +66873,11 @@ ** then the cache of the cursor is reset prior to extracting the column. ** The first OP_Column against a pseudo-table after the value of the content ** register has changed should have this bit set. */ case OP_Column: { -#if 0 /* local variables moved into u.am */ +#if 0 /* local variables moved into u.an */ u32 payloadSize; /* Number of bytes in the record */ i64 payloadSize64; /* Number of bytes in the record */ int p1; /* P1 value of the opcode */ int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ @@ -65670,130 +66897,130 @@ u32 szField; /* Number of bytes in the content of a field */ int szHdr; /* Size of the header size field at start of record */ int avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ -#endif /* local variables moved into u.am */ +#endif /* local variables moved into u.an */ - u.am.p1 = pOp->p1; - u.am.p2 = pOp->p2; - u.am.pC = 0; - memset(&u.am.sMem, 0, sizeof(u.am.sMem)); - assert( u.am.p1nCursor ); + u.an.p1 = pOp->p1; + u.an.p2 = pOp->p2; + u.an.pC = 0; + memset(&u.an.sMem, 0, sizeof(u.an.sMem)); + assert( u.an.p1nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.am.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.am.pDest); - u.am.zRec = 0; + u.an.pDest = &aMem[pOp->p3]; + memAboutToChange(p, u.an.pDest); + u.an.zRec = 0; - /* This block sets the variable u.am.payloadSize to be the total number of + /* This block sets the variable u.an.payloadSize to be the total number of ** bytes in the record. ** - ** u.am.zRec is set to be the complete text of the record if it is available. + ** u.an.zRec is set to be the complete text of the record if it is available. ** The complete record text is always available for pseudo-tables ** If the record is stored in a cursor, the complete record text - ** might be available in the u.am.pC->aRow cache. Or it might not be. - ** If the data is unavailable, u.am.zRec is set to NULL. + ** might be available in the u.an.pC->aRow cache. Or it might not be. + ** If the data is unavailable, u.an.zRec is set to NULL. ** ** We also compute the number of columns in the record. For cursors, ** the number of columns is stored in the VdbeCursor.nField element. */ - u.am.pC = p->apCsr[u.am.p1]; - assert( u.am.pC!=0 ); + u.an.pC = p->apCsr[u.an.p1]; + assert( u.an.pC!=0 ); #ifndef SQLITE_OMIT_VIRTUALTABLE - assert( u.am.pC->pVtabCursor==0 ); + assert( u.an.pC->pVtabCursor==0 ); #endif - u.am.pCrsr = u.am.pC->pCursor; - if( u.am.pCrsr!=0 ){ + u.an.pCrsr = u.an.pC->pCursor; + if( u.an.pCrsr!=0 ){ /* The record is stored in a B-Tree */ - rc = sqlite3VdbeCursorMoveto(u.am.pC); + rc = sqlite3VdbeCursorMoveto(u.an.pC); if( rc ) goto abort_due_to_error; - if( u.am.pC->nullRow ){ - u.am.payloadSize = 0; - }else if( u.am.pC->cacheStatus==p->cacheCtr ){ - u.am.payloadSize = u.am.pC->payloadSize; - u.am.zRec = (char*)u.am.pC->aRow; - }else if( u.am.pC->isIndex ){ - assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) ); - rc = sqlite3BtreeKeySize(u.am.pCrsr, &u.am.payloadSize64); + if( u.an.pC->nullRow ){ + u.an.payloadSize = 0; + }else if( u.an.pC->cacheStatus==p->cacheCtr ){ + u.an.payloadSize = u.an.pC->payloadSize; + u.an.zRec = (char*)u.an.pC->aRow; + }else if( u.an.pC->isIndex ){ + assert( sqlite3BtreeCursorIsValid(u.an.pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeKeySize(u.an.pCrsr, &u.an.payloadSize64); assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for u.am.payloadSize64 to be + ** payload size, so it is impossible for u.an.payloadSize64 to be ** larger than 32 bits. */ - assert( (u.am.payloadSize64 & SQLITE_MAX_U32)==(u64)u.am.payloadSize64 ); - u.am.payloadSize = (u32)u.am.payloadSize64; + assert( (u.an.payloadSize64 & SQLITE_MAX_U32)==(u64)u.an.payloadSize64 ); + u.an.payloadSize = (u32)u.an.payloadSize64; }else{ - assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) ); - rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize); + assert( sqlite3BtreeCursorIsValid(u.an.pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeDataSize(u.an.pCrsr, &u.an.payloadSize); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } - }else if( ALWAYS(u.am.pC->pseudoTableReg>0) ){ - u.am.pReg = &aMem[u.am.pC->pseudoTableReg]; - assert( u.am.pReg->flags & MEM_Blob ); - assert( memIsValid(u.am.pReg) ); - u.am.payloadSize = u.am.pReg->n; - u.am.zRec = u.am.pReg->z; - u.am.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; - assert( u.am.payloadSize==0 || u.am.zRec!=0 ); + }else if( ALWAYS(u.an.pC->pseudoTableReg>0) ){ + u.an.pReg = &aMem[u.an.pC->pseudoTableReg]; + assert( u.an.pReg->flags & MEM_Blob ); + assert( memIsValid(u.an.pReg) ); + u.an.payloadSize = u.an.pReg->n; + u.an.zRec = u.an.pReg->z; + u.an.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; + assert( u.an.payloadSize==0 || u.an.zRec!=0 ); }else{ /* Consider the row to be NULL */ - u.am.payloadSize = 0; + u.an.payloadSize = 0; } - /* If u.am.payloadSize is 0, then just store a NULL. This can happen because of + /* If u.an.payloadSize is 0, then just store a NULL. This can happen because of ** nullRow or because of a corrupt database. */ - if( u.am.payloadSize==0 ){ - MemSetTypeFlag(u.am.pDest, MEM_Null); + if( u.an.payloadSize==0 ){ + MemSetTypeFlag(u.an.pDest, MEM_Null); goto op_column_out; } assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); - if( u.am.payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( u.an.payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - u.am.nField = u.am.pC->nField; - assert( u.am.p2nField; + assert( u.an.p2aType; - if( u.am.pC->cacheStatus==p->cacheCtr ){ - u.am.aOffset = u.am.pC->aOffset; + u.an.aType = u.an.pC->aType; + if( u.an.pC->cacheStatus==p->cacheCtr ){ + u.an.aOffset = u.an.pC->aOffset; }else{ - assert(u.am.aType); - u.am.avail = 0; - u.am.pC->aOffset = u.am.aOffset = &u.am.aType[u.am.nField]; - u.am.pC->payloadSize = u.am.payloadSize; - u.am.pC->cacheStatus = p->cacheCtr; + assert(u.an.aType); + u.an.avail = 0; + u.an.pC->aOffset = u.an.aOffset = &u.an.aType[u.an.nField]; + u.an.pC->payloadSize = u.an.payloadSize; + u.an.pC->cacheStatus = p->cacheCtr; /* Figure out how many bytes are in the header */ - if( u.am.zRec ){ - u.am.zData = u.am.zRec; + if( u.an.zRec ){ + u.an.zData = u.an.zRec; }else{ - if( u.am.pC->isIndex ){ - u.am.zData = (char*)sqlite3BtreeKeyFetch(u.am.pCrsr, &u.am.avail); + if( u.an.pC->isIndex ){ + u.an.zData = (char*)sqlite3BtreeKeyFetch(u.an.pCrsr, &u.an.avail); }else{ - u.am.zData = (char*)sqlite3BtreeDataFetch(u.am.pCrsr, &u.am.avail); + u.an.zData = (char*)sqlite3BtreeDataFetch(u.an.pCrsr, &u.an.avail); } /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the u.am.pC->aRow cache. That will save us from + ** save the payload in the u.an.pC->aRow cache. That will save us from ** having to make additional calls to fetch the content portion of ** the record. */ - assert( u.am.avail>=0 ); - if( u.am.payloadSize <= (u32)u.am.avail ){ - u.am.zRec = u.am.zData; - u.am.pC->aRow = (u8*)u.am.zData; + assert( u.an.avail>=0 ); + if( u.an.payloadSize <= (u32)u.an.avail ){ + u.an.zRec = u.an.zData; + u.an.pC->aRow = (u8*)u.an.zData; }else{ - u.am.pC->aRow = 0; + u.an.pC->aRow = 0; } } /* The following assert is true in all cases accept when ** the database file has been corrupted externally. - ** assert( u.am.zRec!=0 || u.am.avail>=u.am.payloadSize || u.am.avail>=9 ); */ - u.am.szHdr = getVarint32((u8*)u.am.zData, u.am.offset); + ** assert( u.an.zRec!=0 || u.an.avail>=u.an.payloadSize || u.an.avail>=9 ); */ + u.an.szHdr = getVarint32((u8*)u.an.zData, u.an.offset); /* Make sure a corrupt database has not given us an oversize header. ** Do this now to avoid an oversize memory allocation. ** ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte @@ -65800,146 +67027,146 @@ ** types use so much data space that there can only be 4096 and 32 of ** them, respectively. So the maximum header length results from a ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ - if( u.am.offset > 98307 ){ + if( u.an.offset > 98307 ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_out; } - /* Compute in u.am.len the number of bytes of data we need to read in order - ** to get u.am.nField type values. u.am.offset is an upper bound on this. But - ** u.am.nField might be significantly less than the true number of columns - ** in the table, and in that case, 5*u.am.nField+3 might be smaller than u.am.offset. - ** We want to minimize u.am.len in order to limit the size of the memory - ** allocation, especially if a corrupt database file has caused u.am.offset + /* Compute in u.an.len the number of bytes of data we need to read in order + ** to get u.an.nField type values. u.an.offset is an upper bound on this. But + ** u.an.nField might be significantly less than the true number of columns + ** in the table, and in that case, 5*u.an.nField+3 might be smaller than u.an.offset. + ** We want to minimize u.an.len in order to limit the size of the memory + ** allocation, especially if a corrupt database file has caused u.an.offset ** to be oversized. Offset is limited to 98307 above. But 98307 might ** still exceed Robson memory allocation limits on some configurations. - ** On systems that cannot tolerate large memory allocations, u.am.nField*5+3 - ** will likely be much smaller since u.am.nField will likely be less than + ** On systems that cannot tolerate large memory allocations, u.an.nField*5+3 + ** will likely be much smaller since u.an.nField will likely be less than ** 20 or so. This insures that Robson memory allocation limits are ** not exceeded even for corrupt database files. */ - u.am.len = u.am.nField*5 + 3; - if( u.am.len > (int)u.am.offset ) u.am.len = (int)u.am.offset; + u.an.len = u.an.nField*5 + 3; + if( u.an.len > (int)u.an.offset ) u.an.len = (int)u.an.offset; /* The KeyFetch() or DataFetch() above are fast and will get the entire ** record header in most cases. But they will fail to get the complete ** record header if the record header does not fit on a single page ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to ** acquire the complete header text. */ - if( !u.am.zRec && u.am.availisIndex, &u.am.sMem); + if( !u.an.zRec && u.an.availisIndex, &u.an.sMem); if( rc!=SQLITE_OK ){ goto op_column_out; } - u.am.zData = u.am.sMem.z; - } - u.am.zEndHdr = (u8 *)&u.am.zData[u.am.len]; - u.am.zIdx = (u8 *)&u.am.zData[u.am.szHdr]; - - /* Scan the header and use it to fill in the u.am.aType[] and u.am.aOffset[] - ** arrays. u.am.aType[u.am.i] will contain the type integer for the u.am.i-th - ** column and u.am.aOffset[u.am.i] will contain the u.am.offset from the beginning - ** of the record to the start of the data for the u.am.i-th column - */ - for(u.am.i=0; u.am.i u.am.zEndHdr) || (u.am.offset > u.am.payloadSize) - || (u.am.zIdx==u.am.zEndHdr && u.am.offset!=u.am.payloadSize) ){ + if( (u.an.zIdx > u.an.zEndHdr) || (u.an.offset > u.an.payloadSize) + || (u.an.zIdx==u.an.zEndHdr && u.an.offset!=u.an.payloadSize) ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_out; } } - /* Get the column information. If u.am.aOffset[u.am.p2] is non-zero, then - ** deserialize the value from the record. If u.am.aOffset[u.am.p2] is zero, + /* Get the column information. If u.an.aOffset[u.an.p2] is non-zero, then + ** deserialize the value from the record. If u.an.aOffset[u.an.p2] is zero, ** then there are not enough fields in the record to satisfy the ** request. In this case, set the value NULL or to P4 if P4 is ** a pointer to a Mem object. */ - if( u.am.aOffset[u.am.p2] ){ + if( u.an.aOffset[u.an.p2] ){ assert( rc==SQLITE_OK ); - if( u.am.zRec ){ - MemReleaseExt(u.am.pDest); - sqlite3VdbeSerialGet((u8 *)&u.am.zRec[u.am.aOffset[u.am.p2]], u.am.aType[u.am.p2], u.am.pDest); + if( u.an.zRec ){ + VdbeMemRelease(u.an.pDest); + sqlite3VdbeSerialGet((u8 *)&u.an.zRec[u.an.aOffset[u.an.p2]], u.an.aType[u.an.p2], u.an.pDest); }else{ - u.am.len = sqlite3VdbeSerialTypeLen(u.am.aType[u.am.p2]); - sqlite3VdbeMemMove(&u.am.sMem, u.am.pDest); - rc = sqlite3VdbeMemFromBtree(u.am.pCrsr, u.am.aOffset[u.am.p2], u.am.len, u.am.pC->isIndex, &u.am.sMem); + u.an.len = sqlite3VdbeSerialTypeLen(u.an.aType[u.an.p2]); + sqlite3VdbeMemMove(&u.an.sMem, u.an.pDest); + rc = sqlite3VdbeMemFromBtree(u.an.pCrsr, u.an.aOffset[u.an.p2], u.an.len, u.an.pC->isIndex, &u.an.sMem); if( rc!=SQLITE_OK ){ goto op_column_out; } - u.am.zData = u.am.sMem.z; - sqlite3VdbeSerialGet((u8*)u.am.zData, u.am.aType[u.am.p2], u.am.pDest); + u.an.zData = u.an.sMem.z; + sqlite3VdbeSerialGet((u8*)u.an.zData, u.an.aType[u.an.p2], u.an.pDest); } - u.am.pDest->enc = encoding; + u.an.pDest->enc = encoding; }else{ if( pOp->p4type==P4_MEM ){ - sqlite3VdbeMemShallowCopy(u.am.pDest, pOp->p4.pMem, MEM_Static); + sqlite3VdbeMemShallowCopy(u.an.pDest, pOp->p4.pMem, MEM_Static); }else{ - MemSetTypeFlag(u.am.pDest, MEM_Null); + MemSetTypeFlag(u.an.pDest, MEM_Null); } } /* If we dynamically allocated space to hold the data (in the ** sqlite3VdbeMemFromBtree() call above) then transfer control of that - ** dynamically allocated space over to the u.am.pDest structure. + ** dynamically allocated space over to the u.an.pDest structure. ** This prevents a memory copy. */ - if( u.am.sMem.zMalloc ){ - assert( u.am.sMem.z==u.am.sMem.zMalloc ); - assert( !(u.am.pDest->flags & MEM_Dyn) ); - assert( !(u.am.pDest->flags & (MEM_Blob|MEM_Str)) || u.am.pDest->z==u.am.sMem.z ); - u.am.pDest->flags &= ~(MEM_Ephem|MEM_Static); - u.am.pDest->flags |= MEM_Term; - u.am.pDest->z = u.am.sMem.z; - u.am.pDest->zMalloc = u.am.sMem.zMalloc; + if( u.an.sMem.zMalloc ){ + assert( u.an.sMem.z==u.an.sMem.zMalloc ); + assert( !(u.an.pDest->flags & MEM_Dyn) ); + assert( !(u.an.pDest->flags & (MEM_Blob|MEM_Str)) || u.an.pDest->z==u.an.sMem.z ); + u.an.pDest->flags &= ~(MEM_Ephem|MEM_Static); + u.an.pDest->flags |= MEM_Term; + u.an.pDest->z = u.an.sMem.z; + u.an.pDest->zMalloc = u.an.sMem.zMalloc; } - rc = sqlite3VdbeMemMakeWriteable(u.am.pDest); + rc = sqlite3VdbeMemMakeWriteable(u.an.pDest); op_column_out: - UPDATE_MAX_BLOBSIZE(u.am.pDest); - REGISTER_TRACE(pOp->p3, u.am.pDest); + UPDATE_MAX_BLOBSIZE(u.an.pDest); + REGISTER_TRACE(pOp->p3, u.an.pDest); break; } /* Opcode: Affinity P1 P2 * P4 * ** @@ -65948,24 +67175,24 @@ ** P4 is a string that is P2 characters long. The nth character of the ** string indicates the column affinity that should be used for the nth ** memory cell in the range. */ case OP_Affinity: { -#if 0 /* local variables moved into u.an */ +#if 0 /* local variables moved into u.ao */ const char *zAffinity; /* The affinity to be applied */ char cAff; /* A single character of affinity */ -#endif /* local variables moved into u.an */ +#endif /* local variables moved into u.ao */ - u.an.zAffinity = pOp->p4.z; - assert( u.an.zAffinity!=0 ); - assert( u.an.zAffinity[pOp->p2]==0 ); + u.ao.zAffinity = pOp->p4.z; + assert( u.ao.zAffinity!=0 ); + assert( u.ao.zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; - while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ + while( (u.ao.cAff = *(u.ao.zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); assert( memIsValid(pIn1) ); ExpandBlob(pIn1); - applyAffinity(pIn1, u.an.cAff, encoding); + applyAffinity(pIn1, u.ao.cAff, encoding); pIn1++; } break; } @@ -65983,11 +67210,11 @@ ** macros defined in sqliteInt.h. ** ** If P4 is NULL then all index fields have the affinity NONE. */ case OP_MakeRecord: { -#if 0 /* local variables moved into u.ao */ +#if 0 /* local variables moved into u.ap */ u8 *zNewRecord; /* A buffer to hold the data for the new record */ Mem *pRec; /* The new record */ u64 nData; /* Number of bytes of data space */ int nHdr; /* Number of bytes of header space */ i64 nByte; /* Data space required for this record */ @@ -65999,11 +67226,11 @@ int nField; /* Number of fields in the record */ char *zAffinity; /* The affinity string for the record */ int file_format; /* File format to use for encoding */ int i; /* Space used in zNewRecord[] */ int len; /* Length of a field */ -#endif /* local variables moved into u.ao */ +#endif /* local variables moved into u.ap */ /* Assuming the record contains N fields, the record format looks ** like this: ** ** ------------------------------------------------------------------------ @@ -66016,87 +67243,87 @@ ** Each type field is a varint representing the serial type of the ** corresponding data element (see sqlite3VdbeSerialType()). The ** hdr-size field is also a varint which is the offset from the beginning ** of the record to data0. */ - u.ao.nData = 0; /* Number of bytes of data space */ - u.ao.nHdr = 0; /* Number of bytes of header space */ - u.ao.nZero = 0; /* Number of zero bytes at the end of the record */ - u.ao.nField = pOp->p1; - u.ao.zAffinity = pOp->p4.z; - assert( u.ao.nField>0 && pOp->p2>0 && pOp->p2+u.ao.nField<=p->nMem+1 ); - u.ao.pData0 = &aMem[u.ao.nField]; - u.ao.nField = pOp->p2; - u.ao.pLast = &u.ao.pData0[u.ao.nField-1]; - u.ao.file_format = p->minWriteFileFormat; + u.ap.nData = 0; /* Number of bytes of data space */ + u.ap.nHdr = 0; /* Number of bytes of header space */ + u.ap.nZero = 0; /* Number of zero bytes at the end of the record */ + u.ap.nField = pOp->p1; + u.ap.zAffinity = pOp->p4.z; + assert( u.ap.nField>0 && pOp->p2>0 && pOp->p2+u.ap.nField<=p->nMem+1 ); + u.ap.pData0 = &aMem[u.ap.nField]; + u.ap.nField = pOp->p2; + u.ap.pLast = &u.ap.pData0[u.ap.nField-1]; + u.ap.file_format = p->minWriteFileFormat; /* Identify the output register */ assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ - for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ - assert( memIsValid(u.ao.pRec) ); - if( u.ao.zAffinity ){ - applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding); - } - if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){ - sqlite3VdbeMemExpandBlob(u.ao.pRec); - } - u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format); - u.ao.len = sqlite3VdbeSerialTypeLen(u.ao.serial_type); - u.ao.nData += u.ao.len; - u.ao.nHdr += sqlite3VarintLen(u.ao.serial_type); - if( u.ao.pRec->flags & MEM_Zero ){ + for(u.ap.pRec=u.ap.pData0; u.ap.pRec<=u.ap.pLast; u.ap.pRec++){ + assert( memIsValid(u.ap.pRec) ); + if( u.ap.zAffinity ){ + applyAffinity(u.ap.pRec, u.ap.zAffinity[u.ap.pRec-u.ap.pData0], encoding); + } + if( u.ap.pRec->flags&MEM_Zero && u.ap.pRec->n>0 ){ + sqlite3VdbeMemExpandBlob(u.ap.pRec); + } + u.ap.serial_type = sqlite3VdbeSerialType(u.ap.pRec, u.ap.file_format); + u.ap.len = sqlite3VdbeSerialTypeLen(u.ap.serial_type); + u.ap.nData += u.ap.len; + u.ap.nHdr += sqlite3VarintLen(u.ap.serial_type); + if( u.ap.pRec->flags & MEM_Zero ){ /* Only pure zero-filled BLOBs can be input to this Opcode. ** We do not allow blobs with a prefix and a zero-filled tail. */ - u.ao.nZero += u.ao.pRec->u.nZero; - }else if( u.ao.len ){ - u.ao.nZero = 0; + u.ap.nZero += u.ap.pRec->u.nZero; + }else if( u.ap.len ){ + u.ap.nZero = 0; } } /* Add the initial header varint and total the size */ - u.ao.nHdr += u.ao.nVarint = sqlite3VarintLen(u.ao.nHdr); - if( u.ao.nVarintdb->aLimit[SQLITE_LIMIT_LENGTH] ){ + u.ap.nByte = u.ap.nHdr+u.ap.nData-u.ap.nZero; + if( u.ap.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ - if( sqlite3VdbeMemGrow(pOut, (int)u.ao.nByte, 0) ){ + if( sqlite3VdbeMemGrow(pOut, (int)u.ap.nByte, 0) ){ goto no_mem; } - u.ao.zNewRecord = (u8 *)pOut->z; + u.ap.zNewRecord = (u8 *)pOut->z; /* Write the record */ - u.ao.i = putVarint32(u.ao.zNewRecord, u.ao.nHdr); - for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ - u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format); - u.ao.i += putVarint32(&u.ao.zNewRecord[u.ao.i], u.ao.serial_type); /* serial type */ - } - for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ /* serial data */ - u.ao.i += sqlite3VdbeSerialPut(&u.ao.zNewRecord[u.ao.i], (int)(u.ao.nByte-u.ao.i), u.ao.pRec,u.ao.file_format); - } - assert( u.ao.i==u.ao.nByte ); + u.ap.i = putVarint32(u.ap.zNewRecord, u.ap.nHdr); + for(u.ap.pRec=u.ap.pData0; u.ap.pRec<=u.ap.pLast; u.ap.pRec++){ + u.ap.serial_type = sqlite3VdbeSerialType(u.ap.pRec, u.ap.file_format); + u.ap.i += putVarint32(&u.ap.zNewRecord[u.ap.i], u.ap.serial_type); /* serial type */ + } + for(u.ap.pRec=u.ap.pData0; u.ap.pRec<=u.ap.pLast; u.ap.pRec++){ /* serial data */ + u.ap.i += sqlite3VdbeSerialPut(&u.ap.zNewRecord[u.ap.i], (int)(u.ap.nByte-u.ap.i), u.ap.pRec,u.ap.file_format); + } + assert( u.ap.i==u.ap.nByte ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pOut->n = (int)u.ao.nByte; + pOut->n = (int)u.ap.nByte; pOut->flags = MEM_Blob | MEM_Dyn; pOut->xDel = 0; - if( u.ao.nZero ){ - pOut->u.nZero = u.ao.nZero; + if( u.ap.nZero ){ + pOut->u.nZero = u.ap.nZero; pOut->flags |= MEM_Zero; } pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */ REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); @@ -66108,22 +67335,22 @@ ** Store the number of entries (an integer value) in the table or index ** opened by cursor P1 in register P2 */ #ifndef SQLITE_OMIT_BTREECOUNT case OP_Count: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ap */ +#if 0 /* local variables moved into u.aq */ i64 nEntry; BtCursor *pCrsr; -#endif /* local variables moved into u.ap */ - - u.ap.pCrsr = p->apCsr[pOp->p1]->pCursor; - if( ALWAYS(u.ap.pCrsr) ){ - rc = sqlite3BtreeCount(u.ap.pCrsr, &u.ap.nEntry); - }else{ - u.ap.nEntry = 0; - } - pOut->u.i = u.ap.nEntry; +#endif /* local variables moved into u.aq */ + + u.aq.pCrsr = p->apCsr[pOp->p1]->pCursor; + if( ALWAYS(u.aq.pCrsr) ){ + rc = sqlite3BtreeCount(u.aq.pCrsr, &u.aq.nEntry); + }else{ + u.aq.nEntry = 0; + } + pOut->u.i = u.aq.nEntry; break; } #endif /* Opcode: Savepoint P1 * * P4 * @@ -66131,42 +67358,42 @@ ** Open, release or rollback the savepoint named by parameter P4, depending ** on the value of P1. To open a new savepoint, P1==0. To release (commit) an ** existing savepoint, P1==1, or to rollback an existing savepoint P1==2. */ case OP_Savepoint: { -#if 0 /* local variables moved into u.aq */ +#if 0 /* local variables moved into u.ar */ int p1; /* Value of P1 operand */ char *zName; /* Name of savepoint */ int nName; Savepoint *pNew; Savepoint *pSavepoint; Savepoint *pTmp; int iSavepoint; int ii; -#endif /* local variables moved into u.aq */ +#endif /* local variables moved into u.ar */ - u.aq.p1 = pOp->p1; - u.aq.zName = pOp->p4.z; + u.ar.p1 = pOp->p1; + u.ar.zName = pOp->p4.z; - /* Assert that the u.aq.p1 parameter is valid. Also that if there is no open + /* Assert that the u.ar.p1 parameter is valid. Also that if there is no open ** transaction, then there cannot be any savepoints. */ assert( db->pSavepoint==0 || db->autoCommit==0 ); - assert( u.aq.p1==SAVEPOINT_BEGIN||u.aq.p1==SAVEPOINT_RELEASE||u.aq.p1==SAVEPOINT_ROLLBACK ); + assert( u.ar.p1==SAVEPOINT_BEGIN||u.ar.p1==SAVEPOINT_RELEASE||u.ar.p1==SAVEPOINT_ROLLBACK ); assert( db->pSavepoint || db->isTransactionSavepoint==0 ); assert( checkSavepointCount(db) ); - if( u.aq.p1==SAVEPOINT_BEGIN ){ + if( u.ar.p1==SAVEPOINT_BEGIN ){ if( db->writeVdbeCnt>0 ){ /* A new savepoint cannot be created if there are active write ** statements (i.e. open read/write incremental blob handles). */ sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - " "SQL statements in progress"); rc = SQLITE_BUSY; }else{ - u.aq.nName = sqlite3Strlen30(u.aq.zName); + u.ar.nName = sqlite3Strlen30(u.ar.zName); #ifndef SQLITE_OMIT_VIRTUALTABLE /* This call is Ok even if this savepoint is actually a transaction ** savepoint (and therefore should not prompt xSavepoint()) callbacks. ** If this is a transaction savepoint being opened, it is guaranteed @@ -66176,14 +67403,14 @@ db->nStatement+db->nSavepoint); if( rc!=SQLITE_OK ) goto abort_due_to_error; #endif /* Create a new savepoint structure. */ - u.aq.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.aq.nName+1); - if( u.aq.pNew ){ - u.aq.pNew->zName = (char *)&u.aq.pNew[1]; - memcpy(u.aq.pNew->zName, u.aq.zName, u.aq.nName+1); + u.ar.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.ar.nName+1); + if( u.ar.pNew ){ + u.ar.pNew->zName = (char *)&u.ar.pNew[1]; + memcpy(u.ar.pNew->zName, u.ar.zName, u.ar.nName+1); /* If there is no open transaction, then mark this as a special ** "transaction savepoint". */ if( db->autoCommit ){ db->autoCommit = 0; @@ -66191,50 +67418,50 @@ }else{ db->nSavepoint++; } /* Link the new savepoint into the database handle's list. */ - u.aq.pNew->pNext = db->pSavepoint; - db->pSavepoint = u.aq.pNew; - u.aq.pNew->nDeferredCons = db->nDeferredCons; + u.ar.pNew->pNext = db->pSavepoint; + db->pSavepoint = u.ar.pNew; + u.ar.pNew->nDeferredCons = db->nDeferredCons; } } }else{ - u.aq.iSavepoint = 0; + u.ar.iSavepoint = 0; /* Find the named savepoint. If there is no such savepoint, then an ** an error is returned to the user. */ for( - u.aq.pSavepoint = db->pSavepoint; - u.aq.pSavepoint && sqlite3StrICmp(u.aq.pSavepoint->zName, u.aq.zName); - u.aq.pSavepoint = u.aq.pSavepoint->pNext + u.ar.pSavepoint = db->pSavepoint; + u.ar.pSavepoint && sqlite3StrICmp(u.ar.pSavepoint->zName, u.ar.zName); + u.ar.pSavepoint = u.ar.pSavepoint->pNext ){ - u.aq.iSavepoint++; + u.ar.iSavepoint++; } - if( !u.aq.pSavepoint ){ - sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.aq.zName); + if( !u.ar.pSavepoint ){ + sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.ar.zName); rc = SQLITE_ERROR; }else if( - db->writeVdbeCnt>0 || (u.aq.p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1) + db->writeVdbeCnt>0 || (u.ar.p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1) ){ /* It is not possible to release (commit) a savepoint if there are ** active write statements. It is not possible to rollback a savepoint ** if there are any active statements at all. */ sqlite3SetString(&p->zErrMsg, db, "cannot %s savepoint - SQL statements in progress", - (u.aq.p1==SAVEPOINT_ROLLBACK ? "rollback": "release") + (u.ar.p1==SAVEPOINT_ROLLBACK ? "rollback": "release") ); rc = SQLITE_BUSY; }else{ /* Determine whether or not this is a transaction savepoint. If so, ** and this is a RELEASE command, then the current transaction ** is committed. */ - int isTransaction = u.aq.pSavepoint->pNext==0 && db->isTransactionSavepoint; - if( isTransaction && u.aq.p1==SAVEPOINT_RELEASE ){ + int isTransaction = u.ar.pSavepoint->pNext==0 && db->isTransactionSavepoint; + if( isTransaction && u.ar.p1==SAVEPOINT_RELEASE ){ if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ @@ -66244,50 +67471,50 @@ goto vdbe_return; } db->isTransactionSavepoint = 0; rc = p->rc; }else{ - u.aq.iSavepoint = db->nSavepoint - u.aq.iSavepoint - 1; - for(u.aq.ii=0; u.aq.iinDb; u.aq.ii++){ - rc = sqlite3BtreeSavepoint(db->aDb[u.aq.ii].pBt, u.aq.p1, u.aq.iSavepoint); + u.ar.iSavepoint = db->nSavepoint - u.ar.iSavepoint - 1; + for(u.ar.ii=0; u.ar.iinDb; u.ar.ii++){ + rc = sqlite3BtreeSavepoint(db->aDb[u.ar.ii].pBt, u.ar.p1, u.ar.iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } - if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ + if( u.ar.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ - while( db->pSavepoint!=u.aq.pSavepoint ){ - u.aq.pTmp = db->pSavepoint; - db->pSavepoint = u.aq.pTmp->pNext; - sqlite3DbFree(db, u.aq.pTmp); + while( db->pSavepoint!=u.ar.pSavepoint ){ + u.ar.pTmp = db->pSavepoint; + db->pSavepoint = u.ar.pTmp->pNext; + sqlite3DbFree(db, u.ar.pTmp); db->nSavepoint--; } /* If it is a RELEASE, then destroy the savepoint being operated on ** too. If it is a ROLLBACK TO, then set the number of deferred ** constraint violations present in the database to the value stored ** when the savepoint was created. */ - if( u.aq.p1==SAVEPOINT_RELEASE ){ - assert( u.aq.pSavepoint==db->pSavepoint ); - db->pSavepoint = u.aq.pSavepoint->pNext; - sqlite3DbFree(db, u.aq.pSavepoint); + if( u.ar.p1==SAVEPOINT_RELEASE ){ + assert( u.ar.pSavepoint==db->pSavepoint ); + db->pSavepoint = u.ar.pSavepoint->pNext; + sqlite3DbFree(db, u.ar.pSavepoint); if( !isTransaction ){ db->nSavepoint--; } }else{ - db->nDeferredCons = u.aq.pSavepoint->nDeferredCons; + db->nDeferredCons = u.ar.pSavepoint->nDeferredCons; } if( !isTransaction ){ - rc = sqlite3VtabSavepoint(db, u.aq.p1, u.aq.iSavepoint); + rc = sqlite3VtabSavepoint(db, u.ar.p1, u.ar.iSavepoint); if( rc!=SQLITE_OK ) goto abort_due_to_error; } } } @@ -66302,50 +67529,50 @@ ** there are active writing VMs or active VMs that use shared cache. ** ** This instruction causes the VM to halt. */ case OP_AutoCommit: { -#if 0 /* local variables moved into u.ar */ +#if 0 /* local variables moved into u.as */ int desiredAutoCommit; int iRollback; int turnOnAC; -#endif /* local variables moved into u.ar */ +#endif /* local variables moved into u.as */ - u.ar.desiredAutoCommit = pOp->p1; - u.ar.iRollback = pOp->p2; - u.ar.turnOnAC = u.ar.desiredAutoCommit && !db->autoCommit; - assert( u.ar.desiredAutoCommit==1 || u.ar.desiredAutoCommit==0 ); - assert( u.ar.desiredAutoCommit==1 || u.ar.iRollback==0 ); + u.as.desiredAutoCommit = pOp->p1; + u.as.iRollback = pOp->p2; + u.as.turnOnAC = u.as.desiredAutoCommit && !db->autoCommit; + assert( u.as.desiredAutoCommit==1 || u.as.desiredAutoCommit==0 ); + assert( u.as.desiredAutoCommit==1 || u.as.iRollback==0 ); assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */ - if( u.ar.turnOnAC && u.ar.iRollback && db->activeVdbeCnt>1 ){ + if( u.as.turnOnAC && u.as.iRollback && db->activeVdbeCnt>1 ){ /* If this instruction implements a ROLLBACK and other VMs are ** still running, and a transaction is active, return an error indicating ** that the other VMs must complete first. */ sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; - }else if( u.ar.turnOnAC && !u.ar.iRollback && db->writeVdbeCnt>0 ){ + }else if( u.as.turnOnAC && !u.as.iRollback && db->writeVdbeCnt>0 ){ /* If this instruction implements a COMMIT and other VMs are writing ** return an error indicating that the other VMs must complete first. */ sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; - }else if( u.ar.desiredAutoCommit!=db->autoCommit ){ - if( u.ar.iRollback ){ - assert( u.ar.desiredAutoCommit==1 ); + }else if( u.as.desiredAutoCommit!=db->autoCommit ){ + if( u.as.iRollback ){ + assert( u.as.desiredAutoCommit==1 ); sqlite3RollbackAll(db); db->autoCommit = 1; }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; }else{ - db->autoCommit = (u8)u.ar.desiredAutoCommit; + db->autoCommit = (u8)u.as.desiredAutoCommit; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = pc; - db->autoCommit = (u8)(1-u.ar.desiredAutoCommit); + db->autoCommit = (u8)(1-u.as.desiredAutoCommit); p->rc = rc = SQLITE_BUSY; goto vdbe_return; } } assert( db->nStatement==0 ); @@ -66356,12 +67583,12 @@ rc = SQLITE_ERROR; } goto vdbe_return; }else{ sqlite3SetString(&p->zErrMsg, db, - (!u.ar.desiredAutoCommit)?"cannot start a transaction within a transaction":( - (u.ar.iRollback)?"cannot rollback - no transaction is active": + (!u.as.desiredAutoCommit)?"cannot start a transaction within a transaction":( + (u.as.iRollback)?"cannot rollback - no transaction is active": "cannot commit - no transaction is active")); rc = SQLITE_ERROR; } break; @@ -66397,20 +67624,20 @@ ** will automatically commit when the VDBE halts. ** ** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { -#if 0 /* local variables moved into u.as */ +#if 0 /* local variables moved into u.at */ Btree *pBt; -#endif /* local variables moved into u.as */ +#endif /* local variables moved into u.at */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.as.pBt = db->aDb[pOp->p1].pBt; + u.at.pBt = db->aDb[pOp->p1].pBt; - if( u.as.pBt ){ - rc = sqlite3BtreeBeginTrans(u.as.pBt, pOp->p2); + if( u.at.pBt ){ + rc = sqlite3BtreeBeginTrans(u.at.pBt, pOp->p2); if( rc==SQLITE_BUSY ){ p->pc = pc; p->rc = rc = SQLITE_BUSY; goto vdbe_return; } @@ -66419,20 +67646,20 @@ } if( pOp->p2 && p->usesStmtJournal && (db->autoCommit==0 || db->activeVdbeCnt>1) ){ - assert( sqlite3BtreeIsInTrans(u.as.pBt) ); + assert( sqlite3BtreeIsInTrans(u.at.pBt) ); if( p->iStatement==0 ){ assert( db->nStatement>=0 && db->nSavepoint>=0 ); db->nStatement++; p->iStatement = db->nSavepoint + db->nStatement; } rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement); + rc = sqlite3BtreeBeginStmt(u.at.pBt, p->iStatement); } /* Store the current value of the database handles deferred constraint ** counter. If the statement transaction needs to be rolled back, ** the value of this counter needs to be restored too. */ @@ -66453,25 +67680,25 @@ ** There must be a read-lock on the database (either a transaction ** must be started or there must be an open cursor) before ** executing this instruction. */ case OP_ReadCookie: { /* out2-prerelease */ -#if 0 /* local variables moved into u.at */ +#if 0 /* local variables moved into u.au */ int iMeta; int iDb; int iCookie; -#endif /* local variables moved into u.at */ +#endif /* local variables moved into u.au */ - u.at.iDb = pOp->p1; - u.at.iCookie = pOp->p3; + u.au.iDb = pOp->p1; + u.au.iCookie = pOp->p3; assert( pOp->p3=0 && u.at.iDbnDb ); - assert( db->aDb[u.at.iDb].pBt!=0 ); - assert( (p->btreeMask & (((yDbMask)1)<=0 && u.au.iDbnDb ); + assert( db->aDb[u.au.iDb].pBt!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<aDb[u.at.iDb].pBt, u.at.iCookie, (u32 *)&u.at.iMeta); - pOut->u.i = u.at.iMeta; + sqlite3BtreeGetMeta(db->aDb[u.au.iDb].pBt, u.au.iCookie, (u32 *)&u.au.iMeta); + pOut->u.i = u.au.iMeta; break; } /* Opcode: SetCookie P1 P2 P3 * * ** @@ -66482,30 +67709,30 @@ ** database file used to store temporary tables. ** ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { /* in3 */ -#if 0 /* local variables moved into u.au */ +#if 0 /* local variables moved into u.av */ Db *pDb; -#endif /* local variables moved into u.au */ +#endif /* local variables moved into u.av */ assert( pOp->p2p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.au.pDb = &db->aDb[pOp->p1]; - assert( u.au.pDb->pBt!=0 ); + u.av.pDb = &db->aDb[pOp->p1]; + assert( u.av.pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ - rc = sqlite3BtreeUpdateMeta(u.au.pDb->pBt, pOp->p2, (int)pIn3->u.i); + rc = sqlite3BtreeUpdateMeta(u.av.pDb->pBt, pOp->p2, (int)pIn3->u.i); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ - u.au.pDb->pSchema->schema_cookie = (int)pIn3->u.i; + u.av.pDb->pSchema->schema_cookie = (int)pIn3->u.i; db->flags |= SQLITE_InternChanges; }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ - u.au.pDb->pSchema->file_format = (u8)pIn3->u.i; + u.av.pDb->pSchema->file_format = (u8)pIn3->u.i; } if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ sqlite3ExpirePreparedStatements(db); @@ -66531,27 +67758,27 @@ ** Either a transaction needs to have been started or an OP_Open needs ** to be executed (to establish a read lock) before this opcode is ** invoked. */ case OP_VerifyCookie: { -#if 0 /* local variables moved into u.av */ +#if 0 /* local variables moved into u.aw */ int iMeta; int iGen; Btree *pBt; -#endif /* local variables moved into u.av */ +#endif /* local variables moved into u.aw */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); - u.av.pBt = db->aDb[pOp->p1].pBt; - if( u.av.pBt ){ - sqlite3BtreeGetMeta(u.av.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.av.iMeta); - u.av.iGen = db->aDb[pOp->p1].pSchema->iGeneration; + u.aw.pBt = db->aDb[pOp->p1].pBt; + if( u.aw.pBt ){ + sqlite3BtreeGetMeta(u.aw.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.aw.iMeta); + u.aw.iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ - u.av.iGen = u.av.iMeta = 0; + u.aw.iGen = u.aw.iMeta = 0; } - if( u.av.iMeta!=pOp->p2 || u.av.iGen!=pOp->p3 ){ + if( u.aw.iMeta!=pOp->p2 || u.aw.iGen!=pOp->p3 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie ** stored with the in-memory representation of the schema, do ** not reload the schema from the database file. @@ -66563,11 +67790,11 @@ ** discard the database schema, as the user code implementing the ** v-table would have to be ready for the sqlite3_vtab structure itself ** to be invalidated whenever sqlite3_step() is called from within ** a v-table method. */ - if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.av.iMeta ){ + if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.aw.iMeta ){ sqlite3ResetInternalSchema(db, pOp->p1); } p->expired = 1; rc = SQLITE_SCHEMA; @@ -66624,86 +67851,86 @@ ** ** See also OpenRead. */ case OP_OpenRead: case OP_OpenWrite: { -#if 0 /* local variables moved into u.aw */ +#if 0 /* local variables moved into u.ax */ int nField; KeyInfo *pKeyInfo; int p2; int iDb; int wrFlag; Btree *pX; VdbeCursor *pCur; Db *pDb; -#endif /* local variables moved into u.aw */ +#endif /* local variables moved into u.ax */ if( p->expired ){ rc = SQLITE_ABORT; break; } - u.aw.nField = 0; - u.aw.pKeyInfo = 0; - u.aw.p2 = pOp->p2; - u.aw.iDb = pOp->p3; - assert( u.aw.iDb>=0 && u.aw.iDbnDb ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.aw.iDb]; - u.aw.pX = u.aw.pDb->pBt; - assert( u.aw.pX!=0 ); + u.ax.nField = 0; + u.ax.pKeyInfo = 0; + u.ax.p2 = pOp->p2; + u.ax.iDb = pOp->p3; + assert( u.ax.iDb>=0 && u.ax.iDbnDb ); + assert( (p->btreeMask & (((yDbMask)1)<aDb[u.ax.iDb]; + u.ax.pX = u.ax.pDb->pBt; + assert( u.ax.pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ - u.aw.wrFlag = 1; - assert( sqlite3SchemaMutexHeld(db, u.aw.iDb, 0) ); - if( u.aw.pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = u.aw.pDb->pSchema->file_format; + u.ax.wrFlag = 1; + assert( sqlite3SchemaMutexHeld(db, u.ax.iDb, 0) ); + if( u.ax.pDb->pSchema->file_format < p->minWriteFileFormat ){ + p->minWriteFileFormat = u.ax.pDb->pSchema->file_format; } }else{ - u.aw.wrFlag = 0; + u.ax.wrFlag = 0; } if( pOp->p5 ){ - assert( u.aw.p2>0 ); - assert( u.aw.p2<=p->nMem ); - pIn2 = &aMem[u.aw.p2]; + assert( u.ax.p2>0 ); + assert( u.ax.p2<=p->nMem ); + pIn2 = &aMem[u.ax.p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); - u.aw.p2 = (int)pIn2->u.i; - /* The u.aw.p2 value always comes from a prior OP_CreateTable opcode and - ** that opcode will always set the u.aw.p2 value to 2 or more or else fail. + u.ax.p2 = (int)pIn2->u.i; + /* The u.ax.p2 value always comes from a prior OP_CreateTable opcode and + ** that opcode will always set the u.ax.p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ - if( NEVER(u.aw.p2<2) ) { + if( NEVER(u.ax.p2<2) ) { rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } } if( pOp->p4type==P4_KEYINFO ){ - u.aw.pKeyInfo = pOp->p4.pKeyInfo; - u.aw.pKeyInfo->enc = ENC(p->db); - u.aw.nField = u.aw.pKeyInfo->nField+1; + u.ax.pKeyInfo = pOp->p4.pKeyInfo; + u.ax.pKeyInfo->enc = ENC(p->db); + u.ax.nField = u.ax.pKeyInfo->nField+1; }else if( pOp->p4type==P4_INT32 ){ - u.aw.nField = pOp->p4.i; + u.ax.nField = pOp->p4.i; } assert( pOp->p1>=0 ); - u.aw.pCur = allocateCursor(p, pOp->p1, u.aw.nField, u.aw.iDb, 1); - if( u.aw.pCur==0 ) goto no_mem; - u.aw.pCur->nullRow = 1; - u.aw.pCur->isOrdered = 1; - rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor); - u.aw.pCur->pKeyInfo = u.aw.pKeyInfo; + u.ax.pCur = allocateCursor(p, pOp->p1, u.ax.nField, u.ax.iDb, 1); + if( u.ax.pCur==0 ) goto no_mem; + u.ax.pCur->nullRow = 1; + u.ax.pCur->isOrdered = 1; + rc = sqlite3BtreeCursor(u.ax.pX, u.ax.p2, u.ax.wrFlag, u.ax.pKeyInfo, u.ax.pCur->pCursor); + u.ax.pCur->pKeyInfo = u.ax.pKeyInfo; /* Since it performs no memory allocation or IO, the only value that ** sqlite3BtreeCursor() may return is SQLITE_OK. */ assert( rc==SQLITE_OK ); /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ - u.aw.pCur->isTable = pOp->p4type!=P4_KEYINFO; - u.aw.pCur->isIndex = !u.aw.pCur->isTable; + u.ax.pCur->isTable = pOp->p4type!=P4_KEYINFO; + u.ax.pCur->isIndex = !u.ax.pCur->isTable; break; } /* Opcode: OpenEphemeral P1 P2 * P4 P5 ** @@ -66735,28 +67962,28 @@ ** by this opcode will be used for automatically created transient ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { -#if 0 /* local variables moved into u.ax */ +#if 0 /* local variables moved into u.ay */ VdbeCursor *pCx; -#endif /* local variables moved into u.ax */ +#endif /* local variables moved into u.ay */ static const int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); - u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.ax.pCx==0 ) goto no_mem; - u.ax.pCx->nullRow = 1; - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.ax.pCx->pBt, + u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + if( u.ay.pCx==0 ) goto no_mem; + u.ay.pCx->nullRow = 1; + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.ay.pCx->pBt, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1); + rc = sqlite3BtreeBeginTrans(u.ay.pCx->pBt, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the @@ -66763,26 +67990,26 @@ ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( pOp->p4.pKeyInfo ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(u.ax.pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); + rc = sqlite3BtreeCreateTable(u.ay.pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); - rc = sqlite3BtreeCursor(u.ax.pCx->pBt, pgno, 1, - (KeyInfo*)pOp->p4.z, u.ax.pCx->pCursor); - u.ax.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.ax.pCx->pKeyInfo->enc = ENC(p->db); - } - u.ax.pCx->isTable = 0; - }else{ - rc = sqlite3BtreeCursor(u.ax.pCx->pBt, MASTER_ROOT, 1, 0, u.ax.pCx->pCursor); - u.ax.pCx->isTable = 1; + rc = sqlite3BtreeCursor(u.ay.pCx->pBt, pgno, 1, + (KeyInfo*)pOp->p4.z, u.ay.pCx->pCursor); + u.ay.pCx->pKeyInfo = pOp->p4.pKeyInfo; + u.ay.pCx->pKeyInfo->enc = ENC(p->db); + } + u.ay.pCx->isTable = 0; + }else{ + rc = sqlite3BtreeCursor(u.ay.pCx->pBt, MASTER_ROOT, 1, 0, u.ay.pCx->pCursor); + u.ay.pCx->isTable = 1; } } - u.ax.pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); - u.ax.pCx->isIndex = !u.ax.pCx->isTable; + u.ay.pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); + u.ay.pCx->isIndex = !u.ay.pCx->isTable; break; } /* Opcode: OpenSorter P1 P2 * P4 * ** @@ -66789,20 +68016,20 @@ ** This opcode works like OP_OpenEphemeral except that it opens ** a transient index that is specifically designed to sort large ** tables using an external merge-sort algorithm. */ case OP_SorterOpen: { -#if 0 /* local variables moved into u.ay */ +#if 0 /* local variables moved into u.az */ VdbeCursor *pCx; -#endif /* local variables moved into u.ay */ +#endif /* local variables moved into u.az */ #ifndef SQLITE_OMIT_MERGE_SORT - u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.ay.pCx==0 ) goto no_mem; - u.ay.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.ay.pCx->pKeyInfo->enc = ENC(p->db); - u.ay.pCx->isSorter = 1; - rc = sqlite3VdbeSorterInit(db, u.ay.pCx); + u.az.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + if( u.az.pCx==0 ) goto no_mem; + u.az.pCx->pKeyInfo = pOp->p4.pKeyInfo; + u.az.pCx->pKeyInfo->enc = ENC(p->db); + u.az.pCx->isSorter = 1; + rc = sqlite3VdbeSorterInit(db, u.az.pCx); #else pOp->opcode = OP_OpenEphemeral; pc--; #endif break; @@ -66822,21 +68049,21 @@ ** ** P3 is the number of fields in the records that will be stored by ** the pseudo-table. */ case OP_OpenPseudo: { -#if 0 /* local variables moved into u.az */ +#if 0 /* local variables moved into u.ba */ VdbeCursor *pCx; -#endif /* local variables moved into u.az */ +#endif /* local variables moved into u.ba */ assert( pOp->p1>=0 ); - u.az.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); - if( u.az.pCx==0 ) goto no_mem; - u.az.pCx->nullRow = 1; - u.az.pCx->pseudoTableReg = pOp->p2; - u.az.pCx->isTable = 1; - u.az.pCx->isIndex = 0; + u.ba.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); + if( u.ba.pCx==0 ) goto no_mem; + u.ba.pCx->nullRow = 1; + u.ba.pCx->pseudoTableReg = pOp->p2; + u.ba.pCx->isTable = 1; + u.ba.pCx->isIndex = 0; break; } /* Opcode: Close P1 * * * * ** @@ -66904,39 +68131,39 @@ */ case OP_SeekLt: /* jump, in3 */ case OP_SeekLe: /* jump, in3 */ case OP_SeekGe: /* jump, in3 */ case OP_SeekGt: { /* jump, in3 */ -#if 0 /* local variables moved into u.ba */ +#if 0 /* local variables moved into u.bb */ int res; int oc; VdbeCursor *pC; UnpackedRecord r; int nField; i64 iKey; /* The rowid we are to seek to */ -#endif /* local variables moved into u.ba */ +#endif /* local variables moved into u.bb */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p2!=0 ); - u.ba.pC = p->apCsr[pOp->p1]; - assert( u.ba.pC!=0 ); - assert( u.ba.pC->pseudoTableReg==0 ); + u.bb.pC = p->apCsr[pOp->p1]; + assert( u.bb.pC!=0 ); + assert( u.bb.pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); - assert( u.ba.pC->isOrdered ); - if( ALWAYS(u.ba.pC->pCursor!=0) ){ - u.ba.oc = pOp->opcode; - u.ba.pC->nullRow = 0; - if( u.ba.pC->isTable ){ + assert( u.bb.pC->isOrdered ); + if( ALWAYS(u.bb.pC->pCursor!=0) ){ + u.bb.oc = pOp->opcode; + u.bb.pC->nullRow = 0; + if( u.bb.pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so covert it. */ pIn3 = &aMem[pOp->p3]; applyNumericAffinity(pIn3); - u.ba.iKey = sqlite3VdbeIntValue(pIn3); - u.ba.pC->rowidIsValid = 0; + u.bb.iKey = sqlite3VdbeIntValue(pIn3); + u.bb.pC->rowidIsValid = 0; /* 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_Real)==0 ){ @@ -66947,105 +68174,105 @@ } /* If we reach this point, then the P3 value must be a floating ** point number. */ assert( (pIn3->flags & MEM_Real)!=0 ); - if( u.ba.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.ba.iKey || pIn3->r>0) ){ - /* The P3 value is too large in magnitude to be expressed as an - ** integer. */ - u.ba.res = 1; - if( pIn3->r<0 ){ - if( u.ba.oc>=OP_SeekGe ){ assert( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt ); - rc = sqlite3BtreeFirst(u.ba.pC->pCursor, &u.ba.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - }else{ - if( u.ba.oc<=OP_SeekLe ){ assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe ); - rc = sqlite3BtreeLast(u.ba.pC->pCursor, &u.ba.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - } - if( u.ba.res ){ - pc = pOp->p2 - 1; - } - break; - }else if( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekGe ){ - /* Use the ceiling() function to convert real->int */ - if( pIn3->r > (double)u.ba.iKey ) u.ba.iKey++; - }else{ - /* Use the floor() function to convert real->int */ - assert( u.ba.oc==OP_SeekLe || u.ba.oc==OP_SeekGt ); - if( pIn3->r < (double)u.ba.iKey ) u.ba.iKey--; - } - } - rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, 0, (u64)u.ba.iKey, 0, &u.ba.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( u.ba.res==0 ){ - u.ba.pC->rowidIsValid = 1; - u.ba.pC->lastRowid = u.ba.iKey; - } - }else{ - u.ba.nField = pOp->p4.i; - assert( pOp->p4type==P4_INT32 ); - assert( u.ba.nField>0 ); - u.ba.r.pKeyInfo = u.ba.pC->pKeyInfo; - u.ba.r.nField = (u16)u.ba.nField; - - /* The next line of code computes as follows, only faster: - ** if( u.ba.oc==OP_SeekGt || u.ba.oc==OP_SeekLe ){ - ** u.ba.r.flags = UNPACKED_INCRKEY; - ** }else{ - ** u.ba.r.flags = 0; - ** } - */ - u.ba.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.ba.oc - OP_SeekLt))); - assert( u.ba.oc!=OP_SeekGt || u.ba.r.flags==UNPACKED_INCRKEY ); - assert( u.ba.oc!=OP_SeekLe || u.ba.r.flags==UNPACKED_INCRKEY ); - assert( u.ba.oc!=OP_SeekGe || u.ba.r.flags==0 ); - assert( u.ba.oc!=OP_SeekLt || u.ba.r.flags==0 ); - - u.ba.r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ipCursor, &u.ba.r, 0, 0, &u.ba.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - u.ba.pC->rowidIsValid = 0; - } - u.ba.pC->deferredMoveto = 0; - u.ba.pC->cacheStatus = CACHE_STALE; -#ifdef SQLITE_TEST - sqlite3_search_count++; -#endif - if( u.ba.oc>=OP_SeekGe ){ assert( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt ); - if( u.ba.res<0 || (u.ba.res==0 && u.ba.oc==OP_SeekGt) ){ - rc = sqlite3BtreeNext(u.ba.pC->pCursor, &u.ba.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.ba.pC->rowidIsValid = 0; - }else{ - u.ba.res = 0; - } - }else{ - assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe ); - if( u.ba.res>0 || (u.ba.res==0 && u.ba.oc==OP_SeekLt) ){ - rc = sqlite3BtreePrevious(u.ba.pC->pCursor, &u.ba.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.ba.pC->rowidIsValid = 0; - }else{ - /* u.ba.res might be negative because the table is empty. Check to - ** see if this is the case. - */ - u.ba.res = sqlite3BtreeEof(u.ba.pC->pCursor); - } - } - assert( pOp->p2>0 ); - if( u.ba.res ){ + if( u.bb.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.bb.iKey || pIn3->r>0) ){ + /* The P3 value is too large in magnitude to be expressed as an + ** integer. */ + u.bb.res = 1; + if( pIn3->r<0 ){ + if( u.bb.oc>=OP_SeekGe ){ assert( u.bb.oc==OP_SeekGe || u.bb.oc==OP_SeekGt ); + rc = sqlite3BtreeFirst(u.bb.pC->pCursor, &u.bb.res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + }else{ + if( u.bb.oc<=OP_SeekLe ){ assert( u.bb.oc==OP_SeekLt || u.bb.oc==OP_SeekLe ); + rc = sqlite3BtreeLast(u.bb.pC->pCursor, &u.bb.res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + } + if( u.bb.res ){ + pc = pOp->p2 - 1; + } + break; + }else if( u.bb.oc==OP_SeekLt || u.bb.oc==OP_SeekGe ){ + /* Use the ceiling() function to convert real->int */ + if( pIn3->r > (double)u.bb.iKey ) u.bb.iKey++; + }else{ + /* Use the floor() function to convert real->int */ + assert( u.bb.oc==OP_SeekLe || u.bb.oc==OP_SeekGt ); + if( pIn3->r < (double)u.bb.iKey ) u.bb.iKey--; + } + } + rc = sqlite3BtreeMovetoUnpacked(u.bb.pC->pCursor, 0, (u64)u.bb.iKey, 0, &u.bb.res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + if( u.bb.res==0 ){ + u.bb.pC->rowidIsValid = 1; + u.bb.pC->lastRowid = u.bb.iKey; + } + }else{ + u.bb.nField = pOp->p4.i; + assert( pOp->p4type==P4_INT32 ); + assert( u.bb.nField>0 ); + u.bb.r.pKeyInfo = u.bb.pC->pKeyInfo; + u.bb.r.nField = (u16)u.bb.nField; + + /* The next line of code computes as follows, only faster: + ** if( u.bb.oc==OP_SeekGt || u.bb.oc==OP_SeekLe ){ + ** u.bb.r.flags = UNPACKED_INCRKEY; + ** }else{ + ** u.bb.r.flags = 0; + ** } + */ + u.bb.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.bb.oc - OP_SeekLt))); + assert( u.bb.oc!=OP_SeekGt || u.bb.r.flags==UNPACKED_INCRKEY ); + assert( u.bb.oc!=OP_SeekLe || u.bb.r.flags==UNPACKED_INCRKEY ); + assert( u.bb.oc!=OP_SeekGe || u.bb.r.flags==0 ); + assert( u.bb.oc!=OP_SeekLt || u.bb.r.flags==0 ); + + u.bb.r.aMem = &aMem[pOp->p3]; +#ifdef SQLITE_DEBUG + { int i; for(i=0; ipCursor, &u.bb.r, 0, 0, &u.bb.res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + u.bb.pC->rowidIsValid = 0; + } + u.bb.pC->deferredMoveto = 0; + u.bb.pC->cacheStatus = CACHE_STALE; +#ifdef SQLITE_TEST + sqlite3_search_count++; +#endif + if( u.bb.oc>=OP_SeekGe ){ assert( u.bb.oc==OP_SeekGe || u.bb.oc==OP_SeekGt ); + if( u.bb.res<0 || (u.bb.res==0 && u.bb.oc==OP_SeekGt) ){ + rc = sqlite3BtreeNext(u.bb.pC->pCursor, &u.bb.res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + u.bb.pC->rowidIsValid = 0; + }else{ + u.bb.res = 0; + } + }else{ + assert( u.bb.oc==OP_SeekLt || u.bb.oc==OP_SeekLe ); + if( u.bb.res>0 || (u.bb.res==0 && u.bb.oc==OP_SeekLt) ){ + rc = sqlite3BtreePrevious(u.bb.pC->pCursor, &u.bb.res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + u.bb.pC->rowidIsValid = 0; + }else{ + /* u.bb.res might be negative because the table is empty. Check to + ** see if this is the case. + */ + u.bb.res = sqlite3BtreeEof(u.bb.pC->pCursor); + } + } + assert( pOp->p2>0 ); + if( u.bb.res ){ pc = pOp->p2 - 1; } }else{ /* This happens when attempting to open the sqlite3_master table ** for read access returns SQLITE_EMPTY. In this case always @@ -67064,24 +68291,24 @@ ** This is actually a deferred seek. Nothing actually happens until ** the cursor is used to read a record. That way, if no reads ** occur, no unnecessary I/O happens. */ case OP_Seek: { /* in2 */ -#if 0 /* local variables moved into u.bb */ +#if 0 /* local variables moved into u.bc */ VdbeCursor *pC; -#endif /* local variables moved into u.bb */ +#endif /* local variables moved into u.bc */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bb.pC = p->apCsr[pOp->p1]; - assert( u.bb.pC!=0 ); - if( ALWAYS(u.bb.pC->pCursor!=0) ){ - assert( u.bb.pC->isTable ); - u.bb.pC->nullRow = 0; + u.bc.pC = p->apCsr[pOp->p1]; + assert( u.bc.pC!=0 ); + if( ALWAYS(u.bc.pC->pCursor!=0) ){ + assert( u.bc.pC->isTable ); + u.bc.pC->nullRow = 0; pIn2 = &aMem[pOp->p2]; - u.bb.pC->movetoTarget = sqlite3VdbeIntValue(pIn2); - u.bb.pC->rowidIsValid = 0; - u.bb.pC->deferredMoveto = 1; + u.bc.pC->movetoTarget = sqlite3VdbeIntValue(pIn2); + u.bc.pC->rowidIsValid = 0; + u.bc.pC->deferredMoveto = 1; } break; } @@ -67109,67 +68336,67 @@ ** ** See also: Found, NotExists, IsUnique */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ -#if 0 /* local variables moved into u.bc */ +#if 0 /* local variables moved into u.bd */ int alreadyExists; VdbeCursor *pC; int res; char *pFree; UnpackedRecord *pIdxKey; UnpackedRecord r; char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; -#endif /* local variables moved into u.bc */ +#endif /* local variables moved into u.bd */ #ifdef SQLITE_TEST sqlite3_found_count++; #endif - u.bc.alreadyExists = 0; + u.bd.alreadyExists = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p4type==P4_INT32 ); - u.bc.pC = p->apCsr[pOp->p1]; - assert( u.bc.pC!=0 ); + u.bd.pC = p->apCsr[pOp->p1]; + assert( u.bd.pC!=0 ); pIn3 = &aMem[pOp->p3]; - if( ALWAYS(u.bc.pC->pCursor!=0) ){ + if( ALWAYS(u.bd.pC->pCursor!=0) ){ - assert( u.bc.pC->isTable==0 ); + assert( u.bd.pC->isTable==0 ); if( pOp->p4.i>0 ){ - u.bc.r.pKeyInfo = u.bc.pC->pKeyInfo; - u.bc.r.nField = (u16)pOp->p4.i; - u.bc.r.aMem = pIn3; + u.bd.r.pKeyInfo = u.bd.pC->pKeyInfo; + u.bd.r.nField = (u16)pOp->p4.i; + u.bd.r.aMem = pIn3; #ifdef SQLITE_DEBUG - { int i; for(i=0; ipKeyInfo, u.bc.aTempRec, sizeof(u.bc.aTempRec), &u.bc.pFree + u.bd.pIdxKey = sqlite3VdbeAllocUnpackedRecord( + u.bd.pC->pKeyInfo, u.bd.aTempRec, sizeof(u.bd.aTempRec), &u.bd.pFree ); - if( u.bc.pIdxKey==0 ) goto no_mem; + if( u.bd.pIdxKey==0 ) goto no_mem; assert( pIn3->flags & MEM_Blob ); assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ - sqlite3VdbeRecordUnpack(u.bc.pC->pKeyInfo, pIn3->n, pIn3->z, u.bc.pIdxKey); - u.bc.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; + sqlite3VdbeRecordUnpack(u.bd.pC->pKeyInfo, pIn3->n, pIn3->z, u.bd.pIdxKey); + u.bd.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; } - rc = sqlite3BtreeMovetoUnpacked(u.bc.pC->pCursor, u.bc.pIdxKey, 0, 0, &u.bc.res); + rc = sqlite3BtreeMovetoUnpacked(u.bd.pC->pCursor, u.bd.pIdxKey, 0, 0, &u.bd.res); if( pOp->p4.i==0 ){ - sqlite3DbFree(db, u.bc.pFree); + sqlite3DbFree(db, u.bd.pFree); } if( rc!=SQLITE_OK ){ break; } - u.bc.alreadyExists = (u.bc.res==0); - u.bc.pC->deferredMoveto = 0; - u.bc.pC->cacheStatus = CACHE_STALE; + u.bd.alreadyExists = (u.bd.res==0); + u.bd.pC->deferredMoveto = 0; + u.bd.pC->cacheStatus = CACHE_STALE; } if( pOp->opcode==OP_Found ){ - if( u.bc.alreadyExists ) pc = pOp->p2 - 1; + if( u.bd.alreadyExists ) pc = pOp->p2 - 1; }else{ - if( !u.bc.alreadyExists ) pc = pOp->p2 - 1; + if( !u.bd.alreadyExists ) pc = pOp->p2 - 1; } break; } /* Opcode: IsUnique P1 P2 P3 P4 * @@ -67197,67 +68424,67 @@ ** instruction. ** ** See also: NotFound, NotExists, Found */ case OP_IsUnique: { /* jump, in3 */ -#if 0 /* local variables moved into u.bd */ +#if 0 /* local variables moved into u.be */ u16 ii; VdbeCursor *pCx; BtCursor *pCrsr; u16 nField; Mem *aMx; UnpackedRecord r; /* B-Tree index search key */ i64 R; /* Rowid stored in register P3 */ -#endif /* local variables moved into u.bd */ +#endif /* local variables moved into u.be */ pIn3 = &aMem[pOp->p3]; - u.bd.aMx = &aMem[pOp->p4.i]; + u.be.aMx = &aMem[pOp->p4.i]; /* Assert that the values of parameters P1 and P4 are in range. */ assert( pOp->p4type==P4_INT32 ); assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); assert( pOp->p1>=0 && pOp->p1nCursor ); /* Find the index cursor. */ - u.bd.pCx = p->apCsr[pOp->p1]; - assert( u.bd.pCx->deferredMoveto==0 ); - u.bd.pCx->seekResult = 0; - u.bd.pCx->cacheStatus = CACHE_STALE; - u.bd.pCrsr = u.bd.pCx->pCursor; + u.be.pCx = p->apCsr[pOp->p1]; + assert( u.be.pCx->deferredMoveto==0 ); + u.be.pCx->seekResult = 0; + u.be.pCx->cacheStatus = CACHE_STALE; + u.be.pCrsr = u.be.pCx->pCursor; /* If any of the values are NULL, take the jump. */ - u.bd.nField = u.bd.pCx->pKeyInfo->nField; - for(u.bd.ii=0; u.bd.iipKeyInfo->nField; + for(u.be.ii=0; u.be.iip2 - 1; - u.bd.pCrsr = 0; + u.be.pCrsr = 0; break; } } - assert( (u.bd.aMx[u.bd.nField].flags & MEM_Null)==0 ); + assert( (u.be.aMx[u.be.nField].flags & MEM_Null)==0 ); - if( u.bd.pCrsr!=0 ){ + if( u.be.pCrsr!=0 ){ /* Populate the index search key. */ - u.bd.r.pKeyInfo = u.bd.pCx->pKeyInfo; - u.bd.r.nField = u.bd.nField + 1; - u.bd.r.flags = UNPACKED_PREFIX_SEARCH; - u.bd.r.aMem = u.bd.aMx; + u.be.r.pKeyInfo = u.be.pCx->pKeyInfo; + u.be.r.nField = u.be.nField + 1; + u.be.r.flags = UNPACKED_PREFIX_SEARCH; + u.be.r.aMem = u.be.aMx; #ifdef SQLITE_DEBUG - { int i; for(i=0; iu.i; + u.be.R = pIn3->u.i; /* Search the B-Tree index. If no conflicting record is found, jump ** to P2. Otherwise, copy the rowid of the conflicting record to ** register P3 and fall through to the next instruction. */ - rc = sqlite3BtreeMovetoUnpacked(u.bd.pCrsr, &u.bd.r, 0, 0, &u.bd.pCx->seekResult); - if( (u.bd.r.flags & UNPACKED_PREFIX_SEARCH) || u.bd.r.rowid==u.bd.R ){ + rc = sqlite3BtreeMovetoUnpacked(u.be.pCrsr, &u.be.r, 0, 0, &u.be.pCx->seekResult); + if( (u.be.r.flags & UNPACKED_PREFIX_SEARCH) || u.be.r.rowid==u.be.R ){ pc = pOp->p2 - 1; }else{ - pIn3->u.i = u.bd.r.rowid; + pIn3->u.i = u.be.r.rowid; } } break; } @@ -67274,46 +68501,46 @@ ** P1 is an index. ** ** See also: Found, NotFound, IsUnique */ case OP_NotExists: { /* jump, in3 */ -#if 0 /* local variables moved into u.be */ +#if 0 /* local variables moved into u.bf */ VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; -#endif /* local variables moved into u.be */ +#endif /* local variables moved into u.bf */ pIn3 = &aMem[pOp->p3]; assert( pIn3->flags & MEM_Int ); assert( pOp->p1>=0 && pOp->p1nCursor ); - u.be.pC = p->apCsr[pOp->p1]; - assert( u.be.pC!=0 ); - assert( u.be.pC->isTable ); - assert( u.be.pC->pseudoTableReg==0 ); - u.be.pCrsr = u.be.pC->pCursor; - if( ALWAYS(u.be.pCrsr!=0) ){ - u.be.res = 0; - u.be.iKey = pIn3->u.i; - rc = sqlite3BtreeMovetoUnpacked(u.be.pCrsr, 0, u.be.iKey, 0, &u.be.res); - u.be.pC->lastRowid = pIn3->u.i; - u.be.pC->rowidIsValid = u.be.res==0 ?1:0; - u.be.pC->nullRow = 0; - u.be.pC->cacheStatus = CACHE_STALE; - u.be.pC->deferredMoveto = 0; - if( u.be.res!=0 ){ + u.bf.pC = p->apCsr[pOp->p1]; + assert( u.bf.pC!=0 ); + assert( u.bf.pC->isTable ); + assert( u.bf.pC->pseudoTableReg==0 ); + u.bf.pCrsr = u.bf.pC->pCursor; + if( ALWAYS(u.bf.pCrsr!=0) ){ + u.bf.res = 0; + u.bf.iKey = pIn3->u.i; + rc = sqlite3BtreeMovetoUnpacked(u.bf.pCrsr, 0, u.bf.iKey, 0, &u.bf.res); + u.bf.pC->lastRowid = pIn3->u.i; + u.bf.pC->rowidIsValid = u.bf.res==0 ?1:0; + u.bf.pC->nullRow = 0; + u.bf.pC->cacheStatus = CACHE_STALE; + u.bf.pC->deferredMoveto = 0; + if( u.bf.res!=0 ){ pc = pOp->p2 - 1; - assert( u.be.pC->rowidIsValid==0 ); + assert( u.bf.pC->rowidIsValid==0 ); } - u.be.pC->seekResult = u.be.res; + u.bf.pC->seekResult = u.bf.res; }else{ /* This happens when an attempt to open a read cursor on the ** sqlite_master table returns SQLITE_EMPTY. */ pc = pOp->p2 - 1; - assert( u.be.pC->rowidIsValid==0 ); - u.be.pC->seekResult = 0; + assert( u.bf.pC->rowidIsValid==0 ); + u.bf.pC->seekResult = 0; } break; } /* Opcode: Sequence P1 P2 * * * @@ -67344,25 +68571,25 @@ ** an SQLITE_FULL error is generated. The P3 register is updated with the ' ** generated record number. This P3 mechanism is used to help implement the ** AUTOINCREMENT feature. */ case OP_NewRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bf */ +#if 0 /* local variables moved into u.bg */ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ int cnt; /* Counter to limit the number of searches */ Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ VdbeFrame *pFrame; /* Root frame of VDBE */ -#endif /* local variables moved into u.bf */ +#endif /* local variables moved into u.bg */ - u.bf.v = 0; - u.bf.res = 0; + u.bg.v = 0; + u.bg.res = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bf.pC = p->apCsr[pOp->p1]; - assert( u.bf.pC!=0 ); - if( NEVER(u.bf.pC->pCursor==0) ){ + u.bg.pC = p->apCsr[pOp->p1]; + assert( u.bg.pC!=0 ); + if( NEVER(u.bg.pC->pCursor==0) ){ /* The zero initialization above is all that is needed */ }else{ /* The next rowid or record number (different terms for the same ** thing) is obtained in a two-step algorithm. ** @@ -67374,11 +68601,11 @@ ** The second algorithm is to select a rowid at random and see if ** it already exists in the table. If it does not exist, we have ** succeeded. If the random rowid does exist, we select a new one ** and try again, up to 100 times. */ - assert( u.bf.pC->isTable ); + assert( u.bg.pC->isTable ); #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff #else /* Some compilers complain about constants of the form 0x7fffffffffffffff. @@ -67386,101 +68613,101 @@ ** to provide the constant while making all compilers happy. */ # define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) #endif - if( !u.bf.pC->useRandomRowid ){ - u.bf.v = sqlite3BtreeGetCachedRowid(u.bf.pC->pCursor); - if( u.bf.v==0 ){ - rc = sqlite3BtreeLast(u.bf.pC->pCursor, &u.bf.res); + if( !u.bg.pC->useRandomRowid ){ + u.bg.v = sqlite3BtreeGetCachedRowid(u.bg.pC->pCursor); + if( u.bg.v==0 ){ + rc = sqlite3BtreeLast(u.bg.pC->pCursor, &u.bg.res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( u.bf.res ){ - u.bf.v = 1; /* IMP: R-61914-48074 */ + if( u.bg.res ){ + u.bg.v = 1; /* IMP: R-61914-48074 */ }else{ - assert( sqlite3BtreeCursorIsValid(u.bf.pC->pCursor) ); - rc = sqlite3BtreeKeySize(u.bf.pC->pCursor, &u.bf.v); + assert( sqlite3BtreeCursorIsValid(u.bg.pC->pCursor) ); + rc = sqlite3BtreeKeySize(u.bg.pC->pCursor, &u.bg.v); assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ - if( u.bf.v==MAX_ROWID ){ - u.bf.pC->useRandomRowid = 1; + if( u.bg.v==MAX_ROWID ){ + u.bg.pC->useRandomRowid = 1; }else{ - u.bf.v++; /* IMP: R-29538-34987 */ + u.bg.v++; /* IMP: R-29538-34987 */ } } } #ifndef SQLITE_OMIT_AUTOINCREMENT if( pOp->p3 ){ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3>0 ); if( p->pFrame ){ - for(u.bf.pFrame=p->pFrame; u.bf.pFrame->pParent; u.bf.pFrame=u.bf.pFrame->pParent); + for(u.bg.pFrame=p->pFrame; u.bg.pFrame->pParent; u.bg.pFrame=u.bg.pFrame->pParent); /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=u.bf.pFrame->nMem ); - u.bf.pMem = &u.bf.pFrame->aMem[pOp->p3]; + assert( pOp->p3<=u.bg.pFrame->nMem ); + u.bg.pMem = &u.bg.pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=p->nMem ); - u.bf.pMem = &aMem[pOp->p3]; - memAboutToChange(p, u.bf.pMem); - } - assert( memIsValid(u.bf.pMem) ); - - REGISTER_TRACE(pOp->p3, u.bf.pMem); - sqlite3VdbeMemIntegerify(u.bf.pMem); - assert( (u.bf.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( u.bf.pMem->u.i==MAX_ROWID || u.bf.pC->useRandomRowid ){ + u.bg.pMem = &aMem[pOp->p3]; + memAboutToChange(p, u.bg.pMem); + } + assert( memIsValid(u.bg.pMem) ); + + REGISTER_TRACE(pOp->p3, u.bg.pMem); + sqlite3VdbeMemIntegerify(u.bg.pMem); + assert( (u.bg.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ + if( u.bg.pMem->u.i==MAX_ROWID || u.bg.pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } - if( u.bf.vu.i+1 ){ - u.bf.v = u.bf.pMem->u.i + 1; + if( u.bg.vu.i+1 ){ + u.bg.v = u.bg.pMem->u.i + 1; } - u.bf.pMem->u.i = u.bf.v; + u.bg.pMem->u.i = u.bg.v; } #endif - sqlite3BtreeSetCachedRowid(u.bf.pC->pCursor, u.bf.vpCursor, u.bg.vuseRandomRowid ){ + if( u.bg.pC->useRandomRowid ){ /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the ** largest possible integer (9223372036854775807) then the database ** engine starts picking positive candidate ROWIDs at random until ** it finds one that is not previously used. */ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ /* on the first attempt, simply do one more than previous */ - u.bf.v = lastRowid; - u.bf.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ - u.bf.v++; /* ensure non-zero */ - u.bf.cnt = 0; - while( ((rc = sqlite3BtreeMovetoUnpacked(u.bf.pC->pCursor, 0, (u64)u.bf.v, - 0, &u.bf.res))==SQLITE_OK) - && (u.bf.res==0) - && (++u.bf.cnt<100)){ + u.bg.v = lastRowid; + u.bg.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ + u.bg.v++; /* ensure non-zero */ + u.bg.cnt = 0; + while( ((rc = sqlite3BtreeMovetoUnpacked(u.bg.pC->pCursor, 0, (u64)u.bg.v, + 0, &u.bg.res))==SQLITE_OK) + && (u.bg.res==0) + && (++u.bg.cnt<100)){ /* collision - try another random rowid */ - sqlite3_randomness(sizeof(u.bf.v), &u.bf.v); - if( u.bf.cnt<5 ){ + sqlite3_randomness(sizeof(u.bg.v), &u.bg.v); + if( u.bg.cnt<5 ){ /* try "small" random rowids for the initial attempts */ - u.bf.v &= 0xffffff; + u.bg.v &= 0xffffff; }else{ - u.bf.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ + u.bg.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ } - u.bf.v++; /* ensure non-zero */ + u.bg.v++; /* ensure non-zero */ } - if( rc==SQLITE_OK && u.bf.res==0 ){ + if( rc==SQLITE_OK && u.bg.res==0 ){ rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } - assert( u.bf.v>0 ); /* EV: R-40812-03570 */ + assert( u.bg.v>0 ); /* EV: R-40812-03570 */ } - u.bf.pC->rowidIsValid = 0; - u.bf.pC->deferredMoveto = 0; - u.bf.pC->cacheStatus = CACHE_STALE; + u.bg.pC->rowidIsValid = 0; + u.bg.pC->deferredMoveto = 0; + u.bg.pC->cacheStatus = CACHE_STALE; } - pOut->u.i = u.bf.v; + pOut->u.i = u.bg.v; break; } /* Opcode: Insert P1 P2 P3 P4 P5 ** @@ -67526,74 +68753,74 @@ ** This works exactly like OP_Insert except that the key is the ** integer value P3, not the value of the integer stored in register P3. */ case OP_Insert: case OP_InsertInt: { -#if 0 /* local variables moved into u.bg */ +#if 0 /* local variables moved into u.bh */ Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ i64 iKey; /* The integer ROWID or key for the record to be inserted */ VdbeCursor *pC; /* Cursor to table into which insert is written */ int nZero; /* Number of zero-bytes to append */ int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ -#endif /* local variables moved into u.bg */ +#endif /* local variables moved into u.bh */ - u.bg.pData = &aMem[pOp->p2]; + u.bh.pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( memIsValid(u.bg.pData) ); - u.bg.pC = p->apCsr[pOp->p1]; - assert( u.bg.pC!=0 ); - assert( u.bg.pC->pCursor!=0 ); - assert( u.bg.pC->pseudoTableReg==0 ); - assert( u.bg.pC->isTable ); - REGISTER_TRACE(pOp->p2, u.bg.pData); + assert( memIsValid(u.bh.pData) ); + u.bh.pC = p->apCsr[pOp->p1]; + assert( u.bh.pC!=0 ); + assert( u.bh.pC->pCursor!=0 ); + assert( u.bh.pC->pseudoTableReg==0 ); + assert( u.bh.pC->isTable ); + REGISTER_TRACE(pOp->p2, u.bh.pData); if( pOp->opcode==OP_Insert ){ - u.bg.pKey = &aMem[pOp->p3]; - assert( u.bg.pKey->flags & MEM_Int ); - assert( memIsValid(u.bg.pKey) ); - REGISTER_TRACE(pOp->p3, u.bg.pKey); - u.bg.iKey = u.bg.pKey->u.i; + u.bh.pKey = &aMem[pOp->p3]; + assert( u.bh.pKey->flags & MEM_Int ); + assert( memIsValid(u.bh.pKey) ); + REGISTER_TRACE(pOp->p3, u.bh.pKey); + u.bh.iKey = u.bh.pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); - u.bg.iKey = pOp->p3; + u.bh.iKey = pOp->p3; } if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bg.iKey; - if( u.bg.pData->flags & MEM_Null ){ - u.bg.pData->z = 0; - u.bg.pData->n = 0; - }else{ - assert( u.bg.pData->flags & (MEM_Blob|MEM_Str) ); - } - u.bg.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bg.pC->seekResult : 0); - if( u.bg.pData->flags & MEM_Zero ){ - u.bg.nZero = u.bg.pData->u.nZero; - }else{ - u.bg.nZero = 0; - } - sqlite3BtreeSetCachedRowid(u.bg.pC->pCursor, 0); - rc = sqlite3BtreeInsert(u.bg.pC->pCursor, 0, u.bg.iKey, - u.bg.pData->z, u.bg.pData->n, u.bg.nZero, - pOp->p5 & OPFLAG_APPEND, u.bg.seekResult + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bh.iKey; + if( u.bh.pData->flags & MEM_Null ){ + u.bh.pData->z = 0; + u.bh.pData->n = 0; + }else{ + assert( u.bh.pData->flags & (MEM_Blob|MEM_Str) ); + } + u.bh.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bh.pC->seekResult : 0); + if( u.bh.pData->flags & MEM_Zero ){ + u.bh.nZero = u.bh.pData->u.nZero; + }else{ + u.bh.nZero = 0; + } + sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, 0); + rc = sqlite3BtreeInsert(u.bh.pC->pCursor, 0, u.bh.iKey, + u.bh.pData->z, u.bh.pData->n, u.bh.nZero, + pOp->p5 & OPFLAG_APPEND, u.bh.seekResult ); - u.bg.pC->rowidIsValid = 0; - u.bg.pC->deferredMoveto = 0; - u.bg.pC->cacheStatus = CACHE_STALE; + u.bh.pC->rowidIsValid = 0; + u.bh.pC->deferredMoveto = 0; + u.bh.pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - u.bg.zDb = db->aDb[u.bg.pC->iDb].zName; - u.bg.zTbl = pOp->p4.z; - u.bg.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); - assert( u.bg.pC->isTable ); - db->xUpdateCallback(db->pUpdateArg, u.bg.op, u.bg.zDb, u.bg.zTbl, u.bg.iKey); - assert( u.bg.pC->iDb>=0 ); + u.bh.zDb = db->aDb[u.bh.pC->iDb].zName; + u.bh.zTbl = pOp->p4.z; + u.bh.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); + assert( u.bh.pC->isTable ); + db->xUpdateCallback(db->pUpdateArg, u.bh.op, u.bh.zDb, u.bh.zTbl, u.bh.iKey); + assert( u.bh.pC->iDb>=0 ); } break; } /* Opcode: Delete P1 P2 * P4 * @@ -67615,51 +68842,51 @@ ** pointing to. The update hook will be invoked, if it exists. ** If P4 is not NULL then the P1 cursor must have been positioned ** using OP_NotFound prior to invoking this opcode. */ case OP_Delete: { -#if 0 /* local variables moved into u.bh */ +#if 0 /* local variables moved into u.bi */ i64 iKey; VdbeCursor *pC; -#endif /* local variables moved into u.bh */ +#endif /* local variables moved into u.bi */ - u.bh.iKey = 0; + u.bi.iKey = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bh.pC = p->apCsr[pOp->p1]; - assert( u.bh.pC!=0 ); - assert( u.bh.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ + u.bi.pC = p->apCsr[pOp->p1]; + assert( u.bi.pC!=0 ); + assert( u.bi.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ - /* If the update-hook will be invoked, set u.bh.iKey to the rowid of the + /* If the update-hook will be invoked, set u.bi.iKey to the rowid of the ** row being deleted. */ if( db->xUpdateCallback && pOp->p4.z ){ - assert( u.bh.pC->isTable ); - assert( u.bh.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ - u.bh.iKey = u.bh.pC->lastRowid; + assert( u.bi.pC->isTable ); + assert( u.bi.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ + u.bi.iKey = u.bi.pC->lastRowid; } /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or ** OP_Column on the same table without any intervening operations that - ** might move or invalidate the cursor. Hence cursor u.bh.pC is always pointing + ** might move or invalidate the cursor. Hence cursor u.bi.pC is always pointing ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation ** below is always a no-op and cannot fail. We will run it anyhow, though, ** to guard against future changes to the code generator. **/ - assert( u.bh.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bh.pC); + assert( u.bi.pC->deferredMoveto==0 ); + rc = sqlite3VdbeCursorMoveto(u.bi.pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, 0); - rc = sqlite3BtreeDelete(u.bh.pC->pCursor); - u.bh.pC->cacheStatus = CACHE_STALE; + sqlite3BtreeSetCachedRowid(u.bi.pC->pCursor, 0); + rc = sqlite3BtreeDelete(u.bi.pC->pCursor); + u.bi.pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - const char *zDb = db->aDb[u.bh.pC->iDb].zName; + const char *zDb = db->aDb[u.bi.pC->iDb].zName; const char *zTbl = pOp->p4.z; - db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bh.iKey); - assert( u.bh.pC->iDb>=0 ); + db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bi.iKey); + assert( u.bi.pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; } /* Opcode: ResetCount * * * * * @@ -67681,20 +68908,20 @@ ** register P3 with the entry that the sorter cursor currently points to. ** If, excluding the rowid fields at the end, the two records are a match, ** fall through to the next instruction. Otherwise, jump to instruction P2. */ case OP_SorterCompare: { -#if 0 /* local variables moved into u.bi */ +#if 0 /* local variables moved into u.bj */ VdbeCursor *pC; int res; -#endif /* local variables moved into u.bi */ +#endif /* local variables moved into u.bj */ - u.bi.pC = p->apCsr[pOp->p1]; - assert( isSorter(u.bi.pC) ); + u.bj.pC = p->apCsr[pOp->p1]; + assert( isSorter(u.bj.pC) ); pIn3 = &aMem[pOp->p3]; - rc = sqlite3VdbeSorterCompare(u.bi.pC, pIn3, &u.bi.res); - if( u.bi.res ){ + rc = sqlite3VdbeSorterCompare(u.bj.pC, pIn3, &u.bj.res); + if( u.bj.res ){ pc = pOp->p2-1; } break; }; @@ -67701,18 +68928,18 @@ /* Opcode: SorterData P1 P2 * * * ** ** Write into register P2 the current sorter data for sorter cursor P1. */ case OP_SorterData: { -#if 0 /* local variables moved into u.bj */ +#if 0 /* local variables moved into u.bk */ VdbeCursor *pC; -#endif /* local variables moved into u.bj */ +#endif /* local variables moved into u.bk */ #ifndef SQLITE_OMIT_MERGE_SORT pOut = &aMem[pOp->p2]; - u.bj.pC = p->apCsr[pOp->p1]; - assert( u.bj.pC->isSorter ); - rc = sqlite3VdbeSorterRowkey(u.bj.pC, pOut); + u.bk.pC = p->apCsr[pOp->p1]; + assert( u.bk.pC->isSorter ); + rc = sqlite3VdbeSorterRowkey(u.bk.pC, pOut); #else pOp->opcode = OP_RowKey; pc--; #endif break; @@ -67738,67 +68965,67 @@ ** If the P1 cursor must be pointing to a valid row (not a NULL row) ** of a real table, not a pseudo-table. */ case OP_RowKey: case OP_RowData: { -#if 0 /* local variables moved into u.bk */ +#if 0 /* local variables moved into u.bl */ VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; -#endif /* local variables moved into u.bk */ +#endif /* local variables moved into u.bl */ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bk.pC = p->apCsr[pOp->p1]; - assert( u.bk.pC->isSorter==0 ); - assert( u.bk.pC->isTable || pOp->opcode!=OP_RowData ); - assert( u.bk.pC->isIndex || pOp->opcode==OP_RowData ); - assert( u.bk.pC!=0 ); - assert( u.bk.pC->nullRow==0 ); - assert( u.bk.pC->pseudoTableReg==0 ); - assert( !u.bk.pC->isSorter ); - assert( u.bk.pC->pCursor!=0 ); - u.bk.pCrsr = u.bk.pC->pCursor; - assert( sqlite3BtreeCursorIsValid(u.bk.pCrsr) ); + u.bl.pC = p->apCsr[pOp->p1]; + assert( u.bl.pC->isSorter==0 ); + assert( u.bl.pC->isTable || pOp->opcode!=OP_RowData ); + assert( u.bl.pC->isIndex || pOp->opcode==OP_RowData ); + assert( u.bl.pC!=0 ); + assert( u.bl.pC->nullRow==0 ); + assert( u.bl.pC->pseudoTableReg==0 ); + assert( !u.bl.pC->isSorter ); + assert( u.bl.pC->pCursor!=0 ); + u.bl.pCrsr = u.bl.pC->pCursor; + assert( sqlite3BtreeCursorIsValid(u.bl.pCrsr) ); /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or ** OP_Rewind/Op_Next with no intervening instructions that might invalidate ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always ** a no-op and can never fail. But we leave it in place as a safety. */ - assert( u.bk.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bk.pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - - if( u.bk.pC->isIndex ){ - assert( !u.bk.pC->isTable ); - rc = sqlite3BtreeKeySize(u.bk.pCrsr, &u.bk.n64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - if( u.bk.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - u.bk.n = (u32)u.bk.n64; - }else{ - rc = sqlite3BtreeDataSize(u.bk.pCrsr, &u.bk.n); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - if( u.bk.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - } - if( sqlite3VdbeMemGrow(pOut, u.bk.n, 0) ){ - goto no_mem; - } - pOut->n = u.bk.n; - MemSetTypeFlag(pOut, MEM_Blob); - if( u.bk.pC->isIndex ){ - rc = sqlite3BtreeKey(u.bk.pCrsr, 0, u.bk.n, pOut->z); - }else{ - rc = sqlite3BtreeData(u.bk.pCrsr, 0, u.bk.n, pOut->z); + assert( u.bl.pC->deferredMoveto==0 ); + rc = sqlite3VdbeCursorMoveto(u.bl.pC); + if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; + + if( u.bl.pC->isIndex ){ + assert( !u.bl.pC->isTable ); + VVA_ONLY(rc =) sqlite3BtreeKeySize(u.bl.pCrsr, &u.bl.n64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ + if( u.bl.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; + } + u.bl.n = (u32)u.bl.n64; + }else{ + VVA_ONLY(rc =) sqlite3BtreeDataSize(u.bl.pCrsr, &u.bl.n); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + if( u.bl.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; + } + } + if( sqlite3VdbeMemGrow(pOut, u.bl.n, 0) ){ + goto no_mem; + } + pOut->n = u.bl.n; + MemSetTypeFlag(pOut, MEM_Blob); + if( u.bl.pC->isIndex ){ + rc = sqlite3BtreeKey(u.bl.pCrsr, 0, u.bl.n, pOut->z); + }else{ + rc = sqlite3BtreeData(u.bl.pCrsr, 0, u.bl.n, pOut->z); } pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -67811,46 +69038,46 @@ ** P1 can be either an ordinary table or a virtual table. There used to ** be a separate OP_VRowid opcode for use with virtual tables, but this ** one opcode now works for both table types. */ case OP_Rowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bl */ +#if 0 /* local variables moved into u.bm */ VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; -#endif /* local variables moved into u.bl */ +#endif /* local variables moved into u.bm */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bl.pC = p->apCsr[pOp->p1]; - assert( u.bl.pC!=0 ); - assert( u.bl.pC->pseudoTableReg==0 ); - if( u.bl.pC->nullRow ){ + u.bm.pC = p->apCsr[pOp->p1]; + assert( u.bm.pC!=0 ); + assert( u.bm.pC->pseudoTableReg==0 ); + if( u.bm.pC->nullRow ){ pOut->flags = MEM_Null; break; - }else if( u.bl.pC->deferredMoveto ){ - u.bl.v = u.bl.pC->movetoTarget; + }else if( u.bm.pC->deferredMoveto ){ + u.bm.v = u.bm.pC->movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( u.bl.pC->pVtabCursor ){ - u.bl.pVtab = u.bl.pC->pVtabCursor->pVtab; - u.bl.pModule = u.bl.pVtab->pModule; - assert( u.bl.pModule->xRowid ); - rc = u.bl.pModule->xRowid(u.bl.pC->pVtabCursor, &u.bl.v); - importVtabErrMsg(p, u.bl.pVtab); + }else if( u.bm.pC->pVtabCursor ){ + u.bm.pVtab = u.bm.pC->pVtabCursor->pVtab; + u.bm.pModule = u.bm.pVtab->pModule; + assert( u.bm.pModule->xRowid ); + rc = u.bm.pModule->xRowid(u.bm.pC->pVtabCursor, &u.bm.v); + importVtabErrMsg(p, u.bm.pVtab); #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ - assert( u.bl.pC->pCursor!=0 ); - rc = sqlite3VdbeCursorMoveto(u.bl.pC); + assert( u.bm.pC->pCursor!=0 ); + rc = sqlite3VdbeCursorMoveto(u.bm.pC); if( rc ) goto abort_due_to_error; - if( u.bl.pC->rowidIsValid ){ - u.bl.v = u.bl.pC->lastRowid; + if( u.bm.pC->rowidIsValid ){ + u.bm.v = u.bm.pC->lastRowid; }else{ - rc = sqlite3BtreeKeySize(u.bl.pC->pCursor, &u.bl.v); + rc = sqlite3BtreeKeySize(u.bm.pC->pCursor, &u.bm.v); assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ } } - pOut->u.i = u.bl.v; + pOut->u.i = u.bm.v; break; } /* Opcode: NullRow P1 * * * * ** @@ -67857,22 +69084,22 @@ ** Move the cursor P1 to a null row. Any OP_Column operations ** that occur while the cursor is on the null row will always ** write a NULL. */ case OP_NullRow: { -#if 0 /* local variables moved into u.bm */ +#if 0 /* local variables moved into u.bn */ VdbeCursor *pC; -#endif /* local variables moved into u.bm */ +#endif /* local variables moved into u.bn */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bm.pC = p->apCsr[pOp->p1]; - assert( u.bm.pC!=0 ); - u.bm.pC->nullRow = 1; - u.bm.pC->rowidIsValid = 0; - assert( u.bm.pC->pCursor || u.bm.pC->pVtabCursor ); - if( u.bm.pC->pCursor ){ - sqlite3BtreeClearCursor(u.bm.pC->pCursor); + u.bn.pC = p->apCsr[pOp->p1]; + assert( u.bn.pC!=0 ); + u.bn.pC->nullRow = 1; + u.bn.pC->rowidIsValid = 0; + assert( u.bn.pC->pCursor || u.bn.pC->pVtabCursor ); + if( u.bn.pC->pCursor ){ + sqlite3BtreeClearCursor(u.bn.pC->pCursor); } break; } /* Opcode: Last P1 P2 * * * @@ -67882,30 +69109,29 @@ ** If the table or index is empty and P2>0, then jump immediately to P2. ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ case OP_Last: { /* jump */ -#if 0 /* local variables moved into u.bn */ +#if 0 /* local variables moved into u.bo */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bn */ +#endif /* local variables moved into u.bo */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bn.pC = p->apCsr[pOp->p1]; - assert( u.bn.pC!=0 ); - u.bn.pCrsr = u.bn.pC->pCursor; - if( NEVER(u.bn.pCrsr==0) ){ - u.bn.res = 1; - }else{ - rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res); - } - u.bn.pC->nullRow = (u8)u.bn.res; - u.bn.pC->deferredMoveto = 0; - u.bn.pC->rowidIsValid = 0; - u.bn.pC->cacheStatus = CACHE_STALE; - if( pOp->p2>0 && u.bn.res ){ + u.bo.pC = p->apCsr[pOp->p1]; + assert( u.bo.pC!=0 ); + u.bo.pCrsr = u.bo.pC->pCursor; + u.bo.res = 0; + if( ALWAYS(u.bo.pCrsr!=0) ){ + rc = sqlite3BtreeLast(u.bo.pCrsr, &u.bo.res); + } + u.bo.pC->nullRow = (u8)u.bo.res; + u.bo.pC->deferredMoveto = 0; + u.bo.pC->rowidIsValid = 0; + u.bo.pC->cacheStatus = CACHE_STALE; + if( pOp->p2>0 && u.bo.res ){ pc = pOp->p2 - 1; } break; } @@ -67941,35 +69167,35 @@ ** If the table or index is empty and P2>0, then jump immediately to P2. ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ case OP_Rewind: { /* jump */ -#if 0 /* local variables moved into u.bo */ +#if 0 /* local variables moved into u.bp */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bo */ +#endif /* local variables moved into u.bp */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bo.pC = p->apCsr[pOp->p1]; - assert( u.bo.pC!=0 ); - assert( u.bo.pC->isSorter==(pOp->opcode==OP_SorterSort) ); - u.bo.res = 1; - if( isSorter(u.bo.pC) ){ - rc = sqlite3VdbeSorterRewind(db, u.bo.pC, &u.bo.res); + u.bp.pC = p->apCsr[pOp->p1]; + assert( u.bp.pC!=0 ); + assert( u.bp.pC->isSorter==(pOp->opcode==OP_SorterSort) ); + u.bp.res = 1; + if( isSorter(u.bp.pC) ){ + rc = sqlite3VdbeSorterRewind(db, u.bp.pC, &u.bp.res); }else{ - u.bo.pCrsr = u.bo.pC->pCursor; - assert( u.bo.pCrsr ); - rc = sqlite3BtreeFirst(u.bo.pCrsr, &u.bo.res); - u.bo.pC->atFirst = u.bo.res==0 ?1:0; - u.bo.pC->deferredMoveto = 0; - u.bo.pC->cacheStatus = CACHE_STALE; - u.bo.pC->rowidIsValid = 0; - } - u.bo.pC->nullRow = (u8)u.bo.res; + u.bp.pCrsr = u.bp.pC->pCursor; + assert( u.bp.pCrsr ); + rc = sqlite3BtreeFirst(u.bp.pCrsr, &u.bp.res); + u.bp.pC->atFirst = u.bp.res==0 ?1:0; + u.bp.pC->deferredMoveto = 0; + u.bp.pC->cacheStatus = CACHE_STALE; + u.bp.pC->rowidIsValid = 0; + } + u.bp.pC->nullRow = (u8)u.bp.res; assert( pOp->p2>0 && pOp->p2nOp ); - if( u.bo.res ){ + if( u.bp.res ){ pc = pOp->p2 - 1; } break; } @@ -68009,44 +69235,44 @@ #ifdef SQLITE_OMIT_MERGE_SORT pOp->opcode = OP_Next; #endif case OP_Prev: /* jump */ case OP_Next: { /* jump */ -#if 0 /* local variables moved into u.bp */ +#if 0 /* local variables moved into u.bq */ VdbeCursor *pC; int res; -#endif /* local variables moved into u.bp */ +#endif /* local variables moved into u.bq */ CHECK_FOR_INTERRUPT; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p5<=ArraySize(p->aCounter) ); - u.bp.pC = p->apCsr[pOp->p1]; - if( u.bp.pC==0 ){ + u.bq.pC = p->apCsr[pOp->p1]; + if( u.bq.pC==0 ){ break; /* See ticket #2273 */ } - assert( u.bp.pC->isSorter==(pOp->opcode==OP_SorterNext) ); - if( isSorter(u.bp.pC) ){ + assert( u.bq.pC->isSorter==(pOp->opcode==OP_SorterNext) ); + if( isSorter(u.bq.pC) ){ assert( pOp->opcode==OP_SorterNext ); - rc = sqlite3VdbeSorterNext(db, u.bp.pC, &u.bp.res); + rc = sqlite3VdbeSorterNext(db, u.bq.pC, &u.bq.res); }else{ - u.bp.res = 1; - assert( u.bp.pC->deferredMoveto==0 ); - assert( u.bp.pC->pCursor ); + u.bq.res = 1; + assert( u.bq.pC->deferredMoveto==0 ); + assert( u.bq.pC->pCursor ); assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); - rc = pOp->p4.xAdvance(u.bp.pC->pCursor, &u.bp.res); + rc = pOp->p4.xAdvance(u.bq.pC->pCursor, &u.bq.res); } - u.bp.pC->nullRow = (u8)u.bp.res; - u.bp.pC->cacheStatus = CACHE_STALE; - if( u.bp.res==0 ){ + u.bq.pC->nullRow = (u8)u.bq.res; + u.bq.pC->cacheStatus = CACHE_STALE; + if( u.bq.res==0 ){ pc = pOp->p2 - 1; if( pOp->p5 ) p->aCounter[pOp->p5-1]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif } - u.bp.pC->rowidIsValid = 0; + u.bq.pC->rowidIsValid = 0; break; } /* Opcode: IdxInsert P1 P2 P3 * P5 ** @@ -68063,38 +69289,38 @@ case OP_SorterInsert: /* in2 */ #ifdef SQLITE_OMIT_MERGE_SORT pOp->opcode = OP_IdxInsert; #endif case OP_IdxInsert: { /* in2 */ -#if 0 /* local variables moved into u.bq */ +#if 0 /* local variables moved into u.br */ VdbeCursor *pC; BtCursor *pCrsr; int nKey; const char *zKey; -#endif /* local variables moved into u.bq */ +#endif /* local variables moved into u.br */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bq.pC = p->apCsr[pOp->p1]; - assert( u.bq.pC!=0 ); - assert( u.bq.pC->isSorter==(pOp->opcode==OP_SorterInsert) ); + u.br.pC = p->apCsr[pOp->p1]; + assert( u.br.pC!=0 ); + assert( u.br.pC->isSorter==(pOp->opcode==OP_SorterInsert) ); pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); - u.bq.pCrsr = u.bq.pC->pCursor; - if( ALWAYS(u.bq.pCrsr!=0) ){ - assert( u.bq.pC->isTable==0 ); + u.br.pCrsr = u.br.pC->pCursor; + if( ALWAYS(u.br.pCrsr!=0) ){ + assert( u.br.pC->isTable==0 ); rc = ExpandBlob(pIn2); if( rc==SQLITE_OK ){ - if( isSorter(u.bq.pC) ){ - rc = sqlite3VdbeSorterWrite(db, u.bq.pC, pIn2); + if( isSorter(u.br.pC) ){ + rc = sqlite3VdbeSorterWrite(db, u.br.pC, pIn2); }else{ - u.bq.nKey = pIn2->n; - u.bq.zKey = pIn2->z; - rc = sqlite3BtreeInsert(u.bq.pCrsr, u.bq.zKey, u.bq.nKey, "", 0, 0, pOp->p3, - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bq.pC->seekResult : 0) + u.br.nKey = pIn2->n; + u.br.zKey = pIn2->z; + rc = sqlite3BtreeInsert(u.br.pCrsr, u.br.zKey, u.br.nKey, "", 0, 0, pOp->p3, + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.br.pC->seekResult : 0) ); - assert( u.bq.pC->deferredMoveto==0 ); - u.bq.pC->cacheStatus = CACHE_STALE; + assert( u.br.pC->deferredMoveto==0 ); + u.br.pC->cacheStatus = CACHE_STALE; } } } break; } @@ -68104,37 +69330,37 @@ ** The content of P3 registers starting at register P2 form ** an unpacked index key. This opcode removes that entry from the ** index opened by cursor P1. */ case OP_IdxDelete: { -#if 0 /* local variables moved into u.br */ +#if 0 /* local variables moved into u.bs */ VdbeCursor *pC; BtCursor *pCrsr; int res; UnpackedRecord r; -#endif /* local variables moved into u.br */ +#endif /* local variables moved into u.bs */ assert( pOp->p3>0 ); assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 ); assert( pOp->p1>=0 && pOp->p1nCursor ); - u.br.pC = p->apCsr[pOp->p1]; - assert( u.br.pC!=0 ); - u.br.pCrsr = u.br.pC->pCursor; - if( ALWAYS(u.br.pCrsr!=0) ){ - u.br.r.pKeyInfo = u.br.pC->pKeyInfo; - u.br.r.nField = (u16)pOp->p3; - u.br.r.flags = 0; - u.br.r.aMem = &aMem[pOp->p2]; + u.bs.pC = p->apCsr[pOp->p1]; + assert( u.bs.pC!=0 ); + u.bs.pCrsr = u.bs.pC->pCursor; + if( ALWAYS(u.bs.pCrsr!=0) ){ + u.bs.r.pKeyInfo = u.bs.pC->pKeyInfo; + u.bs.r.nField = (u16)pOp->p3; + u.bs.r.flags = 0; + u.bs.r.aMem = &aMem[pOp->p2]; #ifdef SQLITE_DEBUG - { int i; for(i=0; ideferredMoveto==0 ); - u.br.pC->cacheStatus = CACHE_STALE; + assert( u.bs.pC->deferredMoveto==0 ); + u.bs.pC->cacheStatus = CACHE_STALE; } break; } /* Opcode: IdxRowid P1 P2 * * * @@ -68144,32 +69370,32 @@ ** the rowid of the table entry to which this index entry points. ** ** See also: Rowid, MakeRecord. */ case OP_IdxRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bs */ +#if 0 /* local variables moved into u.bt */ BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; -#endif /* local variables moved into u.bs */ +#endif /* local variables moved into u.bt */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bs.pC = p->apCsr[pOp->p1]; - assert( u.bs.pC!=0 ); - u.bs.pCrsr = u.bs.pC->pCursor; + u.bt.pC = p->apCsr[pOp->p1]; + assert( u.bt.pC!=0 ); + u.bt.pCrsr = u.bt.pC->pCursor; pOut->flags = MEM_Null; - if( ALWAYS(u.bs.pCrsr!=0) ){ - rc = sqlite3VdbeCursorMoveto(u.bs.pC); + if( ALWAYS(u.bt.pCrsr!=0) ){ + rc = sqlite3VdbeCursorMoveto(u.bt.pC); if( NEVER(rc) ) goto abort_due_to_error; - assert( u.bs.pC->deferredMoveto==0 ); - assert( u.bs.pC->isTable==0 ); - if( !u.bs.pC->nullRow ){ - rc = sqlite3VdbeIdxRowid(db, u.bs.pCrsr, &u.bs.rowid); + assert( u.bt.pC->deferredMoveto==0 ); + assert( u.bt.pC->isTable==0 ); + if( !u.bt.pC->nullRow ){ + rc = sqlite3VdbeIdxRowid(db, u.bt.pCrsr, &u.bt.rowid); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - pOut->u.i = u.bs.rowid; + pOut->u.i = u.bt.rowid; pOut->flags = MEM_Int; } } break; } @@ -68200,43 +69426,43 @@ ** If P5 is non-zero then the key value is increased by an epsilon prior ** to the comparison. This makes the opcode work like IdxLE. */ case OP_IdxLT: /* jump */ case OP_IdxGE: { /* jump */ -#if 0 /* local variables moved into u.bt */ +#if 0 /* local variables moved into u.bu */ VdbeCursor *pC; int res; UnpackedRecord r; -#endif /* local variables moved into u.bt */ +#endif /* local variables moved into u.bu */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bt.pC = p->apCsr[pOp->p1]; - assert( u.bt.pC!=0 ); - assert( u.bt.pC->isOrdered ); - if( ALWAYS(u.bt.pC->pCursor!=0) ){ - assert( u.bt.pC->deferredMoveto==0 ); + u.bu.pC = p->apCsr[pOp->p1]; + assert( u.bu.pC!=0 ); + assert( u.bu.pC->isOrdered ); + if( ALWAYS(u.bu.pC->pCursor!=0) ){ + assert( u.bu.pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); - u.bt.r.pKeyInfo = u.bt.pC->pKeyInfo; - u.bt.r.nField = (u16)pOp->p4.i; + u.bu.r.pKeyInfo = u.bu.pC->pKeyInfo; + u.bu.r.nField = (u16)pOp->p4.i; if( pOp->p5 ){ - u.bt.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; + u.bu.r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; }else{ - u.bt.r.flags = UNPACKED_IGNORE_ROWID; + u.bu.r.flags = UNPACKED_PREFIX_MATCH; } - u.bt.r.aMem = &aMem[pOp->p3]; + u.bu.r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG - { int i; for(i=0; iopcode==OP_IdxLT ){ - u.bt.res = -u.bt.res; + u.bu.res = -u.bu.res; }else{ assert( pOp->opcode==OP_IdxGE ); - u.bt.res++; + u.bu.res++; } - if( u.bt.res>0 ){ + if( u.bu.res>0 ){ pc = pOp->p2 - 1 ; } } break; } @@ -68260,43 +69486,43 @@ ** If AUTOVACUUM is disabled then a zero is stored in register P2. ** ** See also: Clear */ case OP_Destroy: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bu */ +#if 0 /* local variables moved into u.bv */ int iMoved; int iCnt; Vdbe *pVdbe; int iDb; -#endif /* local variables moved into u.bu */ +#endif /* local variables moved into u.bv */ #ifndef SQLITE_OMIT_VIRTUALTABLE - u.bu.iCnt = 0; - for(u.bu.pVdbe=db->pVdbe; u.bu.pVdbe; u.bu.pVdbe = u.bu.pVdbe->pNext){ - if( u.bu.pVdbe->magic==VDBE_MAGIC_RUN && u.bu.pVdbe->inVtabMethod<2 && u.bu.pVdbe->pc>=0 ){ - u.bu.iCnt++; + u.bv.iCnt = 0; + for(u.bv.pVdbe=db->pVdbe; u.bv.pVdbe; u.bv.pVdbe = u.bv.pVdbe->pNext){ + if( u.bv.pVdbe->magic==VDBE_MAGIC_RUN && u.bv.pVdbe->inVtabMethod<2 && u.bv.pVdbe->pc>=0 ){ + u.bv.iCnt++; } } #else - u.bu.iCnt = db->activeVdbeCnt; + u.bv.iCnt = db->activeVdbeCnt; #endif pOut->flags = MEM_Null; - if( u.bu.iCnt>1 ){ + if( u.bv.iCnt>1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ - u.bu.iDb = pOp->p3; - assert( u.bu.iCnt==1 ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.bu.iDb].pBt, pOp->p1, &u.bu.iMoved); + u.bv.iDb = pOp->p3; + assert( u.bv.iCnt==1 ); + assert( (p->btreeMask & (((yDbMask)1)<aDb[u.bv.iDb].pBt, pOp->p1, &u.bv.iMoved); pOut->flags = MEM_Int; - pOut->u.i = u.bu.iMoved; + pOut->u.i = u.bv.iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM - if( rc==SQLITE_OK && u.bu.iMoved!=0 ){ - sqlite3RootPageMoved(db, u.bu.iDb, u.bu.iMoved, pOp->p1); + if( rc==SQLITE_OK && u.bv.iMoved!=0 ){ + sqlite3RootPageMoved(db, u.bv.iDb, u.bv.iMoved, pOp->p1); /* All OP_Destroy operations occur on the same btree */ - assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.bu.iDb+1 ); - resetSchemaOnFault = u.bu.iDb+1; + assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.bv.iDb+1 ); + resetSchemaOnFault = u.bv.iDb+1; } #endif } break; } @@ -68318,25 +69544,25 @@ ** also incremented by the number of rows in the table being cleared. ** ** See also: Destroy */ case OP_Clear: { -#if 0 /* local variables moved into u.bv */ +#if 0 /* local variables moved into u.bw */ int nChange; -#endif /* local variables moved into u.bv */ +#endif /* local variables moved into u.bw */ - u.bv.nChange = 0; + u.bw.nChange = 0; assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); rc = sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bv.nChange : 0) + db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bw.nChange : 0) ); if( pOp->p3 ){ - p->nChange += u.bv.nChange; + p->nChange += u.bw.nChange; if( pOp->p3>0 ){ assert( memIsValid(&aMem[pOp->p3]) ); memAboutToChange(p, &aMem[pOp->p3]); - aMem[pOp->p3].u.i += u.bv.nChange; + aMem[pOp->p3].u.i += u.bw.nChange; } } break; } @@ -68362,29 +69588,29 @@ ** ** See documentation on OP_CreateTable for additional information. */ case OP_CreateIndex: /* out2-prerelease */ case OP_CreateTable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bw */ +#if 0 /* local variables moved into u.bx */ int pgno; int flags; Db *pDb; -#endif /* local variables moved into u.bw */ +#endif /* local variables moved into u.bx */ - u.bw.pgno = 0; + u.bx.pgno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.bw.pDb = &db->aDb[pOp->p1]; - assert( u.bw.pDb->pBt!=0 ); + u.bx.pDb = &db->aDb[pOp->p1]; + assert( u.bx.pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ - /* u.bw.flags = BTREE_INTKEY; */ - u.bw.flags = BTREE_INTKEY; + /* u.bx.flags = BTREE_INTKEY; */ + u.bx.flags = BTREE_INTKEY; }else{ - u.bw.flags = BTREE_BLOBKEY; + u.bx.flags = BTREE_BLOBKEY; } - rc = sqlite3BtreeCreateTable(u.bw.pDb->pBt, &u.bw.pgno, u.bw.flags); - pOut->u.i = u.bw.pgno; + rc = sqlite3BtreeCreateTable(u.bx.pDb->pBt, &u.bx.pgno, u.bx.flags); + pOut->u.i = u.bx.pgno; break; } /* Opcode: ParseSchema P1 * * P4 * ** @@ -68393,48 +69619,48 @@ ** ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a re-entrant opcode. */ case OP_ParseSchema: { -#if 0 /* local variables moved into u.bx */ +#if 0 /* local variables moved into u.by */ int iDb; const char *zMaster; char *zSql; InitData initData; -#endif /* local variables moved into u.bx */ +#endif /* local variables moved into u.by */ /* Any prepared statement that invokes this opcode will hold mutexes ** on every btree. This is a prerequisite for invoking ** sqlite3InitCallback(). */ #ifdef SQLITE_DEBUG - for(u.bx.iDb=0; u.bx.iDbnDb; u.bx.iDb++){ - assert( u.bx.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bx.iDb].pBt) ); + for(u.by.iDb=0; u.by.iDbnDb; u.by.iDb++){ + assert( u.by.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.by.iDb].pBt) ); } #endif - u.bx.iDb = pOp->p1; - assert( u.bx.iDb>=0 && u.bx.iDbnDb ); - assert( DbHasProperty(db, u.bx.iDb, DB_SchemaLoaded) ); + u.by.iDb = pOp->p1; + assert( u.by.iDb>=0 && u.by.iDbnDb ); + assert( DbHasProperty(db, u.by.iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { - u.bx.zMaster = SCHEMA_TABLE(u.bx.iDb); - u.bx.initData.db = db; - u.bx.initData.iDb = pOp->p1; - u.bx.initData.pzErrMsg = &p->zErrMsg; - u.bx.zSql = sqlite3MPrintf(db, + u.by.zMaster = SCHEMA_TABLE(u.by.iDb); + u.by.initData.db = db; + u.by.initData.iDb = pOp->p1; + u.by.initData.pzErrMsg = &p->zErrMsg; + u.by.zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", - db->aDb[u.bx.iDb].zName, u.bx.zMaster, pOp->p4.z); - if( u.bx.zSql==0 ){ + db->aDb[u.by.iDb].zName, u.by.zMaster, pOp->p4.z); + if( u.by.zSql==0 ){ rc = SQLITE_NOMEM; }else{ assert( db->init.busy==0 ); db->init.busy = 1; - u.bx.initData.rc = SQLITE_OK; + u.by.initData.rc = SQLITE_OK; assert( !db->mallocFailed ); - rc = sqlite3_exec(db, u.bx.zSql, sqlite3InitCallback, &u.bx.initData, 0); - if( rc==SQLITE_OK ) rc = u.bx.initData.rc; - sqlite3DbFree(db, u.bx.zSql); + rc = sqlite3_exec(db, u.by.zSql, sqlite3InitCallback, &u.by.initData, 0); + if( rc==SQLITE_OK ) rc = u.by.initData.rc; + sqlite3DbFree(db, u.by.zSql); db->init.busy = 0; } } if( rc==SQLITE_NOMEM ){ goto no_mem; @@ -68513,45 +69739,45 @@ ** file, not the main database file. ** ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { -#if 0 /* local variables moved into u.by */ +#if 0 /* local variables moved into u.bz */ int nRoot; /* Number of tables to check. (Number of root pages.) */ int *aRoot; /* Array of rootpage numbers for tables to be checked */ int j; /* Loop counter */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ -#endif /* local variables moved into u.by */ +#endif /* local variables moved into u.bz */ - u.by.nRoot = pOp->p2; - assert( u.by.nRoot>0 ); - u.by.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.by.nRoot+1) ); - if( u.by.aRoot==0 ) goto no_mem; + u.bz.nRoot = pOp->p2; + assert( u.bz.nRoot>0 ); + u.bz.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.bz.nRoot+1) ); + if( u.bz.aRoot==0 ) goto no_mem; assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.by.pnErr = &aMem[pOp->p3]; - assert( (u.by.pnErr->flags & MEM_Int)!=0 ); - assert( (u.by.pnErr->flags & (MEM_Str|MEM_Blob))==0 ); + u.bz.pnErr = &aMem[pOp->p3]; + assert( (u.bz.pnErr->flags & MEM_Int)!=0 ); + assert( (u.bz.pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; - for(u.by.j=0; u.by.jp5nDb ); assert( (p->btreeMask & (((yDbMask)1)<p5))!=0 ); - u.by.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.by.aRoot, u.by.nRoot, - (int)u.by.pnErr->u.i, &u.by.nErr); - sqlite3DbFree(db, u.by.aRoot); - u.by.pnErr->u.i -= u.by.nErr; + u.bz.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.bz.aRoot, u.bz.nRoot, + (int)u.bz.pnErr->u.i, &u.bz.nErr); + sqlite3DbFree(db, u.bz.aRoot); + u.bz.pnErr->u.i -= u.bz.nErr; sqlite3VdbeMemSetNull(pIn1); - if( u.by.nErr==0 ){ - assert( u.by.z==0 ); - }else if( u.by.z==0 ){ + if( u.bz.nErr==0 ){ + assert( u.bz.z==0 ); + }else if( u.bz.z==0 ){ goto no_mem; }else{ - sqlite3VdbeMemSetStr(pIn1, u.by.z, -1, SQLITE_UTF8, sqlite3_free); + sqlite3VdbeMemSetStr(pIn1, u.bz.z, -1, SQLITE_UTF8, sqlite3_free); } UPDATE_MAX_BLOBSIZE(pIn1); sqlite3VdbeChangeEncoding(pIn1, encoding); break; } @@ -68581,24 +69807,24 @@ ** Extract the smallest value from boolean index P1 and put that value into ** register P3. Or, if boolean index P1 is initially empty, leave P3 ** unchanged and jump to instruction P2. */ case OP_RowSetRead: { /* jump, in1, out3 */ -#if 0 /* local variables moved into u.bz */ +#if 0 /* local variables moved into u.ca */ i64 val; -#endif /* local variables moved into u.bz */ +#endif /* local variables moved into u.ca */ CHECK_FOR_INTERRUPT; pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_RowSet)==0 - || sqlite3RowSetNext(pIn1->u.pRowSet, &u.bz.val)==0 + || sqlite3RowSetNext(pIn1->u.pRowSet, &u.ca.val)==0 ){ /* The boolean index is empty */ sqlite3VdbeMemSetNull(pIn1); pc = pOp->p2 - 1; }else{ /* A value was pulled from the index */ - sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.bz.val); + sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.ca.val); } break; } /* Opcode: RowSetTest P1 P2 P3 P4 @@ -68623,18 +69849,18 @@ ** inserted, there is no need to search to see if the same value was ** previously inserted as part of set X (only if it was previously ** inserted as part of some other set). */ case OP_RowSetTest: { /* jump, in1, in3 */ -#if 0 /* local variables moved into u.ca */ +#if 0 /* local variables moved into u.cb */ int iSet; int exists; -#endif /* local variables moved into u.ca */ +#endif /* local variables moved into u.cb */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; - u.ca.iSet = pOp->p4.i; + u.cb.iSet = pOp->p4.i; assert( pIn3->flags&MEM_Int ); /* If there is anything other than a rowset object in memory cell P1, ** delete it now and initialize P1 with an empty rowset */ @@ -68642,21 +69868,21 @@ sqlite3VdbeMemSetRowSet(pIn1); if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem; } assert( pOp->p4type==P4_INT32 ); - assert( u.ca.iSet==-1 || u.ca.iSet>=0 ); - if( u.ca.iSet ){ - u.ca.exists = sqlite3RowSetTest(pIn1->u.pRowSet, - (u8)(u.ca.iSet>=0 ? u.ca.iSet & 0xf : 0xff), + assert( u.cb.iSet==-1 || u.cb.iSet>=0 ); + if( u.cb.iSet ){ + u.cb.exists = sqlite3RowSetTest(pIn1->u.pRowSet, + (u8)(u.cb.iSet>=0 ? u.cb.iSet & 0xf : 0xff), pIn3->u.i); - if( u.ca.exists ){ + if( u.cb.exists ){ pc = pOp->p2 - 1; break; } } - if( u.ca.iSet>=0 ){ + if( u.cb.iSet>=0 ){ sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i); } break; } @@ -68675,25 +69901,24 @@ ** memory required by the sub-vdbe at runtime. ** ** P4 is a pointer to the VM containing the trigger program. */ case OP_Program: { /* jump */ -#if 0 /* local variables moved into u.cb */ +#if 0 /* local variables moved into u.cc */ int nMem; /* Number of memory registers for sub-program */ int nByte; /* Bytes of runtime space required for sub-program */ Mem *pRt; /* Register to allocate runtime space */ Mem *pMem; /* Used to iterate through memory cells */ Mem *pEnd; /* Last memory cell in new array */ VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ -#endif /* local variables moved into u.cb */ +#endif /* local variables moved into u.cc */ - u.cb.pProgram = pOp->p4.pProgram; - u.cb.pRt = &aMem[pOp->p3]; - assert( memIsValid(u.cb.pRt) ); - assert( u.cb.pProgram->nOp>0 ); + u.cc.pProgram = pOp->p4.pProgram; + u.cc.pRt = &aMem[pOp->p3]; + assert( u.cc.pProgram->nOp>0 ); /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set ** and cleared by the "PRAGMA recursive_triggers" command is clear). @@ -68703,80 +69928,87 @@ ** SubProgram (if the trigger may be executed with more than one different ** ON CONFLICT algorithm). SubProgram structures associated with a ** single trigger all have the same value for the SubProgram.token ** variable. */ if( pOp->p5 ){ - u.cb.t = u.cb.pProgram->token; - for(u.cb.pFrame=p->pFrame; u.cb.pFrame && u.cb.pFrame->token!=u.cb.t; u.cb.pFrame=u.cb.pFrame->pParent); - if( u.cb.pFrame ) break; + u.cc.t = u.cc.pProgram->token; + for(u.cc.pFrame=p->pFrame; u.cc.pFrame && u.cc.pFrame->token!=u.cc.t; u.cc.pFrame=u.cc.pFrame->pParent); + if( u.cc.pFrame ) break; } if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion"); break; } - /* Register u.cb.pRt is used to store the memory required to save the state + /* Register u.cc.pRt is used to store the memory required to save the state ** of the current program, and the memory required at runtime to execute - ** the trigger program. If this trigger has been fired before, then u.cb.pRt + ** the trigger program. If this trigger has been fired before, then u.cc.pRt ** is already allocated. Otherwise, it must be initialized. */ - if( (u.cb.pRt->flags&MEM_Frame)==0 ){ + if( (u.cc.pRt->flags&MEM_Frame)==0 ){ /* SubProgram.nMem is set to the number of memory cells used by the ** program stored in SubProgram.aOp. As well as these, one memory ** cell is required for each cursor used by the program. Set local - ** variable u.cb.nMem (and later, VdbeFrame.nChildMem) to this value. + ** variable u.cc.nMem (and later, VdbeFrame.nChildMem) to this value. */ - u.cb.nMem = u.cb.pProgram->nMem + u.cb.pProgram->nCsr; - u.cb.nByte = ROUND8(sizeof(VdbeFrame)) - + u.cb.nMem * sizeof(Mem) - + u.cb.pProgram->nCsr * sizeof(VdbeCursor *); - u.cb.pFrame = sqlite3DbMallocZero(db, u.cb.nByte); - if( !u.cb.pFrame ){ + u.cc.nMem = u.cc.pProgram->nMem + u.cc.pProgram->nCsr; + u.cc.nByte = ROUND8(sizeof(VdbeFrame)) + + u.cc.nMem * sizeof(Mem) + + u.cc.pProgram->nCsr * sizeof(VdbeCursor *) + + u.cc.pProgram->nOnce * sizeof(u8); + u.cc.pFrame = sqlite3DbMallocZero(db, u.cc.nByte); + if( !u.cc.pFrame ){ goto no_mem; } - sqlite3VdbeMemRelease(u.cb.pRt); - u.cb.pRt->flags = MEM_Frame; - u.cb.pRt->u.pFrame = u.cb.pFrame; - - u.cb.pFrame->v = p; - u.cb.pFrame->nChildMem = u.cb.nMem; - u.cb.pFrame->nChildCsr = u.cb.pProgram->nCsr; - u.cb.pFrame->pc = pc; - u.cb.pFrame->aMem = p->aMem; - u.cb.pFrame->nMem = p->nMem; - u.cb.pFrame->apCsr = p->apCsr; - u.cb.pFrame->nCursor = p->nCursor; - u.cb.pFrame->aOp = p->aOp; - u.cb.pFrame->nOp = p->nOp; - u.cb.pFrame->token = u.cb.pProgram->token; - - u.cb.pEnd = &VdbeFrameMem(u.cb.pFrame)[u.cb.pFrame->nChildMem]; - for(u.cb.pMem=VdbeFrameMem(u.cb.pFrame); u.cb.pMem!=u.cb.pEnd; u.cb.pMem++){ - u.cb.pMem->flags = MEM_Null; - u.cb.pMem->db = db; + sqlite3VdbeMemRelease(u.cc.pRt); + u.cc.pRt->flags = MEM_Frame; + u.cc.pRt->u.pFrame = u.cc.pFrame; + + u.cc.pFrame->v = p; + u.cc.pFrame->nChildMem = u.cc.nMem; + u.cc.pFrame->nChildCsr = u.cc.pProgram->nCsr; + u.cc.pFrame->pc = pc; + u.cc.pFrame->aMem = p->aMem; + u.cc.pFrame->nMem = p->nMem; + u.cc.pFrame->apCsr = p->apCsr; + u.cc.pFrame->nCursor = p->nCursor; + u.cc.pFrame->aOp = p->aOp; + u.cc.pFrame->nOp = p->nOp; + u.cc.pFrame->token = u.cc.pProgram->token; + u.cc.pFrame->aOnceFlag = p->aOnceFlag; + u.cc.pFrame->nOnceFlag = p->nOnceFlag; + + u.cc.pEnd = &VdbeFrameMem(u.cc.pFrame)[u.cc.pFrame->nChildMem]; + for(u.cc.pMem=VdbeFrameMem(u.cc.pFrame); u.cc.pMem!=u.cc.pEnd; u.cc.pMem++){ + u.cc.pMem->flags = MEM_Invalid; + u.cc.pMem->db = db; } }else{ - u.cb.pFrame = u.cb.pRt->u.pFrame; - assert( u.cb.pProgram->nMem+u.cb.pProgram->nCsr==u.cb.pFrame->nChildMem ); - assert( u.cb.pProgram->nCsr==u.cb.pFrame->nChildCsr ); - assert( pc==u.cb.pFrame->pc ); + u.cc.pFrame = u.cc.pRt->u.pFrame; + assert( u.cc.pProgram->nMem+u.cc.pProgram->nCsr==u.cc.pFrame->nChildMem ); + assert( u.cc.pProgram->nCsr==u.cc.pFrame->nChildCsr ); + assert( pc==u.cc.pFrame->pc ); } p->nFrame++; - u.cb.pFrame->pParent = p->pFrame; - u.cb.pFrame->lastRowid = lastRowid; - u.cb.pFrame->nChange = p->nChange; + u.cc.pFrame->pParent = p->pFrame; + u.cc.pFrame->lastRowid = lastRowid; + u.cc.pFrame->nChange = p->nChange; p->nChange = 0; - p->pFrame = u.cb.pFrame; - p->aMem = aMem = &VdbeFrameMem(u.cb.pFrame)[-1]; - p->nMem = u.cb.pFrame->nChildMem; - p->nCursor = (u16)u.cb.pFrame->nChildCsr; + p->pFrame = u.cc.pFrame; + p->aMem = aMem = &VdbeFrameMem(u.cc.pFrame)[-1]; + p->nMem = u.cc.pFrame->nChildMem; + p->nCursor = (u16)u.cc.pFrame->nChildCsr; p->apCsr = (VdbeCursor **)&aMem[p->nMem+1]; - p->aOp = aOp = u.cb.pProgram->aOp; - p->nOp = u.cb.pProgram->nOp; + p->aOp = aOp = u.cc.pProgram->aOp; + p->nOp = u.cc.pProgram->nOp; + p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor]; + p->nOnceFlag = u.cc.pProgram->nOnce; + p->nOp = u.cc.pProgram->nOp; pc = -1; + memset(p->aOnceFlag, 0, p->nOnceFlag); break; } /* Opcode: Param P1 P2 * * * @@ -68790,17 +70022,17 @@ ** The address of the cell in the parent frame is determined by adding ** the value of the P1 argument to the value of the P1 argument to the ** calling OP_Program instruction. */ case OP_Param: { /* out2-prerelease */ -#if 0 /* local variables moved into u.cc */ +#if 0 /* local variables moved into u.cd */ VdbeFrame *pFrame; Mem *pIn; -#endif /* local variables moved into u.cc */ - u.cc.pFrame = p->pFrame; - u.cc.pIn = &u.cc.pFrame->aMem[pOp->p1 + u.cc.pFrame->aOp[u.cc.pFrame->pc].p1]; - sqlite3VdbeMemShallowCopy(pOut, u.cc.pIn, MEM_Ephem); +#endif /* local variables moved into u.cd */ + u.cd.pFrame = p->pFrame; + u.cd.pIn = &u.cd.pFrame->aMem[pOp->p1 + u.cd.pFrame->aOp[u.cd.pFrame->pc].p1]; + sqlite3VdbeMemShallowCopy(pOut, u.cd.pIn, MEM_Ephem); break; } #endif /* #ifndef SQLITE_OMIT_TRIGGER */ @@ -68852,26 +70084,26 @@ ** ** This instruction throws an error if the memory cell is not initially ** an integer. */ case OP_MemMax: { /* in2 */ -#if 0 /* local variables moved into u.cd */ +#if 0 /* local variables moved into u.ce */ Mem *pIn1; VdbeFrame *pFrame; -#endif /* local variables moved into u.cd */ +#endif /* local variables moved into u.ce */ if( p->pFrame ){ - for(u.cd.pFrame=p->pFrame; u.cd.pFrame->pParent; u.cd.pFrame=u.cd.pFrame->pParent); - u.cd.pIn1 = &u.cd.pFrame->aMem[pOp->p1]; + for(u.ce.pFrame=p->pFrame; u.ce.pFrame->pParent; u.ce.pFrame=u.ce.pFrame->pParent); + u.ce.pIn1 = &u.ce.pFrame->aMem[pOp->p1]; }else{ - u.cd.pIn1 = &aMem[pOp->p1]; + u.ce.pIn1 = &aMem[pOp->p1]; } - assert( memIsValid(u.cd.pIn1) ); - sqlite3VdbeMemIntegerify(u.cd.pIn1); + assert( memIsValid(u.ce.pIn1) ); + sqlite3VdbeMemIntegerify(u.ce.pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); - if( u.cd.pIn1->u.iu.i){ - u.cd.pIn1->u.i = pIn2->u.i; + if( u.ce.pIn1->u.iu.i){ + u.ce.pIn1->u.i = pIn2->u.i; } break; } #endif /* SQLITE_OMIT_AUTOINCREMENT */ @@ -68934,54 +70166,54 @@ ** ** The P5 arguments are taken from register P2 and its ** successors. */ case OP_AggStep: { -#if 0 /* local variables moved into u.ce */ +#if 0 /* local variables moved into u.cf */ int n; int i; Mem *pMem; Mem *pRec; sqlite3_context ctx; sqlite3_value **apVal; -#endif /* local variables moved into u.ce */ - - u.ce.n = pOp->p5; - assert( u.ce.n>=0 ); - u.ce.pRec = &aMem[pOp->p2]; - u.ce.apVal = p->apArg; - assert( u.ce.apVal || u.ce.n==0 ); - for(u.ce.i=0; u.ce.ip4.pFunc; +#endif /* local variables moved into u.cf */ + + u.cf.n = pOp->p5; + assert( u.cf.n>=0 ); + u.cf.pRec = &aMem[pOp->p2]; + u.cf.apVal = p->apArg; + assert( u.cf.apVal || u.cf.n==0 ); + for(u.cf.i=0; u.cf.ip4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.ce.ctx.pMem = u.ce.pMem = &aMem[pOp->p3]; - u.ce.pMem->n++; - u.ce.ctx.s.flags = MEM_Null; - u.ce.ctx.s.z = 0; - u.ce.ctx.s.zMalloc = 0; - u.ce.ctx.s.xDel = 0; - u.ce.ctx.s.db = db; - u.ce.ctx.isError = 0; - u.ce.ctx.pColl = 0; - if( u.ce.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ + u.cf.ctx.pMem = u.cf.pMem = &aMem[pOp->p3]; + u.cf.pMem->n++; + u.cf.ctx.s.flags = MEM_Null; + u.cf.ctx.s.z = 0; + u.cf.ctx.s.zMalloc = 0; + u.cf.ctx.s.xDel = 0; + u.cf.ctx.s.db = db; + u.cf.ctx.isError = 0; + u.cf.ctx.pColl = 0; + if( u.cf.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); - u.ce.ctx.pColl = pOp[-1].p4.pColl; + u.cf.ctx.pColl = pOp[-1].p4.pColl; } - (u.ce.ctx.pFunc->xStep)(&u.ce.ctx, u.ce.n, u.ce.apVal); /* IMP: R-24505-23230 */ - if( u.ce.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ce.ctx.s)); - rc = u.ce.ctx.isError; + (u.cf.ctx.pFunc->xStep)(&u.cf.ctx, u.cf.n, u.cf.apVal); /* IMP: R-24505-23230 */ + if( u.cf.ctx.isError ){ + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cf.ctx.s)); + rc = u.cf.ctx.isError; } - sqlite3VdbeMemRelease(&u.ce.ctx.s); + sqlite3VdbeMemRelease(&u.cf.ctx.s); break; } /* Opcode: AggFinal P1 P2 * P4 * @@ -68995,23 +70227,23 @@ ** functions that can take varying numbers of arguments. The ** P4 argument is only needed for the degenerate case where ** the step function was not previously called. */ case OP_AggFinal: { -#if 0 /* local variables moved into u.cf */ +#if 0 /* local variables moved into u.cg */ Mem *pMem; -#endif /* local variables moved into u.cf */ +#endif /* local variables moved into u.cg */ assert( pOp->p1>0 && pOp->p1<=p->nMem ); - u.cf.pMem = &aMem[pOp->p1]; - assert( (u.cf.pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); - rc = sqlite3VdbeMemFinalize(u.cf.pMem, pOp->p4.pFunc); + u.cg.pMem = &aMem[pOp->p1]; + assert( (u.cg.pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); + rc = sqlite3VdbeMemFinalize(u.cg.pMem, pOp->p4.pFunc); if( rc ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cf.pMem)); + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cg.pMem)); } - sqlite3VdbeChangeEncoding(u.cf.pMem, encoding); - UPDATE_MAX_BLOBSIZE(u.cf.pMem); - if( sqlite3VdbeMemTooBig(u.cf.pMem) ){ + sqlite3VdbeChangeEncoding(u.cg.pMem, encoding); + UPDATE_MAX_BLOBSIZE(u.cg.pMem); + if( sqlite3VdbeMemTooBig(u.cg.pMem) ){ goto too_big; } break; } @@ -69026,29 +70258,29 @@ ** in the WAL that have been checkpointed after the checkpoint ** completes into mem[P3+2]. However on an error, mem[P3+1] and ** mem[P3+2] are initialized to -1. */ case OP_Checkpoint: { -#if 0 /* local variables moved into u.cg */ +#if 0 /* local variables moved into u.ch */ int i; /* Loop counter */ int aRes[3]; /* Results */ Mem *pMem; /* Write results here */ -#endif /* local variables moved into u.cg */ +#endif /* local variables moved into u.ch */ - u.cg.aRes[0] = 0; - u.cg.aRes[1] = u.cg.aRes[2] = -1; + u.ch.aRes[0] = 0; + u.ch.aRes[1] = u.ch.aRes[2] = -1; assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE || pOp->p2==SQLITE_CHECKPOINT_FULL || pOp->p2==SQLITE_CHECKPOINT_RESTART ); - rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.cg.aRes[1], &u.cg.aRes[2]); + rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.ch.aRes[1], &u.ch.aRes[2]); if( rc==SQLITE_BUSY ){ rc = SQLITE_OK; - u.cg.aRes[0] = 1; + u.ch.aRes[0] = 1; } - for(u.cg.i=0, u.cg.pMem = &aMem[pOp->p3]; u.cg.i<3; u.cg.i++, u.cg.pMem++){ - sqlite3VdbeMemSetInt64(u.cg.pMem, (i64)u.cg.aRes[u.cg.i]); + for(u.ch.i=0, u.ch.pMem = &aMem[pOp->p3]; u.ch.i<3; u.ch.i++, u.ch.pMem++){ + sqlite3VdbeMemSetInt64(u.ch.pMem, (i64)u.ch.aRes[u.ch.i]); } break; }; #endif @@ -69063,95 +70295,95 @@ ** If changing into or out of WAL mode the procedure is more complicated. ** ** Write a string containing the final journal-mode to register P2. */ case OP_JournalMode: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ch */ +#if 0 /* local variables moved into u.ci */ Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ int eOld; /* The old journal mode */ const char *zFilename; /* Name of database file for pPager */ -#endif /* local variables moved into u.ch */ - - u.ch.eNew = pOp->p3; - assert( u.ch.eNew==PAGER_JOURNALMODE_DELETE - || u.ch.eNew==PAGER_JOURNALMODE_TRUNCATE - || u.ch.eNew==PAGER_JOURNALMODE_PERSIST - || u.ch.eNew==PAGER_JOURNALMODE_OFF - || u.ch.eNew==PAGER_JOURNALMODE_MEMORY - || u.ch.eNew==PAGER_JOURNALMODE_WAL - || u.ch.eNew==PAGER_JOURNALMODE_QUERY +#endif /* local variables moved into u.ci */ + + u.ci.eNew = pOp->p3; + assert( u.ci.eNew==PAGER_JOURNALMODE_DELETE + || u.ci.eNew==PAGER_JOURNALMODE_TRUNCATE + || u.ci.eNew==PAGER_JOURNALMODE_PERSIST + || u.ci.eNew==PAGER_JOURNALMODE_OFF + || u.ci.eNew==PAGER_JOURNALMODE_MEMORY + || u.ci.eNew==PAGER_JOURNALMODE_WAL + || u.ci.eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1nDb ); - u.ch.pBt = db->aDb[pOp->p1].pBt; - u.ch.pPager = sqlite3BtreePager(u.ch.pBt); - u.ch.eOld = sqlite3PagerGetJournalMode(u.ch.pPager); - if( u.ch.eNew==PAGER_JOURNALMODE_QUERY ) u.ch.eNew = u.ch.eOld; - if( !sqlite3PagerOkToChangeJournalMode(u.ch.pPager) ) u.ch.eNew = u.ch.eOld; + u.ci.pBt = db->aDb[pOp->p1].pBt; + u.ci.pPager = sqlite3BtreePager(u.ci.pBt); + u.ci.eOld = sqlite3PagerGetJournalMode(u.ci.pPager); + if( u.ci.eNew==PAGER_JOURNALMODE_QUERY ) u.ci.eNew = u.ci.eOld; + if( !sqlite3PagerOkToChangeJournalMode(u.ci.pPager) ) u.ci.eNew = u.ci.eOld; #ifndef SQLITE_OMIT_WAL - u.ch.zFilename = sqlite3PagerFilename(u.ch.pPager); + u.ci.zFilename = sqlite3PagerFilename(u.ci.pPager); /* Do not allow a transition to journal_mode=WAL for a database ** in temporary storage or if the VFS does not support shared memory */ - if( u.ch.eNew==PAGER_JOURNALMODE_WAL - && (u.ch.zFilename[0]==0 /* Temp file */ - || !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */ + if( u.ci.eNew==PAGER_JOURNALMODE_WAL + && (sqlite3Strlen30(u.ci.zFilename)==0 /* Temp file */ + || !sqlite3PagerWalSupported(u.ci.pPager)) /* No shared-memory support */ ){ - u.ch.eNew = u.ch.eOld; + u.ci.eNew = u.ci.eOld; } - if( (u.ch.eNew!=u.ch.eOld) - && (u.ch.eOld==PAGER_JOURNALMODE_WAL || u.ch.eNew==PAGER_JOURNALMODE_WAL) + if( (u.ci.eNew!=u.ci.eOld) + && (u.ci.eOld==PAGER_JOURNALMODE_WAL || u.ci.eNew==PAGER_JOURNALMODE_WAL) ){ if( !db->autoCommit || db->activeVdbeCnt>1 ){ rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "cannot change %s wal mode from within a transaction", - (u.ch.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") + (u.ci.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") ); break; }else{ - if( u.ch.eOld==PAGER_JOURNALMODE_WAL ){ + if( u.ci.eOld==PAGER_JOURNALMODE_WAL ){ /* If leaving WAL mode, close the log file. If successful, the call ** to PagerCloseWal() checkpoints and deletes the write-ahead-log ** file. An EXCLUSIVE lock may still be held on the database file ** after a successful return. */ - rc = sqlite3PagerCloseWal(u.ch.pPager); + rc = sqlite3PagerCloseWal(u.ci.pPager); if( rc==SQLITE_OK ){ - sqlite3PagerSetJournalMode(u.ch.pPager, u.ch.eNew); + sqlite3PagerSetJournalMode(u.ci.pPager, u.ci.eNew); } - }else if( u.ch.eOld==PAGER_JOURNALMODE_MEMORY ){ + }else if( u.ci.eOld==PAGER_JOURNALMODE_MEMORY ){ /* Cannot transition directly from MEMORY to WAL. Use mode OFF ** as an intermediate */ - sqlite3PagerSetJournalMode(u.ch.pPager, PAGER_JOURNALMODE_OFF); + sqlite3PagerSetJournalMode(u.ci.pPager, PAGER_JOURNALMODE_OFF); } /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ - assert( sqlite3BtreeIsInTrans(u.ch.pBt)==0 ); + assert( sqlite3BtreeIsInTrans(u.ci.pBt)==0 ); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSetVersion(u.ch.pBt, (u.ch.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + rc = sqlite3BtreeSetVersion(u.ci.pBt, (u.ci.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); } } } #endif /* ifndef SQLITE_OMIT_WAL */ if( rc ){ - u.ch.eNew = u.ch.eOld; + u.ci.eNew = u.ci.eOld; } - u.ch.eNew = sqlite3PagerSetJournalMode(u.ch.pPager, u.ch.eNew); + u.ci.eNew = sqlite3PagerSetJournalMode(u.ci.pPager, u.ci.eNew); pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = (char *)sqlite3JournalModename(u.ch.eNew); + pOut->z = (char *)sqlite3JournalModename(u.ci.eNew); pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); break; }; @@ -69176,18 +70408,18 @@ ** Perform a single step of the incremental vacuum procedure on ** the P1 database. If the vacuum has finished, jump to instruction ** P2. Otherwise, fall through to the next instruction. */ case OP_IncrVacuum: { /* jump */ -#if 0 /* local variables moved into u.ci */ +#if 0 /* local variables moved into u.cj */ Btree *pBt; -#endif /* local variables moved into u.ci */ +#endif /* local variables moved into u.cj */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.ci.pBt = db->aDb[pOp->p1].pBt; - rc = sqlite3BtreeIncrVacuum(u.ci.pBt); + u.cj.pBt = db->aDb[pOp->p1].pBt; + rc = sqlite3BtreeIncrVacuum(u.cj.pBt); if( rc==SQLITE_DONE ){ pc = pOp->p2 - 1; rc = SQLITE_OK; } break; @@ -69253,16 +70485,16 @@ ** Also, whether or not P4 is set, check that this is not being called from ** within a callback to a virtual table xSync() method. If it is, the error ** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { -#if 0 /* local variables moved into u.cj */ +#if 0 /* local variables moved into u.ck */ VTable *pVTab; -#endif /* local variables moved into u.cj */ - u.cj.pVTab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, u.cj.pVTab); - if( u.cj.pVTab ) importVtabErrMsg(p, u.cj.pVTab->pVtab); +#endif /* local variables moved into u.ck */ + u.ck.pVTab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, u.ck.pVTab); + if( u.ck.pVTab ) importVtabErrMsg(p, u.ck.pVTab->pVtab); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -69297,36 +70529,36 @@ ** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. ** P1 is a cursor number. This opcode opens a cursor to the virtual ** table and stores that cursor in P1. */ case OP_VOpen: { -#if 0 /* local variables moved into u.ck */ +#if 0 /* local variables moved into u.cl */ VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; -#endif /* local variables moved into u.ck */ - - u.ck.pCur = 0; - u.ck.pVtabCursor = 0; - u.ck.pVtab = pOp->p4.pVtab->pVtab; - u.ck.pModule = (sqlite3_module *)u.ck.pVtab->pModule; - assert(u.ck.pVtab && u.ck.pModule); - rc = u.ck.pModule->xOpen(u.ck.pVtab, &u.ck.pVtabCursor); - importVtabErrMsg(p, u.ck.pVtab); +#endif /* local variables moved into u.cl */ + + u.cl.pCur = 0; + u.cl.pVtabCursor = 0; + u.cl.pVtab = pOp->p4.pVtab->pVtab; + u.cl.pModule = (sqlite3_module *)u.cl.pVtab->pModule; + assert(u.cl.pVtab && u.cl.pModule); + rc = u.cl.pModule->xOpen(u.cl.pVtab, &u.cl.pVtabCursor); + importVtabErrMsg(p, u.cl.pVtab); if( SQLITE_OK==rc ){ /* Initialize sqlite3_vtab_cursor base class */ - u.ck.pVtabCursor->pVtab = u.ck.pVtab; + u.cl.pVtabCursor->pVtab = u.cl.pVtab; /* Initialise vdbe cursor object */ - u.ck.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); - if( u.ck.pCur ){ - u.ck.pCur->pVtabCursor = u.ck.pVtabCursor; - u.ck.pCur->pModule = u.ck.pVtabCursor->pVtab->pModule; + u.cl.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); + if( u.cl.pCur ){ + u.cl.pCur->pVtabCursor = u.cl.pVtabCursor; + u.cl.pCur->pModule = u.cl.pVtabCursor->pVtab->pModule; }else{ db->mallocFailed = 1; - u.ck.pModule->xClose(u.ck.pVtabCursor); + u.cl.pModule->xClose(u.cl.pVtabCursor); } } break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -69349,11 +70581,11 @@ ** xFilter as argv. Register P3+2 becomes argv[0] when passed to xFilter. ** ** A jump is made to P2 if the result set after filtering would be empty. */ case OP_VFilter: { /* jump */ -#if 0 /* local variables moved into u.cl */ +#if 0 /* local variables moved into u.cm */ int nArg; int iQuery; const sqlite3_module *pModule; Mem *pQuery; Mem *pArgc; @@ -69361,49 +70593,49 @@ sqlite3_vtab *pVtab; VdbeCursor *pCur; int res; int i; Mem **apArg; -#endif /* local variables moved into u.cl */ - - u.cl.pQuery = &aMem[pOp->p3]; - u.cl.pArgc = &u.cl.pQuery[1]; - u.cl.pCur = p->apCsr[pOp->p1]; - assert( memIsValid(u.cl.pQuery) ); - REGISTER_TRACE(pOp->p3, u.cl.pQuery); - assert( u.cl.pCur->pVtabCursor ); - u.cl.pVtabCursor = u.cl.pCur->pVtabCursor; - u.cl.pVtab = u.cl.pVtabCursor->pVtab; - u.cl.pModule = u.cl.pVtab->pModule; +#endif /* local variables moved into u.cm */ + + u.cm.pQuery = &aMem[pOp->p3]; + u.cm.pArgc = &u.cm.pQuery[1]; + u.cm.pCur = p->apCsr[pOp->p1]; + assert( memIsValid(u.cm.pQuery) ); + REGISTER_TRACE(pOp->p3, u.cm.pQuery); + assert( u.cm.pCur->pVtabCursor ); + u.cm.pVtabCursor = u.cm.pCur->pVtabCursor; + u.cm.pVtab = u.cm.pVtabCursor->pVtab; + u.cm.pModule = u.cm.pVtab->pModule; /* Grab the index number and argc parameters */ - assert( (u.cl.pQuery->flags&MEM_Int)!=0 && u.cl.pArgc->flags==MEM_Int ); - u.cl.nArg = (int)u.cl.pArgc->u.i; - u.cl.iQuery = (int)u.cl.pQuery->u.i; + assert( (u.cm.pQuery->flags&MEM_Int)!=0 && u.cm.pArgc->flags==MEM_Int ); + u.cm.nArg = (int)u.cm.pArgc->u.i; + u.cm.iQuery = (int)u.cm.pQuery->u.i; /* Invoke the xFilter method */ { - u.cl.res = 0; - u.cl.apArg = p->apArg; - for(u.cl.i = 0; u.cl.iapArg; + for(u.cm.i = 0; u.cm.iinVtabMethod = 1; - rc = u.cl.pModule->xFilter(u.cl.pVtabCursor, u.cl.iQuery, pOp->p4.z, u.cl.nArg, u.cl.apArg); + rc = u.cm.pModule->xFilter(u.cm.pVtabCursor, u.cm.iQuery, pOp->p4.z, u.cm.nArg, u.cm.apArg); p->inVtabMethod = 0; - importVtabErrMsg(p, u.cl.pVtab); + importVtabErrMsg(p, u.cm.pVtab); if( rc==SQLITE_OK ){ - u.cl.res = u.cl.pModule->xEof(u.cl.pVtabCursor); + u.cm.res = u.cm.pModule->xEof(u.cm.pVtabCursor); } - if( u.cl.res ){ + if( u.cm.res ){ pc = pOp->p2 - 1; } } - u.cl.pCur->nullRow = 0; + u.cm.pCur->nullRow = 0; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -69413,55 +70645,55 @@ ** Store the value of the P2-th column of ** the row of the virtual-table that the ** P1 cursor is pointing to into register P3. */ case OP_VColumn: { -#if 0 /* local variables moved into u.cm */ +#if 0 /* local variables moved into u.cn */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; -#endif /* local variables moved into u.cm */ +#endif /* local variables moved into u.cn */ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.cm.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.cm.pDest); + u.cn.pDest = &aMem[pOp->p3]; + memAboutToChange(p, u.cn.pDest); if( pCur->nullRow ){ - sqlite3VdbeMemSetNull(u.cm.pDest); + sqlite3VdbeMemSetNull(u.cn.pDest); break; } - u.cm.pVtab = pCur->pVtabCursor->pVtab; - u.cm.pModule = u.cm.pVtab->pModule; - assert( u.cm.pModule->xColumn ); - memset(&u.cm.sContext, 0, sizeof(u.cm.sContext)); + u.cn.pVtab = pCur->pVtabCursor->pVtab; + u.cn.pModule = u.cn.pVtab->pModule; + assert( u.cn.pModule->xColumn ); + memset(&u.cn.sContext, 0, sizeof(u.cn.sContext)); /* The output cell may already have a buffer allocated. Move - ** the current contents to u.cm.sContext.s so in case the user-function + ** the current contents to u.cn.sContext.s so in case the user-function ** can use the already allocated buffer instead of allocating a ** new one. */ - sqlite3VdbeMemMove(&u.cm.sContext.s, u.cm.pDest); - MemSetTypeFlag(&u.cm.sContext.s, MEM_Null); + sqlite3VdbeMemMove(&u.cn.sContext.s, u.cn.pDest); + MemSetTypeFlag(&u.cn.sContext.s, MEM_Null); - rc = u.cm.pModule->xColumn(pCur->pVtabCursor, &u.cm.sContext, pOp->p2); - importVtabErrMsg(p, u.cm.pVtab); - if( u.cm.sContext.isError ){ - rc = u.cm.sContext.isError; + rc = u.cn.pModule->xColumn(pCur->pVtabCursor, &u.cn.sContext, pOp->p2); + importVtabErrMsg(p, u.cn.pVtab); + if( u.cn.sContext.isError ){ + rc = u.cn.sContext.isError; } /* Copy the result of the function to the P3 register. We ** do this regardless of whether or not an error occurred to ensure any - ** dynamic allocation in u.cm.sContext.s (a Mem struct) is released. + ** dynamic allocation in u.cn.sContext.s (a Mem struct) is released. */ - sqlite3VdbeChangeEncoding(&u.cm.sContext.s, encoding); - sqlite3VdbeMemMove(u.cm.pDest, &u.cm.sContext.s); - REGISTER_TRACE(pOp->p3, u.cm.pDest); - UPDATE_MAX_BLOBSIZE(u.cm.pDest); + sqlite3VdbeChangeEncoding(&u.cn.sContext.s, encoding); + sqlite3VdbeMemMove(u.cn.pDest, &u.cn.sContext.s); + REGISTER_TRACE(pOp->p3, u.cn.pDest); + UPDATE_MAX_BLOBSIZE(u.cn.pDest); - if( sqlite3VdbeMemTooBig(u.cm.pDest) ){ + if( sqlite3VdbeMemTooBig(u.cn.pDest) ){ goto too_big; } break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -69472,42 +70704,42 @@ ** Advance virtual table P1 to the next row in its result set and ** jump to instruction P2. Or, if the virtual table has reached ** the end of its result set, then fall through to the next instruction. */ case OP_VNext: { /* jump */ -#if 0 /* local variables moved into u.cn */ +#if 0 /* local variables moved into u.co */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; -#endif /* local variables moved into u.cn */ +#endif /* local variables moved into u.co */ - u.cn.res = 0; - u.cn.pCur = p->apCsr[pOp->p1]; - assert( u.cn.pCur->pVtabCursor ); - if( u.cn.pCur->nullRow ){ + u.co.res = 0; + u.co.pCur = p->apCsr[pOp->p1]; + assert( u.co.pCur->pVtabCursor ); + if( u.co.pCur->nullRow ){ break; } - u.cn.pVtab = u.cn.pCur->pVtabCursor->pVtab; - u.cn.pModule = u.cn.pVtab->pModule; - assert( u.cn.pModule->xNext ); + u.co.pVtab = u.co.pCur->pVtabCursor->pVtab; + u.co.pModule = u.co.pVtab->pModule; + assert( u.co.pModule->xNext ); /* Invoke the xNext() method of the module. There is no way for the ** underlying implementation to return an error if one occurs during ** xNext(). Instead, if an error occurs, true is returned (indicating that ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ p->inVtabMethod = 1; - rc = u.cn.pModule->xNext(u.cn.pCur->pVtabCursor); + rc = u.co.pModule->xNext(u.co.pCur->pVtabCursor); p->inVtabMethod = 0; - importVtabErrMsg(p, u.cn.pVtab); + importVtabErrMsg(p, u.co.pVtab); if( rc==SQLITE_OK ){ - u.cn.res = u.cn.pModule->xEof(u.cn.pCur->pVtabCursor); + u.co.res = u.co.pModule->xEof(u.co.pCur->pVtabCursor); } - if( !u.cn.res ){ + if( !u.co.res ){ /* If there is data, jump to P2 */ pc = pOp->p2 - 1; } break; } @@ -69519,25 +70751,30 @@ ** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. ** This opcode invokes the corresponding xRename method. The value ** in register P1 is passed as the zName argument to the xRename method. */ case OP_VRename: { -#if 0 /* local variables moved into u.co */ +#if 0 /* local variables moved into u.cp */ sqlite3_vtab *pVtab; Mem *pName; -#endif /* local variables moved into u.co */ - - u.co.pVtab = pOp->p4.pVtab->pVtab; - u.co.pName = &aMem[pOp->p1]; - assert( u.co.pVtab->pModule->xRename ); - assert( memIsValid(u.co.pName) ); - REGISTER_TRACE(pOp->p1, u.co.pName); - assert( u.co.pName->flags & MEM_Str ); - rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z); - importVtabErrMsg(p, u.co.pVtab); - p->expired = 0; - +#endif /* local variables moved into u.cp */ + + u.cp.pVtab = pOp->p4.pVtab->pVtab; + u.cp.pName = &aMem[pOp->p1]; + assert( u.cp.pVtab->pModule->xRename ); + assert( memIsValid(u.cp.pName) ); + REGISTER_TRACE(pOp->p1, u.cp.pName); + assert( u.cp.pName->flags & MEM_Str ); + testcase( u.cp.pName->enc==SQLITE_UTF8 ); + testcase( u.cp.pName->enc==SQLITE_UTF16BE ); + testcase( u.cp.pName->enc==SQLITE_UTF16LE ); + rc = sqlite3VdbeChangeEncoding(u.cp.pName, SQLITE_UTF8); + if( rc==SQLITE_OK ){ + rc = u.cp.pVtab->pModule->xRename(u.cp.pVtab, u.cp.pName->z); + importVtabErrMsg(p, u.cp.pVtab); + p->expired = 0; + } break; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -69563,45 +70800,45 @@ ** P1 is a boolean flag. If it is set to true and the xUpdate call ** is successful, then the value returned by sqlite3_last_insert_rowid() ** is set to the value of the rowid for the row just inserted. */ case OP_VUpdate: { -#if 0 /* local variables moved into u.cp */ +#if 0 /* local variables moved into u.cq */ sqlite3_vtab *pVtab; sqlite3_module *pModule; int nArg; int i; sqlite_int64 rowid; Mem **apArg; Mem *pX; -#endif /* local variables moved into u.cp */ +#endif /* local variables moved into u.cq */ assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace ); - u.cp.pVtab = pOp->p4.pVtab->pVtab; - u.cp.pModule = (sqlite3_module *)u.cp.pVtab->pModule; - u.cp.nArg = pOp->p2; + u.cq.pVtab = pOp->p4.pVtab->pVtab; + u.cq.pModule = (sqlite3_module *)u.cq.pVtab->pModule; + u.cq.nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); - if( ALWAYS(u.cp.pModule->xUpdate) ){ + if( ALWAYS(u.cq.pModule->xUpdate) ){ u8 vtabOnConflict = db->vtabOnConflict; - u.cp.apArg = p->apArg; - u.cp.pX = &aMem[pOp->p3]; - for(u.cp.i=0; u.cp.iapArg; + u.cq.pX = &aMem[pOp->p3]; + for(u.cq.i=0; u.cq.ivtabOnConflict = pOp->p5; - rc = u.cp.pModule->xUpdate(u.cp.pVtab, u.cp.nArg, u.cp.apArg, &u.cp.rowid); + rc = u.cq.pModule->xUpdate(u.cq.pVtab, u.cq.nArg, u.cq.apArg, &u.cq.rowid); db->vtabOnConflict = vtabOnConflict; - importVtabErrMsg(p, u.cp.pVtab); + importVtabErrMsg(p, u.cq.pVtab); if( rc==SQLITE_OK && pOp->p1 ){ - assert( u.cp.nArg>1 && u.cp.apArg[0] && (u.cp.apArg[0]->flags&MEM_Null) ); - db->lastRowid = lastRowid = u.cp.rowid; + assert( u.cq.nArg>1 && u.cq.apArg[0] && (u.cq.apArg[0]->flags&MEM_Null) ); + db->lastRowid = lastRowid = u.cq.rowid; } if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ if( pOp->p5==OE_Ignore ){ rc = SQLITE_OK; }else{ @@ -69657,25 +70894,25 @@ ** ** If tracing is enabled (by the sqlite3_trace()) interface, then ** the UTF-8 string contained in P4 is emitted on the trace callback. */ case OP_Trace: { -#if 0 /* local variables moved into u.cq */ +#if 0 /* local variables moved into u.cr */ char *zTrace; char *z; -#endif /* local variables moved into u.cq */ +#endif /* local variables moved into u.cr */ - if( db->xTrace && (u.cq.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ - u.cq.z = sqlite3VdbeExpandSql(p, u.cq.zTrace); - db->xTrace(db->pTraceArg, u.cq.z); - sqlite3DbFree(db, u.cq.z); + if( db->xTrace && (u.cr.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ + u.cr.z = sqlite3VdbeExpandSql(p, u.cr.zTrace); + db->xTrace(db->pTraceArg, u.cr.z); + sqlite3DbFree(db, u.cr.z); } #ifdef SQLITE_DEBUG if( (db->flags & SQLITE_SqlTrace)!=0 - && (u.cq.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + && (u.cr.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ - sqlite3DebugPrintf("SQL-trace: %s\n", u.cq.zTrace); + sqlite3DebugPrintf("SQL-trace: %s\n", u.cr.zTrace); } #endif /* SQLITE_DEBUG */ break; } #endif @@ -71891,10 +73128,28 @@ ExprSetProperty(pExpr, EP_Static); sqlite3ExprDelete(db, pExpr); memcpy(pExpr, pDup, sizeof(*pExpr)); sqlite3DbFree(db, pDup); } + + +/* +** Return TRUE if the name zCol occurs anywhere in the USING clause. +** +** Return FALSE if the USING clause is NULL or if it does not contain +** zCol. +*/ +static int nameInUsingClause(IdList *pUsing, const char *zCol){ + if( pUsing ){ + int k; + for(k=0; knId; k++){ + if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; + } + } + return 0; +} + /* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up ** that name in the set of source tables in pSrcList and make the pExpr ** expression node refer back to that source column. The following changes @@ -71983,38 +73238,25 @@ pSchema = pTab->pSchema; pMatch = pItem; } for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - IdList *pUsing; + /* If there has been exactly one prior match and this match + ** is for the right-hand table of a NATURAL JOIN or is in a + ** USING clause, then skip this match. + */ + if( cnt==1 ){ + if( pItem->jointype & JT_NATURAL ) continue; + if( nameInUsingClause(pItem->pUsing, zCol) ) continue; + } cnt++; pExpr->iTable = pItem->iCursor; pExpr->pTab = pTab; pMatch = pItem; pSchema = pTab->pSchema; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; - if( inSrc-1 ){ - if( pItem[1].jointype & JT_NATURAL ){ - /* If this match occurred in the left table of a natural join, - ** then skip the right table to avoid a duplicate match */ - pItem++; - i++; - }else if( (pUsing = pItem[1].pUsing)!=0 ){ - /* If this match occurs on a column that is in the USING clause - ** of a join, skip the search of the right table of the join - ** to avoid a duplicate match there. */ - int k; - for(k=0; knId; k++){ - if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){ - pItem++; - i++; - break; - } - } - } - } break; } } } } @@ -72588,11 +73830,11 @@ pItem->pExpr = pE = sqlite3Expr(db, TK_INTEGER, 0); if( pE==0 ) return 1; pE->pColl = pColl; pE->flags |= EP_IntValue | flags; pE->u.iValue = iCol; - pItem->iCol = (u16)iCol; + pItem->iOrderByCol = (u16)iCol; pItem->done = 1; }else{ moreToDo = 1; } } @@ -72637,16 +73879,16 @@ } #endif pEList = pSelect->pEList; assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */ for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - if( pItem->iCol ){ - if( pItem->iCol>pEList->nExpr ){ + if( pItem->iOrderByCol ){ + if( pItem->iOrderByCol>pEList->nExpr ){ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType); + resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType); } } return 0; } @@ -72689,11 +73931,11 @@ if( iCol>0 ){ /* If an AS-name match is found, mark this ORDER BY column as being ** a copy of the iCol-th result-set column. The subsequent call to ** sqlite3ResolveOrderGroupBy() will convert the expression to a ** copy of the iCol-th result-set expression. */ - pItem->iCol = (u16)iCol; + pItem->iOrderByCol = (u16)iCol; continue; } if( sqlite3ExprIsInteger(pE, &iCol) ){ /* The ORDER BY term is an integer constant. Again, set the column ** number so that sqlite3ResolveOrderGroupBy() will convert the @@ -72700,16 +73942,16 @@ ** order-by term to a copy of the result-set expression */ if( iCol<1 ){ resolveOutOfRangeError(pParse, zType, i+1, nResult); return 1; } - pItem->iCol = (u16)iCol; + pItem->iOrderByCol = (u16)iCol; continue; } /* Otherwise, treat the ORDER BY term as an ordinary expression */ - pItem->iCol = 0; + pItem->iOrderByCol = 0; if( sqlite3ResolveExprNames(pNC, pE) ){ return 1; } } return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType); @@ -73417,11 +74659,12 @@ pNew->flags |= EP_IntValue; pNew->u.iValue = iValue; }else{ int c; pNew->u.zToken = (char*)&pNew[1]; - memcpy(pNew->u.zToken, pToken->z, pToken->n); + assert( pToken->z!=0 || pToken->n==0 ); + if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); pNew->u.zToken[pToken->n] = 0; if( dequote && nExtra>=3 && ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){ sqlite3Dequote(pNew->u.zToken); if( c=='"' ) pNew->flags |= EP_DblQuoted; @@ -73883,11 +75126,11 @@ pItem->pExpr = sqlite3ExprDup(db, pOldExpr, flags); pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan); pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; - pItem->iCol = pOldItem->iCol; + pItem->iOrderByCol = pOldItem->iOrderByCol; pItem->iAlias = pOldItem->iAlias; } return pNew; } @@ -73953,11 +75196,11 @@ pNewItem->idx = pOldItem->idx; } return pNew; } SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ - Select *pNew; + Select *pNew, *pPrior; if( p==0 ) return 0; pNew = sqlite3DbMallocRaw(db, sizeof(*p) ); if( pNew==0 ) return 0; pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags); pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags); @@ -73964,11 +75207,13 @@ pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags); pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags); pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags); pNew->op = p->op; - pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags); + pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags); + if( pPrior ) pPrior->pNext = pNew; + pNew->pNext = 0; pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags); pNew->iLimit = 0; pNew->iOffset = 0; pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; @@ -74385,10 +75630,19 @@ if( pEList->nExpr!=1 ) return 0; /* One column in the result set */ if( pEList->a[0].pExpr->op!=TK_COLUMN ) return 0; /* Result is a column */ return 1; } #endif /* SQLITE_OMIT_SUBQUERY */ + +/* +** Code an OP_Once instruction and allocate space for its flag. Return the +** address of the new instruction. +*/ +SQLITE_PRIVATE int sqlite3CodeOnce(Parse *pParse){ + Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ + return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++); +} /* ** This function is used by the implementation of the IN (...) operator. ** It's job is to find or create a b-tree structure that may be used ** either to test for membership of the (...) set or to iterate through @@ -74446,10 +75700,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ int iTab = pParse->nTab++; /* Cursor of the RHS table */ int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */ + Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ assert( pX->op==TK_IN ); /* Check to see if an existing table or index can be used to ** satisfy the query. This is preferable to generating a new @@ -74456,15 +75711,22 @@ ** ephemeral table. */ p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0); if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){ sqlite3 *db = pParse->db; /* Database connection */ - Expr *pExpr = p->pEList->a[0].pExpr; /* Expression */ - int iCol = pExpr->iColumn; /* Index of column */ - Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ - Table *pTab = p->pSrc->a[0].pTab; /* Table . */ + Table *pTab; /* Table
    . */ + Expr *pExpr; /* Expression */ + int iCol; /* Index of column */ int iDb; /* Database idx for pTab */ + + assert( p ); /* Because of isCandidateForInOpt(p) */ + assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ + assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ + assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ + pTab = p->pSrc->a[0].pTab; + pExpr = p->pEList->a[0].pExpr; + iCol = pExpr->iColumn; /* Code an OP_VerifyCookie and OP_TableLock for
    . */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); @@ -74473,14 +75735,13 @@ ** has already been allocated. So assume sqlite3GetVdbe() is always ** successful here. */ assert(v); if( iCol<0 ){ - int iMem = ++pParse->nMem; int iAddr; - iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem); + iAddr = sqlite3CodeOnce(pParse); sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); eType = IN_INDEX_ROWID; sqlite3VdbeJumpHere(v, iAddr); @@ -74502,25 +75763,25 @@ for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ if( (pIdx->aiColumn[0]==iCol) && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None)) ){ - int iMem = ++pParse->nMem; int iAddr; char *pKey; pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem); + iAddr = sqlite3CodeOnce(pParse); sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, pKey,P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); eType = IN_INDEX_INDEX; sqlite3VdbeJumpHere(v, iAddr); if( prNotFound && !pTab->aCol[iCol].notNull ){ *prNotFound = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); } } } } } @@ -74532,10 +75793,11 @@ double savedNQueryLoop = pParse->nQueryLoop; int rMayHaveNull = 0; eType = IN_INDEX_EPH; if( prNotFound ){ *prNotFound = rMayHaveNull = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); }else{ testcase( pParse->nQueryLoop>(double)1 ); pParse->nQueryLoop = (double)1; if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){ eType = IN_INDEX_ROWID; @@ -74604,13 +75866,12 @@ ** * We are inside a trigger ** ** If all of the above are false, then we can run this code just once ** save the results, and reuse the same result on subsequent invocations. */ - if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ - int mem = ++pParse->nMem; - testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem); + if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){ + testAddr = sqlite3CodeOnce(pParse); } #ifndef SQLITE_OMIT_EXPLAIN if( pParse->explain==2 ){ char *zMsg = sqlite3MPrintf( @@ -75944,10 +77205,268 @@ pExpr->op = TK_REGISTER; } return inReg; } +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) +/* +** Generate a human-readable explanation of an expression tree. +*/ +SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ + int op; /* The opcode being coded */ + const char *zBinOp = 0; /* Binary operator */ + const char *zUniOp = 0; /* Unary operator */ + if( pExpr==0 ){ + op = TK_NULL; + }else{ + op = pExpr->op; + } + switch( op ){ + case TK_AGG_COLUMN: { + sqlite3ExplainPrintf(pOut, "AGG{%d:%d}", + pExpr->iTable, pExpr->iColumn); + break; + } + case TK_COLUMN: { + if( pExpr->iTable<0 ){ + /* This only happens when coding check constraints */ + sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn); + }else{ + sqlite3ExplainPrintf(pOut, "{%d:%d}", + pExpr->iTable, pExpr->iColumn); + } + break; + } + case TK_INTEGER: { + if( pExpr->flags & EP_IntValue ){ + sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue); + }else{ + sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken); + } + break; + } +#ifndef SQLITE_OMIT_FLOATING_POINT + case TK_FLOAT: { + sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken); + break; + } +#endif + case TK_STRING: { + sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken); + break; + } + case TK_NULL: { + sqlite3ExplainPrintf(pOut,"NULL"); + break; + } +#ifndef SQLITE_OMIT_BLOB_LITERAL + case TK_BLOB: { + sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken); + break; + } +#endif + case TK_VARIABLE: { + sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)", + pExpr->u.zToken, pExpr->iColumn); + break; + } + case TK_REGISTER: { + sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable); + break; + } + case TK_AS: { + sqlite3ExplainExpr(pOut, pExpr->pLeft); + break; + } +#ifndef SQLITE_OMIT_CAST + case TK_CAST: { + /* Expressions of the form: CAST(pLeft AS token) */ + const char *zAff = "unk"; + switch( sqlite3AffinityType(pExpr->u.zToken) ){ + case SQLITE_AFF_TEXT: zAff = "TEXT"; break; + case SQLITE_AFF_NONE: zAff = "NONE"; break; + case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break; + case SQLITE_AFF_INTEGER: zAff = "INTEGER"; break; + case SQLITE_AFF_REAL: zAff = "REAL"; break; + } + sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff); + sqlite3ExplainExpr(pOut, pExpr->pLeft); + sqlite3ExplainPrintf(pOut, ")"); + break; + } +#endif /* SQLITE_OMIT_CAST */ + case TK_LT: zBinOp = "LT"; break; + case TK_LE: zBinOp = "LE"; break; + case TK_GT: zBinOp = "GT"; break; + case TK_GE: zBinOp = "GE"; break; + case TK_NE: zBinOp = "NE"; break; + case TK_EQ: zBinOp = "EQ"; break; + case TK_IS: zBinOp = "IS"; break; + case TK_ISNOT: zBinOp = "ISNOT"; break; + case TK_AND: zBinOp = "AND"; break; + case TK_OR: zBinOp = "OR"; break; + case TK_PLUS: zBinOp = "ADD"; break; + case TK_STAR: zBinOp = "MUL"; break; + case TK_MINUS: zBinOp = "SUB"; break; + case TK_REM: zBinOp = "REM"; break; + case TK_BITAND: zBinOp = "BITAND"; break; + case TK_BITOR: zBinOp = "BITOR"; break; + case TK_SLASH: zBinOp = "DIV"; break; + case TK_LSHIFT: zBinOp = "LSHIFT"; break; + case TK_RSHIFT: zBinOp = "RSHIFT"; break; + case TK_CONCAT: zBinOp = "CONCAT"; break; + + case TK_UMINUS: zUniOp = "UMINUS"; break; + case TK_UPLUS: zUniOp = "UPLUS"; break; + case TK_BITNOT: zUniOp = "BITNOT"; break; + case TK_NOT: zUniOp = "NOT"; break; + case TK_ISNULL: zUniOp = "ISNULL"; break; + case TK_NOTNULL: zUniOp = "NOTNULL"; break; + + case TK_AGG_FUNCTION: + case TK_CONST_FUNC: + case TK_FUNCTION: { + ExprList *pFarg; /* List of function arguments */ + if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){ + pFarg = 0; + }else{ + pFarg = pExpr->x.pList; + } + sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(", + op==TK_AGG_FUNCTION ? "AGG_" : "", + pExpr->u.zToken); + if( pFarg ){ + sqlite3ExplainExprList(pOut, pFarg); + } + sqlite3ExplainPrintf(pOut, ")"); + break; + } +#ifndef SQLITE_OMIT_SUBQUERY + case TK_EXISTS: { + sqlite3ExplainPrintf(pOut, "EXISTS("); + sqlite3ExplainSelect(pOut, pExpr->x.pSelect); + sqlite3ExplainPrintf(pOut,")"); + break; + } + case TK_SELECT: { + sqlite3ExplainPrintf(pOut, "("); + sqlite3ExplainSelect(pOut, pExpr->x.pSelect); + sqlite3ExplainPrintf(pOut, ")"); + break; + } + case TK_IN: { + sqlite3ExplainPrintf(pOut, "IN("); + sqlite3ExplainExpr(pOut, pExpr->pLeft); + sqlite3ExplainPrintf(pOut, ","); + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + sqlite3ExplainSelect(pOut, pExpr->x.pSelect); + }else{ + sqlite3ExplainExprList(pOut, pExpr->x.pList); + } + sqlite3ExplainPrintf(pOut, ")"); + break; + } +#endif /* SQLITE_OMIT_SUBQUERY */ + + /* + ** x BETWEEN y AND z + ** + ** This is equivalent to + ** + ** x>=y AND x<=z + ** + ** X is stored in pExpr->pLeft. + ** Y is stored in pExpr->pList->a[0].pExpr. + ** Z is stored in pExpr->pList->a[1].pExpr. + */ + case TK_BETWEEN: { + Expr *pX = pExpr->pLeft; + Expr *pY = pExpr->x.pList->a[0].pExpr; + Expr *pZ = pExpr->x.pList->a[1].pExpr; + sqlite3ExplainPrintf(pOut, "BETWEEN("); + sqlite3ExplainExpr(pOut, pX); + sqlite3ExplainPrintf(pOut, ","); + sqlite3ExplainExpr(pOut, pY); + sqlite3ExplainPrintf(pOut, ","); + sqlite3ExplainExpr(pOut, pZ); + sqlite3ExplainPrintf(pOut, ")"); + break; + } + case TK_TRIGGER: { + /* If the opcode is TK_TRIGGER, then the expression is a reference + ** to a column in the new.* or old.* pseudo-tables available to + ** trigger programs. In this case Expr.iTable is set to 1 for the + ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn + ** is set to the column of the pseudo-table to read, or to -1 to + ** read the rowid field. + */ + sqlite3ExplainPrintf(pOut, "%s(%d)", + pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn); + break; + } + case TK_CASE: { + sqlite3ExplainPrintf(pOut, "CASE("); + sqlite3ExplainExpr(pOut, pExpr->pLeft); + sqlite3ExplainPrintf(pOut, ","); + sqlite3ExplainExprList(pOut, pExpr->x.pList); + break; + } +#ifndef SQLITE_OMIT_TRIGGER + case TK_RAISE: { + const char *zType = "unk"; + switch( pExpr->affinity ){ + case OE_Rollback: zType = "rollback"; break; + case OE_Abort: zType = "abort"; break; + case OE_Fail: zType = "fail"; break; + case OE_Ignore: zType = "ignore"; break; + } + sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken); + break; + } +#endif + } + if( zBinOp ){ + sqlite3ExplainPrintf(pOut,"%s(", zBinOp); + sqlite3ExplainExpr(pOut, pExpr->pLeft); + sqlite3ExplainPrintf(pOut,","); + sqlite3ExplainExpr(pOut, pExpr->pRight); + sqlite3ExplainPrintf(pOut,")"); + }else if( zUniOp ){ + sqlite3ExplainPrintf(pOut,"%s(", zUniOp); + sqlite3ExplainExpr(pOut, pExpr->pLeft); + sqlite3ExplainPrintf(pOut,")"); + } +} +#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */ + +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) +/* +** Generate a human-readable explanation of an expression list. +*/ +SQLITE_PRIVATE void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ + int i; + if( pList==0 || pList->nExpr==0 ){ + sqlite3ExplainPrintf(pOut, "(empty-list)"); + return; + }else if( pList->nExpr==1 ){ + sqlite3ExplainExpr(pOut, pList->a[0].pExpr); + }else{ + sqlite3ExplainPush(pOut); + for(i=0; inExpr; i++){ + sqlite3ExplainPrintf(pOut, "item[%d] = ", i); + sqlite3ExplainPush(pOut); + sqlite3ExplainExpr(pOut, pList->a[i].pExpr); + sqlite3ExplainPop(pOut); + if( inExpr-1 ){ + sqlite3ExplainNL(pOut); + } + } + sqlite3ExplainPop(pOut); + } +} +#endif /* SQLITE_DEBUG */ + /* ** Return TRUE if pExpr is an constant expression that is appropriate ** for factoring out of a loop. Appropriate expressions are: ** ** * Any expression that evaluates to two or more opcodes. @@ -76467,11 +77986,11 @@ if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ return 2; } }else if( pA->op!=TK_COLUMN && pA->u.zToken ){ if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; - if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ){ + if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return 2; } } if( (pA->flags & EP_ExpCollate)!=(pB->flags & EP_ExpCollate) ) return 1; if( (pA->flags & EP_ExpCollate)!=0 && pA->pColl!=pB->pColl ) return 2; @@ -76767,10 +78286,18 @@ if( nReg>pParse->nRangeReg ){ pParse->nRangeReg = nReg; pParse->iRangeReg = iReg; } } + +/* +** Mark all temporary registers as being unavailable for reuse. +*/ +SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){ + pParse->nTempReg = 0; + pParse->nRangeReg = 0; +} /************** End of expr.c ************************************************/ /************** Begin file alter.c *******************************************/ /* ** 2005 February 15 @@ -77610,26 +79137,128 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code associated with the ANALYZE command. +** +** The ANALYZE command gather statistics about the content of tables +** and indices. These statistics are made available to the query planner +** to help it make better decisions about how to perform queries. +** +** The following system tables are or have been supported: +** +** CREATE TABLE sqlite_stat1(tbl, idx, stat); +** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample); +** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample); +** +** Additional tables might be added in future releases of SQLite. +** The sqlite_stat2 table is not created or used unless the SQLite version +** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled +** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated. +** The sqlite_stat2 table is superceded by sqlite_stat3, which is only +** created and used by SQLite versions 3.7.9 and later and with +** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3 +** is a superset of sqlite_stat2. +** +** Format of sqlite_stat1: +** +** There is normally one row per index, with the index identified by the +** name in the idx column. The tbl column is the name of the table to +** which the index belongs. In each such row, the stat column will be +** a string consisting of a list of integers. The first integer in this +** list is the number of rows in the index and in the table. The second +** integer is the average number of rows in the index that have the same +** value in the first column of the index. The third integer is the average +** number of rows in the index that have the same value for the first two +** columns. The N-th integer (for N>1) is the average number of rows in +** the index which have the same value for the first N-1 columns. For +** a K-column index, there will be K+1 integers in the stat column. If +** the index is unique, then the last integer will be 1. +** +** The list of integers in the stat column can optionally be followed +** by the keyword "unordered". The "unordered" keyword, if it is present, +** must be separated from the last integer by a single space. If the +** "unordered" keyword is present, then the query planner assumes that +** the index is unordered and will not use the index for a range query. +** +** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat +** column contains a single integer which is the (estimated) number of +** rows in the table identified by sqlite_stat1.tbl. +** +** Format of sqlite_stat2: +** +** The sqlite_stat2 is only created and is only used if SQLite is compiled +** with SQLITE_ENABLE_STAT2 and if the SQLite version number is between +** 3.6.18 and 3.7.8. The "stat2" table contains additional information +** about the distribution of keys within an index. The index is identified by +** the "idx" column and the "tbl" column is the name of the table to which +** the index belongs. There are usually 10 rows in the sqlite_stat2 +** table for each index. +** +** The sqlite_stat2 entries for an index that have sampleno between 0 and 9 +** inclusive are samples of the left-most key value in the index taken at +** evenly spaced points along the index. Let the number of samples be S +** (10 in the standard build) and let C be the number of rows in the index. +** Then the sampled rows are given by: +** +** rownumber = (i*C*2 + C)/(S*2) +** +** For i between 0 and S-1. Conceptually, the index space is divided into +** S uniform buckets and the samples are the middle row from each bucket. +** +** The format for sqlite_stat2 is recorded here for legacy reference. This +** version of SQLite does not support sqlite_stat2. It neither reads nor +** writes the sqlite_stat2 table. This version of SQLite only supports +** sqlite_stat3. +** +** Format for sqlite_stat3: +** +** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is +** used to avoid compatibility problems. +** +** The format of the sqlite_stat3 table is similar to the format of +** the sqlite_stat2 table. There are multiple entries for each index. +** The idx column names the index and the tbl column is the table of the +** index. If the idx and tbl columns are the same, then the sample is +** of the INTEGER PRIMARY KEY. The sample column is a value taken from +** the left-most column of the index. The nEq column is the approximate +** number of entires in the index whose left-most column exactly matches +** the sample. nLt is the approximate number of entires whose left-most +** column is less than the sample. The nDLt column is the approximate +** number of distinct left-most entries in the index that are less than +** the sample. +** +** Future versions of SQLite might change to store a string containing +** multiple integers values in the nDLt column of sqlite_stat3. The first +** integer will be the number of prior index entires that are distinct in +** the left-most column. The second integer will be the number of prior index +** entries that are distinct in the first two columns. The third integer +** will be the number of prior index entries that are distinct in the first +** three columns. And so forth. With that extension, the nDLt field is +** similar in function to the sqlite_stat1.stat field. +** +** There can be an arbitrary number of sqlite_stat3 entries per index. +** The ANALYZE command will typically generate sqlite_stat3 tables +** that contain between 10 and 40 samples which are distributed across +** the key space, though not uniformly, and which include samples with +** largest possible nEq values. */ #ifndef SQLITE_OMIT_ANALYZE /* ** This routine generates code that opens the sqlite_stat1 table for ** writing with cursor iStatCur. If the library was built with the -** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is +** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is ** opened for writing using cursor (iStatCur+1) ** ** If the sqlite_stat1 tables does not previously exist, it is created. -** Similarly, if the sqlite_stat2 table does not exist and the library -** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. +** Similarly, if the sqlite_stat3 table does not exist and the library +** is compiled with SQLITE_ENABLE_STAT3 defined, it is created. ** ** Argument zWhere may be a pointer to a buffer containing a table name, ** or it may be a NULL pointer. If it is not NULL, then all entries in -** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated +** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated ** with the named table are deleted. If zWhere==0, then code is generated ** to delete all stat table entries. */ static void openStatTable( Parse *pParse, /* Parsing context */ @@ -77641,12 +79270,12 @@ static const struct { const char *zName; const char *zCols; } aTable[] = { { "sqlite_stat1", "tbl,idx,stat" }, -#ifdef SQLITE_ENABLE_STAT2 - { "sqlite_stat2", "tbl,idx,sampleno,sample" }, +#ifdef SQLITE_ENABLE_STAT3 + { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" }, #endif }; int aRoot[] = {0, 0}; u8 aCreateTbl[] = {0, 0}; @@ -77658,10 +79287,13 @@ if( v==0 ) return; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3VdbeDb(v)==db ); pDb = &db->aDb[iDb]; + /* Create new statistic tables if they do not exist, or clear them + ** if they do already exist. + */ for(i=0; izName))==0 ){ /* The sqlite_stat[12] table does not exist. Create it. Note that a @@ -77688,17 +79320,237 @@ sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); } } } - /* Open the sqlite_stat[12] tables for writing. */ + /* Open the sqlite_stat[13] tables for writing. */ for(i=0; ia[0])*mxSample; + p = sqlite3_malloc( n ); + if( p==0 ){ + sqlite3_result_error_nomem(context); + return; + } + memset(p, 0, n); + p->a = (struct Stat3Sample*)&p[1]; + p->nRow = nRow; + p->mxSample = mxSample; + p->nPSample = p->nRow/(mxSample/3+1) + 1; + sqlite3_randomness(sizeof(p->iPrn), &p->iPrn); + sqlite3_result_blob(context, p, sizeof(p), sqlite3_free); +} +static const FuncDef stat3InitFuncdef = { + 2, /* nArg */ + SQLITE_UTF8, /* iPrefEnc */ + 0, /* flags */ + 0, /* pUserData */ + 0, /* pNext */ + stat3Init, /* xFunc */ + 0, /* xStep */ + 0, /* xFinalize */ + "stat3_init", /* zName */ + 0, /* pHash */ + 0 /* pDestructor */ +}; + + +/* +** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The +** arguments describe a single key instance. This routine makes the +** decision about whether or not to retain this key for the sqlite_stat3 +** table. +** +** The return value is NULL. +*/ +static void stat3Push( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]); + tRowcnt nEq = sqlite3_value_int64(argv[0]); + tRowcnt nLt = sqlite3_value_int64(argv[1]); + tRowcnt nDLt = sqlite3_value_int64(argv[2]); + i64 rowid = sqlite3_value_int64(argv[3]); + u8 isPSample = 0; + u8 doInsert = 0; + int iMin = p->iMin; + struct Stat3Sample *pSample; + int i; + u32 h; + + UNUSED_PARAMETER(context); + UNUSED_PARAMETER(argc); + if( nEq==0 ) return; + h = p->iPrn = p->iPrn*1103515245 + 12345; + if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){ + doInsert = isPSample = 1; + }else if( p->nSamplemxSample ){ + doInsert = 1; + }else{ + if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){ + doInsert = 1; + } + } + if( !doInsert ) return; + if( p->nSample==p->mxSample ){ + assert( p->nSample - iMin - 1 >= 0 ); + memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1)); + pSample = &p->a[p->nSample-1]; + }else{ + pSample = &p->a[p->nSample++]; + } + pSample->iRowid = rowid; + pSample->nEq = nEq; + pSample->nLt = nLt; + pSample->nDLt = nDLt; + pSample->iHash = h; + pSample->isPSample = isPSample; + + /* Find the new minimum */ + if( p->nSample==p->mxSample ){ + pSample = p->a; + i = 0; + while( pSample->isPSample ){ + i++; + pSample++; + assert( inSample ); + } + nEq = pSample->nEq; + h = pSample->iHash; + iMin = i; + for(i++, pSample++; inSample; i++, pSample++){ + if( pSample->isPSample ) continue; + if( pSample->nEqnEq==nEq && pSample->iHashnEq; + h = pSample->iHash; + } + } + p->iMin = iMin; + } +} +static const FuncDef stat3PushFuncdef = { + 5, /* nArg */ + SQLITE_UTF8, /* iPrefEnc */ + 0, /* flags */ + 0, /* pUserData */ + 0, /* pNext */ + stat3Push, /* xFunc */ + 0, /* xStep */ + 0, /* xFinalize */ + "stat3_push", /* zName */ + 0, /* pHash */ + 0 /* pDestructor */ +}; + +/* +** Implementation of the stat3_get(P,N,...) SQL function. This routine is +** used to query the results. Content is returned for the Nth sqlite_stat3 +** row where N is between 0 and S-1 and S is the number of samples. The +** value returned depends on the number of arguments. +** +** argc==2 result: rowid +** argc==3 result: nEq +** argc==4 result: nLt +** argc==5 result: nDLt +*/ +static void stat3Get( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int n = sqlite3_value_int(argv[1]); + Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]); + + assert( p!=0 ); + if( p->nSample<=n ) return; + switch( argc ){ + case 2: sqlite3_result_int64(context, p->a[n].iRowid); break; + case 3: sqlite3_result_int64(context, p->a[n].nEq); break; + case 4: sqlite3_result_int64(context, p->a[n].nLt); break; + default: sqlite3_result_int64(context, p->a[n].nDLt); break; + } +} +static const FuncDef stat3GetFuncdef = { + -1, /* nArg */ + SQLITE_UTF8, /* iPrefEnc */ + 0, /* flags */ + 0, /* pUserData */ + 0, /* pNext */ + stat3Get, /* xFunc */ + 0, /* xStep */ + 0, /* xFinalize */ + "stat3_get", /* zName */ + 0, /* pHash */ + 0 /* pDestructor */ +}; +#endif /* SQLITE_ENABLE_STAT3 */ + + + /* ** Generate code to do an analysis of all indices associated with ** a single table. */ @@ -77718,24 +79570,31 @@ int endOfLoop; /* The end of the loop */ int jZeroRows = -1; /* Jump from here if number of rows is zero */ int iDb; /* Index of database containing pTab */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ - int regSampleno = iMem++; /* Register containing next sample number */ - int regCol = iMem++; /* Content of a column analyzed table */ + int regStat1 = iMem++; /* The stat column of sqlite_stat1 */ +#ifdef SQLITE_ENABLE_STAT3 + int regNumEq = regStat1; /* Number of instances. Same as regStat1 */ + int regNumLt = iMem++; /* Number of keys less than regSample */ + int regNumDLt = iMem++; /* Number of distinct keys less than regSample */ + int regSample = iMem++; /* The next sample value */ + int regRowid = regSample; /* Rowid of a sample */ + int regAccum = iMem++; /* Register to hold Stat3Accum object */ + int regLoop = iMem++; /* Loop counter */ + int regCount = iMem++; /* Number of rows in the table or index */ + int regTemp1 = iMem++; /* Intermediate register */ + int regTemp2 = iMem++; /* Intermediate register */ + int once = 1; /* One-time initialization */ + int shortJump = 0; /* Instruction address */ + int iTabCur = pParse->nTab++; /* Table cursor */ +#endif + int regCol = iMem++; /* Content of a column in analyzed table */ int regRec = iMem++; /* Register holding completed record */ int regTemp = iMem++; /* Temporary use register */ - int regRowid = iMem++; /* Rowid for the inserted record */ - -#ifdef SQLITE_ENABLE_STAT2 - int addr = 0; /* Instruction address */ - int regTemp2 = iMem++; /* Temporary use register */ - int regSamplerecno = iMem++; /* Index of next sample to record */ - int regRecno = iMem++; /* Current sample index */ - int regLast = iMem++; /* Index of last sample to record */ - int regFirst = iMem++; /* Index of first sample to record */ -#endif + int regNewRowid = iMem++; /* Rowid for the inserted record */ + v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) ){ return; } @@ -77764,13 +79623,18 @@ iIdxCur = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; KeyInfo *pKey; + int addrIfNot = 0; /* address of OP_IfNot */ + int *aChngAddr; /* Array of jump instruction addresses */ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; + VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName)); nCol = pIdx->nColumn; + aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol); + if( aChngAddr==0 ) continue; pKey = sqlite3IndexKeyinfo(pParse, pIdx); if( iMem+1+(nCol*2)>pParse->nMem ){ pParse->nMem = iMem+1+(nCol*2); } @@ -77781,35 +79645,25 @@ VdbeComment((v, "%s", pIdx->zName)); /* Populate the register containing the index name. */ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); -#ifdef SQLITE_ENABLE_STAT2 - - /* If this iteration of the loop is generating code to analyze the - ** first index in the pTab->pIndex list, then register regLast has - ** not been populated. In this case populate it now. */ - if( pTab->pIndex==pIdx ){ - sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno); - sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp); - sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2); - - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast); - sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst); - addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast); - sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst); - sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast); - sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2); - sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast); - sqlite3VdbeJumpHere(v, addr); - } - - /* Zero the regSampleno and regRecno registers. */ - sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno); - sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno); -#endif +#ifdef SQLITE_ENABLE_STAT3 + if( once ){ + once = 0; + sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); + } + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount); + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1); + sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq); + sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt); + sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt); + sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum); + sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum, + (char*)&stat3InitFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 2); +#endif /* SQLITE_ENABLE_STAT3 */ /* The block of memory cells initialized here is used as follows. ** ** iMem: ** The total number of rows in the table. @@ -77835,79 +79689,87 @@ /* Start the analysis loop. This loop runs through all the entries in ** the index b-tree. */ endOfLoop = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop); topOfLoop = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); + sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); /* Increment row counter */ for(i=0; iazColl!=0 ); assert( pIdx->azColl[i]!=0 ); pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); - sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1, - (char*)pColl, P4_COLLSEQ); + aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1, + (char*)pColl, P4_COLLSEQ); sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); - } - if( db->mallocFailed ){ - /* If a malloc failure has occurred, then the result of the expression - ** passed as the second argument to the call to sqlite3VdbeJumpHere() - ** below may be negative. Which causes an assert() to fail (or an - ** out-of-bounds write if SQLITE_DEBUG is not defined). */ - return; + VdbeComment((v, "jump if column %d changed", i)); +#ifdef SQLITE_ENABLE_STAT3 + if( i==0 ){ + sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1); + VdbeComment((v, "incr repeat count")); + } +#endif } sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); for(i=0; inColumn, regRowid); + sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt); + sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1); + sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq); +#endif } - sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */ sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1); sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1); } + sqlite3DbFree(db, aChngAddr); - /* End of the analysis loop. */ + /* Always jump here after updating the iMem+1...iMem+1+nCol counters */ sqlite3VdbeResolveLabel(v, endOfLoop); + sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop); sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); +#ifdef SQLITE_ENABLE_STAT3 + sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2, + (char*)&stat3PushFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 5); + sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop); + shortJump = + sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1); + sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1, + (char*)&stat3GetFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 2); + sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1); + sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1); + sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample); + sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample); + sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq, + (char*)&stat3GetFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 3); + sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt, + (char*)&stat3GetFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 4); + sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt, + (char*)&stat3GetFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 5); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0); + sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid); + sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump); + sqlite3VdbeJumpHere(v, shortJump+2); +#endif /* Store the results in sqlite_stat1. ** ** The result is a single row of the sqlite_stat1 table. The first ** two columns are the names of the table and index. The third column @@ -77923,50 +79785,51 @@ ** ** If K==0 then no entry is made into the sqlite_stat1 table. ** If K>0 then it is always the case the D>0 so division by zero ** is never possible. */ - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); + sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1); if( jZeroRows<0 ){ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); } for(i=0; ipIndex==0 ){ sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb); VdbeComment((v, "%s", pTab->zName)); - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno); + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1); sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); - jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno); + jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); }else{ sqlite3VdbeJumpHere(v, jZeroRows); jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto); } sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); - sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); + sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); if( pParse->nMemnMem = regRec; sqlite3VdbeJumpHere(v, jZeroRows); } + /* ** Generate code that will cause the most recent index analysis to ** be loaded into internal hash tables where is can be used. */ @@ -77987,11 +79850,11 @@ int iStatCur; int iMem; sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; - pParse->nTab += 2; + pParse->nTab += 3; openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); @@ -78012,11 +79875,11 @@ assert( pTab!=0 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; - pParse->nTab += 2; + pParse->nTab += 3; if( pOnlyIdx ){ openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx"); }else{ openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); } @@ -78117,11 +79980,11 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ analysisInfo *pInfo = (analysisInfo*)pData; Index *pIndex; Table *pTable; int i, c, n; - unsigned int v; + tRowcnt v; const char *z; assert( argc==3 ); UNUSED_PARAMETER2(NotUsed, argc); @@ -78160,40 +80023,172 @@ /* ** If the Index.aSample variable is not NULL, delete the aSample[] array ** and its contents. */ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 if( pIdx->aSample ){ int j; - for(j=0; jnSample; j++){ IndexSample *p = &pIdx->aSample[j]; if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ sqlite3DbFree(db, p->u.z); } } sqlite3DbFree(db, pIdx->aSample); + } + if( db && db->pnBytesFreed==0 ){ + pIdx->nSample = 0; + pIdx->aSample = 0; } #else UNUSED_PARAMETER(db); UNUSED_PARAMETER(pIdx); #endif } +#ifdef SQLITE_ENABLE_STAT3 /* -** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The +** Load content from the sqlite_stat3 table into the Index.aSample[] +** arrays of all indices. +*/ +static int loadStat3(sqlite3 *db, const char *zDb){ + int rc; /* Result codes from subroutines */ + sqlite3_stmt *pStmt = 0; /* An SQL statement being run */ + char *zSql; /* Text of the SQL statement */ + Index *pPrevIdx = 0; /* Previous index in the loop */ + int idx = 0; /* slot in pIdx->aSample[] for next sample */ + int eType; /* Datatype of a sample */ + IndexSample *pSample; /* A slot in pIdx->aSample[] */ + + if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){ + return SQLITE_OK; + } + + zSql = sqlite3MPrintf(db, + "SELECT idx,count(*) FROM %Q.sqlite_stat3" + " GROUP BY idx", zDb); + if( !zSql ){ + return SQLITE_NOMEM; + } + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + sqlite3DbFree(db, zSql); + if( rc ) return rc; + + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + char *zIndex; /* Index name */ + Index *pIdx; /* Pointer to the index object */ + int nSample; /* Number of samples */ + + zIndex = (char *)sqlite3_column_text(pStmt, 0); + if( zIndex==0 ) continue; + nSample = sqlite3_column_int(pStmt, 1); + pIdx = sqlite3FindIndex(db, zIndex, zDb); + if( pIdx==0 ) continue; + assert( pIdx->nSample==0 ); + pIdx->nSample = nSample; + pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) ); + pIdx->avgEq = pIdx->aiRowEst[1]; + if( pIdx->aSample==0 ){ + db->mallocFailed = 1; + sqlite3_finalize(pStmt); + return SQLITE_NOMEM; + } + } + rc = sqlite3_finalize(pStmt); + if( rc ) return rc; + + zSql = sqlite3MPrintf(db, + "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb); + if( !zSql ){ + return SQLITE_NOMEM; + } + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + sqlite3DbFree(db, zSql); + if( rc ) return rc; + + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + char *zIndex; /* Index name */ + Index *pIdx; /* Pointer to the index object */ + int i; /* Loop counter */ + tRowcnt sumEq; /* Sum of the nEq values */ + + zIndex = (char *)sqlite3_column_text(pStmt, 0); + if( zIndex==0 ) continue; + pIdx = sqlite3FindIndex(db, zIndex, zDb); + if( pIdx==0 ) continue; + if( pIdx==pPrevIdx ){ + idx++; + }else{ + pPrevIdx = pIdx; + idx = 0; + } + assert( idxnSample ); + pSample = &pIdx->aSample[idx]; + pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1); + pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2); + pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3); + if( idx==pIdx->nSample-1 ){ + if( pSample->nDLt>0 ){ + for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq; + pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt; + } + if( pIdx->avgEq<=0 ) pIdx->avgEq = 1; + } + eType = sqlite3_column_type(pStmt, 4); + pSample->eType = (u8)eType; + switch( eType ){ + case SQLITE_INTEGER: { + pSample->u.i = sqlite3_column_int64(pStmt, 4); + break; + } + case SQLITE_FLOAT: { + pSample->u.r = sqlite3_column_double(pStmt, 4); + break; + } + case SQLITE_NULL: { + break; + } + default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); { + const char *z = (const char *)( + (eType==SQLITE_BLOB) ? + sqlite3_column_blob(pStmt, 4): + sqlite3_column_text(pStmt, 4) + ); + int n = z ? sqlite3_column_bytes(pStmt, 4) : 0; + pSample->nByte = n; + if( n < 1){ + pSample->u.z = 0; + }else{ + pSample->u.z = sqlite3Malloc(n); + if( pSample->u.z==0 ){ + db->mallocFailed = 1; + sqlite3_finalize(pStmt); + return SQLITE_NOMEM; + } + memcpy(pSample->u.z, z, n); + } + } + } + } + return sqlite3_finalize(pStmt); +} +#endif /* SQLITE_ENABLE_STAT3 */ + +/* +** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] -** arrays. The contents of sqlite_stat2 are used to populate the +** arrays. The contents of sqlite_stat3 are used to populate the ** Index.aSample[] arrays. ** ** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR -** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined -** during compilation and the sqlite_stat2 table is present, no data is +** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined +** during compilation and the sqlite_stat3 table is present, no data is ** read from it. ** -** If SQLITE_ENABLE_STAT2 was defined during compilation and the -** sqlite_stat2 table is not present in the database, SQLITE_ERROR is +** If SQLITE_ENABLE_STAT3 was defined during compilation and the +** sqlite_stat3 table is not present in the database, SQLITE_ERROR is ** returned. However, in this case, data is read from the sqlite_stat1 ** table (if it is present) before returning. ** ** If an OOM error occurs, this function always sets db->mallocFailed. ** This means if the caller does not care about other errors, the return @@ -78211,12 +80206,14 @@ /* Clear any prior statistics */ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); +#ifdef SQLITE_ENABLE_STAT3 sqlite3DeleteIndexSamples(db, pIdx); pIdx->aSample = 0; +#endif } /* Check to make sure the sqlite_stat1 table exists */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zName; @@ -78224,91 +80221,23 @@ return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ zSql = sqlite3MPrintf(db, - "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); + "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); } - /* Load the statistics from the sqlite_stat2 table. */ -#ifdef SQLITE_ENABLE_STAT2 - if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){ - rc = SQLITE_ERROR; - } - if( rc==SQLITE_OK ){ - sqlite3_stmt *pStmt = 0; - - zSql = sqlite3MPrintf(db, - "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase); - if( !zSql ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - sqlite3DbFree(db, zSql); - } - - if( rc==SQLITE_OK ){ - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char *zIndex; /* Index name */ - Index *pIdx; /* Pointer to the index object */ - - zIndex = (char *)sqlite3_column_text(pStmt, 0); - pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0; - if( pIdx ){ - int iSample = sqlite3_column_int(pStmt, 1); - if( iSample=0 ){ - int eType = sqlite3_column_type(pStmt, 2); - - if( pIdx->aSample==0 ){ - static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES; - pIdx->aSample = (IndexSample *)sqlite3DbMallocRaw(0, sz); - if( pIdx->aSample==0 ){ - db->mallocFailed = 1; - break; - } - memset(pIdx->aSample, 0, sz); - } - - assert( pIdx->aSample ); - { - IndexSample *pSample = &pIdx->aSample[iSample]; - pSample->eType = (u8)eType; - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - pSample->u.r = sqlite3_column_double(pStmt, 2); - }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ - const char *z = (const char *)( - (eType==SQLITE_BLOB) ? - sqlite3_column_blob(pStmt, 2): - sqlite3_column_text(pStmt, 2) - ); - int n = sqlite3_column_bytes(pStmt, 2); - if( n>24 ){ - n = 24; - } - pSample->nByte = (u8)n; - if( n < 1){ - pSample->u.z = 0; - }else{ - pSample->u.z = sqlite3DbStrNDup(0, z, n); - if( pSample->u.z==0 ){ - db->mallocFailed = 1; - break; - } - } - } - } - } - } - } - rc = sqlite3_finalize(pStmt); - } + /* Load the statistics from the sqlite_stat3 table. */ +#ifdef SQLITE_ENABLE_STAT3 + if( rc==SQLITE_OK ){ + rc = loadStat3(db, sInfo.zDatabase); } #endif if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; @@ -81111,31 +83040,102 @@ } #endif } /* -** Remove entries from the sqlite_stat1 and sqlite_stat2 tables +** Remove entries from the sqlite_statN tables (for N in (1,2,3)) ** after a DROP INDEX or DROP TABLE command. */ static void sqlite3ClearStatTables( Parse *pParse, /* The parsing context */ int iDb, /* The database number */ const char *zType, /* "idx" or "tbl" */ const char *zName /* Name of index or table */ ){ - static const char *azStatTab[] = { "sqlite_stat1", "sqlite_stat2" }; int i; const char *zDbName = pParse->db->aDb[iDb].zName; - for(i=0; idb, azStatTab[i], zDbName) ){ + for(i=1; i<=3; i++){ + char zTab[24]; + sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i); + if( sqlite3FindTable(pParse->db, zTab, zDbName) ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE %s=%Q", - zDbName, azStatTab[i], zType, zName + zDbName, zTab, zType, zName ); } } } + +/* +** Generate code to drop a table. +*/ +SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){ + Vdbe *v; + sqlite3 *db = pParse->db; + Trigger *pTrigger; + Db *pDb = &db->aDb[iDb]; + + v = sqlite3GetVdbe(pParse); + assert( v!=0 ); + sqlite3BeginWriteOperation(pParse, 1, iDb); + +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( IsVirtual(pTab) ){ + sqlite3VdbeAddOp0(v, OP_VBegin); + } +#endif + + /* Drop all triggers associated with the table being dropped. Code + ** is generated to remove entries from sqlite_master and/or + ** sqlite_temp_master if required. + */ + pTrigger = sqlite3TriggerList(pParse, pTab); + while( pTrigger ){ + assert( pTrigger->pSchema==pTab->pSchema || + pTrigger->pSchema==db->aDb[1].pSchema ); + sqlite3DropTriggerPtr(pParse, pTrigger); + pTrigger = pTrigger->pNext; + } + +#ifndef SQLITE_OMIT_AUTOINCREMENT + /* Remove any entries of the sqlite_sequence table associated with + ** the table being dropped. This is done before the table is dropped + ** at the btree level, in case the sqlite_sequence table needs to + ** move as a result of the drop (can happen in auto-vacuum mode). + */ + if( pTab->tabFlags & TF_Autoincrement ){ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.sqlite_sequence WHERE name=%Q", + pDb->zName, pTab->zName + ); + } +#endif + + /* Drop all SQLITE_MASTER table and index entries that refer to the + ** table. The program name loops through the master table and deletes + ** every row that refers to a table of the same name as the one being + ** dropped. Triggers are handled seperately because a trigger can be + ** created in the temp database that refers to a table in another + ** database. + */ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", + pDb->zName, SCHEMA_TABLE(iDb), pTab->zName); + if( !isView && !IsVirtual(pTab) ){ + destroyTable(pParse, pTab); + } + + /* Remove the table entry from SQLite's internal schema and modify + ** the schema cookie. + */ + if( IsVirtual(pTab) ){ + sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0); + } + sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); + sqlite3ChangeCookie(pParse, iDb); + sqliteViewResetAll(db, iDb); +} /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. */ @@ -81201,11 +83201,12 @@ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ goto exit_drop_table; } } #endif - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ + if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 + && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){ sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); goto exit_drop_table; } #ifndef SQLITE_OMIT_VIEW @@ -81225,72 +83226,15 @@ /* Generate code to remove the table from the master table ** on disk. */ v = sqlite3GetVdbe(pParse); if( v ){ - Trigger *pTrigger; - Db *pDb = &db->aDb[iDb]; sqlite3BeginWriteOperation(pParse, 1, iDb); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - sqlite3VdbeAddOp0(v, OP_VBegin); - } -#endif + sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); sqlite3FkDropTable(pParse, pName, pTab); - - /* Drop all triggers associated with the table being dropped. Code - ** is generated to remove entries from sqlite_master and/or - ** sqlite_temp_master if required. - */ - pTrigger = sqlite3TriggerList(pParse, pTab); - while( pTrigger ){ - assert( pTrigger->pSchema==pTab->pSchema || - pTrigger->pSchema==db->aDb[1].pSchema ); - sqlite3DropTriggerPtr(pParse, pTrigger); - pTrigger = pTrigger->pNext; - } - -#ifndef SQLITE_OMIT_AUTOINCREMENT - /* Remove any entries of the sqlite_sequence table associated with - ** the table being dropped. This is done before the table is dropped - ** at the btree level, in case the sqlite_sequence table needs to - ** move as a result of the drop (can happen in auto-vacuum mode). - */ - if( pTab->tabFlags & TF_Autoincrement ){ - sqlite3NestedParse(pParse, - "DELETE FROM %s.sqlite_sequence WHERE name=%Q", - pDb->zName, pTab->zName - ); - } -#endif - - /* Drop all SQLITE_MASTER table and index entries that refer to the - ** table. The program name loops through the master table and deletes - ** every row that refers to a table of the same name as the one being - ** dropped. Triggers are handled seperately because a trigger can be - ** created in the temp database that refers to a table in another - ** database. - */ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", - pDb->zName, SCHEMA_TABLE(iDb), pTab->zName); - sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); - if( !isView && !IsVirtual(pTab) ){ - destroyTable(pParse, pTab); - } - - /* Remove the table entry from SQLite's internal schema and modify - ** the schema cookie. - */ - if( IsVirtual(pTab) ){ - sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0); - } - sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); - sqlite3ChangeCookie(pParse, iDb); - } - sqliteViewResetAll(db, iDb); + sqlite3CodeDropTable(pParse, pTab, iDb, isView); + } exit_drop_table: sqlite3SrcListDelete(db, pName); } @@ -81454,17 +83398,19 @@ */ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ Table *pTab = pIndex->pTable; /* The table that is indexed */ int iTab = pParse->nTab++; /* Btree cursor used for pTab */ int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ - int iSorter = iTab; /* Cursor opened by OpenSorter (if in use) */ + int iSorter; /* Cursor opened by OpenSorter (if in use) */ int addr1; /* Address of top of loop */ int addr2; /* Address to jump to for next iteration */ int tnum; /* Root page of index */ Vdbe *v; /* Generate code into this virtual machine */ KeyInfo *pKey; /* KeyInfo for index */ +#ifdef SQLITE_OMIT_MERGE_SORT int regIdxKey; /* Registers containing the index key */ +#endif int regRecord; /* Register holding assemblied index record */ sqlite3 *db = pParse->db; /* The database connection */ int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_AUTHORIZATION @@ -81494,21 +83440,22 @@ #ifndef SQLITE_OMIT_MERGE_SORT /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO); +#else + iSorter = iTab; #endif /* Open the table. Loop through all rows of the table, inserting index ** records into the sorter. */ sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); - addr2 = addr1 + 1; regRecord = sqlite3GetTempReg(pParse); - regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1); #ifndef SQLITE_OMIT_MERGE_SORT + sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); sqlite3VdbeJumpHere(v, addr1); addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); if( pIndex->onError!=OE_None ){ @@ -81524,10 +83471,12 @@ } sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord); sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); #else + regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1); + addr2 = addr1 + 1; if( pIndex->onError!=OE_None ){ const int regRowid = regIdxKey + pIndex->nColumn; const int j2 = sqlite3VdbeCurrentAddr(v) + 2; void * const pRegKey = SQLITE_INT_TO_PTR(regIdxKey); @@ -81621,10 +83570,11 @@ ** before looking up the table. */ assert( pName1 && pName2 ); iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) goto exit_create_index; + assert( pName && pName->z ); #ifndef SQLITE_OMIT_TEMPDB /* If the index name was unqualified, check if the the table ** is a temp table. If so, set the database to 1. Do not do this ** if initialising a database schema. @@ -81648,10 +83598,11 @@ pTblName->a[0].zDatabase); if( !pTab || db->mallocFailed ) goto exit_create_index; assert( db->aDb[iDb].pSchema==pTab->pSchema ); }else{ assert( pName==0 ); + assert( pStart==0 ); pTab = pParse->pNewTable; if( !pTab ) goto exit_create_index; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); } pDb = &db->aDb[iDb]; @@ -81690,10 +83641,11 @@ ** own name. */ if( pName ){ zName = sqlite3NameFromToken(db, pName); if( zName==0 ) goto exit_create_index; + assert( pName->z!=0 ); if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto exit_create_index; } if( !db->init.busy ){ if( sqlite3FindTable(db, zName, 0)!=0 ){ @@ -81768,25 +83720,29 @@ ** Allocate the index structure. */ nName = sqlite3Strlen30(zName); nCol = pList->nExpr; pIndex = sqlite3DbMallocZero(db, - sizeof(Index) + /* Index structure */ - sizeof(int)*nCol + /* Index.aiColumn */ - sizeof(int)*(nCol+1) + /* Index.aiRowEst */ - sizeof(char *)*nCol + /* Index.azColl */ - sizeof(u8)*nCol + /* Index.aSortOrder */ - nName + 1 + /* Index.zName */ - nExtra /* Collation sequence names */ + ROUND8(sizeof(Index)) + /* Index structure */ + ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */ + sizeof(char *)*nCol + /* Index.azColl */ + sizeof(int)*nCol + /* Index.aiColumn */ + sizeof(u8)*nCol + /* Index.aSortOrder */ + nName + 1 + /* Index.zName */ + nExtra /* Collation sequence names */ ); if( db->mallocFailed ){ goto exit_create_index; } - pIndex->azColl = (char**)(&pIndex[1]); + zExtra = (char*)pIndex; + pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))]; + pIndex->azColl = (char**) + ((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1)); + assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) ); + assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) ); pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]); - pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]); - pIndex->aSortOrder = (u8 *)(&pIndex->aiRowEst[nCol+1]); + pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]); pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]); zExtra = (char *)(&pIndex->zName[nName+1]); memcpy(pIndex->zName, zName, nName+1); pIndex->pTable = pTab; pIndex->nColumn = pList->nExpr; @@ -82059,13 +84015,13 @@ ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here ** are based on typical values found in actual indices. */ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ - unsigned *a = pIdx->aiRowEst; + tRowcnt *a = pIdx->aiRowEst; int i; - unsigned n; + tRowcnt n; assert( a!=0 ); a[0] = pIdx->pTable->nRowEst; if( a[0]<10 ) a[0] = 10; n = 10; for(i=1; i<=pIdx->nColumn; i++){ @@ -82545,17 +84501,14 @@ /* ** Commit a transaction */ SQLITE_PRIVATE void sqlite3CommitTransaction(Parse *pParse){ - sqlite3 *db; Vdbe *v; assert( pParse!=0 ); - db = pParse->db; - assert( db!=0 ); -/* if( db->aDb[0].pBt==0 ) return; */ + assert( pParse->db!=0 ); if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ){ return; } v = sqlite3GetVdbe(pParse); if( v ){ @@ -82565,17 +84518,14 @@ /* ** Rollback a transaction */ SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse *pParse){ - sqlite3 *db; Vdbe *v; assert( pParse!=0 ); - db = pParse->db; - assert( db!=0 ); -/* if( db->aDb[0].pBt==0 ) return; */ + assert( pParse->db!=0 ); if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ){ return; } v = sqlite3GetVdbe(pParse); if( v ){ @@ -83540,11 +85490,10 @@ /* Check that there isn't an ORDER BY without a LIMIT clause. */ if( pOrderBy && (pLimit == 0) ) { sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType); - pParse->parseError = 1; goto limit_where_cleanup_2; } /* We only need to generate a select expression if there ** is a limit/offset term to enforce. @@ -84377,20 +86326,19 @@ /* Verify that the call to _bytes() does not invalidate the _text() pointer */ assert( z2==(char*)sqlite3_value_text(argv[0]) ); if( z2 ){ z1 = contextMalloc(context, ((i64)n)+1); if( z1 ){ - memcpy(z1, z2, n+1); - for(i=0; z1[i]; i++){ - z1[i] = (char)sqlite3Toupper(z1[i]); + for(i=0; imallocFailed==1 ){ fkTriggerDelete(db, pTrigger); return 0; } + assert( pStep!=0 ); switch( action ){ case OE_Restrict: pStep->op = TK_SELECT; break; @@ -86921,11 +88869,11 @@ ** 'b' NONE ** 'c' NUMERIC ** 'd' INTEGER ** 'e' REAL ** -** An extra 'b' is appended to the end of the string to cover the +** An extra 'd' is appended to the end of the string to cover the ** rowid that appears as the last column in every index. ** ** Memory for the buffer containing the column index affinity string ** is managed along with the rest of the Index structure. It will be ** released when sqlite3DeleteIndex() is called. @@ -86949,11 +88897,11 @@ return 0; } for(n=0; nnColumn; n++){ pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; } - pIdx->zColAff[n++] = SQLITE_AFF_NONE; + pIdx->zColAff[n++] = SQLITE_AFF_INTEGER; pIdx->zColAff[n] = 0; } return pIdx->zColAff; } @@ -87113,10 +89061,11 @@ for(p = pParse->pAinc; p; p = p->pNext){ pDb = &db->aDb[p->iDb]; memId = p->regCtr; assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); + sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); @@ -87980,11 +89929,11 @@ ** any ABORT Back out changes from the current command ** only (do not do a complete rollback) then ** cause sqlite3_exec() to return immediately ** with SQLITE_CONSTRAINT. ** -** any FAIL Sqlite_exec() returns immediately with a +** any FAIL Sqlite3_exec() returns immediately with a ** return code of SQLITE_CONSTRAINT. The ** transaction is not rolled back and any ** prior changes are retained. ** ** any IGNORE The record number and data is popped from @@ -88452,35 +90401,29 @@ /* ** Attempt the transfer optimization on INSERTs of the form ** ** INSERT INTO tab1 SELECT * FROM tab2; ** -** This optimization is only attempted if -** -** (1) tab1 and tab2 have identical schemas including all the -** same indices and constraints -** -** (2) tab1 and tab2 are different tables -** -** (3) There must be no triggers on tab1 -** -** (4) The result set of the SELECT statement is "*" -** -** (5) The SELECT statement has no WHERE, HAVING, ORDER BY, GROUP BY, -** or LIMIT clause. -** -** (6) The SELECT statement is a simple (not a compound) select that -** contains only tab2 in its FROM clause -** -** This method for implementing the INSERT transfers raw records from -** tab2 over to tab1. The columns are not decoded. Raw records from -** the indices of tab2 are transfered to tab1 as well. In so doing, -** the resulting tab1 has much less fragmentation. -** -** This routine returns TRUE if the optimization is attempted. If any -** of the conditions above fail so that the optimization should not -** be attempted, then this routine returns FALSE. +** The xfer optimization transfers raw records from tab2 over to tab1. +** Columns are not decoded and reassemblied, which greatly improves +** performance. Raw index records are transferred in the same way. +** +** The xfer optimization is only attempted if tab1 and tab2 are compatible. +** There are lots of rules for determining compatibility - see comments +** embedded in the code for details. +** +** This routine returns TRUE if the optimization is guaranteed to be used. +** Sometimes the xfer optimization will only work if the destination table +** is empty - a factor that can only be determined at run-time. In that +** case, this routine generates code for the xfer optimization but also +** does a test to see if the destination table is empty and jumps over the +** xfer optimization code if the test fails. In that case, this routine +** returns FALSE so that the caller will know to go ahead and generate +** an unoptimized transfer. This routine also returns FALSE if there +** is no chance that the xfer optimization can be applied. +** +** This optimization is particularly useful at making VACUUM run faster. */ static int xferOptimization( Parse *pParse, /* Parser context */ Table *pDest, /* The table we are inserting into */ Select *pSelect, /* A SELECT statement to use as the data source */ @@ -88513,14 +90456,12 @@ if( pDest->tabFlags & TF_Virtual ){ return 0; /* tab1 must not be a virtual table */ } #endif if( onError==OE_Default ){ - onError = OE_Abort; - } - if( onError!=OE_Abort && onError!=OE_Rollback ){ - return 0; /* Cannot do OR REPLACE or OR IGNORE or OR FAIL */ + if( pDest->iPKey>=0 ) onError = pDest->keyConf; + if( onError==OE_Default ) onError = OE_Abort; } assert(pSelect->pSrc); /* allocated even if there is no FROM clause */ if( pSelect->pSrc->nSrc!=1 ){ return 0; /* FROM clause must have exactly one term */ } @@ -88621,18 +90562,17 @@ */ if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ return 0; } #endif - - /* If we get this far, it means either: - ** - ** * We can always do the transfer if the table contains an - ** an integer primary key - ** - ** * We can conditionally do the transfer if the destination - ** table is empty. + if( (pParse->db->flags & SQLITE_CountRows)!=0 ){ + return 0; /* xfer opt does not play well with PRAGMA count_changes */ + } + + /* If we get this far, it means that the xfer optimization is at + ** least a possibility, though it might only work if the destination + ** table (tab1) is initially empty. */ #ifdef SQLITE_TEST sqlite3_xferopt_count++; #endif iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema); @@ -88640,20 +90580,27 @@ sqlite3CodeVerifySchema(pParse, iDbSrc); iSrc = pParse->nTab++; iDest = pParse->nTab++; regAutoinc = autoIncBegin(pParse, iDbDest, pDest); sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); - if( (pDest->iPKey<0 && pDest->pIndex!=0) || destHasUniqueIdx ){ - /* If tables do not have an INTEGER PRIMARY KEY and there - ** are indices to be copied and the destination is not empty, - ** we have to disallow the transfer optimization because the - ** the rowids might change which will mess up indexing. + if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ + || destHasUniqueIdx /* (2) */ + || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */ + ){ + /* In some circumstances, we are able to run the xfer optimization + ** only if the destination table is initially empty. This code makes + ** that determination. Conditions under which the destination must + ** be empty: ** - ** Or if the destination has a UNIQUE index and is not empty, - ** we also disallow the transfer optimization because we cannot - ** insure that all entries in the union of DEST and SRC will be - ** unique. + ** (1) There is no INTEGER PRIMARY KEY but there are indices. + ** (If the destination is not initially empty, the rowid fields + ** of index entries might need to change.) + ** + ** (2) The destination has a unique index. (The xfer optimization + ** is unable to test uniqueness.) + ** + ** (3) onError is something other than OE_Abort and OE_Rollback. */ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); sqlite3VdbeJumpHere(v, addr1); }else{ @@ -88935,12 +90882,14 @@ 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*); 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*)); + 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); int (*column_count)(sqlite3_stmt*pStmt); const char * (*column_database_name)(sqlite3_stmt*,int); @@ -88961,14 +90910,22 @@ int (*column_type)(sqlite3_stmt*,int iCol); sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); void * (*commit_hook)(sqlite3*,int(*)(void*),void*); int (*complete)(const char*sql); int (*complete16)(const void*sql); - int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*)); - int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*)); - int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); - int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); + int (*create_collation)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_collation16)(sqlite3*,const void*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_function)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + int (*create_function16)(sqlite3*,const void*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); int (*data_count)(sqlite3_stmt*pStmt); sqlite3 * (*db_handle)(sqlite3_stmt*); int (*declare_vtab)(sqlite3*,const char*); int (*enable_shared_cache)(int); @@ -89009,20 +90966,23 @@ void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_value)(sqlite3_context*,sqlite3_value*); void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); - int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*); + int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, + const char*,const char*),void*); void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); char * (*snprintf)(int,char*,const char*,...); int (*step)(sqlite3_stmt*); - int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*); + 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*); 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 * (*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*); double (*value_double)(sqlite3_value*); @@ -89040,19 +91000,23 @@ /* Added by 3.3.13 */ int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); int (*clear_bindings)(sqlite3_stmt*); /* Added by 3.4.1 */ - int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *)); + int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, + void (*xDestroy)(void *)); /* Added by 3.5.0 */ int (*bind_zeroblob)(sqlite3_stmt*,int,int); int (*blob_bytes)(sqlite3_blob*); int (*blob_close)(sqlite3_blob*); - int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**); + int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, + int,sqlite3_blob**); int (*blob_read)(sqlite3_blob*,void*,int,int); int (*blob_write)(sqlite3_blob*,const void*,int,int); - int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*)); + int (*create_collation_v2)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*), + void(*)(void*)); int (*file_control)(sqlite3*,const char*,int,void*); sqlite3_int64 (*memory_highwater)(int); sqlite3_int64 (*memory_used)(void); sqlite3_mutex *(*mutex_alloc)(int); void (*mutex_enter)(sqlite3_mutex*); @@ -89084,11 +91048,15 @@ int (*backup_pagecount)(sqlite3_backup*); int (*backup_remaining)(sqlite3_backup*); int (*backup_step)(sqlite3_backup*,int); const char *(*compileoption_get)(int); int (*compileoption_used)(const char*); - int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*)); + int (*create_function_v2)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*)); int (*db_config)(sqlite3*,int,...); sqlite3_mutex *(*db_mutex)(sqlite3*); int (*db_status)(sqlite3*,int,int*,int*,int); int (*extended_errcode)(sqlite3*); void (*log)(int,const char*,...); @@ -89698,11 +91666,11 @@ sqlite3_vfs *pVfs = db->pVfs; void *handle; int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); char *zErrmsg = 0; void **aHandle; - const int nMsg = 300; + int nMsg = 300 + sqlite3Strlen30(zFile); if( pzErrMsg ) *pzErrMsg = 0; /* Ticket #1863. To avoid a creating security problems for older ** applications that relink against newer versions of SQLite, the @@ -89735,10 +91703,11 @@ } xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) sqlite3OsDlSym(pVfs, handle, zProc); if( xInit==0 ){ if( pzErrMsg ){ + nMsg += sqlite3Strlen30(zProc); *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); if( zErrmsg ){ sqlite3_snprintf(nMsg, zErrmsg, "no entry point [%s] in shared library [%s]", zProc,zFile); sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); @@ -89919,10 +91888,11 @@ ** If anything goes wrong, set an error in the database connection. */ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ int i; int go = 1; + int rc; int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); wsdAutoextInit; if( wsdAutoext.nExt==0 ){ /* Common case: early out without every having to acquire a mutex */ @@ -89941,12 +91911,12 @@ xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) wsdAutoext.aExt[i]; } sqlite3_mutex_leave(mutex); zErrmsg = 0; - if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){ - sqlite3Error(db, SQLITE_ERROR, + if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){ + sqlite3Error(db, rc, "automatic extension loading failed: %s", zErrmsg); go = 0; } sqlite3_free(zErrmsg); } @@ -90299,11 +92269,11 @@ zDb = pId2->n>0 ? pDb->zName : 0; if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){ goto pragma_out; } -#ifndef SQLITE_OMIT_PAGER_PRAGMAS +#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) /* ** PRAGMA [database.]default_cache_size ** PRAGMA [database.]default_cache_size=N ** ** The first form reports the current persistent setting for the @@ -90348,11 +92318,13 @@ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } }else +#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */ +#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) /* ** PRAGMA [database.]page_size ** PRAGMA [database.]page_size=N ** ** The first form reports the current setting for the @@ -90369,11 +92341,11 @@ }else{ /* Malloc may fail when setting the page-size, as there is an internal ** buffer that the pager module resizes using sqlite3_realloc(). */ db->nextPagesize = sqlite3Atoi(zRight); - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ + if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ db->mallocFailed = 1; } } }else @@ -90409,10 +92381,14 @@ ** The first form reports the current setting for the ** maximum number of pages in the database file. The ** second form attempts to change this setting. Both ** forms return the current setting. ** + ** The absolute value of N is used. This is undocumented and might + ** change. The only purpose is to provide an easy way to test + ** the sqlite3AbsInt32() function. + ** ** PRAGMA [database.]page_count ** ** Return the number of pages in the specified database. */ if( sqlite3StrICmp(zLeft,"page_count")==0 @@ -90420,14 +92396,15 @@ ){ int iReg; if( sqlite3ReadSchema(pParse) ) goto pragma_out; sqlite3CodeVerifySchema(pParse, iDb); iReg = ++pParse->nMem; - if( zLeft[0]=='p' ){ + if( sqlite3Tolower(zLeft[0])=='p' ){ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); }else{ - sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3Atoi(zRight)); + sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, + sqlite3AbsInt32(sqlite3Atoi(zRight))); } sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); }else @@ -90486,12 +92463,14 @@ */ if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){ int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ int ii; /* Loop counter */ - /* Force the schema to be loaded on all databases. This cases all - ** database files to be opened and the journal_modes set. */ + /* Force the schema to be loaded on all databases. This causes all + ** database files to be opened and the journal_modes set. This is + ** necessary because subsequent processing must know if the databases + ** are in WAL mode. */ if( sqlite3ReadSchema(pParse) ){ goto pragma_out; } sqlite3VdbeSetNumCols(v, 1); @@ -90635,26 +92614,23 @@ /* ** PRAGMA [database.]cache_size ** PRAGMA [database.]cache_size=N ** ** The first form reports the current local setting for the - ** page cache size. The local setting can be different from - ** the persistent cache size value that is stored in the database - ** file itself. The value returned is the maximum number of - ** pages in the page cache. The second form sets the local - ** page cache size value. It does not change the persistent - ** cache size stored on the disk so the cache size will revert - ** to its default value when the database is closed and reopened. - ** N should be a positive integer. + ** page cache size. The second form sets the local + ** page cache size value. If N is positive then that is the + ** number of pages in the cache. If N is negative, then the + ** number of pages is adjusted so that the cache uses -N kibibytes + ** of memory. */ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ - int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); + int size = sqlite3Atoi(zRight); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } }else @@ -90742,11 +92718,11 @@ if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){ if( !zRight ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); char *proxy_file_path = NULL; sqlite3_file *pFile = sqlite3PagerFile(pPager); - sqlite3OsFileControl(pFile, SQLITE_GET_LOCKPROXYFILE, + sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE, &proxy_file_path); if( proxy_file_path ){ sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, @@ -91031,11 +93007,11 @@ { OP_IfNeg, 1, 0, 0}, /* 1 */ { OP_String8, 0, 3, 0}, /* 2 */ { OP_ResultRow, 3, 1, 0}, }; - int isQuick = (zLeft[0]=='q'); + int isQuick = (sqlite3Tolower(zLeft[0])=='q'); /* Initialize the VDBE program */ if( sqlite3ReadSchema(pParse) ) goto pragma_out; pParse->nMem = 6; sqlite3VdbeSetNumCols(v, 1); @@ -91385,10 +93361,20 @@ db->xWalCallback==sqlite3WalDefaultHook ? SQLITE_PTR_TO_INT(db->pWalArg) : 0); }else #endif + /* + ** PRAGMA shrink_memory + ** + ** This pragma attempts to free as much memory as possible from the + ** current database connection. + */ + if( sqlite3StrICmp(zLeft, "shrink_memory")==0 ){ + sqlite3_db_release_memory(db); + }else + #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases */ if( sqlite3StrICmp(zLeft, "lock_status")==0 ){ @@ -91759,13 +93745,17 @@ DbSetProperty(db, iDb, DB_Empty); } pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ +#ifndef SQLITE_OMIT_DEPRECATED size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } pDb->pSchema->cache_size = size; +#else + pDb->pSchema->cache_size = SQLITE_DEFAULT_CACHE_SIZE; +#endif sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } /* ** file_format==1 Version 3.0.0. @@ -92406,10 +94396,11 @@ Select standin; sqlite3 *db = pParse->db; pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */ if( pNew==0 ){ + assert( db->mallocFailed ); pNew = &standin; memset(pNew, 0, sizeof(*pNew)); } if( pEList==0 ){ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0)); @@ -92433,10 +94424,11 @@ if( pNew!=&standin ) sqlite3DbFree(db, pNew); pNew = 0; }else{ assert( pNew->pSrc!=0 || pParse->nErr>0 ); } + assert( pNew!=&standin ); return pNew; } /* ** Delete the given Select structure and all of its substructures. @@ -93611,11 +95603,14 @@ /* If the column contains an "AS " phrase, use as the name */ zName = sqlite3DbStrDup(db, zName); }else{ Expr *pColExpr = p; /* The expression that is the result column name */ Table *pTab; /* Table associated with this expression */ - while( pColExpr->op==TK_DOT ) pColExpr = pColExpr->pRight; + while( pColExpr->op==TK_DOT ){ + pColExpr = pColExpr->pRight; + assert( pColExpr!=0 ); + } if( pColExpr->op==TK_COLUMN && ALWAYS(pColExpr->pTab!=0) ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; pTab = pColExpr->pTab; if( iCol<0 ) iCol = pTab->iPKey; @@ -94555,20 +96550,20 @@ */ if( op!=TK_ALL ){ for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ struct ExprList_item *pItem; for(j=0, pItem=pOrderBy->a; jiCol>0 ); - if( pItem->iCol==i ) break; + assert( pItem->iOrderByCol>0 ); + if( pItem->iOrderByCol==i ) break; } if( j==nOrderBy ){ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); if( pNew==0 ) return SQLITE_NOMEM; pNew->flags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); - pOrderBy->a[nOrderBy++].iCol = (u16)i; + pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i; } } } /* Compute the comparison permutation and keyinfo that is used with @@ -94580,12 +96575,12 @@ */ aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy); if( aPermute ){ struct ExprList_item *pItem; for(i=0, pItem=pOrderBy->a; iiCol>0 && pItem->iCol<=p->pEList->nExpr ); - aPermute[i] = pItem->iCol - 1; + assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr ); + aPermute[i] = pItem->iOrderByCol - 1; } pKeyMerge = sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1)); if( pKeyMerge ){ pKeyMerge->aSortOrder = (u8*)&pKeyMerge->aColl[nOrderBy]; @@ -94924,13 +96919,12 @@ } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* -** This routine attempts to flatten subqueries in order to speed -** execution. It returns 1 if it makes changes and 0 if no flattening -** occurs. +** This routine attempts to flatten subqueries as a performance optimization. +** This routine returns 1 if it makes changes and 0 if no flattening occurs. ** ** To understand the concept of flattening, consider the following ** query: ** ** SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5 @@ -94968,11 +96962,14 @@ ** (4) has since been expanded to exclude all DISTINCT subqueries. ** ** (6) The subquery does not use aggregates or the outer query is not ** DISTINCT. ** -** (7) The subquery has a FROM clause. +** (7) The subquery has a FROM clause. TODO: For subqueries without +** A FROM clause, consider adding a FROM close with the special +** table sqlite_once that consists of a single row containing a +** single NULL. ** ** (8) The subquery does not use LIMIT or the outer query is not a join. ** ** (9) The subquery does not use LIMIT or the outer query does not use ** aggregates. @@ -95001,15 +96998,18 @@ ** compound clause made up entirely of non-aggregate queries, and ** the parent query: ** ** * is not itself part of a compound select, ** * is not an aggregate or DISTINCT query, and -** * has no other tables or sub-selects in the FROM clause. +** * is not a join ** ** The parent and sub-query may contain WHERE clauses. Subject to ** rules (11), (13) and (14), they may also contain ORDER BY, -** LIMIT and OFFSET clauses. +** LIMIT and OFFSET clauses. The subquery cannot use any compound +** operator other than UNION ALL because all the other compound +** operators have an implied DISTINCT which is disallowed by +** restriction (4). ** ** (18) If the sub-query is a compound select, then all terms of the ** ORDER by clause of the parent must be simple references to ** columns of the sub-query. ** @@ -95017,11 +97017,11 @@ ** have a WHERE clause. ** ** (20) If the sub-query is a compound select, then it must not use ** an ORDER BY clause. Ticket #3773. We could relax this constraint ** somewhat by saying that the terms of the ORDER BY clause must -** appear as unmodified result columns in the outer query. But +** appear as unmodified result columns in the outer query. But we ** have other optimizations in mind to deal with that case. ** ** (21) The subquery does not use LIMIT or the outer query is not ** DISTINCT. (See ticket [752e1646fc]). ** @@ -95146,23 +97146,25 @@ return 0; } for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); + assert( pSub->pSrc!=0 ); if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 || (pSub1->pPrior && pSub1->op!=TK_ALL) - || NEVER(pSub1->pSrc==0) || pSub1->pSrc->nSrc!=1 + || pSub1->pSrc->nSrc<1 ){ return 0; } + testcase( pSub1->pSrc->nSrc>1 ); } /* Restriction 18. */ if( p->pOrderBy ){ int ii; for(ii=0; iipOrderBy->nExpr; ii++){ - if( p->pOrderBy->a[ii].iCol==0 ) return 0; + if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0; } } } /***** If we reach this point, flattening is permitted. *****/ @@ -96181,26 +98183,25 @@ assert( pItem->addrFillSub==0 ); pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); pItem->addrFillSub = topAddr+1; VdbeNoopComment((v, "materialize %s", pItem->pTab->zName)); - if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){ + if( pItem->isCorrelated==0 ){ /* If the subquery is no correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ - int regOnce = ++pParse->nMem; - onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce); + onceAddr = sqlite3CodeOnce(pParse); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); VdbeComment((v, "end %s", pItem->pTab->zName)); sqlite3VdbeChangeP1(v, topAddr, retAddr); - + sqlite3ClearTempRegCache(pParse); } if( /*pParse->nErr ||*/ db->mallocFailed ){ goto select_end; } pParse->nHeight -= sqlite3SelectExprHeight(p); @@ -96491,10 +98492,11 @@ pParse->nMem += pGroupBy->nExpr; sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag); VdbeComment((v, "clear abort flag")); sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag); VdbeComment((v, "indicate accumulator empty")); + sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1); /* Begin a loop that will extract all source rows in GROUP BY order. ** This might involve two separate loops with an OP_Sort in between, or ** it might be a single loop that uses an index to extract information ** in the right order to begin with. @@ -96830,102 +98832,102 @@ sqlite3DbFree(db, sAggInfo.aCol); sqlite3DbFree(db, sAggInfo.aFunc); return rc; } -#if defined(SQLITE_DEBUG) -/* -******************************************************************************* -** The following code is used for testing and debugging only. The code -** that follows does not appear in normal builds. -** -** These routines are used to print out the content of all or part of a -** parse structures such as Select or Expr. Such printouts are useful -** for helping to understand what is happening inside the code generator -** during the execution of complex SELECT statements. -** -** These routine are not called anywhere from within the normal -** code base. Then are intended to be called from within the debugger -** or from temporary "printf" statements inserted for debugging. -*/ -SQLITE_PRIVATE void sqlite3PrintExpr(Expr *p){ - if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ - sqlite3DebugPrintf("(%s", p->u.zToken); - }else{ - sqlite3DebugPrintf("(%d", p->op); - } - if( p->pLeft ){ - sqlite3DebugPrintf(" "); - sqlite3PrintExpr(p->pLeft); - } - if( p->pRight ){ - sqlite3DebugPrintf(" "); - sqlite3PrintExpr(p->pRight); - } - sqlite3DebugPrintf(")"); -} -SQLITE_PRIVATE void sqlite3PrintExprList(ExprList *pList){ - int i; - for(i=0; inExpr; i++){ - sqlite3PrintExpr(pList->a[i].pExpr); - if( inExpr-1 ){ - sqlite3DebugPrintf(", "); - } - } -} -SQLITE_PRIVATE void sqlite3PrintSelect(Select *p, int indent){ - sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p); - sqlite3PrintExprList(p->pEList); - sqlite3DebugPrintf("\n"); - if( p->pSrc ){ - char *zPrefix; - int i; - zPrefix = "FROM"; +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) +/* +** Generate a human-readable description of a the Select object. +*/ +static void explainOneSelect(Vdbe *pVdbe, Select *p){ + sqlite3ExplainPrintf(pVdbe, "SELECT "); + if( p->selFlags & (SF_Distinct|SF_Aggregate) ){ + if( p->selFlags & SF_Distinct ){ + sqlite3ExplainPrintf(pVdbe, "DISTINCT "); + } + if( p->selFlags & SF_Aggregate ){ + sqlite3ExplainPrintf(pVdbe, "agg_flag "); + } + sqlite3ExplainNL(pVdbe); + sqlite3ExplainPrintf(pVdbe, " "); + } + sqlite3ExplainExprList(pVdbe, p->pEList); + sqlite3ExplainNL(pVdbe); + if( p->pSrc && p->pSrc->nSrc ){ + int i; + sqlite3ExplainPrintf(pVdbe, "FROM "); + sqlite3ExplainPush(pVdbe); for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; - sqlite3DebugPrintf("%*s ", indent+6, zPrefix); - zPrefix = ""; + sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor); if( pItem->pSelect ){ - sqlite3DebugPrintf("(\n"); - sqlite3PrintSelect(pItem->pSelect, indent+10); - sqlite3DebugPrintf("%*s)", indent+8, ""); + sqlite3ExplainSelect(pVdbe, pItem->pSelect); + if( pItem->pTab ){ + sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName); + } }else if( pItem->zName ){ - sqlite3DebugPrintf("%s", pItem->zName); - } - if( pItem->pTab ){ - sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName); + sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName); } if( pItem->zAlias ){ - sqlite3DebugPrintf(" AS %s", pItem->zAlias); + sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias); } - if( ipSrc->nSrc-1 ){ - sqlite3DebugPrintf(","); + if( pItem->jointype & JT_LEFT ){ + sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN"); } - sqlite3DebugPrintf("\n"); + sqlite3ExplainNL(pVdbe); } + sqlite3ExplainPop(pVdbe); } if( p->pWhere ){ - sqlite3DebugPrintf("%*s WHERE ", indent, ""); - sqlite3PrintExpr(p->pWhere); - sqlite3DebugPrintf("\n"); + sqlite3ExplainPrintf(pVdbe, "WHERE "); + sqlite3ExplainExpr(pVdbe, p->pWhere); + sqlite3ExplainNL(pVdbe); } if( p->pGroupBy ){ - sqlite3DebugPrintf("%*s GROUP BY ", indent, ""); - sqlite3PrintExprList(p->pGroupBy); - sqlite3DebugPrintf("\n"); + sqlite3ExplainPrintf(pVdbe, "GROUPBY "); + sqlite3ExplainExprList(pVdbe, p->pGroupBy); + sqlite3ExplainNL(pVdbe); } if( p->pHaving ){ - sqlite3DebugPrintf("%*s HAVING ", indent, ""); - sqlite3PrintExpr(p->pHaving); - sqlite3DebugPrintf("\n"); + sqlite3ExplainPrintf(pVdbe, "HAVING "); + sqlite3ExplainExpr(pVdbe, p->pHaving); + sqlite3ExplainNL(pVdbe); } if( p->pOrderBy ){ - sqlite3DebugPrintf("%*s ORDER BY ", indent, ""); - sqlite3PrintExprList(p->pOrderBy); - sqlite3DebugPrintf("\n"); + sqlite3ExplainPrintf(pVdbe, "ORDERBY "); + sqlite3ExplainExprList(pVdbe, p->pOrderBy); + sqlite3ExplainNL(pVdbe); + } + if( p->pLimit ){ + sqlite3ExplainPrintf(pVdbe, "LIMIT "); + sqlite3ExplainExpr(pVdbe, p->pLimit); + sqlite3ExplainNL(pVdbe); + } + if( p->pOffset ){ + sqlite3ExplainPrintf(pVdbe, "OFFSET "); + sqlite3ExplainExpr(pVdbe, p->pOffset); + sqlite3ExplainNL(pVdbe); } } +SQLITE_PRIVATE void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ + if( p==0 ){ + sqlite3ExplainPrintf(pVdbe, "(null-select)"); + return; + } + while( p->pPrior ) p = p->pPrior; + sqlite3ExplainPush(pVdbe); + while( p ){ + explainOneSelect(pVdbe, p); + p = p->pNext; + if( p==0 ) break; + sqlite3ExplainNL(pVdbe); + sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op)); + } + sqlite3ExplainPrintf(pVdbe, "END"); + sqlite3ExplainPop(pVdbe); +} + /* End of the structure debug printing code *****************************************************************************/ #endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ /************** End of select.c **********************************************/ @@ -98032,10 +100034,11 @@ if( db->mallocFailed==0 ){ pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } pProgram->nMem = pSubParse->nMem; pProgram->nCsr = pSubParse->nTab; + pProgram->nOnce = pSubParse->nOnce; pProgram->token = (void *)pTrigger; pPrg->aColmask[0] = pSubParse->oldmask; pPrg->aColmask[1] = pSubParse->newmask; sqlite3VdbeDelete(v); } @@ -98379,12 +100382,12 @@ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ - int regNew; - int regOld = 0; + int regNew; /* Content of the NEW.* table in triggers */ + int regOld = 0; /* Content of OLD.* table in triggers */ int regRowSet = 0; /* Rowset of rows to be updated */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ @@ -98529,10 +100532,11 @@ goto update_cleanup; } #endif /* Allocate required registers. */ + regRowSet = ++pParse->nMem; regOldRowid = regNewRowid = ++pParse->nMem; if( pTrigger || hasFK ){ regOld = pParse->nMem + 1; pParse->nMem += pTab->nCol; } @@ -98563,11 +100567,11 @@ goto update_cleanup; } /* Begin the database scan */ - sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); + sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED ); if( pWInfo==0 ) goto update_cleanup; okOnePass = pWInfo->okOnePass; @@ -98574,11 +100578,10 @@ /* Remember the rowid of every item to be updated. */ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid); if( !okOnePass ){ - regRowSet = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); } /* End the database scan loop. */ @@ -98609,10 +100612,11 @@ break; } } } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + assert( aRegIdx ); if( openAll || aRegIdx[i]>0 ){ KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb, (char*)pKey, P4_KEYINFO_HANDOFF); assert( pParse->nTab>iCur+i+1 ); @@ -98677,13 +100681,14 @@ ** be used eliminates some redundant opcodes. */ newmask = sqlite3TriggerColmask( pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError ); + sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1); for(i=0; inCol; i++){ if( i==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); + /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ }else{ j = aXRef[i]; if( j>=0 ){ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<pIndex; pIdx; pIdx=pIdx->pNext, i++){ + assert( aRegIdx ); if( openAll || aRegIdx[i]>0 ){ sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0); } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); @@ -98969,11 +100975,11 @@ if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db)); return sqlite3_errcode(db); } VVA_ONLY( rc = ) sqlite3_step(pStmt); - assert( rc!=SQLITE_ROW ); + assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) ); return vacuumFinalize(db, pStmt, pzErrMsg); } /* ** Execute zSql on database db. The statement returns exactly @@ -99187,17 +101193,15 @@ " WHERE type='view' OR type='trigger'" " OR (type='table' AND rootpage=0)" ); if( rc ) goto end_of_vacuum; - /* At this point, unless the main db was completely empty, there is now a - ** transaction open on the vacuum database, but not on the main database. - ** Open a btree level transaction on the main database. This allows a - ** call to sqlite3BtreeCopyFile(). The main database btree level - ** transaction is then committed, so the SQL level never knows it was - ** opened for writing. This way, the SQL transaction used to create the - ** temporary database never needs to be committed. + /* At this point, there is a write transaction open on both the + ** vacuum database and the main database. Assuming no error occurs, + ** both transactions are closed by this block - the main database + ** transaction by sqlite3BtreeCopyFile() and the other by an explicit + ** call to sqlite3BtreeCommit(). */ { u32 meta; int i; @@ -100162,11 +102166,11 @@ if( db->aVTrans ){ int i; for(i=0; rc==SQLITE_OK && inVTrans; i++){ VTable *pVTab = db->aVTrans[i]; const sqlite3_module *pMod = pVTab->pMod->pModule; - if( pMod->iVersion>=2 ){ + if( pVTab->pVtab && pMod->iVersion>=2 ){ int (*xMethod)(sqlite3_vtab *, int); switch( op ){ case SAVEPOINT_BEGIN: xMethod = pMod->xSavepoint; pVTab->iSavepoint = iSavepoint+1; @@ -100177,11 +102181,11 @@ default: xMethod = pMod->xRelease; break; } if( xMethod && pVTab->iSavepoint>iSavepoint ){ - rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint); + rc = xMethod(pVTab->pVtab, iSavepoint); } } } } return rc; @@ -100457,25 +102461,35 @@ #define TERM_CODED 0x04 /* This term is already coded */ #define TERM_COPIED 0x08 /* Has a child */ #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 # define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ #else -# define TERM_VNULL 0x00 /* Disabled if not using stat2 */ +# define TERM_VNULL 0x00 /* Disabled if not using stat3 */ #endif /* ** An instance of the following structure holds all information about a ** WHERE clause. Mostly this is a container for one or more WhereTerms. +** +** Explanation of pOuter: For a WHERE clause of the form +** +** a AND ((b AND c) OR (d AND e)) AND f +** +** There are separate WhereClause objects for the whole clause and for +** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the +** subclauses points to the WhereClause object for the whole clause. */ struct WhereClause { Parse *pParse; /* The parser context */ WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */ Bitmask vmask; /* Bitmask identifying virtual table cursors */ + WhereClause *pOuter; /* Outer conjunction */ u8 op; /* Split operator. TK_AND or TK_OR */ + u16 wctrlFlags; /* Might include WHERE_AND_ONLY */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ #if defined(SQLITE_SMALL_STACK) WhereTerm aStatic[1]; /* Initial static space for a[] */ @@ -100600,18 +102614,21 @@ ** Initialize a preallocated WhereClause structure. */ static void whereClauseInit( WhereClause *pWC, /* The WhereClause to be initialized */ Parse *pParse, /* The parsing context */ - WhereMaskSet *pMaskSet /* Mapping from table cursor numbers to bitmasks */ + WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */ + u16 wctrlFlags /* Might include WHERE_AND_ONLY */ ){ pWC->pParse = pParse; pWC->pMaskSet = pMaskSet; + pWC->pOuter = 0; pWC->nTerm = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; pWC->vmask = 0; + pWC->wctrlFlags = wctrlFlags; } /* Forward reference */ static void whereClauseClear(WhereClause*); @@ -100923,40 +102940,42 @@ ){ WhereTerm *pTerm; int k; assert( iCur>=0 ); op &= WO_ALL; - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ - if( pTerm->leftCursor==iCur - && (pTerm->prereqRight & notReady)==0 - && pTerm->u.leftColumn==iColumn - && (pTerm->eOperator & op)!=0 - ){ - if( pIdx && pTerm->eOperator!=WO_ISNULL ){ - Expr *pX = pTerm->pExpr; - CollSeq *pColl; - char idxaff; - int j; - Parse *pParse = pWC->pParse; - - idxaff = pIdx->pTable->aCol[iColumn].affinity; - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; - - /* Figure out the collation sequence required from an index for - ** it to be useful for optimising expression pX. Store this - ** value in variable pColl. - */ - assert(pX->pLeft); - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - assert(pColl || pParse->nErr); - - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; - } - if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; - } - return pTerm; + for(; pWC; pWC=pWC->pOuter){ + for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ + if( pTerm->leftCursor==iCur + && (pTerm->prereqRight & notReady)==0 + && pTerm->u.leftColumn==iColumn + && (pTerm->eOperator & op)!=0 + ){ + if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ + Expr *pX = pTerm->pExpr; + CollSeq *pColl; + char idxaff; + int j; + Parse *pParse = pWC->pParse; + + idxaff = pIdx->pTable->aCol[iColumn].affinity; + if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; + + /* Figure out the collation sequence required from an index for + ** it to be useful for optimising expression pX. Store this + ** value in variable pColl. + */ + assert(pX->pLeft); + pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); + assert(pColl || pParse->nErr); + + for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ + if( NEVER(j>=pIdx->nColumn) ) return 0; + } + if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; + } + return pTerm; + } } } return 0; } @@ -101029,11 +103048,11 @@ int iCol = pRight->iColumn; pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ z = (char *)sqlite3_value_text(pVal); } - sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */ + sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ z = pRight->u.zToken; } if( z ){ @@ -101047,11 +103066,11 @@ pPrefix = sqlite3Expr(db, TK_STRING, z); if( pPrefix ) pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; - sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */ + sqlite3VdbeSetVarmask(v, pRight->iColumn); if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE ** function, then no OP_Variable will be added to the program. ** This causes problems for the sqlite3_bind_parameter_name() @@ -101216,11 +103235,11 @@ assert( pExpr->op==TK_OR ); pTerm->u.pOrInfo = pOrInfo = sqlite3DbMallocZero(db, sizeof(*pOrInfo)); if( pOrInfo==0 ) return; pTerm->wtFlags |= TERM_ORINFO; pOrWc = &pOrInfo->wc; - whereClauseInit(pOrWc, pWC->pParse, pMaskSet); + whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags); whereSplit(pOrWc, pExpr, TK_OR); exprAnalyzeAll(pSrc, pOrWc); if( db->mallocFailed ) return; assert( pOrWc->nTerm>=2 ); @@ -101243,13 +103262,14 @@ Bitmask b = 0; pOrTerm->u.pAndInfo = pAndInfo; pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; pAndWC = &pAndInfo->wc; - whereClauseInit(pAndWC, pWC->pParse, pMaskSet); + whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags); whereSplit(pAndWC, pOrTerm->pExpr, TK_AND); exprAnalyzeAll(pSrc, pAndWC); + pAndWC->pOuter = pWC; testcase( db->mallocFailed ); if( !db->mallocFailed ){ for(j=0, pAndTerm=pAndWC->a; jnTerm; j++, pAndTerm++){ assert( pAndTerm->pExpr ); if( allowedOp(pAndTerm->pExpr->op) ){ @@ -101679,12 +103699,12 @@ pNewTerm->prereqAll = pTerm->prereqAll; } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_STAT2 - /* When sqlite_stat2 histogram data is available an operator of the +#ifdef SQLITE_ENABLE_STAT3 + /* When sqlite_stat3 histogram data is available an operator of the ** form "x IS NOT NULL" can sometimes be evaluated more efficiently ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a ** virtual term of that form. ** ** Note that the virtual term must be tagged with TERM_VNULL. This @@ -101718,11 +103738,11 @@ pTerm->nChild = 1; pTerm->wtFlags |= TERM_COPIED; pNewTerm->prereqAll = pTerm->prereqAll; } } -#endif /* SQLITE_ENABLE_STAT2 */ +#endif /* SQLITE_ENABLE_STAT */ /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ pTerm->prereqRight |= extraRight; @@ -102140,14 +104160,17 @@ const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ - /* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses - ** are used */ + /* The OR-clause optimization is disallowed if the INDEXED BY or + ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */ if( pSrc->notIndexed || pSrc->pIndex!=0 ){ return; + } + if( pWC->wctrlFlags & WHERE_AND_ONLY ){ + return; } /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTermeOperator==WO_OR @@ -102172,12 +104195,14 @@ bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost); }else if( pOrTerm->leftCursor==iCur ){ WhereClause tempWC; tempWC.pParse = pWC->pParse; tempWC.pMaskSet = pWC->pMaskSet; + tempWC.pOuter = pWC; tempWC.op = TK_AND; tempWC.a = pOrTerm; + tempWC.wctrlFlags = 0; tempWC.nTerm = 1; bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); }else{ continue; } @@ -102323,11 +104348,10 @@ WhereTerm *pTerm; /* A single term of the WHERE clause */ WhereTerm *pWCEnd; /* End of pWC->a[] */ int nByte; /* Byte of memory needed for pIdx */ Index *pIdx; /* Object describing the transient index */ Vdbe *v; /* Prepared statement under construction */ - int regIsInit; /* Register set by initialization */ int addrInit; /* Address of the initialization bypass jump */ Table *pTable; /* The table being indexed */ KeyInfo *pKeyinfo; /* Key information for the index */ int addrTop; /* Top of the index fill loop */ int regRecord; /* Register holding an index record */ @@ -102340,12 +104364,11 @@ /* Generate code to skip over the creation and initialization of the ** transient index on 2nd and subsequent iterations of the loop. */ v = pParse->pVdbe; assert( v!=0 ); - regIsInit = ++pParse->nMem; - addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit); + addrInit = sqlite3CodeOnce(pParse); /* Count the number of columns that will be added to the index ** and used to match WHERE clause constraints */ nColumn = 0; pTable = pSrc->pTab; @@ -102766,71 +104789,89 @@ */ bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifdef SQLITE_ENABLE_STAT3 /* -** Argument pIdx is a pointer to an index structure that has an array of -** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column -** stored in Index.aSample. These samples divide the domain of values stored -** the index into (SQLITE_INDEX_SAMPLES+1) regions. -** Region 0 contains all values less than the first sample value. Region -** 1 contains values between the first and second samples. Region 2 contains -** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES -** contains values larger than the last sample. -** -** If the index contains many duplicates of a single value, then it is -** possible that two or more adjacent samples can hold the same value. -** When that is the case, the smallest possible region code is returned -** when roundUp is false and the largest possible region code is returned -** when roundUp is true. -** -** If successful, this function determines which of the regions value -** pVal lies in, sets *piRegion to the region index (a value between 0 -** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK. -** Or, if an OOM occurs while converting text values between encodings, -** SQLITE_NOMEM is returned and *piRegion is undefined. -*/ -#ifdef SQLITE_ENABLE_STAT2 -static int whereRangeRegion( +** Estimate the location of a particular key among all keys in an +** index. Store the results in aStat as follows: +** +** aStat[0] Est. number of rows less than pVal +** aStat[1] Est. number of rows equal to pVal +** +** Return SQLITE_OK on success. +*/ +static int whereKeyStats( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ sqlite3_value *pVal, /* Value to consider */ - int roundUp, /* Return largest valid region if true */ - int *piRegion /* OUT: Region of domain in which value lies */ + int roundUp, /* Round up if true. Round down if false */ + tRowcnt *aStat /* OUT: stats written here */ ){ + tRowcnt n; + IndexSample *aSample; + int i, eType; + int isEq = 0; + i64 v; + double r, rS; + assert( roundUp==0 || roundUp==1 ); - if( ALWAYS(pVal) ){ - IndexSample *aSample = pIdx->aSample; - int i = 0; - int eType = sqlite3_value_type(pVal); - - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - double r = sqlite3_value_double(pVal); - for(i=0; i=SQLITE_TEXT ) break; - if( roundUp ){ - if( aSample[i].u.r>r ) break; - }else{ - if( aSample[i].u.r>=r ) break; - } - } - }else if( eType==SQLITE_NULL ){ - i = 0; - if( roundUp ){ - while( inSample>0 ); + if( pVal==0 ) return SQLITE_ERROR; + n = pIdx->aiRowEst[0]; + aSample = pIdx->aSample; + eType = sqlite3_value_type(pVal); + + if( eType==SQLITE_INTEGER ){ + v = sqlite3_value_int64(pVal); + r = (i64)v; + for(i=0; inSample; i++){ + if( aSample[i].eType==SQLITE_NULL ) continue; + if( aSample[i].eType>=SQLITE_TEXT ) break; + if( aSample[i].eType==SQLITE_INTEGER ){ + if( aSample[i].u.i>=v ){ + isEq = aSample[i].u.i==v; + break; + } + }else{ + assert( aSample[i].eType==SQLITE_FLOAT ); + if( aSample[i].u.r>=r ){ + isEq = aSample[i].u.r==r; + break; + } + } + } + }else if( eType==SQLITE_FLOAT ){ + r = sqlite3_value_double(pVal); + for(i=0; inSample; i++){ + if( aSample[i].eType==SQLITE_NULL ) continue; + if( aSample[i].eType>=SQLITE_TEXT ) break; + if( aSample[i].eType==SQLITE_FLOAT ){ + rS = aSample[i].u.r; + }else{ + rS = aSample[i].u.i; + } + if( rS>=r ){ + isEq = rS==r; + break; + } + } + }else if( eType==SQLITE_NULL ){ + i = 0; + if( aSample[0].eType==SQLITE_NULL ) isEq = 1; + }else{ + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + for(i=0; inSample; i++){ + if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){ + break; + } + } + if( inSample ){ sqlite3 *db = pParse->db; CollSeq *pColl; const u8 *z; - int n; - - /* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */ - assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); - if( eType==SQLITE_BLOB ){ z = (const u8 *)sqlite3_value_blob(pVal); pColl = db->pDfltColl; assert( pColl->enc==SQLITE_UTF8 ); }else{ @@ -102845,16 +104886,16 @@ return SQLITE_NOMEM; } assert( z && pColl && pColl->xCmp ); } n = sqlite3ValueBytes(pVal, pColl->enc); - - for(i=0; inSample; i++){ int c; int eSampletype = aSample[i].eType; - if( eSampletype==SQLITE_NULL || eSampletypeenc!=SQLITE_UTF8 ){ int nSample; char *zSample = sqlite3Utf8to16( db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample @@ -102868,20 +104909,51 @@ }else #endif { c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); } - if( c-roundUp>=0 ) break; + if( c>=0 ){ + if( c==0 ) isEq = 1; + break; + } } } + } - assert( i>=0 && i<=SQLITE_INDEX_SAMPLES ); - *piRegion = i; + /* At this point, aSample[i] is the first sample that is greater than + ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less + ** than pVal. If aSample[i]==pVal, then isEq==1. + */ + if( isEq ){ + assert( inSample ); + aStat[0] = aSample[i].nLt; + aStat[1] = aSample[i].nEq; + }else{ + tRowcnt iLower, iUpper, iGap; + if( i==0 ){ + iLower = 0; + iUpper = aSample[0].nLt; + }else{ + iUpper = i>=pIdx->nSample ? n : aSample[i].nLt; + iLower = aSample[i-1].nEq + aSample[i-1].nLt; + } + aStat[1] = pIdx->avgEq; + if( iLower>=iUpper ){ + iGap = 0; + }else{ + iGap = iUpper - iLower; + } + if( roundUp ){ + iGap = (iGap*2)/3; + }else{ + iGap = iGap/3; + } + aStat[0] = iLower + iGap; } return SQLITE_OK; } -#endif /* #ifdef SQLITE_ENABLE_STAT2 */ +#endif /* SQLITE_ENABLE_STAT3 */ /* ** If expression pExpr represents a literal value, set *pp to point to ** an sqlite3_value structure containing the same value, with affinity ** aff applied to it, before returning. It is the responsibility of the @@ -102895,11 +104967,11 @@ ** ** If neither of the above apply, set *pp to NULL. ** ** If an error occurs, return an error code. Otherwise, SQLITE_OK. */ -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 static int valueFromExpr( Parse *pParse, Expr *pExpr, u8 aff, sqlite3_value **pp @@ -102906,11 +104978,11 @@ ){ if( pExpr->op==TK_VARIABLE || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) ){ int iVar = pExpr->iColumn; - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */ + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); return SQLITE_OK; } return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); } @@ -102943,106 +105015,92 @@ ** ** ... FROM t1 WHERE a > ? AND a < ? ... ** ** then nEq should be passed 0. ** -** The returned value is an integer between 1 and 100, inclusive. A return -** value of 1 indicates that the proposed range scan is expected to visit -** approximately 1/100th (1%) of the rows selected by the nEq equality -** constraints (if any). A return value of 100 indicates that it is expected -** that the range scan will visit every row (100%) selected by the equality -** constraints. +** The returned value is an integer divisor to reduce the estimated +** search space. A return value of 1 means that range constraints are +** no help at all. A return value of 2 means range constraints are +** expected to reduce the search space by half. And so forth... ** -** In the absence of sqlite_stat2 ANALYZE data, each range inequality -** reduces the search space by 3/4ths. Hence a single constraint (x>?) -** results in a return of 25 and a range constraint (x>? AND x?) +** results in a return of 4 and a range constraint (x>? AND xaCol[] of the range-compared column */ WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ - int *piEst /* OUT: Return value */ + double *pRangeDiv /* OUT: Reduce search space by this divisor */ ){ int rc = SQLITE_OK; -#ifdef SQLITE_ENABLE_STAT2 - - if( nEq==0 && p->aSample ){ - sqlite3_value *pLowerVal = 0; - sqlite3_value *pUpperVal = 0; - int iEst; - int iLower = 0; - int iUpper = SQLITE_INDEX_SAMPLES; - int roundUpUpper = 0; - int roundUpLower = 0; +#ifdef SQLITE_ENABLE_STAT3 + + if( nEq==0 && p->nSample ){ + sqlite3_value *pRangeVal; + tRowcnt iLower = 0; + tRowcnt iUpper = p->aiRowEst[0]; + tRowcnt a[2]; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); - roundUpLower = (pLower->eOperator==WO_GT) ?1:0; + if( rc==SQLITE_OK + && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK + ){ + iLower = a[0]; + if( pLower->eOperator==WO_GT ) iLower += a[1]; + } + sqlite3ValueFree(pRangeVal); } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); - roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0; - } - - if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ - sqlite3ValueFree(pLowerVal); - sqlite3ValueFree(pUpperVal); - goto range_est_fallback; - }else if( pLowerVal==0 ){ - rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); - if( pLower ) iLower = iUpper/2; - }else if( pUpperVal==0 ){ - rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); - if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; - }else{ - rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); - if( rc==SQLITE_OK ){ - rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); - } - } - WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper)); - - iEst = iUpper - iLower; - testcase( iEst==SQLITE_INDEX_SAMPLES ); - assert( iEst<=SQLITE_INDEX_SAMPLES ); - if( iEst<1 ){ - *piEst = 50/SQLITE_INDEX_SAMPLES; - }else{ - *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES; - } - sqlite3ValueFree(pLowerVal); - sqlite3ValueFree(pUpperVal); - return rc; - } -range_est_fallback: + if( rc==SQLITE_OK + && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK + ){ + iUpper = a[0]; + if( pUpper->eOperator==WO_LE ) iUpper += a[1]; + } + sqlite3ValueFree(pRangeVal); + } + if( rc==SQLITE_OK ){ + if( iUpper<=iLower ){ + *pRangeDiv = (double)p->aiRowEst[0]; + }else{ + *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower); + } + WHERETRACE(("range scan regions: %u..%u div=%g\n", + (u32)iLower, (u32)iUpper, *pRangeDiv)); + return SQLITE_OK; + } + } #else UNUSED_PARAMETER(pParse); UNUSED_PARAMETER(p); UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); - *piEst = 100; - if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4; - if( pUpper ) *piEst /= 4; + *pRangeDiv = (double)1; + if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4; + if( pUpper ) *pRangeDiv *= (double)4; return rc; } -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 /* ** Estimate the number of rows that will be returned based on ** an equality constraint x=VALUE and where that VALUE occurs in ** the histogram data. This only works when x is the left-most -** column of an index and sqlite_stat2 histogram data is available +** column of an index and sqlite_stat3 histogram data is available ** for that index. When pExpr==NULL that means the constraint is ** "x IS NULL" instead of "x=VALUE". ** ** Write the estimated row count into *pnRow and return SQLITE_OK. ** If unable to make an estimate, leave *pnRow unchanged and return @@ -103058,44 +105116,36 @@ Index *p, /* The index whose left-most column is pTerm */ Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ double *pnRow /* Write the revised row estimate here */ ){ sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ - int iLower, iUpper; /* Range of histogram regions containing pRhs */ u8 aff; /* Column affinity */ int rc; /* Subfunction return code */ - double nRowEst; /* New estimate of the number of rows */ + tRowcnt a[2]; /* Statistics */ assert( p->aSample!=0 ); + assert( p->nSample>0 ); aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pExpr ){ rc = valueFromExpr(pParse, pExpr, aff, &pRhs); if( rc ) goto whereEqualScanEst_cancel; }else{ pRhs = sqlite3ValueNew(pParse->db); } if( pRhs==0 ) return SQLITE_NOTFOUND; - rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower); - if( rc ) goto whereEqualScanEst_cancel; - rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper); - if( rc ) goto whereEqualScanEst_cancel; - WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper)); - if( iLower>=iUpper ){ - nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2); - if( nRowEst<*pnRow ) *pnRow = nRowEst; - }else{ - nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES; - *pnRow = nRowEst; - } - + rc = whereKeyStats(pParse, p, pRhs, 0, a); + if( rc==SQLITE_OK ){ + WHERETRACE(("equality scan regions: %d\n", (int)a[1])); + *pnRow = a[1]; + } whereEqualScanEst_cancel: sqlite3ValueFree(pRhs); return rc; } -#endif /* defined(SQLITE_ENABLE_STAT2) */ +#endif /* defined(SQLITE_ENABLE_STAT3) */ -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 /* ** Estimate the number of rows that will be returned based on ** an IN constraint where the right-hand side of the IN operator ** is a list of values. Example: ** @@ -103114,64 +105164,29 @@ Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ double *pnRow /* Write the revised row estimate here */ ){ - sqlite3_value *pVal = 0; /* One value from list */ - int iLower, iUpper; /* Range of histogram regions containing pRhs */ - u8 aff; /* Column affinity */ - int rc = SQLITE_OK; /* Subfunction return code */ - double nRowEst; /* New estimate of the number of rows */ - int nSpan = 0; /* Number of histogram regions spanned */ - int nSingle = 0; /* Histogram regions hit by a single value */ - int nNotFound = 0; /* Count of values that are not constants */ - int i; /* Loop counter */ - u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */ - u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */ + int rc = SQLITE_OK; /* Subfunction return code */ + double nEst; /* Number of rows for a single term */ + double nRowEst = (double)0; /* New estimate of the number of rows */ + int i; /* Loop counter */ assert( p->aSample!=0 ); - aff = p->pTable->aCol[p->aiColumn[0]].affinity; - memset(aSpan, 0, sizeof(aSpan)); - memset(aSingle, 0, sizeof(aSingle)); - for(i=0; inExpr; i++){ - sqlite3ValueFree(pVal); - rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal); - if( rc ) break; - if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ - nNotFound++; - continue; - } - rc = whereRangeRegion(pParse, p, pVal, 0, &iLower); - if( rc ) break; - rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper); - if( rc ) break; - if( iLower>=iUpper ){ - aSingle[iLower] = 1; - }else{ - assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES ); - while( iLowernExpr; i++){ + nEst = p->aiRowEst[0]; + rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst); + nRowEst += nEst; } if( rc==SQLITE_OK ){ - for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){ - if( aSpan[i] ){ - nSpan++; - }else if( aSingle[i] ){ - nSingle++; - } - } - nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES) - + nNotFound*p->aiRowEst[1]; if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; - WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n", - nSpan, nSingle, nNotFound, nRowEst)); + WHERETRACE(("IN row estimate: est=%g\n", nRowEst)); } - sqlite3ValueFree(pVal); return rc; } -#endif /* defined(SQLITE_ENABLE_STAT2) */ +#endif /* defined(SQLITE_ENABLE_STAT3) */ /* ** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the @@ -103214,11 +105229,11 @@ Index *pProbe; /* An index we are evaluating */ Index *pIdx; /* Copy of pProbe, or zero for IPK index */ int eqTermMask; /* Current mask of valid equality operators */ int idxEqTermMask; /* Index mask of valid equality operators */ Index sPk; /* A fake index object for the primary key */ - unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ + tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */ /* Initialize the cost to a worst-case value */ memset(pCost, 0, sizeof(*pCost)); @@ -103269,14 +105284,14 @@ } /* Loop over all indices looking for the best one to use */ for(; pProbe; pIdx=pProbe=pProbe->pNext){ - const unsigned int * const aiRowEst = pProbe->aiRowEst; + const tRowcnt * const aiRowEst = pProbe->aiRowEst; double cost; /* Cost of using pProbe */ double nRow; /* Estimated number of rows in result set */ - double log10N; /* base-10 logarithm of nRow (inexact) */ + double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */ int rev; /* True to scan in reverse order */ int wsFlags = 0; Bitmask used = 0; /* The following variables are populated based on the properties of @@ -103312,18 +105327,16 @@ ** Set to true if there was at least one "x IN (SELECT ...)" term used ** in determining the value of nInMul. Note that the RHS of the ** IN operator must be a SELECT, not a value list, for this variable ** to be true. ** - ** estBound: - ** An estimate on the amount of the table that must be searched. A - ** value of 100 means the entire table is searched. Range constraints - ** might reduce this to a value less than 100 to indicate that only - ** a fraction of the table needs searching. In the absence of - ** sqlite_stat2 ANALYZE data, a single inequality reduces the search - ** space to 1/4rd its original size. So an x>? constraint reduces - ** estBound to 25. Two constraints (x>? AND xnColumn; nEq++){ int j = pProbe->aiColumn[nEq]; pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); if( pTerm==0 ) break; wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); + testcase( pTerm->pWC!=pWC ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ @@ -103374,41 +105388,51 @@ nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; #endif used |= pTerm->prereqRight; } - - /* Determine the value of estBound. */ - if( nEqnColumn && pProbe->bUnordered==0 ){ - int j = pProbe->aiColumn[nEq]; + + /* If the index being considered is UNIQUE, and there is an equality + ** constraint for all columns in the index, then this search will find + ** at most a single row. In this case set the WHERE_UNIQUE flag to + ** indicate this to the caller. + ** + ** Otherwise, if the search may find more than one row, test to see if + ** there is a range constraint on indexed column (nEq+1) that can be + ** optimized using the index. + */ + if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ + testcase( wsFlags & WHERE_COLUMN_IN ); + testcase( wsFlags & WHERE_COLUMN_NULL ); + if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ + wsFlags |= WHERE_UNIQUE; + } + }else if( pProbe->bUnordered==0 ){ + int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]); if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); - whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound); + whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv); if( pTop ){ nBound = 1; wsFlags |= WHERE_TOP_LIMIT; used |= pTop->prereqRight; + testcase( pTop->pWC!=pWC ); } if( pBtm ){ nBound++; wsFlags |= WHERE_BTM_LIMIT; used |= pBtm->prereqRight; + testcase( pBtm->pWC!=pWC ); } wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); } - }else if( pProbe->onError!=OE_None ){ - testcase( wsFlags & WHERE_COLUMN_IN ); - testcase( wsFlags & WHERE_COLUMN_NULL ); - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - wsFlags |= WHERE_UNIQUE; - } } /* If there is an ORDER BY clause and the index being considered will ** naturally scan rows in the required order, set the appropriate flags ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index @@ -103458,32 +105482,34 @@ if( bInEst && nRow*2>aiRowEst[0] ){ nRow = aiRowEst[0]/2; nInMul = (int)(nRow / aiRowEst[nEq]); } -#ifdef SQLITE_ENABLE_STAT2 +#ifdef SQLITE_ENABLE_STAT3 /* If the constraint is of the form x=VALUE or x IN (E1,E2,...) ** and we do not think that values of x are unique and if histogram ** data is available for column x, then it might be possible ** to get a better estimate on the number of rows based on ** VALUE and how common that value is according to the histogram. */ if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){ + assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ testcase( pFirstTerm->eOperator==WO_EQ ); testcase( pFirstTerm->eOperator==WO_ISNULL ); whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); - }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){ + }else if( bInEst==0 ){ + assert( pFirstTerm->eOperator==WO_IN ); whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); } } -#endif /* SQLITE_ENABLE_STAT2 */ +#endif /* SQLITE_ENABLE_STAT3 */ /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ - nRow = (nRow * (double)estBound) / (double)100; + nRow = nRow/rangeDiv; if( nRow<1 ) nRow = 1; /* Experiments run on real SQLite databases show that the time needed ** to do a binary search to locate a row in a table or index is roughly ** log10(N) times the time to move from one row to the next row within @@ -103490,11 +105516,11 @@ ** a table or index. The actual times can vary, with the size of ** records being an important factor. Both moves and searches are ** slower with larger records, presumably because fewer records fit ** on one page and hence more pages have to be fetched. ** - ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do + ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do ** not give us data on the relative sizes of table and index records. ** So this computation assumes table records are about twice as big ** as index records */ if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ @@ -103608,14 +105634,14 @@ if( nRow<2 ) nRow = 2; } WHERETRACE(( - "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" + "%s(%s): nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%x\n" " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), - nEq, nInMul, estBound, bSort, bLookup, wsFlags, + nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags, notReady, log10N, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. @@ -104013,14 +106039,16 @@ explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "="); } j = i; if( pPlan->wsFlags&WHERE_BTM_LIMIT ){ - explainAppendTerm(&txt, i++, aCol[aiColumn[j]].zName, ">"); + char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; + explainAppendTerm(&txt, i++, z, ">"); } if( pPlan->wsFlags&WHERE_TOP_LIMIT ){ - explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<"); + char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; + explainAppendTerm(&txt, i, z, "<"); } sqlite3StrAccumAppend(&txt, ")", 1); return sqlite3StrAccumFinish(&txt); } @@ -104115,11 +106143,12 @@ */ static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ - Bitmask notReady /* Which tables are currently available */ + Bitmask notReady, /* Which tables are currently available */ + Expr *pWhere /* Complete WHERE clause */ ){ int j, k; /* Loop counters */ int iCur; /* The VDBE cursor for the table */ int addrNxt; /* Where to jump to continue with the next IN case */ int omitTable; /* True if we use the index only */ @@ -104373,11 +106402,11 @@ char *zStartAff; /* Affinity for start of range constraint */ char *zEndAff; /* Affinity for end of range constraint */ pIdx = pLevel->plan.u.pIdx; iIdxCur = pLevel->iIdxCur; - k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */ + k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]); /* If this loop satisfies a sort order (pOrderBy) request that ** was passed to this function to implement a "SELECT min(x) ..." ** query, then the caller will only allow the loop to run for ** a single iteration. This means that the first row returned @@ -104419,11 +106448,13 @@ /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). */ - if( nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC) ){ + if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) + || (bRev && pIdx->nColumn==nEq) + ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); } testcase( pRangeStart && pRangeStart->eOperator & WO_LE ); testcase( pRangeStart && pRangeStart->eOperator & WO_GE ); @@ -104597,11 +106628,12 @@ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ - int ii; + int ii; /* Loop counter */ + Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ pTerm = pLevel->plan.u.pTerm; assert( pTerm!=0 ); assert( pTerm->eOperator==WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); @@ -104646,18 +106678,33 @@ regRowset = ++pParse->nMem; regRowid = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); } iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); + + /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y + ** Then for every term xN, evaluate as the subexpression: xN AND z + ** That way, terms in y that are factored into the disjunction will + ** be picked up by the recursive calls to sqlite3WhereBegin() below. + */ + if( pWC->nTerm>1 ){ + pAndExpr = sqlite3ExprAlloc(pParse->db, TK_AND, 0, 0); + pAndExpr->pRight = pWhere; + } for(ii=0; iinTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ + Expr *pOrExpr = pOrTerm->pExpr; + if( pAndExpr ){ + pAndExpr->pLeft = pOrExpr; + pOrExpr = pAndExpr; + } /* Loop through table entries that match term pOrTerm. */ - pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0, - WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, + WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY | WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ explainOneScan( pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 ); @@ -104681,10 +106728,11 @@ /* Finish the loop through table entries that match term pOrTerm. */ sqlite3WhereEnd(pSubWInfo); } } } + sqlite3DbFree(pParse->db, pAndExpr); sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab); @@ -104962,11 +107010,11 @@ /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); - whereClauseInit(pWC, pParse, pMaskSet); + whereClauseInit(pWC, pParse, pMaskSet, wctrlFlags); sqlite3ExprCodeConstants(pParse, pWhere); whereSplit(pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */ /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. @@ -105201,11 +107249,12 @@ assert( bestJ>=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d for loop %d" " with cost=%g and nRow=%g\n", bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow)); - if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ + /* The ALWAYS() that follows was added to hush up clang scan-build */ + if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 && ALWAYS(ppOrderBy) ){ *ppOrderBy = 0; } if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ assert( pWInfo->eDistinct==0 ); pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; @@ -105290,11 +107339,11 @@ int iCur = pTabItem->iCursor; sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); }else #endif if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OMIT_OPEN)==0 ){ + && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); testcase( pTab->nCol==BMS-1 ); testcase( pTab->nCol==BMS ); if( !pWInfo->okOnePass && pTab->nCola[i]; explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags); - notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); + notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady, pWhere); pWInfo->iContinue = pLevel->addrCont; } #ifdef SQLITE_TEST /* For testing and debugging use only */ /* Record in the query plan information about the current table @@ -105470,11 +107519,11 @@ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 + && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ int ws = pLevel->plan.wsFlags; if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } @@ -107167,11 +109216,10 @@ /* Here code is inserted which will execute if the parser ** stack every overflows */ UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */ sqlite3ErrorMsg(pParse, "parser stack overflow"); - pParse->parseError = 1; sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */ } /* ** Perform a shift action. @@ -107890,10 +109938,13 @@ break; case 112: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0}; sqlite3Select(pParse, yymsp[0].minor.yy387, &dest); + sqlite3ExplainBegin(pParse->pVdbe); + sqlite3ExplainSelect(pParse->pVdbe, yymsp[0].minor.yy387); + sqlite3ExplainFinish(pParse->pVdbe); sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy387); } break; case 113: /* select ::= oneselect */ {yygotominor.yy387 = yymsp[0].minor.yy387;} @@ -108768,11 +110819,10 @@ #define TOKEN (yyminor.yy0) UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); - pParse->parseError = 1; sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ } /* ** The following is executed when the parser accepts @@ -108817,11 +110867,13 @@ sqlite3ParserTOKENTYPE yyminor /* The value for the token */ sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */ ){ YYMINORTYPE yyminorunion; int yyact; /* The parser action. */ +#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) int yyendofinput; /* True if we are at the end of input */ +#endif #ifdef YYERRORSYMBOL int yyerrorhit = 0; /* True if yymajor has invoked an error */ #endif yyParser *yypParser; /* The parser */ @@ -108840,11 +110892,13 @@ yypParser->yyerrcnt = -1; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; } yyminorunion.yy0 = yyminor; +#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) yyendofinput = (yymajor==0); +#endif sqlite3ParserARG_STORE; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); @@ -108852,11 +110906,10 @@ #endif do{ yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); if( yyactyyerrcnt--; yymajor = YYNOCODE; }else if( yyact < YYNSTATE + YYNRULE ){ yy_reduce(yypParser,yyact-YYNSTATE); @@ -109360,11 +111413,11 @@ *tokenType = TK_SPACE; return i; } case '-': { if( z[1]=='-' ){ - /* IMP: R-15891-05542 -- syntax diagram for comments */ + /* IMP: R-50417-27976 -- syntax diagram for comments */ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } *tokenType = TK_MINUS; @@ -109393,11 +111446,11 @@ case '/': { if( z[1]!='*' || z[2]==0 ){ *tokenType = TK_SLASH; return 1; } - /* IMP: R-15891-05542 -- syntax diagram for comments */ + /* IMP: R-50417-27976 -- syntax diagram for comments */ for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } @@ -110187,12 +112240,12 @@ /* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function ** returns an integer equal to SQLITE_VERSION_NUMBER. */ SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } -/* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns -** zero if and only if SQLite was compiled mutexing code omitted due to +/* IMPLEMENTATION-OF: R-20790-14025 The sqlite3_threadsafe() function returns +** zero if and only if SQLite was compiled with mutexing code omitted due to ** the SQLITE_THREADSAFE compile-time option being set to 0. */ SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } #if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) @@ -110244,11 +112297,11 @@ ** ** * Recursive calls to this routine from thread X return immediately ** without blocking. */ SQLITE_API int sqlite3_initialize(void){ - sqlite3_mutex *pMaster; /* The main static mutex */ + MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ int rc; /* Result code */ #ifdef SQLITE_OMIT_WSD rc = sqlite3_wsd_init(4096, 24); if( rc!=SQLITE_OK ){ @@ -110278,11 +112331,11 @@ ** This operation is protected by the STATIC_MASTER mutex. Note that ** MutexAlloc() is called for a static mutex prior to initializing the ** malloc subsystem - this implies that the allocation of a static ** mutex must not require support from the malloc subsystem. */ - pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) sqlite3_mutex_enter(pMaster); sqlite3GlobalConfig.isMutexInit = 1; if( !sqlite3GlobalConfig.isMallocInit ){ rc = sqlite3MallocInit(); } @@ -110377,12 +112430,12 @@ /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT ** compile-time option. */ #ifdef SQLITE_EXTRA_INIT if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){ - int SQLITE_EXTRA_INIT(void); - rc = SQLITE_EXTRA_INIT(); + int SQLITE_EXTRA_INIT(const char*); + rc = SQLITE_EXTRA_INIT(0); } #endif return rc; } @@ -110395,10 +112448,14 @@ ** on when SQLite is already shut down. If SQLite is already shut down ** when this routine is invoked, then this routine is a harmless no-op. */ SQLITE_API int sqlite3_shutdown(void){ if( sqlite3GlobalConfig.isInit ){ +#ifdef SQLITE_EXTRA_SHUTDOWN + void SQLITE_EXTRA_SHUTDOWN(void); + SQLITE_EXTRA_SHUTDOWN(); +#endif sqlite3_os_end(); sqlite3_reset_auto_extension(); sqlite3GlobalConfig.isInit = 0; } if( sqlite3GlobalConfig.isPCacheInit ){ @@ -110503,20 +112560,29 @@ sqlite3GlobalConfig.nPage = va_arg(ap, int); break; } case SQLITE_CONFIG_PCACHE: { - /* Specify an alternative page cache implementation */ - sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*); + /* no-op */ break; } - case SQLITE_CONFIG_GETPCACHE: { - if( sqlite3GlobalConfig.pcache.xInit==0 ){ + /* now an error */ + rc = SQLITE_ERROR; + break; + } + + case SQLITE_CONFIG_PCACHE2: { + /* Specify an alternative page cache implementation */ + sqlite3GlobalConfig.pcache2 = *va_arg(ap, sqlite3_pcache_methods2*); + break; + } + case SQLITE_CONFIG_GETPCACHE2: { + if( sqlite3GlobalConfig.pcache2.xInit==0 ){ sqlite3PCacheSetDefault(); } - *va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache; + *va_arg(ap, sqlite3_pcache_methods2*) = sqlite3GlobalConfig.pcache2; break; } #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) case SQLITE_CONFIG_HEAP: { @@ -110611,25 +112677,25 @@ ** both at the same time. */ if( db->lookaside.bMalloced ){ sqlite3_free(db->lookaside.pStart); } - /* The size of a lookaside slot needs to be larger than a pointer - ** to be useful. + /* The size of a lookaside slot after ROUNDDOWN8 needs to be larger + ** than a pointer to be useful. */ + sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; if( cnt<0 ) cnt = 0; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ - sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ sqlite3BeginBenignMalloc(); pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); + if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; }else{ - sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ pStart = pBuf; } db->lookaside.pStart = pStart; db->lookaside.pFree = 0; db->lookaside.sz = (u16)sz; @@ -110658,10 +112724,30 @@ ** Return the mutex associated with a database connection. */ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){ return db->mutex; } + +/* +** Free up as much memory as we can from the given database +** connection. +*/ +SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){ + int i; + sqlite3_mutex_enter(db->mutex); + sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + Pager *pPager = sqlite3BtreePager(pBt); + sqlite3PagerShrink(pPager); + } + } + sqlite3BtreeLeaveAll(db); + sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; +} /* ** Configuration settings for an individual database connection */ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ @@ -111352,17 +113438,17 @@ sqlite3 *db, const char *zName, int nArg ){ int nName = sqlite3Strlen30(zName); - int rc; + int rc = SQLITE_OK; sqlite3_mutex_enter(db->mutex); if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ - sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, - 0, sqlite3InvalidFunction, 0, 0, 0); + rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, + 0, sqlite3InvalidFunction, 0, 0, 0); } - rc = sqlite3ApiExit(db, SQLITE_OK); + rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #ifndef SQLITE_OMIT_TRACE @@ -111766,11 +113852,10 @@ */ static int createCollation( sqlite3* db, const char *zName, u8 enc, - u8 collType, void* pCtx, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDel)(void*) ){ CollSeq *pColl; @@ -111831,11 +113916,10 @@ if( pColl==0 ) return SQLITE_NOMEM; pColl->xCmp = xCompare; pColl->pUser = pCtx; pColl->xDel = xDel; pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED)); - pColl->type = collType; sqlite3Error(db, SQLITE_OK, 0); return SQLITE_OK; } @@ -112292,27 +114376,22 @@ /* Add the default collation sequence BINARY. BINARY works for both UTF-8 ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. */ - createCollation(db, "BINARY", SQLITE_UTF8, SQLITE_COLL_BINARY, 0, - binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16BE, SQLITE_COLL_BINARY, 0, - binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16LE, SQLITE_COLL_BINARY, 0, - binCollFunc, 0); - createCollation(db, "RTRIM", SQLITE_UTF8, SQLITE_COLL_USER, (void*)1, - binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0); + createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0); if( db->mallocFailed ){ goto opendb_out; } db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0); assert( db->pDfltColl!=0 ); /* Also add a UTF-8 case-insensitive collation sequence. */ - createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, - nocaseCollatingFunc, 0); + createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); /* Parse the filename/URI argument. */ db->openFlags = flags; rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); if( rc!=SQLITE_OK ){ @@ -112357,14 +114436,17 @@ sqlite3RegisterBuiltinFunctions(db); /* Load automatic extensions - extensions that have been registered ** using the sqlite3_automatic_extension() API. */ - sqlite3AutoLoadExtensions(db); rc = sqlite3_errcode(db); - if( rc!=SQLITE_OK ){ - goto opendb_out; + if( rc==SQLITE_OK ){ + sqlite3AutoLoadExtensions(db); + rc = sqlite3_errcode(db); + if( rc!=SQLITE_OK ){ + goto opendb_out; + } } #ifdef SQLITE_ENABLE_FTS1 if( !db->mallocFailed ){ extern int sqlite3Fts1Init(sqlite3*); @@ -112420,10 +114502,11 @@ if( db ){ assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); } rc = sqlite3_errcode(db); + assert( db!=0 || rc==SQLITE_NOMEM ); if( rc==SQLITE_NOMEM ){ sqlite3_close(db); db = 0; }else if( rc!=SQLITE_OK ){ db->magic = SQLITE_MAGIC_SICK; @@ -112500,11 +114583,11 @@ int(*xCompare)(void*,int,const void*,int,const void*) ){ int rc; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); - rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0); + rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, 0); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } @@ -112520,11 +114603,11 @@ void(*xDel)(void*) ){ int rc; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); - rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, xDel); + rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xDel); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } @@ -112543,11 +114626,11 @@ char *zName8; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE); if( zName8 ){ - rc = createCollation(db, zName8, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0); + rc = createCollation(db, zName8, (u8)enc, pCtx, xCompare, 0); sqlite3DbFree(db, zName8); } rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; @@ -113026,19 +115109,10 @@ rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; break; } #endif - /* sqlite3_test_control(SQLITE_TESTCTRL_PGHDRSZ) - ** - ** Return the size of a pcache header in bytes. - */ - case SQLITE_TESTCTRL_PGHDRSZ: { - rc = sizeof(PgHdr); - break; - } - /* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree); ** ** Pass pFree into sqlite3ScratchFree(). ** If sz>0 then allocate a scratch buffer into pNew. */ @@ -113062,10 +115136,26 @@ case SQLITE_TESTCTRL_LOCALTIME_FAULT: { sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); break; } +#if defined(SQLITE_ENABLE_TREE_EXPLAIN) + /* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, + ** sqlite3_stmt*,const char**); + ** + ** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds + ** a string that describes the optimized parse tree. This test-control + ** returns a pointer to that string. + */ + case SQLITE_TESTCTRL_EXPLAIN_STMT: { + sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*); + const char **pzRet = va_arg(ap, const char**); + *pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt); + break; + } +#endif + } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } @@ -113080,16 +115170,55 @@ ** query parameter we seek. This routine returns the value of the zParam ** parameter if it exists. If the parameter does not exist, this routine ** returns a NULL pointer. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){ + if( zFilename==0 ) return 0; zFilename += sqlite3Strlen30(zFilename) + 1; while( zFilename[0] ){ int x = strcmp(zFilename, zParam); zFilename += sqlite3Strlen30(zFilename) + 1; if( x==0 ) return zFilename; zFilename += sqlite3Strlen30(zFilename) + 1; + } + return 0; +} + +/* +** Return a boolean value for a query parameter. +*/ +SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ + const char *z = sqlite3_uri_parameter(zFilename, zParam); + return z ? sqlite3GetBoolean(z) : (bDflt!=0); +} + +/* +** Return a 64-bit integer value for a query parameter. +*/ +SQLITE_API sqlite3_int64 sqlite3_uri_int64( + const char *zFilename, /* Filename as passed to xOpen */ + const char *zParam, /* URI parameter sought */ + sqlite3_int64 bDflt /* return if parameter is missing */ +){ + const char *z = sqlite3_uri_parameter(zFilename, zParam); + sqlite3_int64 v; + if( z && sqlite3Atoi64(z, &v, sqlite3Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){ + bDflt = v; + } + return bDflt; +} + +/* +** Return the filename of the database associated with a database +** connection. +*/ +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ + int i; + for(i=0; inDb; i++){ + if( db->aDb[i].pBt && sqlite3StrICmp(zDbName, db->aDb[i].zName)==0 ){ + return sqlite3BtreeGetFilename(db->aDb[i].pBt); + } } return 0; } /************** End of main.c ************************************************/ @@ -114148,10 +116277,17 @@ #else # define TESTONLY(X) #endif #endif /* SQLITE_AMALGAMATION */ + +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); +# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt() +#else +# define FTS_CORRUPT_VTAB SQLITE_CORRUPT_VTAB +#endif typedef struct Fts3Table Fts3Table; typedef struct Fts3Cursor Fts3Cursor; typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; @@ -114176,10 +116312,11 @@ const char *zDb; /* logical database name */ const char *zName; /* virtual table name */ int nColumn; /* number of named columns in virtual table */ char **azColumn; /* column names. malloced */ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ + char *zContentTbl; /* content=xxx option, or NULL */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ sqlite3_stmt *aStmt[27]; @@ -114216,11 +116353,11 @@ } *aIndex; int nMaxPendingData; /* Max pending data before flush to disk */ int nPendingData; /* Current bytes of pending data */ sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ -#if defined(SQLITE_DEBUG) +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) /* State variables used for validating that the transaction control ** methods of the virtual table are called at appropriate times. These ** values do not contribution to the FTS computation; they are used for ** verifying the SQLite core. */ @@ -114301,10 +116438,11 @@ */ struct Fts3PhraseToken { char *z; /* Text of the token */ int n; /* Number of bytes in buffer z */ int isPrefix; /* True if token ends with a "*" character */ + int bFirst; /* True if token must appear at position 0 */ /* Variables above this point are populated when the expression is ** parsed (by code in fts3_expr.c). Below this point the variables are ** used when evaluating the expression. */ Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ @@ -114419,10 +116557,11 @@ #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 #define FTS3_SEGMENT_PREFIX 0x00000008 #define FTS3_SEGMENT_SCAN 0x00000010 +#define FTS3_SEGMENT_FIRST 0x00000020 /* Type passed as 4th argument to SegmentReaderIterate() */ struct Fts3SegFilter { const char *zTerm; int nTerm; @@ -114458,12 +116597,12 @@ SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); - SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); +SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); /* fts3_tokenizer.c */ SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, @@ -114478,11 +116617,11 @@ ); SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *); /* fts3_expr.c */ SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, - char **, int, int, const char *, int, Fts3Expr ** + char **, int, int, int, const char *, int, Fts3Expr ** ); SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); @@ -114649,11 +116788,11 @@ char **pp, char *pStart, sqlite3_int64 *pVal ){ sqlite3_int64 iVal; - char *p = *pp; + char *p; /* Pointer p now points at the first byte past the varint we are ** interested in. So, unless the doclist is corrupt, the 0x80 bit is ** clear on character p[-1]. */ for(p = (*pp)-2; p>=pStart && *p&0x80; p--); @@ -114679,10 +116818,11 @@ sqlite3_finalize(p->aStmt[i]); } sqlite3_free(p->zSegmentsTbl); sqlite3_free(p->zReadExprlist); sqlite3_free(p->zWriteExprlist); + sqlite3_free(p->zContentTbl); /* Invoke the tokenizer destructor to free the tokenizer. */ p->pTokenizer->pModule->xDestroy(p->pTokenizer); sqlite3_free(p); @@ -114718,20 +116858,23 @@ /* ** The xDestroy() virtual table method. */ static int fts3DestroyMethod(sqlite3_vtab *pVtab){ - int rc = SQLITE_OK; /* Return code */ Fts3Table *p = (Fts3Table *)pVtab; - sqlite3 *db = p->db; + int rc = SQLITE_OK; /* Return code */ + const char *zDb = p->zDb; /* Name of database (e.g. "main", "temp") */ + sqlite3 *db = p->db; /* Database handle */ /* Drop the shadow tables */ - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", p->zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", p->zDb,p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", p->zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", p->zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", p->zDb, p->zName); + if( p->zContentTbl==0 ){ + fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName); + } + fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName); + fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName); + fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName); + fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName); /* If everything has worked, invoke fts3DisconnectMethod() to free the ** memory associated with the Fts3Table structure and return SQLITE_OK. ** Otherwise, return an SQLite error code. */ @@ -114789,27 +116932,31 @@ ** %_stat tables required by FTS4. */ static int fts3CreateTables(Fts3Table *p){ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ - char *zContentCols; /* Columns of %_content table */ sqlite3 *db = p->db; /* The database connection */ - /* Create a list of user columns for the content table */ - zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY"); - for(i=0; zContentCols && inColumn; i++){ - char *z = p->azColumn[i]; - zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); - } - if( zContentCols==0 ) rc = SQLITE_NOMEM; - - /* Create the content table */ - fts3DbExec(&rc, db, - "CREATE TABLE %Q.'%q_content'(%s)", - p->zDb, p->zName, zContentCols - ); - sqlite3_free(zContentCols); + if( p->zContentTbl==0 ){ + char *zContentCols; /* Columns of %_content table */ + + /* Create a list of user columns for the content table */ + zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY"); + for(i=0; zContentCols && inColumn; i++){ + char *z = p->azColumn[i]; + zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); + } + if( zContentCols==0 ) rc = SQLITE_NOMEM; + + /* Create the content table */ + fts3DbExec(&rc, db, + "CREATE TABLE %Q.'%q_content'(%s)", + p->zDb, p->zName, zContentCols + ); + sqlite3_free(zContentCols); + } + /* Create other tables */ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);", p->zDb, p->zName ); @@ -114915,10 +117062,11 @@ if( *pRc==SQLITE_OK ){ va_list ap; char *z; va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); + va_end(ap); if( z && *pz ){ char *z2 = sqlite3_mprintf("%s%s", *pz, z); sqlite3_free(z); z = z2; } @@ -114956,12 +117104,12 @@ } return zRet; } /* -** Return a list of comma separated SQL expressions that could be used -** in a SELECT statement such as the following: +** Return a list of comma separated SQL expressions and a FROM clause that +** could be used in a SELECT statement such as the following: ** ** SELECT FROM %_content AS x ... ** ** to return the docid, followed by each column of text data in order ** from left to write. If parameter zFunc is not NULL, then instead of @@ -114968,11 +117116,11 @@ ** being returned directly each column of text data is passed to an SQL ** function named zFunc first. For example, if zFunc is "unzip" and the ** table has the three user-defined columns "a", "b", and "c", the following ** string is returned: ** -** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')" +** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c') FROM %_content AS x" ** ** The pointer returned points to a buffer allocated by sqlite3_malloc(). It ** is the responsibility of the caller to eventually free it. ** ** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and @@ -114984,20 +117132,32 @@ char *zRet = 0; char *zFree = 0; char *zFunction; int i; - if( !zFunc ){ - zFunction = ""; + if( p->zContentTbl==0 ){ + if( !zFunc ){ + zFunction = ""; + }else{ + zFree = zFunction = fts3QuoteId(zFunc); + } + fts3Appendf(pRc, &zRet, "docid"); + for(i=0; inColumn; i++){ + fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); + } + sqlite3_free(zFree); }else{ - zFree = zFunction = fts3QuoteId(zFunc); + fts3Appendf(pRc, &zRet, "rowid"); + for(i=0; inColumn; i++){ + fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]); + } } - fts3Appendf(pRc, &zRet, "docid"); - for(i=0; inColumn; i++){ - fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); - } - sqlite3_free(zFree); + fts3Appendf(pRc, &zRet, "FROM '%q'.'%q%s' AS x", + p->zDb, + (p->zContentTbl ? p->zContentTbl : p->zName), + (p->zContentTbl ? "" : "_content") + ); return zRet; } /* ** Return a list of N comma separated question marks, where N is the number @@ -115050,11 +117210,11 @@ ** the output value undefined. Otherwise SQLITE_OK is returned. ** ** This function is used when parsing the "prefix=" FTS4 parameter. */ static int fts3GobbleInt(const char **pp, int *pnOut){ - const char *p = *pp; /* Iterator pointer */ + const char *p; /* Iterator pointer */ int nInt = 0; /* Output value */ for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ nInt = nInt * 10 + (p[0] - '0'); } @@ -115116,10 +117276,95 @@ } } return SQLITE_OK; } + +/* +** This function is called when initializing an FTS4 table that uses the +** content=xxx option. It determines the number of and names of the columns +** of the new FTS4 table. +** +** The third argument passed to this function is the value passed to the +** config=xxx option (i.e. "xxx"). This function queries the database for +** a table of that name. If found, the output variables are populated +** as follows: +** +** *pnCol: Set to the number of columns table xxx has, +** +** *pnStr: Set to the total amount of space required to store a copy +** of each columns name, including the nul-terminator. +** +** *pazCol: Set to point to an array of *pnCol strings. Each string is +** the name of the corresponding column in table xxx. The array +** and its contents are allocated using a single allocation. It +** is the responsibility of the caller to free this allocation +** by eventually passing the *pazCol value to sqlite3_free(). +** +** If the table cannot be found, an error code is returned and the output +** variables are undefined. Or, if an OOM is encountered, SQLITE_NOMEM is +** returned (and the output variables are undefined). +*/ +static int fts3ContentColumns( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of db (i.e. "main", "temp" etc.) */ + const char *zTbl, /* Name of content table */ + const char ***pazCol, /* OUT: Malloc'd array of column names */ + int *pnCol, /* OUT: Size of array *pazCol */ + int *pnStr /* OUT: Bytes of string content */ +){ + int rc = SQLITE_OK; /* Return code */ + char *zSql; /* "SELECT *" statement on zTbl */ + sqlite3_stmt *pStmt = 0; /* Compiled version of zSql */ + + zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zTbl); + if( !zSql ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + } + sqlite3_free(zSql); + + if( rc==SQLITE_OK ){ + const char **azCol; /* Output array */ + int nStr = 0; /* Size of all column names (incl. 0x00) */ + int nCol; /* Number of table columns */ + int i; /* Used to iterate through columns */ + + /* Loop through the returned columns. Set nStr to the number of bytes of + ** space required to store a copy of each column name, including the + ** nul-terminator byte. */ + nCol = sqlite3_column_count(pStmt); + for(i=0; i MATCHINFO */ - { "prefix", 6, 0 }, /* 1 -> PREFIX */ - { "compress", 8, 0 }, /* 2 -> COMPRESS */ - { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */ - { "order", 5, 0 } /* 4 -> ORDER */ + { "matchinfo", 9 }, /* 0 -> MATCHINFO */ + { "prefix", 6 }, /* 1 -> PREFIX */ + { "compress", 8 }, /* 2 -> COMPRESS */ + { "uncompress", 10 }, /* 3 -> UNCOMPRESS */ + { "order", 5 }, /* 4 -> ORDER */ + { "content", 7 } /* 5 -> CONTENT */ }; int iOpt; if( !zVal ){ rc = SQLITE_NOMEM; @@ -115256,17 +117502,24 @@ zVal = 0; break; case 4: /* ORDER */ if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) - && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3)) + && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4)) ){ *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); rc = SQLITE_ERROR; } bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); break; + + default: /* CONTENT */ + assert( iOpt==5 ); + sqlite3_free(zUncompress); + zContent = zVal; + zVal = 0; + break; } } sqlite3_free(zVal); } } @@ -115275,10 +117528,30 @@ else { nString += (int)(strlen(z) + 1); aCol[nCol++] = z; } } + + /* If a content=xxx option was specified, the following: + ** + ** 1. Ignore any compress= and uncompress= options. + ** + ** 2. If no column names were specified as part of the CREATE VIRTUAL + ** TABLE statement, use all columns from the content table. + */ + if( rc==SQLITE_OK && zContent ){ + sqlite3_free(zCompress); + sqlite3_free(zUncompress); + zCompress = 0; + zUncompress = 0; + if( nCol==0 ){ + sqlite3_free((void*)aCol); + aCol = 0; + rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString); + } + assert( rc!=SQLITE_OK || nCol>0 ); + } if( rc!=SQLITE_OK ) goto fts3_init_out; if( nCol==0 ){ assert( nString==0 ); aCol[0] = "content"; @@ -115319,10 +117592,12 @@ p->pTokenizer = pTokenizer; p->nMaxPendingData = FTS3_MAX_PENDING_DATA; p->bHasDocsize = (isFts4 && bNoDocsize==0); p->bHasStat = isFts4; p->bDescIdx = bDescIdx; + p->zContentTbl = zContent; + zContent = 0; TESTONLY( p->inTransaction = -1 ); TESTONLY( p->mxSavepoint = -1 ); p->aIndex = (struct Fts3Index *)&p->azColumn[nCol]; memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex); @@ -115380,10 +117655,11 @@ fts3_init_out: sqlite3_free(zPrefix); sqlite3_free(aIndex); sqlite3_free(zCompress); sqlite3_free(zUncompress); + sqlite3_free(zContent); sqlite3_free((void *)aCol); if( rc!=SQLITE_OK ){ if( p ){ fts3DisconnectMethod((sqlite3_vtab *)p); }else if( pTokenizer ){ @@ -115530,40 +117806,69 @@ sqlite3_free(pCsr->aMatchinfo); assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); sqlite3_free(pCsr); return SQLITE_OK; } + +/* +** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then +** compose and prepare an SQL statement of the form: +** +** "SELECT FROM %_content WHERE rowid = ?" +** +** (or the equivalent for a content=xxx table) and set pCsr->pStmt to +** it. If an error occurs, return an SQLite error code. +** +** Otherwise, set *ppStmt to point to pCsr->pStmt and return SQLITE_OK. +*/ +static int fts3CursorSeekStmt(Fts3Cursor *pCsr, sqlite3_stmt **ppStmt){ + int rc = SQLITE_OK; + if( pCsr->pStmt==0 ){ + Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; + char *zSql; + zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); + if( !zSql ) return SQLITE_NOMEM; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + } + *ppStmt = pCsr->pStmt; + return rc; +} /* ** Position the pCsr->pStmt statement so that it is on the row ** of the %_content table that contains the last match. Return ** SQLITE_OK on success. */ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ + int rc = SQLITE_OK; if( pCsr->isRequireSeek ){ - sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); - pCsr->isRequireSeek = 0; - if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ - return SQLITE_OK; - }else{ - int rc = sqlite3_reset(pCsr->pStmt); - if( rc==SQLITE_OK ){ - /* If no row was found and no error has occured, then the %_content - ** table is missing a row that is present in the full-text index. - ** The data structures are corrupt. - */ - rc = SQLITE_CORRUPT_VTAB; - } - pCsr->isEof = 1; - if( pContext ){ - sqlite3_result_error_code(pContext, rc); - } - return rc; - } - }else{ - return SQLITE_OK; - } + sqlite3_stmt *pStmt = 0; + + rc = fts3CursorSeekStmt(pCsr, &pStmt); + if( rc==SQLITE_OK ){ + sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); + pCsr->isRequireSeek = 0; + if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ + return SQLITE_OK; + }else{ + rc = sqlite3_reset(pCsr->pStmt); + if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){ + /* If no row was found and no error has occured, then the %_content + ** table is missing a row that is present in the full-text index. + ** The data structures are corrupt. */ + rc = FTS_CORRUPT_VTAB; + pCsr->isEof = 1; + } + } + } + } + + if( rc!=SQLITE_OK && pContext ){ + sqlite3_result_error_code(pContext, rc); + } + return rc; } /* ** This function is used to process a single interior node when searching ** a b-tree for a term or term prefix. The node data is passed to this @@ -115609,11 +117914,11 @@ ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). */ zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); if( zCsr>zEnd ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } while( zCsrzEnd ){ - rc = SQLITE_CORRUPT_VTAB; + rc = FTS_CORRUPT_VTAB; goto finish_scan; } if( nPrefix+nSuffix>nAlloc ){ char *zNew; nAlloc = (nPrefix+nSuffix) * 2; @@ -115640,10 +117945,11 @@ rc = SQLITE_NOMEM; goto finish_scan; } zBuffer = zNew; } + assert( zBuffer ); memcpy(&zBuffer[nPrefix], zCsr, nSuffix); nBuffer = nPrefix + nSuffix; zCsr += nSuffix; /* Compare the term we are searching for with the term just loaded from @@ -115998,20 +118304,20 @@ int isSaveLeft, /* Save the left position */ int isExact, /* If *pp1 is exactly nTokens before *pp2 */ char **pp1, /* IN/OUT: Left input list */ char **pp2 /* IN/OUT: Right input list */ ){ - char *p = (pp ? *pp : 0); + char *p = *pp; char *p1 = *pp1; char *p2 = *pp2; int iCol1 = 0; int iCol2 = 0; /* Never set both isSaveLeft and isExact for the same invocation. */ assert( isSaveLeft==0 || isExact==0 ); - assert( *p1!=0 && *p2!=0 ); + assert( p!=0 && *p1!=0 && *p2!=0 ); if( *p1==POS_COLUMN ){ p1++; p1 += sqlite3Fts3GetVarint32(p1, &iCol1); } if( *p2==POS_COLUMN ){ @@ -116024,11 +118330,11 @@ char *pSave = p; sqlite3_int64 iPrev = 0; sqlite3_int64 iPos1 = 0; sqlite3_int64 iPos2 = 0; - if( pp && iCol1 ){ + if( iCol1 ){ *p++ = POS_COLUMN; p += sqlite3Fts3PutVarint(p, iCol1); } assert( *p1!=POS_END && *p1!=POS_COLUMN ); @@ -116039,20 +118345,14 @@ while( 1 ){ if( iPos2==iPos1+nToken || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken) ){ sqlite3_int64 iSave; - if( !pp ){ - fts3PoslistCopy(0, &p2); - fts3PoslistCopy(0, &p1); - *pp1 = p1; - *pp2 = p2; - return 1; - } iSave = isSaveLeft ? iPos1 : iPos2; fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2; pSave = 0; + assert( p ); } if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){ if( (*p2&0xFE)==0 ) break; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; }else{ @@ -116097,11 +118397,11 @@ fts3PoslistCopy(0, &p2); fts3PoslistCopy(0, &p1); *pp1 = p1; *pp2 = p2; - if( !pp || *pp==p ){ + if( *pp==p ){ return 0; } *p++ = 0x00; *pp = p; return 1; @@ -116399,10 +118699,60 @@ } } *pnRight = p - aOut; } + +/* +** Argument pList points to a position list nList bytes in size. This +** function checks to see if the position list contains any entries for +** a token in position 0 (of any column). If so, it writes argument iDelta +** to the output buffer pOut, followed by a position list consisting only +** of the entries from pList at position 0, and terminated by an 0x00 byte. +** The value returned is the number of bytes written to pOut (if any). +*/ +SQLITE_PRIVATE int sqlite3Fts3FirstFilter( + sqlite3_int64 iDelta, /* Varint that may be written to pOut */ + char *pList, /* Position list (no 0x00 term) */ + int nList, /* Size of pList in bytes */ + char *pOut /* Write output here */ +){ + int nOut = 0; + int bWritten = 0; /* True once iDelta has been written */ + char *p = pList; + char *pEnd = &pList[nList]; + + if( *p!=0x01 ){ + if( *p==0x02 ){ + nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta); + pOut[nOut++] = 0x02; + bWritten = 1; + } + fts3ColumnlistCopy(0, &p); + } + + while( ppSegcsr; memset(&tsc, 0, sizeof(TermSelect)); filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS | (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0) + | (pTok->bFirst ? FTS3_SEGMENT_FIRST : 0) | (iColumnnColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0); filter.iCol = iColumn; filter.zTerm = pTok->z; filter.nTerm = pTok->n; @@ -116896,12 +119247,12 @@ if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ return SQLITE_NOMEM; } - rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn, - iCol, zQuery, -1, &pCsr->pExpr + rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->bHasStat, + p->nColumn, iCol, zQuery, -1, &pCsr->pExpr ); if( rc!=SQLITE_OK ){ if( rc==SQLITE_ERROR ){ static const char *zErr = "malformed MATCH expression: [%s]"; p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery); @@ -116924,26 +119275,27 @@ ** statement loops through all rows of the %_content table. For a ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ if( idxNum==FTS3_FULLSCAN_SEARCH ){ - const char *zSort = (pCsr->bDesc ? "DESC" : "ASC"); - const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s"; - zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort); - }else{ - const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?"; - zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName); - } - if( !zSql ) return SQLITE_NOMEM; - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); - sqlite3_free(zSql); - if( rc!=SQLITE_OK ) return rc; - - if( idxNum==FTS3_DOCID_SEARCH ){ - rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); - if( rc!=SQLITE_OK ) return rc; - } + zSql = sqlite3_mprintf( + "SELECT %s ORDER BY rowid %s", + p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") + ); + if( zSql ){ + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + }else{ + rc = SQLITE_NOMEM; + } + }else if( idxNum==FTS3_DOCID_SEARCH ){ + rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt); + if( rc==SQLITE_OK ){ + rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); + } + } + if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); } /* @@ -116992,11 +119344,11 @@ ** Return a blob which is a pointer to the cursor. */ sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT); }else{ rc = fts3CursorSeek(0, pCsr); - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){ sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1)); } } assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); @@ -117076,11 +119428,11 @@ ** moves *ppPoslist so that it instead points to the first byte of the ** same position list. */ static void fts3ReversePoslist(char *pStart, char **ppPoslist){ char *p = &(*ppPoslist)[-2]; - char c; + char c = 0; while( p>pStart && (c=*p--)==0 ); while( p>pStart && (*p & 0x80) | c ){ c = *p--; } @@ -117285,19 +119637,26 @@ ){ Fts3Table *p = (Fts3Table *)pVtab; sqlite3 *db = p->db; /* Database connection */ int rc; /* Return Code */ + /* As it happens, the pending terms table is always empty here. This is + ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction + ** always opens a savepoint transaction. And the xSavepoint() method + ** flushes the pending terms table. But leave the (no-op) call to + ** PendingTermsFlush() in in case that changes. + */ + assert( p->nPendingData==0 ); rc = sqlite3Fts3PendingTermsFlush(p); - if( rc!=SQLITE_OK ){ - return rc; + + if( p->zContentTbl==0 ){ + fts3DbExec(&rc, db, + "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", + p->zDb, p->zName, zName + ); } - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", - p->zDb, p->zName, zName - ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';", p->zDb, p->zName, zName ); @@ -117652,25 +120011,24 @@ ** ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. */ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ int iToken; /* Used to iterate through phrase tokens */ - int rc = SQLITE_OK; /* Return code */ char *aPoslist = 0; /* Position list for deferred tokens */ int nPoslist = 0; /* Number of bytes in aPoslist */ int iPrev = -1; /* Token number of previous deferred token */ assert( pPhrase->doclist.bFreeList==0 ); - for(iToken=0; rc==SQLITE_OK && iTokennToken; iToken++){ + for(iToken=0; iTokennToken; iToken++){ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; Fts3DeferredToken *pDeferred = pToken->pDeferred; if( pDeferred ){ char *pList; int nList; - rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); + int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); if( rc!=SQLITE_OK ) return rc; if( pList==0 ){ sqlite3_free(aPoslist); pPhrase->doclist.pList = 0; @@ -117767,10 +120125,11 @@ if( pCsr->bDesc==pTab->bDescIdx && bOptOk==1 && p->nToken==1 && pFirst->pSegcsr && pFirst->pSegcsr->bLookup + && pFirst->bFirst==0 ){ /* Use the incremental approach. */ int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn); rc = sqlite3Fts3MsrIncrStart( pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n); @@ -117996,11 +120355,11 @@ Fts3Expr *pExpr, /* Expression to consider */ Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */ Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */ int *pRc /* IN/OUT: Error code */ ){ - if( *pRc==SQLITE_OK && pExpr ){ + if( *pRc==SQLITE_OK ){ if( pExpr->eType==FTSQUERY_PHRASE ){ Fts3Phrase *pPhrase = pExpr->pPhrase; int i; for(i=0; *pRc==SQLITE_OK && inToken; i++){ Fts3TokenAndCost *pTC = (*ppTC)++; @@ -118010,10 +120369,15 @@ pTC->pToken = &pPhrase->aToken[i]; pTC->iCol = pPhrase->iColumn; *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl); } }else if( pExpr->eType!=FTSQUERY_NOT ){ + assert( pExpr->eType==FTSQUERY_OR + || pExpr->eType==FTSQUERY_AND + || pExpr->eType==FTSQUERY_NEAR + ); + assert( pExpr->pLeft && pExpr->pRight ); if( pExpr->eType==FTSQUERY_OR ){ pRoot = pExpr->pLeft; **ppOr = pRoot; (*ppOr)++; } @@ -118070,11 +120434,11 @@ while( anDoc = nDoc; pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz); assert( pCsr->nRowAvg>0 ); @@ -118113,10 +120477,19 @@ int nOvfl = 0; /* Total overflow pages used by doclists */ int nToken = 0; /* Total number of tokens in cluster */ int nMinEst = 0; /* The minimum count for any phrase so far. */ int nLoad4 = 1; /* (Phrases that will be loaded)^4. */ + + /* Tokens are never deferred for FTS tables created using the content=xxx + ** option. The reason being that it is not guaranteed that the content + ** table actually contains the same data as the index. To prevent this from + ** causing any problems, the deferred token optimization is completely + ** disabled for content=xxx tables. */ + if( pTab->zContentTbl ){ + return SQLITE_OK; + } /* Count the tokens in this AND/NEAR cluster. If none of the doclists ** associated with the tokens spill onto overflow pages, or if there is ** only 1 token, exit early. No tokens to defer in this case. */ for(ii=0; iipToken; rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol); fts3SegReaderCursorFree(pToken->pSegcsr); pToken->pSegcsr = 0; }else{ - nLoad4 = nLoad4*4; + /* Set nLoad4 to the value of (4^nOther) for the next iteration of the + ** for-loop. Except, limit the value to 2^24 to prevent it from + ** overflowing the 32-bit integer it is stored in. */ + if( ii<12 ) nLoad4 = nLoad4*4; + if( ii==0 || pTC->pPhrase->nToken>1 ){ /* Either this is the cheapest token in the entire query, or it is ** part of a multi-token phrase. Either way, the entire doclist will ** (eventually) be loaded into memory. It may as well be now. */ Fts3PhraseToken *pToken = pTC->pToken; @@ -118546,12 +120923,15 @@ } aPoslist = pExpr->pRight->pPhrase->doclist.pList; nToken = pExpr->pRight->pPhrase->nToken; for(p=pExpr->pLeft; p && res; p=p->pLeft){ - int nNear = p->pParent->nNear; - Fts3Phrase *pPhrase = ( + int nNear; + Fts3Phrase *pPhrase; + assert( p->pParent && p->pParent->pLeft==p ); + nNear = p->pParent->nNear; + pPhrase = ( p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase ); res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); } } @@ -119037,10 +121417,19 @@ pPhrase->aToken[i].pSegcsr = 0; } } } +/* +** Return SQLITE_CORRUPT_VTAB. +*/ +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ + return SQLITE_CORRUPT_VTAB; +} +#endif + #if !SQLITE_CORE /* ** Initialize API pointer table, if required. */ SQLITE_API int sqlite3_extension_init( @@ -119625,10 +122014,11 @@ */ typedef struct ParseContext ParseContext; struct ParseContext { sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ const char **azCol; /* Array of column names for fts3 table */ + int bFts4; /* True to allow FTS4-only syntax */ int nCol; /* Number of entries in azCol[] */ int iDefaultCol; /* Default column to query */ int isNot; /* True if getNextNode() sees a unary - */ sqlite3_context *pCtx; /* Write error message here */ int nNest; /* Number of nested brackets */ @@ -119712,13 +122102,25 @@ if( iEndpPhrase->aToken[0].isPrefix = 1; iEnd++; } - if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){ - pParse->isNot = 1; + + while( 1 ){ + if( !sqlite3_fts3_enable_parentheses + && iStart>0 && z[iStart-1]=='-' + ){ + pParse->isNot = 1; + iStart--; + }else if( pParse->bFts4 && iStart>0 && z[iStart-1]=='^' ){ + pRet->pPhrase->aToken[0].bFirst = 1; + iStart--; + }else{ + break; + } } + } nConsumed = iEnd; } pModule->xClose(pCursor); @@ -119813,10 +122215,11 @@ memcpy(&zTemp[nTemp], zByte, nByte); nTemp += nByte; pToken->n = nByte; pToken->isPrefix = (iEndbFirst = (iBegin>0 && zInput[iBegin-1]=='^'); nToken = ii+1; } } pModule->xClose(pCursor); @@ -119834,12 +122237,16 @@ p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase->iColumn = pParse->iDefaultCol; p->pPhrase->nToken = nToken; zBuf = (char *)&p->pPhrase->aToken[nToken]; - memcpy(zBuf, zTemp, nTemp); - sqlite3_free(zTemp); + if( zTemp ){ + memcpy(zBuf, zTemp, nTemp); + sqlite3_free(zTemp); + }else{ + assert( nTemp==0 ); + } for(jj=0; jjpPhrase->nToken; jj++){ p->pPhrase->aToken[jj].z = zBuf; zBuf += p->pPhrase->aToken[jj].n; } @@ -120260,10 +122667,11 @@ ** match any table column. */ SQLITE_PRIVATE int sqlite3Fts3ExprParse( sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ char **azCol, /* Array of column names for fts3 table */ + int bFts4, /* True to allow FTS4-only syntax */ int nCol, /* Number of entries in azCol[] */ int iDefaultCol, /* Default column to query */ const char *z, int n, /* Text of MATCH query */ Fts3Expr **ppExpr /* OUT: Parsed query structure */ ){ @@ -120273,10 +122681,11 @@ sParse.pTokenizer = pTokenizer; sParse.azCol = (const char **)azCol; sParse.nCol = nCol; sParse.iDefaultCol = iDefaultCol; sParse.nNest = 0; + sParse.bFts4 = bFts4; if( z==0 ){ *ppExpr = 0; return SQLITE_OK; } if( n<0 ){ @@ -120462,11 +122871,11 @@ for(ii=0; iizDb, p->zName, p->zWriteExprlist); }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ - zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName); + zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); } if( !zSql ){ rc = SQLITE_NOMEM; @@ -122594,11 +125003,11 @@ sqlite3_bind_int64(pStmt, 1, iDocid); } rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ rc = sqlite3_reset(pStmt); - if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_OK ) rc = FTS_CORRUPT_VTAB; pStmt = 0; }else{ rc = SQLITE_OK; } } @@ -122662,21 +125071,28 @@ ** We try to avoid this because if FTS3 returns any error when committing ** a transaction, the whole transaction will be rolled back. And this is ** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can ** still happen if the user reads data directly from the %_segments or ** %_segdir tables instead of going through FTS3 though. +** +** This reasoning does not apply to a content=xxx table. */ SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){ int rc; /* Return code */ sqlite3_stmt *pStmt; /* Statement used to obtain lock */ - rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_null(pStmt, 1); - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); + if( p->zContentTbl==0 ){ + rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_null(pStmt, 1); + sqlite3_step(pStmt); + rc = sqlite3_reset(pStmt); + } + }else{ + rc = SQLITE_OK; } + return rc; } /* ** Set *ppStmt to a statement handle that may be used to iterate through @@ -123032,10 +125448,22 @@ sqlite3_value **apVal, /* Array of values to insert */ sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */ ){ int rc; /* Return code */ sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */ + + if( p->zContentTbl ){ + sqlite3_value *pRowid = apVal[p->nColumn+3]; + if( sqlite3_value_type(pRowid)==SQLITE_NULL ){ + pRowid = apVal[1]; + } + if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){ + return SQLITE_CONSTRAINT; + } + *piDocid = sqlite3_value_int64(pRowid); + return SQLITE_OK; + } /* Locate the statement handle used to insert data into the %_content ** table. The SQL for this statement is: ** ** INSERT INTO %_content VALUES(?, ?, ?, ...) @@ -123083,18 +125511,20 @@ /* ** Remove all data from the FTS3 table. Clear the hash table containing ** pending terms. */ -static int fts3DeleteAll(Fts3Table *p){ +static int fts3DeleteAll(Fts3Table *p, int bContent){ int rc = SQLITE_OK; /* Return code */ /* Discard the contents of the pending-terms hash table. */ sqlite3Fts3PendingTermsClear(p); - /* Delete everything from the %_content, %_segments and %_segdir tables. */ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); + /* Delete everything from the shadow tables. Except, leave %_content as + ** is if bContent is false. */ + assert( p->zContentTbl==0 || bContent==0 ); + if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); } @@ -123398,11 +125828,11 @@ pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); if( nPrefix<0 || nSuffix<=0 || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } if( nPrefix+nSuffix>pReader->nTermAlloc ){ int nNew = (nPrefix+nSuffix)*2; char *zNew = sqlite3_realloc(pReader->zTerm, nNew); @@ -123428,11 +125858,11 @@ ** of these statements is untrue, then the data structure is corrupt. */ if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } return SQLITE_OK; } /* @@ -123618,11 +126048,10 @@ sqlite3_int64 iEndBlock, /* Final block of segment */ const char *zRoot, /* Buffer containing root node */ int nRoot, /* Size of buffer containing root node */ Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ ){ - int rc = SQLITE_OK; /* Return code */ Fts3SegReader *pReader; /* Newly allocated SegReader object */ int nExtra = 0; /* Bytes to allocate segment root node */ assert( iStartLeaf<=iEndLeaf ); if( iStartLeaf==0 ){ @@ -123646,17 +126075,12 @@ memcpy(pReader->aNode, zRoot, nRoot); memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING); }else{ pReader->iCurrentBlock = iStartLeaf-1; } - - if( rc==SQLITE_OK ){ - *ppReader = pReader; - }else{ - sqlite3Fts3SegReaderFree(pReader); - } - return rc; + *ppReader = pReader; + return SQLITE_OK; } /* ** This is a comparison function used as a qsort() callback when sorting ** an array of pending terms by term. This occurs as part of flushing @@ -123702,19 +126126,19 @@ int nTerm, /* Size of buffer zTerm */ int bPrefix, /* True for a prefix iterator */ Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */ ){ Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */ + Fts3HashElem *pE; /* Iterator variable */ Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */ int nElem = 0; /* Size of array at aElem */ int rc = SQLITE_OK; /* Return Code */ Fts3Hash *pHash; pHash = &p->aIndex[iIndex].hPending; if( bPrefix ){ int nAlloc = 0; /* Size of allocated array at aElem */ - Fts3HashElem *pE = 0; /* Iterator variable */ for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){ char *zKey = (char *)fts3HashKey(pE); int nKey = fts3HashKeysize(pE); if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){ @@ -123744,12 +126168,17 @@ qsort(aElem, nElem, sizeof(Fts3HashElem *), fts3CompareElemByTerm); } }else{ /* The query is a simple term lookup that matches at most one term in - ** the index. All that is required is a straight hash-lookup. */ - Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm); + ** the index. All that is required is a straight hash-lookup. + ** + ** Because the stack address of pE may be accessed via the aElem pointer + ** below, the "Fts3HashElem *pE" must be declared so that it is valid + ** within this entire function, not just this "else{...}" block. + */ + pE = fts3HashFindElem(pHash, zTerm, nTerm); if( pE ){ aElem = &pE; nElem = 1; } } @@ -124378,16 +126807,22 @@ ** error occurs, an SQLite error code is returned. */ static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){ sqlite3_stmt *pStmt; int rc; - rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid); - if( rc==SQLITE_OK ){ - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pisEmpty = sqlite3_column_int(pStmt, 0); + if( p->zContentTbl ){ + /* If using the content=xxx option, assume the table is never empty */ + *pisEmpty = 0; + rc = SQLITE_OK; + }else{ + rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid); + if( rc==SQLITE_OK ){ + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + *pisEmpty = sqlite3_column_int(pStmt, 0); + } + rc = sqlite3_reset(pStmt); } - rc = sqlite3_reset(pStmt); } return rc; } /* @@ -124735,10 +127170,11 @@ int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX); int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN); + int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST); Fts3SegReader **apSegment = pCsr->apSegment; int nSegment = pCsr->nSegment; Fts3SegFilter *pFilter = pCsr->pFilter; int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( @@ -124794,10 +127230,11 @@ } assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); if( nMerge==1 && !isIgnoreEmpty + && !isFirst && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0) ){ pCsr->nDoclist = apSegment[0]->nDoclist; if( fts3SegReaderIsPending(apSegment[0]) ){ rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); @@ -124859,16 +127296,28 @@ if( !aNew ){ return SQLITE_NOMEM; } pCsr->aBuffer = aNew; } - nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta); - iPrev = iDocid; - if( isRequirePos ){ - memcpy(&pCsr->aBuffer[nDoclist], pList, nList); - nDoclist += nList; - pCsr->aBuffer[nDoclist++] = '\0'; + + if( isFirst ){ + char *a = &pCsr->aBuffer[nDoclist]; + int nWrite; + + nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a); + if( nWrite ){ + iPrev = iDocid; + nDoclist += nWrite; + } + }else{ + nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta); + iPrev = iDocid; + if( isRequirePos ){ + memcpy(&pCsr->aBuffer[nDoclist], pList, nList); + nDoclist += nList; + pCsr->aBuffer[nDoclist++] = '\0'; + } } } fts3SegReaderSort(apSegment, nMerge, j, xCmp); } @@ -125040,13 +127489,13 @@ ** Insert the sizes (in tokens) for each column of the document ** with docid equal to p->iPrevDocid. The sizes are encoded as ** a blob of varints. */ static void fts3InsertDocsize( - int *pRC, /* Result code */ - Fts3Table *p, /* Table into which to insert */ - u32 *aSz /* Sizes of each column */ + int *pRC, /* Result code */ + Fts3Table *p, /* Table into which to insert */ + u32 *aSz /* Sizes of each column, in tokens */ ){ char *pBlob; /* The BLOB encoding of the document size */ int nBlob; /* Number of bytes in the BLOB */ sqlite3_stmt *pStmt; /* Statement used to insert the encoding */ int rc; /* Result code from subfunctions */ @@ -125163,10 +127612,90 @@ sqlite3Fts3SegmentsClose(p); sqlite3Fts3PendingTermsClear(p); return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc; } + +/* +** This function is called when the user executes the following statement: +** +** INSERT INTO () VALUES('rebuild'); +** +** The entire FTS index is discarded and rebuilt. If the table is one +** created using the content=xxx option, then the new index is based on +** the current contents of the xxx table. Otherwise, it is rebuilt based +** on the contents of the %_content table. +*/ +static int fts3DoRebuild(Fts3Table *p){ + int rc; /* Return Code */ + + rc = fts3DeleteAll(p, 0); + if( rc==SQLITE_OK ){ + u32 *aSz = 0; + u32 *aSzIns = 0; + u32 *aSzDel = 0; + sqlite3_stmt *pStmt = 0; + int nEntry = 0; + + /* Compose and prepare an SQL statement to loop through the content table */ + char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); + if( !zSql ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + } + + if( rc==SQLITE_OK ){ + int nByte = sizeof(u32) * (p->nColumn+1)*3; + aSz = (u32 *)sqlite3_malloc(nByte); + if( aSz==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(aSz, 0, nByte); + aSzIns = &aSz[p->nColumn+1]; + aSzDel = &aSzIns[p->nColumn+1]; + } + } + + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + int iCol; + rc = fts3PendingTermsDocid(p, sqlite3_column_int64(pStmt, 0)); + aSz[p->nColumn] = 0; + for(iCol=0; rc==SQLITE_OK && iColnColumn; iCol++){ + const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1); + rc = fts3PendingTermsAdd(p, z, iCol, &aSz[iCol]); + aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1); + } + if( p->bHasDocsize ){ + fts3InsertDocsize(&rc, p, aSz); + } + if( rc!=SQLITE_OK ){ + sqlite3_finalize(pStmt); + pStmt = 0; + }else{ + nEntry++; + for(iCol=0; iCol<=p->nColumn; iCol++){ + aSzIns[iCol] += aSz[iCol]; + } + } + } + if( p->bHasStat ){ + fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry); + } + sqlite3_free(aSz); + + if( pStmt ){ + int rc2 = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ){ + rc = rc2; + } + } + } + + return rc; +} /* ** Handle a 'special' INSERT of the form: ** ** "INSERT INTO tbl(tbl) VALUES()" @@ -125181,10 +127710,12 @@ if( !zVal ){ return SQLITE_NOMEM; }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ rc = fts3DoOptimize(p, 0); + }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){ + rc = fts3DoRebuild(p); #ifdef SQLITE_TEST }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ p->nNodeSize = atoi(&zVal[9]); rc = SQLITE_OK; }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ @@ -125261,10 +127792,11 @@ pTC->pTokenizer = pT; rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos); for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ Fts3PhraseToken *pPT = pDef->pToken; if( (pDef->iCol>=p->nColumn || pDef->iCol==i) + && (pPT->bFirst==0 || iPos==0) && (pPT->n==nToken || (pPT->isPrefix && pPT->nz, pPT->n)) ){ fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc); } @@ -125352,18 +127884,22 @@ if( rc==SQLITE_OK ){ if( isEmpty ){ /* Deleting this row means the whole table is empty. In this case ** delete the contents of all three tables and throw away any ** data in the pendingTerms hash table. */ - rc = fts3DeleteAll(p); + rc = fts3DeleteAll(p, 1); *pnDoc = *pnDoc - 1; }else{ sqlite3_int64 iRemove = sqlite3_value_int64(pRowid); rc = fts3PendingTermsDocid(p, iRemove); fts3DeleteTerms(&rc, p, pRowid, aSzDel); - fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid); - if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1; + if( p->zContentTbl==0 ){ + fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid); + if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1; + }else{ + *pnDoc = *pnDoc - 1; + } if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid); } } } @@ -125382,11 +127918,10 @@ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ ){ Fts3Table *p = (Fts3Table *)pVtab; int rc = SQLITE_OK; /* Return Code */ int isRemove = 0; /* True for an UPDATE or DELETE */ - sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */ u32 *aSzIns = 0; /* Sizes of inserted documents */ u32 *aSzDel; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ int bInsertDone = 0; @@ -125420,11 +127955,11 @@ ** should be deleted from the database before inserting the new row. Or, ** if the on-conflict mode is other than REPLACE, then this method must ** detect the conflict and return SQLITE_CONSTRAINT before beginning to ** modify the database file. */ - if( nArg>1 ){ + if( nArg>1 && p->zContentTbl==0 ){ /* Find the value object that holds the new rowid value. */ sqlite3_value *pNewRowid = apVal[3+p->nColumn]; if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){ pNewRowid = apVal[1]; } @@ -125465,23 +128000,25 @@ /* If this is a DELETE or UPDATE operation, remove the old record. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ); rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel); isRemove = 1; - iRemove = sqlite3_value_int64(apVal[0]); } /* If this is an INSERT or UPDATE operation, insert the new record. */ if( nArg>1 && rc==SQLITE_OK ){ if( bInsertDone==0 ){ rc = fts3InsertData(p, apVal, pRowid); - if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){ + rc = FTS_CORRUPT_VTAB; + } } - if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){ + if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){ rc = fts3PendingTermsDocid(p, *pRowid); } if( rc==SQLITE_OK ){ + assert( p->iPrevDocid==*pRowid ); rc = fts3InsertTerms(p, apVal, aSzIns); } if( p->bHasDocsize ){ fts3InsertDocsize(&rc, p, aSzIns); } @@ -125891,10 +128428,11 @@ pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol); if( pCsr ){ int iFirst = 0; pPhrase->pList = pCsr; fts3GetDeltaPosition(&pCsr, &iFirst); + assert( iFirst>=0 ); pPhrase->pHead = pCsr; pPhrase->pTail = pCsr; pPhrase->iHead = iFirst; pPhrase->iTail = iFirst; }else{ @@ -126371,11 +128909,11 @@ pStmt = *ppStmt; assert( sqlite3_data_count(pStmt)==1 ); a = sqlite3_column_blob(pStmt, 0); a += sqlite3Fts3GetVarint(a, &nDoc); - if( nDoc==0 ) return SQLITE_CORRUPT_VTAB; + if( nDoc==0 ) return FTS_CORRUPT_VTAB; *pnDoc = (u32)nDoc; if( paLen ) *paLen = a; return SQLITE_OK; } @@ -126932,11 +129470,11 @@ } } if( !pTerm ){ /* All offsets for this column have been gathered. */ - break; + rc = SQLITE_DONE; }else{ assert( iCurrent<=iMinPos ); if( 0==(0xFE&*pTerm->pList) ){ pTerm->pList = 0; }else{ @@ -126949,12 +129487,12 @@ char aBuffer[64]; sqlite3_snprintf(sizeof(aBuffer), aBuffer, "%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart ); rc = fts3StringAppend(&res, aBuffer, -1); - }else if( rc==SQLITE_DONE ){ - rc = SQLITE_CORRUPT_VTAB; + }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){ + rc = FTS_CORRUPT_VTAB; } } } if( rc==SQLITE_DONE ){ rc = SQLITE_OK; @@ -128216,11 +130754,11 @@ RtreeMatchArg *p; sqlite3_rtree_geometry *pGeom; int nBlob; /* Check that value is actually a blob. */ - if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR; + if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR; /* Check that the blob is roughly the right size. */ nBlob = sqlite3_value_bytes(pValue); if( nBlob<(int)sizeof(RtreeMatchArg) || ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0 @@ -128291,11 +130829,12 @@ pCsr->nConstraint = argc; if( !pCsr->aConstraint ){ rc = SQLITE_NOMEM; }else{ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); - assert( (idxStr==0 && argc==0) || (int)strlen(idxStr)==argc*2 ); + assert( (idxStr==0 && argc==0) + || (idxStr && (int)strlen(idxStr)==argc*2) ); for(ii=0; iiaConstraint[ii]; p->op = idxStr[ii*2]; p->iCoord = idxStr[ii*2+1]-'a'; if( p->op==RTREE_MATCH ){ @@ -128592,11 +131131,14 @@ int iCell; sqlite3_int64 iBest = 0; float fMinGrowth = 0.0; float fMinArea = 0.0; +#if VARIANT_RSTARTREE_CHOOSESUBTREE float fMinOverlap = 0.0; + float overlap; +#endif int nCell = NCELL(pNode); RtreeCell cell; RtreeNode *pChild; @@ -128624,33 +131166,34 @@ */ for(iCell=0; iCelliDepth-1) ){ overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); + }else{ + overlap = 0.0; } if( (iCell==0) || (overlapSQLITE_CONFIG_PAGECACHE **
    ^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implementation. ** This configuration should not be used if an application-define page -** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. +** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each ** page header. ^The page header size is 20 to 40 bytes depending on @@ -1397,12 +1429,12 @@ ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte ** boundary or subsequent behavior of SQLite will be undefined. -** The minimum allocation size is capped at 2^12. Reasonable values -** for the minimum allocation size are 2^5 through 2^8.
    +** The minimum allocation size is capped at 2**12. Reasonable values +** for the minimum allocation size are 2**5 through 2**8. ** ** [[SQLITE_CONFIG_MUTEX]]
    SQLITE_CONFIG_MUTEX
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place @@ -1435,19 +1467,19 @@ ** slots allocated to each database connection.)^ ^(This option sets the ** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] ** verb to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^
    ** -** [[SQLITE_CONFIG_PCACHE]]
    SQLITE_CONFIG_PCACHE
    +** [[SQLITE_CONFIG_PCACHE2]]
    SQLITE_CONFIG_PCACHE2
    **
    ^(This option takes a single argument which is a pointer to -** an [sqlite3_pcache_methods] object. This object specifies the interface +** an [sqlite3_pcache_methods2] object. This object specifies the interface ** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.
    ** -** [[SQLITE_CONFIG_GETPCACHE]]
    SQLITE_CONFIG_GETPCACHE
    +** [[SQLITE_CONFIG_GETPCACHE2]]
    SQLITE_CONFIG_GETPCACHE2
    **
    ^(This option takes a single argument which is a pointer to an -** [sqlite3_pcache_methods] object. SQLite copies of the current +** [sqlite3_pcache_methods2] object. SQLite copies of the current ** page cache implementation into that object.)^
    ** ** [[SQLITE_CONFIG_LOG]]
    SQLITE_CONFIG_LOG
    **
    ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), @@ -1476,10 +1508,15 @@ ** connection is opened. If it is globally disabled, filenames are ** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the ** database connection is opened. By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** [SQLITE_USE_URI] symbol defined. +** +** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] +**
    SQLITE_CONFIG_PCACHE and SQLITE_CONFNIG_GETPCACHE +**
    These options are obsolete and should not be used by new code. +** They are retained for backwards compatibility but are now no-ops. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ @@ -1491,14 +1528,16 @@ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ -#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that @@ -1979,11 +2018,11 @@ ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", and "%z" options. ** -** ^(The %q option works like %s in that it substitutes a null-terminated +** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. ** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into ** the string. ** @@ -2587,25 +2626,44 @@ ); /* ** CAPI3REF: Obtain Values For URI Parameters ** -** This is a utility routine, useful to VFS implementations, that checks +** These are utility routines, useful to VFS implementations, that check ** to see if a database file was a URI that contained a specific query -** parameter, and if so obtains the value of the query parameter. -** -** The zFilename argument is the filename pointer passed into the xOpen() -** method of a VFS implementation. The zParam argument is the name of the -** query parameter we seek. This routine returns the value of the zParam -** parameter if it exists. If the parameter does not exist, this routine -** returns a NULL pointer. -** -** If the zFilename argument to this function is not a pointer that SQLite -** passed into the xOpen VFS method, then the behavior of this routine -** is undefined and probably undesirable. +** parameter, and if so obtains the value of that query parameter. +** +** If F is the database filename pointer passed into the xOpen() method of +** a VFS implementation when the flags parameter to xOpen() has one or +** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and +** P is the name of the query parameter, then +** sqlite3_uri_parameter(F,P) returns the value of the P +** parameter if it exists or a NULL pointer if P does not appear as a +** query parameter on F. If P is a query parameter of F +** has no explicit value, then sqlite3_uri_parameter(F,P) returns +** a pointer to an empty string. +** +** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean +** parameter and returns true (1) or false (0) according to the value +** of P. The value of P is true if it is "yes" or "true" or "on" or +** a non-zero number and is false otherwise. If P is not a query parameter +** on F then sqlite3_uri_boolean(F,P,B) returns (B!=0). +** +** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a +** 64-bit signed integer and returns that integer, or D if P does not +** exist. If the value of P is something other than an integer, then +** zero is returned. +** +** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and +** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and +** is not a database file pathname pointer that SQLite passed into the xOpen +** VFS method, then the behavior of this routine is undefined and probably +** undesirable. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages ** @@ -2797,11 +2855,12 @@ ** zSql string ends at either the first '\000' or '\u0000' character or ** the nByte-th byte, whichever comes first. If the caller knows ** that the supplied string is nul-terminated, then there is a small ** performance advantage to be gained by passing an nByte parameter that ** is equal to the number of bytes in the input string including -** the nul-terminator bytes. +** the nul-terminator bytes as this saves SQLite from having to +** make a copy of the input string. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. @@ -2848,11 +2907,11 @@ ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column -** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled. +** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** the ** ** */ SQLITE_API int sqlite3_prepare( @@ -2922,10 +2981,29 @@ ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** +** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the +** [prepared statement] S has been stepped at least once using +** [sqlite3_step(S)] but has not run to completion and/or has not +** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) +** interface returns false if S is a NULL pointer. If S is not a +** NULL pointer and is not a pointer to a valid [prepared statement] +** object, then the behavior is undefined and probably undesirable. +** +** This interface can be used in combination [sqlite3_next_stmt()] +** to locate all prepared statements associated with a database +** connection that are in need of being reset. This can be used, +** for example, in diagnostic routines to search for prepared +** statements that are holding a transaction open. +*/ +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values @@ -3018,10 +3096,17 @@ ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the ** number of bytes in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. +** If a non-negative fourth parameter is provided to sqlite3_bind_text() +** or sqlite3_bind_text16() then that parameter must be the byte offset +** where the NUL terminator would occur assuming the string were NUL +** terminated. If any NUL characters occur at byte offsets less than +** the value of the fourth parameter then the resulting string value will +** contain embedded NULs. The result of expressions involving strings +** with embedded NULs is undefined. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called ** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), @@ -3351,10 +3436,16 @@ ** current row of the result set of [prepared statement] P. ** ^If prepared statement P does not have results ready to return ** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of ** interfaces) then sqlite3_data_count(P) returns 0. ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. +** ^The sqlite3_data_count(P) routine returns 0 if the previous call to +** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) +** will return non-zero if previous call to [sqlite3_step](P) returned +** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] +** where it always returns zero since each step of that multi-step +** pragma returns 0 columns of data. ** ** See also: [sqlite3_column_count()] */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); @@ -3450,11 +3541,11 @@ ** of the string. ^For clarity: the values returned by ** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero terminated. ^The return +** even empty strings, are always zero-terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. @@ -4030,11 +4121,16 @@ ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined -** function result. +** function result. If the 3rd parameter is non-negative, then it +** must be the byte offset into the string where the NUL terminator would +** appear if the string where NUL terminated. If any NUL characters occur +** in the string at a byte offset that is less than the value of the 3rd +** parameter, then the resulting string will contain embedded NULs and the +** result of expressions operating on strings with embedded NULs is undefined. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to @@ -4345,10 +4441,26 @@ ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +/* +** CAPI3REF: Return The Filename For A Database Connection +** +** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename +** associated with database N of connection D. ^The main database file +** has the name "main". If there is no attached database N on the database +** connection D, or if database N is a temporary or in-memory database, then +** a NULL pointer is returned. +** +** ^The filename returned by this function is the output of the +** xFullPathname method of the [VFS]. ^In other words, the filename +** will be an absolute pathname, even if the filename used +** to open the database originally was a URI or relative pathname. +*/ +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + /* ** CAPI3REF: Find the next prepared statement ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL @@ -4380,17 +4492,19 @@ ** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions ** return the P argument from the previous call of the same function ** on the same [database connection] D, or NULL for ** the first call for each function on D. ** +** The commit and rollback hook callbacks are not reentrant. ** The callback implementation must not do anything that will modify ** the database connection that invoked the callback. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the commit ** or rollback hook in the first place. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. +** Note that running any other SQL statements, including SELECT statements, +** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify +** the database connections for the meaning of "modify" in this paragraph. ** ** ^Registering a NULL function disables the callback. ** ** ^When the commit hook callback routine returns zero, the [COMMIT] ** operation is allowed to continue normally. ^If the commit hook @@ -4499,13 +4613,28 @@ ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** ^The sqlite3_release_memory() routine is a no-op returning zero ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** See also: [sqlite3_db_release_memory()] */ SQLITE_API int sqlite3_release_memory(int); +/* +** CAPI3REF: Free Memory Used By A Database Connection +** +** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap +** memory as possible from database connection D. Unlike the +** [sqlite3_release_memory()] interface, this interface is effect even +** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** omitted. +** +** See also: [sqlite3_release_memory()] +*/ +SQLITE_API int sqlite3_db_release_memory(sqlite3*); + /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. @@ -4516,11 +4645,12 @@ ** below the limit, it will exceed the limit rather than generate ** an [SQLITE_NOMEM] error. In other words, the soft heap limit ** is advisory only. ** ** ^The return value from sqlite3_soft_heap_limit64() is the size of -** the soft heap limit prior to the call. ^If the argument N is negative +** the soft heap limit prior to the call, or negative in the case of an +** error. ^If the argument N is negative ** then no change is made to the soft heap limit. Hence, the current ** size of the soft heap limit can be determined by invoking ** sqlite3_soft_heap_limit64() with a negative argument. ** ** ^If the argument N is zero then the soft heap limit is disabled. @@ -4532,11 +4662,11 @@ **
  • The soft heap limit is set to zero. **
  • Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. **
  • An alternative page cache implementation is specified using -** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). +** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). **
  • The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** )^ ** @@ -5274,19 +5404,19 @@ ** is selected automatically at compile-time. ^(The following ** implementations are available in the SQLite core: ** **
      **
    • SQLITE_MUTEX_OS2 -**
    • SQLITE_MUTEX_PTHREAD +**
    • SQLITE_MUTEX_PTHREADS **
    • SQLITE_MUTEX_W32 **
    • SQLITE_MUTEX_NOOP **
    )^ ** ** ^The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in ** a single-threaded application. ^The SQLITE_MUTEX_OS2, -** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations +** SQLITE_MUTEX_PTHREADS, and SQLITE_MUTEX_W32 implementations ** are appropriate for use on OS/2, Unix, and Windows. ** ** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex ** implementation is included with the library. In this case the @@ -5472,11 +5602,11 @@ ** defined and if NDEBUG is not defined. ** ** ^These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** -** ^The implementation is not required to provided versions of these +** ^The implementation is not required to provide versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then @@ -5600,13 +5730,13 @@ #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 -#define SQLITE_TESTCTRL_PGHDRSZ 17 -#define SQLITE_TESTCTRL_SCRATCHMALLOC 18 -#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19 +#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 #define SQLITE_TESTCTRL_LAST 19 /* ** CAPI3REF: SQLite Runtime Status ** @@ -5813,20 +5943,34 @@ **
    This parameter returns the approximate number of of bytes of heap ** and lookaside memory used by all prepared statements associated with ** the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. **
    +** +** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
    SQLITE_DBSTATUS_CACHE_HIT
    +**
    This parameter returns the number of pager cache hits that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT +** is always 0. +**
    +** +** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
    SQLITE_DBSTATUS_CACHE_MISS
    +**
    This parameter returns the number of pager cache misses that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS +** is always 0. +**
    ** */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_SCHEMA_USED 2 #define SQLITE_DBSTATUS_STMT_USED 3 #define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 -#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_CACHE_HIT 7 +#define SQLITE_DBSTATUS_CACHE_MISS 8 +#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** @@ -5876,11 +6020,10 @@ **
    ^This is the number of rows inserted into transient indices that ** were created automatically in order to help joins run faster. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance by adding permanent indices that do not ** need to be reinitialized each time the statement is run.
    -** ** */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 #define SQLITE_STMTSTATUS_AUTOINDEX 3 @@ -5892,21 +6035,37 @@ ** the pluggable module. The SQLite core has no knowledge of ** its size or internal structure and never deals with the ** sqlite3_pcache object except by holding and passing pointers ** to the object. ** -** See [sqlite3_pcache_methods] for additional information. +** See [sqlite3_pcache_methods2] for additional information. */ typedef struct sqlite3_pcache sqlite3_pcache; + +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache_page object represents a single page in the +** page cache. The page cache will allocate instances of this +** object. Various methods of the page cache use pointers to instances +** of this object as parameters or as their return value. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache_page sqlite3_pcache_page; +struct sqlite3_pcache_page { + void *pBuf; /* The content of the page */ + void *pExtra; /* Extra information associated with the page */ +}; /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can ** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods structure.)^ +** instance of the sqlite3_pcache_methods2 structure.)^ ** In many applications, most of the heap memory allocated by ** SQLite is used for the page cache. ** By implementing a ** custom page cache using this API, an application can better control ** the amount of memory consumed by SQLite, the way in which @@ -5916,20 +6075,20 @@ ** ** The alternative page cache mechanism is an ** extreme measure that is only needed by the most demanding applications. ** The built-in page cache is recommended for most uses. ** -** ^(The contents of the sqlite3_pcache_methods structure are copied to an +** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** ** [[the xInit() page cache method]] ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() -** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ +** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ ** The intent of the xInit() method is to set up global data structures ** required by the custom page cache implementation. ** ^(If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache.)^ @@ -5952,21 +6111,19 @@ ** [[the xCreate() page cache methods]] ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. ^szPage will not be a power of two. ^szPage -** will the page size of the database file that is to be cached plus an -** increment (here called "R") of less than 250. SQLite will use the -** extra R bytes on each page to store metadata about the underlying -** database page on disk. The value of R depends +** be allocated by the cache. ^szPage will always a power of two. ^The +** second parameter szExtra is a number of bytes of extra storage +** associated with each page cache entry. ^The szExtra parameter will +** a number less than 250. SQLite will use the +** extra szExtra bytes on each page to store metadata about the underlying +** database page on disk. The value passed into szExtra depends ** on the SQLite version, the target platform, and how SQLite was compiled. -** ^(R is constant for a particular build of SQLite. Except, there are two -** distinct values of R when SQLite is compiled with the proprietary -** ZIPVFS extension.)^ ^The second argument to -** xCreate(), bPurgeable, is true if the cache being created will -** be used to cache database pages of a file stored on disk, or +** ^The third argument to xCreate(), bPurgeable, is true if the cache being +** created will be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to @@ -5986,15 +6143,20 @@ ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** ** [[the xFetch() page cache methods]] ** The xFetch() method locates a page in the cache and returns a pointer to -** the page, or a NULL pointer. -** A "page", in this context, means a buffer of szPage bytes aligned at an -** 8-byte boundary. The page to be fetched is determined by the key. ^The -** minimum key value is 1. After it has been retrieved using xFetch, the page -** is considered to be "pinned". +** an sqlite3_pcache_page object associated with that page, or a NULL pointer. +** The pBuf element of the returned sqlite3_pcache_page object will be a +** pointer to a buffer of szPage bytes used to store the content of a +** single database page. The pExtra element of sqlite3_pcache_page will be +** a pointer to the szExtra bytes of extra storage that SQLite has requested +** for each entry in the page cache. +** +** The page to be fetched is determined by the key. ^The minimum key value +** is 1. After it has been retrieved using xFetch, the page is considered +** to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** cache implementation should use the value of the createFlag @@ -6043,12 +6205,41 @@ ** ** [[the xDestroy() page cache method]] ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] -** handle invalid, and will not use it with any other sqlite3_pcache_methods +** handle invalid, and will not use it with any other sqlite3_pcache_methods2 ** functions. +** +** [[the xShrink() page cache method]] +** ^SQLite invokes the xShrink() method when it wants the page cache to +** free up as much of heap memory as possible. The page cache implementation +** is not obligated to free any memory, but well-behaved implementations should +** do their best. +*/ +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; +struct sqlite3_pcache_methods2 { + int iVersion; + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); + void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, + unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + void (*xShrink)(sqlite3_pcache*); +}; + +/* +** This is the obsolete pcache_methods object that has now been replaced +** by sqlite3_pcache_methods2. This object is not used by SQLite. It is +** retained in the header file for backwards compatibility only. */ typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; struct sqlite3_pcache_methods { void *pArg; int (*xInit)(void*); @@ -6060,10 +6251,11 @@ void (*xUnpin)(sqlite3_pcache*, void*, int discard); void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); void (*xDestroy)(sqlite3_pcache*); }; + /* ** CAPI3REF: Online Backup Object ** ** The sqlite3_backup object records state information about an ongoing Index: SQLite.Interop/src/core/sqlite3ext.h ================================================================== --- SQLite.Interop/src/core/sqlite3ext.h +++ SQLite.Interop/src/core/sqlite3ext.h @@ -47,12 +47,14 @@ 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*); 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*)); + 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); int (*column_count)(sqlite3_stmt*pStmt); const char * (*column_database_name)(sqlite3_stmt*,int); @@ -73,14 +75,22 @@ int (*column_type)(sqlite3_stmt*,int iCol); sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); void * (*commit_hook)(sqlite3*,int(*)(void*),void*); int (*complete)(const char*sql); int (*complete16)(const void*sql); - int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*)); - int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*)); - int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); - int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); + int (*create_collation)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_collation16)(sqlite3*,const void*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_function)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + int (*create_function16)(sqlite3*,const void*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); int (*data_count)(sqlite3_stmt*pStmt); sqlite3 * (*db_handle)(sqlite3_stmt*); int (*declare_vtab)(sqlite3*,const char*); int (*enable_shared_cache)(int); @@ -121,20 +131,23 @@ void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_value)(sqlite3_context*,sqlite3_value*); void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); - int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*); + int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, + const char*,const char*),void*); void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); char * (*snprintf)(int,char*,const char*,...); int (*step)(sqlite3_stmt*); - int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*); + 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*); 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 * (*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*); double (*value_double)(sqlite3_value*); @@ -152,19 +165,23 @@ /* Added by 3.3.13 */ int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); int (*clear_bindings)(sqlite3_stmt*); /* Added by 3.4.1 */ - int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *)); + int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, + void (*xDestroy)(void *)); /* Added by 3.5.0 */ int (*bind_zeroblob)(sqlite3_stmt*,int,int); int (*blob_bytes)(sqlite3_blob*); int (*blob_close)(sqlite3_blob*); - int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**); + int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, + int,sqlite3_blob**); int (*blob_read)(sqlite3_blob*,void*,int,int); int (*blob_write)(sqlite3_blob*,const void*,int,int); - int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*)); + int (*create_collation_v2)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*), + void(*)(void*)); int (*file_control)(sqlite3*,const char*,int,void*); sqlite3_int64 (*memory_highwater)(int); sqlite3_int64 (*memory_used)(void); sqlite3_mutex *(*mutex_alloc)(int); void (*mutex_enter)(sqlite3_mutex*); @@ -196,11 +213,15 @@ int (*backup_pagecount)(sqlite3_backup*); int (*backup_remaining)(sqlite3_backup*); int (*backup_step)(sqlite3_backup*,int); const char *(*compileoption_get)(int); int (*compileoption_used)(const char*); - int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*)); + int (*create_function_v2)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*)); int (*db_config)(sqlite3*,int,...); sqlite3_mutex *(*db_mutex)(sqlite3*); int (*db_status)(sqlite3*,int,int*,int*,int); int (*extended_errcode)(sqlite3*); void (*log)(int,const char*,...); Index: SQLite.Interop/src/win/interop.c ================================================================== --- SQLite.Interop/src/win/interop.c +++ SQLite.Interop/src/win/interop.c @@ -81,10 +81,16 @@ if (ret) { // finalize failed -- so we must put back anything we munged CopyMemory(po, p, sizeof(Vdbe)); db->pVdbe = po; + + // + // NOTE: Ok, we must free this block that *we* allocated (above) since + // finalize did not do so. + // + sqlite3DbFree_interop(db, p); break; } else { ZeroMemory(po, sizeof(Vdbe)); @@ -324,11 +330,13 @@ if ((ctx->pFunc->flags & SQLITE_FUNC_NEEDCOLL) == 0) return NULL; if (pColl) { *enc = pColl->enc; +#if SQLITE_VERSION_NUMBER < 3007010 *ptype = pColl->type; +#endif *plen = (pColl->zName != 0) ? strlen(pColl->zName) : 0; return pColl->zName; } return NULL; Index: SQLite.Interop/src/win/interop.h ================================================================== --- SQLite.Interop/src/win/interop.h +++ SQLite.Interop/src/win/interop.h @@ -6,7 +6,7 @@ * Released to the public domain, use at your own risk! * */ #ifndef INTEROP_VERSION -#define INTEROP_VERSION "1.0.76.0" +#define INTEROP_VERSION "1.0.78.0" #endif Index: SQLite.MSIL.nuspec ================================================================== --- SQLite.MSIL.nuspec +++ SQLite.MSIL.nuspec @@ -1,10 +1,10 @@ System.Data.SQLite.MSIL - 1.0.76.0 + 1.0.78.0 SQLite Development Team An ADO.NET provider for SQLite (managed-only). en-US http://system.data.sqlite.org/ http://system.data.sqlite.org/images/sqlite32.png Index: SQLite.NET.2008.MSBuild.sln ================================================================== --- SQLite.NET.2008.MSBuild.sln +++ SQLite.NET.2008.MSBuild.sln @@ -20,10 +20,12 @@ {AC139952-261A-4463-B6FA-AEBC25284A66} = {AC139952-261A-4463-B6FA-AEBC25284A66} EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SQLite.Linq.2008", "System.Data.SQLite.Linq\System.Data.SQLite.Linq.2008.csproj", "{E6BF9F74-58E2-413B-A7CE-EA653ECB728D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.Designer.2008", "SQLite.Designer\SQLite.Designer.2008.csproj", "{9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testlinq.2008", "testlinq\testlinq.2008.csproj", "{9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}" ProjectSection(ProjectDependencies) = postProject {AC139952-261A-4463-B6FA-AEBC25283A66} = {AC139952-261A-4463-B6FA-AEBC25283A66} {E6BF9F74-58E2-413B-A7CE-EA653ECB728D} = {E6BF9F74-58E2-413B-A7CE-EA653ECB728D} EndProjectSection @@ -303,10 +305,56 @@ {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Any CPU.ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Mixed Platforms.ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Pocket PC 2003 (ARMV4).ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Win32.ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|x64.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Win32.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Win32.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|x64.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Any CPU.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Any CPU.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Mixed Platforms.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Win32.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Win32.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|x64.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|x64.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Any CPU.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Win32.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|x64.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Any CPU.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Pocket PC 2003 (ARMV4).ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Win32.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Win32.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|x64.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|x64.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Any CPU.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Any CPU.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Mixed Platforms.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Mixed Platforms.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Pocket PC 2003 (ARMV4).ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Win32.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Win32.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|x64.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|x64.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Any CPU.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Mixed Platforms.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Pocket PC 2003 (ARMV4).ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Win32.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|x64.ActiveCfg = Release|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|Any CPU Index: SQLite.NET.2010.MSBuild.sln ================================================================== --- SQLite.NET.2010.MSBuild.sln +++ SQLite.NET.2010.MSBuild.sln @@ -20,10 +20,12 @@ {AC139952-261A-4463-B6FA-AEBC25284A66} = {AC139952-261A-4463-B6FA-AEBC25284A66} EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SQLite.Linq.2010", "System.Data.SQLite.Linq\System.Data.SQLite.Linq.2010.csproj", "{E6BF9F74-58E2-413B-A7CE-EA653ECB728D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.Designer.2010", "SQLite.Designer\SQLite.Designer.2010.csproj", "{9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testlinq.2010", "testlinq\testlinq.2010.csproj", "{9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}" ProjectSection(ProjectDependencies) = postProject {AC139952-261A-4463-B6FA-AEBC25283A66} = {AC139952-261A-4463-B6FA-AEBC25283A66} {E6BF9F74-58E2-413B-A7CE-EA653ECB728D} = {E6BF9F74-58E2-413B-A7CE-EA653ECB728D} EndProjectSection @@ -261,10 +263,50 @@ {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseManagedOnly|x64.Build.0 = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Any CPU.ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Mixed Platforms.ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|Win32.ActiveCfg = Release|Any CPU {E6BF9F74-58E2-413B-A7CE-EA653ECB728D}.ReleaseNativeOnly|x64.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Win32.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|Win32.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Debug|x64.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Any CPU.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Any CPU.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Mixed Platforms.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Win32.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|Win32.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|x64.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugManagedOnly|x64.Build.0 = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Any CPU.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|Win32.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.DebugNativeOnly|x64.ActiveCfg = Debug|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Any CPU.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Win32.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|Win32.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|x64.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.Release|x64.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Any CPU.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Any CPU.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Mixed Platforms.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Mixed Platforms.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Win32.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|Win32.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|x64.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseManagedOnly|x64.Build.0 = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Any CPU.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Mixed Platforms.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|Win32.ActiveCfg = Release|Any CPU + {9B4A5CF6-5BE5-4926-ACC7-B729A8C05198}.ReleaseNativeOnly|x64.ActiveCfg = Release|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {9D3CF7A6-092A-4B05-B0E4-BEF6944525B3}.Debug|Win32.ActiveCfg = Debug|Any CPU Index: SQLite.NET.Settings.targets ================================================================== --- SQLite.NET.Settings.targets +++ SQLite.NET.Settings.targets @@ -104,10 +104,18 @@ false + + + true + (('$(IsCompactFramework)' == 'false' And Exists('$(SQLiteNetDir)\System.Data.SQLite\System.Data.SQLite.snk')) Or + ('$(IsCompactFramework)' != 'false' And Exists('$(SQLiteNetDir)\System.Data.SQLite\System.Data.SQLite.CF.snk')))"> true ADDED SQLite.NET.targets Index: SQLite.NET.targets ================================================================== --- /dev/null +++ SQLite.NET.targets @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: SQLite.nuspec ================================================================== --- SQLite.nuspec +++ SQLite.nuspec @@ -1,11 +1,11 @@ System.Data.SQLite System.Data.SQLite (x86) - 1.0.76.0 + 1.0.78.0 SQLite Development Team The official SQLite database engine combined with a complete ADO.NET provider all rolled into a single mixed-mode assembly for x86. en-US http://system.data.sqlite.org/ http://system.data.sqlite.org/images/sqlite32.png Index: SQLite.x64.nuspec ================================================================== --- SQLite.x64.nuspec +++ SQLite.x64.nuspec @@ -1,10 +1,10 @@ System.Data.SQLite.x64 - 1.0.76.0 + 1.0.78.0 SQLite Development Team The official SQLite database engine combined with a complete ADO.NET provider all rolled into a single mixed-mode assembly for x64. en-US http://system.data.sqlite.org/ http://system.data.sqlite.org/images/sqlite32.png Index: SQLite.x86.nuspec ================================================================== --- SQLite.x86.nuspec +++ SQLite.x86.nuspec @@ -1,10 +1,10 @@ System.Data.SQLite.x86 - 1.0.76.0 + 1.0.78.0 SQLite Development Team The official SQLite database engine combined with a complete ADO.NET provider all rolled into a single mixed-mode assembly for x86. en-US http://system.data.sqlite.org/ http://system.data.sqlite.org/images/sqlite32.png Index: Setup/SQLite.iss ================================================================== --- Setup/SQLite.iss +++ Setup/SQLite.iss @@ -7,13 +7,15 @@ #define BaseConfiguration StringChange(AppConfiguration, "NativeOnly", "") #define GacProcessor StringChange(AppProcessor, "x64", "amd64") #if Pos("NativeOnly", AppConfiguration) == 0 +#define InstallerCondition "Application\Core\MSIL and Application\Designer and Application\Designer\Installer" #define AppVersion GetStringFileInfo("..\bin\" + Year + "\" + AppPlatform + "\" + AppConfiguration + "\System.Data.SQLite.dll", PRODUCT_VERSION) #define OutputConfiguration StringChange(StringChange(AppConfiguration, "Debug", "setup"), "Release", "setup") + "-bundle" #else +#define InstallerCondition "Application\Core\MSIL and Application\Core\" + AppProcessor + " and Application\Designer and Application\Designer\Installer" #define AppVersion GetStringFileInfo("..\bin\" + Year + "\" + BaseConfiguration + "\bin\System.Data.SQLite.dll", PRODUCT_VERSION) #define OutputConfiguration StringChange(StringChange(BaseConfiguration, "Debug", "setup"), "Release", "setup") #endif [Setup] @@ -54,16 +56,20 @@ Name: Application; Description: System.Data.SQLite components.; Types: custom compact full Name: Application\Core; Description: Core components.; Types: custom compact full Name: Application\Core\MSIL; Description: Core managed components.; Types: custom compact full Name: Application\Core\{#AppProcessor}; Description: Core native components.; Types: custom compact full Name: Application\LINQ; Description: LINQ support components.; Types: custom compact full +Name: Application\Designer; Description: Visual Studio designer components.; Types: custom full +Name: Application\Designer\Installer; Description: Visual Studio designer installer components.; Types: custom full Name: Application\Symbols; Description: Debugging symbol components.; Types: custom compact full Name: Application\Documentation; Description: Documentation components.; Types: custom compact full Name: Application\Test; Description: Test components.; Types: custom compact full [Tasks] Components: Application\Core\MSIL Or Application\LINQ; Name: ngen; Description: Generate native images for the assemblies and install the images in the native image cache.; Check: CheckIsNetFx2Setup() or CheckIsNetFx4Setup() +Components: {#InstallerCondition}; Name: vs2008; Description: Install the designer components for Visual Studio 2008.; Check: CheckIsNetFx2Setup() +Components: {#InstallerCondition}; Name: vs2010; Description: Install the designer components for Visual Studio 2010.; Check: CheckIsNetFx4Setup() #if Pos("NativeOnly", AppConfiguration) == 0 Components: Application\Core\MSIL Or Application\LINQ; Name: gac; Description: Install the assemblies into the global assembly cache.; Flags: unchecked; Check: CheckIsNetFx2Setup() or CheckIsNetFx4Setup() #endif @@ -70,12 +76,16 @@ [Run] Components: Application\Core\MSIL; Tasks: ngen; Filename: {code:GetNetFx2InstallRoot|Ngen.exe}; Parameters: "install ""{app}\bin\System.Data.SQLite.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx2Setup() Components: Application\Core\MSIL; Tasks: ngen; Filename: {code:GetNetFx4InstallRoot|Ngen.exe}; Parameters: "install ""{app}\bin\System.Data.SQLite.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx4Setup() Components: Application\LINQ; Tasks: ngen; Filename: {code:GetNetFx2InstallRoot|Ngen.exe}; Parameters: "install ""{app}\bin\System.Data.SQLite.Linq.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx2Setup() and CheckForNetFx35(1) Components: Application\LINQ; Tasks: ngen; Filename: {code:GetNetFx4InstallRoot|Ngen.exe}; Parameters: "install ""{app}\bin\System.Data.SQLite.Linq.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx4Setup() +Components: {#InstallerCondition}; Tasks: vs2008; Filename: {app}\bin\Installer.exe; Parameters: "-install true -installFlags AllExceptGAC -tracePriority Lowest -verbose true -noCompact true -noNetFx40 true -noVs2010 true -whatIf false -confirm true"; Flags: skipifdoesntexist; Check: CheckIsNetFx2Setup() +Components: {#InstallerCondition}; Tasks: vs2010; Filename: {app}\bin\Installer.exe; Parameters: "-install true -installFlags AllExceptGAC -tracePriority Lowest -verbose true -noCompact true -noNetFx20 true -noVs2008 true -whatIf false -confirm true"; Flags: skipifdoesntexist; Check: CheckIsNetFx4Setup() [UninstallRun] +Components: {#InstallerCondition}; Tasks: vs2010; Filename: {app}\bin\Installer.exe; Parameters: "-install false -installFlags AllExceptGAC -tracePriority Lowest -verbose true -noCompact true -noNetFx20 true -noVs2008 true -whatIf false -confirm true"; Flags: skipifdoesntexist; Check: CheckIsNetFx4Setup() +Components: {#InstallerCondition}; Tasks: vs2008; Filename: {app}\bin\Installer.exe; Parameters: "-install false -installFlags AllExceptGAC -tracePriority Lowest -verbose true -noCompact true -noNetFx40 true -noVs2010 true -whatIf false -confirm true"; Flags: skipifdoesntexist; Check: CheckIsNetFx2Setup() Components: Application\LINQ; Tasks: ngen; Filename: {code:GetNetFx4InstallRoot|Ngen.exe}; Parameters: "uninstall ""{app}\bin\System.Data.SQLite.Linq.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx4Setup() Components: Application\LINQ; Tasks: ngen; Filename: {code:GetNetFx2InstallRoot|Ngen.exe}; Parameters: "uninstall ""{app}\bin\System.Data.SQLite.Linq.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx2Setup() and CheckForNetFx35(1) Components: Application\Core\MSIL; Tasks: ngen; Filename: {code:GetNetFx4InstallRoot|Ngen.exe}; Parameters: "uninstall ""{app}\bin\System.Data.SQLite.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx4Setup() Components: Application\Core\MSIL; Tasks: ngen; Filename: {code:GetNetFx2InstallRoot|Ngen.exe}; Parameters: "uninstall ""{app}\bin\System.Data.SQLite.dll"" /nologo"; Flags: skipifdoesntexist; Check: CheckIsNetFx2Setup() @@ -111,10 +121,14 @@ Components: Application\Core\{#AppProcessor}; Source: ..\bin\{#Year}\{#AppPlatform}\{#AppConfiguration}\SQLite.Interop.dll; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Components: Application\Core\{#AppProcessor} and Application\Symbols; Source: ..\bin\{#Year}\{#AppPlatform}\{#AppConfiguration}\SQLite.Interop.pdb; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete #endif Components: Application\Documentation; Source: ..\doc\SQLite.NET.chm; DestDir: {app}\doc; Flags: restartreplace uninsrestartdelete +Components: Application\Designer; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\SQLite.Designer.dll; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete +Components: Application\Designer and Application\Symbols; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\SQLite.Designer.pdb; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete +Components: Application\Designer\Installer; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\Installer.exe; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete +Components: Application\Designer\Installer and Application\Symbols; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\Installer.pdb; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Components: Application\Test; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\test.exe; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Components: Application\Test and Application\Symbols; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\test.pdb; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Components: Application\Test; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\test.exe.config; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Components: Application\Test and Application\LINQ; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\testlinq.exe; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Components: Application\Test and Application\LINQ and Application\Symbols; Source: ..\bin\{#Year}\{#BaseConfiguration}\bin\testlinq.pdb; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete Index: Setup/archive.bat ================================================================== --- Setup/archive.bat +++ Setup/archive.bat @@ -64,15 +64,17 @@ %_VECHO% Version = '%VERSION%' CALL :fn_ResetErrorLevel -%_ECHO% IF NOT EXIST Setup\Output MKDIR Setup\Output +IF NOT EXIST Setup\Output ( + %_ECHO% MKDIR Setup\Output -IF ERRORLEVEL 1 ( - ECHO Could not create directory "Setup\Output". - GOTO errors + IF ERRORLEVEL 1 ( + ECHO Could not create directory "Setup\Output". + GOTO errors + ) ) %_ECHO% zip.exe -v -r Setup\Output\sqlite-netFx-source-%VERSION%.zip * -x @exclude_src.txt IF ERRORLEVEL 1 ( Index: Setup/release.bat ================================================================== --- Setup/release.bat +++ Setup/release.bat @@ -145,15 +145,17 @@ %_VECHO% Version = '%VERSION%' CALL :fn_ResetErrorLevel -%_ECHO% IF NOT EXIST Setup\Output MKDIR Setup\Output +IF NOT EXIST Setup\Output ( + %_ECHO% MKDIR Setup\Output -IF ERRORLEVEL 1 ( - ECHO Could not create directory "Setup\Output". - GOTO errors + IF ERRORLEVEL 1 ( + ECHO Could not create directory "Setup\Output". + GOTO errors + ) ) IF DEFINED BASE_CONFIGURATIONSUFFIX ( %_ECHO% zip.exe -v -j -r "Setup\Output\sqlite-%FRAMEWORK%-%TYPE%-%BASE_PLATFORM%-%YEAR%-%VERSION%.zip" "bin\%YEAR%\%BASE_CONFIGURATION%%BASE_CONFIGURATIONSUFFIX%\bin" -x @exclude_bin.txt ) ELSE ( ADDED Setup/set_netFx20.bat Index: Setup/set_netFx20.bat ================================================================== --- /dev/null +++ Setup/set_netFx20.bat @@ -0,0 +1,38 @@ +@ECHO OFF + +:: +:: set_default.bat -- +:: +:: Written by Joe Mistachkin. +:: Released to the public domain, use at your own risk! +:: + +IF NOT DEFINED ISNETFX2 ( + SET ISNETFX2=True +) + +IF NOT DEFINED VCRUNTIME ( + SET VCRUNTIME=2008_SP1 +) + +IF NOT DEFINED CONFIGURATION ( + SET CONFIGURATION=Release +) + +IF NOT DEFINED PLATFORM ( + SET PLATFORM=Win32 +) + +IF NOT DEFINED PROCESSOR ( + SET PROCESSOR=x86 +) + +IF NOT DEFINED YEAR ( + SET YEAR=2008 +) + +IF NOT DEFINED FRAMEWORK ( + SET FRAMEWORK=netFx35 +) + +:end_of_file ADDED Setup/set_netFx40.bat Index: Setup/set_netFx40.bat ================================================================== --- /dev/null +++ Setup/set_netFx40.bat @@ -0,0 +1,38 @@ +@ECHO OFF + +:: +:: set_netFx40.bat -- +:: +:: Written by Joe Mistachkin. +:: Released to the public domain, use at your own risk! +:: + +IF NOT DEFINED ISNETFX2 ( + SET ISNETFX2=False +) + +IF NOT DEFINED VCRUNTIME ( + SET VCRUNTIME=2010_SP1 +) + +IF NOT DEFINED CONFIGURATION ( + SET CONFIGURATION=Release +) + +IF NOT DEFINED PLATFORM ( + SET PLATFORM=Win32 +) + +IF NOT DEFINED PROCESSOR ( + SET PROCESSOR=x86 +) + +IF NOT DEFINED YEAR ( + SET YEAR=2010 +) + +IF NOT DEFINED FRAMEWORK ( + SET FRAMEWORK=netFx40 +) + +:end_of_file Index: Setup/set_x64_2010.bat ================================================================== --- Setup/set_x64_2010.bat +++ Setup/set_x64_2010.bat @@ -5,10 +5,10 @@ :: :: Written by Joe Mistachkin. :: Released to the public domain, use at your own risk! :: -SET ISNETFX2=True +SET ISNETFX2=False SET VCRUNTIME=2010_SP1 SET PLATFORM=x64 SET PROCESSOR=x64 SET YEAR=2010 Index: Setup/set_x86_2010.bat ================================================================== --- Setup/set_x86_2010.bat +++ Setup/set_x86_2010.bat @@ -5,10 +5,10 @@ :: :: Written by Joe Mistachkin. :: Released to the public domain, use at your own risk! :: -SET ISNETFX2=True +SET ISNETFX2=False SET VCRUNTIME=2010_SP1 SET PLATFORM=Win32 SET PROCESSOR=x86 SET YEAR=2010 Index: Setup/test_all.bat ================================================================== --- Setup/test_all.bat +++ Setup/test_all.bat @@ -62,15 +62,15 @@ SET YEARS=2008 ) %_VECHO% Years = '%YEARS%' -IF "%PROCESSOR_ARCHITECTURE%" == "x86" ( +IF /I "%PROCESSOR_ARCHITECTURE%" == "x86" ( SET PLATFORM=Win32 ) -IF "%PROCESSOR_ARCHITECTURE%" == "amd64" ( +IF /I "%PROCESSOR_ARCHITECTURE%" == "AMD64" ( SET PLATFORM=x64 ) IF NOT DEFINED PLATFORM ( ECHO Unsupported platform. @@ -85,50 +85,70 @@ ECHO Could not change directory to "%ROOT%". GOTO errors ) FOR %%Y IN (%YEARS%) DO ( - %_ECHO% Externals\Eagle\bin\EagleShell.exe -preInitialize "set test_year {%%Y}" -file Tests\all.eagle - - IF ERRORLEVEL 1 ( - ECHO Testing of "%%Y" managed-only assembly failed. - GOTO errors - ) - - %_ECHO% XCOPY "bin\%%Y\Release\bin\test.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% - - IF ERRORLEVEL 1 ( - ECHO Failed to copy "bin\%%Y\Release\bin\test.*" to "bin\%%Y\%PLATFORM%\Release". - GOTO errors - ) - - %_ECHO% XCOPY "bin\%%Y\Release\bin\System.Data.SQLite.Linq.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% - - IF ERRORLEVEL 1 ( - ECHO Failed to copy "bin\%%Y\Release\bin\System.Data.SQLite.Linq.*" to "bin\%%Y\%PLATFORM%\Release". - GOTO errors - ) - - %_ECHO% XCOPY "bin\%%Y\Release\bin\testlinq.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% - - IF ERRORLEVEL 1 ( - ECHO Failed to copy "bin\%%Y\Release\bin\testlinq.*" to "bin\%%Y\%PLATFORM%\Release". - GOTO errors - ) - - %_ECHO% XCOPY "bin\%%Y\Release\bin\northwindEF.db" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% - - IF ERRORLEVEL 1 ( - ECHO Failed to copy "bin\%%Y\Release\bin\northwindEF.db" to "bin\%%Y\%PLATFORM%\Release". - GOTO errors - ) - - %_ECHO% Externals\Eagle\bin\EagleShell.exe -preInitialize "set test_year {%%Y}" -initialize -runtimeOption native -file Tests\all.eagle - - IF ERRORLEVEL 1 ( - ECHO Testing of "%%Y" mixed-mode assembly failed. - GOTO errors + IF NOT DEFINED NOMANAGEDONLY ( + %_ECHO% Externals\Eagle\bin\EagleShell.exe -preInitialize "set test_year {%%Y}" -file Tests\all.eagle + + IF ERRORLEVEL 1 ( + ECHO Testing of "%%Y" managed-only assembly failed. + GOTO errors + ) + ) + + IF NOT DEFINED NOMIXEDMODE ( + IF NOT DEFINED NOXCOPY ( + %_ECHO% XCOPY "bin\%%Y\Release\bin\test.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% + + IF ERRORLEVEL 1 ( + ECHO Failed to copy "bin\%%Y\Release\bin\test.*" to "bin\%%Y\%PLATFORM%\Release". + GOTO errors + ) + + %_ECHO% XCOPY "bin\%%Y\Release\bin\System.Data.SQLite.Linq.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% + + IF ERRORLEVEL 1 ( + ECHO Failed to copy "bin\%%Y\Release\bin\System.Data.SQLite.Linq.*" to "bin\%%Y\%PLATFORM%\Release". + GOTO errors + ) + + %_ECHO% XCOPY "bin\%%Y\Release\bin\testlinq.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% + + IF ERRORLEVEL 1 ( + ECHO Failed to copy "bin\%%Y\Release\bin\testlinq.*" to "bin\%%Y\%PLATFORM%\Release". + GOTO errors + ) + + %_ECHO% XCOPY "bin\%%Y\Release\bin\northwindEF.db" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% + + IF ERRORLEVEL 1 ( + ECHO Failed to copy "bin\%%Y\Release\bin\northwindEF.db" to "bin\%%Y\%PLATFORM%\Release". + GOTO errors + ) + + %_ECHO% XCOPY "bin\%%Y\Release\bin\SQLite.Designer.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% + + IF ERRORLEVEL 1 ( + ECHO Failed to copy "bin\%%Y\Release\bin\SQLite.Designer.*" to "bin\%%Y\%PLATFORM%\Release". + GOTO errors + ) + + %_ECHO% XCOPY "bin\%%Y\Release\bin\Installer.*" "bin\%%Y\%PLATFORM%\Release" %FFLAGS% %DFLAGS% + + IF ERRORLEVEL 1 ( + ECHO Failed to copy "bin\%%Y\Release\bin\Installer.*" to "bin\%%Y\%PLATFORM%\Release". + GOTO errors + ) + ) + + %_ECHO% Externals\Eagle\bin\EagleShell.exe -preInitialize "set test_year {%%Y}" -initialize -runtimeOption native -file Tests\all.eagle + + IF ERRORLEVEL 1 ( + ECHO Testing of "%%Y" mixed-mode assembly failed. + GOTO errors + ) ) ) %_ECHO% POPD Index: System.Data.SQLite.Linq/AssemblyInfo.cs ================================================================== --- System.Data.SQLite.Linq/AssemblyInfo.cs +++ System.Data.SQLite.Linq/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using System.Runtime.ConstrainedExecution; @@ -35,7 +42,7 @@ // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] Index: System.Data.SQLite.Linq/Properties/Resources.Designer.cs ================================================================== --- System.Data.SQLite.Linq/Properties/Resources.Designer.cs +++ System.Data.SQLite.Linq/Properties/Resources.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // Index: System.Data.SQLite.Linq/Resources/SQLiteProviderServices.ProviderManifest.xml ================================================================== --- System.Data.SQLite.Linq/Resources/SQLiteProviderServices.ProviderManifest.xml +++ System.Data.SQLite.Linq/Resources/SQLiteProviderServices.ProviderManifest.xml @@ -1,6 +1,16 @@ + + + - + Index: System.Data.SQLite/AssemblyInfo.cs ================================================================== --- System.Data.SQLite/AssemblyInfo.cs +++ System.Data.SQLite/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Resources; @@ -31,11 +38,11 @@ // Setting ComVisible to false makes the types in this assembly not visible // to COM componenets. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("System.Data.SQLite.Linq, PublicKey=002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0a25a843266973704137cbb0e7441c1fe7cae4e2440ae91ab8cde3933febcb1ac48dd33b40e13c421d8215c18a4349a436dd499e3c385cc683015f886f6c10bd90115eb2bd61b67750839e3a19941dc9c")] +[assembly: InternalsVisibleTo("System.Data.SQLite.Linq, PublicKey=" + System.Data.SQLite.SQLite3.PublicKey)] [assembly: NeutralResourcesLanguage("en")] #if !PLATFORM_COMPACTFRAMEWORK [assembly: AllowPartiallyTrustedCallers] [assembly: ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] @@ -55,9 +62,9 @@ // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] #if !PLATFORM_COMPACTFRAMEWORK -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] #endif Index: System.Data.SQLite/DataTypes.xml ================================================================== --- System.Data.SQLite/DataTypes.xml +++ System.Data.SQLite/DataTypes.xml @@ -1,6 +1,16 @@ + + + smallint 10 5 Index: System.Data.SQLite/MetaDataCollections.xml ================================================================== --- System.Data.SQLite/MetaDataCollections.xml +++ System.Data.SQLite/MetaDataCollections.xml @@ -1,6 +1,16 @@ + + + MetaDataCollections 0 0 Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -6,10 +6,13 @@ ********************************************************/ namespace System.Data.SQLite { using System; +#if DEBUG + using System.Diagnostics; +#endif using System.Runtime.InteropServices; #if !PLATFORM_COMPACTFRAMEWORK [UnmanagedFunctionPointer(CallingConvention.Cdecl)] #endif @@ -18,12 +21,22 @@ /// /// This class implements SQLiteBase completely, and is the guts of the code that interop's SQLite with .NET /// internal class SQLite3 : SQLiteBase { + // + // NOTE: This is the public key for the System.Data.SQLite assembly. If you change the + // SNK file, you will need to change this as well. + // + internal const string PublicKey = + "002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1" + + "b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0" + + "a25a843266973704137cbb0e7441c1fe7cae4e2440ae91ab8cde3933febcb1ac48dd33b40e13c421" + + "d8215c18a4349a436dd499e3c385cc683015f886f6c10bd90115eb2bd61b67750839e3a19941dc9c"; + #if !PLATFORM_COMPACTFRAMEWORK - internal const string DesignerVersion = "1.0.76.0"; + internal const string DesignerVersion = "1.0.78.0"; #endif /// /// The opaque pointer returned to us by the sqlite provider /// @@ -38,40 +51,82 @@ /// /// The user-defined functions registered on this connection /// protected SQLiteFunction[] _functionsArray; - internal SQLite3(SQLiteDateFormats fmt) - : base(fmt) + internal SQLite3(SQLiteDateFormats fmt, DateTimeKind kind) + : base(fmt, kind) { } - protected override void Dispose(bool bDisposing) + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ { - if (bDisposing) - Close(); +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLite3).Name); +#endif } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + Close(); + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// // It isn't necessary to cleanup any functions we've registered. If the connection // goes to the pool and is resurrected later, re-registered functions will overwrite the // previous functions. The SQLiteFunctionCookieHandle will take care of freeing unmanaged // resources belonging to the previously-registered functions. internal override void Close() { if (_sql != null) { - if (_usePool) - { - SQLiteBase.ResetConnection(_sql); - SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion); - } - else - _sql.Dispose(); - } - - _sql = null; - } + if (_usePool) + { + SQLiteBase.ResetConnection(_sql); + SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion); + } + else + { + _sql.Dispose(); + } + _sql = null; + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// internal override void Cancel() { UnsafeNativeMethods.sqlite3_interrupt(_sql); } @@ -89,10 +144,18 @@ get { return UTF8ToString(UnsafeNativeMethods.sqlite3_libversion(), -1); } } + + internal static string SQLiteSourceId + { + get + { + return UTF8ToString(UnsafeNativeMethods.sqlite3_sourceid(), -1); + } + } internal override bool AutoCommit { get { @@ -113,10 +176,26 @@ get { return UnsafeNativeMethods.sqlite3_changes(_sql); } } + + internal override long MemoryUsed + { + get + { + return UnsafeNativeMethods.sqlite3_memory_used(); + } + } + + internal override long MemoryHighwater + { + get + { + return UnsafeNativeMethods.sqlite3_memory_highwater(0); + } + } /// /// Shutdown the SQLite engine so that it can be restarted with different config options. /// We depend on auto initialization to recover. /// @@ -150,10 +229,15 @@ #if !SQLITE_STANDARD int n = UnsafeNativeMethods.sqlite3_open_interop(ToUTF8(strFilename), (int)flags, out db); #else int n = UnsafeNativeMethods.sqlite3_open_v2(ToUTF8(strFilename), out db, (int)flags, IntPtr.Zero); #endif + +#if DEBUG + Trace.WriteLine(String.Format("Open: {0}", db)); +#endif + if (n > 0) throw new SQLiteException(n, null); _sql = db; } // Bind functions to this connection. If any previous functions of the same name @@ -262,10 +346,36 @@ return SQLiteBase.SQLiteLastError(_sql); } internal override SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, out string strRemain) { + if (!String.IsNullOrEmpty(strSql)) + { + // + // NOTE: SQLite does not support the concept of separate schemas + // in one database; therefore, remove the base schema name + // used to smooth integration with the base .NET Framework + // data classes. + // + string baseSchemaName = (cnn != null) ? cnn._baseSchemaName : null; + + if (!String.IsNullOrEmpty(baseSchemaName)) + { + strSql = strSql.Replace( + String.Format("[{0}].", baseSchemaName), String.Empty); + + strSql = strSql.Replace( + String.Format("{0}.", baseSchemaName), String.Empty); + } + } + + if ((cnn != null) && + ((cnn.Flags & SQLiteConnectionFlags.LogPrepare) == SQLiteConnectionFlags.LogPrepare)) + { + LogMessage(0, String.Format("Preparing {{{0}}}...", strSql)); + } + IntPtr stmt = IntPtr.Zero; IntPtr ptr = IntPtr.Zero; int len = 0; int n = 17; int retries = 0; @@ -285,10 +395,14 @@ n = UnsafeNativeMethods.sqlite3_prepare_interop(_sql, psql, b.Length - 1, out stmt, out ptr, out len); #else n = UnsafeNativeMethods.sqlite3_prepare(_sql, psql, b.Length - 1, out stmt, out ptr); len = -1; #endif + +#if DEBUG + Trace.WriteLine(String.Format("Prepare: {0}", stmt)); +#endif if (n == 17) retries++; else if (n == 1) { @@ -386,10 +500,16 @@ internal override void Bind_Int32(SQLiteStatement stmt, int index, int value) { int n = UnsafeNativeMethods.sqlite3_bind_int(stmt._sqlite_stmt, index, value); if (n > 0) throw new SQLiteException(n, SQLiteLastError()); } + + internal override void Bind_UInt32(SQLiteStatement stmt, int index, uint value) + { + int n = UnsafeNativeMethods.sqlite3_bind_uint(stmt._sqlite_stmt, index, value); + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + } internal override void Bind_Int64(SQLiteStatement stmt, int index, long value) { #if !PLATFORM_COMPACTFRAMEWORK int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value); @@ -396,10 +516,20 @@ #else int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value); #endif if (n > 0) throw new SQLiteException(n, SQLiteLastError()); } + + internal override void Bind_UInt64(SQLiteStatement stmt, int index, ulong value) + { +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_uint64(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_uint64_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + } internal override void Bind_Text(SQLiteStatement stmt, int index, string value) { byte[] b = ToUTF8(value); int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1)); @@ -406,13 +536,56 @@ if (n > 0) throw new SQLiteException(n, SQLiteLastError()); } internal override void Bind_DateTime(SQLiteStatement stmt, int index, DateTime dt) { - byte[] b = ToUTF8(dt); - int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1)); - if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + switch (_datetimeFormat) + { + case SQLiteDateFormats.Ticks: + { + long value = dt.Ticks; + +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + case SQLiteDateFormats.JulianDay: + { + double value = ToJulianDay(dt); + +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_double(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_double_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + case SQLiteDateFormats.UnixEpoch: + { + long value = Convert.ToInt64(dt.Subtract(UnixEpoch).TotalSeconds); + +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + default: + { + byte[] b = ToUTF8(dt); + int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1)); + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + } } internal override void Bind_Blob(SQLiteStatement stmt, int index, byte[] blobData) { int n = UnsafeNativeMethods.sqlite3_bind_blob(stmt._sqlite_stmt, index, blobData, blobData.Length, (IntPtr)(-1)); @@ -952,13 +1125,68 @@ /// /// The callback function to invoke. /// Returns a result code internal override int SetLogCallback(SQLiteLogCallback func) { - int rc = UnsafeNativeMethods.sqlite3_config((int)SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, (IntPtr)0); + int rc = UnsafeNativeMethods.sqlite3_config( + (int)SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, (IntPtr)0); + return rc; } + + /// + /// Determines if the SQLite core library has been initialized for the + /// current process. + /// + /// + /// A boolean indicating whether or not the SQLite core library has been + /// initialized for the current process. + /// + internal override bool IsInitialized() + { + return StaticIsInitialized(); + } + + /// + /// Determines if the SQLite core library has been initialized for the + /// current process. + /// + /// + /// A boolean indicating whether or not the SQLite core library has been + /// initialized for the current process. + /// + internal static bool StaticIsInitialized() + { +#if !PLATFORM_COMPACTFRAMEWORK + // + // NOTE: Save the state of the logging class and then restore it + // after we are done to avoid logging too many false errors. + // + bool savedEnabled = SQLiteLog.Enabled; + SQLiteLog.Enabled = false; + + try + { +#endif + // + // NOTE: This method [ab]uses the fact that SQLite will always + // return SQLITE_ERROR for any unknown configuration option + // *unless* the SQLite library has already been initialized. + // In that case it will always return SQLITE_MISUSE. + // + int rc = UnsafeNativeMethods.sqlite3_config( + (int)SQLiteConfigOpsEnum.SQLITE_CONFIG_NONE, null, (IntPtr)0); + + return (rc == /* SQLITE_MISUSE */ 21); +#if !PLATFORM_COMPACTFRAMEWORK + } + finally + { + SQLiteLog.Enabled = savedEnabled; + } +#endif + } /// /// Helper function to retrieve a column of data from an active statement. /// /// The statement being step()'d through Index: System.Data.SQLite/SQLite3_UTF16.cs ================================================================== --- System.Data.SQLite/SQLite3_UTF16.cs +++ System.Data.SQLite/SQLite3_UTF16.cs @@ -6,30 +6,77 @@ ********************************************************/ namespace System.Data.SQLite { using System; +#if DEBUG + using System.Diagnostics; +#endif using System.Runtime.InteropServices; /// /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode) /// internal class SQLite3_UTF16 : SQLite3 { - internal SQLite3_UTF16(SQLiteDateFormats fmt) - : base(fmt) - { - } - + internal SQLite3_UTF16(SQLiteDateFormats fmt, DateTimeKind kind) + : base(fmt, kind) + { + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLite3_UTF16).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8 /// /// A pointer to a UTF-16 string /// The length (IN BYTES) of the string /// A .NET string public override string ToString(IntPtr b, int nbytelen) - { + { + CheckDisposed(); return UTF16ToString(b, nbytelen); } public static string UTF16ToString(IntPtr b, int nbytelen) { @@ -40,11 +87,11 @@ else return Marshal.PtrToStringUni(b, nbytelen / 2); } internal override void Open(string strFilename, SQLiteOpenFlagsEnum flags, int maxPoolSize, bool usePool) - { + { if (_sql != null) return; _usePool = usePool; if (usePool) { @@ -61,100 +108,148 @@ #else if ((flags & SQLiteOpenFlagsEnum.Create) == 0 && System.IO.File.Exists(strFilename) == false) throw new SQLiteException((int)SQLiteErrorCode.CantOpen, strFilename); int n = UnsafeNativeMethods.sqlite3_open16(strFilename, out db); +#endif + +#if DEBUG + Trace.WriteLine(String.Format("Open: {0}", db)); #endif + if (n > 0) throw new SQLiteException(n, null); _sql = db; } _functionsArray = SQLiteFunction.BindFunctions(this); } internal override void Bind_DateTime(SQLiteStatement stmt, int index, DateTime dt) - { - Bind_Text(stmt, index, ToString(dt)); + { + switch (_datetimeFormat) + { + case SQLiteDateFormats.Ticks: + { + long value = dt.Ticks; + +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + case SQLiteDateFormats.JulianDay: + { + double value = ToJulianDay(dt); + +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_double(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_double_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + case SQLiteDateFormats.UnixEpoch: + { + long value = Convert.ToInt64(dt.Subtract(UnixEpoch).TotalSeconds); + +#if !PLATFORM_COMPACTFRAMEWORK + int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value); +#else + int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value); +#endif + if (n > 0) throw new SQLiteException(n, SQLiteLastError()); + break; + } + default: + { + Bind_Text(stmt, index, ToString(dt)); + break; + } + } } internal override void Bind_Text(SQLiteStatement stmt, int index, string value) - { + { int n = UnsafeNativeMethods.sqlite3_bind_text16(stmt._sqlite_stmt, index, value, value.Length * 2, (IntPtr)(-1)); if (n > 0) throw new SQLiteException(n, SQLiteLastError()); } - internal override DateTime GetDateTime(SQLiteStatement stmt, int index) - { + internal override DateTime GetDateTime(SQLiteStatement stmt, int index) + { return ToDateTime(GetText(stmt, index)); } internal override string ColumnName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string GetText(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index), -1); #endif } internal override string ColumnOriginalName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string ColumnDatabaseName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string ColumnTableName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string GetParamValueText(IntPtr ptr) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16_interop(ptr, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16(ptr), -1); #endif } internal override void ReturnError(IntPtr context, string value) - { + { UnsafeNativeMethods.sqlite3_result_error16(context, value, value.Length * 2); } - internal override void ReturnText(IntPtr context, string value) - { + internal override void ReturnText(IntPtr context, string value) + { UnsafeNativeMethods.sqlite3_result_text16(context, value, value.Length * 2, (IntPtr)(-1)); } } } Index: System.Data.SQLite/SQLiteBase.cs ================================================================== --- System.Data.SQLite/SQLiteBase.cs +++ System.Data.SQLite/SQLiteBase.cs @@ -13,12 +13,12 @@ /// This internal class provides the foundation of SQLite support. It defines all the abstract members needed to implement /// a SQLite data provider, and inherits from SQLiteConvert which allows for simple translations of string to and from SQLite. /// internal abstract class SQLiteBase : SQLiteConvert, IDisposable { - internal SQLiteBase(SQLiteDateFormats fmt) - : base(fmt) { } + internal SQLiteBase(SQLiteDateFormats fmt, DateTimeKind kind) + : base(fmt, kind) { } static internal object _lock = new object(); /// /// Returns a string representing the active version of SQLite @@ -30,10 +30,18 @@ internal abstract long LastInsertRowId { get; } /// /// Returns the number of changes the last executing insert/update caused. /// internal abstract int Changes { get; } + /// + /// Returns the amount of memory (in bytes) currently in use by the SQLite core library. + /// + internal abstract long MemoryUsed { get; } + /// + /// Returns the maximum amount of memory (in bytes) used by the SQLite core library since the high-water mark was last reset. + /// + internal abstract long MemoryHighwater { get; } /// /// Shutdown the SQLite engine so that it can be restarted with different config options. /// We depend on auto initialization to recover. /// internal abstract int Shutdown(); @@ -105,11 +113,13 @@ internal abstract int Reset(SQLiteStatement stmt); internal abstract void Cancel(); internal abstract void Bind_Double(SQLiteStatement stmt, int index, double value); internal abstract void Bind_Int32(SQLiteStatement stmt, int index, Int32 value); + internal abstract void Bind_UInt32(SQLiteStatement stmt, int index, UInt32 value); internal abstract void Bind_Int64(SQLiteStatement stmt, int index, Int64 value); + internal abstract void Bind_UInt64(SQLiteStatement stmt, int index, UInt64 value); internal abstract void Bind_Text(SQLiteStatement stmt, int index, string value); internal abstract void Bind_Blob(SQLiteStatement stmt, int index, byte[] blobData); internal abstract void Bind_DateTime(SQLiteStatement stmt, int index, DateTime dt); internal abstract void Bind_Null(SQLiteStatement stmt, int index); @@ -196,10 +206,11 @@ internal abstract void SetUpdateHook(SQLiteUpdateCallback func); internal abstract void SetCommitHook(SQLiteCommitCallback func); internal abstract void SetTraceCallback(SQLiteTraceCallback func); internal abstract void SetRollbackHook(SQLiteRollbackCallback func); internal abstract int SetLogCallback(SQLiteLogCallback func); + internal abstract bool IsInitialized(); internal abstract int GetCursorForTable(SQLiteStatement stmt, int database, int rootPage); internal abstract long GetRowIdForCursor(SQLiteStatement stmt, int cursor); internal abstract object GetValue(SQLiteStatement stmt, int index, SQLiteType typ); @@ -209,19 +220,64 @@ get; } internal abstract int FileControl(string zDbName, int op, IntPtr pArg); - protected virtual void Dispose(bool bDisposing) - { - } + /////////////////////////////////////////////////////////////////////////////////////////////// + #region IDisposable Members public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteBase).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteBase() + { + Dispose(false); } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// // These statics are here for lack of a better place to put them. // They exist here because they are called during the finalization of // a SQLiteStatementHandle, SQLiteConnectionHandle, and SQLiteFunctionCookieHandle. // Therefore these functions have to be static, and have to be low-level. @@ -310,14 +366,37 @@ ReadWrite = 0x02, Create = 0x04, SharedCache = 0x01000000, Default = 0x06, } + + /// + /// The extra behavioral flags that can be applied to a connection. + /// + [Flags()] + public enum SQLiteConnectionFlags + { + /// + /// No extra flags. + /// + None = 0x0, + + /// + /// Enable logging of all SQL statements to be prepared. + /// + LogPrepare = 0x1, + + /// + /// The default extra flags for new connections. + /// + Default = None + } // These are the options to the internal sqlite3_config call. internal enum SQLiteConfigOpsEnum { + SQLITE_CONFIG_NONE = 0, // nil SQLITE_CONFIG_SINGLETHREAD = 1, // nil SQLITE_CONFIG_MULTITHREAD = 2, // nil SQLITE_CONFIG_SERIALIZED = 3, // nil SQLITE_CONFIG_MALLOC = 4, // sqlite3_mem_methods* SQLITE_CONFIG_GETMALLOC = 5, // sqlite3_mem_methods* Index: System.Data.SQLite/SQLiteCommand.cs ================================================================== --- System.Data.SQLite/SQLiteCommand.cs +++ System.Data.SQLite/SQLiteCommand.cs @@ -141,45 +141,80 @@ if (transaction != null) Transaction = transaction; } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteCommand).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Disposes of the command and clears all member variables /// /// Whether or not the class is being explicitly or implicitly disposed protected override void Dispose(bool disposing) { - base.Dispose(disposing); - - if (disposing) - { - // If a reader is active on this command, don't destroy the command, instead let the reader do it - SQLiteDataReader reader = null; - if (_activeReader != null) - { - try - { - reader = _activeReader.Target as SQLiteDataReader; - } - catch(InvalidOperationException) - { - } - } - - if (reader != null) - { - reader._disposeCommand = true; - _activeReader = null; - return; - } - - Connection = null; - _parameterCollection.Clear(); - _commandText = null; - } - } + try + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + // If a reader is active on this command, don't destroy the command, instead let the reader do it + SQLiteDataReader reader = null; + if (_activeReader != null) + { + try + { + reader = _activeReader.Target as SQLiteDataReader; + } + catch (InvalidOperationException) + { + } + } + + if (reader != null) + { + reader._disposeCommand = true; + _activeReader = null; + return; + } + + Connection = null; + _parameterCollection.Clear(); + _commandText = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Clears and destroys all statements currently prepared /// internal void ClearCommands() @@ -219,32 +254,37 @@ { SQLiteStatement stmt = null; try { - if (_statementList == null) - _remainingText = _commandText; - - stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText); - if (stmt != null) - { - stmt._command = this; - if (_statementList == null) - _statementList = new List(); - - _statementList.Add(stmt); - - _parameterCollection.MapParameters(stmt); - stmt.BindParameters(); - } + if ((_cnn != null) && (_cnn._sql != null)) + { + if (_statementList == null) + _remainingText = _commandText; + + stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText); + + if (stmt != null) + { + stmt._command = this; + + if (_statementList == null) + _statementList = new List(); + + _statementList.Add(stmt); + + _parameterCollection.MapParameters(stmt); + stmt.BindParameters(); + } + } return stmt; } catch (Exception) { if (stmt != null) { - if (_statementList.Contains(stmt)) + if ((_statementList != null) && _statementList.Contains(stmt)) _statementList.Remove(stmt); stmt.Dispose(); } @@ -276,10 +316,12 @@ /// /// Not implemented /// public override void Cancel() { + CheckDisposed(); + if (_activeReader != null) { SQLiteDataReader reader = _activeReader.Target as SQLiteDataReader; if (reader != null) reader.Cancel(); @@ -294,14 +336,17 @@ #endif public override string CommandText { get { + CheckDisposed(); return _commandText; } set { + CheckDisposed(); + if (_commandText == value) return; if (_activeReader != null && _activeReader.IsAlive) { throw new InvalidOperationException("Cannot set CommandText while a DataReader is active"); @@ -322,14 +367,16 @@ #endif public override int CommandTimeout { get { + CheckDisposed(); return _commandTimeout; } set { + CheckDisposed(); _commandTimeout = value; } } /// @@ -340,14 +387,17 @@ #endif public override CommandType CommandType { get { + CheckDisposed(); return CommandType.Text; } set { + CheckDisposed(); + if (value != CommandType.Text) { throw new NotSupportedException(); } } @@ -366,10 +416,11 @@ /// Create a new parameter /// /// public new SQLiteParameter CreateParameter() { + CheckDisposed(); return new SQLiteParameter(); } /// /// The connection associated with this command @@ -377,13 +428,15 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteConnection Connection { - get { return _cnn; } + get { CheckDisposed(); return _cnn; } set { + CheckDisposed(); + if (_activeReader != null && _activeReader.IsAlive) throw new InvalidOperationException("Cannot set Connection while a DataReader is active"); if (_cnn != null) { @@ -421,11 +474,11 @@ #if !PLATFORM_COMPACTFRAMEWORK [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] #endif public new SQLiteParameterCollection Parameters { - get { return _parameterCollection; } + get { CheckDisposed(); return _parameterCollection; } } /// /// Forwards to the local Parameters property /// @@ -444,13 +497,15 @@ #if !PLATFORM_COMPACTFRAMEWORK [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] #endif public new SQLiteTransaction Transaction { - get { return _transaction; } + get { CheckDisposed(); return _transaction; } set { + CheckDisposed(); + if (_cnn != null) { if (_activeReader != null && _activeReader.IsAlive) throw new InvalidOperationException("Cannot set Transaction while a DataReader is active"); @@ -529,10 +584,11 @@ /// /// The flags to be associated with the reader /// A SQLiteDataReader public new SQLiteDataReader ExecuteReader(CommandBehavior behavior) { + CheckDisposed(); InitializeForReader(); SQLiteDataReader rd = new SQLiteDataReader(this, behavior); _activeReader = new WeakReference(rd, false); @@ -543,10 +599,11 @@ /// Overrides the default behavior of DbDataReader to return a specialized SQLiteDataReader class /// /// A SQLiteDataReader public new SQLiteDataReader ExecuteReader() { + CheckDisposed(); return ExecuteReader(CommandBehavior.Default); } /// /// Called by the SQLiteDataReader when the data reader is closed. @@ -560,10 +617,12 @@ /// Execute the command and return the number of rows inserted/updated affected by it. /// /// public override int ExecuteNonQuery() { + CheckDisposed(); + using (SQLiteDataReader reader = ExecuteReader(CommandBehavior.SingleRow | CommandBehavior.SingleResult)) { while (reader.NextResult()) ; return reader.RecordsAffected; } @@ -574,10 +633,12 @@ /// (if present), or null if no resultset was returned. /// /// The first column of the first row of the first resultset from the query public override object ExecuteScalar() { + CheckDisposed(); + using (SQLiteDataReader reader = ExecuteReader(CommandBehavior.SingleRow | CommandBehavior.SingleResult)) { if (reader.Read()) return reader[0]; } @@ -587,10 +648,11 @@ /// /// Does nothing. Commands are prepared as they are executed the first time, and kept in prepared state afterwards. /// public override void Prepare() { + CheckDisposed(); } /// /// Sets the method the SQLiteCommandBuilder uses to determine how to update inserted or updated rows in a DataTable. /// @@ -597,14 +659,16 @@ [DefaultValue(UpdateRowSource.None)] public override UpdateRowSource UpdatedRowSource { get { + CheckDisposed(); return _updateRowSource; } set { + CheckDisposed(); _updateRowSource = value; } } /// @@ -615,14 +679,17 @@ #endif public override bool DesignTimeVisible { get { + CheckDisposed(); return _designTimeVisible; } set { + CheckDisposed(); + _designTimeVisible = value; #if !PLATFORM_COMPACTFRAMEWORK TypeDescriptor.Refresh(this); #endif } @@ -632,9 +699,10 @@ /// Clones a command, including all its parameters /// /// A new SQLiteCommand with the same commandtext, connection and parameters public object Clone() { + CheckDisposed(); return new SQLiteCommand(this); } } } Index: System.Data.SQLite/SQLiteCommandBuilder.cs ================================================================== --- System.Data.SQLite/SQLiteCommandBuilder.cs +++ System.Data.SQLite/SQLiteCommandBuilder.cs @@ -32,11 +32,54 @@ public SQLiteCommandBuilder(SQLiteDataAdapter adp) { QuotePrefix = "["; QuoteSuffix = "]"; DataAdapter = adp; - } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteCommandBuilder).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Minimal amount of parameter processing. Primarily sets the DbType for the parameter equal to the provider type in the schema /// /// The parameter to use in applying custom behaviors to a row @@ -103,69 +146,75 @@ /// /// Gets/sets the DataAdapter for this CommandBuilder /// public new SQLiteDataAdapter DataAdapter - { - get { return (SQLiteDataAdapter)base.DataAdapter; } - set { base.DataAdapter = value; } + { + get { CheckDisposed(); return (SQLiteDataAdapter)base.DataAdapter; } + set { CheckDisposed(); base.DataAdapter = value; } } /// /// Returns the automatically-generated SQLite command to delete rows from the database /// /// - public new SQLiteCommand GetDeleteCommand() - { + public new SQLiteCommand GetDeleteCommand() + { + CheckDisposed(); return (SQLiteCommand)base.GetDeleteCommand(); } /// /// Returns the automatically-generated SQLite command to delete rows from the database /// /// /// public new SQLiteCommand GetDeleteCommand(bool useColumnsForParameterNames) - { + { + CheckDisposed(); return (SQLiteCommand)base.GetDeleteCommand(useColumnsForParameterNames); } /// /// Returns the automatically-generated SQLite command to update rows in the database /// /// - public new SQLiteCommand GetUpdateCommand() - { + public new SQLiteCommand GetUpdateCommand() + { + CheckDisposed(); return (SQLiteCommand)base.GetUpdateCommand(); } /// /// Returns the automatically-generated SQLite command to update rows in the database /// /// /// public new SQLiteCommand GetUpdateCommand(bool useColumnsForParameterNames) - { + { + CheckDisposed(); return (SQLiteCommand)base.GetUpdateCommand(useColumnsForParameterNames); } /// /// Returns the automatically-generated SQLite command to insert rows into the database /// /// public new SQLiteCommand GetInsertCommand() - { + { + CheckDisposed(); return (SQLiteCommand)base.GetInsertCommand(); } /// /// Returns the automatically-generated SQLite command to insert rows into the database /// /// /// public new SQLiteCommand GetInsertCommand(bool useColumnsForParameterNames) - { + { + CheckDisposed(); return (SQLiteCommand)base.GetInsertCommand(useColumnsForParameterNames); } /// /// Overridden to hide its property from the designer @@ -174,15 +223,17 @@ [Browsable(false)] #endif public override CatalogLocation CatalogLocation { get - { + { + CheckDisposed(); return base.CatalogLocation; } set - { + { + CheckDisposed(); base.CatalogLocation = value; } } /// @@ -192,15 +243,17 @@ [Browsable(false)] #endif public override string CatalogSeparator { get - { + { + CheckDisposed(); return base.CatalogSeparator; } set - { + { + CheckDisposed(); base.CatalogSeparator = value; } } /// @@ -211,15 +264,17 @@ #endif [DefaultValue("[")] public override string QuotePrefix { get - { + { + CheckDisposed(); return base.QuotePrefix; } set - { + { + CheckDisposed(); base.QuotePrefix = value; } } /// @@ -229,26 +284,30 @@ [Browsable(false)] #endif public override string QuoteSuffix { get - { + { + CheckDisposed(); return base.QuoteSuffix; } set - { + { + CheckDisposed(); base.QuoteSuffix = value; } } /// /// Places brackets around an identifier /// /// The identifier to quote /// The bracketed identifier - public override string QuoteIdentifier(string unquotedIdentifier) - { + public override string QuoteIdentifier(string unquotedIdentifier) + { + CheckDisposed(); + if (String.IsNullOrEmpty(QuotePrefix) || String.IsNullOrEmpty(QuoteSuffix) || String.IsNullOrEmpty(unquotedIdentifier)) return unquotedIdentifier; @@ -258,12 +317,14 @@ /// /// Removes brackets around an identifier /// /// The quoted (bracketed) identifier /// The undecorated identifier - public override string UnquoteIdentifier(string quotedIdentifier) - { + public override string UnquoteIdentifier(string quotedIdentifier) + { + CheckDisposed(); + if (String.IsNullOrEmpty(QuotePrefix) || String.IsNullOrEmpty(QuoteSuffix) || String.IsNullOrEmpty(quotedIdentifier)) return quotedIdentifier; @@ -281,15 +342,17 @@ [Browsable(false)] #endif public override string SchemaSeparator { get - { + { + CheckDisposed(); return base.SchemaSeparator; } - set - { + set + { + CheckDisposed(); base.SchemaSeparator = value; } } /// Index: System.Data.SQLite/SQLiteConnection.cs ================================================================== --- System.Data.SQLite/SQLiteConnection.cs +++ System.Data.SQLite/SQLiteConnection.cs @@ -50,10 +50,25 @@ /// DateTimeFormat /// Ticks - Use DateTime.Ticks
    ISO8601 - Use ISO8601 DateTime format
    /// N /// ISO8601 /// + /// + /// DateTimeKind + /// Unspecified - Not specified as either UTC or local time.
    Utc - The time represented is UTC.
    Local - The time represented is local time.
    + /// N + /// Unspecified + ///
    + /// + /// BaseSchemaName + /// Some base data classes in the framework (e.g. those that build SQL queries dynamically) + /// assume that an ADO.NET provider cannot support an alternate catalog (i.e. database) without supporting + /// alternate schemas as well; however, SQLite does not fit into this model. Therefore, this value is used + /// as a placeholder and removed prior to preparing any SQL statements that may contain it. + /// N + /// sqlite_default_schema + /// /// /// BinaryGUID /// True - Store GUID columns in binary form
    False - Store GUID columns as text
    /// N /// True @@ -146,14 +161,29 @@ /// Foreign Keys /// Enable foreign key constraints /// N /// False ///
    + /// + /// Flags + /// Extra behavioral flags for the connection. See the SQLiteConnectionFlags enumeration for possible values. + /// N + /// Default + /// /// /// public sealed partial class SQLiteConnection : DbConnection, ICloneable { + /// + /// The default "stub" (i.e. placeholder) base schema name to use when + /// returning column schema information. Used as the initial value of + /// the BaseSchemaName property. This should start with "sqlite_*" + /// because those names are reserved for use by SQLite (i.e. they cannot + /// be confused with the names of user objects). + /// + internal const string DefaultBaseSchemaName = "sqlite_default_schema"; + private const int SQLITE_FCNTL_WIN32_AV_RETRY = 9; private const string _dataDirectory = "|DataDirectory|"; private const string _masterdb = "sqlite_master"; private const string _tempmasterdb = "sqlite_temp_master"; @@ -193,10 +223,22 @@ /// /// Temporary password storage, emptied after the database has been opened /// private byte[] _password; + /// + /// The "stub" (i.e. placeholder) base schema name to use when returning + /// column schema information. + /// + internal string _baseSchemaName; + + /// + /// The extra behavioral flags for this connection, if any. See the + /// SQLiteConnectionFlags enumeration for a list of possible values. + /// + private SQLiteConnectionFlags _flags; + /// /// Default command timeout /// private int _defaultTimeout = 30; @@ -238,10 +280,11 @@ { #if !PLATFORM_COMPACTFRAMEWORK SQLiteLog.Initialize(); #endif + _flags = SQLiteConnectionFlags.Default; _connectionState = ConnectionState.Closed; _connectionString = ""; //_commandList = new List(); if (connectionString != null) @@ -281,10 +324,55 @@ } } } } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteConnection).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + Close(); + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + #if PLATFORM_COMPACTFRAMEWORK /// /// Obsolete /// public override int ConnectionTimeout @@ -304,22 +392,10 @@ public object Clone() { return new SQLiteConnection(this); } - /// - /// Disposes of the SQLiteConnection, closing it if it is active. - /// - /// True if the connection is being explicitly closed. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - Close(); - } - /// /// Creates a database file. This just creates a zero-byte file which SQLite /// will turn into a database when the file is opened properly. /// /// The file to create @@ -455,12 +531,12 @@ } #endif if (_sql != null) { _sql.Close(); + _sql = null; } - _sql = null; _transactionLevel = 0; } OnStateChange(ConnectionState.Closed); } @@ -766,10 +842,13 @@ throw new InvalidOperationException(); Close(); SortedList opts = ParseConnectionString(_connectionString); + + _flags = (SQLiteConnectionFlags)Enum.Parse(typeof(SQLiteConnectionFlags), FindKey(opts, "Flags", "Default"), true); + string fileName; if (Convert.ToInt32(FindKey(opts, "Version", "3"), CultureInfo.InvariantCulture) != 3) throw new NotSupportedException("Only SQLite Version 3 is supported at this time"); @@ -787,11 +866,11 @@ if (String.Compare(fileName, ":MEMORY:", StringComparison.OrdinalIgnoreCase) == 0) fileName = ":memory:"; else { #if PLATFORM_COMPACTFRAMEWORK - if (fileName.StartsWith(".\\")) + if (fileName.StartsWith("./") || fileName.StartsWith(".\\")) fileName = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase) + fileName.Substring(1); #endif fileName = ExpandFileName(fileName); } try @@ -803,10 +882,12 @@ _defaultIsolation = (IsolationLevel)Enum.Parse(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", "Serializable"), true); if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted) throw new NotSupportedException("Invalid Default IsolationLevel specified"); + _baseSchemaName = FindKey(opts, "BaseSchemaName", DefaultBaseSchemaName); + //string temp = FindKey(opts, "DateTimeFormat", "ISO8601"); //if (String.Compare(temp, "ticks", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.Ticks; //else if (String.Compare(temp, "julianday", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.JulianDay; if (_sql == null) @@ -813,15 +894,18 @@ { bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true); SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat", "ISO8601"), true); + + DateTimeKind kind = (DateTimeKind)Enum.Parse(typeof(DateTimeKind), + FindKey(opts, "DateTimeKind", "Unspecified"), true); if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16() - _sql = new SQLite3_UTF16(dateFormat); + _sql = new SQLite3_UTF16(dateFormat, kind); else - _sql = new SQLite3(dateFormat); + _sql = new SQLite3(dateFormat, kind); } SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None; if (SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", Boolean.FalseString)) == false) @@ -947,10 +1031,20 @@ public int DefaultTimeout { get { return _defaultTimeout; } set { _defaultTimeout = value; } } + + /// + /// Gets/sets the extra behavioral flags for this connection. See the + /// SQLiteConnectionFlags enumeration for a list of possible values. + /// + public SQLiteConnectionFlags Flags + { + get { return _flags; } + set { _flags = value; } + } /// /// Returns the version of the underlying SQLite database engine /// #if !PLATFORM_COMPACTFRAMEWORK @@ -1000,18 +1094,62 @@ throw new InvalidOperationException("Database connection not valid for getting number of changes."); return _sql.Changes; } } + + /// + /// Returns the amount of memory (in bytes) currently in use by the SQLite core library. + /// +#if !PLATFORM_COMPACTFRAMEWORK + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] +#endif + public long MemoryUsed + { + get + { + if (_sql == null) + throw new InvalidOperationException("Database connection not valid for getting memory used."); + + return _sql.MemoryUsed; + } + } + + /// + /// Returns the maximum amount of memory (in bytes) used by the SQLite core library since the high-water mark was last reset. + /// +#if !PLATFORM_COMPACTFRAMEWORK + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] +#endif + public long MemoryHighwater + { + get + { + if (_sql == null) + throw new InvalidOperationException("Database connection not valid for getting maximum memory used."); + + return _sql.MemoryHighwater; + } + } /// /// Returns the version of the underlying SQLite database engine /// public static string SQLiteVersion { get { return SQLite3.SQLiteVersion; } } + + /// + /// This method returns the string whose value is the same as the + /// SQLITE_SOURCE_ID C preprocessor macro used when compiling the + /// SQLite core library. + /// + public static string SQLiteSourceId + { + get { return SQLite3.SQLiteSourceId; } + } /// /// Returns the state of the connection. /// #if !PLATFORM_COMPACTFRAMEWORK @@ -1035,15 +1173,18 @@ bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true); SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat", "ISO8601"), true); + + DateTimeKind kind = (DateTimeKind)Enum.Parse(typeof(DateTimeKind), + FindKey(opts, "DateTimeKind", "Unspecified"), true); if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16() - _sql = new SQLite3_UTF16(dateFormat); + _sql = new SQLite3_UTF16(dateFormat, kind); else - _sql = new SQLite3(dateFormat); + _sql = new SQLite3(dateFormat, kind); } if (_sql != null) return _sql.Shutdown(); throw new InvalidOperationException("Database connection not active."); } @@ -2233,10 +2374,11 @@ tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("CONSTRAINT_TYPE", typeof(string)); tbl.Columns.Add("IS_DEFERRABLE", typeof(bool)); tbl.Columns.Add("INITIALLY_DEFERRED", typeof(bool)); + tbl.Columns.Add("FKEY_ID", typeof(int)); tbl.Columns.Add("FKEY_FROM_COLUMN", typeof(string)); tbl.Columns.Add("FKEY_FROM_ORDINAL_POSITION", typeof(int)); tbl.Columns.Add("FKEY_TO_CATALOG", typeof(string)); tbl.Columns.Add("FKEY_TO_SCHEMA", typeof(string)); tbl.Columns.Add("FKEY_TO_TABLE", typeof(string)); @@ -2268,16 +2410,17 @@ { while (rdKey.Read()) { row = tbl.NewRow(); row["CONSTRAINT_CATALOG"] = strCatalog; - row["CONSTRAINT_NAME"] = String.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}", rdTables[2], rdKey.GetInt32(0)); + row["CONSTRAINT_NAME"] = String.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}_{2}", rdTables[2], rdKey.GetInt32(0), rdKey.GetInt32(1)); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = builder.UnquoteIdentifier(rdTables.GetString(2)); row["CONSTRAINT_TYPE"] = "FOREIGN KEY"; row["IS_DEFERRABLE"] = false; row["INITIALLY_DEFERRED"] = false; + row["FKEY_ID"] = rdKey[0]; row["FKEY_FROM_COLUMN"] = builder.UnquoteIdentifier(rdKey[3].ToString()); row["FKEY_TO_CATALOG"] = strCatalog; row["FKEY_TO_TABLE"] = builder.UnquoteIdentifier(rdKey[2].ToString()); row["FKEY_TO_COLUMN"] = builder.UnquoteIdentifier(rdKey[4].ToString()); row["FKEY_FROM_ORDINAL_POSITION"] = rdKey[1]; Index: System.Data.SQLite/SQLiteConnectionStringBuilder.cs ================================================================== --- System.Data.SQLite/SQLiteConnectionStringBuilder.cs +++ System.Data.SQLite/SQLiteConnectionStringBuilder.cs @@ -388,28 +388,91 @@ this["cache size"] = value; } } /// - /// Gets/Sets the datetime format for the connection. - /// + /// Gets/Sets the DateTime format for the connection. + ///
    + [Browsable(true)] + [DefaultValue(SQLiteDateFormats.Default)] + public SQLiteDateFormats DateTimeFormat + { + get + { + object value; + + if (TryGetValue("datetimeformat", out value)) + { + if (value is SQLiteDateFormats) + return (SQLiteDateFormats)value; + else if (value != null) + return (SQLiteDateFormats)TypeDescriptor.GetConverter( + typeof(SQLiteDateFormats)).ConvertFrom(value); + } + + return SQLiteDateFormats.Default; + } + set + { + this["datetimeformat"] = value; + } + } + + /// + /// Gets/Sets the DateTime kind for the connection. + /// + [Browsable(true)] + [DefaultValue(DateTimeKind.Unspecified)] + public DateTimeKind DateTimeKind + { + get + { + object value; + + if (TryGetValue("datetimekind", out value)) + { + if (value is DateTimeKind) + return (DateTimeKind)value; + else if (value != null) + return (DateTimeKind)TypeDescriptor.GetConverter( + typeof(DateTimeKind)).ConvertFrom(value); + } + + return DateTimeKind.Unspecified; + } + set + { + this["datetimekind"] = value; + } + } + + /// + /// Gets/Sets the placeholder base schema name used for + /// .NET Framework compatibility purposes. + /// [Browsable(true)] - [DefaultValue(SQLiteDateFormats.Default)] - public SQLiteDateFormats DateTimeFormat - { - get - { - object value; - TryGetValue("datetimeformat", out value); - if (value is string) - return (SQLiteDateFormats)TypeDescriptor.GetConverter(typeof(SQLiteDateFormats)).ConvertFrom(value); - else return (SQLiteDateFormats)value; - } - set - { - this["datetimeformat"] = value; - } + [DefaultValue(SQLiteConnection.DefaultBaseSchemaName)] + public string BaseSchemaName + { + get + { + object value; + + if (TryGetValue("baseschemaname", out value)) + { + if (value is string) + return (string)value; + else if (value != null) + return value.ToString(); + } + + return null; + } + set + { + this["baseschemaname"] = value; + } } /// /// Determines how SQLite handles the transaction journal file. /// @@ -472,11 +535,39 @@ } set { this["foreign keys"] = value; } - } + } + + /// + /// Gets/Sets the extra behavioral flags. + /// + [Browsable(true)] + [DefaultValue(SQLiteConnectionFlags.Default)] + public SQLiteConnectionFlags Flags + { + get + { + object value; + + if (TryGetValue("flags", out value)) + { + if (value is SQLiteConnectionFlags) + return (SQLiteConnectionFlags)value; + else if (value != null) + return (SQLiteConnectionFlags)TypeDescriptor.GetConverter( + typeof(SQLiteConnectionFlags)).ConvertFrom(value); + } + + return SQLiteConnectionFlags.Default; + } + set + { + this["flags"] = value; + } + } /// /// Helper function for retrieving values from the connectionstring /// /// The keyword to retrieve settings for Index: System.Data.SQLite/SQLiteConvert.cs ================================================================== --- System.Data.SQLite/SQLiteConvert.cs +++ System.Data.SQLite/SQLiteConvert.cs @@ -19,39 +19,58 @@ public abstract class SQLiteConvert { /// /// The value for the Unix epoch (e.g. January 1, 1970 at midnight, in UTC). /// - private static readonly DateTime UnixEpoch = + protected static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + /// + /// The value of the OLE Automation epoch represented as a Julian day. + /// + private static readonly double OleAutomationEpochAsJulianDay = 2415018.5; + /// /// The format string for DateTime values when using the InvariantCulture or CurrentCulture formats. /// private const string FullFormat = "yyyy-MM-ddTHH:mm:ss.fffffffK"; /// /// An array of ISO8601 datetime formats we support conversion from /// private static string[] _datetimeFormats = new string[] { + "THHmmssK", + "THHmmK", + "HH:mm:ss.FFFFFFFK", + "HH:mm:ssK", + "HH:mmK", + "yyyy-MM-dd HH:mm:ss.FFFFFFFK", /* NOTE: UTC default (5). */ + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-dd HH:mmK", + "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", + "yyyy-MM-ddTHH:mmK", + "yyyy-MM-ddTHH:mm:ssK", + "yyyyMMddHHmmssK", + "yyyyMMddHHmmK", + "yyyyMMddTHHmmssFFFFFFFK", "THHmmss", "THHmm", + "HH:mm:ss.FFFFFFF", "HH:mm:ss", "HH:mm", - "HH:mm:ss.FFFFFFF", - "yy-MM-dd", - "yyyy-MM-dd", - "yyyy-MM-dd HH:mm:ss.FFFFFFF", + "yyyy-MM-dd HH:mm:ss.FFFFFFF", /* NOTE: Non-UTC default (19). */ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-dd HH:mm", + "yyyy-MM-dd HH:mm", "yyyy-MM-ddTHH:mm:ss.FFFFFFF", "yyyy-MM-ddTHH:mm", "yyyy-MM-ddTHH:mm:ss", "yyyyMMddHHmmss", "yyyyMMddHHmm", "yyyyMMddTHHmmssFFFFFFF", - "yyyyMMdd" + "yyyy-MM-dd", + "yyyyMMdd", + "yy-MM-dd" }; /// /// An UTF-8 Encoding instance, so we can convert strings to and from UTF-8 /// @@ -59,16 +78,22 @@ /// /// The default DateTime format for this instance /// internal SQLiteDateFormats _datetimeFormat; /// + /// The default DateTimeKind for this instance. + /// + internal DateTimeKind _datetimeKind; + /// /// Initializes the conversion class /// /// The default date/time format to use for this instance - internal SQLiteConvert(SQLiteDateFormats fmt) + /// The DateTimeKind to use. + internal SQLiteConvert(SQLiteDateFormats fmt, DateTimeKind kind) { _datetimeFormat = fmt; + _datetimeKind = kind; } #region UTF-8 Conversion Functions /// /// Converts a string to a UTF-8 encoded byte array sized to include a null-terminating character. @@ -143,61 +168,183 @@ /// /// Converts a string into a DateTime, using the current DateTimeFormat specified for the connection when it was opened. /// /// /// Acceptable ISO8601 DateTime formats are: - /// yyyy-MM-dd HH:mm:ss - /// yyyyMMddHHmmss - /// yyyyMMddTHHmmssfffffff - /// yyyy-MM-dd - /// yy-MM-dd - /// yyyyMMdd - /// HH:mm:ss - /// THHmmss + /// + /// THHmmssK + /// THHmmK + /// HH:mm:ss.FFFFFFFK + /// HH:mm:ssK + /// HH:mmK + /// yyyy-MM-dd HH:mm:ss.FFFFFFFK + /// yyyy-MM-dd HH:mm:ssK + /// yyyy-MM-dd HH:mmK + /// yyyy-MM-ddTHH:mm:ss.FFFFFFFK + /// yyyy-MM-ddTHH:mmK + /// yyyy-MM-ddTHH:mm:ssK + /// yyyyMMddHHmmssK + /// yyyyMMddHHmmK + /// yyyyMMddTHHmmssFFFFFFFK + /// THHmmss + /// THHmm + /// HH:mm:ss.FFFFFFF + /// HH:mm:ss + /// HH:mm + /// yyyy-MM-dd HH:mm:ss.FFFFFFF + /// yyyy-MM-dd HH:mm:ss + /// yyyy-MM-dd HH:mm + /// yyyy-MM-ddTHH:mm:ss.FFFFFFF + /// yyyy-MM-ddTHH:mm + /// yyyy-MM-ddTHH:mm:ss + /// yyyyMMddHHmmss + /// yyyyMMddHHmm + /// yyyyMMddTHHmmssFFFFFFF + /// yyyy-MM-dd + /// yyyyMMdd + /// yy-MM-dd + /// + /// If the string cannot be matched to one of the above formats, an exception will be thrown. /// /// The string containing either a long integer number of 100-nanosecond units since /// System.DateTime.MinValue, a Julian day double, an integer number of seconds since the Unix epoch, a /// culture-independent formatted date and time string, a formatted date and time string in the current /// culture, or an ISO8601-format string. /// A DateTime value public DateTime ToDateTime(string dateText) { - switch (_datetimeFormat) - { - case SQLiteDateFormats.Ticks: - return new DateTime(Convert.ToInt64(dateText, CultureInfo.InvariantCulture)); - case SQLiteDateFormats.JulianDay: - return ToDateTime(Convert.ToDouble(dateText, CultureInfo.InvariantCulture)); - case SQLiteDateFormats.UnixEpoch: - return UnixEpoch.AddSeconds(Convert.ToInt32(dateText, CultureInfo.InvariantCulture)); - case SQLiteDateFormats.InvariantCulture: - return DateTime.Parse(dateText, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None); - case SQLiteDateFormats.CurrentCulture: - return DateTime.Parse(dateText, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None); - default: - return DateTime.ParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None); - } + return ToDateTime(dateText, _datetimeFormat, _datetimeKind); + } + + /// + /// Converts a string into a DateTime, using the specified DateTimeFormat and DateTimeKind. + /// + /// + /// Acceptable ISO8601 DateTime formats are: + /// + /// THHmmssK + /// THHmmK + /// HH:mm:ss.FFFFFFFK + /// HH:mm:ssK + /// HH:mmK + /// yyyy-MM-dd HH:mm:ss.FFFFFFFK + /// yyyy-MM-dd HH:mm:ssK + /// yyyy-MM-dd HH:mmK + /// yyyy-MM-ddTHH:mm:ss.FFFFFFFK + /// yyyy-MM-ddTHH:mmK + /// yyyy-MM-ddTHH:mm:ssK + /// yyyyMMddHHmmssK + /// yyyyMMddHHmmK + /// yyyyMMddTHHmmssFFFFFFFK + /// THHmmss + /// THHmm + /// HH:mm:ss.FFFFFFF + /// HH:mm:ss + /// HH:mm + /// yyyy-MM-dd HH:mm:ss.FFFFFFF + /// yyyy-MM-dd HH:mm:ss + /// yyyy-MM-dd HH:mm + /// yyyy-MM-ddTHH:mm:ss.FFFFFFF + /// yyyy-MM-ddTHH:mm + /// yyyy-MM-ddTHH:mm:ss + /// yyyyMMddHHmmss + /// yyyyMMddHHmm + /// yyyyMMddTHHmmssFFFFFFF + /// yyyy-MM-dd + /// yyyyMMdd + /// yy-MM-dd + /// + /// If the string cannot be matched to one of the above formats, an exception will be thrown. + /// + /// The string containing either a long integer number of 100-nanosecond units since + /// System.DateTime.MinValue, a Julian day double, an integer number of seconds since the Unix epoch, a + /// culture-independent formatted date and time string, a formatted date and time string in the current + /// culture, or an ISO8601-format string. + /// The SQLiteDateFormats to use. + /// The DateTimeKind to use. + /// A DateTime value + public static DateTime ToDateTime(string dateText, SQLiteDateFormats format, DateTimeKind kind) + { + switch (format) + { + case SQLiteDateFormats.Ticks: + { + return new DateTime(Convert.ToInt64( + dateText, CultureInfo.InvariantCulture), kind); + } + case SQLiteDateFormats.JulianDay: + { + return ToDateTime(Convert.ToDouble( + dateText, CultureInfo.InvariantCulture), kind); + } + case SQLiteDateFormats.UnixEpoch: + { + return DateTime.SpecifyKind( + UnixEpoch.AddSeconds(Convert.ToInt32( + dateText, CultureInfo.InvariantCulture)), kind); + } + case SQLiteDateFormats.InvariantCulture: + { + return DateTime.SpecifyKind(DateTime.Parse( + dateText, DateTimeFormatInfo.InvariantInfo, + kind == DateTimeKind.Utc ? + DateTimeStyles.AdjustToUniversal : + DateTimeStyles.None), + kind); + } + case SQLiteDateFormats.CurrentCulture: + { + return DateTime.SpecifyKind(DateTime.Parse( + dateText, DateTimeFormatInfo.CurrentInfo, + kind == DateTimeKind.Utc ? + DateTimeStyles.AdjustToUniversal : + DateTimeStyles.None), + kind); + } + default: + { + return DateTime.SpecifyKind(DateTime.ParseExact( + dateText, _datetimeFormats, + DateTimeFormatInfo.InvariantInfo, + kind == DateTimeKind.Utc ? + DateTimeStyles.AdjustToUniversal : + DateTimeStyles.None), + kind); + } + } } /// /// Converts a julianday value into a DateTime /// /// The value to convert /// A .NET DateTime public DateTime ToDateTime(double julianDay) { - return DateTime.FromOADate(julianDay - 2415018.5); + return ToDateTime(julianDay, _datetimeKind); + } + + /// + /// Converts a julianday value into a DateTime + /// + /// The value to convert + /// The DateTimeKind to use. + /// A .NET DateTime + public static DateTime ToDateTime(double julianDay, DateTimeKind kind) + { + return DateTime.SpecifyKind( + DateTime.FromOADate(julianDay - OleAutomationEpochAsJulianDay), kind); } /// /// Converts a DateTime struct to a JulianDay double /// /// The DateTime to convert /// The JulianDay value the Datetime represents - public double ToJulianDay(DateTime value) + public static double ToJulianDay(DateTime value) { - return value.ToOADate() + 2415018.5; + return value.ToOADate() + OleAutomationEpochAsJulianDay; } /// /// Converts a DateTime to a string value, using the current DateTimeFormat specified for the connection when it was opened. /// @@ -218,11 +365,13 @@ case SQLiteDateFormats.InvariantCulture: return dateValue.ToString(FullFormat, CultureInfo.InvariantCulture); case SQLiteDateFormats.CurrentCulture: return dateValue.ToString(FullFormat, CultureInfo.CurrentCulture); default: - return dateValue.ToString(_datetimeFormats[7], CultureInfo.InvariantCulture); + return (dateValue.Kind == DateTimeKind.Utc) ? + dateValue.ToString(_datetimeFormats[5], CultureInfo.InvariantCulture) : // include "Z" + dateValue.ToString(_datetimeFormats[19], CultureInfo.InvariantCulture); } } /// /// Internal function to convert a UTF-8 encoded IntPtr of the specified length to a DateTime. @@ -651,63 +800,69 @@ /// The .NET DBType the text evaluates to. internal static DbType TypeNameToDbType(string Name) { if (String.IsNullOrEmpty(Name)) return DbType.Object; - if (_typeNames == null) - { - _typeNames = new Dictionary(new TypeNameStringComparer()); - - foreach (SQLiteTypeNames typeName in new SQLiteTypeNames[] { - new SQLiteTypeNames("COUNTER", DbType.Int64), - new SQLiteTypeNames("AUTOINCREMENT", DbType.Int64), - new SQLiteTypeNames("IDENTITY", DbType.Int64), - new SQLiteTypeNames("LONGTEXT", DbType.String), - new SQLiteTypeNames("LONGCHAR", DbType.String), - new SQLiteTypeNames("LONGVARCHAR", DbType.String), - new SQLiteTypeNames("LONG", DbType.Int64), - new SQLiteTypeNames("TINYINT", DbType.Byte), - new SQLiteTypeNames("INTEGER", DbType.Int64), - new SQLiteTypeNames("INT", DbType.Int32), - new SQLiteTypeNames("VARCHAR", DbType.String), - new SQLiteTypeNames("NVARCHAR", DbType.String), - new SQLiteTypeNames("CHAR", DbType.String), - new SQLiteTypeNames("NCHAR", DbType.String), - new SQLiteTypeNames("TEXT", DbType.String), - new SQLiteTypeNames("NTEXT", DbType.String), - new SQLiteTypeNames("STRING", DbType.String), - new SQLiteTypeNames("DOUBLE", DbType.Double), - new SQLiteTypeNames("FLOAT", DbType.Double), - new SQLiteTypeNames("REAL", DbType.Double), - new SQLiteTypeNames("BIT", DbType.Boolean), - new SQLiteTypeNames("YESNO", DbType.Boolean), - new SQLiteTypeNames("LOGICAL", DbType.Boolean), - new SQLiteTypeNames("BOOL", DbType.Boolean), - new SQLiteTypeNames("NUMERIC", DbType.Decimal), - new SQLiteTypeNames("DECIMAL", DbType.Decimal), - new SQLiteTypeNames("MONEY", DbType.Decimal), - new SQLiteTypeNames("CURRENCY", DbType.Decimal), - new SQLiteTypeNames("TIME", DbType.DateTime), - new SQLiteTypeNames("DATE", DbType.DateTime), - new SQLiteTypeNames("DATETIME", DbType.DateTime), - new SQLiteTypeNames("SMALLDATE", DbType.DateTime), - new SQLiteTypeNames("BLOB", DbType.Binary), - new SQLiteTypeNames("BINARY", DbType.Binary), - new SQLiteTypeNames("VARBINARY", DbType.Binary), - new SQLiteTypeNames("IMAGE", DbType.Binary), - new SQLiteTypeNames("GENERAL", DbType.Binary), - new SQLiteTypeNames("OLEOBJECT", DbType.Binary), - new SQLiteTypeNames("GUID", DbType.Guid), - new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid), - new SQLiteTypeNames("MEMO", DbType.String), - new SQLiteTypeNames("NOTE", DbType.String), - new SQLiteTypeNames("SMALLINT", DbType.Int16), - new SQLiteTypeNames("BIGINT", DbType.Int64) - }) + lock (_syncRoot) + { + if (_typeNames == null) + { + _typeNames = new Dictionary( + new TypeNameStringComparer()); + + foreach (SQLiteTypeNames typeName in new SQLiteTypeNames[] { + new SQLiteTypeNames("COUNTER", DbType.Int64), + new SQLiteTypeNames("AUTOINCREMENT", DbType.Int64), + new SQLiteTypeNames("IDENTITY", DbType.Int64), + new SQLiteTypeNames("LONGTEXT", DbType.String), + new SQLiteTypeNames("LONGCHAR", DbType.String), + new SQLiteTypeNames("LONGVARCHAR", DbType.String), + new SQLiteTypeNames("LONG", DbType.Int64), + new SQLiteTypeNames("TINYINT", DbType.Byte), + new SQLiteTypeNames("INTEGER", DbType.Int64), + new SQLiteTypeNames("INT", DbType.Int32), + new SQLiteTypeNames("VARCHAR", DbType.String), + new SQLiteTypeNames("NVARCHAR", DbType.String), + new SQLiteTypeNames("CHAR", DbType.String), + new SQLiteTypeNames("NCHAR", DbType.String), + new SQLiteTypeNames("TEXT", DbType.String), + new SQLiteTypeNames("NTEXT", DbType.String), + new SQLiteTypeNames("STRING", DbType.String), + new SQLiteTypeNames("DOUBLE", DbType.Double), + new SQLiteTypeNames("FLOAT", DbType.Double), + new SQLiteTypeNames("REAL", DbType.Double), + new SQLiteTypeNames("BIT", DbType.Boolean), + new SQLiteTypeNames("YESNO", DbType.Boolean), + new SQLiteTypeNames("LOGICAL", DbType.Boolean), + new SQLiteTypeNames("BOOL", DbType.Boolean), + new SQLiteTypeNames("BOOLEAN", DbType.Boolean), + new SQLiteTypeNames("NUMERIC", DbType.Decimal), + new SQLiteTypeNames("DECIMAL", DbType.Decimal), + new SQLiteTypeNames("MONEY", DbType.Decimal), + new SQLiteTypeNames("CURRENCY", DbType.Decimal), + new SQLiteTypeNames("TIME", DbType.DateTime), + new SQLiteTypeNames("DATE", DbType.DateTime), + new SQLiteTypeNames("DATETIME", DbType.DateTime), + new SQLiteTypeNames("SMALLDATE", DbType.DateTime), + new SQLiteTypeNames("TIMESTAMP", DbType.DateTime), + new SQLiteTypeNames("BLOB", DbType.Binary), + new SQLiteTypeNames("BINARY", DbType.Binary), + new SQLiteTypeNames("VARBINARY", DbType.Binary), + new SQLiteTypeNames("IMAGE", DbType.Binary), + new SQLiteTypeNames("GENERAL", DbType.Binary), + new SQLiteTypeNames("OLEOBJECT", DbType.Binary), + new SQLiteTypeNames("GUID", DbType.Guid), + new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid), + new SQLiteTypeNames("MEMO", DbType.String), + new SQLiteTypeNames("NOTE", DbType.String), + new SQLiteTypeNames("SMALLINT", DbType.Int16), + new SQLiteTypeNames("BIGINT", DbType.Int64) + }) { _typeNames.Add(typeName.typeName, typeName); } + } } SQLiteTypeNames value; if (_typeNames.TryGetValue(Name, out value)) @@ -727,10 +882,11 @@ return DbType.Object; } #endregion + private static object _syncRoot = new object(); private static Dictionary _typeNames = null; } /// /// SQLite has very limited types, and is inherently text-based. The first 5 types below represent the sum of all types SQLite Index: System.Data.SQLite/SQLiteDataAdapter.cs ================================================================== --- System.Data.SQLite/SQLiteDataAdapter.cs +++ System.Data.SQLite/SQLiteDataAdapter.cs @@ -62,17 +62,62 @@ { SQLiteConnection cnn = new SQLiteConnection(connectionString); SelectCommand = new SQLiteCommand(commandText, cnn); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteDataAdapter).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Row updating event handler /// public event EventHandler RowUpdating { add { + CheckDisposed(); + #if !PLATFORM_COMPACTFRAMEWORK EventHandler previous = (EventHandler)base.Events[_updatingEventPH]; if ((previous != null) && (value.Target is DbCommandBuilder)) { EventHandler handler = (EventHandler)FindBuilder(previous); @@ -82,11 +127,11 @@ } } #endif base.Events.AddHandler(_updatingEventPH, value); } - remove { base.Events.RemoveHandler(_updatingEventPH, value); } + remove { CheckDisposed(); base.Events.RemoveHandler(_updatingEventPH, value); } } #if !PLATFORM_COMPACTFRAMEWORK internal static Delegate FindBuilder(MulticastDelegate mcd) { @@ -108,12 +153,12 @@ /// /// Row updated event handler /// public event EventHandler RowUpdated { - add { base.Events.AddHandler(_updatedEventPH, value); } - remove { base.Events.RemoveHandler(_updatedEventPH, value); } + add { CheckDisposed(); base.Events.AddHandler(_updatedEventPH, value); } + remove { CheckDisposed(); base.Events.RemoveHandler(_updatedEventPH, value); } } /// /// Raised by the underlying DbDataAdapter when a row is being updated /// @@ -144,12 +189,12 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand SelectCommand { - get { return (SQLiteCommand)base.SelectCommand; } - set { base.SelectCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.SelectCommand; } + set { CheckDisposed(); base.SelectCommand = value; } } /// /// Gets/sets the insert command for this DataAdapter /// @@ -156,12 +201,12 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand InsertCommand { - get { return (SQLiteCommand)base.InsertCommand; } - set { base.InsertCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.InsertCommand; } + set { CheckDisposed(); base.InsertCommand = value; } } /// /// Gets/sets the update command for this DataAdapter /// @@ -168,12 +213,12 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand UpdateCommand { - get { return (SQLiteCommand)base.UpdateCommand; } - set { base.UpdateCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.UpdateCommand; } + set { CheckDisposed(); base.UpdateCommand = value; } } /// /// Gets/sets the delete command for this DataAdapter /// @@ -180,10 +225,10 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand DeleteCommand { - get { return (SQLiteCommand)base.DeleteCommand; } - set { base.DeleteCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.DeleteCommand; } + set { CheckDisposed(); base.DeleteCommand = value; } } } } Index: System.Data.SQLite/SQLiteDataReader.cs ================================================================== --- System.Data.SQLite/SQLiteDataReader.cs +++ System.Data.SQLite/SQLiteDataReader.cs @@ -5,20 +5,21 @@ * Released to the public domain, use at your own risk! ********************************************************/ namespace System.Data.SQLite { - using System; + using System; + using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Globalization; /// /// SQLite implementation of DbDataReader. /// public sealed class SQLiteDataReader : DbDataReader - { + { /// /// Underlying command this reader is attached to /// private SQLiteCommand _command; /// @@ -62,22 +63,27 @@ /// /// If set, then raise an exception when the object is accessed after being disposed. /// internal bool _throwOnDisposed; - - /// - /// If set, then the object is currently being disposed. - /// - internal bool _disposing; /// /// An array of rowid's for the active statement if CommandBehavior.KeyInfo is specified /// private SQLiteKeyReader _keyInfo; - internal long _version; // Matches the version of the connection + /// + /// Matches the version of the connection. + /// + internal long _version; + + /// + /// The "stub" (i.e. placeholder) base schema name to use when returning + /// column schema information. Matches the base schema name used by the + /// associated connection. + /// + private string _baseSchemaName; /// /// Internal constructor, initializes the datareader and sets up to begin executing statements /// /// The SQLiteCommand this data reader is for @@ -84,46 +90,85 @@ /// The expected behavior of the data reader internal SQLiteDataReader(SQLiteCommand cmd, CommandBehavior behave) { _throwOnDisposed = true; _command = cmd; - _version = _command.Connection._version; + _version = _command.Connection._version; + _baseSchemaName = _command.Connection._baseSchemaName; _commandBehavior = behave; _activeStatementIndex = -1; _rowsAffected = -1; if (_command != null) NextResult(); - } - - internal void Cancel() - { - _version = 0; + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed && _throwOnDisposed) + throw new ObjectDisposedException(typeof(SQLiteDataReader).Name); +#endif } + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Dispose of all resources used by this datareader. /// /// protected override void Dispose(bool disposing) { - // - // NOTE: Fix for ticket [e1b2e0f769], do NOT throw exceptions while we - // are being disposed. - // - _disposing = true; - _throwOnDisposed = false; - - base.Dispose(disposing); - } + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + // + // NOTE: Fix for ticket [e1b2e0f769], do NOT throw exceptions + // while we are being disposed. + // + _throwOnDisposed = false; + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + internal void Cancel() + { + _version = 0; + } /// /// Closes the datareader, potentially closing the connection as well if CommandBehavior.CloseConnection was specified. /// public override void Close() - { + { + CheckDisposed(); + try { if (_command != null) { try @@ -202,22 +247,24 @@ /// /// Enumerator support /// /// Returns a DbEnumerator object. - public override Collections.IEnumerator GetEnumerator() - { + public override Collections.IEnumerator GetEnumerator() + { + CheckDisposed(); return new DbEnumerator(this, ((_commandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)); } /// /// Not implemented. Returns 0 /// public override int Depth { get - { + { + CheckDisposed(); CheckClosed(); return 0; } } @@ -225,12 +272,14 @@ /// Returns the number of columns in the current resultset /// public override int FieldCount { get - { + { + CheckDisposed(); CheckClosed(); + if (_keyInfo == null) return _fieldCount; return _fieldCount + _keyInfo.Count; } @@ -239,12 +288,13 @@ /// /// Returns the number of visible fielsd in the current resultset /// public override int VisibleFieldCount { - get - { + get + { + CheckDisposed(); CheckClosed(); return _fieldCount; } } @@ -260,13 +310,14 @@ /// This function throws an InvalidTypeCast() exception if the requested type doesn't match the column's definition or affinity. /// /// The index of the column to type-check /// The type we want to get out of the column private TypeAffinity VerifyType(int i, DbType typ) - { + { CheckClosed(); CheckValidRow(); + TypeAffinity affinity = GetSQLiteType(i).Affinity; switch (affinity) { case TypeAffinity.Int64: @@ -307,12 +358,14 @@ /// /// Retrieves the column as a boolean value /// /// The index of the column to retrieve /// bool - public override bool GetBoolean(int i) - { + public override bool GetBoolean(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetBoolean(i - VisibleFieldCount); VerifyType(i, DbType.Boolean); return Convert.ToBoolean(GetValue(i), CultureInfo.CurrentCulture); @@ -322,11 +375,13 @@ /// Retrieves the column as a single byte value /// /// The index of the column to retrieve /// byte public override byte GetByte(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetByte(i - VisibleFieldCount); VerifyType(i, DbType.Byte); return Convert.ToByte(_activeStatement._sql.GetInt32(_activeStatement, i)); @@ -343,11 +398,13 @@ /// The actual number of bytes written into the array /// /// To determine the number of bytes in the column, pass a null value for the buffer. The total length will be returned. /// public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetBytes(i - VisibleFieldCount, fieldOffset, buffer, bufferoffset, length); VerifyType(i, DbType.Binary); return _activeStatement._sql.GetBytes(_activeStatement, i, (int)fieldOffset, buffer, bufferoffset, length); @@ -357,11 +414,13 @@ /// Returns the column as a single character /// /// The index of the column to retrieve /// char public override char GetChar(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetChar(i - VisibleFieldCount); VerifyType(i, DbType.SByte); return Convert.ToChar(_activeStatement._sql.GetInt32(_activeStatement, i)); @@ -378,11 +437,13 @@ /// The actual number of characters written into the array /// /// To determine the number of characters in the column, pass a null value for the buffer. The total length will be returned. /// public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetChars(i - VisibleFieldCount, fieldoffset, buffer, bufferoffset, length); VerifyType(i, DbType.String); return _activeStatement._sql.GetChars(_activeStatement, i, (int)fieldoffset, buffer, bufferoffset, length); @@ -392,11 +453,13 @@ /// Retrieves the name of the back-end datatype of the column /// /// The index of the column to retrieve /// string public override string GetDataTypeName(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDataTypeName(i - VisibleFieldCount); SQLiteType typ = GetSQLiteType(i); if (typ.Type == DbType.Object) return SQLiteConvert.SQLiteTypeToType(typ).Name; @@ -406,12 +469,14 @@ /// /// Retrieve the column as a date/time value /// /// The index of the column to retrieve /// DateTime - public override DateTime GetDateTime(int i) - { + public override DateTime GetDateTime(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDateTime(i - VisibleFieldCount); VerifyType(i, DbType.DateTime); return _activeStatement._sql.GetDateTime(_activeStatement, i); @@ -421,11 +486,13 @@ /// Retrieve the column as a decimal value ///
    /// The index of the column to retrieve /// decimal public override decimal GetDecimal(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDecimal(i - VisibleFieldCount); VerifyType(i, DbType.Decimal); return Decimal.Parse(_activeStatement._sql.GetText(_activeStatement, i), NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture); @@ -435,11 +502,13 @@ /// Returns the column as a double ///
    /// The index of the column to retrieve /// double public override double GetDouble(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDouble(i - VisibleFieldCount); VerifyType(i, DbType.Double); return _activeStatement._sql.GetDouble(_activeStatement, i); @@ -449,11 +518,13 @@ /// Returns the .NET type of a given column ///
    /// The index of the column to retrieve /// Type public override Type GetFieldType(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetFieldType(i - VisibleFieldCount); return SQLiteConvert.SQLiteTypeToType(GetSQLiteType(i)); } @@ -462,11 +533,13 @@ /// Returns a column as a float value ///
    /// The index of the column to retrieve /// float public override float GetFloat(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetFloat(i - VisibleFieldCount); VerifyType(i, DbType.Single); return Convert.ToSingle(_activeStatement._sql.GetDouble(_activeStatement, i)); @@ -475,12 +548,14 @@ /// /// Returns the column as a Guid /// /// The index of the column to retrieve /// Guid - public override Guid GetGuid(int i) - { + public override Guid GetGuid(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetGuid(i - VisibleFieldCount); TypeAffinity affinity = VerifyType(i, DbType.Guid); if (affinity == TypeAffinity.Blob) @@ -496,12 +571,14 @@ /// /// Returns the column as a short /// /// The index of the column to retrieve /// Int16 - public override Int16 GetInt16(int i) - { + public override Int16 GetInt16(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetInt16(i - VisibleFieldCount); VerifyType(i, DbType.Int16); return Convert.ToInt16(_activeStatement._sql.GetInt32(_activeStatement, i)); @@ -510,12 +587,14 @@ /// /// Retrieves the column as an int /// /// The index of the column to retrieve /// Int32 - public override Int32 GetInt32(int i) - { + public override Int32 GetInt32(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetInt32(i - VisibleFieldCount); VerifyType(i, DbType.Int32); return _activeStatement._sql.GetInt32(_activeStatement, i); @@ -524,12 +603,14 @@ /// /// Retrieves the column as a long /// /// The index of the column to retrieve /// Int64 - public override Int64 GetInt64(int i) - { + public override Int64 GetInt64(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetInt64(i - VisibleFieldCount); VerifyType(i, DbType.Int64); return _activeStatement._sql.GetInt64(_activeStatement, i); @@ -539,11 +620,13 @@ /// Retrieves the name of the column /// /// The index of the column to retrieve /// string public override string GetName(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetName(i - VisibleFieldCount); return _activeStatement._sql.ColumnName(_activeStatement, i); } @@ -552,12 +635,14 @@ /// Retrieves the i of a column, given its name /// /// The name of the column to retrieve /// The int i of the column public override int GetOrdinal(string name) - { + { + CheckDisposed(); CheckClosed(); + int r = _activeStatement._sql.ColumnIndex(_activeStatement, name); if (r == -1 && _keyInfo != null) { r = _keyInfo.GetOrdinal(name); if (r > -1) r += VisibleFieldCount; @@ -570,17 +655,146 @@ /// Schema information in SQLite is difficult to map into .NET conventions, so a lot of work must be done /// to gather the necessary information so it can be represented in an ADO.NET manner. /// /// Returns a DataTable containing the schema information for the active SELECT statement being processed. public override DataTable GetSchemaTable() - { + { + CheckDisposed(); return GetSchemaTable(true, false); + } + + private class ColumnParent : IEqualityComparer + { + public string DatabaseName; + public string TableName; + public string ColumnName; + + public ColumnParent() + { + // do nothing. + } + + public ColumnParent( + string databaseName, + string tableName, + string columnName + ) + : this() + { + this.DatabaseName = databaseName; + this.TableName = tableName; + this.ColumnName = columnName; + } + + #region IEqualityComparer Members + public bool Equals(ColumnParent x, ColumnParent y) + { + if ((x == null) && (y == null)) + { + return true; + } + else if ((x == null) || (y == null)) + { + return false; + } + else + { + if (!String.Equals(x.DatabaseName, y.DatabaseName, + StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!String.Equals(x.TableName, y.TableName, + StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!String.Equals(x.ColumnName, y.ColumnName, + StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return true; + } + } + + public int GetHashCode(ColumnParent obj) + { + int result = 0; + + if ((obj != null) && (obj.DatabaseName != null)) + result ^= obj.DatabaseName.GetHashCode(); + + if ((obj != null) && (obj.TableName != null)) + result ^= obj.TableName.GetHashCode(); + + if ((obj != null) && (obj.ColumnName != null)) + result ^= obj.ColumnName.GetHashCode(); + + return result; + } + #endregion + } + + private static void GetStatementColumnParents( + SQLiteBase sql, + SQLiteStatement stmt, + int fieldCount, + ref Dictionary> parentToColumns, + ref Dictionary columnToParent + ) + { + if (parentToColumns == null) + parentToColumns = new Dictionary>( + new ColumnParent()); + + if (columnToParent == null) + columnToParent = new Dictionary(); + + for (int n = 0; n < fieldCount; n++) + { + string databaseName = sql.ColumnDatabaseName(stmt, n); + string tableName = sql.ColumnTableName(stmt, n); + string columnName = sql.ColumnOriginalName(stmt, n); + + ColumnParent key = new ColumnParent(databaseName, tableName, null); + ColumnParent value = new ColumnParent(databaseName, tableName, columnName); + + if (!parentToColumns.ContainsKey(key)) + parentToColumns.Add(key, new List(new int[] { n })); + else + parentToColumns[key].Add(n); + + columnToParent.Add(n, value); + } } internal DataTable GetSchemaTable(bool wantUniqueInfo, bool wantDefaultValue) { - CheckClosed(); + CheckClosed(); + + // + // BUGFIX: We need to quickly scan all the fields in the current + // "result set" to see how many distinct tables are actually + // involved. This information is necessary so that some + // intelligent decisions can be made when constructing the + // metadata below. For example, we need to be very careful + // about flagging a particular column as "unique" just + // because it was in its original underlying database table + // if there are now multiple tables involved in the + // "result set". See ticket [7e3fa93744] for more detailed + // information. + // + Dictionary> parentToColumns = null; + Dictionary columnToParent = null; + + GetStatementColumnParents( + _command.Connection._sql, _activeStatement, _fieldCount, + ref parentToColumns, ref columnToParent); DataTable tbl = new DataTable("SchemaTable"); DataTable tblIndexes = null; DataTable tblIndexColumns; DataRow row; @@ -637,22 +851,23 @@ row[SchemaTableOptionalColumn.IsRowVersion] = false; row[SchemaTableColumn.IsUnique] = false; row[SchemaTableColumn.IsKey] = false; row[SchemaTableOptionalColumn.IsAutoIncrement] = false; row[SchemaTableColumn.DataType] = GetFieldType(n); - row[SchemaTableOptionalColumn.IsHidden] = false; - - strColumn = _command.Connection._sql.ColumnOriginalName(_activeStatement, n); + row[SchemaTableOptionalColumn.IsHidden] = false; + row[SchemaTableColumn.BaseSchemaName] = _baseSchemaName; + + strColumn = columnToParent[n].ColumnName; if (String.IsNullOrEmpty(strColumn) == false) row[SchemaTableColumn.BaseColumnName] = strColumn; row[SchemaTableColumn.IsExpression] = String.IsNullOrEmpty(strColumn); - row[SchemaTableColumn.IsAliased] = (String.Compare(GetName(n), strColumn, StringComparison.OrdinalIgnoreCase) != 0); - - temp = _command.Connection._sql.ColumnTableName(_activeStatement, n); - if (String.IsNullOrEmpty(temp) == false) row[SchemaTableColumn.BaseTableName] = temp; - - temp = _command.Connection._sql.ColumnDatabaseName(_activeStatement, n); + row[SchemaTableColumn.IsAliased] = (String.Compare(GetName(n), strColumn, StringComparison.OrdinalIgnoreCase) != 0); + + temp = columnToParent[n].TableName; + if (String.IsNullOrEmpty(temp) == false) row[SchemaTableColumn.BaseTableName] = temp; + + temp = columnToParent[n].DatabaseName; if (String.IsNullOrEmpty(temp) == false) row[SchemaTableOptionalColumn.BaseCatalogName] = temp; string dataType = null; // If we have a table-bound column, extract the extra information from it if (String.IsNullOrEmpty(strColumn) == false) @@ -747,12 +962,18 @@ null }); foreach (DataRow rowColumnIndex in tblIndexColumns.Rows) { if (String.Compare((string)rowColumnIndex["COLUMN_NAME"], strColumn, StringComparison.OrdinalIgnoreCase) == 0) - { - if (tblIndexColumns.Rows.Count == 1 && (bool)row[SchemaTableColumn.AllowDBNull] == false) + { + // + // BUGFIX: Make sure that we only flag this column as "unique" + // if we are not processing of some kind of multi-table + // construct (i.e. a join) because in that case we must + // allow duplicate values (refer to ticket [7e3fa93744]). + // + if (parentToColumns.Count == 1 && tblIndexColumns.Rows.Count == 1 && (bool)row[SchemaTableColumn.AllowDBNull] == false) row[SchemaTableColumn.IsUnique] = rowIndexes["UNIQUE"]; // If its an integer primary key and the only primary key in the table, then its a rowid alias and is autoincrement // NOTE: Currently commented out because this is not always the desired behavior. For example, a 1:1 relationship with // another table, where the other table is autoincrement, but this one is not, and uses the rowid from the other. @@ -795,11 +1016,13 @@ /// Retrieves the column as a string /// /// The index of the column to retrieve /// string public override string GetString(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetString(i - VisibleFieldCount); VerifyType(i, DbType.String); return _activeStatement._sql.GetText(_activeStatement, i); @@ -809,11 +1032,13 @@ /// Retrieves the column as an object corresponding to the underlying datatype of the column /// /// The index of the column to retrieve /// object public override object GetValue(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetValue(i - VisibleFieldCount); SQLiteType typ = GetSQLiteType(i); @@ -823,12 +1048,14 @@ /// /// Retreives the values of multiple columns, up to the size of the supplied array /// /// The array to fill with values from the columns in the current resultset /// The number of columns retrieved - public override int GetValues(object[] values) - { + public override int GetValues(object[] values) + { + CheckDisposed(); + int nMax = FieldCount; if (values.Length < nMax) nMax = values.Length; for (int n = 0; n < nMax; n++) { @@ -841,32 +1068,35 @@ /// /// Returns True if the resultset has rows that can be fetched /// public override bool HasRows { - get - { + get + { + CheckDisposed(); CheckClosed(); return (_readingState != 1); } } /// /// Returns True if the data reader is closed /// public override bool IsClosed - { - get { return (_command == null); } + { + get { CheckDisposed(); return (_command == null); } } /// /// Returns True if the specified column is null /// /// The index of the column to retrieve /// True or False - public override bool IsDBNull(int i) - { + public override bool IsDBNull(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.IsDBNull(i - VisibleFieldCount); return _activeStatement._sql.IsNull(_activeStatement, i); } @@ -874,11 +1104,12 @@ /// /// Moves to the next resultset in multiple row-returning SQL command. /// /// True if the command was successful and a new resultset is available, False otherwise. public override bool NextResult() - { + { + CheckDisposed(); CheckClosed(); SQLiteStatement stmt = null; int fieldCount; @@ -989,11 +1220,12 @@ /// /// Reads the next row from the resultset /// /// True if a new row was successfully loaded and is ready for processing public override bool Read() - { + { + CheckDisposed(); CheckClosed(); if (_readingState == -1) // First step was already done at the NextResult() level, so don't step again, just return true. { _readingState = 0; @@ -1021,32 +1253,32 @@ /// /// Retrieve the count of records affected by an update/insert command. Only valid once the data reader is closed! /// public override int RecordsAffected - { - get { return (_rowsAffected < 0) ? 0 : _rowsAffected; } + { + get { CheckDisposed(); return (_rowsAffected < 0) ? 0 : _rowsAffected; } } /// /// Indexer to retrieve data from a column given its name /// /// The name of the column to retrieve data for /// The value contained in the column public override object this[string name] - { - get { return GetValue(GetOrdinal(name)); } + { + get { CheckDisposed(); return GetValue(GetOrdinal(name)); } } /// /// Indexer to retrieve data from a column given its i /// /// The index of the column to retrieve /// The value contained in the column public override object this[int i] - { - get { return GetValue(i); } + { + get { CheckDisposed(); return GetValue(i); } } private void LoadKeyInfo() { if (_keyInfo != null) Index: System.Data.SQLite/SQLiteEnlistment.cs ================================================================== --- System.Data.SQLite/SQLiteEnlistment.cs +++ System.Data.SQLite/SQLiteEnlistment.cs @@ -8,11 +8,11 @@ #if !PLATFORM_COMPACTFRAMEWORK namespace System.Data.SQLite { using System.Transactions; - internal class SQLiteEnlistment : IEnlistmentNotification + internal class SQLiteEnlistment : IEnlistmentNotification, IDisposable { internal SQLiteTransaction _transaction; internal Transaction _scope; internal bool _disposeConnection; @@ -22,10 +22,77 @@ _scope = scope; _scope.EnlistVolatile(this, System.Transactions.EnlistmentOptions.None); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteEnlistment).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (_transaction != null) + { + _transaction.Dispose(); + _transaction = null; + } + + if (_scope != null) + { + // _scope.Dispose(); // NOTE: Not "owned" by us. + _scope = null; + } + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteEnlistment() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + private void Cleanup(SQLiteConnection cnn) { if (_disposeConnection) cnn.Dispose(); @@ -34,11 +101,13 @@ } #region IEnlistmentNotification Members public void Commit(Enlistment enlistment) - { + { + CheckDisposed(); + SQLiteConnection cnn = _transaction.Connection; cnn._enlistment = null; try { @@ -52,25 +121,30 @@ { Cleanup(cnn); } } - public void InDoubt(Enlistment enlistment) - { + public void InDoubt(Enlistment enlistment) + { + CheckDisposed(); enlistment.Done(); } - public void Prepare(PreparingEnlistment preparingEnlistment) - { + public void Prepare(PreparingEnlistment preparingEnlistment) + { + CheckDisposed(); + if (_transaction.IsValid(false) == false) preparingEnlistment.ForceRollback(); else preparingEnlistment.Prepared(); } public void Rollback(Enlistment enlistment) - { + { + CheckDisposed(); + SQLiteConnection cnn = _transaction.Connection; cnn._enlistment = null; try { Index: System.Data.SQLite/SQLiteFactory.cs ================================================================== --- System.Data.SQLite/SQLiteFactory.cs +++ System.Data.SQLite/SQLiteFactory.cs @@ -12,24 +12,12 @@ #if !PLATFORM_COMPACTFRAMEWORK /// /// SQLite implementation of DbProviderFactory. /// - public sealed partial class SQLiteFactory : DbProviderFactory - { - /// - /// This event is raised whenever SQLite raises a logging event. - /// Note that this should be set as one of the first things in the - /// application. This event is provided for backward compatibility only. - /// New code should use the SQLiteLog class instead. - /// - public event SQLiteLogEventHandler Log - { - add { SQLiteLog.Log += value; } - remove { SQLiteLog.Log -= value; } - } - + public sealed partial class SQLiteFactory : DbProviderFactory, IDisposable + { /// /// Constructs a new SQLiteFactory object /// /// /// Default constructor @@ -39,10 +27,77 @@ // // NOTE: Do nothing here now. All the logging setup related code has // been moved to the new SQLiteLog static class. // } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteFactory).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteFactory() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// This event is raised whenever SQLite raises a logging event. + /// Note that this should be set as one of the first things in the + /// application. This event is provided for backward compatibility only. + /// New code should use the SQLiteLog class instead. + /// + public event SQLiteLogEventHandler Log + { + add { CheckDisposed(); SQLiteLog.Log += value; } + remove { CheckDisposed(); SQLiteLog.Log -= value; } + } /// /// Static instance member which returns an instanced SQLiteFactory class. /// public static readonly SQLiteFactory Instance = new SQLiteFactory(); @@ -51,55 +106,61 @@ /// Returns a new SQLiteCommand object. /// /// A SQLiteCommand object. public override DbCommand CreateCommand() { + CheckDisposed(); return new SQLiteCommand(); } /// /// Returns a new SQLiteCommandBuilder object. /// /// A SQLiteCommandBuilder object. public override DbCommandBuilder CreateCommandBuilder() { + CheckDisposed(); return new SQLiteCommandBuilder(); } /// /// Creates a new SQLiteConnection. /// /// A SQLiteConnection object. public override DbConnection CreateConnection() { + CheckDisposed(); return new SQLiteConnection(); } /// /// Creates a new SQLiteConnectionStringBuilder. /// /// A SQLiteConnectionStringBuilder object. public override DbConnectionStringBuilder CreateConnectionStringBuilder() { + CheckDisposed(); return new SQLiteConnectionStringBuilder(); } /// /// Creates a new SQLiteDataAdapter. /// /// A SQLiteDataAdapter object. public override DbDataAdapter CreateDataAdapter() { + CheckDisposed(); return new SQLiteDataAdapter(); } /// /// Creates a new SQLiteParameter. /// /// A SQLiteParameter object. public override DbParameter CreateParameter() { + CheckDisposed(); return new SQLiteParameter(); } } #endif } Index: System.Data.SQLite/SQLiteFunction.cs ================================================================== --- System.Data.SQLite/SQLiteFunction.cs +++ System.Data.SQLite/SQLiteFunction.cs @@ -79,20 +79,101 @@ /// Internal constructor, initializes the function's internal variables. /// protected SQLiteFunction() { _contextDataList = new Dictionary(); - } - + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + /// + /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if + /// someone closes the connection while a DataReader is open. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteFunction).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Placeholder for a user-defined disposal routine + /// + /// True if the object is being disposed explicitly + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + IDisposable disp; + + foreach (KeyValuePair kv in _contextDataList) + { + disp = kv.Value._data as IDisposable; + if (disp != null) + disp.Dispose(); + } + _contextDataList.Clear(); + + _InvokeFunc = null; + _StepFunc = null; + _FinalFunc = null; + _CompareFunc = null; + _base = null; + _contextDataList = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteFunction() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Returns a reference to the underlying connection's SQLiteConvert class, which can be used to convert /// strings and DateTime's into the current connection's encoding schema. /// public SQLiteConvert SQLiteConvert { get - { + { + CheckDisposed(); return _base; } } /// @@ -105,12 +186,13 @@ /// /// The arguments for the command to process /// You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error, /// just return it! - public virtual object Invoke(object[] args) - { + public virtual object Invoke(object[] args) + { + CheckDisposed(); return null; } /// /// Aggregate functions override this method to do their magic. @@ -119,12 +201,13 @@ /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible. /// /// The arguments for the command to process /// The 1-based step number. This is incrememted each time the step method is called. /// A placeholder for implementers to store contextual data pertaining to the current context. - public virtual void Step(object[] args, int stepNumber, ref object contextData) - { + public virtual void Step(object[] args, int stepNumber, ref object contextData) + { + CheckDisposed(); } /// /// Aggregate functions override this method to finish their aggregate processing. /// @@ -140,22 +223,24 @@ /// You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error, /// just return it! /// public virtual object Final(object contextData) - { + { + CheckDisposed(); return null; } /// /// User-defined collation sequences override this method to provide a custom string sorting algorithm. /// /// The first string to compare /// The second strnig to compare /// 1 if param1 is greater than param2, 0 if they are equal, or -1 if param1 is less than param2 - public virtual int Compare(string param1, string param2) - { + public virtual int Compare(string param1, string param2) + { + CheckDisposed(); return 0; } /// /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to. @@ -216,11 +301,11 @@ /// /// Takes the return value from Invoke() and Final() and figures out how to return it to SQLite's context. /// /// The context the return value applies to /// The parameter to return to SQLite - void SetReturnValue(IntPtr context, object returnValue) + private void SetReturnValue(IntPtr context, object returnValue) { if (returnValue == null || returnValue == DBNull.Value) { _base.ReturnNull(context); return; @@ -349,47 +434,10 @@ IDisposable disp = obj as IDisposable; if (disp != null) disp.Dispose(); } - /// - /// Placeholder for a user-defined disposal routine - /// - /// True if the object is being disposed explicitly - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - IDisposable disp; - - foreach (KeyValuePair kv in _contextDataList) - { - disp = kv.Value._data as IDisposable; - if (disp != null) - disp.Dispose(); - } - _contextDataList.Clear(); - - _InvokeFunc = null; - _StepFunc = null; - _FinalFunc = null; - _CompareFunc = null; - _base = null; - _contextDataList = null; - } - } - - /// - /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if - /// someone closes the connection while a DataReader is open. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - /// /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that /// have a SQLiteFunctionAttribute attribute, and registering them accordingly. /// #if !PLATFORM_COMPACTFRAMEWORK @@ -537,11 +585,52 @@ /// /// protected CollationSequence GetCollationSequence() { return _base.GetCollationSequence(this, _context); - } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteFunctionEx).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion } /// /// The type of user-defined function to declare /// Index: System.Data.SQLite/SQLiteKeyReader.cs ================================================================== --- System.Data.SQLite/SQLiteKeyReader.cs +++ System.Data.SQLite/SQLiteKeyReader.cs @@ -40,58 +40,108 @@ internal int column; } /// /// A single sub-query for a given table/database. - /// - private sealed class KeyQuery : IDisposable - { - private SQLiteCommand _command; - internal SQLiteDataReader _reader; - - internal KeyQuery(SQLiteConnection cnn, string database, string table, params string[] columns) - { - using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder()) - { - _command = cnn.CreateCommand(); - for (int n = 0; n < columns.Length; n++) - { - columns[n] = builder.QuoteIdentifier(columns[n]); - } - } - _command.CommandText = String.Format(CultureInfo.InvariantCulture, "SELECT {0} FROM [{1}].[{2}] WHERE ROWID = ?", String.Join(",", columns), database, table); - _command.Parameters.AddWithValue(null, (long)0); - } - - internal bool IsValid - { - set - { - if (value != false) throw new ArgumentException(); - if (_reader != null) - { - _reader.Dispose(); - _reader = null; - } - } - } - - internal void Sync(long rowid) - { - IsValid = false; - _command.Parameters[0].Value = rowid; - _reader = _command.ExecuteReader(); - _reader.Read(); - } - - public void Dispose() - { - IsValid = false; - - if (_command != null) _command.Dispose(); - _command = null; - } + /// + private sealed class KeyQuery : IDisposable + { + private SQLiteCommand _command; + internal SQLiteDataReader _reader; + + internal KeyQuery(SQLiteConnection cnn, string database, string table, params string[] columns) + { + using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder()) + { + _command = cnn.CreateCommand(); + for (int n = 0; n < columns.Length; n++) + { + columns[n] = builder.QuoteIdentifier(columns[n]); + } + } + _command.CommandText = String.Format(CultureInfo.InvariantCulture, "SELECT {0} FROM [{1}].[{2}] WHERE ROWID = ?", String.Join(",", columns), database, table); + _command.Parameters.AddWithValue(null, (long)0); + } + + internal bool IsValid + { + set + { + if (value != false) throw new ArgumentException(); + if (_reader != null) + { + _reader.Dispose(); + _reader = null; + } + } + } + + internal void Sync(long rowid) + { + IsValid = false; + _command.Parameters[0].Value = rowid; + _reader = _command.ExecuteReader(); + _reader.Read(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(KeyQuery).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + IsValid = false; + + if (_command != null) _command.Dispose(); + _command = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~KeyQuery() + { + Dispose(false); + } + #endregion } /// /// This function does all the nasty work at determining what keys need to be returned for /// a given statement. @@ -257,10 +307,77 @@ // CommandBehavior.KeyInfo _keyInfo = new KeyInfo[keys.Count]; keys.CopyTo(_keyInfo); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteKeyReader).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + _stmt = null; + + if (_keyInfo == null) return; + + for (int n = 0; n < _keyInfo.Length; n++) + { + if (_keyInfo[n].query != null) + _keyInfo[n].query.Dispose(); + } + + _keyInfo = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteKeyReader() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// How many additional columns of keyinfo we're holding /// internal int Count { @@ -312,24 +429,10 @@ if (_keyInfo[n].query != null) _keyInfo[n].query.IsValid = false; } } - public void Dispose() - { - _stmt = null; - - if (_keyInfo == null) return; - - for (int n = 0; n < _keyInfo.Length; n++) - { - if (_keyInfo[n].query != null) - _keyInfo[n].query.Dispose(); - } - _keyInfo = null; - } - internal string GetDataTypeName(int i) { Sync(); if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetDataTypeName(_keyInfo[i].column); else return "integer"; Index: System.Data.SQLite/SQLiteLog.cs ================================================================== --- System.Data.SQLite/SQLiteLog.cs +++ System.Data.SQLite/SQLiteLog.cs @@ -109,10 +109,18 @@ /// /// Initializes the SQLite logging facilities. /// public static void Initialize() { + // + // BUFXIX: We cannot initialize the logging interface if the SQLite + // core library has already been initialized anywhere in + // the process (see ticket [2ce0870fad]). + // + if (SQLite3.StaticIsInitialized()) + return; + // // BUGFIX: To avoid nasty situations where multiple AppDomains are // attempting to initialize and/or shutdown what is really // a shared native resource (i.e. the SQLite core library // is loaded per-process and has only one logging callback, @@ -146,11 +154,12 @@ // // NOTE: Create an instance of the SQLite wrapper class. // if (_sql == null) - _sql = new SQLite3(SQLiteDateFormats.Default); + _sql = new SQLite3(SQLiteDateFormats.Default, + DateTimeKind.Unspecified); // // NOTE: Create a single "global" (i.e. per-process) callback // to register with SQLite. This callback will pass the // event on to any registered handler. We only want to @@ -189,10 +198,21 @@ EventArgs e ) { lock (syncRoot) { + // + // NOTE: Remove the default log event handler. + // + RemoveDefaultHandler(); + + // + // NOTE: Disable logging. If necessary, it can be re-enabled + // later by the Initialize method. + // + _enabled = false; + // // BUGBUG: This will cause serious problems if other AppDomains // have any open SQLite connections; however, there is // currently no way around this limitation. // @@ -208,10 +228,22 @@ if (rc != 0) throw new SQLiteException(rc, "Failed to shutdown logging."); } + + // + // BUGFIX: Make sure to reset the callback for next time. This + // must be done after it has been succesfully removed + // as logging callback by the SQLite core library as we + // cannot allow native code to refer to a delegate that + // has been garbage collected. + // + if (_callback != null) + { + _callback = null; + } // // NOTE: Remove the event handler for the DomainUnload event // that we added earlier. // @@ -259,10 +291,35 @@ public static bool Enabled { get { lock (syncRoot) { return _enabled; } } set { lock (syncRoot) { _enabled = value; } } } + + /// + /// Log a message to all the registered log event handlers without going + /// through the SQLite library. + /// + /// The error code or zero for success. + /// The message to be logged. + public static void LogMessage( + int errorCode, + string message + ) + { + bool enabled; + SQLiteLogEventHandler handlers; + + lock (syncRoot) + { + enabled = _enabled; + handlers = _handlers; + } + + if (enabled && (handlers != null)) + handlers(null, new LogEventArgs( + IntPtr.Zero, errorCode, message, null)); + } /// /// Creates and initializes the default log event handler. /// private static void InitializeDefaultHandler() @@ -331,15 +388,25 @@ return; string message = e.Message; if (message == null) + { message = ""; - else if (message.Length == 0) - message = ""; + } + else + { + message = message.Trim(); + + if (message.Length == 0) + message = ""; + } + + int errorCode = e.ErrorCode; - Trace.WriteLine(String.Format("SQLite error ({0}): {1}", - e.ErrorCode, message)); + Trace.WriteLine(String.Format( + "SQLite {0} ({1}): {2}", errorCode == 0 ? + "message" : "error", errorCode, message)); } } #endif } Index: System.Data.SQLite/SQLiteStatement.cs ================================================================== --- System.Data.SQLite/SQLiteStatement.cs +++ System.Data.SQLite/SQLiteStatement.cs @@ -5,11 +5,11 @@ * Released to the public domain, use at your own risk! ********************************************************/ namespace System.Data.SQLite { - using System; + using System; using System.Globalization; /// /// Represents a single SQL statement in SQLite. /// @@ -86,10 +86,79 @@ _paramValues[x] = null; } } } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + /// + /// Disposes and finalizes the statement + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteStatement).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (_sqlite_stmt != null) + { + _sqlite_stmt.Dispose(); + _sqlite_stmt = null; + } + + _paramNames = null; + _paramValues = null; + _sql = null; + _sqlStatement = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteStatement() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Called by SQLiteParameterCollection, this function determines if the specified parameter name belongs to /// this statement, and if so, keeps a reference to the parameter so it can be bound later. /// /// The parameter name to map @@ -115,29 +184,10 @@ } } return false; } - #region IDisposable Members - /// - /// Disposes and finalizes the statement - /// - public void Dispose() - { - if (_sqlite_stmt != null) - { - _sqlite_stmt.Dispose(); - } - _sqlite_stmt = null; - - _paramNames = null; - _paramValues = null; - _sql = null; - _sqlStatement = null; - } - #endregion - /// /// Bind all parameters, making sure the caller didn't miss any /// internal void BindParameters() { @@ -147,10 +197,65 @@ for (int n = 0; n < x; n++) { BindParameter(n + 1, _paramValues[n]); } } + + /// + /// Attempts to convert an arbitrary object to the Boolean data type. + /// Null object values are converted to false. Throws a SQLiteException + /// upon failure. + /// + /// The object value to convert. + /// The format provider to use. + /// The converted boolean value. + private static bool ToBoolean(object obj, IFormatProvider provider) + { + if (obj == null) + return false; + + TypeCode typeCode = Type.GetTypeCode(obj.GetType()); + + switch (typeCode) + { + case TypeCode.Empty: + case TypeCode.DBNull: + return false; + case TypeCode.Boolean: + return (bool)obj; + case TypeCode.Char: + return ((char)obj) != (char)0 ? true : false; + case TypeCode.SByte: + return ((sbyte)obj) != (sbyte)0 ? true : false; + case TypeCode.Byte: + return ((byte)obj) != (byte)0 ? true : false; + case TypeCode.Int16: + return ((short)obj) != (short)0 ? true : false; + case TypeCode.UInt16: + return ((ushort)obj) != (ushort)0 ? true : false; + case TypeCode.Int32: + return ((int)obj) != (int)0 ? true : false; + case TypeCode.UInt32: + return ((uint)obj) != (uint)0 ? true : false; + case TypeCode.Int64: + return ((long)obj) != (long)0 ? true : false; + case TypeCode.UInt64: + return ((ulong)obj) != (ulong)0 ? true : false; + case TypeCode.Single: + return ((float)obj) != (float)0.0 ? true : false; + case TypeCode.Double: + return ((double)obj) != (double)0.0 ? true : false; + case TypeCode.Decimal: + return ((decimal)obj) != Decimal.Zero ? true : false; + case TypeCode.String: + return Convert.ToBoolean(obj, provider); + default: + throw new SQLiteException((int)SQLiteErrorCode.Error, + String.Format("Cannot convert type {0} to boolean", + typeCode)); + } + } /// /// Perform the bind operation for an individual parameter /// /// The index of the parameter to bind @@ -177,27 +282,41 @@ case DbType.Date: case DbType.Time: case DbType.DateTime: // // NOTE: The old method (commented below) does not honor the selected date format - // for the connection. - // _sql.Bind_DateTime(this, index, Convert.ToDateTime(obj, CultureInfo.CurrentCulture)); + // for the connection. + // _sql.Bind_DateTime(this, index, Convert.ToDateTime(obj, CultureInfo.CurrentCulture)); _sql.Bind_DateTime(this, index, (obj is string) ? _sql.ToDateTime((string)obj) : Convert.ToDateTime(obj, CultureInfo.CurrentCulture)); break; + case DbType.Boolean: + _sql.Bind_Int32(this, index, ToBoolean(obj, CultureInfo.CurrentCulture) ? 1 : 0); + break; + case DbType.SByte: + _sql.Bind_Int32(this, index, Convert.ToSByte(obj, CultureInfo.CurrentCulture)); + break; + case DbType.Int16: + _sql.Bind_Int32(this, index, Convert.ToInt16(obj, CultureInfo.CurrentCulture)); + break; + case DbType.Int32: + _sql.Bind_Int32(this, index, Convert.ToInt32(obj, CultureInfo.CurrentCulture)); + break; case DbType.Int64: - case DbType.UInt64: _sql.Bind_Int64(this, index, Convert.ToInt64(obj, CultureInfo.CurrentCulture)); break; - case DbType.Boolean: - case DbType.Int16: - case DbType.Int32: + case DbType.Byte: + _sql.Bind_UInt32(this, index, Convert.ToByte(obj, CultureInfo.CurrentCulture)); + break; case DbType.UInt16: + _sql.Bind_UInt32(this, index, Convert.ToUInt16(obj, CultureInfo.CurrentCulture)); + break; case DbType.UInt32: - case DbType.SByte: - case DbType.Byte: - _sql.Bind_Int32(this, index, Convert.ToInt32(obj, CultureInfo.CurrentCulture)); + _sql.Bind_UInt32(this, index, Convert.ToUInt32(obj, CultureInfo.CurrentCulture)); + break; + case DbType.UInt64: + _sql.Bind_UInt64(this, index, Convert.ToUInt64(obj, CultureInfo.CurrentCulture)); break; case DbType.Single: case DbType.Double: case DbType.Currency: //case DbType.Decimal: // Dont store decimal as double ... loses precision Index: System.Data.SQLite/SQLiteTransaction.cs ================================================================== --- System.Data.SQLite/SQLiteTransaction.cs +++ System.Data.SQLite/SQLiteTransaction.cs @@ -55,17 +55,69 @@ _cnn._transactionLevel--; _cnn = null; throw; } } - } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteTransaction).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Disposes the transaction. If it is currently active, any changes are rolled back. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (IsValid(false)) + { + IssueRollback(); + } + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Commits the current transaction. /// public override void Commit() - { + { + CheckDisposed(); IsValid(true); if (_cnn._transactionLevel - 1 == 0) { using (SQLiteCommand cmd = _cnn.CreateCommand()) @@ -80,50 +132,36 @@ /// /// Returns the underlying connection to which this transaction applies. /// public new SQLiteConnection Connection - { - get { return _cnn; } + { + get { CheckDisposed(); return _cnn; } } /// /// Forwards to the local Connection property /// protected override DbConnection DbConnection - { + { get { return Connection; } } - /// - /// Disposes the transaction. If it is currently active, any changes are rolled back. - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (IsValid(false)) - { - IssueRollback(); - } - } - base.Dispose(disposing); - } - /// /// Gets the isolation level of the transaction. SQLite only supports Serializable transactions. /// public override IsolationLevel IsolationLevel - { - get { return _level; } + { + get { CheckDisposed(); return _level; } } /// /// Rolls back the active transaction. /// public override void Rollback() - { + { + CheckDisposed(); IsValid(true); IssueRollback(); } internal void IssueRollback() Index: System.Data.SQLite/SR.Designer.cs ================================================================== --- System.Data.SQLite/SR.Designer.cs +++ System.Data.SQLite/SR.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // Index: System.Data.SQLite/System.Data.SQLite.2010.csproj ================================================================== --- System.Data.SQLite/System.Data.SQLite.2010.csproj +++ System.Data.SQLite/System.Data.SQLite.2010.csproj @@ -17,10 +17,11 @@ Library Properties System.Data.SQLite System.Data.SQLite 3.5 + Client $(MSBuildProjectDirectory)\.. 2010 @@ -49,6 +50,6 @@ --> - + Index: System.Data.SQLite/System.Data.SQLite.Module.2010.csproj ================================================================== --- System.Data.SQLite/System.Data.SQLite.Module.2010.csproj +++ System.Data.SQLite/System.Data.SQLite.Module.2010.csproj @@ -17,10 +17,11 @@ Module Properties System.Data.SQLite System.Data.SQLite 3.5 + Client false $(MSBuildProjectDirectory)\.. 2010 Module false Index: System.Data.SQLite/System.Data.SQLite.Properties.targets ================================================================== --- System.Data.SQLite/System.Data.SQLite.Properties.targets +++ System.Data.SQLite/System.Data.SQLite.Properties.targets @@ -48,6 +48,13 @@ flag from the AssemblyNameFlags enumeration? --> $(DefineConstants);RETARGETABLE + + + + $(DefineConstants);THROW_ON_DISPOSED + Index: System.Data.SQLite/UnsafeNativeMethods.cs ================================================================== --- System.Data.SQLite/UnsafeNativeMethods.cs +++ System.Data.SQLite/UnsafeNativeMethods.cs @@ -6,10 +6,13 @@ ********************************************************/ namespace System.Data.SQLite { using System; +#if DEBUG + using System.Diagnostics; +#endif #if !PLATFORM_COMPACTFRAMEWORK && !DEBUG using System.Security; #endif @@ -25,11 +28,11 @@ #if !USE_INTEROP_DLL #if !PLATFORM_COMPACTFRAMEWORK private const string SQLITE_DLL = "System.Data.SQLite.dll"; #else - internal const string SQLITE_DLL = "SQLite.Interop.076.dll"; + internal const string SQLITE_DLL = "SQLite.Interop.078.dll"; #endif // PLATFORM_COMPACTFRAMEWORK #else private const string SQLITE_DLL = "SQLite.Interop.dll"; #endif // USE_INTEROP_DLL @@ -352,10 +355,17 @@ internal static extern IntPtr sqlite3_libversion(); #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else + [DllImport(SQLITE_DLL)] +#endif + internal static extern IntPtr sqlite3_sourceid(); + +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else [DllImport(SQLITE_DLL)] #endif internal static extern void sqlite3_interrupt(IntPtr db); #if !PLATFORM_COMPACTFRAMEWORK @@ -370,10 +380,24 @@ #else [DllImport(SQLITE_DLL)] #endif internal static extern int sqlite3_changes(IntPtr db); +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport(SQLITE_DLL)] +#endif + internal static extern long sqlite3_memory_used(); + +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport(SQLITE_DLL)] +#endif + internal static extern long sqlite3_memory_highwater(int resetFlag); + #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] #endif @@ -406,17 +430,40 @@ #else [DllImport(SQLITE_DLL)] #endif internal static extern int sqlite3_bind_int(IntPtr stmt, int index, int value); + // + // NOTE: This really just calls "sqlite3_bind_int"; however, it has the + // correct type signature for an unsigned (32-bit) integer. + // +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int", CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int")] +#endif + internal static extern int sqlite3_bind_uint(IntPtr stmt, int index, uint value); + #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_bind_int64(IntPtr stmt, int index, long value); #else [DllImport(SQLITE_DLL)] internal static extern int sqlite3_bind_int64_interop(IntPtr stmt, int index, ref long value); #endif + + // + // NOTE: This really just calls "sqlite3_bind_int64"; however, it has the + // correct type signature for an unsigned long (64-bit) integer. + // +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int64", CallingConvention = CallingConvention.Cdecl)] + internal static extern int sqlite3_bind_uint64(IntPtr stmt, int index, ulong value); +#else + [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int64_interop")] + internal static extern int sqlite3_bind_uint64_interop(IntPtr stmt, int index, ref ulong value); +#endif #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] @@ -858,15 +905,49 @@ protected override bool ReleaseHandle() { try { SQLiteBase.CloseConnection(this); + +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "CloseConnection: {0}", handle)); + } + catch + { + } +#endif + +#if DEBUG + return true; +#endif } +#if DEBUG + catch (SQLiteException e) +#else catch (SQLiteException) +#endif { +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "CloseConnection: {0}, exception: {1}", + handle, e)); + } + catch + { + } +#endif } +#if DEBUG + return false; +#else return true; +#endif } public override bool IsInvalid { get { return (handle == IntPtr.Zero); } @@ -900,18 +981,52 @@ protected override bool ReleaseHandle() { try { SQLiteBase.FinalizeStatement(this); + +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "FinalizeStatement: {0}", handle)); + } + catch + { + } +#endif + +#if DEBUG + return true; +#endif } +#if DEBUG + catch (SQLiteException e) +#else catch (SQLiteException) +#endif { +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "FinalizeStatement: {0}, exception: {1}", + handle, e)); + } + catch + { + } +#endif } +#if DEBUG + return false; +#else return true; +#endif } public override bool IsInvalid { get { return (handle == IntPtr.Zero); } } } } ADDED Tests/Installer_Test_Vs2008.log Index: Tests/Installer_Test_Vs2008.log ================================================================== --- /dev/null +++ Tests/Installer_Test_Vs2008.log @@ -0,0 +1,64 @@ +Installer.exe: #1: Configuration.Process: No actual changes will be made to this system because "what-if" mode is enabled. +Installer.exe: #2: Installer.Main: GacInstall: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.dll]]" +Installer.exe: #3: Installer.Main: GacInstall: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.Linq.dll]]" +Installer.exe: #4: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727", writable = False +Installer.exe: #5: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #6: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #7: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx", writable = True +Installer.exe: #8: RegistryHelper.DeleteSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx", subKeyName = "SQLite" +Installer.exe: #9: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx", writable = True +Installer.exe: #10: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx", subKeyName = "System.Data.SQLite" +Installer.exe: #11: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx\System.Data.SQLite", name = , value = "[file nativename [getBuildDirectory]]" +Installer.exe: #12: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727", writable = False +Installer.exe: #13: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #14: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #15: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #16: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #17: Installer.RemoveDbProviderFactory: element = +Installer.exe: #18: Installer.AddDbProviderFactory: element = +Installer.exe: #19: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #20: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", name = "InstallDir", defaultValue = +Installer.exe: #21: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #22: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "Packages", writable = True +Installer.exe: #23: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #24: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = , value = "System.Data.SQLite Designer Package" +Installer.exe: #25: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "Class", value = "SQLite.Designer.SQLitePackage" +Installer.exe: #26: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "CodeBase", value = "[file nativename [file join [getBuildDirectory] SQLite.Designer.dll]]" +Installer.exe: #27: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "ID", value = 400 +Installer.exe: #28: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "InprocServer32", value = "[file nativename [file join $::env(windir) system32 mscoree.dll]]" +Installer.exe: #29: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "CompanyName", value = "http://system.data.sqlite.org/" +Installer.exe: #30: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "MinEdition", value = "standard" +Installer.exe: #31: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "ProductName", value = "System.Data.SQLite Designer Package" +Installer.exe: #32: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "ProductVersion", value = "1.0" +Installer.exe: #33: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", subKeyName = "Toolbox" +Installer.exe: #34: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\Toolbox", name = "Default Items", value = 3 +Installer.exe: #35: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "Menus", writable = True +Installer.exe: #36: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Menus", name = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", value = ", 1000, 3" +Installer.exe: #37: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "Services", writable = True +Installer.exe: #38: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Services", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}" +Installer.exe: #39: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Services\{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}", name = , value = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #40: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Services\{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}", name = "Name", value = "System.Data.SQLite Designer Service" +Installer.exe: #41: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #42: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", name = "InstallDir", defaultValue = +Installer.exe: #43: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #44: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "DataSources", writable = True +Installer.exe: #45: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataSources", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}" +Installer.exe: #46: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataSources\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}", name = , value = "System.Data.SQLite Database File" +Installer.exe: #47: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataSources\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}", subKeyName = "SupportingProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}" +Installer.exe: #48: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #49: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", name = "InstallDir", defaultValue = +Installer.exe: #50: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #51: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "DataProviders", writable = True +Installer.exe: #52: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}" +Installer.exe: #53: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = , value = ".NET Framework Data Provider for SQLite" +Installer.exe: #54: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "InvariantName", value = "System.Data.SQLite" +Installer.exe: #55: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "Technology", value = "{77ab9a9d-78b9-4ba7-91ac-873f5338f1d2}" +Installer.exe: #56: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "CodeBase", value = "[file nativename [file join [getBuildDirectory] SQLite.Designer.dll]]" +Installer.exe: #57: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "FactoryService", value = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}" +Installer.exe: #58: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataConnectionUIControl" +Installer.exe: #59: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataConnectionProperties" +Installer.exe: #60: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataConnectionSupport" +Installer.exe: #61: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataObjectSupport" +Installer.exe: #62: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataViewSupport" +Installer.exe: #63: Installer.Main: subKeysCreated = 12, subKeysDeleted = 1, keyValuesSet = 20, keyValuesDeleted = 0 +Installer.exe: #64: Installer.Main: Success. ADDED Tests/Installer_Test_Vs2010.log Index: Tests/Installer_Test_Vs2010.log ================================================================== --- /dev/null +++ Tests/Installer_Test_Vs2010.log @@ -0,0 +1,64 @@ +Installer.exe: #1: Configuration.Process: No actual changes will be made to this system because "what-if" mode is enabled. +Installer.exe: #2: Installer.Main: GacInstall: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.dll]]" +Installer.exe: #3: Installer.Main: GacInstall: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.Linq.dll]]" +Installer.exe: #4: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319", writable = False +Installer.exe: #5: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #6: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #7: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", writable = True +Installer.exe: #8: RegistryHelper.DeleteSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", subKeyName = "SQLite" +Installer.exe: #9: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", writable = True +Installer.exe: #10: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", subKeyName = "System.Data.SQLite" +Installer.exe: #11: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\System.Data.SQLite", name = , value = "[file nativename [getBuildDirectory]]" +Installer.exe: #12: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319", writable = False +Installer.exe: #13: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #14: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #15: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #16: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #17: Installer.RemoveDbProviderFactory: element = +Installer.exe: #18: Installer.AddDbProviderFactory: element = +Installer.exe: #19: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #20: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", name = "InstallDir", defaultValue = +Installer.exe: #21: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #22: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "Packages", writable = True +Installer.exe: #23: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #24: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = , value = "System.Data.SQLite Designer Package" +Installer.exe: #25: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "Class", value = "SQLite.Designer.SQLitePackage" +Installer.exe: #26: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "CodeBase", value = "[file nativename [file join [getBuildDirectory] SQLite.Designer.dll]]" +Installer.exe: #27: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "ID", value = 400 +Installer.exe: #28: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "InprocServer32", value = "[file nativename [file join $::env(windir) system32 mscoree.dll]]" +Installer.exe: #29: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "CompanyName", value = "http://system.data.sqlite.org/" +Installer.exe: #30: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "MinEdition", value = "standard" +Installer.exe: #31: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "ProductName", value = "System.Data.SQLite Designer Package" +Installer.exe: #32: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", name = "ProductVersion", value = "1.0" +Installer.exe: #33: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", subKeyName = "Toolbox" +Installer.exe: #34: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages\Toolbox", name = "Default Items", value = 3 +Installer.exe: #35: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "Menus", writable = True +Installer.exe: #36: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Menus", name = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}", value = ", 1000, 3" +Installer.exe: #37: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "Services", writable = True +Installer.exe: #38: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Services", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}" +Installer.exe: #39: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Services\{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}", name = , value = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #40: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Services\{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}", name = "Name", value = "System.Data.SQLite Designer Service" +Installer.exe: #41: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #42: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", name = "InstallDir", defaultValue = +Installer.exe: #43: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #44: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "DataSources", writable = True +Installer.exe: #45: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataSources", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}" +Installer.exe: #46: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataSources\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}", name = , value = "System.Data.SQLite Database File" +Installer.exe: #47: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataSources\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}", subKeyName = "SupportingProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}" +Installer.exe: #48: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #49: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", name = "InstallDir", defaultValue = +Installer.exe: #50: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #51: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "DataProviders", writable = True +Installer.exe: #52: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}" +Installer.exe: #53: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = , value = ".NET Framework Data Provider for SQLite" +Installer.exe: #54: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "InvariantName", value = "System.Data.SQLite" +Installer.exe: #55: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "Technology", value = "{77ab9a9d-78b9-4ba7-91ac-873f5338f1d2}" +Installer.exe: #56: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "CodeBase", value = "[file nativename [file join [getBuildDirectory] SQLite.Designer.dll]]" +Installer.exe: #57: RegistryHelper.SetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", name = "FactoryService", value = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}" +Installer.exe: #58: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataConnectionUIControl" +Installer.exe: #59: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataConnectionProperties" +Installer.exe: #60: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataConnectionSupport" +Installer.exe: #61: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataObjectSupport" +Installer.exe: #62: RegistryHelper.CreateSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders\{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}", subKeyName = "SupportedObjects\DataViewSupport" +Installer.exe: #63: Installer.Main: subKeysCreated = 12, subKeysDeleted = 1, keyValuesSet = 20, keyValuesDeleted = 0 +Installer.exe: #64: Installer.Main: Success. ADDED Tests/Uninstaller_Test_Vs2008.log Index: Tests/Uninstaller_Test_Vs2008.log ================================================================== --- /dev/null +++ Tests/Uninstaller_Test_Vs2008.log @@ -0,0 +1,35 @@ +Installer.exe: #1: Configuration.Process: No actual changes will be made to this system because "what-if" mode is enabled. +Installer.exe: #2: Installer.Main: GacRemove: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.Linq.dll]]" +Installer.exe: #3: Installer.Main: GacRemove: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.dll]]" +Installer.exe: #4: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727", writable = False +Installer.exe: #5: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #6: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #7: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx", writable = True +Installer.exe: #8: RegistryHelper.DeleteSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx", subKeyName = "System.Data.SQLite" +Installer.exe: #9: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v2.0.50727", writable = False +Installer.exe: #10: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #11: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #12: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #13: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #14: Installer.RemoveDbProviderFactory: element = +Installer.exe: #15: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #16: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", name = "InstallDir", defaultValue = +Installer.exe: #17: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #18: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "Packages", writable = True +Installer.exe: #19: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #20: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "Menus", writable = True +Installer.exe: #21: RegistryHelper.DeleteValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Menus", name = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #22: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "Services", writable = True +Installer.exe: #23: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Services", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}" +Installer.exe: #24: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #25: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", name = "InstallDir", defaultValue = +Installer.exe: #26: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #27: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "DataSources", writable = True +Installer.exe: #28: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataSources", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}" +Installer.exe: #29: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #30: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", name = "InstallDir", defaultValue = +Installer.exe: #31: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\9.0", writable = False +Installer.exe: #32: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0", subKeyName = "DataProviders", writable = True +Installer.exe: #33: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\DataProviders", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}" +Installer.exe: #34: Installer.Main: subKeysCreated = 0, subKeysDeleted = 5, keyValuesSet = 0, keyValuesDeleted = 1 +Installer.exe: #35: Installer.Main: Success. ADDED Tests/Uninstaller_Test_Vs2010.log Index: Tests/Uninstaller_Test_Vs2010.log ================================================================== --- /dev/null +++ Tests/Uninstaller_Test_Vs2010.log @@ -0,0 +1,35 @@ +Installer.exe: #1: Configuration.Process: No actual changes will be made to this system because "what-if" mode is enabled. +Installer.exe: #2: Installer.Main: GacRemove: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.Linq.dll]]" +Installer.exe: #3: Installer.Main: GacRemove: assemblyPath = "[file nativename [file join [getBuildDirectory] System.Data.SQLite.dll]]" +Installer.exe: #4: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319", writable = False +Installer.exe: #5: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #6: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #7: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", writable = True +Installer.exe: #8: RegistryHelper.DeleteSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", subKeyName = "System.Data.SQLite" +Installer.exe: #9: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework\v4.0.30319", writable = False +Installer.exe: #10: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #11: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #12: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\.NETFramework", writable = False +Installer.exe: #13: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework", name = "InstallRoot", defaultValue = +Installer.exe: #14: Installer.RemoveDbProviderFactory: element = +Installer.exe: #15: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #16: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", name = "InstallDir", defaultValue = +Installer.exe: #17: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #18: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "Packages", writable = True +Installer.exe: #19: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Packages", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #20: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "Menus", writable = True +Installer.exe: #21: RegistryHelper.DeleteValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Menus", name = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9c}" +Installer.exe: #22: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "Services", writable = True +Installer.exe: #23: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\Services", subKeyName = "{dcbe6c8d-0e57-4099-a183-98ff74c64d9d}" +Installer.exe: #24: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #25: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", name = "InstallDir", defaultValue = +Installer.exe: #26: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #27: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "DataSources", writable = True +Installer.exe: #28: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataSources", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c71}" +Installer.exe: #29: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #30: RegistryHelper.GetValue: key = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", name = "InstallDir", defaultValue = +Installer.exe: #31: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE", subKeyName = "Software\Microsoft\VisualStudio\10.0", writable = False +Installer.exe: #32: RegistryHelper.OpenSubKey: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0", subKeyName = "DataProviders", writable = True +Installer.exe: #33: RegistryHelper.DeleteSubKeyTree: rootKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\10.0\DataProviders", subKeyName = "{0ebaab6e-ca80-4b4a-8ddf-cbe6bf058c70}" +Installer.exe: #34: Installer.Main: subKeysCreated = 0, subKeysDeleted = 5, keyValuesSet = 0, keyValuesDeleted = 1 +Installer.exe: #35: Installer.Main: Success. Index: Tests/all.eagle ================================================================== --- Tests/all.eagle +++ Tests/all.eagle @@ -44,13 +44,13 @@ # # NOTE: Run all the unit tests. # set test_time [time { runAllTests $test_channel $path \ - [getTestFiles [list $path] $test_flags(-file) $test_flags(-notFile)] \ - [list [file tail [info script]] *.tcl pkgIndex.eagle common.eagle \ - constraints.eagle epilogue.eagle prologue.eagle] + [getTestFiles [list $path] $test_flags(-file) $test_flags(-notFile)] \ + [list [file tail [info script]] *.tcl pkgIndex.eagle common.eagle \ + constraints.eagle epilogue.eagle prologue.eagle] }] # # NOTE: Run the local test epilogue, if any. # Index: Tests/basic.eagle ================================================================== --- Tests/basic.eagle +++ Tests/basic.eagle @@ -49,32 +49,42 @@ checkForFile $test_channel $testLinqOutFile } ############################################################################### -runTest {test basic-1.1 {unit tests from the 'test' project} -setup { - catch {file delete [file join [file dirname $testExeFile] Test.db3]} +runTest {test data-1.1 {unit tests from the 'test' project} -setup { + cleanupFile [file join [file dirname $testExeFile] Test.db3] + + set fileName [file join [getDatabaseDirectory] data-1.1.db] + cleanupDb $fileName } -body { set output "" set code [catch { + # + # NOTE: For the sake of backward compatibility, the "-autoRun" argument + # must be first. + # testClrExec $testExeFile [list -eventflags Wait -directory \ - [file dirname $testExeFile] -stdout output -success 0] -autoRun + [file dirname $testExeFile] -stdout output -success 0] -autoRun \ + -fileName [appendArgs \" [file nativename $fileName] \"] } error] tlog "---- BEGIN STDOUT OUTPUT\n" tlog $output tlog "\n---- END STDOUT OUTPUT\n" list $code [expr {$code == 0 ? "" : $error}] } -cleanup { - unset -nocomplain code output error + cleanupDb $fileName + + unset -nocomplain code output error fileName } -constraints {eagle file_test.exe} -result {0 {}}} ############################################################################### -runTest {test basic-1.2 {unit tests from the 'testlinq' project} -setup { +runTest {test data-1.2 {unit tests from the 'testlinq' project} -setup { # # NOTE: Re-copy the reference database file used for this unit test to the # build directory in case it has been changed by a previous test run. # file copy -force $northwindEfDbFile \ @@ -91,11 +101,11 @@ } -body { set output "" set code [catch { testClrExec $testLinqExeFile [list -eventflags Wait -directory \ - [file dirname $testLinqExeFile] -stdout output -success 0] + [file dirname $testLinqExeFile] -stdout output -success 0] } error] tlog "---- BEGIN STDOUT OUTPUT\n" tlog $output tlog "\n---- END STDOUT OUTPUT\n" @@ -110,12 +120,12 @@ {eagle monoToDo file_testlinq.exe file_northwindEF.db file_testlinq.out} \ -result {0 True {}}} ############################################################################### -runTest {test basic-1.3 {SELECT scalar/reader, CREATE, INSERT} -setup { - setupDb [set fileName basic-1.3.db] +runTest {test data-1.3 {SELECT scalar/reader, CREATE, INSERT} -setup { + setupDb [set fileName data-1.3.db] } -body { set result [list] lappend result [sql execute -execute scalar $db \ "SELECT sqlite_source_id();"] @@ -139,15 +149,15 @@ -match regexp -result {^\{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [0-9a-f]{40}\}\ \{1 \{\{x 1\} \{y foo\} \{z 1234\}\}\} \{count 1\} \{names \{x y z\}\}$}} ############################################################################### -runTest {test basic-1.4 {GetSchema with ReservedWords} -setup { - setupDb [set fileName basic-1.4.db] +runTest {test data-1.4 {GetSchema with ReservedWords} -setup { + setupDb [set fileName data-1.4.db] } -body { set id [object invoke Interpreter.GetActive NextId] - set dataSource [file join [getTemporaryPath] basic-1.4.db] + set dataSource [file join [getDatabaseDirectory] $fileName] unset -nocomplain results errors set code [compileCSharpWith [subst { using System.Data; @@ -172,11 +182,11 @@ { // do nothing. } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} GetReservedWords @@ -189,12 +199,12 @@ -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ System#Data#DataTable#\d+$}} ############################################################################### -runTest {test basic-1.5 {GetSchema with ForeignKeys} -setup { - setupDb [set fileName basic-1.5.db] +runTest {test data-1.5 {GetSchema with ForeignKeys} -setup { + setupDb [set fileName data-1.5.db] } -body { sql execute $db { CREATE TABLE t1( x INTEGER REFERENCES t2 MATCH FULL ON UPDATE SET DEFAULT ON DELETE CASCADE @@ -203,11 +213,11 @@ } sql execute $db "CREATE TABLE t2(x INTEGER REFERENCES t3);" set id [object invoke Interpreter.GetActive NextId] - set dataSource [file join [getTemporaryPath] basic-1.5.db] + set dataSource [file join [getDatabaseDirectory] $fileName] unset -nocomplain results errors set code [compileCSharpWith [subst { using System.Data; @@ -232,11 +242,11 @@ { // do nothing. } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { set rows [list] @@ -249,10 +259,11 @@ [$foreignKey Item TABLE_CATALOG] \ [$foreignKey Item TABLE_NAME] \ [$foreignKey Item CONSTRAINT_TYPE] \ [$foreignKey Item IS_DEFERRABLE] \ [$foreignKey Item INITIALLY_DEFERRED] \ + [$foreignKey Item FKEY_ID] \ [$foreignKey Item FKEY_FROM_COLUMN] \ [$foreignKey Item FKEY_TO_CATALOG] \ [$foreignKey Item FKEY_TO_TABLE] \ [$foreignKey Item FKEY_TO_COLUMN] \ [$foreignKey Item FKEY_FROM_ORDINAL_POSITION] \ @@ -269,21 +280,21 @@ unset -nocomplain result rows foreignKey foreignKeys results errors code \ dataSource id db fileName } -constraints \ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ -\{\{main FK_t1_0 main t1 \{FOREIGN KEY\} False False x main t2 \{\} 0 \{SET\ -DEFAULT\} CASCADE NONE\} \{main FK_t2_0 main t2 \{FOREIGN KEY\} False False x\ -main t3 \{\} 0 \{NO ACTION\} \{NO ACTION\} NONE\}\}$}} +\{\{main FK_t1_0_0 main t1 \{FOREIGN KEY\} False False 0 x main t2 \{\} 0 \{SET\ +DEFAULT\} CASCADE NONE\} \{main FK_t2_0_0 main t2 \{FOREIGN KEY\} False False 0\ +x main t3 \{\} 0 \{NO ACTION\} \{NO ACTION\} NONE\}\}$}} ############################################################################### -runTest {test basic-1.6 {SQLITE_FCNTL_WIN32_AV_RETRY} -setup { - setupDb [set fileName basic-1.6.db] +runTest {test data-1.6 {SQLITE_FCNTL_WIN32_AV_RETRY} -setup { + setupDb [set fileName data-1.6.db] } -body { set id [object invoke Interpreter.GetActive NextId] - set dataSource [file join [getTemporaryPath] basic-1.6.db] + set dataSource [file join [getDatabaseDirectory] $fileName] unset -nocomplain results errors set code [compileCSharpWith [subst { using System.Data; @@ -328,11 +339,11 @@ { // do nothing. } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { set savedCount -1; set savedInterval -1 @@ -375,15 +386,15 @@ -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ True$}} ############################################################################### -runTest {test basic-1.7 {properly closed database file (non-query)} -setup { - set fileName basic-1.7.db +runTest {test data-1.7 {properly closed database file (non-query)} -setup { + set fileName data-1.7.db } -body { set id [object invoke Interpreter.GetActive NextId] - set dataSource [file join [getTemporaryPath] $fileName] + set dataSource [file join [getDatabaseDirectory] $fileName] set sql { \ BEGIN EXCLUSIVE TRANSACTION; \ CREATE TABLE t1(x INTEGER); \ INSERT INTO t1 (x) VALUES(1); \ @@ -414,11 +425,11 @@ } } } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} Main @@ -433,15 +444,15 @@ -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ \{\} \{\}$}} ############################################################################### -runTest {test basic-1.8 {properly closed database file (reader #1)} -setup { - set fileName basic-1.8.db +runTest {test data-1.8 {properly closed database file (reader #1)} -setup { + set fileName data-1.8.db } -body { set id [object invoke Interpreter.GetActive NextId] - set dataSource [file join [getTemporaryPath] $fileName] + set dataSource [file join [getDatabaseDirectory] $fileName] set sql { \ BEGIN EXCLUSIVE TRANSACTION; \ CREATE TABLE t1(x INTEGER); \ INSERT INTO t1 (x) VALUES(1); \ @@ -475,11 +486,11 @@ } } } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} Main @@ -494,15 +505,15 @@ -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ \{\} \{\}$}} ############################################################################### -runTest {test basic-1.9 {properly closed database file (reader #2)} -setup { - set fileName basic-1.9.db +runTest {test data-1.9 {properly closed database file (reader #2)} -setup { + set fileName data-1.9.db } -body { set id [object invoke Interpreter.GetActive NextId] - set dataSource [file join [getTemporaryPath] $fileName] + set dataSource [file join [getDatabaseDirectory] $fileName] set sql { \ BEGIN EXCLUSIVE TRANSACTION; \ CREATE TABLE t1(x INTEGER); \ INSERT INTO t1 (x) VALUES(1); \ @@ -537,11 +548,11 @@ } } } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} Main @@ -556,12 +567,12 @@ -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ \{\} \{\}$}} ############################################################################### -runTest {test basic-1.10 {Changes property} -setup { - setupDb [set fileName basic-1.10.db] +runTest {test data-1.10 {Changes property} -setup { + setupDb [set fileName data-1.10.db] } -body { set connection [object invoke -flags +NonPublic -objectflags +NoDispose \ Interpreter.GetActive.connections Item $db] set result [list] @@ -591,12 +602,12 @@ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ -result {1 2 2 {1 {{x 1} {y foobar} {z 1234}}} {2 {{x 2} {y foobar} {z 5678}}}}} ############################################################################### -runTest {test basic-1.11 {LastInsertRowId property} -setup { - setupDb [set fileName basic-1.11.db] +runTest {test data-1.11 {LastInsertRowId property} -setup { + setupDb [set fileName data-1.11.db] } -body { set connection [object invoke -flags +NonPublic -objectflags +NoDispose \ Interpreter.GetActive.connections Item $db] set result [list] @@ -648,12 +659,12 @@ -result {0 1 2 2 1 2 2 2 {1 {{x 1} {y foobar} {z 1234}}} {2 {{x 2} {y foobar}\ {z 5678}}} 2 {1 {{x 1} {y foobar} {z 1234}}} {2 {{x 2} {y foobar} {z 5678}}}}} ############################################################################### -runTest {test basic-1.12 {DateTime using Unix epoch} -setup { - setupDb [set fileName basic-1.12.db] "" "" "DateTimeFormat=UnixEpoch;" +runTest {test data-1.12 {DateTime using Unix epoch} -setup { + setupDb [set fileName data-1.12.db] "" UnixEpoch Utc } -body { set result [list] sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC, y DATETIME);" sql execute $db "INSERT INTO t1 (x, y) VALUES(1, 1302825600);" @@ -676,11 +687,11 @@ [list param1 DateTime 1334448000] sql execute -verbatim $db "INSERT INTO t1 (x, y) VALUES(9, ?);" \ [list param1 DateTime 1365984000] - sql execute -execute reader -datetimeformat "MM/dd/yyyy HH:mm:ss" $db \ + sql execute -execute reader -datetimeformat [getDateTimeFormat] $db \ "SELECT x, y FROM t1 ORDER BY x;" foreach name [lsort -integer [array names rows -regexp {^\d+$}]] { lappend result [list $name $rows($name)] } @@ -690,24 +701,24 @@ cleanupDb $fileName unset -nocomplain name rows result db fileName } -constraints \ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ --result {{1 {{x 1} {y {04/15/2011 00:00:00}}}} {2 {{x 2} {y {04/15/2012\ -00:00:00}}}} {3 {{x 3} {y {04/15/2013 00:00:00}}}} {4 {{x 4} {y {04/15/2011\ -00:00:00}}}} {5 {{x 5} {y {04/15/2012 00:00:00}}}} {6 {{x 6} {y {04/15/2013\ -00:00:00}}}} {7 {{x 7} {y {04/15/2011 00:00:00}}}} {8 {{x 8} {y {04/15/2012\ -00:00:00}}}} {9 {{x 9} {y {04/15/2013 00:00:00}}}}}} +-result {{1 {{x 1} {y {2011-04-15 00:00:00Z}}}} {2 {{x 2} {y {2012-04-15\ +00:00:00Z}}}} {3 {{x 3} {y {2013-04-15 00:00:00Z}}}} {4 {{x 4} {y {2011-04-15\ +00:00:00Z}}}} {5 {{x 5} {y {2012-04-15 00:00:00Z}}}} {6 {{x 6} {y {2013-04-15\ +00:00:00Z}}}} {7 {{x 7} {y {2011-04-15 00:00:00Z}}}} {8 {{x 8} {y {2012-04-15\ +00:00:00Z}}}} {9 {{x 9} {y {2013-04-15 00:00:00Z}}}}}} ############################################################################### set date [clock format [clock seconds] -format yyyy-MM-dd] ############################################################################### -runTest {test basic-1.13 {DateTime using invariant culture} -setup { - setupDb [set fileName basic-1.13.db] "" "" "DateTimeFormat=InvariantCulture;" +runTest {test data-1.13 {DateTime using invariant culture} -setup { + setupDb [set fileName data-1.13.db] "" InvariantCulture Utc } -body { set result [list] sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC, y DATETIME);" @@ -739,11 +750,11 @@ [list param1 DateTime 12:00] sql execute $db "INSERT INTO t1 (x, y) VALUES(12, ?);" \ [list param1 DateTime "12/16/2009 12:00"] - sql execute -execute reader -datetimeformat "MM/dd/yyyy HH:mm:ss" $db \ + sql execute -execute reader -datetimeformat [getDateTimeFormat] $db \ "SELECT x, CAST(y AS TEXT) AS y2 FROM t1 ORDER BY x;" foreach name [lsort -integer [array names rows -regexp {^\d+$}]] { lappend result [list $name $rows($name)] } @@ -763,12 +774,12 @@ {10 {{x 10} {y2 2009-12-16T00:00:00.0000000Z}}} {11 {{x 11} {y2\ ${date}T12:00:00.0000000Z}}} {12 {{x 12} {y2 2009-12-16T12:00:00.0000000Z}}}}]} ############################################################################### -runTest {test basic-1.14 {DateTime using current culture} -setup { - setupDb [set fileName basic-1.14.db] "" "" "DateTimeFormat=CurrentCulture;" +runTest {test data-1.14 {DateTime using current culture} -setup { + setupDb [set fileName data-1.14.db] "" CurrentCulture Utc } -body { set result [list] sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC, y DATETIME);" @@ -800,11 +811,11 @@ [list param1 DateTime 12:00] sql execute $db "INSERT INTO t1 (x, y) VALUES(12, ?);" \ [list param1 DateTime "12/16/2009 12:00"] - sql execute -execute reader -datetimeformat "MM/dd/yyyy HH:mm:ss" $db \ + sql execute -execute reader -datetimeformat [getDateTimeFormat] $db \ "SELECT x, CAST(y AS TEXT) AS y2 FROM t1 ORDER BY x;" foreach name [lsort -integer [array names rows -regexp {^\d+$}]] { lappend result [list $name $rows($name)] } @@ -813,11 +824,11 @@ } -cleanup { cleanupDb $fileName unset -nocomplain name rows result db fileName } -constraints \ -{eagle culture.en_US monoBug28 command.sql compile.DATA SQLite\ +{eagle culture.invariant monoBug28 command.sql compile.DATA SQLite\ System.Data.SQLite} -result [subst {{1 {{x 1} {y2 {Wednesday, 16 December\ 2009}}}} {2 {{x 2} {y2 12:00:00}}} {3 {{x 3} {y2 {Wednesday, 16 December 2009\ 12:00:00}}}} {4 {{x 4} {y2 12/16/2009}}} {5 {{x 5} {y2 12:00}}} {6 {{x 6} {y2\ {12/16/2009 12:00}}}} {7 {{x 7} {y2 2009-12-16T00:00:00.0000000Z}}} {8 {{x 8}\ {y2 ${date}T12:00:00.0000000Z}}} {9 {{x 9} {y2 2009-12-16T12:00:00.0000000Z}}}\ @@ -825,14 +836,200 @@ ${date}T12:00:00.0000000Z}}} {12 {{x 12} {y2 2009-12-16T12:00:00.0000000Z}}}}]} ############################################################################### unset -nocomplain date + +############################################################################### + +runTest {test data-1.15 {SQLiteConnectionStringBuilder DateTime} -body { + set id [object invoke Interpreter.GetActive NextId] + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System.Data.SQLite; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static string GetConnectionString( + string format, + string kind + ) + { + SQLiteConnectionStringBuilder builder = + new SQLiteConnectionStringBuilder(); + + builder.Add("Date Source", "test.db"); + builder.Add("DateTimeFormat", format); + builder.Add("DateTimeKind", kind); + + return builder.ToString(); + } + + public static void Main() + { + // do nothing. + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} GetConnectionString \ + null null + } result] : [set result ""]}] $result \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} GetConnectionString \ + Default null + } result] : [set result ""]}] $result \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} GetConnectionString \ + null Unspecified + } result] : [set result ""]}] $result \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} GetConnectionString \ + ISO8601 Utc + } result] : [set result ""]}] $result +} -cleanup { + unset -nocomplain result results errors code id +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ +regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{Date\ +Source=test\.db\} 0 \{Date Source=test\.db;DateTimeFormat=ISO8601\} 0 \{Date\ +Source=test\.db;DateTimeKind=Unspecified\} 0 \{Date\ +Source=test\.db;DateTimeFormat=ISO8601;DateTimeKind=Utc\}$}} + +############################################################################### + +runTest {test data-1.16 {SQLiteConnectionStringBuilder properties} -body { + set id [object invoke Interpreter.GetActive NextId] + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data.SQLite; + using System.Reflection; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static string GetConnectionString( + string key, + string value, + string propertyName + ) + { + SQLiteConnectionStringBuilder builder = + new SQLiteConnectionStringBuilder(); + + if (key != null) + builder.Add(key, value); + + object propertyValue = null; + + if (propertyName != null) + { + propertyValue = typeof(SQLiteConnectionStringBuilder).InvokeMember( + propertyName, BindingFlags.Instance | BindingFlags.Public | + BindingFlags.GetProperty, null, builder, null); + } + + return String.Format("{0}, {1}", propertyValue, builder); + } + + public static void Main() + { + // do nothing. + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + lappend results $code [expr {[info exists errors] ? $errors : ""}] + + if {$code eq "Ok"} then { + set keys [list null Version Synchronous UseUTF16Encoding Pooling \ + BinaryGUID "Data Source" Uri "Default Timeout" \ + Enlist FailIfMissing "Legacy Format" "Read Only" \ + Password "Page Size" "Max Page Count" "Cache Size" \ + DateTimeFormat DateTimeKind BaseSchemaName \ + "Journal Mode" "Default IsolationLevel" "Foreign Keys" \ + Flags] + + set values [list null 3 Full True False \ + True test.db test.db 60 \ + False True False True \ + secret 4096 1024 8192 \ + UnixEpoch Utc sqlite_schema \ + Memory Serializable False \ + Default] + + set propertyNames [list null Version SyncMode UseUTF16Encoding Pooling \ + BinaryGUID DataSource Uri DefaultTimeout \ + Enlist FailIfMissing LegacyFormat ReadOnly \ + Password PageSize MaxPageCount CacheSize \ + DateTimeFormat DateTimeKind BaseSchemaName \ + JournalMode DefaultIsolationLevel ForeignKeys \ + Flags] + + foreach key $keys value $values propertyName $propertyNames { + set code [catch { + object invoke _Dynamic${id}.Test${id} GetConnectionString \ + $key $value $propertyName + } result] + + lappend results $code $result + } + } + + set results +} -cleanup { + unset -nocomplain propertyName propertyNames value key values keys result \ + results errors code id +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ +regexp -result {^System#CodeDom#Compiler#CompilerResults#\d+ Ok \{\} 0 \{, \} 0\ +\{3, Version=3\} 0 \{Full, Synchronous=Full\} 0 \{True, UseUTF16Encoding=True\}\ +0 \{False, Pooling=False\} 0 \{True, BinaryGUID=True\} 0 \{test\.db, Data\ +Source=test\.db\} 0 \{test\.db, Uri=test\.db\} 0 \{60, Default Timeout=60\} 0\ +\{False, Enlist=False\} 0 \{True, FailIfMissing=True\} 0 \{False, Legacy\ +Format=False\} 0 \{True, Read Only=True\} 0 \{secret, Password=secret\} 0\ +\{4096, Page Size=4096\} 0 \{1024, Max Page Count=1024\} 0 \{8192, Cache\ +Size=8192\} 0 \{UnixEpoch, DateTimeFormat=UnixEpoch\} 0 \{Utc,\ +DateTimeKind=Utc\} 0 \{sqlite_schema, BaseSchemaName=sqlite_schema\} 0\ +\{Memory, Journal Mode=Memory\} 0 \{Serializable, Default\ +IsolationLevel=Serializable\} 0 \{False, Foreign Keys=False\} 0 \{Default,\ +Flags=Default\}$}} + +############################################################################### + +runTest {test data-1.17 {SQLiteConvert ToDateTime (Julian Day)} -body { + set dateTime [object invoke System.Data.SQLite.SQLiteConvert ToDateTime \ + 2455928.0 Utc] + + object invoke $dateTime ToString [getDateTimeFormat] +} -cleanup { + unset -nocomplain dateTime +} -constraints {eagle System.Data.SQLite} -result {2012-01-01 12:00:00Z}} + +############################################################################### + +runTest {test data-1.18 {SQLiteConvert ToJulianDay} -body { + expr {round([object invoke System.Data.SQLite.SQLiteConvert ToJulianDay \ + "2012-01-01 12:00:00Z"])} +} -constraints {eagle System.Data.SQLite} -result {2455928}} ############################################################################### unset -nocomplain testExeFile testLinqExeFile northwindEfDbFile testLinqOutFile ############################################################################### runSQLiteTestEpilogue runTestEpilogue Index: Tests/common.eagle ================================================================== --- Tests/common.eagle +++ Tests/common.eagle @@ -106,21 +106,63 @@ # # EagleShell.exe -initialize -postInitialize # "object invoke Interpreter.GetActive AddRuntimeOption native" # -file .\path\to\all.eagle # - if {[hasRuntimeOption native]} then { - return [file join [file dirname $::path] bin [getBuildYear] \ - [machineToPlatform $::tcl_platform(machine)] \ - [getBuildConfiguration]] + if {[info exists ::build_directory] && \ + [string length $::build_directory] > 0} then { + # + # NOTE: The location of the build directory has been overridden; + # therefore, use it verbatim. + # + return $::build_directory } else { - return [file join [file dirname $::path] bin [getBuildYear] \ - [getBuildConfiguration] bin] + # + # NOTE: Figure out the build base directory. This will be the directory + # that contains the actual build output directory (e.g. "bin"). + # + if {[info exists ::build_base_directory] && \ + [string length $::build_base_directory] > 0} then { + # + # NOTE: The location of the build base directory has been overridden; + # therefore, use it verbatim. + # + set path $::build_base_directory + } elseif {[info exists ::common_directory] && \ + [string length $::common_directory] > 0} then { + # + # NOTE: Next, fallback to the parent directory of the one containing + # this file (i.e. "common.eagle"), if available. + # + set path [file dirname $::common_directory] + } else { + # + # NOTE: Finally, fallback to the parent directory of the EagleTest + # path. The EagleTest package guarantees that this variable + # will be set to the directory containing the first file to + # execute the [runTestPrologue] script library procedure. + # + set path [file dirname $::path] + } + + if {[hasRuntimeOption native]} then { + return [file join $path bin [getBuildYear] \ + [machineToPlatform $::tcl_platform(machine)] \ + [getBuildConfiguration]] + } else { + return [file join $path bin [getBuildYear] \ + [getBuildConfiguration] bin] + } } } proc getBuildFileName { fileName } { + # + # NOTE: Returns the specified file name as if it were located in the + # build directory, discarding any directory information present + # in the file name as provided by the caller. + # return [file nativename \ [file join [getBuildDirectory] [file tail $fileName]]] } proc getBinaryDirectory {} { @@ -129,19 +171,70 @@ # itself (i.e. the Eagle shell) is located. This will be used as # the destination for the copied System.Data.SQLite native and # managed assemblies (i.e. because this is one of the few places # where the CLR will actually find and load them properly). # - return [info binary] + if {[info exists ::binary_directory] && \ + [string length $::binary_directory] > 0} then { + # + # NOTE: The location of the binary directory has been overridden; + # therefore, use it verbatim. + # + return $::binary_directory + } else { + return [info binary] + } } proc getBinaryFileName { fileName } { + # + # NOTE: Returns the specified file name as if it were located in the + # binary directory, discarding any directory information present + # in the file name as provided by the caller. + # return [file nativename \ [file join [getBinaryDirectory] [file tail $fileName]]] } + + proc getDatabaseDirectory {} { + # + # NOTE: This procedure returns the directory where the test databases + # should be located. By default, this just uses the temporary + # directory configured for this system. + # + if {[info exists ::database_directory] && \ + [string length $::database_directory] > 0} then { + # + # NOTE: The location of the database directory has been overridden; + # therefore, use it. + # + return [file normalize $::database_directory] + } else { + return [getTemporaryPath] + } + } proc getAppDomainPreamble { {prefix ""} {suffix ""} } { + # + # NOTE: This procedure returns a test setup script suitable for evaluation + # by a test interpreter created in an isolated application domain. + # The script being returned will be surrounded by the prefix and + # suffix "script fragments" specified by the caller, if any. The + # entire script being returned will be substituted via [subst], in + # the context of the caller. This step is necessary so that some + # limited context information, primarily related to the test build + # directory, can be transferred to the interpreter in the isolated + # application domain, making it able to successfully run tests that + # require one or more of the files in the build directory. Callers + # to this procedure should keep in mind that the test script being + # returned cannot only rely on any script library procedures not + # included in the EagleLibrary package (i.e. "init.eagle"). Also, + # all variable references and all "nested" commands (i.e. those in + # square brackets), unless they are specially quoted, will end up + # being evaluated in the context of the calling interpreter and not + # the test interpreter created in the isolated application domain. + # return [uplevel 1 [list subst [appendArgs $prefix { if {[hasRuntimeOption native]} then { object invoke Interpreter.GetActive AddRuntimeOption native } @@ -217,16 +310,23 @@ proc tryLoadAssembly { fileName } { set fileName [getBinaryFileName $fileName] if {[catch {set assembly \ - [object load -loadtype File $fileName]}] == 0} then { + [object load -loadtype File -alias $fileName]}] == 0} then { # # NOTE: Now, add the necessary test constraint. # addConstraint [file rootname [file tail $fileName]] + # + # NOTE: Grab the image runtime version from the assembly because + # several tests rely on it having a certain value. + # + addConstraint [appendArgs [file tail $fileName] _ \ + [$assembly ImageRuntimeVersion]] + # # NOTE: Return the full path of the loaded file. # return $fileName } @@ -237,20 +337,54 @@ proc checkForSQLite { channel } { tputs $channel "---- checking for core SQLite library... " if {[catch {object invoke -flags +NonPublic System.Data.SQLite.SQLite3 \ SQLiteVersion} version] == 0} then { + # + # NOTE: Attempt to query the Fossil source identifier for the SQLite + # core library. + # + if {[catch {object invoke -flags +NonPublic System.Data.SQLite.SQLite3 \ + SQLiteSourceId} sourceId]} then { + # + # NOTE: We failed to query the Fossil source identifier. + # + set sourceId unknown + } + # # NOTE: Yes, the SQLite core library appears to be available. # addConstraint SQLite - tputs $channel [appendArgs "yes (" $version ")\n"] + tputs $channel [appendArgs "yes (" $version " " $sourceId ")\n"] } else { tputs $channel no\n } } + + proc getDateTimeFormat {} { + # + # NOTE: This procedure simply returns the "default" DateTime format used + # by the test suite. + # + if {[info exists ::datetime_format] && \ + [string length $::datetime_format] > 0} then { + # + # NOTE: Return the manually overridden value for the DateTime format. + # + return $::datetime_format + } else { + # + # NOTE: Return an ISO8601 DateTime format compatible with SQLite, + # System.Data.SQLite, and suitable for round-tripping with the + # DateTime class of the framework. If this value is changed, + # various tests may fail. + # + return "yyyy-MM-dd HH:mm:ss.FFFFFFFK" + } + } proc enumerableToList { enumerable } { set result [list] if {[string length $enumerable] == 0 || $enumerable eq "null"} then { @@ -265,25 +399,31 @@ return $result } proc compileCSharpWith { - text resultsVarName errorsVarName fileNames args } { + text memory symbols strict resultsVarName errorsVarName fileNames + args } { + # + # NOTE: Since we are going to use this method name a lot, assign it to a + # variable first. + # + set add ReferencedAssemblies.Add + # # NOTE: Create the base command to evaluate and add the property settings # that are almost always needed by our unit tests (i.e. the System # and System.Data assembly references). # - set command [list compileCSharp $text results errors \ - ReferencedAssemblies.Add System.dll ReferencedAssemblies.Add \ - System.Data.dll ReferencedAssemblies.Add System.Xml.dll] + set command [list compileCSharp $text $memory $symbols $strict results \ + errors $add System.dll $add System.Data.dll $add System.Xml.dll] # # NOTE: Add all the provided file names as assembly references. # foreach fileName $fileNames { - lappend command ReferencedAssemblies.Add [getBinaryFileName $fileName] + lappend command $add [getBinaryFileName $fileName] } # # NOTE: Add the extra arguments, if any, to the command to evaluate. # @@ -301,24 +441,37 @@ # result. # eval $command } - proc setupDb {fileName {mode ""} {delete ""} {extra ""} {varName db}} { + proc setupDb { + fileName {mode ""} {dateTimeFormat ""} {dateTimeKind ""} {flags ""} + {extra ""} {delete true} {varName db} } { # # NOTE: For now, all test databases used by the test suite are placed into # the temporary directory. Each database used by a test should be # cleaned up by that test using the "cleanupDb" procedure, below. # - set fileName [file join [getTemporaryPath] [file tail $fileName]] + set fileName [file join [getDatabaseDirectory] [file tail $fileName]] # # NOTE: By default, delete any pre-existing database with the same file - # name. + # name if it currently exists. # - if {[string length $delete] == 0 || $delete} then { - catch {file delete $fileName} + if {$delete && [file exists $fileName]} then { + # + # NOTE: Attempt to delete any pre-existing database with the same file + # name. + # + if {[catch {file delete $fileName} error]} then { + # + # NOTE: We somehow failed to delete the file, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to delete database file \"" $fileName \ + "\" during setup, error: " \n\t $error \n] + } } # # NOTE: Refer to the specified variable (e.g. "db") in the context of the # caller. The handle to the opened database will be stored there. @@ -337,10 +490,57 @@ # if {[string length $mode] > 0} then { append connection {;Journal Mode=${mode}} } + # + # NOTE: If the caller specified a DateTime format, add the necessary + # portion of the connection string now. + # + if {[string length $dateTimeFormat] > 0} then { + append connection {;DateTimeFormat=${dateTimeFormat}} + } + + # + # NOTE: If the caller specified a DateTimeKind, add the necessary portion + # of the connection string now. + # + if {[string length $dateTimeKind] > 0} then { + append connection {;DateTimeKind=${dateTimeKind}} + } + + # + # NOTE: If there are any global (per test run) connection flags currently + # set, use them now (i.e. by combining them with the ones for this + # connection). + # + if {[info exists ::connection_flags] && \ + [string length $::connection_flags] > 0} then { + # + # NOTE: Show (and log) that we detected some global connection flags. + # + tputs $::test_channel [appendArgs \ + "---- global connection flags detected: " $::connection_flags \n] + + # + # NOTE: Combine and/or replace the connection flags and then show the + # new value. + # + set flags [combineFlags $flags $::connection_flags] + + tputs $::test_channel [appendArgs \ + "---- combined connection flags are: " $flags \n] + } + + # + # NOTE: If the caller specified a SQLiteConnectionFlags, add the necessary + # portion of the connection string now. + # + if {[string length $flags] > 0} then { + append connection {;Flags=${flags}} + } + # # NOTE: If the caller specified an extra payload to the connection string, # append it now. # if {[string length $extra] > 0} then { @@ -352,11 +552,11 @@ # into the variable specified by the caller. # set db [sql open -type SQLite [subst $connection]] } - proc cleanupDb {fileName {varName db}} { + proc cleanupDb { fileName {varName db} } { # # NOTE: Refer to the specified variable (e.g. "db") in the context of the # caller. The handle to the opened database is stored there. # upvar 1 $varName db @@ -363,17 +563,187 @@ # # NOTE: Close the connection to the database now. This should allow us to # delete the underlying database file. # - catch {sql close $db} + if {[info exists db] && [catch {sql close $db} error]} then { + # + # NOTE: We somehow failed to close the database, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to close database \"" $db "\", error: " \ + \n\t $error \n] + } + + # + # NOTE: Build the full path to the database file name. For now, all test + # database files are stored in the temporary directory. + # + set fileName [file join [getDatabaseDirectory] [file tail $fileName]] + + # + # NOTE: Check if the file still exists. + # + if {[file exists $fileName]} then { + # + # NOTE: Skip deleting database files if somebody sets the global + # variable to prevent it. + # + if {![info exists ::no(cleanupDb)]} then { + # + # NOTE: Attempt to delete the test database file now. + # + if {[set code [catch {file delete $fileName} error]]} then { + # + # NOTE: We somehow failed to delete the file, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to delete database file \"" $fileName \ + "\" during cleanup, error: " \n\t $error \n] + } + } else { + # + # NOTE: Show that we skipped deleting the file. + # + set code 0 + + tputs $::test_channel [appendArgs \ + "==== WARNING: skipped deleting database file \"" $fileName \ + "\" during cleanup\n"] + } + } else { + # + # NOTE: The file does not exist, success! + # + set code 0 + } + + return $code + } + + proc cleanupFile { fileName {force false} } { + # + # NOTE: Check if the file still exists. + # + if {[file exists $fileName]} then { + # + # NOTE: Skip deleting test files if somebody sets the global variable + # to prevent it. + # + if {$force || ![info exists ::no(cleanupFile)]} then { + # + # NOTE: Attempt to delete the test file now. + # + if {[set code [catch {file delete $fileName} error]]} then { + # + # NOTE: We somehow failed to delete the file, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to delete test file \"" $fileName \ + "\" during cleanup, error: " \n\t $error \n] + } + } else { + # + # NOTE: Show that we skipped deleting the file. + # + set code 0 + + tputs $::test_channel [appendArgs \ + "==== WARNING: skipped deleting test file \"" $fileName \ + "\" during cleanup\n"] + } + } else { + # + # NOTE: The file does not exist, success! + # + set code 0 + } + return $code + } + + proc reportSQLiteResources { channel {quiet false} {collect true} } { # - # NOTE: Delete the test database file now. For now, all test database - # files are stored in the temporary directory. + # NOTE: Skip all output if we are running in "quiet" mode. # - catch {file delete [file join [getTemporaryPath] [file tail $fileName]]} + if {!$quiet} then { + tputs $channel "---- current memory in use by SQLite... " + } + + if {[catch {object invoke -flags +NonPublic \ + System.Data.SQLite.UnsafeNativeMethods \ + sqlite3_memory_used} memory] == 0} then { + if {!$quiet} then { + tputs $channel [appendArgs $memory " bytes\n"] + } + } else { + # + # NOTE: Maybe the SQLite native library is unavailable? + # + set memory unknown + + if {!$quiet} then { + tputs $channel [appendArgs $memory \n] + } + } + + set result $memory; # NOTE: Return memory in-use to caller. + + if {!$quiet} then { + tputs $channel "---- maximum memory in use by SQLite... " + } + + if {[catch {object invoke -flags +NonPublic \ + System.Data.SQLite.UnsafeNativeMethods \ + sqlite3_memory_highwater 0} memory] == 0} then { + if {!$quiet} then { + tputs $channel [appendArgs $memory " bytes\n"] + } + } else { + # + # NOTE: Maybe the SQLite native library is unavailable? + # + set memory unknown + + if {!$quiet} then { + tputs $channel [appendArgs $memory \n] + } + } + + if {$collect} then { + if {[catch {object invoke GC GetTotalMemory true} error]} then { + tputs $channel [appendArgs \ + "==== WARNING: failed full garbage collection, error: " \ + \n\t $error \n] + } + } + + if {!$quiet} then { + tputs $channel "---- current memory in use by the CLR... " + } + + if {[catch {object invoke GC GetTotalMemory false} memory] == 0} then { + if {[string is integer -strict $memory]} then { + if {!$quiet} then { + tputs $channel [appendArgs $memory " bytes\n"] + } + } else { + set memory invalid + + if {!$quiet} then { + tputs $channel [appendArgs $memory \n] + } + } + } else { + set memory unknown + + if {!$quiet} then { + tputs $channel [appendArgs $memory \n] + } + } + + return $result } proc runSQLiteTestPrologue {} { # # NOTE: Skip running our custom prologue if the main one has been skipped. @@ -386,19 +756,21 @@ if {![info exists ::no(sqliteFiles)]} then { # # NOTE: Skip trying to delete any files if we are so instructed. # if {![info exists ::no(deleteSqliteFiles)]} then { + tryDeleteAssembly sqlite3.dll tryDeleteAssembly SQLite.Interop.dll tryDeleteAssembly System.Data.SQLite.dll tryDeleteAssembly System.Data.SQLite.Linq.dll } # # NOTE: Skip trying to copy any files if we are so instructed. # if {![info exists ::no(copySqliteFiles)]} then { + tryCopyAssembly sqlite3.dll tryCopyAssembly SQLite.Interop.dll tryCopyAssembly System.Data.SQLite.dll tryCopyAssembly System.Data.SQLite.Linq.dll } @@ -444,29 +816,46 @@ # load without it; however, it cannot do anything useful without # it). If we are using the mixed-mode assembly and we already # found it (above), this should always succeed. # checkForSQLite $::test_channel + + # + # NOTE: Report the resource usage prior to running any tests. + # + reportSQLiteResources $::test_channel + + # + # NOTE: Show the active test constraints. + # + tputs $::test_channel [appendArgs "---- constraints: " \ + [formatList [lsort [getConstraints]]] \n] } } proc runSQLiteTestEpilogue {} { # # NOTE: Skip running our custom epilogue if the main one has been skipped. # if {![info exists ::no(epilogue.eagle)]} then { # - # NOTE: For now, nothing is done here. + # NOTE: Also report the resource usage after running the tests. # + reportSQLiteResources $::test_channel } } ########################################################################### ############################# END Eagle ONLY ############################## ########################################################################### } + # + # NOTE: Save the name of the directory containing this file. + # + set ::common_directory [file dirname [info script]] + # # NOTE: Provide the System.Data.SQLite test package to the interpreter. # package provide System.Data.SQLite.Test 1.0 } ADDED Tests/installer.eagle Index: Tests/installer.eagle ================================================================== --- /dev/null +++ Tests/installer.eagle @@ -0,0 +1,207 @@ +############################################################################### +# +# installer.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +# +# NOTE: Setup the variables that refer to the various files required by the +# tests in this file. +# +set systemDataSQLiteDllFile [getBuildFileName System.Data.SQLite.dll] +set installerExeFile [getBuildFileName Installer.exe] +set testInstallVs2008LogFile [file nativename [file join $path \ + Installer_Test_Vs2008.log]] +set testInstallVs2010LogFile [file nativename [file join $path \ + Installer_Test_Vs2010.log]] +set testUninstallVs2008LogFile [file nativename [file join $path \ + Uninstaller_Test_Vs2008.log]] +set testUninstallVs2010LogFile [file nativename [file join $path \ + Uninstaller_Test_Vs2010.log]] + +# +# NOTE: Setup the test constraints specific to the tests in this file. +# +if {![haveConstraint [appendArgs file_ \ + [file tail $installerExeFile]]]} then { + checkForFile $test_channel $installerExeFile +} + +if {![haveConstraint [appendArgs file_ \ + [file tail $testInstallVs2008LogFile]]]} then { + checkForFile $test_channel $testInstallVs2008LogFile +} + +if {![haveConstraint [appendArgs file_ \ + [file tail $testInstallVs2010LogFile]]]} then { + checkForFile $test_channel $testInstallVs2010LogFile +} + +if {![haveConstraint [appendArgs file_ \ + [file tail $testUninstallVs2008LogFile]]]} then { + checkForFile $test_channel $testUninstallVs2008LogFile +} + +if {![haveConstraint [appendArgs file_ \ + [file tail $testUninstallVs2010LogFile]]]} then { + checkForFile $test_channel $testUninstallVs2010LogFile +} + +############################################################################### + +runTest {test installer-1.1 {installer tool / Visual Studio 2008} -setup { + set fileName [file join [getTemporaryPath] [file tail [string map [list \ + .log [appendArgs _ [pid] .log]] $testInstallVs2008LogFile]]] + + cleanupFile $fileName +} -body { + set output "" + + set code [catch { + testClrExec $installerExeFile [list -eventflags Wait -stdout output \ + -success 0] -debugPriority Lowest -tracePriority MediumHigh \ + -noRuntimeVersion true -noCompact true -noNetFx40 true -noVs2010 true \ + -whatIf true -verbose true -confirm true -install true \ + -logFileName [appendArgs \" [file nativename $fileName] \"] \ + -traceFormat [appendArgs \" "#{0}: {2}" \"] + } error] + + tlog "---- BEGIN STDOUT OUTPUT\n" + tlog $output + tlog "\n---- END STDOUT OUTPUT\n" + + list $code [expr {$code == 0 ? [string equal [readFile $fileName] \ + [subst -nobackslashes [readFile $testInstallVs2008LogFile]]] : $error}] +} -cleanup { + cleanupFile $fileName + + unset -nocomplain code output error fileName +} -constraints {eagle administrator visualStudio2008\ +System.Data.SQLite.dll_v2.0.50727 file_Installer.exe\ +file_Installer_Test_Vs2008.log} -result {0 True}} + +############################################################################### + +runTest {test installer-1.2 {uninstaller tool / Visual Studio 2008} -setup { + set fileName [file join [getTemporaryPath] [file tail [string map [list \ + .log [appendArgs _ [pid] .log]] $testUninstallVs2008LogFile]]] + + cleanupFile $fileName +} -body { + set output "" + + set code [catch { + testClrExec $installerExeFile [list -eventflags Wait -stdout output \ + -success 0] -debugPriority Lowest -tracePriority MediumHigh \ + -noRuntimeVersion true -noCompact true -noNetFx40 true -noVs2010 true \ + -whatIf true -verbose true -confirm true -install false \ + -logFileName [appendArgs \" [file nativename $fileName] \"] \ + -traceFormat [appendArgs \" "#{0}: {2}" \"] + } error] + + tlog "---- BEGIN STDOUT OUTPUT\n" + tlog $output + tlog "\n---- END STDOUT OUTPUT\n" + + list $code [expr {$code == 0 ? [string equal [readFile $fileName] \ + [subst -nobackslashes [readFile $testUninstallVs2008LogFile]]] : $error}] +} -cleanup { + cleanupFile $fileName + + unset -nocomplain code output error fileName +} -constraints {eagle administrator visualStudio2008\ +System.Data.SQLite.dll_v2.0.50727 file_Installer.exe\ +file_Uninstaller_Test_Vs2008.log} -result {0 True}} + +############################################################################### + +runTest {test installer-1.3 {installer tool / Visual Studio 2010} -setup { + set fileName [file join [getTemporaryPath] [file tail [string map [list \ + .log [appendArgs _ [pid] .log]] $testInstallVs2010LogFile]]] + + cleanupFile $fileName +} -body { + set output "" + + set code [catch { + testClrExec $installerExeFile [list -eventflags Wait -stdout output \ + -success 0] -debugPriority Lowest -tracePriority MediumHigh \ + -noRuntimeVersion true -noCompact true -noNetFx20 true -noVs2008 true \ + -whatIf true -verbose true -confirm true -install true \ + -logFileName [appendArgs \" [file nativename $fileName] \"] \ + -traceFormat [appendArgs \" "#{0}: {2}" \"] + } error] + + tlog "---- BEGIN STDOUT OUTPUT\n" + tlog $output + tlog "\n---- END STDOUT OUTPUT\n" + + list $code [expr {$code == 0 ? [string equal [readFile $fileName] \ + [subst -nobackslashes [readFile $testInstallVs2010LogFile]]] : $error}] +} -cleanup { + cleanupFile $fileName + + unset -nocomplain code output error fileName +} -constraints {eagle administrator visualStudio2010\ +System.Data.SQLite.dll_v4.0.30319 file_Installer.exe\ +file_Installer_Test_Vs2010.log} -result {0 True}} + +############################################################################### + +runTest {test installer-1.4 {uninstaller tool / Visual Studio 2010} -setup { + set fileName [file join [getTemporaryPath] [file tail [string map [list \ + .log [appendArgs _ [pid] .log]] $testUninstallVs2010LogFile]]] + + cleanupFile $fileName +} -body { + set output "" + + set code [catch { + testClrExec $installerExeFile [list -eventflags Wait -stdout output \ + -success 0] -debugPriority Lowest -tracePriority MediumHigh \ + -noRuntimeVersion true -noCompact true -noNetFx20 true -noVs2008 true \ + -whatIf true -verbose true -confirm true -install false \ + -logFileName [appendArgs \" [file nativename $fileName] \"] \ + -traceFormat [appendArgs \" "#{0}: {2}" \"] + } error] + + tlog "---- BEGIN STDOUT OUTPUT\n" + tlog $output + tlog "\n---- END STDOUT OUTPUT\n" + + list $code [expr {$code == 0 ? [string equal [readFile $fileName] \ + [subst -nobackslashes [readFile $testUninstallVs2010LogFile]]] : $error}] +} -cleanup { + cleanupFile $fileName + + unset -nocomplain code output error fileName +} -constraints {eagle administrator visualStudio2010\ +System.Data.SQLite.dll_v4.0.30319 file_Installer.exe\ +file_Uninstaller_Test_Vs2010.log} -result {0 True}} + +############################################################################### + +unset -nocomplain testUninstallVs2010LogFile testUninstallVs2008LogFile \ + testInstallVs2010LogFile testInstallVs2008LogFile installerExeFile \ + systemDataSQLiteDllFile + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-00f86f9739.eagle ================================================================== --- Tests/tkt-00f86f9739.eagle +++ Tests/tkt-00f86f9739.eagle @@ -47,12 +47,12 @@ foreach value [list "" a b z 1+1 don notthere] { set output "" set code [catch { testClrExec $testLinqExeFile [list -eventflags Wait -directory \ - [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ - -success 0] -startsWith $value + [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ + -success 0] -startsWith $value } error] tlog "---- BEGIN STDOUT OUTPUT\n" tlog $output tlog "\n---- END STDOUT OUTPUT\n" Index: Tests/tkt-201128cc88.eagle ================================================================== --- Tests/tkt-201128cc88.eagle +++ Tests/tkt-201128cc88.eagle @@ -66,11 +66,11 @@ { SQLiteFunction.RegisterFunction(typeof(Test${id})); } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] # # NOTE: Compile the C# code (above) to register the custom SQLite function # and then open the database for this test case and attempt to execute # the function. Normally, we would open the database in the test setup @@ -91,11 +91,11 @@ unset -nocomplain result code results errors id db fileName } -constraints \ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}\ -SQLiteConnection#\d+ \{\} \{\} Zm9v$}} +System#Data#SQLite#SQLiteConnection#\d+ \{\} \{\} Zm9v$}} ############################################################################### runSQLiteTestEpilogue runTestEpilogue Index: Tests/tkt-2c630bffa7.eagle ================================================================== --- Tests/tkt-2c630bffa7.eagle +++ Tests/tkt-2c630bffa7.eagle @@ -33,11 +33,11 @@ sql execute $db \ "INSERT INTO t1 (x, y) VALUES($x, [lindex $y $x]);" } set result [sql execute -execute reader -format list $db \ - "SELECT x, y FROM t1 ORDER BY x;"] + "SELECT x, y FROM t1 ORDER BY x;"] } -cleanup { cleanupDb $fileName unset -nocomplain x result db fileName } -constraints \ @@ -55,11 +55,11 @@ sql execute $db \ "INSERT INTO t2 (x, y) VALUES($x, [lindex $y $x]);" } set result [sql execute -execute reader -format list $db \ - "SELECT x, y FROM t2 ORDER BY x;"] + "SELECT x, y FROM t2 ORDER BY x;"] } -cleanup { cleanupDb $fileName unset -nocomplain x result db fileName } -constraints \ @@ -77,11 +77,11 @@ sql execute $db \ "INSERT INTO t3 (x, y) VALUES($x, [lindex $y $x]);" } set result [sql execute -execute reader -format list $db \ - "SELECT x, y FROM t3 ORDER BY x;"] + "SELECT x, y FROM t3 ORDER BY x;"] } -cleanup { cleanupDb $fileName unset -nocomplain x result db fileName } -constraints \ ADDED Tests/tkt-2ce0870fad.eagle Index: Tests/tkt-2ce0870fad.eagle ================================================================== --- /dev/null +++ Tests/tkt-2ce0870fad.eagle @@ -0,0 +1,98 @@ +############################################################################### +# +# tkt-2ce0870fad.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +# +# NOTE: Make sure that SQLite core library is completely shutdown prior to +# starting any of the tests in this file. +# +if {[haveConstraint SQLite]} then { + object invoke -flags +NonPublic System.Data.SQLite.UnsafeNativeMethods \ + sqlite3_shutdown +} + +############################################################################### + +for {set i 1} {$i < 3} {incr i} { + runTest {test [appendArgs tkt-2ce0870fad-1. $i] {logging setup} -setup \ + [getAppDomainPreamble { + set i {$i} + set appDomainId($i) {[object invoke AppDomain.CurrentDomain Id]} + set fileName {[appendArgs tkt-2ce0870fad-1. $i .db]} + + # + # NOTE: Keep track of whether or not the global test year and configuration + # variables already exist in the primary application domain before the + # test. If not, we will need to unset them after the test. + # + set hadTestYear {[info exists ::test_year]} + set hadTestConfiguration {[info exists ::test_configuration]} + }] -body { + set appDomainId(3) [object invoke AppDomain.CurrentDomain Id] + + package require EagleLibrary + package require EagleTest + package require System.Data.SQLite.Test + + set assembly [object load -loadtype File [file join [getBinaryDirectory] \ + System.Data.SQLite.dll]] + + object invoke System.Data.SQLite.SQLiteLog Initialize + + list $appDomainId($i) $appDomainId(3) [expr {$i == 1 ? \ + $appDomainId($i) != $appDomainId(3) : \ + $appDomainId($i) == $appDomainId(3)}] [setupDb $fileName] + } -cleanup { + cleanupDb $fileName + + if {!$hadTestConfiguration} then { + unset -nocomplain ::test_configuration + } + + if {!$hadTestYear} then { + unset -nocomplain ::test_year + } + + # + # NOTE: If this is the primary application domain, skip unsetting the + # loop variable because the surrounding [for] command still needs + # it. + # + if {$i <= 1} then { + unset -nocomplain i + } + + unset -nocomplain assembly appDomainId db fileName hadTestConfiguration \ + hadTestYear + } -constraints {eagle monoBug28 command.sql compile.DATA\ +compile.ISOLATED_INTERPRETERS SQLite System.Data.SQLite} -isolationLevel \ +[expr {$i == 1 ? "AppDomain" : "Default"}] -match regexp -result \ +{^\d+ \d+ True System#Data#SQLite#SQLiteConnection#\d+$}} +} + +############################################################################### + +unset -nocomplain i + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue ADDED Tests/tkt-343d392b51.eagle Index: Tests/tkt-343d392b51.eagle ================================================================== --- /dev/null +++ Tests/tkt-343d392b51.eagle @@ -0,0 +1,440 @@ +############################################################################### +# +# tkt-343d392b51.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +set dateTimeFormats [list "" Ticks ISO8601 JulianDay UnixEpoch] + +for {set i 1} {$i < 5} {incr i} { + set dateTimeFormat [lindex $dateTimeFormats $i] + + runTest {test [appendArgs tkt-343d392b51-1. $i] [subst {DateTime\ + binding $dateTimeFormat format}] -setup { + setupDb [set fileName [appendArgs tkt-343d392b51-1. $i .db]] "" \ + $dateTimeFormat Utc + + set dateTime "4 October, 2011 3:27:50 PM GMT" + } -body { + sql execute $db "CREATE TABLE t1(x DATETIME);" + + set paramDateTime1 [clock format [clock scan $dateTime] -format \ + [getDateTimeFormat] -gmt true] + + switch -exact -- $dateTimeFormat { + Ticks { + set paramDateTime1 [object invoke -alias DateTime Parse $paramDateTime1] + set paramDateTime1 [$paramDateTime1 ToUniversalTime.Ticks] + set paramDateTime2 $paramDateTime1 + } + ISO8601 { + set paramDateTime2 [appendArgs ' $paramDateTime1 '] + } + JulianDay { + set paramDateTime1 [object invoke -alias DateTime Parse $paramDateTime1] + set paramDateTime1 [$paramDateTime1 -alias ToUniversalTime] + + set paramDateTime1 [expr {[$paramDateTime1 ToOADate] + \ + [object invoke -flags +NonPublic System.Data.SQLite.SQLiteConvert \ + OleAutomationEpochAsJulianDay]}] + + set paramDateTime2 $paramDateTime1 + } + UnixEpoch { + set paramDateTime1 [clock scan $dateTime] + set paramDateTime2 $paramDateTime1 + } + } + + sql execute $db [appendArgs "INSERT INTO t1 (x) VALUES(" $paramDateTime2 \ + ");"] + + list [sql execute -verbatim -execute reader -format list -datetimeformat \ + [getDateTimeFormat] $db "SELECT x FROM t1 WHERE x = ?;" \ + [list param1 String $paramDateTime1]] \ + [sql execute -verbatim -execute reader -format list -datetimeformat \ + [getDateTimeFormat] $db "SELECT x FROM t1 WHERE x = ?;" \ + [list param1 DateTime $paramDateTime1]] + } -cleanup { + cleanupDb $fileName + + unset -nocomplain paramDateTime2 paramDateTime1 dateTime db fileName + } -constraints \ +{eagle threadCulture.en_US monoBug28 command.sql compile.DATA SQLite\ +System.Data.SQLite} -result {{{2011-10-04 15:27:50Z}} {{2011-10-04 15:27:50Z}}}} +} + +############################################################################### + +unset -nocomplain dateTimeFormat i dateTimeFormats + +############################################################################### + +runTest {test tkt-343d392b51-2.1 {SQLiteDataAdapter update fail} -setup { + setupDb [set fileName tkt-343d392b51-2.1.db] + set otherFileName tkt-343d392b51-2.1-otherDb.db +} -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + set otherDataSource [file join [getDatabaseDirectory] $otherFileName] + set otherDbName otherDb + set otherTable [appendArgs $otherDbName .t1] + + set sql(inserts) "" + set sql(1) [subst { \ + ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \ + CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY, y DATETIME); \ + [for {set i 1} {$i < 3} {incr i} { + append sql(inserts) [appendArgs \ + "INSERT INTO " ${otherTable} " (x, y) VALUES(" $i ", '" \ + [clock format $i -format [getDateTimeFormat]] "'); "] + }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \ + }] + + set sql(2) [subst { \ + SELECT x, y FROM ${otherTable} ORDER BY x; \ + }] + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data; + using System.Data.SQLite; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static void Main() + { + using (SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};")) + { + connection.Open(); + + using (SQLiteCommand command = connection.CreateCommand()) + { + command.CommandText = "${sql(1)}"; + command.ExecuteNonQuery(); + } + + using (SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter( + "${sql(2)}", connection)) + { + using (DataSet dataSet = new DataSet()) + { + dataAdapter.Fill(dataSet, "${otherTable}"); + + DataTable dataTable = dataSet.Tables\["${otherTable}"\]; + + dataTable.Columns\["x"\].Unique = true; + dataTable.PrimaryKey = new DataColumn\[\] { + dataTable.Columns\["x"\] + }; + + [expr {[isMono] ? "#pragma warning disable 219" : ""}] + SQLiteCommandBuilder commandBuilder = + new SQLiteCommandBuilder(dataAdapter); + [expr {[isMono] ? "#pragma warning restore 219" : ""}] + + foreach (DataRow dataRow in dataTable.Rows) + { + // + // NOTE: Update even rows and delete odd rows. + // + if ((long)dataRow\["x"\] % 2 == 0) + dataRow\["y"\] = + DateTime.UtcNow.ToString("[getDateTimeFormat]"); + else + dataRow.Delete(); + } + + dataAdapter.Update(dataTable); // DBConcurrencyException (?) + } + } + } + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result +} -cleanup { + cleanupDb $otherFileName + cleanupDb $fileName + + unset -nocomplain result code results errors i sql otherTable otherDbName \ + otherDataSource dataSource id db otherFileName fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ +glob -result {* System.Data.DBConcurrencyException: *}} + +############################################################################### + +runTest {test tkt-343d392b51-2.2 {SQLiteDataAdapter update success} -setup { + setupDb [set fileName tkt-343d392b51-2.2.db] "" JulianDay + set otherFileName tkt-343d392b51-2.2-otherDb.db +} -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + set otherDataSource [file join [getDatabaseDirectory] $otherFileName] + set otherDbName otherDb + set otherTable [appendArgs $otherDbName .t1] + + set sql(inserts) "" + set sql(1) [subst { \ + ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \ + CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY, y DATETIME); \ + [for {set i 1} {$i < 3} {incr i} { + append sql(inserts) [appendArgs \ + "INSERT INTO " ${otherTable} " (x, y) VALUES(" $i ", JULIANDAY('" \ + [clock format $i -format [getDateTimeFormat]] "')); "] + }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \ + }] + + set sql(2) [subst { \ + SELECT x, y FROM ${otherTable} ORDER BY x; \ + }] + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data; + using System.Data.SQLite; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static void Main() + { + using (SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};DateTimeFormat=JulianDay;")) + { + connection.Open(); + + using (SQLiteCommand command = connection.CreateCommand()) + { + command.CommandText = "${sql(1)}"; + command.ExecuteNonQuery(); + } + + using (SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter( + "${sql(2)}", connection)) + { + using (DataSet dataSet = new DataSet()) + { + dataAdapter.Fill(dataSet, "${otherTable}"); + + DataTable dataTable = dataSet.Tables\["${otherTable}"\]; + + dataTable.Columns\["x"\].Unique = true; + dataTable.PrimaryKey = new DataColumn\[\] { + dataTable.Columns\["x"\] + }; + + [expr {[isMono] ? "#pragma warning disable 219" : ""}] + SQLiteCommandBuilder commandBuilder = + new SQLiteCommandBuilder(dataAdapter); + [expr {[isMono] ? "#pragma warning restore 219" : ""}] + + foreach (DataRow dataRow in dataTable.Rows) + { + // + // NOTE: Update even rows and delete odd rows. + // + if ((long)dataRow\["x"\] % 2 == 0) + dataRow\["y"\] = + DateTime.UtcNow.ToString("[getDateTimeFormat]"); + else + dataRow.Delete(); + } + + dataAdapter.Update(dataTable); // DBConcurrencyException (?) + } + } + } + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result +} -cleanup { + cleanupDb $otherFileName + cleanupDb $fileName + + unset -nocomplain result code results errors i sql otherTable otherDbName \ + otherDataSource dataSource id db otherFileName fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ +regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}} + +############################################################################### + +runTest {test tkt-343d392b51-3.1 {attached database, same table name} -setup { + setupDb [set fileName tkt-343d392b51-3.1.db] + set otherFileName tkt-343d392b51-3.1-otherDb.db +} -body { + set otherDataSource [file join [getDatabaseDirectory] $otherFileName] + set otherDbName otherDb + set otherTable [appendArgs $otherDbName .t1] + + set sql(inserts) "" + set sql(1) [subst { \ + CREATE TABLE t1(x INTEGER PRIMARY KEY); \ + ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \ + CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY); \ + [for {set i 1} {$i < 3} {incr i} { + append sql(inserts) [appendArgs \ + "INSERT INTO t1 (x) VALUES(" $i "); "] + + append sql(inserts) [appendArgs \ + "INSERT INTO " ${otherTable} " (x) VALUES(" [expr {$i * 2}] "); "] + }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \ + }] + + sql execute $db $sql(1) + + list [sql execute -execute reader -format list $db "SELECT x FROM t1;"] \ + [sql execute -execute reader -format list $db [appendArgs \ + "SELECT x FROM " ${otherTable} ";"]] +} -cleanup { + cleanupDb $otherFileName + cleanupDb $fileName + + unset -nocomplain i sql otherTable otherDbName otherDataSource db \ + otherFileName fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{{1 2} {2 4}}} + +############################################################################### + +runTest {test tkt-343d392b51-3.2 {adapter, attached db, table names} -setup { + setupDb [set fileName tkt-343d392b51-3.2.db] + set otherFileName tkt-343d392b51-3.2-otherDb.db +} -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + set otherDataSource [file join [getDatabaseDirectory] $otherFileName] + set otherDbName otherDb + set otherTable [appendArgs $otherDbName .t1] + + set sql(inserts) "" + set sql(1) [subst { \ + CREATE TABLE t1(x INTEGER PRIMARY KEY); \ + ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \ + CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY); \ + [for {set i 1} {$i < 3} {incr i} { + append sql(inserts) [appendArgs \ + "INSERT INTO t1 (x) VALUES(" $i ");"] + append sql(inserts) [appendArgs \ + "INSERT INTO " ${otherTable} " (x) VALUES(" [expr {$i * 2}] "); "] + }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \ + }] + + set sql(2) [subst { \ + SELECT x FROM ${otherTable} ORDER BY x; \ + }] + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data; + using System.Data.SQLite; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static void Main() + { + using (SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};")) + { + connection.Open(); + + using (SQLiteCommand command = connection.CreateCommand()) + { + command.CommandText = "${sql(1)}"; + command.ExecuteNonQuery(); + } + + using (SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter( + "${sql(2)}", connection)) + { + using (DataSet dataSet = new DataSet()) + { + dataAdapter.Fill(dataSet, "${otherTable}"); + + DataTable dataTable = dataSet.Tables\["${otherTable}"\]; + + dataTable.Columns\["x"\].Unique = true; + dataTable.PrimaryKey = new DataColumn\[\] { + dataTable.Columns\["x"\] + }; + + [expr {[isMono] ? "#pragma warning disable 219" : ""}] + SQLiteCommandBuilder commandBuilder = + new SQLiteCommandBuilder(dataAdapter); + [expr {[isMono] ? "#pragma warning restore 219" : ""}] + + foreach (DataRow dataRow in dataTable.Rows) + dataRow.Delete(); + + dataAdapter.Update(dataTable); // DBConcurrencyException (?) + } + } + } + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result +} -cleanup { + cleanupDb $otherFileName + cleanupDb $fileName + + unset -nocomplain result code results errors i sql otherTable otherDbName \ + otherDataSource dataSource id db otherFileName fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ +regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-448d663d11.eagle ================================================================== --- Tests/tkt-448d663d11.eagle +++ Tests/tkt-448d663d11.eagle @@ -33,12 +33,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.2 {missing journal mode, WAL db} -body { set fileName tkt-448d663d11-1.2.db file copy -force [file join $path wal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName "" false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName "" "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -48,12 +48,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.3 {missing journal mode, non-WAL db} -body { set fileName tkt-448d663d11-1.3.db file copy -force [file join $path nonWal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName "" false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName "" "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -75,12 +75,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.5 {'Default' journal mode, WAL db} -body { set fileName tkt-448d663d11-1.5.db file copy -force [file join $path wal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Default false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Default "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -90,12 +90,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.6 {'Default' journal mode, non-WAL db} -body { set fileName tkt-448d663d11-1.6.db file copy -force [file join $path nonWal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Default false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Default "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -117,12 +117,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.8 {'Delete' journal mode, WAL db} -body { set fileName tkt-448d663d11-1.8.db file copy -force [file join $path wal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Delete false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Delete "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -132,12 +132,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.9 {'Delete' journal mode, non-WAL db} -body { set fileName tkt-448d663d11-1.9.db file copy -force [file join $path nonWal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Delete false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Delete "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -207,12 +207,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.15 {'Wal' journal mode, non-WAL db} -body { set fileName tkt-448d663d11-1.15.db file copy -force [file join $path nonWal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Wal false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Wal "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -222,12 +222,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.16 {'Wal' journal mode, WAL db} -body { set fileName tkt-448d663d11-1.16.db file copy -force [file join $path wal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Wal false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Wal "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -249,12 +249,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.18 {'Bad' journal mode, non-WAL db} -body { set fileName tkt-448d663d11-1.18.db file copy -force [file join $path nonWal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Bad false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Bad "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ @@ -264,12 +264,12 @@ ############################################################################### runTest {test tkt-448d663d11-1.19 {'Bad' journal mode, WAL db} -body { set fileName tkt-448d663d11-1.19.db file copy -force [file join $path wal.db] \ - [file join [getTemporaryPath] $fileName] - setupDb $fileName Bad false + [file join [getDatabaseDirectory] $fileName] + setupDb $fileName Bad "" "" "" "" false sql execute -execute scalar $db "PRAGMA journal_mode;" } -cleanup { cleanupDb $fileName unset -nocomplain db fileName } -constraints \ ADDED Tests/tkt-544dba0a2f.eagle Index: Tests/tkt-544dba0a2f.eagle ================================================================== --- /dev/null +++ Tests/tkt-544dba0a2f.eagle @@ -0,0 +1,45 @@ +############################################################################### +# +# tkt-544dba0a2f.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-544dba0a2f-1.1 {BOOL versus BOOLEAN} -setup { + setupDb [set fileName tkt-544dba0a2f-1.1.db] +} -body { + sql execute $db "CREATE TABLE t1(x BOOL, y BOOLEAN);" + sql execute $db "INSERT INTO t1 (x, y) VALUES(0, 0);" + sql execute $db "INSERT INTO t1 (x, y) VALUES(0, 1);" + sql execute $db "INSERT INTO t1 (x, y) VALUES(1, 0);" + sql execute $db "INSERT INTO t1 (x, y) VALUES(1, 1);" + + sql execute -execute reader -format list $db \ + "SELECT x, y FROM t1 ORDER BY rowid;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{False False False True True False True True}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-59edc1018b.eagle ================================================================== --- Tests/tkt-59edc1018b.eagle +++ Tests/tkt-59edc1018b.eagle @@ -47,12 +47,12 @@ foreach value [list "" a b z 1+1 don notthere] { set output "" set code [catch { testClrExec $testLinqExeFile [list -eventflags Wait -directory \ - [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ - -success 0] -endsWith $value + [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ + -success 0] -endsWith $value } error] tlog "---- BEGIN STDOUT OUTPUT\n" tlog $output tlog "\n---- END STDOUT OUTPUT\n" ADDED Tests/tkt-7e3fa93744.eagle Index: Tests/tkt-7e3fa93744.eagle ================================================================== --- /dev/null +++ Tests/tkt-7e3fa93744.eagle @@ -0,0 +1,138 @@ +############################################################################### +# +# tkt-7e3fa93744.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-7e3fa93744-1.1 {composite primary key, baseline} -setup { + setupDb [set fileName tkt-7e3fa93744-1.1.db] +} -body { + set sql { + CREATE TABLE t1 ( + id1 INTEGER PRIMARY KEY + ); + + CREATE TABLE t2 ( + id1 INTEGER NOT NULL, + id2 INTEGER NOT NULL, + PRIMARY KEY (id1, id2) + ); + + INSERT INTO t1 (id1) VALUES (1); + INSERT INTO t1 (id1) VALUES (2); + + INSERT INTO t2 (id1, id2) VALUES (1, 1); + INSERT INTO t2 (id1, id2) VALUES (1, 2); + INSERT INTO t2 (id1, id2) VALUES (2, 1); + INSERT INTO t2 (id1, id2) VALUES (2, 2); + + SELECT t1.id1, t2.id1, t2.id2 + FROM t1, t2 + ORDER BY t1.id1, t2.id1, t2.id2; + } + + sql execute -execute reader -format list $db $sql +} -cleanup { + cleanupDb $fileName + + unset -nocomplain sql db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{1 1 1 1 1 2 1 2 1 1 2 2 2 1 1 2 1 2 2 2 1 2 2 2}} + +############################################################################### + +runTest {test tkt-7e3fa93744-1.2 {composite primary key, DataTable} -setup { + setupDb [set fileName tkt-7e3fa93744-1.2.db] +} -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + + set sql { \ + CREATE TABLE t1 ( \ + id1 INTEGER PRIMARY KEY NOT NULL \ + ); \ + CREATE TABLE t2 ( \ + id1 INTEGER NOT NULL, \ + id2 INTEGER NOT NULL, \ + PRIMARY KEY (id1, id2) \ + ); \ + INSERT INTO t1 (id1) VALUES (1); \ + INSERT INTO t1 (id1) VALUES (2); \ + INSERT INTO t2 (id1, id2) VALUES (1, 1); \ + INSERT INTO t2 (id1, id2) VALUES (1, 2); \ + INSERT INTO t2 (id1, id2) VALUES (2, 1); \ + INSERT INTO t2 (id1, id2) VALUES (2, 2); \ + SELECT t1.id1, t2.id1, t2.id2 \ + FROM t1, t2 \ + ORDER BY t1.id1, t2.id1, t2.id2; \ + } + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System.Data; + using System.Data.SQLite; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static int Main() + { + using (SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};")) + { + connection.Open(); + + using (SQLiteCommand command = connection.CreateCommand()) + { + command.CommandText = "${sql}"; + + using (SQLiteDataReader dataReader = command.ExecuteReader()) + { + DataTable dataTable = new DataTable(); + dataTable.Load(dataReader); + + return dataTable.Rows.Count; + } + } + } + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result +} -cleanup { + cleanupDb $fileName + + unset -nocomplain result code results errors sql dataSource id db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ +regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 8$}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue ADDED Tests/tkt-84718e79fa.eagle Index: Tests/tkt-84718e79fa.eagle ================================================================== --- /dev/null +++ Tests/tkt-84718e79fa.eagle @@ -0,0 +1,84 @@ +############################################################################### +# +# tkt-84718e79fa.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +set c 10 + +############################################################################### + +runTest {test tkt-84718e79fa-1.1 {SQLiteConvert thread safety} -setup { + proc threadStart { args } { + lappend ::results [sql execute -execute reader -format list $::db \ + "SELECT x FROM t1;"] + } + + object import System.Threading + + setupDb [set fileName tkt-84718e79fa-1.1.db] +} -body { + sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC);" + sql execute $db "INSERT INTO t1 (x) VALUES(1);" + + for {set i 0} {$i < $c} {incr i} { + set t($i) [object create -alias Thread threadStart] + } + + set ::results [list] + + for {set i 0} {$i < $c} {incr i} { + $t($i) Start + } + + after 4000; # wait for other threads to do something... + + for {set i 0} {$i < $c} {incr i} { + $t($i) Join + } + + set ::results +} -cleanup { + cleanupDb $fileName + + object unimport -importpattern System.Threading + + for {set i 0} {$i < $c} {incr i} { + if {[info exists t($i)] && [cleanupThread $t($i)]} then { + unset t($i) + } + } + + catch {object removecallback threadStart} + + unset -nocomplain results t i c db fileName + + rename threadStart "" +} -constraints \ +{eagle shell monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ +-result [lrepeat $c 1]} + +############################################################################### + +unset -nocomplain c + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-8554170e09.eagle ================================================================== --- Tests/tkt-8554170e09.eagle +++ Tests/tkt-8554170e09.eagle @@ -25,11 +25,11 @@ } -body { sql execute $db "CREATE TABLE t1(x INTEGER, y DATETIME);" sql execute $db "INSERT INTO t1 (x, y) VALUES(1, NULL);" set result [sql execute -execute reader -format list $db \ - "SELECT x, y FROM t1 ORDER BY x;"] + "SELECT x, y FROM t1 ORDER BY x;"] } -cleanup { cleanupDb $fileName unset -nocomplain result db fileName } -constraints \ @@ -43,11 +43,11 @@ } -body { sql execute $db "CREATE TABLE t1(x INTEGER, y DATETIME);" sql execute $db "INSERT INTO t1 (x, y) VALUES(1, '');" set result [sql execute -execute reader -format list $db \ - "SELECT x, y FROM t1 ORDER BY x;"] + "SELECT x, y FROM t1 ORDER BY x;"] } -cleanup { cleanupDb $fileName unset -nocomplain result db fileName } -constraints \ Index: Tests/tkt-8b7d179c3c.eagle ================================================================== --- Tests/tkt-8b7d179c3c.eagle +++ Tests/tkt-8b7d179c3c.eagle @@ -47,12 +47,12 @@ for {set pageSize 0} {$pageSize <= 2} {incr pageSize} { set output "" set code [catch { testClrExec $testLinqExeFile [list -eventflags Wait -directory \ - [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ - -success 0] -skip $pageSize + [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ + -success 0] -skip $pageSize } error] tlog "---- BEGIN STDOUT OUTPUT\n" tlog $output tlog "\n---- END STDOUT OUTPUT\n" Index: Tests/tkt-b4a7ddc83f.eagle ================================================================== --- Tests/tkt-b4a7ddc83f.eagle +++ Tests/tkt-b4a7ddc83f.eagle @@ -30,14 +30,14 @@ } ############################################################################### for {set i 1} {$i < 3} {incr i} { - runTest {test tkt-b4a7ddc83f-1.$i {logging shutdown} -setup \ + runTest {test [appendArgs tkt-b4a7ddc83f-1. $i] {logging shutdown} -setup \ [getAppDomainPreamble { set appDomainId(1) {[object invoke AppDomain.CurrentDomain Id]} - set fileName {tkt-b4a7ddc83f-1.$i.db} + set fileName {[appendArgs tkt-b4a7ddc83f-1. $i .db]} }] -body { set appDomainId(2) [object invoke AppDomain.CurrentDomain Id] package require EagleLibrary package require EagleTest @@ -53,11 +53,12 @@ } -cleanup { cleanupDb $fileName unset -nocomplain appDomainId db fileName } -constraints {eagle monoBug28 command.sql compile.DATA\ compile.ISOLATED_INTERPRETERS SQLite System.Data.SQLite} -isolationLevel \ -AppDomain -match regexp -result {^\d+ \d+ True SQLiteConnection#\d+$}} +AppDomain -match regexp -result {^\d+ \d+ True\ +System#Data#SQLite#SQLiteConnection#\d+$}} } ############################################################################### unset -nocomplain i ADDED Tests/tkt-bb4b04d457.eagle Index: Tests/tkt-bb4b04d457.eagle ================================================================== --- /dev/null +++ Tests/tkt-bb4b04d457.eagle @@ -0,0 +1,47 @@ +############################################################################### +# +# tkt-bb4b04d457.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-bb4b04d457-1.1 {} -setup { + setupDb [set fileName tkt-bb4b04d457-1.1.db] "" Ticks Utc +} -body { + set dateTime [object invoke -alias DateTime Parse 2011-11-29T12:34:56Z] + set dateTime [$dateTime -alias ToUniversalTime] + + sql execute $db "CREATE TABLE t1(x TIMESTAMP NOT NULL);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 Int64 [$dateTime Ticks]] + + sql execute -execute reader -format list -datetimeformat \ + [getDateTimeFormat] $db "SELECT x FROM t1 ORDER BY rowid;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain dateTime db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{{2011-11-29 12:34:56Z}}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-ccfa69fc32.eagle ================================================================== --- Tests/tkt-ccfa69fc32.eagle +++ Tests/tkt-ccfa69fc32.eagle @@ -47,12 +47,12 @@ foreach add [list false true false] { set output "" set code [catch { testClrExec $testLinqExeFile [list -eventflags Wait -directory \ - [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ - -success 0] -efTransaction $add + [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \ + -success 0] -efTransaction $add } error] tlog "---- BEGIN STDOUT OUTPUT\n" tlog $output tlog "\n---- END STDOUT OUTPUT\n" @@ -68,12 +68,12 @@ set result } -cleanup { unset -nocomplain code output error result add } -constraints {eagle monoToDo file_testlinq.exe file_northwindEF.db} -match \ -glob -result {0 {1581 1730 1833 2116 2139} 0 {System.Data.UpdateException: * --->\ -System.Data.SQLite.SQLiteException: Abort due to constraint violation +glob -result {0 {1581 1730 1833 2116 2139} 0 {System.Data.UpdateException: *\ +---> System.Data.SQLite.SQLiteException: Abort due to constraint violation PRIMARY KEY must be unique *} 0 {1 2 3 4 5 6 7 8 9 10 1576 1577 1578 1579 1580 1581 1730 1833 2116 2139}}} ############################################################################### Index: Tests/tkt-e1b2e0f769.eagle ================================================================== --- Tests/tkt-e1b2e0f769.eagle +++ Tests/tkt-e1b2e0f769.eagle @@ -29,11 +29,11 @@ foreach x [list 1 2 3] { sql execute $db "INSERT INTO t1 (x) VALUES($x);" } set result1 [list] - set dataSource [file join [getTemporaryPath] tkt-e1b2e0f769-1.1.db] + set dataSource [file join [getDatabaseDirectory] $fileName] foreach table [list t1 t2] { set id [object invoke Interpreter.GetActive NextId] set sql "SELECT x FROM $table ORDER BY x;" @@ -101,11 +101,11 @@ return Tkt_e1b2e0f769(connection).Count; } } } } - }] results errors System.Data.SQLite.dll] + }] true true true results errors System.Data.SQLite.dll] lappend result1 $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} Main ADDED Tests/tkt-e30b820248.eagle Index: Tests/tkt-e30b820248.eagle ================================================================== --- /dev/null +++ Tests/tkt-e30b820248.eagle @@ -0,0 +1,275 @@ +############################################################################### +# +# tkt-e30b820248.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require EagleLibrary +package require EagleTest + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +set memory_used [reportSQLiteResources $test_channel true] + +############################################################################### + +runTest {test tkt-e30b820248-1.1 {disposal ordering} -setup { + set fileName tkt-e30b820248-1.1.db +} -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + set name [file rootname [file tail $fileName]] + + set sql { \ + CREATE TABLE t1 (id1 INTEGER); \ + INSERT INTO t1 (id1) VALUES (1); \ + INSERT INTO t1 (id1) VALUES (2); \ + INSERT INTO t1 (id1) VALUES (?); \ + INSERT INTO t1 (id1) VALUES (?); \ + INSERT INTO t1 (id1) VALUES (?); \ + SELECT * FROM t1 ORDER BY id1; \ + } + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System.Data.SQLite; + using System.Diagnostics; + using System.IO; + + namespace _Dynamic${id} + { + public class Test${id} + { + public static void Main() + { + using (TraceListener listener = new TextWriterTraceListener( + new FileStream("${test_log}", FileMode.Append, + FileAccess.Write, FileShare.ReadWrite), "${name}")) + { + Trace.Listeners.Add(listener); + Trace.WriteLine("---- START TRACE \\"${name}\\""); + + using (SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};")) + { + connection.Open(); + connection.LogMessage(0, "Connection opened."); + + using (SQLiteTransaction transaction = + connection.BeginTransaction()) + { + connection.LogMessage(0, "Transaction started."); + + using (SQLiteCommand command = connection.CreateCommand()) + { + command.Transaction = transaction; + command.CommandText = "${sql}"; + + command.Parameters.AddWithValue("x", 3); + command.Parameters.AddWithValue("y", 4); + command.Parameters.AddWithValue("z", 5); + + command.ExecuteNonQuery(); + connection.LogMessage(0, "Command executed."); + } + + transaction.Commit(); + connection.LogMessage(0, "Transaction committed."); + } + } + + Trace.WriteLine("---- END TRACE \\"${name}\\""); + Trace.Listeners.Remove(listener); + } + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result \ + [reportSQLiteResources $test_channel true] +} -cleanup { + cleanupDb $fileName + + unset -nocomplain result code results errors sql dataSource id db fileName +} -constraints \ +{eagle logFile monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ +-match regexp -result [appendArgs "^Ok\ +System#CodeDom#Compiler#CompilerResults#\\d+ \\{\\} 0 \\{\\} " $memory_used \$]} + +############################################################################### + +for {set i 2} {$i < 5} {incr i} { + set memory_used [reportSQLiteResources $test_channel true] + + ############################################################################# + + runTest {test [appendArgs tkt-e30b820248-1. $i] {disposal ordering} -setup { + set fileName [appendArgs tkt-e30b820248-1. $i .db] + } -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + set name [file rootname [file tail $fileName]] + + set sql { \ + CREATE TABLE t1 (id1 INTEGER); \ + INSERT INTO t1 (id1) VALUES (1); \ + INSERT INTO t1 (id1) VALUES (2); \ + INSERT INTO t1 (id1) VALUES (3); \ + INSERT INTO t1 (id1) VALUES (4); \ + INSERT INTO t1 (id1) VALUES (5); \ + SELECT * FROM t1 ORDER BY id1; \ + } + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data.SQLite; + using System.Diagnostics; + using System.IO; + + namespace _Dynamic${id} + { + public class Test${id} + { + #region Private Static Data + private static SQLiteConnection connection; + #endregion + + ///////////////////////////////////////////////////////////////////// + + #region Public Static Methods + public static void OpenConnection() + { + connection = new SQLiteConnection("Data Source=${dataSource};"); + connection.Open(); + connection.LogMessage(0, "Connection opened."); + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteCommand CreateCommand(string sql) + { + SQLiteCommand command = connection.CreateCommand(); + command.CommandText = sql; + connection.LogMessage(0, "Command created."); + return command; + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteDataReader ExecuteReader(SQLiteCommand command) + { + SQLiteDataReader dataReader = command.ExecuteReader(); + connection.LogMessage(0, "Command executed."); + return dataReader; + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteDataReader ExecuteReader(string sql) + { + SQLiteCommand command = CreateCommand(sql); + SQLiteDataReader dataReader = command.ExecuteReader(); + connection.LogMessage(0, "Command executed."); + return dataReader; + } + + ///////////////////////////////////////////////////////////////////// + + public static void CloseConnection() + { + connection.LogMessage(0, "Closing connection..."); + connection.Close(); + } + #endregion + + ///////////////////////////////////////////////////////////////////// + + public static void Main() + { + using (TraceListener listener = new TextWriterTraceListener( + new FileStream("${test_log}", FileMode.Append, + FileAccess.Write, FileShare.ReadWrite), "${name}")) + { + Trace.Listeners.Add(listener); + Trace.WriteLine("---- START TRACE \\"${name}\\""); + + OpenConnection(); + SQLiteDataReader dataReader = ExecuteReader("${sql}"); + + [expr {$i <= 2 ? { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } : ""}] + + dataReader.Close(); + + [expr {$i <= 3 ? { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } : ""}] + + CloseConnection(); + + [expr {$i <= 4 ? { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } : ""}] + + Trace.WriteLine("---- END TRACE \\"${name}\\""); + Trace.Listeners.Remove(listener); + } + } + } + } + }] true true true results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result \ + [reportSQLiteResources $test_channel true] + } -cleanup { + cleanupDb $fileName + + unset -nocomplain result code results errors sql name dataSource id db \ + fileName + } -constraints {eagle logFile monoBug28 command.sql compile.DATA SQLite\ +System.Data.SQLite} -match regexp -result [appendArgs "^Ok\ +System#CodeDom#Compiler#CompilerResults#\\d+ \\{\\} 0 \\{\\} " $memory_used \$]} +} + +############################################################################### + +unset -nocomplain i + +############################################################################### + +unset -nocomplain memory_used + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/version.eagle ================================================================== --- Tests/version.eagle +++ Tests/version.eagle @@ -27,11 +27,11 @@ # numbers must be manually kept synchronized with the version numbers for # the source code files, the built binaries, and the release packages. # set version(major) 1 set version(minor) 0 -set version(build) 76; # NOTE: Incremented with each release. +set version(build) 78; # NOTE: Incremented with each release. set version(revision) 0 ############################################################################### # ********************* END VOLATILE VERSION INFORMATION ********************** ############################################################################### @@ -81,29 +81,65 @@ file version $systemDataSQLiteDllFile } -constraints {eagle file_System.Data.SQLite.dll} -result $version(full)} ############################################################################### -runTest {test version-1.2 {'System.Data.SQLite.Linq' binary version} -body { +runTest {test version-1.2 {'System.Data.SQLite' assembly version} -body { + set assembly [object load System.Data.SQLite] + + foreach assembly [object assemblies] { + if {[string match System.Data.SQLite,* $assembly]} then { + return [regexp -- [appendArgs Version= [string map [list . \\.] \ + $version(full)] ,] $assembly] + } + } + + return false +} -cleanup { + unset -nocomplain assembly +} -constraints {eagle SQLite System.Data.SQLite} -result {1}} + +############################################################################### + +runTest {test version-1.3 {'System.Data.SQLite.Linq' binary version} -body { file version $systemDataSQLiteLinqDllFile } -constraints {eagle file_System.Data.SQLite.Linq.dll} -result $version(full)} ############################################################################### -runTest {test version-1.3 {'test' binary version} -body { +runTest {test version-1.4 {'System.Data.SQLite.Linq' assembly version} -body { + set assembly [object load System.Data.SQLite.Linq] + + foreach assembly [object assemblies] { + if {[string match System.Data.SQLite.Linq,* $assembly]} then { + return [regexp -- [appendArgs Version= [string map [list . \\.] \ + $version(full)] ,] $assembly] + } + } + + return false +} -cleanup { + unset -nocomplain assembly +} -constraints {eagle SQLite System.Data.SQLite System.Data.SQLite.Linq} \ +-result {1}} + +############################################################################### + +runTest {test version-1.5 {'test' binary version} -body { file version $testExeFile } -constraints {eagle file_test.exe} -result $version(full)} ############################################################################### -runTest {test version-1.4 {'testlinq' binary version} -body { +runTest {test version-1.6 {'testlinq' binary version} -body { file version $testLinqExeFile } -constraints {eagle file_testlinq.exe} -result $version(full)} ############################################################################### set patterns [list \ + [appendArgs Version= [string map [list . \\.] $version(full)] ,] \ [appendArgs [string map [list . \\.] $version(full)] \ ] \ [appendArgs [string map [list . \\.] $version(full)] \ ] \ [appendArgs [string map [list . \\.] $version(full)] \ @@ -118,10 +154,12 @@ $version(full)] \"\\)] \ [appendArgs AssemblyVersion\\(\" [string map [list . \\.] \ $version(full)] \"\\)] \ [appendArgs AssemblyFileVersion\\(\" [string map [list . \\.] \ $version(full)] \"\\)] \ + [appendArgs [string map [list . \\.] \ + $version(full)] ] \ [appendArgs Value=\" [format %03d $version(build)] \"] \ [appendArgs Value=\" [string map [list . \\.] $version(full)] \"] \ [appendArgs Value=\" [string map [list . ,] $version(full)] \"] \ [appendArgs [format %03d $version(build)] \ ] \ @@ -159,10 +197,11 @@ $version(full)] \"\\)] \ [appendArgs AssemblyFileVersion\\(\" [string map [list . \\.] \ $version(full)] \"\\)]] set fileNames [list \ + readme.htm \ SQLite.nuspec \ SQLite.MSIL.nuspec \ SQLite.x64.nuspec \ SQLite.x86.nuspec \ [file join Doc Extra dbfactorysupport.html] \ @@ -169,10 +208,11 @@ [file join Doc Extra welcome.html] \ [file join Membership Properties AssemblyInfo.cs] \ [file join Membership Properties AssemblyInfo.cs] \ [file join SQLite.Designer AssemblyInfo.cs] \ [file join SQLite.Designer AssemblyInfo.cs] \ + [file join SQLite.Designer source.extension.vsixmanifest] \ [file join SQLite.Interop props SQLite.Interop.vsprops] \ [file join SQLite.Interop props SQLite.Interop.vsprops] \ [file join SQLite.Interop props SQLite.Interop.vsprops] \ [file join SQLite.Interop props SQLite.Interop.props] \ [file join SQLite.Interop props SQLite.Interop.props] \ @@ -204,11 +244,11 @@ if {![haveConstraint [appendArgs file_ $constraint]]} then { checkForFile $test_channel $fileName $constraint } - runTest {test [appendArgs version-1.5. $i] \ + runTest {test [appendArgs version-1.7. $i] \ [appendArgs "pattern {" $pattern "} in file \"" $fileName \"] -body { regexp -- $pattern [readFile $fileName] } -constraints [list eagle [appendArgs file_ $constraint]] -result {1}} } Index: exclude_bin.txt ================================================================== --- exclude_bin.txt +++ exclude_bin.txt @@ -1,6 +1,6 @@ +*.done *.exp *.lib *.map -*Installer.* *EnvDTE.* *Microsoft.* Index: exclude_src.txt ================================================================== --- exclude_src.txt +++ exclude_src.txt @@ -25,11 +25,13 @@ Externals/Eagle/lib/Test1.0/constraints.eagle Externals/Eagle/lib/Test1.0/epilogue.eagle Externals/Eagle/lib/Test1.0/pkgIndex.eagle Externals/Eagle/lib/Test1.0/pkgIndex.tcl Externals/Eagle/lib/Test1.0/prologue.eagle +Externals/HtmlHelp/* Externals/MSVCPP/* +Externals/NDoc3/* Membership/* Membership/obj/* Membership/Profile/* Membership/SiteMap/* obj/* Index: readme.htm ================================================================== --- readme.htm +++ readme.htm @@ -3,19 +3,19 @@ ADO.NET SQLite Data Provider
    -Version 1.0.75.0 October 3, 2011
    -Using SQLite 3.7.8 [3e0da808d2]
    +Version 1.0.78.0 January 27, 2012
    +Using SQLite 3.7.10
    Originally written by Robert Simpson
    Released to the public domain, use at your own risk!
    -Official provider website:  http://system.data.sqlite.org/
    -Legacy versions:  http://sqlite.phxsoftware.com/
    +Official provider website: http://system.data.sqlite.org/
    +Legacy versions: http://sqlite.phxsoftware.com/

    -The current development version can be downloaded from -http://system.data.sqlite.org/index.html/timeline?n=20&y=ci +The current development version can be downloaded from +http://system.data.sqlite.org/index.html/timeline?y=ci

    Features

    @@ -54,13 +54,12 @@ 2005/2008/2010. You can add a SQLite database to the Servers list, design queries with the Query Designer, drag-and-drop tables onto a Typed DataSet, etc.
    - Currently not included. We are still updating the design-time support - installer. Due to Visual Studio licensing restrictions, the Express - Editions can no longer be supported. + Due to Visual Studio licensing restrictions, the Express Editions can no + longer be supported.
  • Full SQLite schema editing inside Visual Studio. You can create/edit tables, @@ -129,17 +128,15 @@ Domain. 100% free for commercial and non-commercial use.
  • Design-Time Support

    -Currently not included. We are still updating the Design-Time -support installer.

    -In Windows Explorer, navigate to the SQLite.NET\bin\Designer folder -and execute the INSTALL.EXE file.  The program will automatically -detect what version(s) of Visual Studio 2005/2008 are installed and allow you to -selectively install and uninstall the designer for each edition.

    +Download and run one of the setup packages and then select the +"Install the designer components for Visual Studio 20XX." +option when prompted. +

    DbFactory Support (Non-Compact Framework)

    In order to use the SQLiteFactory and have the SQLite data provider enumerated in the DbProviderFactories methods, you must add the following segment into your application's app.config file:
    @@ -147,11 +144,11 @@ <configuration> <system.data> <DbProviderFactories> <remove invariant="System.Data.SQLite" /> <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" - type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /> + type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.78.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" /> </DbProviderFactories> </system.data> </configuration>

    @@ -179,18 +176,57 @@ are not supporting the mixed CF assembly I was building prior to this version.

    Development Notes Regarding the SQLite 3 Source Code

    -The core sqlite engine is compiled directly from the unmodified source code available +The core SQLite engine is compiled directly from the unmodified source code available at the sqlite.org website.  Several additional pieces are compiled on top of it to extend its functionality, but the core engine's source is not changed.

    Version History

    +

    + 1.0.78.0 - January XX, 2012 +

    +
      +
    • Updated to SQLite 3.7.10.
    • +
    • Redesign the VS designer support installer and integrate it into the setup packages.
    • +
    • When emitting SQL for foreign keys in the VS designer, be sure to take all returned schema rows into account. Remainder of fix for [b226147b37].
    • +
    • Add Flags connection string property to control extra behavioral flags for the connection.
    • +
    • Refactor all IDisposable implementations to conform to best practices, potentially eliminating leaks in certain circumstances.
    • +
    • Even more enhancements to the build and test automation.
    • +
    • Support parameter binding to more primitive types, including unsigned integer types.
    • +
    • Recognize the TIMESTAMP column data type as the DateTime type. Fix for [bb4b04d457].
    • +
    • Prevent logging superfluous messages having to do with library initialization checking. Fix for [3fc172d1be].
    • +
    • Support the DateTimeKind and BaseSchemaName connection string properties in the SQLiteConnectionStringBuilder class. Fix for [f3ec1e0066].
    • +
    • Overloads of the SQLiteConvert.ToDateTime and SQLiteConvert.ToJulianDay methods that do not require an instance should be static. Partial fix for [4bbf851fa5]. ** Potentially Incompatible Change **
    • +
    +

    + 1.0.77.0 - November 28, 2011 +

    +
      +
    • Updated to SQLite 3.7.9.
    • +
    • More enhancements to the build and test automation.
    • +
    • Plug native memory leak when closing a database connection containing a statement that cannot be finalized for some reason.
    • +
    • The SQLite3 class should always attempt to dispose the contained SQLiteConnectionHandle, even when called via the finalizer.
    • +
    • When compiled with DEBUG defined, emit diagnostic information related to resource cleanup to any TraceListener objects that may be registered.
    • +
    • Stop characterizing all log messages as errors. From now on, if the errorCode is zero, the message will not be considered an error.
    • +
    • Never attempt to configure the native logging interface if the SQLite core library has already been initialized for the process. Fix for [2ce0870fad].
    • +
    • Allow the SQLiteLog class to be used for logging messages without having an open connection.
    • +
    • Support building the core System.Data.SQLite assemblies using the .NET Framework 4.0 Client Profile. Fix for [566f1ad1e4].
    • +
    • When generating the schema based on the contents of a SQLiteDataReader, skip flagging columns as unique if the data reader is holding the result of some kind of multi-table construct (e.g. a cross join) because we must allow duplicate values in that case. Fix for [7e3fa93744].
    • +
    • When returning schema information that may be used by the .NET Framework to construct dynamic SQL, use a fake schema name (instead of null) so that the table names will be properly qualified with the catalog name (i.e. the attached database name). Partial fix for [343d392b51].
    • +
    • Add SQLiteSourceId property to the SQLiteConnection class to return the SQLite source identifier.
    • +
    • Add MemoryUsed and MemoryHighwater properties to the SQLiteConnection class to help determine the memory usage of SQLite.
    • +
    • Add DateTimeKind connection string property to control the DateTimeKind of parsed DateTime values. Partial fix for [343d392b51]. ** Potentially Incompatible Change **
    • +
    • Improve the robustness of the SQLiteLog class when it will be initialized and unloaded multiple times.
    • +
    • Fix the name of the interop assembly for Windows CE. Add unit tests to prevent this type of issue from happening again. Fix for [737ca4ff74].
    • +
    • Formally support the SQL type name BOOLEAN in addition to BOOL. Fix for [544dba0a2f].
    • +
    • Make sure the SQLiteConvert.TypeNameToDbType method is thread-safe. Fix for [84718e79fa].
    • +

    1.0.76.0 - October 4, 2011

    • Prevent the domain unload event handler in SQLiteLog from being registered multiple times. Fix for [0d5b1ef362].
    • @@ -198,11 +234,11 @@

    1.0.75.0 - October 3, 2011

      -
    • Updated to SQLite 3.7.8 [3e0da808d2].
    • +
    • Updated to SQLite 3.7.8.
    • More enhancements to the build system.
    • Add official NuGet packages for x86 and x64.
    • Add Changes and LastInsertRowId properties to the connection class.
    • Support more formats when converting data from/to the DateTime type.
    • Make all the assembly versioning attributes consistent.
    • @@ -227,11 +263,11 @@

    1.0.74.0 - July 4, 2011

      -
    • Updated to SQLite 3.7.7.1 [af0d91adf4].
    • +
    • Updated to SQLite 3.7.7.1.
    • Fix incorrect hard-coded .NET Framework version information SQLiteFactory_Linq.cs that was causing IServiceProvider.GetService to fail when running against the .NET Framework 3.5.
    • Fix all XML documentation warnings.
    • Restore support for the mixed-mode assembly (i.e. the one that can be registered in the Global Assembly Cache).
    • Restore support for the Compact Framework.
    • Remove unused "using" statements from the System.Data.SQLite and System.Data.SQLite.Linq projects.
    • @@ -246,11 +282,11 @@

    1.0.73.0 - June 2, 2011

      -
    • Updated to SQLite 3.7.6.3 [ed1da510a2] +
    • Updated to SQLite 3.7.6.3.
    • Minor optimization to GetBytes(). Fix for [8c1650482e].
    • Update various assembly information settings.
    • Correct System.Data.SQLite.Linq version and resource information. Fix for [6489c5a396] and [133daf50d6].
    • Moved log handler from SQLiteConnection object to SQLiteFactory object to prevent if from being prematurely GCed.
    • We should block x64 installs on x86 and we should install native only if the setup package itself is native. Fix for [e058ce156e].
    • @@ -271,36 +307,36 @@

    1.0.70.0 - April 22, 2011

      -
    • Added support for sqlite3_extended_result_codes(), sqlite3_errcode(), and sqlite3_extended_errcode() +
    • Added support for sqlite3_extended_result_codes(), sqlite3_errcode(), and sqlite3_extended_errcode() via SetExtendedResultCodes(), ResultCode(), and ExtendedResultCode().
    • Added support for SQLITE_CONFIG_LOG via SQLiteLogEventHandler().

    1.0.69.0 - April 12, 2011

      -
    • Code merge with SQLite 3.7.6
    • +
    • Code merge with SQLite 3.7.6
    • New VS2008 and VS2010 solution files
    • Build and packaging automation
    • New Inno Setup files
    • Designer support currently not ready for release

    1.0.68.0 - February 2011

      -
    • Code merge with SQLite 3.7.5
    • +
    • Code merge with SQLite 3.7.5
    • Continuing work on supporting Visual Studio 2010

    1.0.67.0 - January 3, 2011

      -
    • Code merge with SQLite 3.7.4
    • +
    • Code merge with SQLite 3.7.4
    • Continuing work on supporting Visual Studio 2010

    1.0.66.1 - August 1, 2010

      @@ -810,11 +846,11 @@
    • Fixed a multi-threaded race condition bug in the garbage collector when commands and/or connections are not properly disposed by the user.
    • Switched the encryption's internal deallocation code to use sqlite's built-in aux functions instead of modifying the pager.c source to free the crypt block.  This eliminates the last of the code changes the provider makes to the original -sqlite engine sources.  Props to Ralf Junker for pointing that out.
    • +SQLite engine sources.  Props to Ralf Junker for pointing that out.

    1.0.38.0 - November 22, 2006

    • Fixed a bug when using CommandBehavior.KeyInfo whereby integer primary key columns @@ -1107,11 +1143,11 @@
    • Rewrote the locking implementation for the Compact Framework.  It is now more robust and incorporates into the SQLite codebase more efficiently than the previous CE adaptation.
    • Moved some of the embedded schema XML data into a resource file to ease code readability.
    • -
    • Automated the fixup of the original sqlite codebase's source prior to compiling, +
    • Automated the fixup of the original SQLite codebase's source prior to compiling, to ease merging with sqlite.org's source.
    • Fixed a memory leak in SQLiteCommand due to it not removing an internal reference to itself in SQLiteConnection. 

    Index: test/AssemblyInfo.cs ================================================================== --- test/AssemblyInfo.cs +++ test/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -29,7 +36,7 @@ // Major Version // Minor Version // Build Number // Revision // -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] Index: test/Program.cs ================================================================== --- test/Program.cs +++ test/Program.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Diagnostics; using System.Windows.Forms; namespace test @@ -25,27 +32,41 @@ } Debugger.Break(); } + string fileName = "test.db"; // NOTE: New default, was "Test.db3". bool autoRun = false; - if ((args != null) && (args.Length > 0)) - { - string arg = args[0]; - - if (arg != null) - { - arg = arg.TrimStart(new char[] { '-', '/' }); - - if (String.Equals(arg, "autoRun", - StringComparison.OrdinalIgnoreCase)) - { - autoRun = true; + if (args != null) + { + int length = args.Length; + + for (int index = 0; index < length; index++) + { + string arg = args[index]; + + if (arg != null) + { + arg = arg.TrimStart(new char[] { '-', '/' }); + + if (String.Equals(arg, "fileName", + StringComparison.OrdinalIgnoreCase)) + { + index++; + + if (index < length) + fileName = args[index]; + } + else if (String.Equals(arg, "autoRun", + StringComparison.OrdinalIgnoreCase)) + { + autoRun = true; + } } } } - Application.Run(new TestCasesDialog(autoRun)); + Application.Run(new TestCasesDialog(fileName, autoRun)); } } } Index: test/Properties/Resources.Designer.cs ================================================================== --- test/Properties/Resources.Designer.cs +++ test/Properties/Resources.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // Index: test/TestCases.cs ================================================================== --- test/TestCases.cs +++ test/TestCases.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Data.Common; using System.Data; using System.Data.SQLite; using System.Transactions; @@ -109,10 +116,32 @@ // Decrpyt database cnn.ChangePassword(""); cnn.Close(); + + /////////////////////////////////////////////////////////////////// + + cnn.Open(); + + // Re-Encrypts the database. The connection remains valid and usable afterwards. + cnn.ChangePassword("mypassword"); + cnn.ChangePassword("mynewerpassword"); + + maydroptable.Add("ChangePasswordTest2"); + if (cnn.State != ConnectionState.Open) cnn.Open(); + using (DbCommand cmd = cnn.CreateCommand()) + { + cmd.CommandText = "CREATE TABLE ChangePasswordTest2(ID int primary key)"; + cmd.ExecuteNonQuery(); + } + + // Decrpyt database + cnn.ChangePassword(""); + cnn.Close(); + + /////////////////////////////////////////////////////////////////// // Try opening now without password cnn.Open(); cnn.Close(); Index: test/TestCasesDialog.Designer.cs ================================================================== --- test/TestCasesDialog.Designer.cs +++ test/TestCasesDialog.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace test { partial class TestCasesDialog { ///

    Index: test/TestCasesDialog.cs ================================================================== --- test/TestCasesDialog.cs +++ test/TestCasesDialog.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Text; @@ -23,11 +30,11 @@ private bool _autoRun; private TestCases _test; private TestCases _testitems; - public TestCasesDialog(bool autoRun) + public TestCasesDialog(string fileName, bool autoRun) { InitializeComponent(); using (DataTable tbl = DbProviderFactories.GetFactoryClasses()) { @@ -40,11 +47,11 @@ ) _provider.Items.Add(prov); if (prov == "System.Data.SQLite") _provider.SelectedItem = prov; } } - _connectionString.Items.Add("Data Source=Test.db3;Pooling=true;FailIfMissing=false"); + _connectionString.Items.Add(String.Format("Data Source={0};Pooling=true;FailIfMissing=false", fileName)); _connectionString.Items.Add("Data Source=(local);Initial Catalog=sqlite;Integrated Security=True;Max Pool Size=10"); _connectionString.SelectedIndex = 0; _autoRun = autoRun; _testitems = new TestCases(); Index: test/app.config ================================================================== --- test/app.config +++ test/app.config @@ -1,8 +1,8 @@ - + Index: test/test.2010.csproj ================================================================== --- test/test.2010.csproj +++ test/test.2010.csproj @@ -17,10 +17,11 @@ Properties Exe test test 3.5 + Client $(MSBuildProjectDirectory)\.. 2010 Index: testce/AssemblyInfo.cs ================================================================== --- testce/AssemblyInfo.cs +++ testce/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -29,8 +36,8 @@ // Major Version // Minor Version // Build Number // Revision // -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +// [assembly: AssemblyFileVersion("1.0.78.0")] Index: testce/Form1.Designer.cs ================================================================== --- testce/Form1.Designer.cs +++ testce/Form1.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + namespace test { partial class Form1 { /// Index: testce/Form1.cs ================================================================== --- testce/Form1.cs +++ testce/Form1.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Windows.Forms; namespace test { Index: testce/TestCases.cs ================================================================== --- testce/TestCases.cs +++ testce/TestCases.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Data.Common; using System.Data; using System.Data.SQLite; Index: testlinq/2008/App.config ================================================================== --- testlinq/2008/App.config +++ testlinq/2008/App.config @@ -1,11 +1,11 @@ - + Index: testlinq/2010/App.config ================================================================== --- testlinq/2010/App.config +++ testlinq/2010/App.config @@ -1,11 +1,11 @@ - + Index: testlinq/NorthwindModel2008.Designer.cs ================================================================== --- testlinq/NorthwindModel2008.Designer.cs +++ testlinq/NorthwindModel2008.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:2.0.50727.3053 // Index: testlinq/NorthwindModel2008.edmx ================================================================== --- testlinq/NorthwindModel2008.edmx +++ testlinq/NorthwindModel2008.edmx @@ -1,6 +1,16 @@ + + + Index: testlinq/NorthwindModel2010.Designer.cs ================================================================== --- testlinq/NorthwindModel2010.Designer.cs +++ testlinq/NorthwindModel2010.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. Index: testlinq/NorthwindModel2010.edmx ================================================================== --- testlinq/NorthwindModel2010.edmx +++ testlinq/NorthwindModel2010.edmx @@ -1,6 +1,16 @@ + + + Index: testlinq/Program.cs ================================================================== --- testlinq/Program.cs +++ testlinq/Program.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Diagnostics; using System.Linq; using System.Data.Objects; using System.Text; Index: testlinq/Properties/AssemblyInfo.cs ================================================================== --- testlinq/Properties/AssemblyInfo.cs +++ testlinq/Properties/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -32,7 +39,7 @@ // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] Index: testlinq/testlinq.2010.csproj ================================================================== --- testlinq/testlinq.2010.csproj +++ testlinq/testlinq.2010.csproj @@ -17,10 +17,11 @@ Properties Exe testlinq testlinq 3.5 + Client $(MSBuildProjectDirectory)\.. 2010 Index: tools/install/Installer.2008.csproj ================================================================== --- tools/install/Installer.2008.csproj +++ tools/install/Installer.2008.csproj @@ -16,16 +16,18 @@ {A41FE2A5-07AD-4CE7-B836-1544634816F5} Properties Exe Installer Installer + true 2.0 $(MSBuildProjectDirectory)\..\.. true 2008 + $(BinaryOutputPath) true @@ -48,14 +50,24 @@ + + + + + + $(BuildDependsOn); + EmbedExeManifest; + StrongNameSign; + + - + Index: tools/install/Installer.2010.csproj ================================================================== --- tools/install/Installer.2010.csproj +++ tools/install/Installer.2010.csproj @@ -16,15 +16,18 @@ {A41FE2A5-07AD-4CE7-B836-1544634816F5} Properties Exe Installer Installer + true 3.5 + Client $(MSBuildProjectDirectory)\..\.. 2010 + $(BinaryOutputPath) true @@ -47,14 +50,24 @@ + + + + + + $(BuildDependsOn); + EmbedExeManifest; + StrongNameSign; + + - + Index: tools/install/Installer.cs ================================================================== --- tools/install/Installer.cs +++ tools/install/Installer.cs @@ -3,10 +3,11 @@ * * Written by Joe Mistachkin. * Released to the public domain, use at your own risk! */ +using System; using System.Collections.Generic; using System.Diagnostics; using System.EnterpriseServices.Internal; using System.IO; using System.Reflection; @@ -32,39 +33,42 @@ string name, string description, string typeName, AssemblyName assemblyName, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref bool saved, ref string error ); /////////////////////////////////////////////////////////////////////////// internal delegate bool FrameworkRegistryCallback( - RegistryKey rootKey, + Installer.MockRegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ); /////////////////////////////////////////////////////////////////////////// internal delegate bool VisualStudioRegistryCallback( - RegistryKey rootKey, + Installer.MockRegistryKey rootKey, Version vsVersion, - Guid packageId, - Guid serviceId, - Guid dataSourceId, - Guid dataProviderId, + Installer.Package package, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ); #endregion @@ -83,12 +87,31 @@ VsDataSource = 0x10, VsDataProvider = 0x20, Framework = GAC | AssemblyFolders | DbProviderFactory, Vs = VsPackage | VsDataSource | VsDataProvider, All = Framework | Vs, + AllExceptGAC = All & ~GAC, Default = All } + + /////////////////////////////////////////////////////////////////////////// + + [Flags()] + public enum TracePriority + { + None = 0x0, + Lowest = 0x1, + Lower = 0x2, + Low = 0x4, + MediumLow = 0x8, + Medium = 0x10, + MediumHigh = 0x20, + High = 0x40, + Higher = 0x80, + Highest = 0x100, + Default = Medium + } #endregion /////////////////////////////////////////////////////////////////////////// #region Installer Class @@ -150,55 +173,70 @@ #region TraceOps Class private static class TraceOps { #region Private Constants + private const string DefaultDebugFormat = "#{0} @ {1}: {2}"; + private const string DefaultTraceFormat = "#{0} @ {1}: {2}"; + private const string Iso8601DateTimeOutputFormat = "yyyy.MM.ddTHH:mm:ss.fffffff"; #endregion /////////////////////////////////////////////////////////////////// - #region Private Data + #region Private Static Data private static object syncRoot = new object(); - private static long nextId; + private static long nextDebugId; + private static long nextTraceId; + private static IList debugListeners; + private static TracePriority debugPriority = TracePriority.Default; + private static TracePriority tracePriority = TracePriority.Default; + private static string debugFormat = DefaultDebugFormat; + private static string traceFormat = DefaultTraceFormat; + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Public Static Properties + public static TracePriority DebugPriority + { + get { lock (syncRoot) { return debugPriority; } } + set { lock (syncRoot) { debugPriority = value; } } + } + + /////////////////////////////////////////////////////////////////// + + public static TracePriority TracePriority + { + get { lock (syncRoot) { return tracePriority; } } + set { lock (syncRoot) { tracePriority = value; } } + } + + /////////////////////////////////////////////////////////////////// + + public static string DebugFormat + { + get { lock (syncRoot) { return debugFormat; } } + set { lock (syncRoot) { debugFormat = value; } } + } + + /////////////////////////////////////////////////////////////////// + + public static string TraceFormat + { + get { lock (syncRoot) { return traceFormat; } } + set { lock (syncRoot) { traceFormat = value; } } + } #endregion /////////////////////////////////////////////////////////////////// #region Interactive Support Methods - public static string GetAssemblyTitle( - Assembly assembly - ) - { - if (assembly != null) - { - try - { - if (assembly.IsDefined( - typeof(AssemblyTitleAttribute), false)) - { - AssemblyTitleAttribute title = - (AssemblyTitleAttribute) - assembly.GetCustomAttributes( - typeof(AssemblyTitleAttribute), false)[0]; - - return title.Title; - } - } - catch - { - // do nothing. - } - } - - return null; - } - - /////////////////////////////////////////////////////////////////// - public static DialogResult ShowMessage( + TracePriority tracePriority, + TraceCallback debugCallback, TraceCallback traceCallback, Assembly assembly, string message, string category, MessageBoxButtons buttons, @@ -205,11 +243,12 @@ MessageBoxIcon icon ) { DialogResult result = DialogResult.OK; - Trace(traceCallback, message, category); + DebugAndTrace(tracePriority, + debugCallback, traceCallback, message, category); if (SystemInformation.UserInteractive) { string title = GetAssemblyTitle(assembly); @@ -216,29 +255,50 @@ if (title == null) title = Application.ProductName; result = MessageBox.Show(message, title, buttons, icon); - Trace(traceCallback, String.Format( - "User choice of \"{0}\".", result), category); + DebugAndTrace(tracePriority, + debugCallback, traceCallback, String.Format( + "User choice of {0}.", ForDisplay(result)), + category); return result; } - Trace(traceCallback, String.Format( - "Default choice of \"{0}\".", result), category); + DebugAndTrace(tracePriority, + debugCallback, traceCallback, String.Format( + "Default choice of {0}.", ForDisplay(result)), + category); return result; } #endregion /////////////////////////////////////////////////////////////////// #region Tracing Support Methods - public static long NextId() + public static void SetupDebugListeners() { - return Interlocked.Increment(ref nextId); + if (debugListeners == null) + debugListeners = new List(); + + debugListeners.Add(new ConsoleTraceListener()); + } + + /////////////////////////////////////////////////////////////////// + + public static long NextDebugId() + { + return Interlocked.Increment(ref nextDebugId); + } + + /////////////////////////////////////////////////////////////////// + + public static long NextTraceId() + { + return Interlocked.Increment(ref nextTraceId); } /////////////////////////////////////////////////////////////////// public static string TimeStamp(DateTime dateTime) @@ -310,55 +370,87 @@ return null; } /////////////////////////////////////////////////////////////////// + public static void DebugCore( + string message, + string category + ) + { + lock (syncRoot) + { + if (debugListeners != null) + { + foreach (TraceListener listener in debugListeners) + { + listener.WriteLine(message, category); + listener.Flush(); + } + } + } + } + + /////////////////////////////////////////////////////////////////// + public static void TraceCore( string message, string category ) { lock (syncRoot) { - System.Diagnostics.Trace.WriteLine(message, category); - System.Diagnostics.Trace.Flush(); + // + // NOTE: Write the message to all the active trace + // listeners. + // + Trace.WriteLine(message, category); + Trace.Flush(); } } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] - public static string Trace( + public static string DebugAndTrace( + TracePriority tracePriority, + TraceCallback debugCallback, TraceCallback traceCallback, Exception exception, string category ) { if (exception != null) - return Trace(traceCallback, - new StackTrace(exception, true), 0, + return DebugAndTrace(tracePriority, debugCallback, + traceCallback, new StackTrace(exception, true), 0, exception.ToString(), category); return null; } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] - public static string Trace( + public static string DebugAndTrace( + TracePriority tracePriority, + TraceCallback debugCallback, TraceCallback traceCallback, string message, string category ) { - return Trace(traceCallback, null, 1, message, category); + return DebugAndTrace( + tracePriority, debugCallback, traceCallback, null, 1, + message, category); } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] - private static string Trace( + private static string DebugAndTrace( + TracePriority tracePriority, + TraceCallback debugCallback, TraceCallback traceCallback, StackTrace stackTrace, int level, string message, string category @@ -369,31 +461,424 @@ // going to be captured by GetMethodName. // if (stackTrace == null) level++; - if (traceCallback == null) - traceCallback = TraceCore; + // + // NOTE: Format the message for display (once). + // + string formatted = String.Format("{0}: {1}", + GetMethodName(stackTrace, level), message); - traceCallback(String.Format("{0}: {1}", - GetMethodName(stackTrace, level), message), category); + // + // NOTE: If the debug callback is invalid or the trace priority + // of this message is less than what we currently want to + // debug, skip it. + // + if ((debugCallback != null) && + (tracePriority >= DebugPriority)) + { + // + // NOTE: Invoke the debug callback with the formatted + // message and the category specified by the + // caller. + // + debugCallback(formatted, category); + } + + // + // NOTE: If the trace callback is invalid or the trace priority + // of this message is less than what we currently want to + // trace, skip it. + // + if ((traceCallback != null) && + (tracePriority >= TracePriority)) + { + // + // NOTE: Invoke the trace callback with the formatted + // message and the category specified by the + // caller. + // + traceCallback(formatted, category); + } return message; } #endregion } #endregion /////////////////////////////////////////////////////////////////////// + #region MockRegistry Class + private sealed class MockRegistry : IDisposable + { + #region Public Constructors + public MockRegistry() + { + whatIf = true; + readOnly = true; + safe = true; + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistry( + bool whatIf + ) + : this() + { + this.whatIf = whatIf; + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistry( + bool whatIf, + bool readOnly + ) + : this(whatIf) + { + this.readOnly = readOnly; + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistry( + bool whatIf, + bool readOnly, + bool safe + ) + : this(whatIf, readOnly) + { + this.safe = safe; + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Public Properties + private bool whatIf; + public bool WhatIf + { + get { CheckDisposed(); return whatIf; } + set { CheckDisposed(); whatIf = value; } + } + + /////////////////////////////////////////////////////////////////// + + private bool readOnly; + public bool ReadOnly + { + get { CheckDisposed(); return readOnly; } + set { CheckDisposed(); readOnly = value; } + } + + /////////////////////////////////////////////////////////////////// + + private bool safe; + public bool Safe + { + get { CheckDisposed(); return safe; } + set { CheckDisposed(); safe = value; } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey classesRoot; + public MockRegistryKey ClassesRoot + { + get + { + CheckDisposed(); + + if (classesRoot == null) + classesRoot = new MockRegistryKey( + Registry.ClassesRoot, whatIf, readOnly, safe); + + return classesRoot; + } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey currentConfig; + public MockRegistryKey CurrentConfig + { + get + { + CheckDisposed(); + + if (currentConfig == null) + currentConfig = new MockRegistryKey( + Registry.CurrentConfig, whatIf, readOnly, safe); + + return currentConfig; + } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey currentUser; + public MockRegistryKey CurrentUser + { + get + { + CheckDisposed(); + + if (currentUser == null) + currentUser = new MockRegistryKey( + Registry.CurrentUser, whatIf, readOnly, safe); + + return currentUser; + } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey dynData; + public MockRegistryKey DynData + { + get + { + CheckDisposed(); + + if (dynData == null) + dynData = new MockRegistryKey( + Registry.DynData, whatIf, readOnly, safe); + + return dynData; + } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey localMachine; + public MockRegistryKey LocalMachine + { + get + { + CheckDisposed(); + + if (localMachine == null) + localMachine = new MockRegistryKey( + Registry.LocalMachine, whatIf, readOnly, safe); + + return localMachine; + } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey performanceData; + public MockRegistryKey PerformanceData + { + get + { + CheckDisposed(); + + if (performanceData == null) + performanceData = new MockRegistryKey( + Registry.PerformanceData, whatIf, readOnly, safe); + + return performanceData; + } + } + + /////////////////////////////////////////////////////////////////// + + private MockRegistryKey users; + public MockRegistryKey Users + { + get + { + CheckDisposed(); + + if (users == null) + users = new MockRegistryKey( + Registry.Users, whatIf, readOnly, safe); + + return users; + } + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Public "Registry" Methods + public object GetValue( + string keyName, + string valueName, + object defaultValue + ) + { + CheckDisposed(); + + return Registry.GetValue(keyName, valueName, defaultValue); + } + + /////////////////////////////////////////////////////////////////// + + public void SetValue( + string keyName, + string valueName, + object value + ) + { + CheckDisposed(); + CheckReadOnly(); + + if (!whatIf) + Registry.SetValue(keyName, valueName, value); + } + + /////////////////////////////////////////////////////////////////// + + public void SetValue( + string keyName, + string valueName, + object value, + RegistryValueKind valueKind + ) + { + CheckDisposed(); + CheckReadOnly(); + + if (!whatIf) + Registry.SetValue(keyName, valueName, value, valueKind); + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Private Methods + private void CheckReadOnly() + { + // + // NOTE: In "read-only" mode, we disallow all write access. + // + if (!readOnly) + return; + + throw new InvalidOperationException(); + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { + if (!disposed) + return; + + throw new ObjectDisposedException( + typeof(MockRegistry).Name); + } + + /////////////////////////////////////////////////////////////////// + + private /* protected virtual */ void Dispose( + bool disposing + ) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (classesRoot != null) + { + classesRoot.Close(); + classesRoot = null; + } + + if (currentConfig != null) + { + currentConfig.Close(); + currentConfig = null; + } + + if (currentUser != null) + { + currentUser.Close(); + currentUser = null; + } + + if (dynData != null) + { + dynData.Close(); + dynData = null; + } + + if (localMachine != null) + { + localMachine.Close(); + localMachine = null; + } + + if (performanceData != null) + { + performanceData.Close(); + performanceData = null; + } + + if (users != null) + { + users.Close(); + users = null; + } + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + // + // NOTE: This object is now disposed. + // + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Destructor + ~MockRegistry() + { + Dispose(false); + } + #endregion + } + #endregion + + /////////////////////////////////////////////////////////////////////// + #region MockRegistryKey Class - private sealed class MockRegistryKey : IDisposable + internal sealed class MockRegistryKey : IDisposable { #region Private Constructors private MockRegistryKey() { whatIf = true; + readOnly = true; + safe = true; } #endregion /////////////////////////////////////////////////////////////////// @@ -431,14 +916,66 @@ /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, + string subKeyName, + bool whatIf, + bool readOnly + ) + : this(key, subKeyName, whatIf) + { + this.readOnly = readOnly; + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistryKey( + RegistryKey key, + string subKeyName, + bool whatIf, + bool readOnly, + bool safe + ) + : this(key, subKeyName, whatIf, readOnly) + { + this.safe = safe; + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistryKey( + RegistryKey key, bool whatIf ) : this(key, null, whatIf) { + // do nothing. + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistryKey( + RegistryKey key, + bool whatIf, + bool readOnly + ) + : this(key, null, whatIf, readOnly) + { + // do nothing. + } + + /////////////////////////////////////////////////////////////////// + + public MockRegistryKey( + RegistryKey key, + bool whatIf, + bool readOnly, + bool safe + ) + : this(key, null, whatIf, readOnly, safe) + { // do nothing. } #endregion /////////////////////////////////////////////////////////////////// @@ -458,10 +995,11 @@ public MockRegistryKey CreateSubKey( string subKeyName ) { CheckDisposed(); + CheckReadOnly(); if (key == null) return null; if (whatIf) @@ -473,42 +1011,47 @@ // mode anyhow. // RegistryKey subKey = key.OpenSubKey(subKeyName); return (subKey != null) ? - new MockRegistryKey(subKey) : - new MockRegistryKey(key, subKeyName); + new MockRegistryKey( + subKey, whatIf, readOnly, safe) : + new MockRegistryKey( + key, subKeyName, whatIf, readOnly, safe); } else { return new MockRegistryKey( - key.CreateSubKey(subKeyName), false); + key.CreateSubKey(subKeyName), whatIf, readOnly, safe); } } /////////////////////////////////////////////////////////////////// public void DeleteSubKey( - string subKeyName + string subKeyName, + bool throwOnMissing ) { CheckDisposed(); + CheckReadOnly(); if (key == null) return; if (!whatIf) - key.DeleteSubKey(subKeyName); + key.DeleteSubKey(subKeyName, throwOnMissing); } /////////////////////////////////////////////////////////////////// public void DeleteSubKeyTree( string subKeyName ) { CheckDisposed(); + CheckReadOnly(); if (key == null) return; if (!whatIf) @@ -516,20 +1059,22 @@ } /////////////////////////////////////////////////////////////////// public void DeleteValue( - string name + string name, + bool throwOnMissing ) { CheckDisposed(); + CheckReadOnly(); if (key == null) return; if (!whatIf) - key.DeleteValue(name); + key.DeleteValue(name, throwOnMissing); } /////////////////////////////////////////////////////////////////// public string[] GetSubKeyNames() @@ -575,18 +1120,21 @@ bool writable ) { CheckDisposed(); + if (writable) + CheckReadOnly(); + if (key == null) return null; RegistryKey subKey = key.OpenSubKey( subKeyName, whatIf ? false : writable); return (subKey != null) ? - new MockRegistryKey(subKey, whatIf) : null; + new MockRegistryKey(subKey, whatIf, readOnly, safe) : null; } /////////////////////////////////////////////////////////////////// public void SetValue( @@ -593,10 +1141,11 @@ string name, object value ) { CheckDisposed(); + CheckReadOnly(); if (key == null) return; if (!whatIf) @@ -625,11 +1174,11 @@ /////////////////////////////////////////////////////////////////// private RegistryKey key; public RegistryKey Key { - get { CheckDisposed(); return key; } + get { CheckDisposed(); CheckSafe(); return key; } } /////////////////////////////////////////////////////////////////// private string subKeyName; @@ -643,10 +1192,55 @@ private bool whatIf; public bool WhatIf { get { CheckDisposed(); return whatIf; } } + + /////////////////////////////////////////////////////////////////// + + private bool readOnly; + public bool ReadOnly + { + get { CheckDisposed(); return readOnly; } + } + + /////////////////////////////////////////////////////////////////// + + public bool safe; + public bool Safe + { + get { CheckDisposed(); return safe; } + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Private Methods + private void CheckReadOnly() + { + // + // NOTE: In "read-only" mode, we disallow all write access. + // + if (!readOnly) + return; + + throw new InvalidOperationException(); + } + + /////////////////////////////////////////////////////////////////// + + private void CheckSafe() + { + // + // NOTE: In "safe" mode, we disallow all direct access to the + // contained registry key. + // + if (!safe) + return; + + throw new InvalidOperationException(); + } #endregion /////////////////////////////////////////////////////////////////// #region System.Object Overrides @@ -659,23 +1253,10 @@ #endregion /////////////////////////////////////////////////////////////////// #region Implicit Conversion Operators - // - // BUGBUG: The 'what-if' mode setting here should probably be based - // on some static property, not hard-coded to true? - // - public static implicit operator MockRegistryKey( - RegistryKey key - ) - { - return new MockRegistryKey(key, null, true); - } - - /////////////////////////////////////////////////////////////////// - // // BUGBUG: Remove me? This should be safe because in 'what-if' // mode all keys are opened read-only. // public static implicit operator RegistryKey( @@ -693,11 +1274,12 @@ private void CheckDisposed() /* throw */ { if (!disposed) return; - throw new ObjectDisposedException(typeof(MockRegistryKey).Name); + throw new ObjectDisposedException( + typeof(MockRegistryKey).Name); } /////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose( @@ -790,47 +1372,10 @@ #endregion /////////////////////////////////////////////////////////////////// #region Public Static Methods - public static RegistryKey GetRootKeyByName( - string keyName - ) - { - if (String.IsNullOrEmpty(keyName)) - return null; - - switch (keyName.ToUpperInvariant()) - { - case "HKCR": - case "HKEY_CLASSES_ROOT": - return Registry.ClassesRoot; - case "HKCC": - case "HKEY_CURRENT_CONFIG": - return Registry.CurrentConfig; - case "HKCU": - case "HKEY_CURRENT_USER": - return Registry.CurrentUser; - case "HKDD": - case "HKEY_DYN_DATA": - return Registry.DynData; - case "HKLM": - case "HKEY_LOCAL_MACHINE": - return Registry.LocalMachine; - case "HKPD": - case "HKEY_PERFORMANCE_DATA": - return Registry.PerformanceData; - case "HKU": - case "HKEY_USERS": - return Registry.Users; - } - - return null; - } - - /////////////////////////////////////////////////////////////////// - public static MockRegistryKey OpenSubKey( MockRegistryKey rootKey, string subKeyName, bool writable, bool whatIf, @@ -839,24 +1384,26 @@ { if (rootKey == null) return null; if (verbose) - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(writable ? + TracePriority.Highest : TracePriority.Higher, + debugCallback, traceCallback, String.Format( "rootKey = {0}, subKeyName = {1}, writable = {2}", - ForDisplay(rootKey), ForDisplay(subKeyName), writable), - traceCategory); + ForDisplay(rootKey), ForDisplay(subKeyName), + ForDisplay(writable)), traceCategory); // // HACK: Always forbid writable access when operating in // 'what-if' mode. // MockRegistryKey key = rootKey.OpenSubKey( subKeyName, whatIf ? false : writable); return (key != null) ? - new MockRegistryKey(key, whatIf) : null; + new MockRegistryKey(key, whatIf, false, false) : null; } /////////////////////////////////////////////////////////////////// public static MockRegistryKey CreateSubKey( @@ -868,13 +1415,15 @@ { if (rootKey == null) return null; if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "rootKey = {0}, subKeyName = {1}", ForDisplay(rootKey), - ForDisplay(subKeyName)), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "rootKey = {0}, subKeyName = {1}", + ForDisplay(rootKey), ForDisplay(subKeyName)), + traceCategory); try { // // HACK: Always open a key, rather than creating one when @@ -882,23 +1431,25 @@ // if (whatIf) { // // HACK: Attempt to open the specified sub-key. If - // this fails, we will simply return the root key - // itself since no writes are allowed in + // this fails, we will simply return the root + // key itself since no writes are allowed in // 'what-if' mode anyhow. // MockRegistryKey key = rootKey.OpenSubKey(subKeyName); return (key != null) ? - key : new MockRegistryKey(rootKey, subKeyName); + key : new MockRegistryKey( + rootKey, subKeyName, whatIf, false, false); } else { return new MockRegistryKey( - rootKey.CreateSubKey(subKeyName), false); + rootKey.CreateSubKey(subKeyName), whatIf, false, + false); } } finally { subKeysCreated++; @@ -908,24 +1459,27 @@ /////////////////////////////////////////////////////////////////// public static void DeleteSubKey( MockRegistryKey rootKey, string subKeyName, + bool throwOnMissing, bool whatIf, bool verbose ) { if (rootKey == null) return; if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "rootKey = {0}, subKeyName = {1}", ForDisplay(rootKey), - ForDisplay(subKeyName)), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "rootKey = {0}, subKeyName = {1}", + ForDisplay(rootKey), ForDisplay(subKeyName)), + traceCategory); if (!whatIf) - rootKey.DeleteSubKey(subKeyName); + rootKey.DeleteSubKey(subKeyName, throwOnMissing); subKeysDeleted++; } /////////////////////////////////////////////////////////////////// @@ -939,13 +1493,15 @@ { if (rootKey == null) return; if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "rootKey = {0}, subKeyName = {1}", ForDisplay(rootKey), - ForDisplay(subKeyName)), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "rootKey = {0}, subKeyName = {1}", + ForDisplay(rootKey), ForDisplay(subKeyName)), + traceCategory); if (!whatIf) rootKey.DeleteSubKeyTree(subKeyName); subKeysDeleted++; @@ -961,11 +1517,12 @@ { if (key == null) return null; if (verbose) - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.High, + debugCallback, traceCallback, String.Format( "key = {0}", ForDisplay(key)), traceCategory); return key.GetSubKeyNames(); } @@ -981,11 +1538,12 @@ { if (key == null) return null; if (verbose) - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.High, + debugCallback, traceCallback, String.Format( "key = {0}, name = {1}, defaultValue = {2}", ForDisplay(key), ForDisplay(name), ForDisplay(defaultValue)), traceCategory); return key.GetValue(name, defaultValue); @@ -1003,13 +1561,15 @@ { if (key == null) return; if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "key = {0}, name = {1}, value = {2}", ForDisplay(key), - ForDisplay(name), ForDisplay(value)), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "key = {0}, name = {1}, value = {2}", + ForDisplay(key), ForDisplay(name), ForDisplay(value)), + traceCategory); if (!whatIf) key.SetValue(name, value); keyValuesSet++; @@ -1018,24 +1578,26 @@ /////////////////////////////////////////////////////////////////// public static void DeleteValue( MockRegistryKey key, string name, + bool throwOnMissing, bool whatIf, bool verbose ) { if (key == null) return; if (verbose) - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( "key = {0}, name = {1}", ForDisplay(key), ForDisplay(name)), traceCategory); if (!whatIf) - key.DeleteValue(name); + key.DeleteValue(name, throwOnMissing); keyValuesDeleted++; } #endregion } @@ -1107,10 +1669,71 @@ } #endregion /////////////////////////////////////////////////////////////////////// + #region Package Class + internal sealed class Package + { + #region Public Constructors + public Package() + { + // do nothing. + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Public Properties + private Guid packageId; + public Guid PackageId + { + get { return packageId; } + set { packageId = value; } + } + + /////////////////////////////////////////////////////////////////// + + private Guid serviceId; + public Guid ServiceId + { + get { return serviceId; } + set { serviceId = value; } + } + + /////////////////////////////////////////////////////////////////// + + private Guid dataSourceId; + public Guid DataSourceId + { + get { return dataSourceId; } + set { dataSourceId = value; } + } + + /////////////////////////////////////////////////////////////////// + + private Guid dataProviderId; + public Guid DataProviderId + { + get { return dataProviderId; } + set { dataProviderId = value; } + } + + /////////////////////////////////////////////////////////////////// + + private Guid adoNetTechnologyId; + public Guid AdoNetTechnologyId + { + get { return adoNetTechnologyId; } + set { adoNetTechnologyId = value; } + } + #endregion + } + #endregion + + /////////////////////////////////////////////////////////////////////// + #region Configuration Class private sealed class Configuration { #region Private Constants private const char Switch = '-'; @@ -1131,22 +1754,30 @@ string logFileName, string directory, string coreFileName, string linqFileName, string designerFileName, + string debugFormat, + string traceFormat, InstallFlags installFlags, + TracePriority debugPriority, + TracePriority tracePriority, bool install, + bool wow64, + bool noRuntimeVersion, bool noDesktop, bool noCompact, bool noNetFx20, bool noNetFx40, bool noVs2008, bool noVs2010, bool noTrace, bool noConsole, bool noLog, + bool throwOnMissing, bool whatIf, + bool debug, bool verbose, bool confirm ) { this.assembly = assembly; @@ -1153,22 +1784,30 @@ this.logFileName = logFileName; this.directory = directory; this.coreFileName = coreFileName; this.linqFileName = linqFileName; this.designerFileName = designerFileName; + this.debugFormat = debugFormat; + this.traceFormat = traceFormat; this.installFlags = installFlags; + this.debugPriority = debugPriority; + this.tracePriority = tracePriority; this.install = install; + this.wow64 = wow64; + this.noRuntimeVersion = noRuntimeVersion; this.noDesktop = noDesktop; this.noCompact = noCompact; this.noNetFx20 = noNetFx20; this.noNetFx40 = noNetFx40; this.noVs2008 = noVs2008; this.noVs2010 = noVs2010; this.noTrace = noTrace; this.noConsole = noConsole; this.noLog = noLog; + this.throwOnMissing = throwOnMissing; this.whatIf = whatIf; + this.debug = debug; this.verbose = verbose; this.confirm = confirm; } #endregion @@ -1311,15 +1950,17 @@ GetDefaultFileNames( ref directory, ref coreFileName, ref linqFileName, ref designerFileName); - return new Configuration( - thisAssembly, null, directory, coreFileName, linqFileName, - designerFileName, InstallFlags.Default, true, false, true, + return new Configuration(thisAssembly, null, directory, + coreFileName, linqFileName, designerFileName, + TraceOps.DebugFormat, TraceOps.TraceFormat, + InstallFlags.Default, TracePriority.Default, + TracePriority.Default, true, false, false, false, false, false, false, false, false, false, false, false, true, - true, false); + true, false, false, false); } /////////////////////////////////////////////////////////////////// public static bool FromArgs( @@ -1341,410 +1982,604 @@ for (int index = 0; index < length; index++) { string arg = args[index]; - if (String.IsNullOrEmpty(arg)) - continue; - - string newArg = arg; - - if (CheckOption(ref newArg)) - { - // - // NOTE: All the supported command line options must - // have a value; therefore, attempt to advance - // to it now. If we fail, we are done. - // - index++; - - if (index >= length) - { - error = TraceOps.Trace( - traceCallback, String.Format( - "Missing value for option: {0}", - ForDisplay(arg)), traceCategory); - - if (strict) - return false; - - break; - } - - // - // NOTE: Grab the textual value of this command line - // option. - // - string text = args[index]; - - // - // NOTE: Figure out which command line option this is - // (based on a partial name match) and then try - // to interpret the textual value as the correct - // type. - // - if (MatchOption(newArg, "strict")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - // - // NOTE: Allow the command line arguments to override - // the "strictness" setting provided by our caller. - // - strict = (bool)value; - } - else if (MatchOption(newArg, "logFileName")) - { - configuration.logFileName = text; - } - else if (MatchOption(newArg, "directory")) - { - configuration.directory = text; - - // - // NOTE: *SPECIAL* Must refresh the file names - // here because the underlying directory - // has changed. - // - string coreFileName = configuration.coreFileName; - - if (!String.IsNullOrEmpty(coreFileName)) - coreFileName = Path.GetFileName(coreFileName); - - if (String.IsNullOrEmpty(coreFileName)) - coreFileName = Installer.CoreFileName; - - configuration.coreFileName = Path.Combine( - configuration.directory, coreFileName); - - string linqFileName = configuration.linqFileName; - - if (!String.IsNullOrEmpty(linqFileName)) - linqFileName = Path.GetFileName(linqFileName); - - if (String.IsNullOrEmpty(linqFileName)) - linqFileName = Installer.LinqFileName; - - configuration.linqFileName = Path.Combine( - configuration.directory, linqFileName); - - string designerFileName = configuration.designerFileName; - - if (!String.IsNullOrEmpty(designerFileName)) - designerFileName = Path.GetFileName(designerFileName); - - if (String.IsNullOrEmpty(designerFileName)) - designerFileName = Installer.DesignerFileName; - - configuration.designerFileName = Path.Combine( - configuration.directory, designerFileName); - } - else if (MatchOption(newArg, "coreFileName")) - { - configuration.coreFileName = text; - } - else if (MatchOption(newArg, "linqFileName")) - { - configuration.linqFileName = text; - } - else if (MatchOption(newArg, "designerFileName")) - { - configuration.designerFileName = text; - } - else if (MatchOption(newArg, "installFlags")) - { - object value = ParseEnum( - typeof(InstallFlags), text, true); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid install flags value: {0}", - ForDisplay(text)), traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.installFlags = (InstallFlags)value; - } - else if (MatchOption(newArg, "install")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.install = (bool)value; - } - else if (MatchOption(newArg, "whatIf")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.whatIf = (bool)value; - } - else if (MatchOption(newArg, "verbose")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.verbose = (bool)value; - } - else if (MatchOption(newArg, "confirm")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.confirm = (bool)value; - } - else if (MatchOption(newArg, "noDesktop")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noDesktop = (bool)value; - } - else if (MatchOption(newArg, "noCompact")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noCompact = (bool)value; - } - else if (MatchOption(newArg, "noNetFx20")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noNetFx20 = (bool)value; - } - else if (MatchOption(newArg, "noNetFx40")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noNetFx40 = (bool)value; - } - else if (MatchOption(newArg, "noVs2008")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noVs2008 = (bool)value; - } - else if (MatchOption(newArg, "noVs2010")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noVs2010 = (bool)value; - } - else if (MatchOption(newArg, "noTrace")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noTrace = (bool)value; - } - else if (MatchOption(newArg, "noConsole")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noConsole = (bool)value; - } - else if (MatchOption(newArg, "noLog")) - { - bool? value = ParseBoolean(text); - - if (value == null) - { - error = TraceOps.Trace(traceCallback, String.Format( - "Invalid {0} boolean value: {1}", - ForDisplay(arg), ForDisplay(text)), - traceCategory); - - if (strict) - return false; - - continue; - } - - configuration.noLog = (bool)value; - } - else - { - error = TraceOps.Trace(traceCallback, String.Format( - "Unsupported command line option: {0}", - ForDisplay(arg)), traceCategory); - - if (strict) - return false; - } - } - else - { - error = TraceOps.Trace(traceCallback, String.Format( - "Unsupported command line argument: {0}", + // + // NOTE: Skip any argument that is null (?) or an empty + // string. + // + if (String.IsNullOrEmpty(arg)) + continue; + + // + // NOTE: We are going to modify the original argument + // by removing any leading option characters; + // therefore, we use a new string to hold the + // modified argument. + // + string newArg = arg; + + // + // NOTE: All the supported command line options must + // begin with an option character (e.g. a minus + // or forward slash); attempt to validate that + // now. If we fail in strict mode, we are done; + // otherwise, just skip this argument and advance + // to the next one. + // + if (!CheckOption(ref newArg)) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Unsupported command line argument: {0}", + ForDisplay(arg)), traceCategory); + + if (strict) + return false; + + continue; + } + + // + // NOTE: All the supported command line options must + // have a value; therefore, attempt to advance + // to it now. If we fail, we are done. + // + index++; + + if (index >= length) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Missing value for option: {0}", + ForDisplay(arg)), traceCategory); + + if (strict) + return false; + + break; + } + + // + // NOTE: Grab the textual value of this command line + // option. + // + string text = args[index]; + + // + // NOTE: Figure out which command line option this is + // (based on a partial name match) and then try + // to interpret the textual value as the correct + // type. + // + if (MatchOption(newArg, "confirm")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.confirm = (bool)value; + } + else if (MatchOption(newArg, "coreFileName")) + { + configuration.coreFileName = text; + } + else if (MatchOption(newArg, "debug")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.debug = (bool)value; + } + else if (MatchOption(newArg, "debugFormat")) + { + configuration.debugFormat = text; + TraceOps.DebugFormat = configuration.debugFormat; + } + else if (MatchOption(newArg, "debugPriority")) + { + object value = ParseEnum( + typeof(TracePriority), text, true); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.debugPriority = (TracePriority)value; + TraceOps.DebugPriority = configuration.debugPriority; + } + else if (MatchOption(newArg, "designerFileName")) + { + configuration.designerFileName = text; + } + else if (MatchOption(newArg, "directory")) + { + configuration.directory = text; + + // + // NOTE: *SPECIAL* Must refresh the file names + // here because the underlying directory + // has changed. + // + string coreFileName = configuration.coreFileName; + + if (!String.IsNullOrEmpty(coreFileName)) + coreFileName = Path.GetFileName(coreFileName); + + if (String.IsNullOrEmpty(coreFileName)) + coreFileName = Installer.CoreFileName; + + configuration.coreFileName = Path.Combine( + configuration.directory, coreFileName); + + string linqFileName = configuration.linqFileName; + + if (!String.IsNullOrEmpty(linqFileName)) + linqFileName = Path.GetFileName(linqFileName); + + if (String.IsNullOrEmpty(linqFileName)) + linqFileName = Installer.LinqFileName; + + configuration.linqFileName = Path.Combine( + configuration.directory, linqFileName); + + string designerFileName = configuration.designerFileName; + + if (!String.IsNullOrEmpty(designerFileName)) + designerFileName = Path.GetFileName(designerFileName); + + if (String.IsNullOrEmpty(designerFileName)) + designerFileName = Installer.DesignerFileName; + + configuration.designerFileName = Path.Combine( + configuration.directory, designerFileName); + } + else if (MatchOption(newArg, "install")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.install = (bool)value; + } + else if (MatchOption(newArg, "installFlags")) + { + object value = ParseEnum( + typeof(InstallFlags), text, true); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid install flags value: {0}", + ForDisplay(text)), traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.installFlags = (InstallFlags)value; + } + else if (MatchOption(newArg, "linqFileName")) + { + configuration.linqFileName = text; + } + else if (MatchOption(newArg, "logFileName")) + { + configuration.logFileName = text; + } + else if (MatchOption(newArg, "noCompact")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noCompact = (bool)value; + } + else if (MatchOption(newArg, "noConsole")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noConsole = (bool)value; + } + else if (MatchOption(newArg, "noDesktop")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noDesktop = (bool)value; + } + else if (MatchOption(newArg, "noLog")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noLog = (bool)value; + } + else if (MatchOption(newArg, "noNetFx20")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noNetFx20 = (bool)value; + } + else if (MatchOption(newArg, "noNetFx40")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noNetFx40 = (bool)value; + } + else if (MatchOption(newArg, "noRuntimeVersion")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noRuntimeVersion = (bool)value; + } + else if (MatchOption(newArg, "noTrace")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noTrace = (bool)value; + } + else if (MatchOption(newArg, "noVs2008")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noVs2008 = (bool)value; + } + else if (MatchOption(newArg, "noVs2010")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.noVs2010 = (bool)value; + } + else if (MatchOption(newArg, "strict")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + // + // NOTE: Allow the command line arguments to + // override the "strictness" setting + // provided by our caller. + // + strict = (bool)value; + } + else if (MatchOption(newArg, "throwOnMissing")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.throwOnMissing = (bool)value; + } + else if (MatchOption(newArg, "traceFormat")) + { + configuration.traceFormat = text; + TraceOps.TraceFormat = configuration.traceFormat; + } + else if (MatchOption(newArg, "tracePriority")) + { + object value = ParseEnum( + typeof(TracePriority), text, true); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.tracePriority = (TracePriority)value; + TraceOps.TracePriority = configuration.tracePriority; + } + else if (MatchOption(newArg, "verbose")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.verbose = (bool)value; + } + else if (MatchOption(newArg, "whatIf")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.whatIf = (bool)value; + } + else if (MatchOption(newArg, "wow64")) + { + bool? value = ParseBoolean(text); + + if (value == null) + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Invalid {0} boolean value: {1}", + ForDisplay(arg), ForDisplay(text)), + traceCategory); + + if (strict) + return false; + + continue; + } + + configuration.wow64 = (bool)value; + } + else + { + error = TraceOps.DebugAndTrace( + TracePriority.Lowest, debugCallback, + traceCallback, String.Format( + "Unsupported command line option: {0}", ForDisplay(arg)), traceCategory); if (strict) return false; } @@ -1752,11 +2587,12 @@ return true; } catch (Exception e) { - TraceOps.Trace(traceCallback, e, traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, e, traceCategory); error = "Failed to modify configuration."; } return false; @@ -1790,19 +2626,52 @@ if (!configuration.noTrace) { if (!configuration.noLog && String.IsNullOrEmpty(configuration.logFileName)) { - configuration.logFileName = GetLogFileName(); + // + // NOTE: Use the default log file name. + // + configuration.logFileName = GetLogFileName( + "trace"); } /////////////////////////////////////////////////////// if (!configuration.noConsole) { - Trace.Listeners.Add(new ConsoleTraceListener()); + // + // NOTE: In verbose mode, debug output (that meets + // the configured priority criteria) will be + // displayed to the console; otherwise, trace + // output (that meets the configured priority + // criteria) will be displayed to the console. + // + if (configuration.debug) + { + // + // NOTE: Add the console trace listener to the + // list of trace listeners maintained by + // the TraceOps class (i.e. only messages + // that meet the debug priority will be + // seen on the console). + // + TraceOps.SetupDebugListeners(); + } + else + { + // + // NOTE: Add the console trace listener to the + // list of built-in trace listeners (i.e. + // only messages that meet the trace + // priority will be seen on the console). + // + Trace.Listeners.Add(new ConsoleTraceListener()); + } } + + /////////////////////////////////////////////////////// if (!configuration.noLog && !String.IsNullOrEmpty(configuration.logFileName)) { Trace.Listeners.Add(new TextWriterTraceListener( @@ -1812,31 +2681,51 @@ // // NOTE: Dump the configuration now in case we need to // troubleshoot any issues. // - configuration.Dump(); + if (configuration.debugPriority <= TracePriority.Medium) + configuration.Dump(debugCallback); + + if (configuration.tracePriority <= TracePriority.Medium) + configuration.Dump(traceCallback); // // NOTE: Show where we are running from and how we were // invoked. // string location = assembly.Location; - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.MediumLow, + debugCallback, traceCallback, String.Format( + "Running executable is: {0}", ForDisplay(location)), + traceCategory); + + TraceOps.DebugAndTrace(TracePriority.MediumLow, + debugCallback, traceCallback, String.Format( "Original command line is: {0}", Environment.CommandLine), traceCategory); - // - // NOTE: If the debugger is attached and What-If mode is - // [now] disabled, issue a warning. - // - if (!configuration.whatIf && Debugger.IsAttached) - { - TraceOps.Trace(traceCallback, - "Forced to disable \"what-if\" mode with " + - "debugger attached.", traceCategory); + if (!configuration.whatIf) + { + // + // NOTE: If the debugger is attached and What-If mode + // is [now] disabled, issue a warning. + // + if (Debugger.IsAttached) + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, + "Forced to disable \"what-if\" mode with " + + "debugger attached.", traceCategory); + } + else + { + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, + "No actual changes will be made to this " + + "system because \"what-if\" mode is enabled.", + traceCategory); } // // NOTE: If the command line has not been manually // confirmed (i.e. via the explicit command line @@ -1855,14 +2744,137 @@ return true; } catch (Exception e) { - TraceOps.Trace(traceCallback, e, traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, e, traceCategory); error = "Failed to process configuration."; } + + return false; + } + + /////////////////////////////////////////////////////////////////// + + public static bool CheckRuntimeVersion( + Configuration configuration, + bool strict, + ref string error + ) + { + try + { + if (configuration == null) + { + error = "Invalid configuration."; + return false; + } + + // + // NOTE: What version of the runtime was the core (primary) + // assembly compiled against (e.g. "v2.0.50727" or + // "v4.0.30319"). + // + string coreImageRuntimeVersion = GetImageRuntimeVersion( + configuration.coreFileName); + + // + // NOTE: We allow the actual image runtime checking to be + // bypassed via the "-noRuntimeVersion" command line + // option. The command line option is intended for + // expert use only. + // + if (configuration.noRuntimeVersion) + { + TraceOps.DebugAndTrace(TracePriority.Medium, + debugCallback, traceCallback, String.Format( + "Assembly is compiled for the .NET Framework {0}; " + + "however, installation restrictions based on this " + + "fact have been disabled via the command line.", + coreImageRuntimeVersion), traceCategory); + + return true; + } + + // + // TODO: Restrict the configuration based on which image + // runtime versions (which more-or-less correspond + // to .NET Framework versions) are supported by the + // versions of Visual Studio that are installed. + // + if (String.IsNullOrEmpty(coreImageRuntimeVersion)) + { + error = "invalid core file image runtime version"; + return false; + } + else if (String.Equals( + coreImageRuntimeVersion, CLRv2ImageRuntimeVersion, + StringComparison.InvariantCulture)) + { + // + // NOTE: For the CLR v2.0 runtime, make sure we disable + // any attempt to use it for things that require + // an assembly compiled for the CLR v4.0. It is + // uncertain if this is actually a problem in + // practice as the CLR v4.0 can load and use an + // assembly compiled with the CLR v2.0; however, + // since this project offers both configurations, + // we currently disallow this mismatch. + // + configuration.noNetFx40 = true; + configuration.noVs2010 = true; + + TraceOps.DebugAndTrace(TracePriority.Medium, + debugCallback, traceCallback, String.Format( + "Assembly is compiled for the .NET Framework {0}, " + + "support for .NET Framework {1} is now disabled.", + CLRv2ImageRuntimeVersion, CLRv4ImageRuntimeVersion), + traceCategory); + } + else if (String.Equals( + coreImageRuntimeVersion, CLRv4ImageRuntimeVersion, + StringComparison.InvariantCulture)) + { + // + // NOTE: For the CLR v4.0 runtime, make sure we disable + // any attempt to use it for things that require + // an assembly compiled for the CLR v2.0. + // + configuration.noNetFx20 = true; + configuration.noVs2008 = true; + + TraceOps.DebugAndTrace(TracePriority.Medium, + debugCallback, traceCallback, String.Format( + "Assembly is compiled for the .NET Framework {0}, " + + "support for .NET Framework {1} is now disabled.", + ForDisplay(CLRv4ImageRuntimeVersion), + ForDisplay(CLRv2ImageRuntimeVersion)), + traceCategory); + } + else + { + error = String.Format( + "unsupported core file image runtime version " + + "{0}, must be {1} or {2}", + ForDisplay(coreImageRuntimeVersion), + ForDisplay(CLRv2ImageRuntimeVersion), + ForDisplay(CLRv4ImageRuntimeVersion)); + + return false; + } + + return true; + } + catch (Exception e) + { + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, e, traceCategory); + + error = "Failed to check image runtime version."; + } return false; } #endregion @@ -1880,11 +2892,13 @@ return ((installFlags & hasFlags) != InstallFlags.None); } /////////////////////////////////////////////////////////////////// - public void Dump() + public void Dump( + TraceCallback traceCallback + ) { if (traceCallback != null) { traceCallback(String.Format(NameAndValueFormat, "Assembly", ForDisplay(assembly)), @@ -1907,18 +2921,42 @@ traceCategory); traceCallback(String.Format(NameAndValueFormat, "DesignerFileName", ForDisplay(designerFileName)), traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "DebugFormat", ForDisplay(debugFormat)), + traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "TraceFormat", ForDisplay(traceFormat)), + traceCategory); traceCallback(String.Format(NameAndValueFormat, "InstallFlags", ForDisplay(installFlags)), traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "DebugPriority", ForDisplay(debugPriority)), + traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "TracePriority", ForDisplay(tracePriority)), + traceCategory); traceCallback(String.Format(NameAndValueFormat, "Install", ForDisplay(install)), traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "Wow64", ForDisplay(wow64)), + traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "NoRuntimeVersion", ForDisplay(noRuntimeVersion)), + traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoDesktop", ForDisplay(noDesktop)), traceCategory); @@ -1951,22 +2989,45 @@ traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoLog", ForDisplay(noLog)), traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "ThrowOnMissing", ForDisplay(throwOnMissing)), + traceCategory); traceCallback(String.Format(NameAndValueFormat, "WhatIf", ForDisplay(whatIf)), traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "Debug", ForDisplay(debug)), + traceCategory); traceCallback(String.Format(NameAndValueFormat, "Verbose", ForDisplay(verbose)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "Confirm", ForDisplay(confirm)), traceCategory); + + /////////////////////////////////////////////////////////// + + if (assembly != null) + { + traceCallback(String.Format(NameAndValueFormat, + "AssemblyTitle", + ForDisplay(GetAssemblyTitle(assembly))), + traceCategory); + + traceCallback(String.Format(NameAndValueFormat, + "AssemblyConfiguration", + ForDisplay(GetAssemblyConfiguration(assembly))), + traceCategory); + } } } #endregion /////////////////////////////////////////////////////////////////// @@ -2024,25 +3085,79 @@ set { designerFileName = value; } } /////////////////////////////////////////////////////////////////// + private string debugFormat; + public string DebugFormat + { + get { return debugFormat; } + set { debugFormat = value; } + } + + /////////////////////////////////////////////////////////////////// + + private string traceFormat; + public string TraceFormat + { + get { return traceFormat; } + set { traceFormat = value; } + } + + /////////////////////////////////////////////////////////////////// + private InstallFlags installFlags; public InstallFlags InstallFlags { get { return installFlags; } set { installFlags = value; } } /////////////////////////////////////////////////////////////////// + private TracePriority debugPriority; + public TracePriority DebugPriority + { + get { return debugPriority; } + set { debugPriority = value; } + } + + /////////////////////////////////////////////////////////////////// + + private TracePriority tracePriority; + public TracePriority TracePriority + { + get { return tracePriority; } + set { tracePriority = value; } + } + + /////////////////////////////////////////////////////////////////// + private bool install; public bool Install { get { return install; } set { install = value; } } + + /////////////////////////////////////////////////////////////////// + + private bool wow64; + public bool Wow64 + { + get { return wow64; } + set { wow64 = value; } + } + + /////////////////////////////////////////////////////////////////// + + private bool noRuntimeVersion; + public bool NoRuntimeVersion + { + get { return noRuntimeVersion; } + set { noRuntimeVersion = value; } + } /////////////////////////////////////////////////////////////////// private bool noDesktop; public bool NoDesktop @@ -2123,16 +3238,34 @@ set { noLog = value; } } /////////////////////////////////////////////////////////////////// + private bool throwOnMissing; + public bool ThrowOnMissing + { + get { return throwOnMissing; } + set { throwOnMissing = value; } + } + + /////////////////////////////////////////////////////////////////// + private bool whatIf; public bool WhatIf { get { return whatIf; } set { whatIf = value; } } + + /////////////////////////////////////////////////////////////////// + + private bool debug; + public bool Debug + { + get { return debug; } + set { debug = value; } + } /////////////////////////////////////////////////////////////////// private bool verbose; public bool Verbose @@ -2150,10 +3283,96 @@ set { confirm = value; } } #endregion } #endregion + + /////////////////////////////////////////////////////////////////////// + + #region FrameworkList Class + private sealed class FrameworkList + { + #region Public Constructors + public FrameworkList() + { + // do nothing. + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Public Methods + private MockRegistryKey rootKey; + public MockRegistryKey RootKey + { + get { return rootKey; } + set { rootKey = value; } + } + + /////////////////////////////////////////////////////////////////// + + private StringList names; + public StringList Names + { + get { return names; } + set { names = value; } + } + + /////////////////////////////////////////////////////////////////// + + private VersionMap versions; + public VersionMap Versions + { + get { return versions; } + set { versions = value; } + } + + /////////////////////////////////////////////////////////////////// + + private StringList platformNames; + public StringList PlatformNames + { + get { return platformNames; } + set { platformNames = value; } + } + #endregion + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region VsList Class + private sealed class VsList + { + #region Public Constructors + public VsList() + { + // do nothing. + } + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Public Properties + private MockRegistryKey rootKey; + public MockRegistryKey RootKey + { + get { return rootKey; } + set { rootKey = value; } + } + + /////////////////////////////////////////////////////////////////// + + private VersionList versions; + public VersionList Versions + { + get { return versions; } + set { versions = value; } + } + #endregion + } + #endregion #endregion /////////////////////////////////////////////////////////////////////// #region Private Constant Data @@ -2167,32 +3386,31 @@ private const string FactoryTypeName = "System.Data.SQLite.SQLiteFactory"; private const string Description = ".NET Framework Data Provider for SQLite"; /////////////////////////////////////////////////////////////////////// + private const string CLRv2ImageRuntimeVersion = "v2.0.50727"; + private const string CLRv4ImageRuntimeVersion = "v4.0.30319"; + + /////////////////////////////////////////////////////////////////////// + private const string NameAndValueFormat = "{0}: {1}"; - private const string TraceFormat = "#{0} @ {1}: {2}"; private const string LogFileSuffix = ".log"; /////////////////////////////////////////////////////////////////////// - private static readonly string VsIdFormat = "B"; + private const string RootKeyName = "Software"; + private const string Wow64SubKeyName = "Wow6432Node"; /////////////////////////////////////////////////////////////////////// - private static readonly string FrameworkKeyName = - "Software\\Microsoft\\.NETFramework"; + private static readonly bool NetFxIs32BitOnly = false; + private static readonly bool VsIs32BitOnly = true; /////////////////////////////////////////////////////////////////////// - private static readonly string FrameworkSdkKeyName = - "Software\\Microsoft\\Microsoft SDKs\\.NETFramework"; - - /////////////////////////////////////////////////////////////////////// - - private static readonly string WindowsSdkKeyName = - "Software\\Microsoft\\Microsoft SDKs\\Windows"; + private static readonly string VsIdFormat = "B"; /////////////////////////////////////////////////////////////////////// private static readonly string XPathForAddElement = "configuration/system.data/DbProviderFactories/add[@invariant=\"{0}\"]"; @@ -2201,47 +3419,51 @@ "configuration/system.data/DbProviderFactories/remove[@invariant=\"{0}\"]"; #endregion /////////////////////////////////////////////////////////////////////// - #region Private Data + #region Private Static Data private static Assembly thisAssembly = Assembly.GetExecutingAssembly(); private static string traceCategory = Path.GetFileName( - thisAssembly.Location); - - private static TraceCallback traceCallback = AppTrace; - - /////////////////////////////////////////////////////////////////////// - - private static RegistryKey frameworkRootKey; - private static StringList frameworkNameList; - private static VersionMap frameworkVersionMap; - private static StringList platformNameList; - - /////////////////////////////////////////////////////////////////////// - - private static RegistryKey vsRootKey; - private static VersionList vsVersionList; - private static Guid? vsPackageId; - private static Guid? vsServiceId; - private static Guid? vsDataSourcesId; - private static Guid? vsDataProviderId; - private static Guid? vsAdoNetTechnologyId; + thisAssembly.Location); /* NOTE: Same for debug and trace. */ + + private static TraceCallback debugCallback = AppDebug; + private static TraceCallback traceCallback = AppTrace; #endregion /////////////////////////////////////////////////////////////////////// #region Trace Handling - private static string GetLogFileName() - { - string result = Path.GetTempFileName(); /* throw */ - - File.Move(result, result + LogFileSuffix); /* throw */ - result += LogFileSuffix; - - return result; + private static string GetLogFileName( + string typeName + ) /* throw */ + { + string fileName = Path.GetTempFileName(); + string directory = Path.GetDirectoryName(fileName); + string fileNameOnly = Path.GetFileNameWithoutExtension(fileName); + + string newFileName = Path.Combine(directory, String.Format( + "{0}{1}{2}", traceCategory, !String.IsNullOrEmpty(typeName) ? + "." + typeName : String.Empty, "." + fileNameOnly + + LogFileSuffix)); + + File.Move(fileName, newFileName); + + return newFileName; + } + + /////////////////////////////////////////////////////////////////////// + + private static void AppDebug( + string message, + string category + ) + { + TraceOps.DebugCore(String.Format( + TraceOps.DebugFormat, TraceOps.NextDebugId(), + TraceOps.TimeStamp(DateTime.UtcNow), message), category); } /////////////////////////////////////////////////////////////////////// private static void AppTrace( @@ -2248,14 +3470,40 @@ string message, string category ) { TraceOps.TraceCore(String.Format( - TraceFormat, TraceOps.NextId(), + TraceOps.TraceFormat, TraceOps.NextTraceId(), TraceOps.TimeStamp(DateTime.UtcNow), message), category); } #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Generic Platform Handling + private static bool Is64Bit() + { + // + // NOTE: Returns true if the current process is 64-bit. If this + // is true, we *know* that we must be running on a 64-bit + // operating system as well. However, if this is false, we + // do not necessarily know that we are running on a 32-bit + // operating system, due to WoW64 (Win32-on-Win64), etc. + // + return (IntPtr.Size == sizeof(long)); + } + + /////////////////////////////////////////////////////////////////////// + + private static string GetRootKeyName( + bool wow64 + ) + { + return String.Format("{0}{1}", RootKeyName, + wow64 && Is64Bit() ? "\\" + Wow64SubKeyName : String.Empty); + } + #endregion /////////////////////////////////////////////////////////////////////// #region Generic String Handling private static string ForDisplay( @@ -2272,10 +3520,16 @@ { XmlElement element = (XmlElement)value; result = element.OuterXml; } + else if (type == typeof(Version)) + { + Version version = (Version)value; + + result = String.Format("v{0}", version); + } else { result = value.ToString(); if (result.Length == 0) @@ -2290,20 +3544,134 @@ } #endregion /////////////////////////////////////////////////////////////////////// + #region Assembly Attribute Handling + public static string GetAssemblyConfiguration( + Assembly assembly + ) + { + if (assembly != null) + { + try + { + if (assembly.IsDefined( + typeof(AssemblyConfigurationAttribute), false)) + { + AssemblyConfigurationAttribute configuration = + (AssemblyConfigurationAttribute) + assembly.GetCustomAttributes( + typeof(AssemblyConfigurationAttribute), + false)[0]; + + return configuration.Configuration; + } + } + catch + { + // do nothing. + } + } + + return null; + } + + /////////////////////////////////////////////////////////////////////// + + public static string GetAssemblyTitle( + Assembly assembly + ) + { + if (assembly != null) + { + try + { + if (assembly.IsDefined( + typeof(AssemblyTitleAttribute), false)) + { + AssemblyTitleAttribute title = + (AssemblyTitleAttribute) + assembly.GetCustomAttributes( + typeof(AssemblyTitleAttribute), false)[0]; + + return title.Title; + } + } + catch + { + // do nothing. + } + } + + return null; + } + #endregion + + /////////////////////////////////////////////////////////////////////// + #region .NET Framework Handling + private static string GetFrameworkRootKeyName( + bool wow64 + ) + { + return String.Format("{0}\\Microsoft\\.NETFramework", + GetRootKeyName(wow64)); + } + + /////////////////////////////////////////////////////////////////////// + + private static string GetFrameworkKeyName( + string frameworkName, + Version frameworkVersion, + string platformName, + bool wow64 + ) + { + string format = !String.IsNullOrEmpty(platformName) ? + "{0}\\Microsoft\\{1}\\v{2}\\{3}" : + "{0}\\Microsoft\\{1}\\v{2}"; + + return String.Format(format, GetRootKeyName(wow64), + frameworkName, frameworkVersion, platformName); + } + + /////////////////////////////////////////////////////////////////////// + + private static string GetImageRuntimeVersion( + string fileName + ) + { + try + { + Assembly assembly = + Assembly.ReflectionOnlyLoadFrom(fileName); /* throw */ + + if (assembly != null) + return assembly.ImageRuntimeVersion; + } + catch + { + // do nothing. + } + + return null; + } + + /////////////////////////////////////////////////////////////////////// + private static string GetFrameworkDirectory( - RegistryKey rootKey, + MockRegistryKey rootKey, Version frameworkVersion, + bool wow64, bool whatIf, bool verbose ) { using (MockRegistryKey key = RegistryHelper.OpenSubKey( - rootKey, FrameworkKeyName, false, whatIf, verbose)) + rootKey, GetFrameworkRootKeyName(wow64), false, + whatIf, verbose)) { if (key == null) return null; object value = RegistryHelper.GetValue( @@ -2314,225 +3682,117 @@ return Path.Combine( (string)value, String.Format("v{0}", frameworkVersion)); } } - - /////////////////////////////////////////////////////////////////////// - - private static string GetSdkBinaryFileName( - RegistryKey rootKey, - string fileName, - bool whatIf, - bool verbose - ) - { - StringDictionary results = new StringDictionary(); - - string[] keyNames = { - FrameworkKeyName, - FrameworkSdkKeyName, - WindowsSdkKeyName - }; - - string[] valueNames = { - "sdkInstallRootv2.0", - "InstallationFolder", - "InstallationFolder" - }; - - bool[] useSubKeys = { - false, - true, - true - }; - - for (int index = 0; index < keyNames.Length; index++) - { - using (MockRegistryKey key = RegistryHelper.OpenSubKey( - rootKey, keyNames[index], false, whatIf, verbose)) - { - if (key == null) - continue; - - if (useSubKeys[index]) - { - foreach (string subKeyName in RegistryHelper.GetSubKeyNames( - key, whatIf, verbose)) - { - using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( - key, subKeyName, false, whatIf, verbose)) - { - if (subKey == null) - continue; - - object value = RegistryHelper.GetValue( - subKey, valueNames[index], null, whatIf, - verbose); - - if (!(value is string)) - continue; - - string path = (string)value; - - if (!Directory.Exists(path)) - continue; - - path = Path.Combine(path, "bin"); - - if (!Directory.Exists(path)) - continue; - - if (String.IsNullOrEmpty(fileName)) - { - results.Add(subKey.Name, path); - continue; - } - - path = Path.Combine(path, fileName); - - if (File.Exists(path)) - results.Add(subKey.Name, path); - } - } - } - else - { - object value = RegistryHelper.GetValue( - key, valueNames[index], null, whatIf, verbose); - - if (!(value is string)) - continue; - - string path = (string)value; - - if (!Directory.Exists(path)) - continue; - - path = Path.Combine(path, "bin"); - - if (!Directory.Exists(path)) - continue; - - if (String.IsNullOrEmpty(fileName)) - { - results.Add(key.Name, path); - continue; - } - - path = Path.Combine(path, fileName); - - if (File.Exists(path)) - results.Add(key.Name, path); - } - } - } - - // - // NOTE: If we found some results, return the last (latest) one. - // - if (results.Count > 0) - return results[new StringList(results.Keys)[results.Count - 1]]; - - return null; - } #endregion /////////////////////////////////////////////////////////////////////// #region Per-Framework/Platform Handling - private static void InitializeAllFrameworks( - Configuration configuration - ) - { - if (frameworkRootKey == null) - frameworkRootKey = Registry.LocalMachine; - - if (frameworkNameList == null) - { - frameworkNameList = new StringList(); - - if ((configuration == null) || !configuration.NoDesktop) - frameworkNameList.Add(".NETFramework"); - - if ((configuration == null) || !configuration.NoCompact) - { - frameworkNameList.Add(".NETCompactFramework"); - frameworkNameList.Add(".NETCompactFramework"); - frameworkNameList.Add(".NETCompactFramework"); - } - } - - if (frameworkVersionMap == null) - frameworkVersionMap = new VersionMap(); - - if ((configuration == null) || !configuration.NoDesktop) - { - VersionList desktopVersionList = new VersionList(); - - if ((configuration == null) || !configuration.NoNetFx20) - desktopVersionList.Add(new Version(2, 0, 50727)); - - if ((configuration == null) || !configuration.NoNetFx40) - desktopVersionList.Add(new Version(4, 0, 30319)); - - frameworkVersionMap.Add(".NETFramework", desktopVersionList); - } - - if ((configuration == null) || !configuration.NoCompact) - { - frameworkVersionMap.Add(".NETCompactFramework", new VersionList( - new Version[] { - new Version(2, 0, 0, 0), new Version(3, 5, 0, 0) - })); - } - - if (platformNameList == null) - { - platformNameList = new StringList(); - - if ((configuration == null) || !configuration.NoDesktop) - platformNameList.Add(null); - - if ((configuration == null) || !configuration.NoCompact) - { - platformNameList.Add("PocketPC"); - platformNameList.Add("Smartphone"); - platformNameList.Add("WindowsCE"); + private static void InitializeFrameworkList( + MockRegistryKey rootKey, + Configuration configuration, + ref FrameworkList frameworkList + ) + { + if (frameworkList == null) + frameworkList = new FrameworkList(); + + if (frameworkList.RootKey == null) + frameworkList.RootKey = rootKey; + + /////////////////////////////////////////////////////////////////// + + if (frameworkList.Names == null) + { + frameworkList.Names = new StringList(); + + if ((configuration == null) || !configuration.NoDesktop) + frameworkList.Names.Add(".NETFramework"); + + if ((configuration == null) || !configuration.NoCompact) + { + frameworkList.Names.Add(".NETCompactFramework"); + frameworkList.Names.Add(".NETCompactFramework"); + frameworkList.Names.Add(".NETCompactFramework"); + } + } + + /////////////////////////////////////////////////////////////////// + + if (frameworkList.Versions == null) + { + frameworkList.Versions = new VersionMap(); + + if ((configuration == null) || !configuration.NoDesktop) + { + VersionList desktopVersionList = new VersionList(); + + if ((configuration == null) || !configuration.NoNetFx20) + desktopVersionList.Add(new Version(2, 0, 50727)); + + if ((configuration == null) || !configuration.NoNetFx40) + desktopVersionList.Add(new Version(4, 0, 30319)); + + frameworkList.Versions.Add(".NETFramework", + desktopVersionList); + } + + if ((configuration == null) || !configuration.NoCompact) + { + frameworkList.Versions.Add(".NETCompactFramework", + new VersionList(new Version[] { + new Version(2, 0, 0, 0), new Version(3, 5, 0, 0) + })); + } + } + + /////////////////////////////////////////////////////////////////// + + if (frameworkList.PlatformNames == null) + { + frameworkList.PlatformNames = new StringList(); + + if ((configuration == null) || !configuration.NoDesktop) + frameworkList.PlatformNames.Add(null); + + if ((configuration == null) || !configuration.NoCompact) + { + frameworkList.PlatformNames.Add("PocketPC"); + frameworkList.PlatformNames.Add("Smartphone"); + frameworkList.PlatformNames.Add("WindowsCE"); } } } /////////////////////////////////////////////////////////////////////// private static bool HaveFramework( - RegistryKey rootKey, + MockRegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, + bool wow64, bool whatIf, bool verbose ) { - string format = !String.IsNullOrEmpty(platformName) ? - "Software\\Microsoft\\{0}\\v{1}\\{2}" : - "Software\\Microsoft\\{0}\\v{1}"; - - string keyName = String.Format( - format, frameworkName, frameworkVersion, platformName); + string keyName = GetFrameworkKeyName( + frameworkName, frameworkVersion, platformName, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) return false; - if (platformName != null) // NOTE: Skip non-desktop frameworks. + if (platformName != null) // NOTE: Skip non-desktop. return true; string directory = GetFrameworkDirectory( - rootKey, frameworkVersion, whatIf, verbose); + rootKey, frameworkVersion, wow64, whatIf, verbose); if (String.IsNullOrEmpty(directory)) return false; if (!Directory.Exists(directory)) @@ -2543,73 +3803,89 @@ } /////////////////////////////////////////////////////////////////////// private static bool ForEachFrameworkConfig( + MockRegistry registry, + FrameworkList frameworkList, FrameworkConfigCallback callback, string invariant, string name, string description, string typeName, AssemblyName assemblyName, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref bool saved, ref string error ) { - RegistryKey rootKey = frameworkRootKey; + if (registry == null) + { + error = "invalid registry"; + return false; + } + + if (frameworkList == null) + { + error = "invalid framework list"; + return false; + } + + MockRegistryKey rootKey = frameworkList.RootKey; if (rootKey == null) { error = "invalid root key"; return false; } - if (!Object.ReferenceEquals(rootKey, Registry.CurrentUser) && - !Object.ReferenceEquals(rootKey, Registry.LocalMachine)) + if (!Object.ReferenceEquals(rootKey, registry.CurrentUser) && + !Object.ReferenceEquals(rootKey, registry.LocalMachine)) { error = "root key must be per-user or per-machine"; return false; } - if (frameworkNameList == null) + if (frameworkList.Names == null) { error = "no framework names found"; return false; } - if (frameworkVersionMap == null) + if (frameworkList.Versions == null) { error = "no framework versions found"; return false; } - if (platformNameList == null) + if (frameworkList.PlatformNames == null) { error = "no platform names found"; return false; } - if (frameworkNameList.Count != platformNameList.Count) + if (frameworkList.Names.Count != frameworkList.PlatformNames.Count) { error = String.Format("framework name count {0} does not " + - "match platform name count {1}", frameworkNameList.Count, - platformNameList.Count); + "match platform name count {1}", frameworkList.Names.Count, + frameworkList.PlatformNames.Count); return false; } - for (int index = 0; index < frameworkNameList.Count; index++) + for (int index = 0; index < frameworkList.Names.Count; index++) { // // NOTE: Grab the name of the framework (e.g. ".NETFramework") // and the name of the platform (e.g. "WindowsCE"). // - string frameworkName = frameworkNameList[index]; - string platformName = platformNameList[index]; + string frameworkName = frameworkList.Names[index]; + string platformName = frameworkList.PlatformNames[index]; // // NOTE: Skip all non-desktop frameworks (i.e. if the platform // name is not null). // @@ -2620,30 +3896,32 @@ // NOTE: Grab the supported versions of this particular // framework. // VersionList frameworkVersionList; - if (!frameworkVersionMap.TryGetValue( + if (!frameworkList.Versions.TryGetValue( frameworkName, out frameworkVersionList) || (frameworkVersionList == null)) { continue; } foreach (Version frameworkVersion in frameworkVersionList) { - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.Lower, + debugCallback, traceCallback, String.Format( "frameworkName = {0}, frameworkVersion = {1}, " + "platformName = {2}", ForDisplay(frameworkName), ForDisplay(frameworkVersion), ForDisplay(platformName)), traceCategory); if (!HaveFramework( rootKey, frameworkName, frameworkVersion, - platformName, whatIf, verbose)) + platformName, wow64, whatIf, verbose)) { - TraceOps.Trace(traceCallback, + TraceOps.DebugAndTrace(TracePriority.Low, + debugCallback, traceCallback, ".NET Framework not found, skipping...", traceCategory); continue; } @@ -2650,63 +3928,69 @@ if (callback == null) continue; string directory = GetFrameworkDirectory( - rootKey, frameworkVersion, whatIf, verbose); + rootKey, frameworkVersion, wow64, whatIf, verbose); if (String.IsNullOrEmpty(directory)) { - TraceOps.Trace(traceCallback, String.Format( - ".NET Framework v{0} directory is invalid, " + - "skipping...", frameworkVersion), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Low, + debugCallback, traceCallback, String.Format( + ".NET Framework {0} directory is invalid, " + + "skipping...", ForDisplay(frameworkVersion)), + traceCategory); continue; } directory = Path.Combine(directory, "Config"); if (!Directory.Exists(directory)) { - TraceOps.Trace(traceCallback, String.Format( - ".NET Framework v{0} directory \"{1}\" does not " + - "exist, skipping...", frameworkVersion, directory), - traceCategory); + TraceOps.DebugAndTrace(TracePriority.Low, + debugCallback, traceCallback, String.Format( + ".NET Framework {0} directory {1} does not " + + "exist, skipping...", ForDisplay(frameworkVersion), + ForDisplay(directory)), traceCategory); continue; } string fileName = Path.Combine(directory, "machine.config"); if (!File.Exists(fileName)) { - TraceOps.Trace(traceCallback, String.Format( - ".NET Framework v{0} file \"{1}\" does not exist, " + - "skipping...", frameworkVersion, fileName), - traceCategory); + TraceOps.DebugAndTrace(TracePriority.Low, + debugCallback, traceCallback, String.Format( + ".NET Framework {0} file {1} does not exist, " + + "skipping...", ForDisplay(frameworkVersion), + ForDisplay(fileName)), traceCategory); continue; } bool localSaved = false; if (!callback( fileName, invariant, name, description, typeName, - assemblyName, clientData, whatIf, verbose, - ref localSaved, ref error)) + assemblyName, clientData, wow64, throwOnMissing, + whatIf, verbose, ref localSaved, ref error)) { return false; } else { if (localSaved && !saved) saved = true; if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "localSaved = {0}, saved = {1}", localSaved, - saved), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Lowest, + debugCallback, traceCallback, String.Format( + "localSaved = {0}, saved = {1}", + ForDisplay(localSaved), ForDisplay(saved)), + traceCategory); } } } return true; @@ -2713,94 +3997,112 @@ } /////////////////////////////////////////////////////////////////////// private static bool ForEachFrameworkRegistry( + MockRegistry registry, + FrameworkList frameworkList, FrameworkRegistryCallback callback, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { - RegistryKey rootKey = frameworkRootKey; + if (registry == null) + { + error = "invalid registry"; + return false; + } + + if (frameworkList == null) + { + error = "invalid framework list"; + return false; + } + + MockRegistryKey rootKey = frameworkList.RootKey; if (rootKey == null) { error = "invalid root key"; return false; } - if (!Object.ReferenceEquals(rootKey, Registry.CurrentUser) && - !Object.ReferenceEquals(rootKey, Registry.LocalMachine)) + if (!Object.ReferenceEquals(rootKey, registry.CurrentUser) && + !Object.ReferenceEquals(rootKey, registry.LocalMachine)) { error = "root key must be per-user or per-machine"; return false; } - if (frameworkNameList == null) + if (frameworkList.Names == null) { error = "no framework names found"; return false; } - if (frameworkVersionMap == null) + if (frameworkList.Versions == null) { error = "no framework versions found"; return false; } - if (platformNameList == null) + if (frameworkList.PlatformNames == null) { error = "no platform names found"; return false; } - if (frameworkNameList.Count != platformNameList.Count) + if (frameworkList.Names.Count != frameworkList.PlatformNames.Count) { error = String.Format("framework name count {0} does not " + - "match platform name count {1}", frameworkNameList.Count, - platformNameList.Count); + "match platform name count {1}", frameworkList.Names.Count, + frameworkList.PlatformNames.Count); return false; } - for (int index = 0; index < frameworkNameList.Count; index++) + for (int index = 0; index < frameworkList.Names.Count; index++) { // // NOTE: Grab the name of the framework (e.g. ".NETFramework") // and the name of the platform (e.g. "WindowsCE"). // - string frameworkName = frameworkNameList[index]; - string platformName = platformNameList[index]; + string frameworkName = frameworkList.Names[index]; + string platformName = frameworkList.PlatformNames[index]; // // NOTE: Grab the supported versions of this particular // framework. // VersionList frameworkVersionList; - if (!frameworkVersionMap.TryGetValue( + if (!frameworkList.Versions.TryGetValue( frameworkName, out frameworkVersionList) || (frameworkVersionList == null)) { continue; } foreach (Version frameworkVersion in frameworkVersionList) { - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.Lower, + debugCallback, traceCallback, String.Format( "frameworkName = {0}, frameworkVersion = {1}, " + "platformName = {2}", ForDisplay(frameworkName), ForDisplay(frameworkVersion), ForDisplay(platformName)), traceCategory); if (!HaveFramework( rootKey, frameworkName, frameworkVersion, - platformName, whatIf, verbose)) + platformName, wow64, whatIf, verbose)) { - TraceOps.Trace(traceCallback, + TraceOps.DebugAndTrace(TracePriority.Low, + debugCallback, traceCallback, ".NET Framework not found, skipping...", traceCategory); continue; } @@ -2808,12 +4110,12 @@ if (callback == null) continue; if (!callback( rootKey, frameworkName, frameworkVersion, - platformName, clientData, whatIf, verbose, - ref error)) + platformName, clientData, wow64, throwOnMissing, + whatIf, verbose, ref error)) { return false; } } } @@ -2823,62 +4125,50 @@ #endregion /////////////////////////////////////////////////////////////////////// #region Per-Visual Studio Version Handling - private static void InitializeAllVsVersions( - Configuration configuration - ) - { - if (vsRootKey == null) - vsRootKey = Registry.LocalMachine; - - if (vsAdoNetTechnologyId == null) - vsAdoNetTechnologyId = new Guid( - "77AB9A9D-78B9-4BA7-91AC-873F5338F1D2"); - - if (vsPackageId == null) - vsPackageId = new Guid( - "DCBE6C8D-0E57-4099-A183-98FF74C64D9C"); - - if (vsServiceId == null) - vsServiceId = new Guid( - "DCBE6C8D-0E57-4099-A183-98FF74C64D9D"); - - if (vsDataSourcesId == null) - vsDataSourcesId = new Guid( - "0EBAAB6E-CA80-4B4A-8DDF-CBE6BF058C71"); - - if (vsDataProviderId == null) - vsDataProviderId = new Guid( - "0EBAAB6E-CA80-4B4A-8DDF-CBE6BF058C70"); - - if (vsVersionList == null) - { - vsVersionList = new VersionList(); - - // vsVersionList.Add(new Version(8, 0)); // Visual Studio 2005 + private static void InitializeVsList( + MockRegistryKey rootKey, + Configuration configuration, + ref VsList vsList + ) + { + if (vsList == null) + vsList = new VsList(); + + if (vsList.RootKey == null) + vsList.RootKey = rootKey; + + if (vsList.Versions == null) + { + vsList.Versions = new VersionList(); + + // vsList.Versions.Add(new Version(8, 0)); // Visual Studio 2005 if ((configuration == null) || !configuration.NoVs2008) - vsVersionList.Add(new Version(9, 0)); // Visual Studio 2008 + vsList.Versions.Add(new Version(9, 0)); // Visual Studio 2008 if ((configuration == null) || !configuration.NoVs2010) - vsVersionList.Add(new Version(10, 0));// Visual Studio 2010 + vsList.Versions.Add(new Version(10, 0));// Visual Studio 2010 } } /////////////////////////////////////////////////////////////////////// private static bool HaveVsVersion( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, + bool wow64, bool whatIf, bool verbose ) { - string format = "Software\\Microsoft\\VisualStudio\\{0}"; - string keyName = String.Format(format, vsVersion); + if (vsVersion == null) + return false; + + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -2903,51 +4193,66 @@ } /////////////////////////////////////////////////////////////////////// private static bool ForEachVsVersionRegistry( + MockRegistry registry, + VsList vsList, VisualStudioRegistryCallback callback, - Guid packageId, - Guid serviceId, - Guid dataSourceId, - Guid dataProviderId, + Package package, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { - RegistryKey rootKey = vsRootKey; + if (registry == null) + { + error = "invalid registry"; + return false; + } + + if (vsList == null) + { + error = "invalid VS list"; + return false; + } + + MockRegistryKey rootKey = vsList.RootKey; if (rootKey == null) { error = "invalid root key"; return false; } - if (!Object.ReferenceEquals(rootKey, Registry.CurrentUser) && - !Object.ReferenceEquals(rootKey, Registry.LocalMachine)) + if (!Object.ReferenceEquals(rootKey, registry.CurrentUser) && + !Object.ReferenceEquals(rootKey, registry.LocalMachine)) { error = "root key must be per-user or per-machine"; return false; } - if (vsVersionList == null) + if (vsList.Versions == null) { error = "no VS versions found"; return false; } - foreach (Version vsVersion in vsVersionList) + foreach (Version vsVersion in vsList.Versions) { - TraceOps.Trace(traceCallback, String.Format( + TraceOps.DebugAndTrace(TracePriority.Lower, + debugCallback, traceCallback, String.Format( "vsVersion = {0}", ForDisplay(vsVersion)), traceCategory); - if (!HaveVsVersion(rootKey, vsVersion, whatIf, verbose)) + if (!HaveVsVersion(rootKey, vsVersion, wow64, whatIf, verbose)) { - TraceOps.Trace(traceCallback, + TraceOps.DebugAndTrace(TracePriority.Low, + debugCallback, traceCallback, "Visual Studio version not found, skipping...", traceCategory); continue; } @@ -2954,13 +4259,12 @@ if (callback == null) continue; if (!callback( - rootKey, vsVersion, packageId, serviceId, - dataSourceId, dataProviderId, clientData, whatIf, - verbose, ref error)) + rootKey, vsVersion, package, clientData, wow64, + throwOnMissing, whatIf, verbose, ref error)) { return false; } } @@ -3055,15 +4359,17 @@ { element.SetAttribute("type", fullTypeName); dirty = true; } - if (dirty) + if (dirty || whatIf) { if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "element = {0}", ForDisplay(element)), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "element = {0}", ForDisplay(element)), + traceCategory); if (!whatIf) document.Save(fileName); saved = true; @@ -3105,15 +4411,17 @@ { element.ParentNode.RemoveChild(element); dirty = true; } - if (dirty) + if (dirty || whatIf) { if (verbose) - TraceOps.Trace(traceCallback, String.Format( - "element = {0}", ForDisplay(element)), traceCategory); + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "element = {0}", ForDisplay(element)), + traceCategory); if (!whatIf) document.Save(fileName); saved = true; @@ -3130,10 +4438,12 @@ string name, string description, string typeName, AssemblyName assemblyName, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref bool saved, ref string error ) @@ -3168,37 +4478,44 @@ #region Assembly Folders Handling private static string GetAssemblyFoldersKeyName( string frameworkName, Version frameworkVersion, - string platformName + string platformName, + bool wow64 ) { + // + // NOTE: This registry key appears to always be 32-bit only + // (i.e. probably because it is only used by Visual + // Studio, which is currently always 32-bit only). + // string format = !String.IsNullOrEmpty(platformName) ? - "Software\\Microsoft\\{0}\\v{1}\\{2}\\AssemblyFoldersEx" : - "Software\\Microsoft\\{0}\\v{1}\\AssemblyFoldersEx"; + "{0}\\Microsoft\\{1}\\v{2}\\{3}\\AssemblyFoldersEx" : + "{0}\\Microsoft\\{1}\\v{2}\\AssemblyFoldersEx"; - return String.Format(format, frameworkName, frameworkVersion, - platformName); + return String.Format(format, GetRootKeyName(wow64), + frameworkName, frameworkVersion, platformName); } /////////////////////////////////////////////////////////////////////// private static bool AddToAssemblyFolders( - RegistryKey rootKey, + MockRegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, string subKeyName, string directory, + bool wow64, bool whatIf, bool verbose, ref string error ) { string keyName = GetAssemblyFoldersKeyName( - frameworkName, frameworkVersion, platformName); + frameworkName, frameworkVersion, platformName, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, true, whatIf, verbose)) { if (key == null) @@ -3231,25 +4548,27 @@ } /////////////////////////////////////////////////////////////////////// private static bool RemoveFromAssemblyFolders( - RegistryKey rootKey, + MockRegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, string subKeyName, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { string keyName = GetAssemblyFoldersKeyName( - frameworkName, frameworkVersion, platformName); + frameworkName, frameworkVersion, platformName, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( - rootKey, keyName, false, whatIf, verbose)) + rootKey, keyName, true, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", @@ -3257,24 +4576,26 @@ return false; } RegistryHelper.DeleteSubKey( - key, subKeyName, whatIf, verbose); + key, subKeyName, throwOnMissing, whatIf, verbose); } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessAssemblyFolders( - RegistryKey rootKey, + MockRegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { @@ -3288,49 +4609,77 @@ if (pair.Y) { return RemoveFromAssemblyFolders( rootKey, frameworkName, frameworkVersion, platformName, - LegacyProjectName, whatIf, verbose, ref error) && + LegacyProjectName, wow64, false, whatIf, verbose, + ref error) && AddToAssemblyFolders( rootKey, frameworkName, frameworkVersion, platformName, - ProjectName, pair.X, whatIf, verbose, ref error); + ProjectName, pair.X, wow64, whatIf, verbose, ref error); } else { return RemoveFromAssemblyFolders( rootKey, frameworkName, frameworkVersion, platformName, - ProjectName, whatIf, verbose, ref error); + ProjectName, wow64, throwOnMissing, whatIf, verbose, + ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Handling + private static string GetVsRootKeyName( + bool wow64 + ) + { + return String.Format("{0}\\Microsoft\\VisualStudio", + GetRootKeyName(wow64)); + } + + /////////////////////////////////////////////////////////////////////// + private static string GetVsKeyName( - Version vsVersion + Version vsVersion, + bool wow64 ) { - return String.Format("Software\\Microsoft\\VisualStudio\\{0}", - vsVersion); + if (vsVersion == null) + return null; + + return String.Format( + "{0}\\{1}", GetVsRootKeyName(wow64), vsVersion); } /////////////////////////////////////////////////////////////////////// #region Visual Studio Data Source Handling private static bool AddVsDataSource( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid dataSourceId, - Guid dataProviderId, + Package package, + bool wow64, bool whatIf, bool verbose, ref string error ) { - string keyName = GetVsKeyName(vsVersion); + if (vsVersion == null) + { + error = "invalid VS version"; + return false; + } + + if (package == null) + { + error = "invalid VS package"; + return false; + } + + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -3352,19 +4701,20 @@ key); return false; } - using (MockRegistryKey dataSourceKey = RegistryHelper.CreateSubKey( - subKey, dataSourceId.ToString(VsIdFormat), whatIf, - verbose)) + using (MockRegistryKey dataSourceKey = + RegistryHelper.CreateSubKey(subKey, + package.DataSourceId.ToString(VsIdFormat), + whatIf, verbose)) { if (dataSourceKey == null) { error = String.Format( - "could not create registry key: {0}\\{1}", - key, dataSourceId.ToString(VsIdFormat)); + "could not create registry key: {0}\\{1}", key, + package.DataSourceId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue( @@ -3372,12 +4722,12 @@ "{0} Database File", ProjectName), whatIf, verbose); RegistryHelper.CreateSubKey(dataSourceKey, String.Format("SupportingProviders\\{0}", - dataProviderId.ToString(VsIdFormat)), whatIf, - verbose); + package.DataProviderId.ToString(VsIdFormat)), + whatIf, verbose); } } } return true; @@ -3384,19 +4734,32 @@ } /////////////////////////////////////////////////////////////////////// private static bool RemoveVsDataSource( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid dataSourceId, + Package package, + bool wow64, bool whatIf, bool verbose, ref string error ) { - string keyName = GetVsKeyName(vsVersion); + if (vsVersion == null) + { + error = "invalid VS version"; + return false; + } + + if (package == null) + { + error = "invalid VS package"; + return false; + } + + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -3419,33 +4782,38 @@ return false; } RegistryHelper.DeleteSubKeyTree( - subKey, dataSourceId.ToString(VsIdFormat), whatIf, - verbose); + subKey, package.DataSourceId.ToString(VsIdFormat), + whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessVsDataSource( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid packageId, /* NOT USED */ - Guid serviceId, /* NOT USED */ - Guid dataSourceId, - Guid dataProviderId, + Package package, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { + if (package == null) + { + error = "invalid VS package"; + return false; + } + AnyPair pair = clientData as AnyPair; if (pair == null) { error = "invalid VS callback data"; @@ -3453,43 +4821,49 @@ } if (pair.Y) { return AddVsDataSource( - rootKey, vsVersion, dataSourceId, dataProviderId, - whatIf, verbose, ref error); + rootKey, vsVersion, package, wow64, whatIf, verbose, + ref error); } else { return RemoveVsDataSource( - rootKey, vsVersion, dataSourceId, whatIf, verbose, + rootKey, vsVersion, package, wow64, whatIf, verbose, ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Data Provider Handling private static bool AddVsDataProvider( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid serviceId, - Guid dataProviderId, + Package package, string fileName, + bool wow64, bool whatIf, bool verbose, ref string error ) { - if (vsAdoNetTechnologyId == null) + if (vsVersion == null) + { + error = "invalid VS version"; + return false; + } + + if (package == null) { - error = "invalid ADO.NET technology Id"; + error = "invalid VS package"; return false; } - string keyName = GetVsKeyName(vsVersion); + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -3511,19 +4885,20 @@ key); return false; } - using (MockRegistryKey dataProviderKey = RegistryHelper.CreateSubKey( - subKey, dataProviderId.ToString(VsIdFormat), whatIf, - verbose)) + using (MockRegistryKey dataProviderKey = + RegistryHelper.CreateSubKey(subKey, + package.DataProviderId.ToString(VsIdFormat), + whatIf, verbose)) { if (dataProviderKey == null) { error = String.Format( - "could not create registry key: {0}\\{1}", - key, dataProviderId.ToString(VsIdFormat)); + "could not create registry key: {0}\\{1}", key, + package.DataProviderId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue( @@ -3534,20 +4909,21 @@ dataProviderKey, "InvariantName", InvariantName, whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "Technology", - ((Guid)vsAdoNetTechnologyId).ToString(VsIdFormat), + package.AdoNetTechnologyId.ToString(VsIdFormat), whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "CodeBase", fileName, whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "FactoryService", - serviceId.ToString(VsIdFormat), whatIf, verbose); + package.ServiceId.ToString(VsIdFormat), whatIf, + verbose); RegistryHelper.CreateSubKey(dataProviderKey, "SupportedObjects\\DataConnectionUIControl", whatIf, verbose); @@ -3574,19 +4950,26 @@ } /////////////////////////////////////////////////////////////////////// private static bool RemoveVsDataProvider( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid dataProviderId, + Package package, + bool wow64, bool whatIf, bool verbose, ref string error ) { - string keyName = GetVsKeyName(vsVersion); + if (vsVersion == null) + { + error = "invalid VS version"; + return false; + } + + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -3609,28 +4992,27 @@ return false; } RegistryHelper.DeleteSubKeyTree( - subKey, dataProviderId.ToString(VsIdFormat), whatIf, - verbose); + subKey, package.DataProviderId.ToString(VsIdFormat), + whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessVsDataProvider( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid packageId, /* NOT USED */ - Guid serviceId, - Guid dataSourceId, /* NOT USED */ - Guid dataProviderId, + Package package, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { @@ -3643,37 +5025,76 @@ } if (pair.Y) { return AddVsDataProvider( - rootKey, vsVersion, serviceId, dataProviderId, pair.X, - whatIf, verbose, ref error); + rootKey, vsVersion, package, pair.X, wow64, whatIf, + verbose, ref error); } else { return RemoveVsDataProvider( - rootKey, vsVersion, dataProviderId, whatIf, verbose, + rootKey, vsVersion, package, wow64, whatIf, verbose, ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Package Handling + private static void InitializeVsPackage( + ref Package package + ) + { + if (package == null) + { + package = new Package(); + + package.AdoNetTechnologyId = new Guid( + "77AB9A9D-78B9-4BA7-91AC-873F5338F1D2"); + + package.PackageId = new Guid( + "DCBE6C8D-0E57-4099-A183-98FF74C64D9C"); + + package.ServiceId = new Guid( + "DCBE6C8D-0E57-4099-A183-98FF74C64D9D"); + + package.DataSourceId = new Guid( + "0EBAAB6E-CA80-4B4A-8DDF-CBE6BF058C71"); + + package.DataProviderId = new Guid( + "0EBAAB6E-CA80-4B4A-8DDF-CBE6BF058C70"); + } + } + + /////////////////////////////////////////////////////////////////////// + private static bool AddVsPackage( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid packageId, - Guid serviceId, + Package package, string fileName, + bool wow64, bool whatIf, bool verbose, ref string error ) { - string keyName = GetVsKeyName(vsVersion); + if (vsVersion == null) + { + error = "invalid VS version"; + return false; + } + + if (package == null) + { + error = "invalid VS package"; + return false; + } + + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -3695,26 +5116,27 @@ key); return false; } - using (MockRegistryKey packageKey = RegistryHelper.CreateSubKey( - subKey, packageId.ToString(VsIdFormat), whatIf, + using (MockRegistryKey packageKey = + RegistryHelper.CreateSubKey(subKey, + package.PackageId.ToString(VsIdFormat), whatIf, verbose)) { if (packageKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", - key, packageId.ToString(VsIdFormat)); + key, package.PackageId.ToString(VsIdFormat)); return false; } - RegistryHelper.SetValue(packageKey, null, String.Format( - "{0} Designer Package", ProjectName), whatIf, - verbose); + RegistryHelper.SetValue(packageKey, null, + String.Format("{0} Designer Package", ProjectName), + whatIf, verbose); RegistryHelper.SetValue(packageKey, "Class", "SQLite.Designer.SQLitePackage", whatIf, verbose); RegistryHelper.SetValue(packageKey, "CodeBase", @@ -3738,18 +5160,19 @@ whatIf, verbose); RegistryHelper.SetValue(packageKey, "ProductVersion", "1.0", whatIf, verbose); - using (MockRegistryKey toolboxKey = RegistryHelper.CreateSubKey( - packageKey, "Toolbox", whatIf, verbose)) + using (MockRegistryKey toolboxKey = + RegistryHelper.CreateSubKey(packageKey, + "Toolbox", whatIf, verbose)) { if (toolboxKey == null) { error = String.Format( - "could not create registry key: {0}\\Toolbox", - packageKey); + "could not create registry key: " + + "{0}\\Toolbox", packageKey); return false; } RegistryHelper.SetValue( @@ -3770,12 +5193,12 @@ return false; } RegistryHelper.SetValue( - subKey, packageId.ToString(VsIdFormat), ", 1000, 3", - whatIf, verbose); + subKey, package.PackageId.ToString(VsIdFormat), + ", 1000, 3", whatIf, verbose); } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Services", true, whatIf, verbose)) { @@ -3786,25 +5209,27 @@ key); return false; } - using (MockRegistryKey serviceKey = RegistryHelper.CreateSubKey( - subKey, serviceId.ToString(VsIdFormat), whatIf, + using (MockRegistryKey serviceKey = + RegistryHelper.CreateSubKey(subKey, + package.ServiceId.ToString(VsIdFormat), whatIf, verbose)) { if (serviceKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", - key, serviceId.ToString(VsIdFormat)); + key, package.ServiceId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue(serviceKey, null, - packageId.ToString(VsIdFormat), whatIf, verbose); + package.PackageId.ToString(VsIdFormat), whatIf, + verbose); RegistryHelper.SetValue(serviceKey, "Name", String.Format("{0} Designer Service", ProjectName), whatIf, verbose); } @@ -3815,20 +5240,33 @@ } /////////////////////////////////////////////////////////////////////// private static bool RemoveVsPackage( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid packageId, - Guid serviceId, + Package package, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { - string keyName = GetVsKeyName(vsVersion); + if (vsVersion == null) + { + error = "invalid VS version"; + return false; + } + + if (package == null) + { + error = "invalid VS package"; + return false; + } + + string keyName = GetVsKeyName(vsVersion, wow64); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) @@ -3851,11 +5289,12 @@ return false; } RegistryHelper.DeleteSubKeyTree( - key, packageId.ToString(VsIdFormat), whatIf, verbose); + subKey, package.PackageId.ToString(VsIdFormat), + whatIf, verbose); } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Menus", true, whatIf, verbose)) { @@ -3867,12 +5306,12 @@ return false; } RegistryHelper.DeleteValue( - subKey, packageId.ToString(VsIdFormat), whatIf, - verbose); + subKey, package.PackageId.ToString(VsIdFormat), + throwOnMissing, whatIf, verbose); } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Services", true, whatIf, verbose)) { @@ -3884,28 +5323,27 @@ return false; } RegistryHelper.DeleteSubKeyTree( - subKey, serviceId.ToString(VsIdFormat), whatIf, - verbose); + subKey, package.ServiceId.ToString(VsIdFormat), + whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessVsPackage( - RegistryKey rootKey, + MockRegistryKey rootKey, Version vsVersion, - Guid packageId, - Guid serviceId, - Guid dataSourceId, - Guid dataProviderId, + Package package, object clientData, + bool wow64, + bool throwOnMissing, bool whatIf, bool verbose, ref string error ) { @@ -3918,219 +5356,346 @@ } if (pair.Y) { return AddVsPackage( - rootKey, vsVersion, packageId, serviceId, pair.X, whatIf, + rootKey, vsVersion, package, pair.X, wow64, whatIf, verbose, ref error); } else { return RemoveVsPackage( - rootKey, vsVersion, packageId, serviceId, whatIf, verbose, - ref error); + rootKey, vsVersion, package, wow64, throwOnMissing, + whatIf, verbose, ref error); } } #endregion #endregion /////////////////////////////////////////////////////////////////////// #region Application Entry Point - private static int Main(string[] args) - { - Configuration configuration = null; - string error = null; - - /////////////////////////////////////////////////////////////////// - - #region Command Line Processing - if (!Configuration.FromArgs( - args, true, ref configuration, ref error) || - !Configuration.Process( - args, configuration, true, ref error)) - { - TraceOps.ShowMessage( - traceCallback, thisAssembly, error, traceCategory, - MessageBoxButtons.OK, MessageBoxIcon.Error); - - return 1; - } - - /////////////////////////////////////////////////////////////////// - - InitializeAllFrameworks(configuration); - InitializeAllVsVersions(configuration); - #endregion - - /////////////////////////////////////////////////////////////////// - - AnyPair directoryPair = new AnyPair( - configuration.Directory, configuration.Install); - - AnyPair fileNamePair = new AnyPair( - configuration.DesignerFileName, configuration.Install); - - /////////////////////////////////////////////////////////////////// - - #region .NET GAC Install/Remove - if (configuration.HasFlags(InstallFlags.GAC, true)) - { - Publish publish = new Publish(); - - if (configuration.Install) - { - if (!configuration.WhatIf) - { - publish.GacInstall(configuration.CoreFileName); /* throw */ - publish.GacInstall(configuration.LinqFileName); /* throw */ - } - else - { - TraceOps.Trace(traceCallback, String.Format( - "GacInstall: assemblyPath = {0}", - configuration.CoreFileName), traceCategory); - - TraceOps.Trace(traceCallback, String.Format( - "GacInstall: assemblyPath = {0}", - configuration.LinqFileName), traceCategory); - } - } - else - { - if (!configuration.WhatIf) - { - publish.GacRemove(configuration.LinqFileName); /* throw */ - publish.GacRemove(configuration.CoreFileName); /* throw */ - } - else - { - TraceOps.Trace(traceCallback, String.Format( - "GacRemove: assemblyPath = {0}", - configuration.LinqFileName), traceCategory); - - TraceOps.Trace(traceCallback, String.Format( - "GacRemove: assemblyPath = {0}", - configuration.CoreFileName), traceCategory); - } - } - } - #endregion - - /////////////////////////////////////////////////////////////////// - - #region .NET AssemblyFolders - if (configuration.HasFlags(InstallFlags.AssemblyFolders, true)) - { - if (!ForEachFrameworkRegistry(ProcessAssemblyFolders, - directoryPair, configuration.WhatIf, - configuration.Verbose, ref error)) - { - TraceOps.ShowMessage( - traceCallback, null, error, traceCategory, - MessageBoxButtons.OK, MessageBoxIcon.Error); - - return 1; - } - } - #endregion - - /////////////////////////////////////////////////////////////////// - - #region .NET DbProviderFactory - if (configuration.HasFlags(InstallFlags.DbProviderFactory, true)) - { - bool saved = false; - - if (!ForEachFrameworkConfig(ProcessDbProviderFactory, - InvariantName, ProviderName, Description, - FactoryTypeName, AssemblyName.GetAssemblyName( - configuration.CoreFileName), directoryPair, - configuration.WhatIf, configuration.Verbose, - ref saved, ref error)) - { - TraceOps.ShowMessage( - traceCallback, null, error, traceCategory, - MessageBoxButtons.OK, MessageBoxIcon.Error); - - return 1; - } - } - #endregion - - /////////////////////////////////////////////////////////////////// - - #region VS Package - if (configuration.HasFlags(InstallFlags.VsPackage, true)) - { - if (!ForEachVsVersionRegistry(ProcessVsPackage, - (Guid)vsPackageId, (Guid)vsServiceId, - (Guid)vsDataSourcesId, (Guid)vsDataProviderId, - fileNamePair, configuration.WhatIf, - configuration.Verbose, ref error)) - { - TraceOps.ShowMessage( - traceCallback, null, error, traceCategory, - MessageBoxButtons.OK, MessageBoxIcon.Error); - - return 1; - } - } - #endregion - - /////////////////////////////////////////////////////////////////// - - #region VS DataSource - if (configuration.HasFlags(InstallFlags.VsDataSource, true)) - { - if (!ForEachVsVersionRegistry(ProcessVsDataSource, - (Guid)vsPackageId, (Guid)vsServiceId, - (Guid)vsDataSourcesId, (Guid)vsDataProviderId, - fileNamePair, configuration.WhatIf, - configuration.Verbose, ref error)) - { - TraceOps.ShowMessage( - traceCallback, null, error, traceCategory, - MessageBoxButtons.OK, MessageBoxIcon.Error); - - return 1; - } - } - #endregion - - /////////////////////////////////////////////////////////////////// - - #region VS DataProvider - if (configuration.HasFlags(InstallFlags.VsDataProvider, true)) - { - if (!ForEachVsVersionRegistry(ProcessVsDataProvider, - (Guid)vsPackageId, (Guid)vsServiceId, - (Guid)vsDataSourcesId, (Guid)vsDataProviderId, - fileNamePair, configuration.WhatIf, - configuration.Verbose, ref error)) - { - TraceOps.ShowMessage( - traceCallback, null, error, traceCategory, - MessageBoxButtons.OK, MessageBoxIcon.Error); - - return 1; - } - } - #endregion - - /////////////////////////////////////////////////////////////////// - - TraceOps.Trace(traceCallback, String.Format( - "subKeysCreated = {0}, subKeysDeleted = {1}, " + - "keyValuesSet = {2}, keyValuesDeleted = {3}", - RegistryHelper.SubKeysCreated, RegistryHelper.SubKeysDeleted, - RegistryHelper.KeyValuesSet, RegistryHelper.KeyValuesDeleted), - traceCategory); - - /////////////////////////////////////////////////////////////////// - - return 0; + private static int Main( + string[] args + ) + { + try + { + Configuration configuration = null; + string error = null; + + /////////////////////////////////////////////////////////////// + + #region Command Line Processing + if (!Configuration.FromArgs( + args, true, ref configuration, ref error) || + !Configuration.Process( + args, configuration, true, ref error) || + !Configuration.CheckRuntimeVersion( + configuration, true, ref error)) + { + TraceOps.ShowMessage(TracePriority.Highest, + debugCallback, traceCallback, thisAssembly, + error, traceCategory, MessageBoxButtons.OK, + MessageBoxIcon.Error); + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Failure.", + traceCategory); + + return 1; /* FAILURE */ + } + #endregion + + /////////////////////////////////////////////////////////////// + + using (MockRegistry registry = new MockRegistry( + configuration.WhatIf, false, false)) + { + #region .NET Framework / Visual Studio Data + Package package = null; + FrameworkList frameworkList = null; + VsList vsList = null; + + /////////////////////////////////////////////////////////// + + InitializeVsPackage(ref package); + + /////////////////////////////////////////////////////////// + + InitializeFrameworkList(registry.LocalMachine, + configuration, ref frameworkList); + + InitializeVsList(registry.LocalMachine, configuration, + ref vsList); + #endregion + + /////////////////////////////////////////////////////////// + + #region Core Assembly Name Check + // + // NOTE: Do this first, before making any changes to the + // system, because it will throw an exception if the + // file name does not represent a valid managed + // assembly. + // + AssemblyName assemblyName = AssemblyName.GetAssemblyName( + configuration.CoreFileName); /* throw */ + #endregion + + /////////////////////////////////////////////////////////// + + #region Shared Client Data Creation + object directoryData = new AnyPair( + configuration.Directory, configuration.Install); + + object fileNameData = new AnyPair( + configuration.DesignerFileName, configuration.Install); + #endregion + + /////////////////////////////////////////////////////////// + + #region .NET GAC Install/Remove + if (configuration.HasFlags(InstallFlags.GAC, true)) + { + Publish publish = null; + + if (!configuration.WhatIf) + publish = new Publish(); + + if (configuration.Install) + { + if (!configuration.WhatIf) + /* throw */ + publish.GacInstall(configuration.CoreFileName); + + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "GacInstall: assemblyPath = {0}", + ForDisplay(configuration.CoreFileName)), + traceCategory); + + if (!configuration.WhatIf) + /* throw */ + publish.GacInstall(configuration.LinqFileName); + + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "GacInstall: assemblyPath = {0}", + ForDisplay(configuration.LinqFileName)), + traceCategory); + } + else + { + if (!configuration.WhatIf) + /* throw */ + publish.GacRemove(configuration.LinqFileName); + + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "GacRemove: assemblyPath = {0}", + ForDisplay(configuration.LinqFileName)), + traceCategory); + + if (!configuration.WhatIf) + /* throw */ + publish.GacRemove(configuration.CoreFileName); + + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, String.Format( + "GacRemove: assemblyPath = {0}", + ForDisplay(configuration.CoreFileName)), + traceCategory); + } + } + #endregion + + /////////////////////////////////////////////////////////// + + #region .NET AssemblyFolders + if (configuration.HasFlags( + InstallFlags.AssemblyFolders, true)) + { + if (!ForEachFrameworkRegistry(registry, + frameworkList, ProcessAssemblyFolders, + directoryData, + NetFxIs32BitOnly || configuration.Wow64, + configuration.ThrowOnMissing, + configuration.WhatIf, configuration.Verbose, + ref error)) + { + TraceOps.ShowMessage(TracePriority.Highest, + debugCallback, traceCallback, thisAssembly, + error, traceCategory, MessageBoxButtons.OK, + MessageBoxIcon.Error); + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Failure.", + traceCategory); + + return 1; /* FAILURE */ + } + } + #endregion + + /////////////////////////////////////////////////////////// + + #region .NET DbProviderFactory + if (configuration.HasFlags( + InstallFlags.DbProviderFactory, true)) + { + bool saved = false; + + if (!ForEachFrameworkConfig(registry, + frameworkList, ProcessDbProviderFactory, + InvariantName, ProviderName, Description, + FactoryTypeName, assemblyName, directoryData, + NetFxIs32BitOnly || configuration.Wow64, + configuration.ThrowOnMissing, + configuration.WhatIf, configuration.Verbose, + ref saved, ref error)) + { + TraceOps.ShowMessage(TracePriority.Highest, + debugCallback, traceCallback, thisAssembly, + error, traceCategory, MessageBoxButtons.OK, + MessageBoxIcon.Error); + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Failure.", + traceCategory); + + return 1; /* FAILURE */ + } + } + #endregion + + /////////////////////////////////////////////////////////// + + #region VS Package + if (configuration.HasFlags( + InstallFlags.VsPackage, true)) + { + // + // NOTE: For now, Visual Studio is always a 32-bit + // native application. + // + if (!ForEachVsVersionRegistry(registry, + vsList, ProcessVsPackage, package, + fileNameData, + VsIs32BitOnly || configuration.Wow64, + configuration.ThrowOnMissing, + configuration.WhatIf, configuration.Verbose, + ref error)) + { + TraceOps.ShowMessage(TracePriority.Highest, + debugCallback, traceCallback, thisAssembly, + error, traceCategory, MessageBoxButtons.OK, + MessageBoxIcon.Error); + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Failure.", + traceCategory); + + return 1; /* FAILURE */ + } + } + #endregion + + /////////////////////////////////////////////////////////// + + #region VS DataSource + if (configuration.HasFlags( + InstallFlags.VsDataSource, true)) + { + // + // NOTE: For now, Visual Studio is always a 32-bit + // native application. + // + if (!ForEachVsVersionRegistry(registry, + vsList, ProcessVsDataSource, package, + fileNameData, + VsIs32BitOnly || configuration.Wow64, + configuration.ThrowOnMissing, + configuration.WhatIf, configuration.Verbose, + ref error)) + { + TraceOps.ShowMessage(TracePriority.Highest, + debugCallback, traceCallback, thisAssembly, + error, traceCategory, MessageBoxButtons.OK, + MessageBoxIcon.Error); + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Failure.", + traceCategory); + + return 1; /* FAILURE */ + } + } + #endregion + + /////////////////////////////////////////////////////////// + + #region VS DataProvider + if (configuration.HasFlags( + InstallFlags.VsDataProvider, true)) + { + // + // NOTE: For now, Visual Studio is always a 32-bit + // native application. + // + if (!ForEachVsVersionRegistry(registry, + vsList, ProcessVsDataProvider, package, + fileNameData, + VsIs32BitOnly || configuration.Wow64, + configuration.ThrowOnMissing, + configuration.WhatIf, configuration.Verbose, + ref error)) + { + TraceOps.ShowMessage(TracePriority.Highest, + debugCallback, traceCallback, thisAssembly, + error, traceCategory, MessageBoxButtons.OK, + MessageBoxIcon.Error); + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Failure.", + traceCategory); + + return 1; /* FAILURE */ + } + } + #endregion + + /////////////////////////////////////////////////////////// + + #region Log Summary + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, String.Format( + "subKeysCreated = {0}, subKeysDeleted = {1}, " + + "keyValuesSet = {2}, keyValuesDeleted = {3}", + ForDisplay(RegistryHelper.SubKeysCreated), + ForDisplay(RegistryHelper.SubKeysDeleted), + ForDisplay(RegistryHelper.KeyValuesSet), + ForDisplay(RegistryHelper.KeyValuesDeleted)), + traceCategory); + #endregion + + /////////////////////////////////////////////////////////// + + TraceOps.DebugAndTrace(TracePriority.MediumHigh, + debugCallback, traceCallback, "Success.", + traceCategory); + + return 0; /* SUCCESS */ + } + } + catch (Exception e) + { + TraceOps.DebugAndTrace(TracePriority.Highest, + debugCallback, traceCallback, e, traceCategory); + + throw; + } } #endregion } #endregion } Index: tools/install/Properties/AssemblyInfo.cs ================================================================== --- tools/install/Properties/AssemblyInfo.cs +++ tools/install/Properties/AssemblyInfo.cs @@ -1,7 +1,6 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. @@ -27,7 +26,7 @@ // Major Version // Minor Version // Build Number // Revision // -[assembly: AssemblyVersion("1.0.76.0")] -[assembly: AssemblyFileVersion("1.0.76.0")] +[assembly: AssemblyVersion("1.0.78.0")] +[assembly: AssemblyFileVersion("1.0.78.0")] ADDED tools/install/Resources/manifest.xml Index: tools/install/Resources/manifest.xml ================================================================== --- /dev/null +++ tools/install/Resources/manifest.xml @@ -0,0 +1,10 @@ + + + + + + + + + + Index: www/build.wiki ================================================================== --- www/build.wiki +++ www/build.wiki @@ -75,18 +75,25 @@ Setup 5" for an 64-bit machines. Alternatively, the Inno Setup directory may be included in the PATH environment variable.
  • - The string "<root>" represents the root of your source tree - for the System.Data.SQLite project. + The string "<root>" represents the root directory of your + local source tree (i.e. the working check-out directory) for the + System.Data.SQLite project.
  • The string "<year>" represents the version of Visual Studio being used (e.g. 2008).
  • + +
  • + For now, the project will always be built on modern Windows using the .NET + Framework even when they will eventually be deployed to run under Mono on + Unix. +
  • All Builds

    @@ -108,18 +115,20 @@
  • Make sure the version information is correct for System.Data.SQLite in the following files:
      +
    • <root>\readme.htm
    • <root>\SQLite.nuspec
    • <root>\SQLite.MSIL.nuspec
    • <root>\SQLite.x86.nuspec
    • <root>\SQLite.x64.nuspec
    • <root>\Doc\Extra\dbfactorysupport.html
    • <root>\Doc\Extra\welcome.html
    • <root>\Membership\Properties\AssemblyInfo.cs
    • <root>\SQLite.Designer\AssemblyInfo.cs
    • +
    • <root>\SQLite.Designer\source.extension.vsixmanifest
    • <root>\SQLite.Interop\props\SQLite.Interop.vsprops
    • <root>\SQLite.Interop\props\SQLite.Interop.props
    • <root>\SQLite.Interop\src\win\interop.h
    • <root>\System.Data.SQLite\AssemblyInfo.cs
    • <root>\System.Data.SQLite\SQLite3.cs
    • @@ -207,15 +216,26 @@
    • Open a normal command prompt window with "cmd.exe".
    • Change the current directory to "<root>\Setup".
    • +
    • + Enter the following command to build the managed-only binaries: +  build.bat ReleaseManagedOnly +
    • + +
    • + Make sure everything succeeds with no errors; the log file + "%TEMP%\System.Data.SQLite.Build_ReleaseManagedOnly_Win32_<year>_Unknown.log" + may be checked if any errors should occur. +
    • +
    • Enter the following command to build the binaries for Win32 (x86):  - build.bat ReleaseNativeOnly Win32
      You may need to enter the - command "setenv /x86" first if you are using a - "Windows SDK Command Prompt" or "Visual Studio Command + build.bat ReleaseNativeOnly Win32
      You may need to + enter the command "setenv /x86" first if you are using + a "Windows SDK Command Prompt" or "Visual Studio Command Prompt" window.
    • Make sure everything succeeds with no errors; the log file @@ -223,13 +243,14 @@ may be checked if any errors should occur.
    • Enter the following command to build the binaries for x64: build.bat - ReleaseNativeOnly x64
      You may need to enter the command - "setenv /x64" first if you are using a "Windows SDK - Command Prompt" or "Visual Studio Command Prompt" window. +  ReleaseNativeOnly x64
      You may need to enter the + command"setenv /x64" first if you are using a + "Windows SDK Command Prompt" or "Visual Studio Command + Prompt" window.
    • Make sure everything succeeds with no errors; the log file "%TEMP%\System.Data.SQLite.Build_ReleaseNativeOnly_x64_<year>_Unknown.log" @@ -248,5 +269,42 @@ "success" messages very similar to the following:  Successful compile (X.XXX sec). Resulting Setup program filename is: abc
    • + + +

      Mono Build

      + +
        +
      1. + Complete the steps outlined in the [./build.wiki#all | All Builds] section + (above). +
      2. + +
      3. + Make sure the "<root>\bin" and "<root>\obj" + directories are completely free of all output files. In theory, you should + be able to simply delete these directories. +
      4. + +
      5. Open a normal command prompt window with "cmd.exe".
      6. + +
      7. Change the current directory to "<root>\Setup".
      8. + +
      9. + Enter the following command to set the environment variable used to pass + the necessary extra arguments to MSBuild: +  SET MSBUILD_ARGS=/property:UseInteropDll=false /property:UseSqliteStandard=true +
      10. + +
      11. + Enter the following command to build the managed-only binaries for Mono: +  build.bat ReleaseManagedOnly +
      12. + +
      13. + Make sure everything succeeds with no errors; the log file + "%TEMP%\System.Data.SQLite.Build_ReleaseManagedOnly_Win32_<year>_Unknown.log" + may be checked if any errors should occur. +
      14. +
      Index: www/downloads.wiki ================================================================== --- www/downloads.wiki +++ www/downloads.wiki @@ -10,20 +10,20 @@
  •   - sqlite-netFx-source-1.0.76.0.zip + sqlite-netFx-source-1.0.78.0.zip
    - (2.53 MiB) + (2.61 MiB)
    This ZIP archive contains all current source code for System.Data.SQLite - 1.0.76.0 (3.7.8) combined into a single archive file. + 1.0.78.0 (3.7.10) combined into a single archive file.
    - (sha1: fba56449601e4f023aa450c1c5471ee58d424b2d) + (sha1: f4544ba980f0ed0576858f6ee232176b52c3687f)
    @@ -32,40 +32,40 @@
      - sqlite-netFx35-setup-bundle-x86-2008-1.0.76.0.exe + sqlite-netFx35-setup-bundle-x86-2008-1.0.78.0.exe
    - (5.86 MiB) + (6.02 MiB)
    This setup package features the mixed-mode assembly and will install all the necessary runtime components and dependencies for the x86 version of - the System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2008 + the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x86 is included. The .NET Framework 3.5 SP1 is required.
    - (sha1: 1f9c794d866e23f9e8828fad13e8d062610cf310) + (sha1: 3c4cce8075de8a1233ecd2923b7050092159ffb0)
      - sqlite-netFx35-setup-x86-2008-1.0.76.0.exe + sqlite-netFx35-setup-x86-2008-1.0.78.0.exe
    - (5.86 MiB) + (6.02 MiB)
    This setup package will install all the necessary runtime components and - dependencies for the x86 version of the System.Data.SQLite 1.0.76.0 - (3.7.8) package. The Visual C++ 2008 SP1 runtime for x86 is included. + dependencies for the x86 version of the System.Data.SQLite 1.0.78.0 + (3.7.10) package. The Visual C++ 2008 SP1 runtime for x86 is included. The .NET Framework 3.5 SP1 is required.
    - (sha1: e0585ea3563eb838bdd8833bd64ebff9802efd0f) + (sha1: 4ea35e63575d6027cce76f35f7cad33bae2bdf92)
    @@ -74,40 +74,40 @@
      - sqlite-netFx35-setup-bundle-x64-2008-1.0.76.0.exe + sqlite-netFx35-setup-bundle-x64-2008-1.0.78.0.exe
    - (6.60 MiB) + (6.75 MiB)
    This setup package features the mixed-mode assembly and will install all the necessary runtime components and dependencies for the x64 version of - the System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2008 + the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x64 is included. The .NET Framework 3.5 SP1 is required.
    - (sha1: 75389cd822172c1690ab80c79229ca992d0d127e) + (sha1: 31f223f6776ad2d9d1d17373812618f101f1bd89)
      - sqlite-netFx35-setup-x64-2008-1.0.76.0.exe + sqlite-netFx35-setup-x64-2008-1.0.78.0.exe
    - (6.60 MiB) + (6.75 MiB)
    This setup package will install all the necessary runtime components and - dependencies for the x64 version of the System.Data.SQLite 1.0.76.0 - (3.7.8) package. The Visual C++ 2008 SP1 runtime for x64 is included. + dependencies for the x64 version of the System.Data.SQLite 1.0.78.0 + (3.7.10) package. The Visual C++ 2008 SP1 runtime for x64 is included. The .NET Framework 3.5 SP1 is required.
    - (sha1: 1a81ecdb4062d0c913551d74dee5eb477677aa00) + (sha1: fe071872f49710fb9f094dad594baacf66c446ca)
    @@ -116,40 +116,40 @@
      - sqlite-netFx40-setup-bundle-x86-2010-1.0.76.0.exe + sqlite-netFx40-setup-bundle-x86-2010-1.0.78.0.exe
    - (10.23 MiB) + (10.35 MiB)
    This setup package features the mixed-mode assembly and will install all the necessary runtime components and dependencies for the x86 version of - the System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2010 + the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x86 is included. The .NET Framework 4.0 is required.
    - (sha1: 0f48a58182afb4aeca22428885520f1359155bbb) + (sha1: 49c1e5fdc6eb21caffae87baf583bbdf1a2b5319)
      - sqlite-netFx40-setup-x86-2010-1.0.76.0.exe + sqlite-netFx40-setup-x86-2010-1.0.78.0.exe
    - (10.23 MiB) + (10.34 MiB)
    This setup package will install all the necessary runtime components and - dependencies for the x86 version of the System.Data.SQLite 1.0.76.0 - (3.7.8) package. The Visual C++ 2010 SP1 runtime for x86 is included. + dependencies for the x86 version of the System.Data.SQLite 1.0.78.0 + (3.7.10) package. The Visual C++ 2010 SP1 runtime for x86 is included. The .NET Framework 4.0 is required.
    - (sha1: 9752bf41e287ff4781673cf79e3a0413c8229997) + (sha1: a2a8cbda939a3f954f3caaf2973e27e95be9ad7c)
    @@ -158,40 +158,40 @@
      - sqlite-netFx40-setup-bundle-x64-2010-1.0.76.0.exe + sqlite-netFx40-setup-bundle-x64-2010-1.0.78.0.exe
    - (11.44 MiB) + (11.60 MiB)
    This setup package features the mixed-mode assembly and will install all the necessary runtime components and dependencies for the x64 version of - the System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2010 + the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x64 is included. The .NET Framework 4.0 is required.
    - (sha1: 86dfd84874d322f0cdb4b3807d02f19b86c28bc6) + (sha1: dadde81326ed7872b30249aab0ed271afc2d6aac)
      - sqlite-netFx40-setup-x64-2010-1.0.76.0.exe + sqlite-netFx40-setup-x64-2010-1.0.78.0.exe
    - (11.44 MiB) + (11.59 MiB)
    This setup package will install all the necessary runtime components and - dependencies for the x64 version of the System.Data.SQLite 1.0.76.0 - (3.7.8) package. The Visual C++ 2010 SP1 runtime for x64 is included. + dependencies for the x64 version of the System.Data.SQLite 1.0.78.0 + (3.7.10) package. The Visual C++ 2010 SP1 runtime for x64 is included. The .NET Framework 4.0 is required.
    - (sha1: 7184b5ff13deb8692a85d0c8c4a06f2f8c64ae4b) + (sha1: 9bf3beb11b9021c76543405fc89e841335f0d9ac)
    @@ -200,39 +200,39 @@
      - sqlite-netFx35-binary-bundle-Win32-2008-1.0.76.0.zip + sqlite-netFx35-binary-bundle-Win32-2008-1.0.78.0.zip
    - (1.66 MiB) + (1.61 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x86 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x86 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x86 and the .NET Framework 3.5 SP1 are required.
    - (sha1: c11309defe95d31acba09e2960bf5f8398772e1f) + (sha1: 9b769fd226d1d30f7caf37363e9c1afe7ca27ac3)
      - sqlite-netFx35-binary-Win32-2008-1.0.76.0.zip + sqlite-netFx35-binary-Win32-2008-1.0.78.0.zip
    - (1.65 MiB) + (1.60 MiB)
    This binary package contains all the binaries for the x86 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2008 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x86 and the .NET Framework 3.5 SP1 are required.
    - (sha1: 8c34d86a88487b2c13b3738222be5a2f546d828b) + (sha1: f4a211fd8fa813eb9e60eca516652a4b1c947f00)
    @@ -241,39 +241,39 @@
      - sqlite-netFx35-binary-bundle-x64-2008-1.0.76.0.zip + sqlite-netFx35-binary-bundle-x64-2008-1.0.78.0.zip
    - (1.46 MiB) + (1.67 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x64 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x64 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x64 and the .NET Framework 3.5 SP1 are required.
    - (sha1: 715cb5c6ded65c143949e91a9e338e86181d7e9b) + (sha1: 2e68a40cb5034f325a5517c7f35ebfd358f0c7c3)
      - sqlite-netFx35-binary-x64-2008-1.0.76.0.zip + sqlite-netFx35-binary-x64-2008-1.0.78.0.zip
    - (1.45 MiB) + (1.66 MiB)
    This binary package contains all the binaries for the x64 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2008 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x64 and the .NET Framework 3.5 SP1 are required.
    - (sha1: 20c08bfb7314489f11b813973464746b97195b88) + (sha1: ee5e88536631edd7c93a3de10cbef3c7e737a4c0)
    @@ -282,39 +282,39 @@
      - sqlite-netFx40-binary-bundle-Win32-2010-1.0.76.0.zip + sqlite-netFx40-binary-bundle-Win32-2010-1.0.78.0.zip
    - (1.44 MiB) + (1.65 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x86 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x86 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x86 and the .NET Framework 4.0 are required.
    - (sha1: cf0efe272bffb48313b9f7781b8de723fb4ea5d2) + (sha1: 96a79eca509b51a855c02d26320be1cf7004020f)
      - sqlite-netFx40-binary-Win32-2010-1.0.76.0.zip + sqlite-netFx40-binary-Win32-2010-1.0.78.0.zip
    - (1.43 MiB) + (1.64 MiB)
    This binary package contains all the binaries for the x86 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2010 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x86 and the .NET Framework 4.0 are required.
    - (sha1: 12d98305a9bd4860bfa98f1ffd7207df67022323) + (sha1: 147164217cb8d69d72db15db2149eae5f1698ebc)
    @@ -323,39 +323,39 @@
      - sqlite-netFx40-binary-bundle-x64-2010-1.0.76.0.zip + sqlite-netFx40-binary-bundle-x64-2010-1.0.78.0.zip
    - (1.46 MiB) + (1.68 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x64 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x64 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x64 and the .NET Framework 4.0 are required.
    - (sha1: 0bb3ebde1b9a0838e911de8eb2a9608cd2f95010) + (sha1: e9b605de964199143f725c1edcefd72a572cf4f8)
      - sqlite-netFx40-binary-x64-2010-1.0.76.0.zip + sqlite-netFx40-binary-x64-2010-1.0.78.0.zip
    - (1.45 MiB) + (1.67 MiB)
    This binary package contains all the binaries for the x64 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2010 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x64 and the .NET Framework 4.0 are required.
    - (sha1: f93461445c3b36586b5eed8e78acd32c0be01f75) + (sha1: c55d638e110cad1e95884abb5a0bbf022e6485c3)
    @@ -364,40 +364,40 @@
      - sqlite-netFx35-static-binary-bundle-Win32-2008-1.0.76.0.zip + sqlite-netFx35-static-binary-bundle-Win32-2008-1.0.78.0.zip
    - (1.61 MiB) + (1.82 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x86 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x86 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x86 is statically linked. The .NET Framework 3.5 SP1 is required.
    - (sha1: 286a2fafef12dfc15810b834c37007a2c97f7bbb) + (sha1: e16cf8b7481382d8ca609c30c6026cc3e91dab6e)
      - sqlite-netFx35-static-binary-Win32-2008-1.0.76.0.zip + sqlite-netFx35-static-binary-Win32-2008-1.0.78.0.zip
    - (1.60 MiB) + (1.82 MiB)
    This binary package contains all the binaries for the x86 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2008 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x86 is statically linked. The .NET Framework 3.5 SP1 is required.
    - (sha1: bb1babce67aa349352ca2ad135d8932702d3859d) + (sha1: 44b65a7262c1e2509411151c090b56e05f744fec)
    @@ -406,40 +406,40 @@
      - sqlite-netFx35-static-binary-bundle-x64-2008-1.0.76.0.zip + sqlite-netFx35-static-binary-bundle-x64-2008-1.0.78.0.zip
    - (1.64 MiB) + (1.85 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x64 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x64 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x64 is statically linked. The .NET Framework 3.5 SP1 is required.
    - (sha1: 4e88bb3125718e19f869bd9d5ffdd60faee275fb) + (sha1: 825355614fd32904c9bb5b8758a03647c29a28ed)
      - sqlite-netFx35-static-binary-x64-2008-1.0.76.0.zip + sqlite-netFx35-static-binary-x64-2008-1.0.78.0.zip
    - (1.63 MiB) + (1.84 MiB)
    This binary package contains all the binaries for the x64 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2008 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2008 SP1 runtime for x64 is statically linked. The .NET Framework 3.5 SP1 is required.
    - (sha1: 8cbea2faa17623d49a407e591f663ce976422b33) + (sha1: 4f78f2c1a5e2a0973ea1c6dd036336a8b84465d5)
    @@ -448,39 +448,39 @@
      - sqlite-netFx40-static-binary-bundle-Win32-2010-1.0.76.0.zip + sqlite-netFx40-static-binary-bundle-Win32-2010-1.0.78.0.zip
    - (1.66 MiB) + (1.87 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x86 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x86 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x86 is statically linked. The .NET Framework 4.0 is required.
    - (sha1: 7942b03061317c3297882a2d9a155abe3a9dca68) + (sha1: 2c86476f8256906527eb48a5138dbe128b3eeae9)
      - sqlite-netFx40-static-binary-Win32-2010-1.0.76.0.zip + sqlite-netFx40-static-binary-Win32-2010-1.0.78.0.zip
    - (1.65 MiB) + (1.86 MiB)
    This binary package contains all the binaries for the x86 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2010 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x86 is statically linked. The .NET Framework 4.0 is required.
    - (sha1: 56763c0e40f347ed1c66917c52dc2f5fd32c572e) + (sha1: 1f0946c3089c4ac064b7bd15b07f9db88c16166e)
    @@ -489,39 +489,39 @@
      - sqlite-netFx40-static-binary-bundle-x64-2010-1.0.76.0.zip + sqlite-netFx40-static-binary-bundle-x64-2010-1.0.78.0.zip
    - (1.40 MiB) + (1.87 MiB)
    This binary package features the mixed-mode assembly and contains all the - binaries for the x64 version of the System.Data.SQLite 1.0.76.0 (3.7.8) + binaries for the x64 version of the System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x64 is statically linked. The .NET Framework 4.0 is required.
    - (sha1: ffa622c031a4bdb9de3dd32839fd261b580426cf) + (sha1: b973c5420ab30a2911310cdb9cd9f0b62d7abc96)
      - sqlite-netFx40-static-binary-x64-2010-1.0.76.0.zip + sqlite-netFx40-static-binary-x64-2010-1.0.78.0.zip
    - (1.39 MiB) + (1.86 MiB)
    This binary package contains all the binaries for the x64 version of the - System.Data.SQLite 1.0.76.0 (3.7.8) package. The Visual C++ 2010 SP1 + System.Data.SQLite 1.0.78.0 (3.7.10) package. The Visual C++ 2010 SP1 runtime for x64 is statically linked. The .NET Framework 4.0 is required.
    - (sha1: 36411ff1712baf96378c4bfdc4bbd08376dc4f5c) + (sha1: cdc0a0216cec67e0de5cfdd35e15df895ec580dd)
    @@ -530,21 +530,21 @@
      - sqlite-netFx35-binary-PocketPC-2008-1.0.76.0.zip + sqlite-netFx35-binary-PocketPC-2008-1.0.78.0.zip
    - (0.79 MiB) + (0.81 MiB)
    This binary package contains all the binaries for the PocketPC version of - the System.Data.SQLite 1.0.76.0 (3.7.8) package. The .NET Compact + the System.Data.SQLite 1.0.78.0 (3.7.10) package. The .NET Compact Framework 3.5 is required.
    - (sha1: 3168092dbacd5faf1140e4fb8637603b237d67c8) + (sha1: 7a8ff81a4669bb44dff00c8b1536da3638cebeea)
    ADDED www/faq.wiki Index: www/faq.wiki ================================================================== --- /dev/null +++ www/faq.wiki @@ -0,0 +1,446 @@ +Frequently Asked Questions + + +

    Frequently Asked Questions

    + +
      +
    1. + When will the next version of System.Data.SQLite be + released? +
    2. +
      +
    3. + When are you planning on adding feature "X"? +
    4. +
      +
    5. + What versions of .NET Framework are supported? +
    6. +
      +
    7. + What versions of Visual Studio are supported? +
    8. +
      +
    9. + Is there a NuGet package? +
    10. +
      +
    11. + How do I build the binaries for Mono? +
    12. +
      +
    13. + How do I build the binaries for .NET Compact Framework? +
    14. +
      +
    15. + How do I install System.Data.SQLite on a development + machine? +
    16. +
      +
    17. + How do I install System.Data.SQLite on end-user + machines? +
    18. +
      +
    19. + Do I need to add an assembly reference to the + "sqlite3.dll" or "SQLite.Interop.dll" in + my project? +
    20. +
      +
    21. + Why do I get a DllNotFoundException (for + "sqlite3.dll" or "SQLite.Interop.dll") when trying to + run my application? +
    22. +
      +
    23. + Why do I get a BadImageFormatException (for + "sqlite3.dll" or "SQLite.Interop.dll") when trying to + run my application? +
    24. +
      +
    25. + Why do I get the error "This assembly is built by a + runtime newer than the currently loaded runtime and cannot be loaded. + "? +
    26. +
      +
    27. + What is a mixed-mode assembly? +
    28. +
      +
    29. + What is a "bundle" package (i.e. from the + download page)? +
    30. +
      +
    31. + What is the difference between the "Setup" + and "Precompiled Binary" packages (i.e. from the download + page)? +
    32. +
      +
    33. + Why is System.Data.SQLite leaking memory, resources, + etc? +
    34. +
      +
    35. + What are the support options for System.Data.SQLite? +
    36. +
      +
    37. + When the solution is loaded in Visual Studio, why do no files + show up for several of the projects in the Solution Explorer window? + +
    38. +
    + +
    + +

    + (1) When will the next version of System.Data.SQLite be released? +

    + +

    + The release schedule for the System.Data.SQLite project is roughly + synchronized (within about two or three weeks) with that of the + [http://www.sqlite.org/ | SQLite] core itself. The release history for the + System.Data.SQLite project is [./news.wiki | here]. +

    + +
    + +

    + (2) When are you planning on adding feature "X"? +

    + +

    + This question is hard to answer precisely. It depends on a number of factors, + including but not limited to: +

      +
    • + Can the feature be implemented in a backward compatible manner? +
    • + +
    • + Can the feature be implemented in a portable fashion for all the currently + supported versions of the .NET Framework and/or Visual Studio? +
    • + +
    • + Does the feature fit well with the current design of the project? +
    • + +
    • + How much time will it take to design, implement, and test the feature? +
    • + +
    • + Will the feature benefit the entire community or only a tiny subset + thereof? +
    • +
    +

    + +
    + +

    + (3) What versions of .NET Framework are supported? +

    + +

    +

      +
    • + The .NET Framework 2.0 SP2 (or higher) for the System.Data.SQLite assembly. +
    • + +
    • + The .NET Framework 3.5 SP1 (or higher) for the System.Data.SQLite.Linq + assembly. +
    • + +
    • + All sub-projects are fully supported with the .NET Framework 4.0. +
    • +
    +

    + +
    + +

    + (4) What versions of Visual Studio are supported? +

    + +

    + Currently, Visual Studio 2005, 2008, and 2010 are supported, including the + "Express" editions; however, in order to build the entire solution, + including the necessary native code, the "Professional" edition (or + higher) is required. It may be possible to install both Visual C# Express + and Visual C++ Express and then build the corresponding sub-projects via their + respective integrated development environments (IDE); however, this + configuration has not been tested. The design-time components are + no longer supported for the Express editions due to licensing + restrictions. +

    + +
    + +

    + (5) Is there a NuGet package? +

    + +

    + Yes, the following official NuGet packages are available: + +

      +
    • + + System.Data.SQLite: The SQLite database engine combined with a + complete ADO.NET provider all rolled into a single mixed-mode assembly for + x86. +
    • + +
    • + + System.Data.SQLite.x86: This currently contains the same content as + the "System.Data.SQLite" package, above. +
    • + +
    • + + System.Data.SQLite.x64: The SQLite database engine combined with a + complete ADO.NET provider all rolled into a single mixed-mode assembly for + x64. +
    • + +
    • + + System.Data.SQLite.MSIL: Just the ADO.NET provider for SQLite + (managed-only). +
    • +
    +

    + +
    + +

    + (6) How do I build the binaries for Mono? +

    + +

    + This is documented on the [./build.wiki#mono | build procedures] page. +

    + +
    + +

    + (7) How do I build the binaries for .NET Compact Framework? +

    + +

    + This is documented on the [./release.wiki#buildCeBinaries | release procedures] + page. +

    + +
    + +

    + (8) How do I install System.Data.SQLite on a development machine? +

    + +

    + Strictly speaking, there is no need to install System.Data.SQLite on any + development machine (e.g. via the setup). The recommended way to use the + assemblies is: + +

      +
    • + Download the precompiled binary package for your target framework and + processor architecture (e.g. 32-bit x86, .NET Framework 2.0). +
    • + +
    • + Extract the package to a directory named "Externals" inside your project + directory. +
    • + +
    • + Add a reference to the "System.Data.SQLite" assembly from the "Externals" + directory. +
    • + +
    • + If necessary (i.e. you require LINQ support), also add a reference to the + "System.Data.SQLite.Linq" assembly from the "Externals" directory. +
    • +
    +

    + +

    + Alternatively, when using Visual Studio 2010, you can simply use the NuGet + package that corresponds to your target processor architecture. Installing + the assemblies into the Global Assembly Cache is not recommended as it may + cause conflicts with other applications installed on the machine. +

    + +
    + +

    + (9) How do I install System.Data.SQLite on end-user machines? +

    + +

    + Strictly speaking, there is no need to install System.Data.SQLite on any + end-user machine (e.g. via the setup). The recommended way to deploy the + assemblies is "application local" (i.e. copy them to the directory + the application is installed to). Installing the assemblies into the Global + Assembly Cache is not recommended as it may cause conflicts with other + applications installed on the machine. +

    + +
    + +

    + (10) Do I need to add an assembly reference to the + "sqlite3.dll" or "SQLite.Interop.dll" in my project? +

    + +

    + No, because they are not managed assemblies and contain no managed code. +

    + +
    + +

    + (11) Why do I get a DllNotFoundException (for "sqlite3.dll" + or "SQLite.Interop.dll") when trying to run my application? +

    + +

    + Either the named dynamic link library (DLL) cannot be located or it cannot be + loaded due to missing dependencies. Make sure the named dynamic link library + is located in the application directory or a directory along the system PATH + and try again. Also, be sure the necessary Visual C++ runtime redistributable + has been installed unless you are using a dynamic link library that was built + statically linked to it. +

    + +
    + +

    + (12) Why do I get a BadImageFormatException (for "sqlite3.dll" or + "SQLite.Interop.dll") when trying to run my application? +

    + +

    + The named dynamic link library (DLL) contains native code that was built for a + processor architecture that is not compatible with the current process (e.g. + you cannot load a 32-bit dynamic link library into a 64-bit process or + vice-versa). Another explanation is that the named dynamic link library was + built for a later version of the CLR than is available in the current process + (e.g. you cannot load an assembly built for the .NET Framework 4.0 into a .NET + Framework 2.0 process, regardless of the processor architecture). +

    + +
    + +

    + (13) Why do I get the error "This assembly is built by a runtime newer + than the currently loaded runtime and cannot be loaded."? +

    + +

    + Because the assembly was built for the .NET Framework 4.0 and you are trying + to load it into a process that is using the .NET Framework 2.0. +

    + +
    + +

    + (14) What is a mixed-mode assembly? +

    + +

    + A mixed-mode assembly is a dynamic link library that contains both managed + code and native code for a particular processor architecture. Since it + contains native code it can only be loaded into a process that matches the + processor architecture it was compiled for. Also see this + StackOverflow question. +

    + +
    + +

    + (15) What is a "bundle" package (i.e. from the download page)? +

    + +

    + The "bundle" packages listed on the download page contains the + System.Data.SQLite mixed-mode assembly in a file named + "System.Data.SQLite.dll" (see question #14) + instead of separate "System.Data.SQLite.dll" and + "SQLite.Interop.dll" files to contain the managed code and native + code, respectively. +

    + +
    + +

    + (16) What is the difference between the "Setup" and + "Precompiled Binary" packages (i.e. from the download page)? +

    + +

    + The "Setup" packages are designed to install the necessary files, + optionally installing the assemblies into the Global Assembly Cache, + generating native images for the managed assemblies via Ngen, adding Start + Menu shortcuts, modifying the .NET Framework machine configuration files to + register the ADO.NET provider, and installing the design-time components for + Visual Studio. The "Precompiled Binary" packages are simply ZIP + files that contain all the binaries compiled for a particular .NET Framework + and processor architecture. +

    + +
    + +

    + (17) Why is System.Data.SQLite leaking memory, resources, etc? +

    + +

    + All System.Data.SQLite objects that implement IDisposable, either directly or + indirectly, should be explicitly disposed when they are no longer needed. If + this is the case and you are still seeing a leak of some kind, please file a + ticket. +

    + +
    + +

    + (18) What are the support options for System.Data.SQLite? +

    + +

    + This is discussed on the [./support.wiki | support] page. +

    + +
    + +

    + (19) When the solution is loaded in Visual Studio, why do no files show up + for several of the projects in the Solution Explorer window? +

    + +

    + Several of the sub-projects (i.e. primarily those that build variants of the + System.Data.SQLite assembly) share an MSBuild "targets" file that + contains the actual references to the C# source code files. Unfortunately, + due to limitations on how Visual Studio reads and interprets MSBuild files at + design-time, the C# source code files do not show up in the Solution Explorer + window. This limitation is largely cosmetic and does not impact the + correctness of the build process itself, whether in Visual Studio or when + using MSBuild on the command line. +

    Index: www/features.wiki ================================================================== --- www/features.wiki +++ www/features.wiki @@ -37,13 +37,12 @@ 2005/2008/2010. You can add a SQLite database to the Servers list, design queries with the Query Designer, drag-and-drop tables onto a Typed DataSet, etc.
    - Currently not included. We are still updating the design-time support - installer. Due to Visual Studio licensing restrictions, the Express - Editions can no longer be supported. + Due to Visual Studio licensing restrictions, the Express Editions can no + longer be supported.
  • Full SQLite schema editing inside Visual Studio. You can create/edit tables, Index: www/index.wiki ================================================================== --- www/index.wiki +++ www/index.wiki @@ -3,14 +3,16 @@ @@ -30,10 +32,22 @@ Historical versions, as well as the original support forums, may still be found at http://sqlite.phxsoftware.com/, though there have been no updates to this version since April of 2010.

    +

    +   +

    + +

    +   +

    + +

    +   +

    +

     

    Index: www/news.wiki ================================================================== --- www/news.wiki +++ www/news.wiki @@ -1,111 +1,149 @@ -News - -Version History - -

    - 1.0.76.0 - October 4, 2011 -

    -
      -
    • Prevent the domain unload event handler in SQLiteLog from being registered multiple times. Fix for [0d5b1ef362].
    • -
    • Stop allowing non-default application domains to initialize the SQLiteLog class. Fix for [ac47dd230a].
    • -
    -

    - 1.0.75.0 - October 3, 2011 -

    -
      -
    • Updated to SQLite 3.7.8 [3e0da808d2].
    • -
    • More enhancements to the build system.
    • -
    • Add official NuGet packages for x86 and x64.
    • -
    • Add Changes and LastInsertRowId properties to the connection class.
    • -
    • Support more formats when converting data from/to the DateTime type.
    • -
    • Make all the assembly versioning attributes consistent.
    • -
    • Add unit testing infrastructure using Eagle.
    • -
    • Integrate all legacy unit tests, including the "testlinq" project, into the new test suite.
    • -
    • Add projects to build the interop assembly statically linked to the Visual C++ runtime. Fix for [53f0c5cbf6].
    • -
    • Add SQLITE_ENABLE_STAT2 compile-time option to the interop assembly. Fix for [74807fbf27].
    • -
    • Fix mutex issues exposed when running the test suite with the debug version of SQLite.
    • -
    • Fix transaction enlistment when repeated attempts are made to enlist in the same transaction. Fix for [ccfa69fc32].
    • -
    • Support the SQLITE_FCNTL_WIN32_AV_RETRY file control to mitigate the impact of file sharing violations caused by external processes.
    • -
    • Refactor the logging interface to be thread-safe and self-initializing.
    • -
    • Shutdown the SQLite native interface when the AppDomain is being unloaded. Fix for [b4a7ddc83f].
    • -
    • Support Skip operation for LINQ using OFFSET. Fix for [8b7d179c3c].
    • -
    • Support EndsWith operation for LINQ using SUBSTR. Fix for [59edc1018b].
    • -
    • Support all SQLite journal modes. Fix for [448d663d11].
    • -
    • Do not throw exceptions when disposing SQLiteDataReader. Fix for [e1b2e0f769].
    • -
    • The REAL type should be mapped to System.Double. Fix for [2c630bffa7] and [b0a5990f48].
    • -
    • Minor optimization to GetParamValueBytes(). Fix for [201128cc88].
    • -
    • Support the ON UPDATE, ON DELETE, and MATCH clause information when generating schema metadata for foreign keys. Partial fix for [b226147b37]. VS designer changes are not yet tested.
    • -
    • Fix incorrect resource name for SR.resx in the mixed-mode assembly.
    • -
    • Reduce the number of String.Compare() calls in the hot path for SQLiteCommand.ExecuteReader().
    • -
    -

    - 1.0.74.0 - July 4, 2011 -

    -
      -
    • Updated to SQLite 3.7.7.1 [af0d91adf4].
    • -
    • Fix incorrect hard-coded .NET Framework version information SQLiteFactory_Linq.cs that was causing IServiceProvider.GetService to fail when running against the .NET Framework 3.5.
    • -
    • Fix all XML documentation warnings.
    • -
    • Restore support for the mixed-mode assembly (i.e. the one that can be registered in the Global Assembly Cache).
    • -
    • Restore support for the Compact Framework.
    • -
    • Remove unused "using" statements from the System.Data.SQLite and System.Data.SQLite.Linq projects.
    • -
    • Remove hard-coded System.Data.SQLite.Linq version from SQLiteFactory_Linq.cs
    • -
    • Modify the setup to support bundled packages (i.e. with the mixed-mode assembly) and standard packages (i.e. with the managed assembly separate from the native interop library).
    • -
    • Disable the ability to register with the Global Assembly Cache in the standard setup package (i.e. it is available in the bundled setup only).
    • -
    • Remove PATH modification from the setup.
    • -
    • Modify the naming scheme for the source, setup, and binary packages to allow for the necessary variants.
    • -
    • In the build automation, attempt to automatically detect if Visual Studio 2008 and/or 2010 are installed and support building binaries for both at once, when available.
    • -
    • Add release automation to build the source, setup, and binary packages in all supported build variants.
    • -
    • Add the testlinq project to the new build system and make it work properly with Visual Studio 2008 and 2010.
    • -
    -

    - 1.0.73.0 - June 2, 2011 -

    -
      -
    • Updated to SQLite 3.7.6.3 [ed1da510a2].
    • -
    • Minor optimization to GetBytes(). Fix for [8c1650482e].
    • -
    • Update various assembly information settings.
    • -
    • Correct System.Data.SQLite.Linq version and resource information. Fix for [6489c5a396] and [133daf50d6].
    • -
    • Moved log handler from SQLiteConnection object to SQLiteFactory object to prevent if from being prematurely GCed.
    • -
    • We should block x64 installs on x86 and we should install native only if the setup package itself is native. Fix for [e058ce156e].
    • -
    -

    - 1.0.72.0 - May 1, 2011 -

    -
      -
    • Add the correct directory to the path. Fix for [50515a0c8e].
    • -
    -

    - 1.0.71.0 - April 27, 2011 -

    -
      -
    • - Updated to SQLite 3.7.6+ [1bd1484cd7] - to get additional Windows error logging. -
    • -
    • Updated setup to optionally add install directory to PATH if GAC option selected.
    • -
    -

    - 1.0.69.0 - April 12, 2011 -

    -
      -
    • Code merge with [http://www.sqlite.org/releaselog/3_7_6.html | SQLite 3.7.6]
    • -
    • New VS2008 and VS2010 solution files
    • -
    • Build and packaging automation
    • -
    • New Inno Setup files
    • -
    • Designer support currently not ready for release
    • -
    -

    - 1.0.68.0 - February 2011 -

    -
      -
    • Code merge with [http://www.sqlite.org/releaselog/3_7_5.html | SQLite 3.7.5]
    • -
    • Continuing work on supporting Visual Studio 2010
    • -
    -

    - 1.0.67.0 - January 3, 2011 -

    -
      -
    • Code merge with [http://www.sqlite.org/releaselog/3_7_4.html | SQLite 3.7.4]
    • -
    • Continuing work on supporting Visual Studio 2010
    • -
    - +News + +Version History + +

    + 1.0.78.0 - January 27, 2012 +

    +
      +
    • Updated to [http://www.sqlite.org/releaselog/3_7_10.html|SQLite 3.7.10] +
    • Redesign the VS designer support installer and integrate it into the setup packages.
    • +
    • When emitting SQL for foreign keys in the VS designer, be sure to take all returned schema rows into account. Remainder of fix for [b226147b37].
    • +
    • Add Flags connection string property to control extra behavioral flags for the connection.
    • +
    • Refactor all IDisposable implementations to conform to best practices, potentially eliminating leaks in certain circumstances.
    • +
    • Even more enhancements to the build and test automation.
    • +
    • Support parameter binding to more primitive types, including unsigned integer types.
    • +
    • Recognize the TIMESTAMP column data type as the DateTime type. Fix for [bb4b04d457].
    • +
    • Prevent logging superfluous messages having to do with library initialization checking. Fix for [3fc172d1be].
    • +
    • Support the DateTimeKind and BaseSchemaName connection string properties in the SQLiteConnectionStringBuilder class. Fix for [f3ec1e0066].
    • +
    • Overloads of the SQLiteConvert.ToDateTime and SQLiteConvert.ToJulianDay methods that do not require an instance should be static. Partial fix for [4bbf851fa5]. ** Potentially Incompatible Change **
    • +
    +

    + 1.0.77.0 - November 28, 2011 +

    +
      +
    • Updated to [http://www.sqlite.org/releaselog/3_7_9.html|SQLite 3.7.9] +
    • More enhancements to the build and test automation.
    • +
    • Plug native memory leak when closing a database connection containing a statement that cannot be finalized for some reason.
    • +
    • The SQLite3 class should always attempt to dispose the contained SQLiteConnectionHandle, even when called via the finalizer.
    • +
    • When compiled with DEBUG defined, emit diagnostic information related to resource cleanup to any TraceListener objects that may be registered.
    • +
    • Stop characterizing all log messages as errors. From now on, if the errorCode is zero, the message will not be considered an error.
    • +
    • Never attempt to configure the native logging interface if the SQLite core library has already been initialized for the process. Fix for [2ce0870fad].
    • +
    • Allow the SQLiteLog class to be used for logging messages without having an open connection.
    • +
    • Support building the core System.Data.SQLite assemblies using the .NET Framework 4.0 Client Profile. Fix for [566f1ad1e4].
    • +
    • When generating the schema based on the contents of a SQLiteDataReader, skip flagging columns as unique if the data reader is holding the result of some kind of multi-table construct (e.g. a cross join) because we must allow duplicate values in that case. Fix for [7e3fa93744].
    • +
    • When returning schema information that may be used by the .NET Framework to construct dynamic SQL, use a fake schema name (instead of null) so that the table names will be properly qualified with the catalog name (i.e. the attached database name). Partial fix for [343d392b51].
    • +
    • Add SQLiteSourceId property to the SQLiteConnection class to return the SQLite source identifier.
    • +
    • Add MemoryUsed and MemoryHighwater properties to the SQLiteConnection class to help determine the memory usage of SQLite.
    • +
    • Add DateTimeKind connection string property to control the DateTimeKind of parsed DateTime values. Partial fix for [343d392b51]. ** Potentially Incompatible Change **
    • +
    • Improve the robustness of the SQLiteLog class when it will be initialized and unloaded multiple times.
    • +
    • Fix the name of the interop assembly for Windows CE. Add unit tests to prevent this type of issue from happening again. Fix for [737ca4ff74].
    • +
    • Formally support the SQL type name BOOLEAN in addition to BOOL. Fix for [544dba0a2f].
    • +
    • Make sure the SQLiteConvert.TypeNameToDbType method is thread-safe. Fix for [84718e79fa].
    • +
    +

    + 1.0.76.0 - October 4, 2011 +

    +
      +
    • Prevent the domain unload event handler in SQLiteLog from being registered multiple times. Fix for [0d5b1ef362].
    • +
    • Stop allowing non-default application domains to initialize the SQLiteLog class. Fix for [ac47dd230a].
    • +
    +

    + 1.0.75.0 - October 3, 2011 +

    +
      +
    • Updated to [http://www.sqlite.org/releaselog/3_7_8.html|SQLite 3.7.8] +
    • More enhancements to the build system.
    • +
    • Add official NuGet packages for x86 and x64.
    • +
    • Add Changes and LastInsertRowId properties to the connection class.
    • +
    • Support more formats when converting data from/to the DateTime type.
    • +
    • Make all the assembly versioning attributes consistent.
    • +
    • Add unit testing infrastructure using Eagle.
    • +
    • Integrate all legacy unit tests, including the "testlinq" project, into the new test suite.
    • +
    • Add projects to build the interop assembly statically linked to the Visual C++ runtime. Fix for [53f0c5cbf6].
    • +
    • Add SQLITE_ENABLE_STAT2 compile-time option to the interop assembly. Fix for [74807fbf27].
    • +
    • Fix mutex issues exposed when running the test suite with the debug version of SQLite.
    • +
    • Fix transaction enlistment when repeated attempts are made to enlist in the same transaction. Fix for [ccfa69fc32].
    • +
    • Support the SQLITE_FCNTL_WIN32_AV_RETRY file control to mitigate the impact of file sharing violations caused by external processes.
    • +
    • Refactor the logging interface to be thread-safe and self-initializing.
    • +
    • Shutdown the SQLite native interface when the AppDomain is being unloaded. Fix for [b4a7ddc83f].
    • +
    • Support Skip operation for LINQ using OFFSET. Fix for [8b7d179c3c].
    • +
    • Support EndsWith operation for LINQ using SUBSTR. Fix for [59edc1018b].
    • +
    • Support all SQLite journal modes. Fix for [448d663d11].
    • +
    • Do not throw exceptions when disposing SQLiteDataReader. Fix for [e1b2e0f769].
    • +
    • The REAL type should be mapped to System.Double. Fix for [2c630bffa7] and [b0a5990f48].
    • +
    • Minor optimization to GetParamValueBytes(). Fix for [201128cc88].
    • +
    • Support the ON UPDATE, ON DELETE, and MATCH clause information when generating schema metadata for foreign keys. Partial fix for [b226147b37]. VS designer changes are not yet tested.
    • +
    • Fix incorrect resource name for SR.resx in the mixed-mode assembly.
    • +
    • Reduce the number of String.Compare() calls in the hot path for SQLiteCommand.ExecuteReader().
    • +
    +

    + 1.0.74.0 - July 4, 2011 +

    +
      +
    • Updated to [http://www.sqlite.org/releaselog/3_7_7_1.html|SQLite 3.7.7.1] +
    • Fix incorrect hard-coded .NET Framework version information SQLiteFactory_Linq.cs that was causing IServiceProvider.GetService to fail when running against the .NET Framework 3.5.
    • +
    • Fix all XML documentation warnings.
    • +
    • Restore support for the mixed-mode assembly (i.e. the one that can be registered in the Global Assembly Cache).
    • +
    • Restore support for the Compact Framework.
    • +
    • Remove unused "using" statements from the System.Data.SQLite and System.Data.SQLite.Linq projects.
    • +
    • Remove hard-coded System.Data.SQLite.Linq version from SQLiteFactory_Linq.cs
    • +
    • Modify the setup to support bundled packages (i.e. with the mixed-mode assembly) and standard packages (i.e. with the managed assembly separate from the native interop library).
    • +
    • Disable the ability to register with the Global Assembly Cache in the standard setup package (i.e. it is available in the bundled setup only).
    • +
    • Remove PATH modification from the setup.
    • +
    • Modify the naming scheme for the source, setup, and binary packages to allow for the necessary variants.
    • +
    • In the build automation, attempt to automatically detect if Visual Studio 2008 and/or 2010 are installed and support building binaries for both at once, when available.
    • +
    • Add release automation to build the source, setup, and binary packages in all supported build variants.
    • +
    • Add the testlinq project to the new build system and make it work properly with Visual Studio 2008 and 2010.
    • +
    +

    + 1.0.73.0 - June 2, 2011 +

    +
      +
    • Updated to [http://www.sqlite.org/releaselog/3_7_6_3.html|SQLite 3.7.6.3] +
    • Minor optimization to GetBytes(). Fix for [8c1650482e].
    • +
    • Update various assembly information settings.
    • +
    • Correct System.Data.SQLite.Linq version and resource information. Fix for [6489c5a396] and [133daf50d6].
    • +
    • Moved log handler from SQLiteConnection object to SQLiteFactory object to prevent if from being prematurely GCed.
    • +
    • We should block x64 installs on x86 and we should install native only if the setup package itself is native. Fix for [e058ce156e].
    • +
    +

    + 1.0.72.0 - May 1, 2011 +

    +
      +
    • Add the correct directory to the path. Fix for [50515a0c8e].
    • +
    +

    + 1.0.71.0 - April 27, 2011 +

    +
      +
    • + Updated to SQLite 3.7.6+ [http://www.sqlite.org/src/info/1bd1484cd7 | [1bd1484cd7]] + to get additional Windows error logging. +
    • +
    • Updated setup to optionally add install directory to PATH if GAC option selected.
    • +
    +

    + 1.0.69.0 - April 12, 2011 +

    +
      +
    • Code merge with [http://www.sqlite.org/releaselog/3_7_6.html | SQLite 3.7.6]
    • +
    • New VS2008 and VS2010 solution files
    • +
    • Build and packaging automation
    • +
    • New Inno Setup files
    • +
    • Designer support currently not ready for release
    • +
    +

    + 1.0.68.0 - February 2011 +

    +
      +
    • Code merge with [http://www.sqlite.org/releaselog/3_7_5.html | SQLite 3.7.5]
    • +
    • Continuing work on supporting Visual Studio 2010
    • +
    +

    + 1.0.67.0 - January 3, 2011 +

    +
      +
    • Code merge with [http://www.sqlite.org/releaselog/3_7_4.html | SQLite 3.7.4]
    • +
    • Continuing work on supporting Visual Studio 2010
    • +
    Index: www/release.wiki ================================================================== --- www/release.wiki +++ www/release.wiki @@ -1,51 +1,313 @@ Release Procedures +

    Release Procedures

    -This content on this page is outdated and needs to be updated. - -

    Follow these steps to prepare a new release of System.Data.SQLite. -Unless otherwise noted, all steps need to be done in the order specified.

    - -
      -
    1. -Build Runtime Packages -
        -1. [./build.wiki | Build] the runtime and design time components, and setup package.
        -2. Copy the and rename the resulting setup.exe to sqlite-dotnet-1xxyyzz.exe
        -
      -
    2. -
    3. -Build Source Package -
        -1. Get a fresh checkout of the "dotnet" Fossil repository.
        -2. Run fossil close to remove the _FOSSIL_ file.
        -3. Remove the bin, obj, and Externals directories.
        -4. Rename the subdirectory to sqlite-dotnetsrc-1xxyyzz
        -5. Package this subdirectory as sqlite-dotnetsrc-1xxyyzz.zip
        -6. Move the ZIP archive to the doc/ folder of the build area.
        -
      -
    4. -
    5. -Update documentation -This section needs updating. -
        -1. Update the readme.htm file with the latest version changes
        -2. Copy the version changes from readme.htm into doc\extra\version.html
        -3. Update doc\extra\dbfactorysupport.html to reflect the latest version # of the DLL
        -4. Copy over doc\extra\dbfactorysupport.html and version.html to my temp -ndoc2 location where all my intermediate files are for the CHM
        -5. Run HTML Help Workshop on the HHP project to get my CHM
        -6. Copy the CHM file over to \doc
        -
      -
    6. -
    7. -Publish the release -
        -1. Update the news.wiki page and the downloads.wiki page.
        -2. Tag the release in Fossil.
        -3. Upload the distributables.
        -4. Announce the release on the mailing list.
        -
      -
    8. +

      + Follow these steps to prepare a new release of System.Data.SQLite. + Unless otherwise noted, all steps need to be done in the order specified. +

      + + +

      Build x86 & x64 Binaries

      + +
        +
      1. + The binaries for all supported architectures and platforms must be built + using procedures very similar to those documented in the normal + [./build.wiki | build procedures]. +
      2. + +
      3. Open a normal command prompt window with "cmd.exe".
      4. + +
      5. Change the current directory to "<root>\Setup".
      6. + +
      7. + Enter the following command to build all the x86 and x64 binaries:  + build_all.bat
        +
      8. +
      + + +

      Test x86 & x64 Binaries

      + +
        +
      1. + The binaries for all supported architectures and platforms must be tested + using procedures very similar to those documented in the normal + [./test.wiki | test procedures]. +
      2. + +
      3. Open a normal command prompt window with "cmd.exe".
      4. + +
      5. Change the current directory to "<root>\Setup".
      6. + +
      7. + Enter the following command to test all the x86 or x64 binaries, depending + on the processor architecture of the current machine:  + test_all.bat
        +
      8. + +
      9. + Locate a machine with a processor architecture different from the one tested + in the previous step and then repeat all the previous steps. +
      10. +
      + + +

      Build Windows CE Binaries

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Enter the following command to build all the binaries available for Windows + CE: build_ce.bat
        +
      6. +
      + + +

      Test Windows CE Binaries

      + +
        +
      1. + Launch Visual Studio 2008, "Professional" edition or + "better". As of this writing, in January 2012, Visual Studio + 2010 and later will not work as they do not include the necessary + built-in support for Windows CE and the .NET Compact Framework. +
      2. + +
      3. + Open the "SQLite.NET.2008.sln" solution file in the + "<root>" directory. +
      4. + +
      5. + Change the active solution configuration to "Debug". +
      6. + +
      7. + Change the active solution platform to "Pocket PC 2003 + (ARMV4)". +
      8. + +
      9. + Right-click the "testce" project in the Solution Explorer + window and select "Set as StartUp Project". +
      10. + +
      11. + Select "Start Debugging" from the "Debug" + menu. +
      12. + +
      13. + If any rebuild prompts appear (e.g. "SQLite.Interop.CE.2008", + "Would you like to build it?"), select "Yes". +
      14. + +
      15. + When prompted for the device type to deploy the application to, select the + "Pocket PC 2003 SE Emulator" device. +
      16. + +
      17. + Make sure all that the tests pass (i.e. they emit "SUCCESS"). +
      18. +
      + + +

      Update Documentation

      + +
        +
      1. + Update the "<root>\readme.htm" file with information about + all the major changes since the last released version. +
      2. + +
      3. + Copy those changes to the "<root>\Doc\Extra\version.html" + and "<root>\www\news.wiki" files, reformatting as necessary + to fit with the existing document conventions. +
      4. +
      + + +

      Build Documentation

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Doc".
      4. + +
      5. + Enter the following command to build all the documentation in + [http://en.wikipedia.org/wiki/Microsoft_Compiled_HTML_Help | CHM] + format: tclsh.exe buildChm.tcl
        This assumes that + [http://www.activestate.com/activetcl | ActiveTcl] version 8.4 or later, the + [http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=21138 | HTML Help Workshop], + and [http://ndoc3.sourceforge.net/ | NDoc3] have all been installed using + the default settings. +
      6. +
      + + +

      Build Setup Release Packages

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Enter the following command to build all the setup packages for x86 and + x64: bake_all.bat
        +
      6. +
      + + +

      Build x86 & x64 Binary Release Packages

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Enter the following command to build all the binary release packages for + x86 and x64: release_all.bat
        +
      6. +
      + + +

      Build x86 & x64 Static Binary Release Packages

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Enter the following command to build all the static binary release packages + for x86 and x64: release_static.bat
        +
      6. +
      + + +

      Build Windows CE Binary Release Packages

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Enter the following command to build all the binary release packages for + Windows CE: release_ce.bat
        +
      6. +
      + + +

      Build Source Release Packages

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Enter the following command to build all the source release packages: +  archive.bat
        +
      6. +
      + + +

      Update Downloads Page

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>\Setup".
      4. + +
      5. + Replace the version numbers for the previous release in the local working + copy of the [./downloads.wiki | downloads page] with the new version numbers + for System.Data.SQLite and the SQLite core. +
      6. + +
      7. + Enter the following command to update the sizes and hashes on the downloads + page based on all the built release packages: tclsh.exe  + updateFileInfo.tcl
        This assumes that + [http://www.activestate.com/activetcl | ActiveTcl] version 8.4 or later has + been installed using the default settings and that the Fossil binary is + available somewhere along the + [http://en.wikipedia.org/wiki/PATH_%28variable%29 | PATH] (i.e. for + calculating the SHA1 hashes). +
      8. +
      + + +

      Build NuGet Packages

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>".
      4. + +
      5. + Enter the following command to build the "default" NuGet package: +  nuget.exe pack SQLite.nuspec
        This assumes + that the NuGet binary is available somewhere along the + [http://en.wikipedia.org/wiki/PATH_%28variable%29 | PATH].
        Please refer + to [http://docs.nuget.org/ | NuGet Documentation] for further details.
        +
      6. + +
      7. + Enter the following command to build the "managed-only" NuGet + package: nuget.exe pack SQLite.MSIL.nuspec
        + This assumes that the NuGet binary is available somewhere along the + [http://en.wikipedia.org/wiki/PATH_%28variable%29 | PATH].
        Please refer + to [http://docs.nuget.org/ | NuGet Documentation] for further details.
        +
      8. + +
      9. + Enter the following command to build the NuGet package for x86:  + nuget.exe pack SQLite.x86.nuspec
        This assumes that + the NuGet binary is available somewhere along the + [http://en.wikipedia.org/wiki/PATH_%28variable%29 | PATH].
        Please refer + to [http://docs.nuget.org/ | NuGet Documentation] for further details.
        +
      10. + +
      11. + Enter the following command to build the NuGet package for x64: +  nuget.exe pack SQLite.x64.nuspec
        This assumes + that the NuGet binary is available somewhere along the + [http://en.wikipedia.org/wiki/PATH_%28variable%29 | PATH].
        Please refer + to [http://docs.nuget.org/ | NuGet Documentation] for further details.
        +
      12. +
      + + +

      Publish Release

      + +
        +
      1. + Commit pending source code changes to the + [http://www.fossil-scm.org/ | Fossil] repository. +
      2. + +
      3. Tag the release in the Fossil repository.
      4. + +
      5. Upload all the release packages to the web site.
      6. + +
      7. + Push the [http://www.nuget.org/ | NuGet] packages.
        + Please refer to [http://docs.nuget.org/ | NuGet Documentation] for + further details. +
      8. + +
      9. Announce the release on the System.Data.SQLite mailing list.
      ADDED www/source.wiki Index: www/source.wiki ================================================================== --- /dev/null +++ www/source.wiki @@ -0,0 +1,93 @@ +Source Code + +

      + Follow these steps to obtain the latest (i.e. unreleased) source code for the + System.Data.SQLite project. To obtain the latest officially released source + code instead, refer to the [./downloads.wiki | downloads page]. Unless + otherwise noted, all steps need to be done in the order specified. +

      + + +

      Assumptions & Prerequisites

      + +

      + The string "<root>" represents the directory where you would + like the local copy of source tree (i.e. the working check-out directory) for + the System.Data.SQLite project to reside (this should be a completely empty + directory). +

      + + +

      Obtain & Install Fossil

      + +

      + The [http://www.fossil-scm.org | Fossil] open-source + [http://en.wikipedia.org/wiki/Revision_control | version control] system is + a computer program that must be installed on your machine before you use it. + Fortunately, installing Fossil is very easy. Fossil consists of a single + executable file that you simply download and run. For convenience, the Fossil + executable file should be placed in a directory present in your + [http://en.wikipedia.org/wiki/PATH_%28variable%29 | PATH]. To uninstall + Fossil, simply delete the executable file. + [http://www.fossil-scm.org/index.html/doc/tip/www/quickstart.wiki | Detailed instructions] + for installing and getting started with Fossil are available on the + [http://www.fossil-scm.org/ | Fossil website]. +

      + + +

      Clone Repository (Windows)

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>".
      4. + +
      5. + Enter the following command to create a complete clone (i.e. local copy) of + the entire source code repository for the System.Data.SQLite project, + including the entire check-in history:  + fossil clone http://system.data.sqlite.org/ sds.fossil +
      6. + +
      7. + The repository itself uses an + [http://www.fossil-scm.org/index.html/doc/trunk/www/fileformat.wiki | enduring file format] + stored in a single + [http://www.fossil-scm.org/index.html/doc/trunk/www/tech_overview.wiki | SQLite database file] + with a particular schema. +
      8. + +
      9. + In this case, after executing the + [http://www.fossil-scm.org/index.html/help/clone | clone] command, + the local clone of the repository will be placed into the + "sds.fossil" file in the current directory. +
      10. +
      + + +

      Working Copy (Windows)

      + +
        +
      1. Open a normal command prompt window with "cmd.exe".
      2. + +
      3. Change the current directory to "<root>".
      4. + +
      5. + Enter the following command to create a complete working copy of all the + files that are currently part of the System.Data.SQLite project: +  fossil open sds.fossil +
      6. + +
      7. + The local source tree should now be ready for use as described in the + [./build.wiki | build procedures] and/or [./test.wiki | test procedures]. +
      8. + +
      9. + In the future, to update the working copy with the latest changes from the + official System.Data.SQLite repository (i.e. instead of having to re-clone + the entire thing), enter the following command from the same directory where + a working copy is located: fossil update +
      10. +
      Index: www/support.wiki ================================================================== --- www/support.wiki +++ www/support.wiki @@ -1,52 +1,90 @@ Support

      Mailing Lists

      -

      SQLite has an active mailing list and support community, and users of this version of -System.Data.SQLite are encouraged to use these for support questions.

      +

      + SQLite has an active mailing list and support community and users of + the System.Data.SQLite project available on this web site are encouraged to + use these for support questions. +

      -

      Three separate mailing lists have been established to help support -SQLite:

      +

      + Three separate mailing lists have been established to help support SQLite and + System.Data.SQLite: +

        -
      • -sqlite-announce - announcements of new releases or significant developments.
      • -
      • -sqlite-users - general user discussion; most postings belong here.
      • -
      • -sqlite-dev - developer conversations; for people who have or aspire to -have write permission on the SQLite source code repository.
      • +
      • + + sqlite-announce - announcements of new releases or significant + developments. +
      • + +
      • + + sqlite-users - general user discussion; most postings belong here. +
      • + +
      • + + sqlite-dev - developer conversations; for people who have or aspire to + have write permission on the SQLite and/or System.Data.SQLite source code + repositories. +

      -Most users of SQLite will want to join the - -sqlite-announce list and many will want to join the - -sqlite-users list. The - -sqlite-dev list is more specialized and appeals to a narrower audience. -Off-site archives of the - -sqlite-users list are available at: + Most users of SQLite and/or System.Data.SQLite will want to join the + + sqlite-announce list and many will want to join the + + sqlite-users list. The + + sqlite-dev list is more specialized and appeals to a narrower audience. + Off-site archives of the + + sqlite-users list are available at:

      Direct E-Mail

      -

      Use the mailing list. Please do not send email directly to the authors of SQLite or System.Data.SQLite -unless:

      +

      + Use the mailing list. Please do not send email directly + to the authors of SQLite or System.Data.SQLite unless: +

      -

      You are welcomed to use SQLite in closed source, proprietary, and/or commercial projects and to -ask questions about such use on the public mailing list. But please do not ask to receive free direct -technical support. The software is free; direct technical support is not.

      +

      + You are welcomed to use SQLite and System.Data.SQLite in closed source, + proprietary, and/or commercial projects and to ask questions about such use + on the public mailing list. But please do not ask to receive free direct + technical support. The software is free; direct technical support is not. +

      Index: www/test.wiki ================================================================== --- www/test.wiki +++ www/test.wiki @@ -3,12 +3,13 @@

      Test Assumptions & Prerequisites

      1. - The string "<root>" represents the root of the source tree - (i.e. the working check-out directory) for the System.Data.SQLite project. + The string "<root>" represents the root directory of your + local source tree (i.e. the working check-out directory) for the + System.Data.SQLite project.
      2. The string "<year>" represents the version of Visual Studio used (e.g. "2008" or "2010") to build the binaries being @@ -49,11 +50,11 @@

        The binaries to test must be [./build.wiki | built] or [./downloads.wiki | downloaded]. If the binaries are downloaded, they must be - placed in the appropriate build output directory (e.g. + placed according to the build output directory structure (e.g. "<root>\bin\<year>\<configuration>\bin" for the separate managed and interop assemblies or "<root>\bin\<year>\<platform>\<configuration>" for the mixed-mode assembly).

        @@ -77,43 +78,43 @@
      3. Change the current directory to "<root>".
      4. Enter the following command to run all the unit tests against the binaries built with a separate managed and interop assembly: - Externals\Eagle\bin\EagleShell.exe -file Tests\all.eagle + Externals\Eagle\bin\EagleShell.exe -file Tests\all.eagle
      5. Enter the following command to run all the unit tests against the binaries built with a mixed-mode assembly: - Externals\Eagle\bin\EagleShell.exe -initialize -runtimeOption native -file Tests\all.eagle + Externals\Eagle\bin\EagleShell.exe -initialize -runtimeOption native -file Tests\all.eagle
      6. To test binaries built with MSBuild 3.5 or Visual Studio 2008 (i.e. because the default is to test binaries built with MSBuild 4.0 or Visual Studio 2010) add the following command line argument right after "Externals\Eagle\bin\EagleShell.exe" in either of the above command lines: - -preInitialize "set test_year 2008" + -preInitialize "set test_year 2008"
      7. To test binaries built in the "Debug" build configuration (i.e. because the default is to test binaries built in the "Release" build configuration) add the following command line argument right after "Externals\Eagle\bin\EagleShell.exe" in either of the above command lines: - -preInitialize "set test_configuration Debug" + -preInitialize "set test_configuration Debug"
      8. Make sure all tests pass; the log file "%TEMP%\EagleShell.exe.test.<pid>.log" may be checked if any errors should occur. EagleTest should produce "success" messages very similar to the following:

        - PASSED: 48
        - TOTAL: 48
        + PASSED: 116
        + TOTAL: 116
        PASS PERCENTAGE: 100%
        OVERALL RESULT: SUCCESS
    • [./features.wiki | Features]
    • +
    • [./faq.wiki | Frequently Asked Questions]
    • [./news.wiki | News]
    • [./support.wiki | Support]
    • [./downloads.wiki | Downloads]
    • [./contribute.wiki | Contributing]
    • +
    • [./source.wiki | Source Code]
    • [./build.wiki | Build Procedures]
    • [./test.wiki | Test Procedures]
    • [./release.wiki | Release Procedures]