diff --git a/Project.toml b/Project.toml index f36409b..632cf75 100644 --- a/Project.toml +++ b/Project.toml @@ -5,13 +5,10 @@ version = "1.4.1" [deps] DBInterface = "a10d1c49-ce27-4219-8d33-6db1a4562965" -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SQLite_jll = "76ed43ae-9a5d-5a62-8c75-30186b810ce8" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" WeakRefStrings = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" [compat] diff --git a/gen/Project.toml b/gen/Project.toml new file mode 100644 index 0000000..4b74567 --- /dev/null +++ b/gen/Project.toml @@ -0,0 +1,7 @@ +[deps] +Clang = "40e3b903-d033-50b4-a0cc-940c62c95e31" +SQLite_jll = "76ed43ae-9a5d-5a62-8c75-30186b810ce8" + +[compat] +Clang = "0.15" +SQLite_jll = "3" \ No newline at end of file diff --git a/gen/generate_wrapper.jl b/gen/generate_wrapper.jl new file mode 100644 index 0000000..6f6f7ed --- /dev/null +++ b/gen/generate_wrapper.jl @@ -0,0 +1,18 @@ +using Clang.Generators +using SQLite_jll + +cd(@__DIR__) + +include_dir = normpath(SQLite_jll.artifact_dir, "include") + +options = load_options(joinpath(@__DIR__, "generator.toml")) + +args = get_default_args() + +headers = [joinpath(include_dir, "sqlite3.h")] + +# create context +ctx = create_context(headers, args, options) + +# run generator +build!(ctx) diff --git a/gen/generator.toml b/gen/generator.toml new file mode 100644 index 0000000..ff3633d --- /dev/null +++ b/gen/generator.toml @@ -0,0 +1,14 @@ +[general] +library_name = "libsqlite" +output_file_path = "../src/capi.jl" +module_name = "C" +jll_pkg_name = "SQLite_jll" +print_using_CEnum = false +output_ignorelist = [ + "SQLITE_STDCALL" +] + +[codegen] +use_julia_bool = true +use_ccall_macro = true +opaque_func_arg_as_PtrCvoid = true diff --git a/src/SQLite.jl b/src/SQLite.jl index 508fcbf..d333f2c 100644 --- a/src/SQLite.jl +++ b/src/SQLite.jl @@ -1,726 +1,25 @@ module SQLite -using Random, Serialization -using WeakRefStrings, DBInterface - -export DBInterface, SQLiteException - -struct SQLiteException <: Exception - msg::AbstractString -end - -include("consts.jl") -include("api.jl") - -const DBHandle = Ptr{Cvoid} # SQLite3 DB connection handle -const StmtHandle = Ptr{Cvoid} # SQLite3 prepared statement handle - -# Normal constructor from filename -sqliteexception(handle::DBHandle) = SQLiteException(unsafe_string(sqlite3_errmsg(handle))) -function sqliteexception(handle::DBHandle, stmt::StmtHandle) - errstr = unsafe_string(sqlite3_errmsg(handle)) - stmt_text_handle = sqlite3_expanded_sql(stmt) - stmt_text = unsafe_string(stmt_text_handle) - msg = "$errstr on statement \"$stmt_text\"" - sqlite3_free(convert(Ptr{Cvoid}, stmt_text_handle)) - return SQLiteException(msg) -end - -sqliteerror(handle::DBHandle) = throw(sqliteexception(handle)) -sqliteerror(handle::DBHandle, stmt::StmtHandle) = throw(sqliteexception(handle, stmt)) - -""" -Internal wrapper that holds the handle to SQLite3 prepared statement. -It is managed by [`SQLite.DB`](@ref) and referenced by the "public" [`SQLite.Stmt`](@ref) object. - -When no `SQLite.Stmt` instances reference the given `SQlite._Stmt` object, -it is closed automatically. - -When `SQLite.DB` is closed or [`SQLite.finalize_statements!`](@ref) is called, -all its `SQLite._Stmt` objects are closed. -""" -mutable struct _Stmt - handle::StmtHandle - params::Dict{Int, Any} - - function _Stmt(handle::StmtHandle) - stmt = new(handle, Dict{Int, Any}()) - finalizer(_close!, stmt) - return stmt - end -end - -# close statement -function _close!(stmt::_Stmt) - stmt.handle == C_NULL || sqlite3_finalize(stmt.handle) - stmt.handle = C_NULL - return -end - -# _Stmt unique identifier in DB -const _StmtId = Int - -""" - `SQLite.DB()` => in-memory SQLite database - `SQLite.DB(file)` => file-based SQLite database - -Constructors for a representation of an sqlite database, either backed by an on-disk file or in-memory. - -`SQLite.DB` requires the `file` string argument in the 2nd definition -as the name of either a pre-defined SQLite database to be opened, -or if the file doesn't exist, a database will be created. -Note that only sqlite 3.x version files are supported. - -The `SQLite.DB` object represents a single connection to an SQLite database. -All other SQLite.jl functions take an `SQLite.DB` as the first argument as context. - -To create an in-memory temporary database, call `SQLite.DB()`. - -The `SQLite.DB` will be automatically closed/shutdown when it goes out of scope -(i.e. the end of the Julia session, end of a function call wherein it was created, etc.) -""" -mutable struct DB <: DBInterface.Connection - file::String - handle::DBHandle - stmts::Dict{_StmtId, _Stmt} # opened prepared statements - - lastStmtId::_StmtId - - function DB(f::AbstractString) - handle = Ref{DBHandle}() - f = String(isempty(f) ? f : expanduser(f)) - if @OK sqlite3_open(f, handle) - db = new(f, handle[], Dict{StmtHandle, _Stmt}(), 0) - finalizer(_close, db) - return db - else # error - sqliteerror(handle[]) - end - end -end -DB() = DB(":memory:") -DBInterface.connect(::Type{DB}) = DB() -DBInterface.connect(::Type{DB}, f::AbstractString) = DB(f) -DBInterface.close!(db::DB) = _close(db) -Base.close(db::DB) = _close(db) -Base.isopen(db::DB) = db.handle != C_NULL - -# close all prepared statements of db connection -function finalize_statements!(db::DB) - for stmt in values(db.stmts) - _close!(stmt) - end - empty!(db.stmts) -end - -function _close(db::DB) - finalize_statements!(db) - # disconnect from DB - db.handle == C_NULL || sqlite3_close_v2(db.handle) - db.handle = C_NULL - return -end - -sqliteerror(db::DB) = sqliteerror(db.handle) -sqliteexception(db::DB) = sqliteexception(db.handle) - -Base.show(io::IO, db::SQLite.DB) = print(io, string("SQLite.DB(", "\"$(db.file)\"", ")")) - -# prepare given sql statement -function _Stmt(db::DB, sql::AbstractString) - handle = Ref{StmtHandle}() - sqliteprepare(db, sql, handle, Ref{StmtHandle}()) - return _Stmt(handle[]) -end - -""" - SQLite.Stmt(db, sql) => SQL.Stmt - -Prepares an optimized internal representation of SQL statement in -the context of the provided SQLite3 `db` and constructs the `SQLite.Stmt` -Julia object that holds a reference to the prepared statement. - -*Note*: the `sql` statement is not actually executed, but only compiled -(mainly for usage where the same statement is executed multiple times -with different parameters bound as values). - -Internally `SQLite.Stmt` constructor creates the [`SQLite._Stmt`](@ref) object that is managed by `db`. -`SQLite.Stmt` references the `SQLite._Stmt` by its unique id. - -The `SQLite.Stmt` will be automatically closed/shutdown when it goes out of scope -(i.e. the end of the Julia session, end of a function call wherein it was created, etc.). -One can also call `DBInterface.close!(stmt)` to immediately close it. - -All prepared statements of a given DB connection are also automatically closed when the -DB is disconnected or when [`SQLite.finalize_statements!`](@ref) is explicitly called. -""" -mutable struct Stmt <: DBInterface.Statement - db::DB - id::_StmtId # id of _Stmt inside db (may refer to already closed connection) - - function Stmt(db::DB, sql::AbstractString) - _stmt = _Stmt(db, sql) - id = (db.lastStmtId += 1) - stmt = new(db, id) - db.stmts[id] = _stmt # FIXME check for duplicate handle? - finalizer(_finalize, stmt) - return stmt - end -end - -sqliteexception(db::DB, stmt::_Stmt) = sqliteexception(db.handle, stmt.handle) - -# check if the statement is ready (not finalized due to -# _close(_Stmt) called and the statment handle removed from DB) -isready(stmt::Stmt) = haskey(stmt.db.stmts, stmt.id) - -# get underlying _Stmt or nothing if not found -_stmt_safe(stmt::Stmt) = get(stmt.db.stmts, stmt.id, nothing) - -# get underlying _Stmt or throw if not found -@inline function _stmt(stmt::Stmt) - _st = _stmt_safe(stmt) - (_st === nothing) && throw(SQLiteException("Statement $(stmt.id) not found")) - return _st -end - -# automatically finalizes prepared statement (_Stmt) -# when no Stmt objects refer to it and removes -# it from the db.stmts collection -_finalize(stmt::Stmt) = DBInterface.close!(stmt) - -DBInterface.getconnection(stmt::Stmt) = stmt.db - -# explicitly close prepared statement -function DBInterface.close!(stmt::Stmt) - _st = _stmt_safe(stmt) - if _st !== nothing - _close!(_st) - delete!(stmt.db.stmts, stmt.id) # remove the _Stmt - end - return stmt -end - -sqliteprepare(db::DB, sql::AbstractString, stmt::Ref{StmtHandle}, null::Ref{StmtHandle}) = - @CHECK db sqlite3_prepare_v2(db.handle, sql, stmt, null) - +import Random +import DBInterface +using Serialization +using WeakRefStrings +using Tables + +include("capi.jl") +import .C as C + +include("base.jl") +include("db_and_stmt.jl") +include("bind.jl") +include("type_conversion.jl") +include("query.jl") +include("transaction.jl") +include("table.jl") +include("index.jl") +include("db_functions.jl") include("UDF.jl") -export @sr_str - -""" - SQLite.clear!(stmt::SQLite.Stmt) - -Clears any bound values to a prepared SQL statement -""" -function clear!(stmt::Stmt) - _st = _stmt(stmt) - sqlite3_clear_bindings(_st.handle) - empty!(_st.params) - return -end - -""" - SQLite.bind!(stmt::SQLite.Stmt, values) - -bind `values` to parameters in a prepared [`SQLite.Stmt`](@ref). Values can be: - -* `Vector` or `Tuple`: where each element will be bound to an SQL parameter by index order -* `Dict` or `NamedTuple`; where values will be bound to named SQL parameters by the `Dict`/`NamedTuple` key - -Additional methods exist for working individual SQL parameters: - -* `SQLite.bind!(stmt, name, val)`: bind a single value to a named SQL parameter -* `SQLite.bind!(stmt, index, val)`: bind a single value to a SQL parameter by index number - -From the [SQLite documentation](https://www3.sqlite.org/cintro.html): - -> Usually, though, -> it is not useful to evaluate exactly the same SQL statement more than once. -> More often, one wants to evaluate similar statements. -> For example, you might want to evaluate an INSERT statement -> multiple times though with different values to insert. -> To accommodate this kind of flexibility, -> SQLite allows SQL statements to contain parameters -> which are "bound" to values prior to being evaluated. -> These values can later be changed and the same prepared statement -> can be evaluated a second time using the new values. -> -> In SQLite, -> wherever it is valid to include a string literal, -> one can use a parameter in one of the following forms: -> -> - `?` -> - `?NNN` -> - `:AAA` -> - `\$AAA` -> - `@AAA` -> -> In the examples above, -> `NNN` is an integer value and `AAA` is an identifier. -> A parameter initially has a value of `NULL`. -> Prior to calling `sqlite3_step()` for the first time -> or immediately after `sqlite3_reset()``, -> the application can invoke one of the `sqlite3_bind()` interfaces -> to attach values to the parameters. -> Each call to `sqlite3_bind()` overrides prior bindings on the same parameter. - -""" -function bind! end - -function bind!(stmt::_Stmt, params::DBInterface.NamedStatementParams) - nparams = sqlite3_bind_parameter_count(stmt.handle) - (nparams <= length(params)) || throw(SQLiteException("values should be provided for all query placeholders")) - for i in 1:nparams - name = unsafe_string(sqlite3_bind_parameter_name(stmt.handle, i)) - isempty(name) && throw(SQLiteException("nameless parameters should be passed as a Vector")) - # name is returned with the ':', '@' or '$' at the start - sym = Symbol(name[2:end]) - haskey(params, sym) || throw(SQLiteException("`$name` not found in values keyword arguments to bind to sql statement")) - bind!(stmt, i, params[sym]) - end -end - -function bind!(stmt::_Stmt, values::DBInterface.PositionalStatementParams) - nparams = sqlite3_bind_parameter_count(stmt.handle) - (nparams == length(values)) || throw(SQLiteException("values should be provided for all query placeholders")) - for i in 1:nparams - @inbounds bind!(stmt, i, values[i]) - end -end - -bind!(stmt::Stmt, values::DBInterface.StatementParams) = bind!(_stmt(stmt), values) - -bind!(stmt::Union{_Stmt, Stmt}; kwargs...) = bind!(stmt, kwargs.data) - -# Binding parameters to SQL statements -function bind!(stmt::_Stmt, name::AbstractString, val::Any) - i::Int = sqlite3_bind_parameter_index(stmt.handle, name) - if i == 0 - throw(SQLiteException("SQL parameter $name not found in $stmt")) - end - return bind!(stmt, i, val) -end - -# binding method for internal _Stmt class -bind!(stmt::_Stmt, i::Integer, val::AbstractFloat) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_double(stmt.handle, i, Float64(val)); return nothing) -bind!(stmt::_Stmt, i::Integer, val::Int32) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_int(stmt.handle, i, val); return nothing) -bind!(stmt::_Stmt, i::Integer, val::Int64) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_int64(stmt.handle, i, val); return nothing) -bind!(stmt::_Stmt, i::Integer, val::Missing) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_null(stmt.handle, i); return nothing) -bind!(stmt::_Stmt, i::Integer, val::Nothing) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_null(stmt.handle, i); return nothing) -bind!(stmt::_Stmt, i::Integer, val::AbstractString) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_text(stmt.handle, i, val); return nothing) -bind!(stmt::_Stmt, i::Integer, val::WeakRefString{UInt8}) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_text(stmt.handle, i, val.ptr, val.len); return nothing) -bind!(stmt::_Stmt, i::Integer, val::WeakRefString{UInt16}) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_text16(stmt.handle, i, val.ptr, val.len*2); return nothing) -bind!(stmt::_Stmt, i::Integer, val::Bool) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_int(stmt.handle, i, Int32(val)); return nothing) -bind!(stmt::_Stmt, i::Integer, val::Vector{UInt8}) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_blob(stmt.handle, i, val); return nothing) -# Fallback is BLOB and defaults to serializing the julia value - -bind!(stmt::Stmt, param::Union{Integer, AbstractString}, val::Any) = bind!(_stmt(stmt), param, val) - -# internal wrapper mutable struct to, in-effect, mark something which has been serialized -struct Serialized - object -end - -function sqlserialize(x) - buffer = IOBuffer() - # deserialize will sometimes return a random object when called on an array - # which has not been previously serialized, we can use this mutable struct to check - # that the array has been serialized - s = Serialized(x) - Serialization.serialize(buffer, s) - return take!(buffer) -end -# fallback method to bind arbitrary julia `val` to the parameter at index `i` (object is serialized) -bind!(stmt::_Stmt, i::Integer, val::Any) = bind!(stmt, i, sqlserialize(val)) - -struct SerializeError <: Exception - msg::String -end - -# magic bytes that indicate that a value is in fact a serialized julia value, instead of just a byte vector -# these bytes depend on the julia version and other things, so they are determined using an actual serialization -const SERIALIZATION = sqlserialize(0)[1:18] - -function sqldeserialize(r) - if sizeof(r) < sizeof(SERIALIZATION) - return r - end - ret = ccall(:memcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}, UInt), - SERIALIZATION, r, min(sizeof(SERIALIZATION), sizeof(r))) - if ret == 0 - try - v = Serialization.deserialize(IOBuffer(r)) - return v.object - catch e - throw(SerializeError("Error deserializing non-primitive value out of database; this is probably due to using SQLite.jl with a different Julia version than was used to originally serialize the database values. The same Julia version that was used to serialize should be used to extract the database values into a different format (csv file, feather file, etc.) and then loaded back into the sqlite database with the current Julia version.")) - end - else - return r - end -end -#TODO: - #int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); - #int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); - -# get julia type for given column of the given statement -function juliatype(handle, col) - stored_typeid = SQLite.sqlite3_column_type(handle, col) - if stored_typeid == SQLite.SQLITE_BLOB - # blobs are serialized julia types, so just try to deserialize it - deser_val = SQLite.sqlitevalue(Any, handle, col) - # FIXME deserialized type have priority over declared type, is it fine? - return typeof(deser_val) - else - stored_type = juliatype(stored_typeid) - end - decl_typestr = SQLite.sqlite3_column_decltype(handle, col) - if decl_typestr != C_NULL - return juliatype(unsafe_string(decl_typestr), stored_type) - else - return stored_type - end -end - -# convert SQLite stored type into Julia equivalent -juliatype(x::Integer) = - x == SQLITE_INTEGER ? Int64 : - x == SQLITE_FLOAT ? Float64 : - x == SQLITE_TEXT ? String : - x == SQLITE_NULL ? Missing : - Any - -# convert SQLite declared type into Julia equivalent, -# fall back to default (stored type), if no good match -function juliatype(decl_typestr::AbstractString, - default::Type = Any) - typeuc = uppercase(decl_typestr) - # try to match the type affinities described in the "Affinity Name Examples" section - # of https://www.sqlite.org/datatype3.html - if typeuc in ("INTEGER", "INT", "TINYINT", "SMALLINT", "MEDIUMINT", "BIGINT", "UNSIGNED BIG INT", "INT2", "INT8") - return Int64 - elseif typeuc in ("NUMERIC", "REAL", "FLOAT", "DOUBLE", "DOUBLE PRECISION") - return Float64 - elseif typeuc == "TEXT" - return String - elseif typeuc == "BLOB" - return Any - elseif typeuc == "DATETIME" - return default # FIXME - elseif typeuc == "TIMESTAMP" - return default # FIXME - elseif occursin(r"^N?V?A?R?Y?I?N?G?\s*CHARA?C?T?E?R?T?E?X?T?\s*\(?\d*\)?$"i, typeuc) - return String - elseif occursin(r"^NUMERIC\(\d+,\d+\)$", typeuc) - return Float64 - else - return default - end -end - -sqlitevalue(::Type{T}, handle, col) where {T <: Union{Base.BitSigned, Base.BitUnsigned}} = convert(T, sqlite3_column_int64(handle, col)) -const FLOAT_TYPES = Union{Float16, Float32, Float64} # exclude BigFloat -sqlitevalue(::Type{T}, handle, col) where {T <: FLOAT_TYPES} = convert(T, sqlite3_column_double(handle, col)) -#TODO: test returning a WeakRefString instead of calling `unsafe_string` -sqlitevalue(::Type{T}, handle, col) where {T <: AbstractString} = convert(T, unsafe_string(sqlite3_column_text(handle, col))) -function sqlitevalue(::Type{T}, handle, col) where {T} - blob = convert(Ptr{UInt8}, sqlite3_column_blob(handle, col)) - b = sqlite3_column_bytes(handle, col) - buf = zeros(UInt8, b) # global const? - unsafe_copyto!(pointer(buf), blob, b) - r = sqldeserialize(buf) - return r -end - -# conversion from Julia to SQLite3 types -sqlitetype_(::Type{<:Integer}) = "INT" -sqlitetype_(::Type{<:AbstractFloat}) = "REAL" -sqlitetype_(::Type{<:AbstractString}) = "TEXT" -sqlitetype_(::Type{Bool}) = "INT" -sqlitetype_(::Type) = "BLOB" # fallback - -sqlitetype(::Type{Missing}) = "NULL" -sqlitetype(::Type{Nothing}) = "NULL" -sqlitetype(::Type{Union{T, Missing}}) where T = sqlitetype_(T) -sqlitetype(::Type{T}) where T = string(sqlitetype_(T), " NOT NULL") - -""" - SQLite.execute(db::SQLite.DB, sql::AbstractString, [params]) -> Int - SQLite.execute(stmt::SQLite.Stmt, [params]) -> Int - -An internal method that executes the SQL statement (provided either as a `db` connection and `sql` command, -or as an already prepared `stmt` (see [`SQLite.Stmt`](@ref))) with given `params` parameters -(either positional (`Vector` or `Tuple`), named (`Dict` or `NamedTuple`), or specified as keyword arguments). - -Returns the SQLite status code of operation. - -*Note*: this is a low-level method that just executes the SQL statement, -but does not retrieve any data from `db`. -To get the results of a SQL query, it is recommended to use [`DBInterface.execute`](@ref). -""" -function execute end - -function execute(db::DB, stmt::_Stmt, params::DBInterface.StatementParams=()) - sqlite3_reset(stmt.handle) - bind!(stmt, params) - r = sqlite3_step(stmt.handle) - if r == SQLITE_DONE - sqlite3_reset(stmt.handle) - elseif r != SQLITE_ROW - e = sqliteexception(db) - sqlite3_reset(stmt.handle) - throw(e) - end - return r -end - -execute(stmt::Stmt, params::DBInterface.StatementParams) = - execute(stmt.db, _stmt(stmt), params) - -execute(stmt::Stmt; kwargs...) = execute(stmt, values(kwargs)) - -function execute(db::DB, sql::AbstractString, params::DBInterface.StatementParams) - # prepare without registering _Stmt in DB - _stmt = _Stmt(db, sql) - try - return execute(db, _stmt, params) - finally - _close!(_stmt) # immediately close, don't wait for GC - end -end - -execute(db::DB, sql::AbstractString; kwargs...) = execute(db, sql, values(kwargs)) - -""" - SQLite.esc_id(x::Union{AbstractString,Vector{AbstractString}}) - -Escape SQLite identifiers -(e.g. column, table or index names). -Can be either a string or a vector of strings -(note does not check for null characters). -A vector of identifiers will be separated by commas. - -Example: - -```julia -julia> using SQLite, DataFrames - -julia> df = DataFrame(label=string.(rand("abcdefg", 10)), value=rand(10)); - -julia> db = SQLite.DB(mktemp()[1]); - -julia> tbl |> SQLite.load!(db, "temp"); - -julia> DBInterface.execute(db,"SELECT * FROM temp WHERE label IN ('a','b','c')") |> DataFrame -4×2 DataFrame -│ Row │ label │ value │ -│ │ String⍰ │ Float64⍰ │ -├─────┼─────────┼──────────┤ -│ 1 │ c │ 0.603739 │ -│ 2 │ c │ 0.429831 │ -│ 3 │ b │ 0.799696 │ -│ 4 │ a │ 0.603586 │ - -julia> q = ['a','b','c']; - -julia> DBInterface.execute(db,"SELECT * FROM temp WHERE label IN (\$(SQLite.esc_id(q)))") |> DataFrame -4×2 DataFrame -│ Row │ label │ value │ -│ │ String⍰ │ Float64⍰ │ -├─────┼─────────┼──────────┤ -│ 1 │ c │ 0.603739 │ -│ 2 │ c │ 0.429831 │ -│ 3 │ b │ 0.799696 │ -│ 4 │ a │ 0.603586 │ -``` -""" -function esc_id end - -esc_id(x::AbstractString) = "\"" * replace(x, "\""=>"\"\"") * "\"" -esc_id(X::AbstractVector{S}) where {S <: AbstractString} = join(map(esc_id, X), ',') - -# Transaction-based commands -""" - SQLite.transaction(db, mode="DEFERRED") - SQLite.transaction(func, db) - -Begin a transaction in the specified `mode`, default = "DEFERRED". - -If `mode` is one of "", "DEFERRED", "IMMEDIATE" or "EXCLUSIVE" then a -transaction of that (or the default) mutable struct is started. Otherwise a savepoint -is created whose name is `mode` converted to AbstractString. - -In the second method, `func` is executed within a transaction (the transaction being committed upon successful execution) -""" -function transaction end - -function transaction(db::DB, mode="DEFERRED") - execute(db, "PRAGMA temp_store=MEMORY;") - if uppercase(mode) in ["", "DEFERRED", "IMMEDIATE", "EXCLUSIVE"] - execute(db, "BEGIN $(mode) TRANSACTION;") - else - execute(db, "SAVEPOINT $(mode);") - end -end - -DBInterface.transaction(f, db::DB) = transaction(f, db) - -@inline function transaction(f::Function, db::DB) - # generate a random name for the savepoint - name = string("SQLITE", Random.randstring(10)) - execute(db, "PRAGMA synchronous = OFF;") - transaction(db, name) - try - f() - catch - rollback(db, name) - rethrow() - finally - # savepoints are not released on rollback - commit(db, name) - execute(db, "PRAGMA synchronous = ON;") - end -end - -""" - SQLite.commit(db) - SQLite.commit(db, name) - -commit a transaction or named savepoint -""" -function commit end - -commit(db::DB) = execute(db, "COMMIT TRANSACTION;") -commit(db::DB, name::AbstractString) = execute(db, "RELEASE SAVEPOINT $(name);") - -""" - SQLite.rollback(db) - SQLite.rollback(db, name) - -rollback transaction or named savepoint -""" -function rollback end - -rollback(db::DB) = execute(db, "ROLLBACK TRANSACTION;") -rollback(db::DB, name::AbstractString) = execute(db, "ROLLBACK TRANSACTION TO SAVEPOINT $(name);") - -""" - SQLite.drop!(db, table; ifexists::Bool=true) - -drop the SQLite table `table` from the database `db`; `ifexists=true` will prevent an error being thrown if `table` doesn't exist -""" -function drop!(db::DB, table::AbstractString; ifexists::Bool=false) - exists = ifexists ? "IF EXISTS" : "" - transaction(db) do - execute(db, "DROP TABLE $exists $(esc_id(table))") - end - execute(db, "VACUUM") - return -end - -""" - SQLite.dropindex!(db, index; ifexists::Bool=true) - -drop the SQLite index `index` from the database `db`; `ifexists=true` will not return an error if `index` doesn't exist -""" -function dropindex!(db::DB, index::AbstractString; ifexists::Bool=false) - exists = ifexists ? "IF EXISTS" : "" - transaction(db) do - execute(db, "DROP INDEX $exists $(esc_id(index))") - end - return -end - -""" - SQLite.createindex!(db, table, index, cols; unique=true, ifnotexists=false) - -create the SQLite index `index` on the table `table` using `cols`, -which may be a single column or vector of columns. -`unique` specifies whether the index will be unique or not. -`ifnotexists=true` will not throw an error if the index already exists -""" -function createindex!(db::DB, table::AbstractString, index::AbstractString, cols::Union{S, AbstractVector{S}}; - unique::Bool=true, ifnotexists::Bool=false) where {S <: AbstractString} - u = unique ? "UNIQUE" : "" - exists = ifnotexists ? "IF NOT EXISTS" : "" - transaction(db) do - execute(db, "CREATE $u INDEX $exists $(esc_id(index)) ON $(esc_id(table)) ($(esc_id(cols)))") - end - execute(db, "ANALYZE $index") - return -end - -""" - SQLite.removeduplicates!(db, table, cols) - -Removes duplicate rows from `table` based on the values in `cols`, which is an array of column names. - -A convenience method for the common task of removing duplicate -rows in a dataset according to some subset of columns that make up a "primary key". -""" -function removeduplicates!(db::DB, table::AbstractString, cols::AbstractArray{T}) where {T <: AbstractString} - colsstr = "" - for c in cols - colsstr = colsstr * esc_id(c) * "," - end - colsstr = chop(colsstr) - transaction(db) do - execute(db, "DELETE FROM $(esc_id(table)) WHERE _ROWID_ NOT IN (SELECT max(_ROWID_) from $(esc_id(table)) GROUP BY $(colsstr));") - end - execute(db, "ANALYZE $table") - return - end - -include("tables.jl") - -""" - SQLite.tables(db, sink=columntable) - -returns a list of tables in `db` -""" -function tables(db::DB, sink=columntable) - tblnames = DBInterface.execute(sink, db, "SELECT name FROM sqlite_master WHERE type='table';") - return [DBTable(tbl, Tables.schema(DBInterface.execute(db,"SELECT * FROM $(tbl) LIMIT 0"))) for tbl in tblnames.name] -end - -""" - SQLite.indices(db, sink=columntable) - -returns a list of indices in `db` -""" -indices(db::DB, sink=columntable) = DBInterface.execute(sink, db, "SELECT name FROM sqlite_master WHERE type='index';") - -""" - SQLite.columns(db, table, sink=columntable) - -returns a list of columns in `table` -""" -columns(db::DB, table::AbstractString, sink=columntable) = DBInterface.execute(sink, db, "PRAGMA table_info($(esc_id(table)))") - -""" - SQLite.last_insert_rowid(db) - -returns the auto increment id of the last row -""" -last_insert_rowid(db::DB) = sqlite3_last_insert_rowid(db.handle) - -""" - SQLite.enable_load_extension(db, enable::Bool=true) - -Enables extension loading (off by default) on the sqlite database `db`. Pass `false` as the second argument to disable. -""" -function enable_load_extension(db::DB, enable::Bool=true) - ccall((:sqlite3_enable_load_extension, SQLite.libsqlite), Cint, (Ptr{Cvoid}, Cint), db.handle, enable) -end - -""" - SQLite.busy_timeout(db, ms::Integer=0) - -Set a busy handler that sleeps for a specified amount of milliseconds when a table is locked. After at least ms milliseconds of sleeping, the handler will return 0, causing sqlite to return SQLITE_BUSY. -""" -function busy_timeout(db::DB, ms::Integer=0) - sqlite3_busy_timeout(db.handle, ms) -end - +export SQLiteException, @sr_str end # module diff --git a/src/UDF.jl b/src/UDF.jl index 30e1a96..8b0312f 100644 --- a/src/UDF.jl +++ b/src/UDF.jl @@ -1,20 +1,20 @@ function sqlvalue(values, i) temp_val_ptr = unsafe_load(values, i) - valuetype = sqlite3_value_type(temp_val_ptr) + valuetype = C.sqlite3_value_type(temp_val_ptr) - if valuetype == SQLITE_INTEGER + if valuetype == C.SQLITE_INTEGER if Sys.WORD_SIZE == 64 - return sqlite3_value_int64(temp_val_ptr) + return C.sqlite3_value_int64(temp_val_ptr) else - return sqlite3_value_int(temp_val_ptr) + return C.sqlite3_value_int(temp_val_ptr) end - elseif valuetype == SQLITE_FLOAT - return sqlite3_value_double(temp_val_ptr) - elseif valuetype == SQLITE_TEXT - return unsafe_string(sqlite3_value_text(temp_val_ptr)) - elseif valuetype == SQLITE_BLOB - nbytes = sqlite3_value_bytes(temp_val_ptr) - blob = sqlite3_value_blob(temp_val_ptr) + elseif valuetype == C.SQLITE_FLOAT + return C.sqlite3_value_double(temp_val_ptr) + elseif valuetype == C.SQLITE_TEXT + return unsafe_string(C.sqlite3_value_text(temp_val_ptr)) + elseif valuetype == C.SQLITE_BLOB + nbytes = C.sqlite3_value_bytes(temp_val_ptr) + blob = C.sqlite3_value_blob(temp_val_ptr) buf = zeros(UInt8, nbytes) unsafe_copyto!(pointer(buf), convert(Ptr{UInt8}, blob), nbytes) return sqldeserialize(buf) @@ -30,12 +30,12 @@ see [below](@ref regex). """ function sqlreturn end -sqlreturn(context, ::Missing) = sqlite3_result_null(context) -sqlreturn(context, val::Int32) = sqlite3_result_int(context, val) -sqlreturn(context, val::Int64) = sqlite3_result_int64(context, val) -sqlreturn(context, val::Float64) = sqlite3_result_double(context, val) -sqlreturn(context, val::AbstractString) = sqlite3_result_text(context, val) -sqlreturn(context, val::Vector{UInt8}) = sqlite3_result_blob(context, val) +sqlreturn(context, ::Missing) = C.sqlite3_result_null(context) +sqlreturn(context, val::Int32) = C.sqlite3_result_int(context, val) +sqlreturn(context, val::Int64) = C.sqlite3_result_int64(context, val) +sqlreturn(context, val::Float64) = C.sqlite3_result_double(context, val) +sqlreturn(context, val::AbstractString) = C.sqlite3_result_text(context, val, sizeof(val), C.SQLITE_TRANSIENT) +sqlreturn(context, val::Vector{UInt8}) = C.sqlite3_result_blob(context, val, sizeof(val), C.SQLITE_TRANSIENT) sqlreturn(context, val::Bool) = sqlreturn(context, Int(val)) sqlreturn(context, val) = sqlreturn(context, sqlserialize(val)) @@ -48,9 +48,9 @@ function scalarfunc(func,fsym=Symbol(string(func))) return quote #nm needs to be a symbol or expr, i.e. :sin or :(Base.sin) function $(nm)(context::Ptr{Cvoid}, nargs::Cint, values::Ptr{Ptr{Cvoid}}) - args = [SQLite.sqlvalue(values, i) for i in 1:nargs] + args = [sqlvalue(values, i) for i in 1:nargs] ret = $(func)(args...) - SQLite.sqlreturn(context, ret) + sqlreturn(context, ret) nothing end return $(nm) @@ -83,7 +83,7 @@ function stepfunc(init, func, fsym=Symbol(string(func)*"_step")) intsize = sizeof(Int) ptrsize = sizeof(Ptr) acsize = intsize + ptrsize - acptr = convert(Ptr{UInt8}, sqlite3_aggregate_context(context, acsize)) + acptr = convert(Ptr{UInt8}, C.sqlite3_aggregate_context(context, acsize)) # acptr will be zeroed-out if this is the first iteration ret = ccall( @@ -152,7 +152,7 @@ function finalfunc(init, func, fsym=Symbol(string(func)*"_final")) nm = isdefined(Base,fsym) ? :(Base.$fsym) : fsym return quote function $(nm)(context::Ptr{Cvoid}, nargs::Cint, values::Ptr{Ptr{Cvoid}}) - acptr = convert(Ptr{UInt8}, sqlite3_aggregate_context(context, 0)) + acptr = convert(Ptr{UInt8}, C.sqlite3_aggregate_context(context, 0)) # step function wasn't run if acptr == C_NULL @@ -215,10 +215,10 @@ function register(db, func::Function; nargs::Int=-1, name::AbstractString=string cfunc = @cfunction($f, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Ptr{Cvoid}})) # TODO: allow the other encodings - enc = SQLITE_UTF8 - enc = isdeterm ? enc | SQLITE_DETERMINISTIC : enc + enc = C.SQLITE_UTF8 + enc = isdeterm ? enc | C.SQLITE_DETERMINISTIC : enc - @CHECK db sqlite3_create_function_v2( + @CHECK db C.sqlite3_create_function_v2( db.handle, name, nargs, enc, C_NULL, cfunc, C_NULL, C_NULL, C_NULL ) end @@ -238,21 +238,13 @@ function register( f = eval(finalfunc(init, final, Base.nameof(final))) cf = @cfunction($f, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Ptr{Cvoid}})) - enc = SQLITE_UTF8 - enc = isdeterm ? enc | SQLITE_DETERMINISTIC : enc + enc = C.SQLITE_UTF8 + enc = isdeterm ? enc | C.SQLITE_DETERMINISTIC : enc - @CHECK db sqlite3_create_function_v2( + @CHECK db C.sqlite3_create_function_v2( db.handle, name, nargs, enc, C_NULL, C_NULL, cs, cf, C_NULL ) end # annotate types because the MethodError makes more sense that way regexp(r::AbstractString, s::AbstractString) = occursin(Regex(r), s) - -""" - sr"..." - -This string literal is used to escape all special characters in the string, -useful for using regex in a query. -""" -macro sr_str(s) s end diff --git a/src/api.jl b/src/api.jl deleted file mode 100644 index 61187a4..0000000 --- a/src/api.jl +++ /dev/null @@ -1,482 +0,0 @@ -function sqlite3_errmsg() - return ccall( (:sqlite3_errmsg, libsqlite), - Ptr{UInt8}, () - ) -end -function sqlite3_errmsg(db::Ptr{Cvoid}) - @NULLCHECK db - return ccall( (:sqlite3_errmsg, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid},), - db) -end -function sqlite3_open(file::AbstractString, handle) - return ccall( (:sqlite3_open, libsqlite), - Cint, (Ptr{UInt8}, Ptr{Cvoid}), - file, handle) -end -function sqlite3_close(handle::Ptr{Cvoid}) - @NULLCHECK handle - return ccall( (:sqlite3_close, libsqlite), - Cint, (Ptr{Cvoid},), - handle) -end -function sqlite3_next_stmt(db::Ptr{Cvoid}, stmt::Ptr{Cvoid}) - @NULLCHECK db - return ccall( (:sqlite3_next_stmt, libsqlite), - Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}), - db, stmt) -end -function sqlite3_prepare_v2(handle::Ptr{Cvoid}, query::AbstractString, stmt, unused) - @NULLCHECK handle - return ccall( (:sqlite3_prepare_v2, libsqlite), - Cint, (Ptr{Cvoid}, Ptr{UInt8}, Cint, Ptr{Cvoid}, Ptr{Cvoid}), - handle, query, sizeof(query), stmt, unused) -end -function sqlite3_prepare16_v2(handle::Ptr{Cvoid}, query::AbstractString, stmt, unused) - @NULLCHECK handle - return ccall( (:sqlite3_prepare16_v2, libsqlite), - Cint, (Ptr{Cvoid}, Ptr{UInt16}, Cint, Ptr{Cvoid}, Ptr{Cvoid}), - handle, query, sizeof(query), stmt, unused) -end -function sqlite3_expanded_sql(stmt::Ptr{Cvoid}) - @NULLCHECK stmt - return ccall( (:sqlite3_expanded_sql, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid},), stmt) -end -sqlite3_free(ptr::Ptr{Cvoid}) = ccall( (:sqlite3_free, libsqlite), Cvoid, (Ptr{Cvoid},), ptr) -function sqlite3_finalize(stmt::Ptr{Cvoid}) - @NULLCHECK stmt - return ccall( (:sqlite3_finalize, libsqlite), - Cint, (Ptr{Cvoid},), - stmt) -end - -# SQLITE_API int sqlite3_bind_paramter_count(sqlite3_stmt*) -function sqlite3_bind_parameter_count(stmt::Ptr{Cvoid}) - return ccall( (:sqlite3_bind_parameter_count, libsqlite), - Cint, (Ptr{Cvoid},), - stmt) -end -#SQLITE_API const char* sqlite3_bind_parameter_name(sqlite3_stmt*, int) -function sqlite3_bind_parameter_name(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_bind_parameter_name, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid}, Cint), - stmt, col) -end -# SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); -function sqlite3_bind_parameter_index(stmt::Ptr{Cvoid}, value::AbstractString) - return ccall( (:sqlite3_bind_parameter_index, libsqlite), - Cint, (Ptr{Cvoid}, Ptr{UInt8}), - stmt, value) -end -# SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); -function sqlite3_bind_double(stmt::Ptr{Cvoid}, col::Int, value::Float64) - return ccall( (:sqlite3_bind_double, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Float64), - stmt, col, value) -end -# SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); -function sqlite3_bind_int(stmt::Ptr{Cvoid}, col::Int, value::Int32) - return ccall( (:sqlite3_bind_int, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Int32), - stmt, col, value) -end -# SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -function sqlite3_bind_int64(stmt::Ptr{Cvoid}, col::Int, value::Int64) - return ccall( (:sqlite3_bind_int64, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Int64), - stmt, col, value) -end -# SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); -function sqlite3_bind_null(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_bind_null, libsqlite), - Cint, (Ptr{Cvoid}, Cint), - stmt, col) -end -# SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); -function sqlite3_bind_text(stmt::Ptr{Cvoid}, col::Int, value::AbstractString) - return ccall( (:sqlite3_bind_text, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Ptr{UInt8}, Cint, Ptr{Cvoid}), - stmt, col, value, sizeof(value), C_NULL) -end -function sqlite3_bind_text(stmt::Ptr{Cvoid}, col::Int, ptr::Ptr{UInt8}, len::Int) - return ccall( (:sqlite3_bind_text, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Ptr{UInt8}, Cint, Ptr{Cvoid}), - stmt, col, ptr, len, C_NULL) -end -# SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -function sqlite3_bind_text16(stmt::Ptr{Cvoid}, col::Int, ptr::Ptr{UInt16}, len::Int) - return ccall( (:sqlite3_bind_text16, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Ptr{UInt16}, Cint, Ptr{Cvoid}), - stmt, col, ptr, len, C_NULL) -end - -# SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -function sqlite3_bind_blob(stmt::Ptr{Cvoid}, col::Int, value) - return ccall( (:sqlite3_bind_blob, libsqlite), - Cint, (Ptr{Cvoid}, Cint, Ptr{UInt8}, Cint, Ptr{Cvoid}), - stmt, col, value, sizeof(value), SQLITE_STATIC) -end -# SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); -# SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); - - -# SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); -function sqlite3_busy_timeout(db::Ptr{Cvoid}, ms) - @NULLCHECK db - return ccall( (:sqlite3_busy_timeout, libsqlite), - Cint, (Ptr{Cvoid}, Cint), - db, ms) -end - -function sqlite3_clear_bindings(stmt::Ptr{Cvoid}) - return ccall( (:sqlite3_clear_bindings, libsqlite), - Cint, (Ptr{Cvoid},), - stmt) -end - -function sqlite3_step(stmt::Ptr{Cvoid}) - return ccall( (:sqlite3_step, libsqlite), - Cint, (Ptr{Cvoid},), - stmt) -end -function sqlite3_column_count(stmt::Ptr{Cvoid}) - return ccall( (:sqlite3_column_count, libsqlite), - Cint, (Ptr{Cvoid},), - stmt) -end -function sqlite3_column_type(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_type, libsqlite), - Cint, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_blob(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_blob, libsqlite), - Ptr{Cvoid}, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_bytes(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_bytes, libsqlite), - Cint, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_bytes16(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_bytes16, libsqlite), - Cint, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_double(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_double, libsqlite), - Cdouble, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_int(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_int, libsqlite), - Cint, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_int64(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_int64, libsqlite), - Clonglong, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_text(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_text, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_text16(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_text16, libsqlite), - Ptr{Cvoid}, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -# function sqlite3_column_value(stmt::Ptr{Cvoid}, col::Cint) -# return ccall( (:sqlite3_column_value, libsqlite), -# Ptr{Cvoid}, (Ptr{Cvoid}, Cint), -# stmt, col-1) -# end -# SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); -function sqlite3_reset(stmt::Ptr{Cvoid}) - return ccall( (:sqlite3_reset, libsqlite), - Cint, (Ptr{Cvoid},), - stmt) -end - -# SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -function sqlite3_column_name(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_name, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -function sqlite3_column_name16(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_name16, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid}, Cint), - stmt, col-1) -end - -function sqlite3_changes(db::Ptr{Cvoid}) - @NULLCHECK db - return ccall( (:sqlite3_changes, libsqlite), - Cint, (Ptr{Cvoid},), - db) -end -function sqlite3_total_changes(db::Ptr{Cvoid}) - @NULLCHECK db - return ccall( (:sqlite3_changes, libsqlite), - Cint, (Ptr{Cvoid},), - db) -end -# SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); - -# SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*, int); -# SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*, int); -# SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*, int); -# SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*, int); -# SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*, int); -# SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*, int); - -function sqlite3_column_decltype(stmt::Ptr{Cvoid}, col::Int) - return ccall( (:sqlite3_column_decltype, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid}, Cint), - stmt, col-1) -end -# SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*, int); -# SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*, int); - -# SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - -# SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -function sqlite3_result_double(context::Ptr{Cvoid}, value::Float64) - return ccall( (:sqlite3_result_double, libsqlite), - Cvoid, (Ptr{Cvoid}, Float64), - context, value) -end -# SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int) -function sqlite3_result_error(context::Ptr{Cvoid}, msg::AbstractString) - return ccall( (:sqlite3_result_error, libsqlite), - Cvoid, (Ptr{Cvoid}, Ptr{UInt8}, Cint), - context, value, sizeof(msg)+1) -end -# SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int) -# SQLITE_API void sqlite3_result_int(sqlite3_context*, int); -function sqlite3_result_int(context::Ptr{Cvoid}, value::Int32) - return ccall( (:sqlite3_result_int, libsqlite), - Cvoid, (Ptr{Cvoid}, Int32), - context, value) -end -# SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -function sqlite3_result_int64(context::Ptr{Cvoid}, value::Int64) - return ccall( (:sqlite3_result_int64, libsqlite), - Cvoid, (Ptr{Cvoid}, Int64), - context, value) -end -# SQLITE_API void sqlite3_result_null(sqlite3_context*); -function sqlite3_result_null(context::Ptr{Cvoid}) - return ccall( (:sqlite3_result_null, libsqlite), - Cvoid, (Ptr{Cvoid},), - context) -end -# SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int n, void(*)(void*)); -function sqlite3_result_text(context::Ptr{Cvoid}, value::AbstractString) - return ccall( (:sqlite3_result_text, libsqlite), - Cvoid, (Ptr{Cvoid}, Ptr{UInt8}, Cint, Ptr{Cvoid}), - context, value, sizeof(value), SQLITE_TRANSIENT) -end -# SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -# SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int n, void(*)(void*)); -function sqlite3_result_blob(context::Ptr{Cvoid}, value) - return ccall( (:sqlite3_result_blob, libsqlite), - Cvoid, (Ptr{Cvoid}, Ptr{UInt8}, Cint, Ptr{Cvoid}), - context, value, sizeof(value), SQLITE_TRANSIENT) -end -# SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); -# SQLITE_API void sqlite3_result_value(sqlite3_context*, const sqlite3_value*); -# SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*) -# SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*) -# SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int) - - -function sqlite3_create_function_v2(db::Ptr{Cvoid}, name::AbstractString, nargs::Integer, - enc::Integer, data::Ptr{Cvoid}, func, - step, final, - destructor::Ptr{Cvoid}) - @NULLCHECK db - return ccall( - (:sqlite3_create_function_v2, libsqlite), - Cint, - (Ptr{Cvoid}, Ptr{UInt8}, Cint, Cint, Ptr{Cvoid}, - Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), - db, name, nargs, enc, data, func, step, final, destructor) -end - -# SQLITE_API void* sqlite3_aggregate_context(sqlite3_context*, int nBytes) -function sqlite3_aggregate_context(context::Ptr{Cvoid}, nbytes::Integer) - return ccall( (:sqlite3_aggregate_context, libsqlite), - Ptr{Cvoid}, (Ptr{Cvoid}, Cint), - context, nbytes) -end - -# SQLITE_API int sqlite3_value_type(sqlite3_value*) -function sqlite3_value_type(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_type, libsqlite), - Cint, (Ptr{Cvoid},), - value) -end - -# SQLITE_API const void* sqlite3_value_blob(sqlite3_value*) -function sqlite3_value_blob(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_blob, libsqlite), - Ptr{Cvoid}, (Ptr{Cvoid},), - value) -end -# SQLITE_API int sqlite3_value_bytes(sqlite3_value*) -function sqlite3_value_bytes(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_bytes, libsqlite), - Cint, (Ptr{Cvoid},), - value) -end -# SQLITE_API int sqlite3_value_bytes16(sqlite3_value*) -function sqlite3_value_bytes16(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_bytes16, libsqlite), - Cint, (Ptr{Cvoid},), - value) -end -# SQLITE_API double sqlite3_value_double(sqlite3_value*) -function sqlite3_value_double(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_double, libsqlite), - Cdouble, (Ptr{Cvoid},), - value) -end -# SQLITE_API int sqlite3_value_int(sqlite3_value*) -function sqlite3_value_int(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_int, libsqlite), - Cint, (Ptr{Cvoid},), - value) -end -# SQLITE_API sqlite_int64 sqlite3_value_int64(sqlite3_value*) -function sqlite3_value_int64(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_int64, libsqlite), - Clonglong, (Ptr{Cvoid},), - value) -end -# SQLITE_API const unsigned char* sqlite3_value_text(sqlite3_value*) -function sqlite3_value_text(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_text, libsqlite), - Ptr{UInt8}, (Ptr{Cvoid},), - value) -end -# SQLITE_API const void* sqlite3_value_text16(sqlite3_value*) -function sqlite3_value_text16(value::Ptr{Cvoid}) - return ccall( (:sqlite3_value_text16, libsqlite), - Ptr{Cvoid}, (Ptr{Cvoid},), - value) -end -# SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*) - - -function sqlite3_initialize() - return ccall( (:sqlite3_initialize, libsqlite), - Cint, (), - ) -end -function sqlite3_shutdown() - return ccall( (:sqlite3_shutdown, libsqlite), - Cint, (), - ) -end -function sqlite3_os_init() - return ccall( (:sqlite3_os_init, libsqlite), - Cint, (), - ) -end -function sqlite3_os_end() - return ccall( (:sqlite3_os_end, libsqlite), - Cint, (), - ) -end -function sqlite3_free_table(result::Array{AbstractString, 1}) - return ccall( (:sqlite3_free_table, libsqlite), - Cvoid, (Ptr{Ptr{Cvoid}},), - result) -end - -# 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); -function sqlite3_errcode(db::Ptr{Cvoid}) - @NULLCHECK db - return ccall( (:sqlite3_errcode, libsqlite), - Cint, (Ptr{Cvoid},), - db) -end -function sqlite3_extended_errcode(db::Ptr{Cvoid}) - @NULLCHECK db - return ccall( (:sqlite3_extended_errcode, libsqlite), - Cint, (Ptr{Cvoid},), - db) -end -# SQLITE_API int sqlite3_errcode(sqlite3 *db); -# SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -function sqlite3_errstr(ret::Cint) - return ccall( (:sqlite3_errstr, libsqlite), - Ptr{UInt8}, (Cint,), - ret) -end -# SQLITE_API const char *sqlite3_errstr(int); - -# SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - -# SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -# SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); - -# SQLITE_API int sqlite3_table_column_metadata( -# sqlite3 *db, /* Connection handle */ -# const char *zDbName, /* Database name or NULL */ -# const char *zTableName, /* Table name */ -# const char *zColumnName, /* Column name */ -# char const **pzDataType, /* OUTPUT: Declared data type */ -# char const **pzCollSeq, /* OUTPUT: Collation sequence name */ -# int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ -# int *pPrimaryKey, /* OUTPUT: True if column part of PK */ -# int *pAutoinc /* OUTPUT: True if column is auto-increment */ -# ); - -# SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); - -# Not directly used -function sqlite3_open_v2(file::AbstractString, handle, flags::Cint, vfs::AbstractString) - return ccall( (:sqlite3_open_v2, libsqlite), - Cint, (Ptr{UInt8}, Ptr{Cvoid}, Cint, Ptr{UInt8}), - file, handle, flags, vfs) -end -function sqlite3_prepare(handle::Ptr{Cvoid}, query::AbstractString, stmt, unused) - @NULLCHECK handle - return ccall( (:sqlite3_prepare, libsqlite), - Cint, (Ptr{Cvoid}, Ptr{UInt8}, Cint, Ptr{Cvoid}, Ptr{Cvoid}), - handle, query, sizeof(query), stmt, unused) -end -function sqlite3_prepare16(handle::Ptr{Cvoid}, query::AbstractString, stmt, unused) - @NULLCHECK handle - return ccall( (:sqlite3_prepare16, libsqlite), - Cint, (Ptr{Cvoid}, Ptr{UInt8}, Cint, Ptr{Cvoid}, Ptr{Cvoid}), - handle, query, sizeof(query), stmt, unused) -end -function sqlite3_close_v2(handle::Ptr{Cvoid}) - @NULLCHECK handle - try - return ccall( (:sqlite3_close_v2, libsqlite), - Cint, (Ptr{Cvoid},), - handle) - catch - # Older versions of the library don't have this, abort to other close - warn("sqlite3_close_v2 not available.") - sqlite3_close(handle) - end -end -function sqlite3_last_insert_rowid(handle::Ptr{Cvoid}) - @NULLCHECK handle - return ccall( (:sqlite3_last_insert_rowid, libsqlite), - Clong, (Ptr{Cvoid},), - handle) -end diff --git a/src/base.jl b/src/base.jl new file mode 100644 index 0000000..7fb6d60 --- /dev/null +++ b/src/base.jl @@ -0,0 +1,56 @@ +struct SQLiteException <: Exception + msg::AbstractString +end + +# SQLite3 DB connection handle +const DBHandle = Ptr{C.sqlite3} +# SQLite3 statement handle +const StmtHandle = Ptr{C.sqlite3_stmt} + +const StmtWrapper = Ref{StmtHandle} + +sqliteexception(handle::DBHandle) = SQLiteException(unsafe_string(C.sqlite3_errmsg(handle))) +function sqliteexception(handle::DBHandle, stmt::StmtHandle) + errstr = unsafe_string(C.sqlite3_errmsg(handle)) + stmt_text_handle = C.sqlite3_expanded_sql(stmt) + stmt_text = unsafe_string(stmt_text_handle) + msg = "$errstr on statement \"$stmt_text\"" + C.sqlite3_free(stmt_text_handle) + return SQLiteException(msg) +end + +sqliteerror(args...) = throw(sqliteexception(args...)) + +# macros + +macro OK(func) + :($(esc(func)) == C.SQLITE_OK) +end + +macro CHECK(db, ex) + esc(quote + if !(@OK $ex) + sqliteerror($db) + end + C.SQLITE_OK + end) +end + +const SQLNullPtrError = SQLiteException("Cannot operate on null pointer") +macro NULLCHECK(ptr) + esc(quote + if $ptr == C_NULL + throw(SQLNullPtrError) + end + end) +end + +""" + sr"..." + +This string literal is used to escape all special characters in the string, +useful for using regex in a query. +""" +macro sr_str(s) + s +end diff --git a/src/bind.jl b/src/bind.jl new file mode 100644 index 0000000..4516d89 --- /dev/null +++ b/src/bind.jl @@ -0,0 +1,214 @@ +""" + SQLite.clear!(stmt::SQLite.Stmt) + +Clears any bound values to a prepared SQL statement +""" +function clear!(stmt::Stmt) + C.sqlite3_clear_bindings(_get_stmt_handle(stmt)) + empty!(stmt.params) + return +end + +""" + SQLite.bind!(stmt::SQLite.Stmt, values) + +bind `values` to parameters in a prepared [`SQLite.Stmt`](@ref). Values can be: + +* `Vector` or `Tuple`: where each element will be bound to an SQL parameter by index order +* `Dict` or `NamedTuple`; where values will be bound to named SQL parameters by the `Dict`/`NamedTuple` key + +Additional methods exist for working individual SQL parameters: + +* `SQLite.bind!(stmt, name, val)`: bind a single value to a named SQL parameter +* `SQLite.bind!(stmt, index, val)`: bind a single value to a SQL parameter by index number + +From the [SQLite documentation](https://www3.sqlite.org/cintro.html): + +> Usually, though, +> it is not useful to evaluate exactly the same SQL statement more than once. +> More often, one wants to evaluate similar statements. +> For example, you might want to evaluate an INSERT statement +> multiple times though with different values to insert. +> To accommodate this kind of flexibility, +> SQLite allows SQL statements to contain parameters +> which are "bound" to values prior to being evaluated. +> These values can later be changed and the same prepared statement +> can be evaluated a second time using the new values. +> +> In SQLite, +> wherever it is valid to include a string literal, +> one can use a parameter in one of the following forms: +> +> - `?` +> - `?NNN` +> - `:AAA` +> - `\$AAA` +> - `@AAA` +> +> In the examples above, +> `NNN` is an integer value and `AAA` is an identifier. +> A parameter initially has a value of `NULL`. +> Prior to calling `sqlite3_step()` for the first time +> or immediately after `sqlite3_reset()``, +> the application can invoke one of the `sqlite3_bind()` interfaces +> to attach values to the parameters. +> Each call to `sqlite3_bind()` overrides prior bindings on the same parameter. + +""" +function bind! end + +function bind!(stmt::Stmt, params::DBInterface.NamedStatementParams) + handle = _get_stmt_handle(stmt) + nparams = C.sqlite3_bind_parameter_count(handle) + (nparams <= length(params)) || + throw(SQLiteException("values should be provided for all query placeholders")) + for i = 1:nparams + name = unsafe_string(C.sqlite3_bind_parameter_name(handle, i)) + isempty(name) && throw(SQLiteException("nameless parameters should be passed as a Vector")) + # name is returned with the ':', '@' or '$' at the start + sym = Symbol(name[2:end]) + haskey(params, sym) || throw( + SQLiteException( + "`$name` not found in values keyword arguments to bind to sql statement", + ), + ) + bind!(stmt, i, params[sym]) + end +end + +function bind!(stmt::Stmt, values::DBInterface.PositionalStatementParams) + nparams = C.sqlite3_bind_parameter_count(_get_stmt_handle(stmt)) + (nparams == length(values)) || + throw(SQLiteException("values should be provided for all query placeholders")) + for i = 1:nparams + @inbounds bind!(stmt, i, values[i]) + end +end + +bind!(stmt::Stmt; kwargs...) = bind!(stmt, kwargs.data) + +# Binding parameters to SQL statements +function bind!(stmt::Stmt, name::AbstractString, val::Any) + i::Int = C.sqlite3_bind_parameter_index(_get_stmt_handle(stmt), name) + if i == 0 + throw(SQLiteException("SQL parameter $name not found in $stmt")) + end + return bind!(stmt, i, val) +end + +# binding method for internal _Stmt class +bind!(stmt::Stmt, i::Integer, val::AbstractFloat) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_double( + _get_stmt_handle(stmt), + i, + Float64(val), + ); return nothing +) +bind!(stmt::Stmt, i::Integer, val::Int32) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_int(_get_stmt_handle(stmt), i, val); return nothing +) +bind!(stmt::Stmt, i::Integer, val::Int64) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_int64(_get_stmt_handle(stmt), i, val); return nothing +) +bind!(stmt::Stmt, i::Integer, val::Missing) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_null(_get_stmt_handle(stmt), i); return nothing +) +bind!(stmt::Stmt, i::Integer, val::Nothing) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_null(_get_stmt_handle(stmt), i); return nothing +) +bind!(stmt::Stmt, i::Integer, val::AbstractString) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_text( + _get_stmt_handle(stmt), + i, + val, + sizeof(val), + C_NULL, + ); return nothing +) +bind!(stmt::Stmt, i::Integer, val::WeakRefString{UInt8}) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_text( + _get_stmt_handle(stmt), + i, + val.ptr, + val.len, + C_NULL, + ); return nothing +) +bind!(stmt::Stmt, i::Integer, val::WeakRefString{UInt16}) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_text16( + _get_stmt_handle(stmt), + i, + val.ptr, + val.len * 2, + C_NULL, + ); return nothing +) +bind!(stmt::Stmt, i::Integer, val::Bool) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_int(_get_stmt_handle(stmt), i, Int32(val)); return nothing +) +bind!(stmt::Stmt, i::Integer, val::Vector{UInt8}) = ( + stmt.params[i] = val; @CHECK stmt.db C.sqlite3_bind_blob( + _get_stmt_handle(stmt), + i, + val, + sizeof(val), + C.SQLITE_STATIC, + ); return nothing +) +# Fallback is BLOB and defaults to serializing the julia value + +# internal wrapper mutable struct to, in-effect, mark something which has been serialized +struct Serialized + object::Any +end + +function sqlserialize(x) + buffer = IOBuffer() + # deserialize will sometimes return a random object when called on an array + # which has not been previously serialized, we can use this mutable struct to check + # that the array has been serialized + s = Serialized(x) + Serialization.serialize(buffer, s) + return take!(buffer) +end +# fallback method to bind arbitrary julia `val` to the parameter at index `i` (object is serialized) +bind!(stmt::Stmt, i::Integer, val::Any) = bind!(stmt, i, sqlserialize(val)) + +struct SerializeError <: Exception + msg::String +end + +# magic bytes that indicate that a value is in fact a serialized julia value, instead of just a byte vector +# these bytes depend on the julia version and other things, so they are determined using an actual serialization +const SERIALIZATION = sqlserialize(0)[1:18] + +function sqldeserialize(r) + if sizeof(r) < sizeof(SERIALIZATION) + return r + end + ret = ccall( + :memcmp, + Int32, + (Ptr{UInt8}, Ptr{UInt8}, UInt), + SERIALIZATION, + r, + min(sizeof(SERIALIZATION), sizeof(r)), + ) + if ret == 0 + try + v = Serialization.deserialize(IOBuffer(r)) + return v.object + catch e + throw( + SerializeError( + "Error deserializing non-primitive value out of database; this is probably due to using SQLite.jl with a different Julia version than was used to originally serialize the database values. The same Julia version that was used to serialize should be used to extract the database values into a different format (csv file, feather file, etc.) and then loaded back into the sqlite database with the current Julia version.", + ), + ) + end + else + return r + end +end +#TODO: +#int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); +#int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); diff --git a/src/capi.jl b/src/capi.jl new file mode 100644 index 0000000..4a40a19 --- /dev/null +++ b/src/capi.jl @@ -0,0 +1,2784 @@ +module C + +using SQLite_jll +export SQLite_jll + +# typedef void ( * sqlite3_destructor_type ) ( void * ) +const sqlite3_destructor_type = Ptr{Cvoid} + +function sqlite3_libversion() + @ccall libsqlite.sqlite3_libversion()::Ptr{Cchar} +end + +function sqlite3_sourceid() + @ccall libsqlite.sqlite3_sourceid()::Ptr{Cchar} +end + +function sqlite3_libversion_number() + @ccall libsqlite.sqlite3_libversion_number()::Cint +end + +function sqlite3_compileoption_used(zOptName) + @ccall libsqlite.sqlite3_compileoption_used(zOptName::Ptr{Cchar})::Cint +end + +function sqlite3_compileoption_get(N) + @ccall libsqlite.sqlite3_compileoption_get(N::Cint)::Ptr{Cchar} +end + +function sqlite3_threadsafe() + @ccall libsqlite.sqlite3_threadsafe()::Cint +end + +mutable struct sqlite3 end + +const sqlite_int64 = Clonglong + +const sqlite_uint64 = Culonglong + +const sqlite3_int64 = sqlite_int64 + +const sqlite3_uint64 = sqlite_uint64 + +function sqlite3_close(arg1) + @ccall libsqlite.sqlite3_close(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_close_v2(arg1) + @ccall libsqlite.sqlite3_close_v2(arg1::Ptr{sqlite3})::Cint +end + +# typedef int ( * sqlite3_callback ) ( void * , int , char * * , char * * ) +const sqlite3_callback = Ptr{Cvoid} + +function sqlite3_exec(arg1, sql, callback, arg4, errmsg) + @ccall libsqlite.sqlite3_exec( + arg1::Ptr{sqlite3}, + sql::Ptr{Cchar}, + callback::Ptr{Cvoid}, + arg4::Ptr{Cvoid}, + errmsg::Ptr{Ptr{Cchar}}, + )::Cint +end + +struct sqlite3_io_methods + iVersion::Cint + xClose::Ptr{Cvoid} + xRead::Ptr{Cvoid} + xWrite::Ptr{Cvoid} + xTruncate::Ptr{Cvoid} + xSync::Ptr{Cvoid} + xFileSize::Ptr{Cvoid} + xLock::Ptr{Cvoid} + xUnlock::Ptr{Cvoid} + xCheckReservedLock::Ptr{Cvoid} + xFileControl::Ptr{Cvoid} + xSectorSize::Ptr{Cvoid} + xDeviceCharacteristics::Ptr{Cvoid} + xShmMap::Ptr{Cvoid} + xShmLock::Ptr{Cvoid} + xShmBarrier::Ptr{Cvoid} + xShmUnmap::Ptr{Cvoid} + xFetch::Ptr{Cvoid} + xUnfetch::Ptr{Cvoid} +end + +struct sqlite3_file + pMethods::Ptr{sqlite3_io_methods} +end + +mutable struct sqlite3_mutex end + +mutable struct sqlite3_api_routines end + +struct sqlite3_vfs + iVersion::Cint + szOsFile::Cint + mxPathname::Cint + pNext::Ptr{sqlite3_vfs} + zName::Ptr{Cchar} + pAppData::Ptr{Cvoid} + xOpen::Ptr{Cvoid} + xDelete::Ptr{Cvoid} + xAccess::Ptr{Cvoid} + xFullPathname::Ptr{Cvoid} + xDlOpen::Ptr{Cvoid} + xDlError::Ptr{Cvoid} + xDlSym::Ptr{Cvoid} + xDlClose::Ptr{Cvoid} + xRandomness::Ptr{Cvoid} + xSleep::Ptr{Cvoid} + xCurrentTime::Ptr{Cvoid} + xGetLastError::Ptr{Cvoid} + xCurrentTimeInt64::Ptr{Cvoid} + xSetSystemCall::Ptr{Cvoid} + xGetSystemCall::Ptr{Cvoid} + xNextSystemCall::Ptr{Cvoid} +end + +# typedef void ( * sqlite3_syscall_ptr ) ( void ) +const sqlite3_syscall_ptr = Ptr{Cvoid} + +function sqlite3_initialize() + @ccall libsqlite.sqlite3_initialize()::Cint +end + +function sqlite3_shutdown() + @ccall libsqlite.sqlite3_shutdown()::Cint +end + +function sqlite3_os_init() + @ccall libsqlite.sqlite3_os_init()::Cint +end + +function sqlite3_os_end() + @ccall libsqlite.sqlite3_os_end()::Cint +end + +struct sqlite3_mem_methods + xMalloc::Ptr{Cvoid} + xFree::Ptr{Cvoid} + xRealloc::Ptr{Cvoid} + xSize::Ptr{Cvoid} + xRoundup::Ptr{Cvoid} + xInit::Ptr{Cvoid} + xShutdown::Ptr{Cvoid} + pAppData::Ptr{Cvoid} +end + +function sqlite3_extended_result_codes(arg1, onoff) + @ccall libsqlite.sqlite3_extended_result_codes(arg1::Ptr{sqlite3}, onoff::Cint)::Cint +end + +function sqlite3_last_insert_rowid(arg1) + @ccall libsqlite.sqlite3_last_insert_rowid(arg1::Ptr{sqlite3})::sqlite3_int64 +end + +function sqlite3_set_last_insert_rowid(arg1, arg2) + @ccall libsqlite.sqlite3_set_last_insert_rowid(arg1::Ptr{sqlite3}, arg2::sqlite3_int64)::Cvoid +end + +function sqlite3_changes(arg1) + @ccall libsqlite.sqlite3_changes(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_changes64(arg1) + @ccall libsqlite.sqlite3_changes64(arg1::Ptr{sqlite3})::sqlite3_int64 +end + +function sqlite3_total_changes(arg1) + @ccall libsqlite.sqlite3_total_changes(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_total_changes64(arg1) + @ccall libsqlite.sqlite3_total_changes64(arg1::Ptr{sqlite3})::sqlite3_int64 +end + +function sqlite3_interrupt(arg1) + @ccall libsqlite.sqlite3_interrupt(arg1::Ptr{sqlite3})::Cvoid +end + +function sqlite3_complete(sql) + @ccall libsqlite.sqlite3_complete(sql::Ptr{Cchar})::Cint +end + +function sqlite3_complete16(sql) + @ccall libsqlite.sqlite3_complete16(sql::Ptr{Cvoid})::Cint +end + +function sqlite3_busy_handler(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_busy_handler( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_busy_timeout(arg1, ms) + @ccall libsqlite.sqlite3_busy_timeout(arg1::Ptr{sqlite3}, ms::Cint)::Cint +end + +function sqlite3_get_table(db, zSql, pazResult, pnRow, pnColumn, pzErrmsg) + @ccall libsqlite.sqlite3_get_table( + db::Ptr{sqlite3}, + zSql::Ptr{Cchar}, + pazResult::Ptr{Ptr{Ptr{Cchar}}}, + pnRow::Ptr{Cint}, + pnColumn::Ptr{Cint}, + pzErrmsg::Ptr{Ptr{Cchar}}, + )::Cint +end + +function sqlite3_free_table(result) + @ccall libsqlite.sqlite3_free_table(result::Ptr{Ptr{Cchar}})::Cvoid +end + +function sqlite3_malloc(arg1) + @ccall libsqlite.sqlite3_malloc(arg1::Cint)::Ptr{Cvoid} +end + +function sqlite3_malloc64(arg1) + @ccall libsqlite.sqlite3_malloc64(arg1::sqlite3_uint64)::Ptr{Cvoid} +end + +function sqlite3_realloc(arg1, arg2) + @ccall libsqlite.sqlite3_realloc(arg1::Ptr{Cvoid}, arg2::Cint)::Ptr{Cvoid} +end + +function sqlite3_realloc64(arg1, arg2) + @ccall libsqlite.sqlite3_realloc64(arg1::Ptr{Cvoid}, arg2::sqlite3_uint64)::Ptr{Cvoid} +end + +function sqlite3_free(arg1) + @ccall libsqlite.sqlite3_free(arg1::Ptr{Cvoid})::Cvoid +end + +function sqlite3_msize(arg1) + @ccall libsqlite.sqlite3_msize(arg1::Ptr{Cvoid})::sqlite3_uint64 +end + +function sqlite3_memory_used() + @ccall libsqlite.sqlite3_memory_used()::sqlite3_int64 +end + +function sqlite3_memory_highwater(resetFlag) + @ccall libsqlite.sqlite3_memory_highwater(resetFlag::Cint)::sqlite3_int64 +end + +function sqlite3_randomness(N, P) + @ccall libsqlite.sqlite3_randomness(N::Cint, P::Ptr{Cvoid})::Cvoid +end + +function sqlite3_set_authorizer(arg1, xAuth, pUserData) + @ccall libsqlite.sqlite3_set_authorizer( + arg1::Ptr{sqlite3}, + xAuth::Ptr{Cvoid}, + pUserData::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_trace(arg1, xTrace, arg3) + @ccall libsqlite.sqlite3_trace( + arg1::Ptr{sqlite3}, + xTrace::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Ptr{Cvoid} +end + +function sqlite3_profile(arg1, xProfile, arg3) + @ccall libsqlite.sqlite3_profile( + arg1::Ptr{sqlite3}, + xProfile::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Ptr{Cvoid} +end + +function sqlite3_trace_v2(arg1, uMask, xCallback, pCtx) + @ccall libsqlite.sqlite3_trace_v2( + arg1::Ptr{sqlite3}, + uMask::Cuint, + xCallback::Ptr{Cvoid}, + pCtx::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_progress_handler(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_progress_handler( + arg1::Ptr{sqlite3}, + arg2::Cint, + arg3::Ptr{Cvoid}, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_open(filename, ppDb) + @ccall libsqlite.sqlite3_open(filename::Ptr{Cchar}, ppDb::Ptr{Ptr{sqlite3}})::Cint +end + +function sqlite3_open16(filename, ppDb) + @ccall libsqlite.sqlite3_open16(filename::Ptr{Cvoid}, ppDb::Ptr{Ptr{sqlite3}})::Cint +end + +function sqlite3_open_v2(filename, ppDb, flags, zVfs) + @ccall libsqlite.sqlite3_open_v2( + filename::Ptr{Cchar}, + ppDb::Ptr{Ptr{sqlite3}}, + flags::Cint, + zVfs::Ptr{Cchar}, + )::Cint +end + +function sqlite3_uri_parameter(zFilename, zParam) + @ccall libsqlite.sqlite3_uri_parameter(zFilename::Ptr{Cchar}, zParam::Ptr{Cchar})::Ptr{Cchar} +end + +function sqlite3_uri_boolean(zFile, zParam, bDefault) + @ccall libsqlite.sqlite3_uri_boolean( + zFile::Ptr{Cchar}, + zParam::Ptr{Cchar}, + bDefault::Cint, + )::Cint +end + +function sqlite3_uri_int64(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_uri_int64( + arg1::Ptr{Cchar}, + arg2::Ptr{Cchar}, + arg3::sqlite3_int64, + )::sqlite3_int64 +end + +function sqlite3_uri_key(zFilename, N) + @ccall libsqlite.sqlite3_uri_key(zFilename::Ptr{Cchar}, N::Cint)::Ptr{Cchar} +end + +function sqlite3_filename_database(arg1) + @ccall libsqlite.sqlite3_filename_database(arg1::Ptr{Cchar})::Ptr{Cchar} +end + +function sqlite3_filename_journal(arg1) + @ccall libsqlite.sqlite3_filename_journal(arg1::Ptr{Cchar})::Ptr{Cchar} +end + +function sqlite3_filename_wal(arg1) + @ccall libsqlite.sqlite3_filename_wal(arg1::Ptr{Cchar})::Ptr{Cchar} +end + +function sqlite3_database_file_object(arg1) + @ccall libsqlite.sqlite3_database_file_object(arg1::Ptr{Cchar})::Ptr{sqlite3_file} +end + +function sqlite3_create_filename(zDatabase, zJournal, zWal, nParam, azParam) + @ccall libsqlite.sqlite3_create_filename( + zDatabase::Ptr{Cchar}, + zJournal::Ptr{Cchar}, + zWal::Ptr{Cchar}, + nParam::Cint, + azParam::Ptr{Ptr{Cchar}}, + )::Ptr{Cchar} +end + +function sqlite3_free_filename(arg1) + @ccall libsqlite.sqlite3_free_filename(arg1::Ptr{Cchar})::Cvoid +end + +function sqlite3_errcode(db) + @ccall libsqlite.sqlite3_errcode(db::Ptr{sqlite3})::Cint +end + +function sqlite3_extended_errcode(db) + @ccall libsqlite.sqlite3_extended_errcode(db::Ptr{sqlite3})::Cint +end + +function sqlite3_errmsg(arg1) + @ccall libsqlite.sqlite3_errmsg(arg1::Ptr{sqlite3})::Ptr{Cchar} +end + +function sqlite3_errmsg16(arg1) + @ccall libsqlite.sqlite3_errmsg16(arg1::Ptr{sqlite3})::Ptr{Cvoid} +end + +function sqlite3_errstr(arg1) + @ccall libsqlite.sqlite3_errstr(arg1::Cint)::Ptr{Cchar} +end + +function sqlite3_error_offset(db) + @ccall libsqlite.sqlite3_error_offset(db::Ptr{sqlite3})::Cint +end + +mutable struct sqlite3_stmt end + +function sqlite3_limit(arg1, id, newVal) + @ccall libsqlite.sqlite3_limit(arg1::Ptr{sqlite3}, id::Cint, newVal::Cint)::Cint +end + +function sqlite3_prepare(db, zSql, nByte, ppStmt, pzTail) + @ccall libsqlite.sqlite3_prepare( + db::Ptr{sqlite3}, + zSql::Ptr{Cchar}, + nByte::Cint, + ppStmt::Ptr{Ptr{sqlite3_stmt}}, + pzTail::Ptr{Ptr{Cchar}}, + )::Cint +end + +function sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail) + @ccall libsqlite.sqlite3_prepare_v2( + db::Ptr{sqlite3}, + zSql::Ptr{Cchar}, + nByte::Cint, + ppStmt::Ptr{Ptr{sqlite3_stmt}}, + pzTail::Ptr{Ptr{Cchar}}, + )::Cint +end + +function sqlite3_prepare_v3(db, zSql, nByte, prepFlags, ppStmt, pzTail) + @ccall libsqlite.sqlite3_prepare_v3( + db::Ptr{sqlite3}, + zSql::Ptr{Cchar}, + nByte::Cint, + prepFlags::Cuint, + ppStmt::Ptr{Ptr{sqlite3_stmt}}, + pzTail::Ptr{Ptr{Cchar}}, + )::Cint +end + +function sqlite3_prepare16(db, zSql, nByte, ppStmt, pzTail) + @ccall libsqlite.sqlite3_prepare16( + db::Ptr{sqlite3}, + zSql::Ptr{Cvoid}, + nByte::Cint, + ppStmt::Ptr{Ptr{sqlite3_stmt}}, + pzTail::Ptr{Ptr{Cvoid}}, + )::Cint +end + +function sqlite3_prepare16_v2(db, zSql, nByte, ppStmt, pzTail) + @ccall libsqlite.sqlite3_prepare16_v2( + db::Ptr{sqlite3}, + zSql::Ptr{Cvoid}, + nByte::Cint, + ppStmt::Ptr{Ptr{sqlite3_stmt}}, + pzTail::Ptr{Ptr{Cvoid}}, + )::Cint +end + +function sqlite3_prepare16_v3(db, zSql, nByte, prepFlags, ppStmt, pzTail) + @ccall libsqlite.sqlite3_prepare16_v3( + db::Ptr{sqlite3}, + zSql::Ptr{Cvoid}, + nByte::Cint, + prepFlags::Cuint, + ppStmt::Ptr{Ptr{sqlite3_stmt}}, + pzTail::Ptr{Ptr{Cvoid}}, + )::Cint +end + +function sqlite3_sql(pStmt) + @ccall libsqlite.sqlite3_sql(pStmt::Ptr{sqlite3_stmt})::Ptr{Cchar} +end + +function sqlite3_expanded_sql(pStmt) + @ccall libsqlite.sqlite3_expanded_sql(pStmt::Ptr{sqlite3_stmt})::Ptr{Cchar} +end + +function sqlite3_stmt_readonly(pStmt) + @ccall libsqlite.sqlite3_stmt_readonly(pStmt::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_stmt_isexplain(pStmt) + @ccall libsqlite.sqlite3_stmt_isexplain(pStmt::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_stmt_busy(arg1) + @ccall libsqlite.sqlite3_stmt_busy(arg1::Ptr{sqlite3_stmt})::Cint +end + +mutable struct sqlite3_value end + +mutable struct sqlite3_context end + +function sqlite3_bind_blob(arg1, arg2, arg3, n, arg5) + @ccall libsqlite.sqlite3_bind_blob( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{Cvoid}, + n::Cint, + arg5::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_bind_blob64(arg1, arg2, arg3, arg4, arg5) + @ccall libsqlite.sqlite3_bind_blob64( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{Cvoid}, + arg4::sqlite3_uint64, + arg5::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_bind_double(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_bind_double(arg1::Ptr{sqlite3_stmt}, arg2::Cint, arg3::Cdouble)::Cint +end + +function sqlite3_bind_int(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_bind_int(arg1::Ptr{sqlite3_stmt}, arg2::Cint, arg3::Cint)::Cint +end + +function sqlite3_bind_int64(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_bind_int64( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::sqlite3_int64, + )::Cint +end + +function sqlite3_bind_null(arg1, arg2) + @ccall libsqlite.sqlite3_bind_null(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Cint +end + +function sqlite3_bind_text(arg1, arg2, arg3, arg4, arg5) + @ccall libsqlite.sqlite3_bind_text( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{Cchar}, + arg4::Cint, + arg5::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_bind_text16(arg1, arg2, arg3, arg4, arg5) + @ccall libsqlite.sqlite3_bind_text16( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{Cvoid}, + arg4::Cint, + arg5::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_bind_text64(arg1, arg2, arg3, arg4, arg5, encoding) + @ccall libsqlite.sqlite3_bind_text64( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{Cchar}, + arg4::sqlite3_uint64, + arg5::Ptr{Cvoid}, + encoding::Cuchar, + )::Cint +end + +function sqlite3_bind_value(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_bind_value( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{sqlite3_value}, + )::Cint +end + +function sqlite3_bind_pointer(arg1, arg2, arg3, arg4, arg5) + @ccall libsqlite.sqlite3_bind_pointer( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::Ptr{Cvoid}, + arg4::Ptr{Cchar}, + arg5::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_bind_zeroblob(arg1, arg2, n) + @ccall libsqlite.sqlite3_bind_zeroblob(arg1::Ptr{sqlite3_stmt}, arg2::Cint, n::Cint)::Cint +end + +function sqlite3_bind_zeroblob64(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_bind_zeroblob64( + arg1::Ptr{sqlite3_stmt}, + arg2::Cint, + arg3::sqlite3_uint64, + )::Cint +end + +function sqlite3_bind_parameter_count(arg1) + @ccall libsqlite.sqlite3_bind_parameter_count(arg1::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_bind_parameter_name(arg1, arg2) + @ccall libsqlite.sqlite3_bind_parameter_name(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cchar} +end + +function sqlite3_bind_parameter_index(arg1, zName) + @ccall libsqlite.sqlite3_bind_parameter_index(arg1::Ptr{sqlite3_stmt}, zName::Ptr{Cchar})::Cint +end + +function sqlite3_clear_bindings(arg1) + @ccall libsqlite.sqlite3_clear_bindings(arg1::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_column_count(pStmt) + @ccall libsqlite.sqlite3_column_count(pStmt::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_column_name(arg1, N) + @ccall libsqlite.sqlite3_column_name(arg1::Ptr{sqlite3_stmt}, N::Cint)::Ptr{Cchar} +end + +function sqlite3_column_name16(arg1, N) + @ccall libsqlite.sqlite3_column_name16(arg1::Ptr{sqlite3_stmt}, N::Cint)::Ptr{Cvoid} +end + +function sqlite3_column_database_name(arg1, arg2) + @ccall libsqlite.sqlite3_column_database_name(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cchar} +end + +function sqlite3_column_database_name16(arg1, arg2) + @ccall libsqlite.sqlite3_column_database_name16(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cvoid} +end + +function sqlite3_column_table_name(arg1, arg2) + @ccall libsqlite.sqlite3_column_table_name(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cchar} +end + +function sqlite3_column_table_name16(arg1, arg2) + @ccall libsqlite.sqlite3_column_table_name16(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cvoid} +end + +function sqlite3_column_origin_name(arg1, arg2) + @ccall libsqlite.sqlite3_column_origin_name(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cchar} +end + +function sqlite3_column_origin_name16(arg1, arg2) + @ccall libsqlite.sqlite3_column_origin_name16(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cvoid} +end + +function sqlite3_column_decltype(arg1, arg2) + @ccall libsqlite.sqlite3_column_decltype(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cchar} +end + +function sqlite3_column_decltype16(arg1, arg2) + @ccall libsqlite.sqlite3_column_decltype16(arg1::Ptr{sqlite3_stmt}, arg2::Cint)::Ptr{Cvoid} +end + +function sqlite3_step(arg1) + @ccall libsqlite.sqlite3_step(arg1::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_data_count(pStmt) + @ccall libsqlite.sqlite3_data_count(pStmt::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_column_blob(arg1, iCol) + @ccall libsqlite.sqlite3_column_blob(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Ptr{Cvoid} +end + +function sqlite3_column_double(arg1, iCol) + @ccall libsqlite.sqlite3_column_double(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Cdouble +end + +function sqlite3_column_int(arg1, iCol) + @ccall libsqlite.sqlite3_column_int(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Cint +end + +function sqlite3_column_int64(arg1, iCol) + @ccall libsqlite.sqlite3_column_int64(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::sqlite3_int64 +end + +function sqlite3_column_text(arg1, iCol) + @ccall libsqlite.sqlite3_column_text(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Ptr{Cuchar} +end + +function sqlite3_column_text16(arg1, iCol) + @ccall libsqlite.sqlite3_column_text16(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Ptr{Cvoid} +end + +function sqlite3_column_value(arg1, iCol) + @ccall libsqlite.sqlite3_column_value(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Ptr{sqlite3_value} +end + +function sqlite3_column_bytes(arg1, iCol) + @ccall libsqlite.sqlite3_column_bytes(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Cint +end + +function sqlite3_column_bytes16(arg1, iCol) + @ccall libsqlite.sqlite3_column_bytes16(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Cint +end + +function sqlite3_column_type(arg1, iCol) + @ccall libsqlite.sqlite3_column_type(arg1::Ptr{sqlite3_stmt}, iCol::Cint)::Cint +end + +function sqlite3_finalize(pStmt) + @ccall libsqlite.sqlite3_finalize(pStmt::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_reset(pStmt) + @ccall libsqlite.sqlite3_reset(pStmt::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_create_function(db, zFunctionName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) + @ccall libsqlite.sqlite3_create_function( + db::Ptr{sqlite3}, + zFunctionName::Ptr{Cchar}, + nArg::Cint, + eTextRep::Cint, + pApp::Ptr{Cvoid}, + xFunc::Ptr{Cvoid}, + xStep::Ptr{Cvoid}, + xFinal::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_create_function16(db, zFunctionName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) + @ccall libsqlite.sqlite3_create_function16( + db::Ptr{sqlite3}, + zFunctionName::Ptr{Cvoid}, + nArg::Cint, + eTextRep::Cint, + pApp::Ptr{Cvoid}, + xFunc::Ptr{Cvoid}, + xStep::Ptr{Cvoid}, + xFinal::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_create_function_v2( + db, + zFunctionName, + nArg, + eTextRep, + pApp, + xFunc, + xStep, + xFinal, + xDestroy, +) + @ccall libsqlite.sqlite3_create_function_v2( + db::Ptr{sqlite3}, + zFunctionName::Ptr{Cchar}, + nArg::Cint, + eTextRep::Cint, + pApp::Ptr{Cvoid}, + xFunc::Ptr{Cvoid}, + xStep::Ptr{Cvoid}, + xFinal::Ptr{Cvoid}, + xDestroy::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_create_window_function( + db, + zFunctionName, + nArg, + eTextRep, + pApp, + xStep, + xFinal, + xValue, + xInverse, + xDestroy, +) + @ccall libsqlite.sqlite3_create_window_function( + db::Ptr{sqlite3}, + zFunctionName::Ptr{Cchar}, + nArg::Cint, + eTextRep::Cint, + pApp::Ptr{Cvoid}, + xStep::Ptr{Cvoid}, + xFinal::Ptr{Cvoid}, + xValue::Ptr{Cvoid}, + xInverse::Ptr{Cvoid}, + xDestroy::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_aggregate_count(arg1) + @ccall libsqlite.sqlite3_aggregate_count(arg1::Ptr{sqlite3_context})::Cint +end + +function sqlite3_expired(arg1) + @ccall libsqlite.sqlite3_expired(arg1::Ptr{sqlite3_stmt})::Cint +end + +function sqlite3_transfer_bindings(arg1, arg2) + @ccall libsqlite.sqlite3_transfer_bindings( + arg1::Ptr{sqlite3_stmt}, + arg2::Ptr{sqlite3_stmt}, + )::Cint +end + +function sqlite3_global_recover() + @ccall libsqlite.sqlite3_global_recover()::Cint +end + +function sqlite3_thread_cleanup() + @ccall libsqlite.sqlite3_thread_cleanup()::Cvoid +end + +function sqlite3_memory_alarm(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_memory_alarm( + arg1::Ptr{Cvoid}, + arg2::Ptr{Cvoid}, + arg3::sqlite3_int64, + )::Cint +end + +function sqlite3_value_blob(arg1) + @ccall libsqlite.sqlite3_value_blob(arg1::Ptr{sqlite3_value})::Ptr{Cvoid} +end + +function sqlite3_value_double(arg1) + @ccall libsqlite.sqlite3_value_double(arg1::Ptr{sqlite3_value})::Cdouble +end + +function sqlite3_value_int(arg1) + @ccall libsqlite.sqlite3_value_int(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_int64(arg1) + @ccall libsqlite.sqlite3_value_int64(arg1::Ptr{sqlite3_value})::sqlite3_int64 +end + +function sqlite3_value_pointer(arg1, arg2) + @ccall libsqlite.sqlite3_value_pointer(arg1::Ptr{sqlite3_value}, arg2::Ptr{Cchar})::Ptr{Cvoid} +end + +function sqlite3_value_text(arg1) + @ccall libsqlite.sqlite3_value_text(arg1::Ptr{sqlite3_value})::Ptr{Cuchar} +end + +function sqlite3_value_text16(arg1) + @ccall libsqlite.sqlite3_value_text16(arg1::Ptr{sqlite3_value})::Ptr{Cvoid} +end + +function sqlite3_value_text16le(arg1) + @ccall libsqlite.sqlite3_value_text16le(arg1::Ptr{sqlite3_value})::Ptr{Cvoid} +end + +function sqlite3_value_text16be(arg1) + @ccall libsqlite.sqlite3_value_text16be(arg1::Ptr{sqlite3_value})::Ptr{Cvoid} +end + +function sqlite3_value_bytes(arg1) + @ccall libsqlite.sqlite3_value_bytes(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_bytes16(arg1) + @ccall libsqlite.sqlite3_value_bytes16(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_type(arg1) + @ccall libsqlite.sqlite3_value_type(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_numeric_type(arg1) + @ccall libsqlite.sqlite3_value_numeric_type(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_nochange(arg1) + @ccall libsqlite.sqlite3_value_nochange(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_frombind(arg1) + @ccall libsqlite.sqlite3_value_frombind(arg1::Ptr{sqlite3_value})::Cint +end + +function sqlite3_value_subtype(arg1) + @ccall libsqlite.sqlite3_value_subtype(arg1::Ptr{sqlite3_value})::Cuint +end + +function sqlite3_value_dup(arg1) + @ccall libsqlite.sqlite3_value_dup(arg1::Ptr{sqlite3_value})::Ptr{sqlite3_value} +end + +function sqlite3_value_free(arg1) + @ccall libsqlite.sqlite3_value_free(arg1::Ptr{sqlite3_value})::Cvoid +end + +function sqlite3_aggregate_context(arg1, nBytes) + @ccall libsqlite.sqlite3_aggregate_context(arg1::Ptr{sqlite3_context}, nBytes::Cint)::Ptr{Cvoid} +end + +function sqlite3_user_data(arg1) + @ccall libsqlite.sqlite3_user_data(arg1::Ptr{sqlite3_context})::Ptr{Cvoid} +end + +function sqlite3_context_db_handle(arg1) + @ccall libsqlite.sqlite3_context_db_handle(arg1::Ptr{sqlite3_context})::Ptr{sqlite3} +end + +function sqlite3_get_auxdata(arg1, N) + @ccall libsqlite.sqlite3_get_auxdata(arg1::Ptr{sqlite3_context}, N::Cint)::Ptr{Cvoid} +end + +function sqlite3_set_auxdata(arg1, N, arg3, arg4) + @ccall libsqlite.sqlite3_set_auxdata( + arg1::Ptr{sqlite3_context}, + N::Cint, + arg3::Ptr{Cvoid}, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_blob(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_blob( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::Cint, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_blob64(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_blob64( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::sqlite3_uint64, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_double(arg1, arg2) + @ccall libsqlite.sqlite3_result_double(arg1::Ptr{sqlite3_context}, arg2::Cdouble)::Cvoid +end + +function sqlite3_result_error(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_result_error( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cchar}, + arg3::Cint, + )::Cvoid +end + +function sqlite3_result_error16(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_result_error16( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::Cint, + )::Cvoid +end + +function sqlite3_result_error_toobig(arg1) + @ccall libsqlite.sqlite3_result_error_toobig(arg1::Ptr{sqlite3_context})::Cvoid +end + +function sqlite3_result_error_nomem(arg1) + @ccall libsqlite.sqlite3_result_error_nomem(arg1::Ptr{sqlite3_context})::Cvoid +end + +function sqlite3_result_error_code(arg1, arg2) + @ccall libsqlite.sqlite3_result_error_code(arg1::Ptr{sqlite3_context}, arg2::Cint)::Cvoid +end + +function sqlite3_result_int(arg1, arg2) + @ccall libsqlite.sqlite3_result_int(arg1::Ptr{sqlite3_context}, arg2::Cint)::Cvoid +end + +function sqlite3_result_int64(arg1, arg2) + @ccall libsqlite.sqlite3_result_int64(arg1::Ptr{sqlite3_context}, arg2::sqlite3_int64)::Cvoid +end + +function sqlite3_result_null(arg1) + @ccall libsqlite.sqlite3_result_null(arg1::Ptr{sqlite3_context})::Cvoid +end + +function sqlite3_result_text(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_text( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cchar}, + arg3::Cint, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_text64(arg1, arg2, arg3, arg4, encoding) + @ccall libsqlite.sqlite3_result_text64( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cchar}, + arg3::sqlite3_uint64, + arg4::Ptr{Cvoid}, + encoding::Cuchar, + )::Cvoid +end + +function sqlite3_result_text16(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_text16( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::Cint, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_text16le(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_text16le( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::Cint, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_text16be(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_text16be( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::Cint, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_value(arg1, arg2) + @ccall libsqlite.sqlite3_result_value( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{sqlite3_value}, + )::Cvoid +end + +function sqlite3_result_pointer(arg1, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_result_pointer( + arg1::Ptr{sqlite3_context}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cchar}, + arg4::Ptr{Cvoid}, + )::Cvoid +end + +function sqlite3_result_zeroblob(arg1, n) + @ccall libsqlite.sqlite3_result_zeroblob(arg1::Ptr{sqlite3_context}, n::Cint)::Cvoid +end + +function sqlite3_result_zeroblob64(arg1, n) + @ccall libsqlite.sqlite3_result_zeroblob64(arg1::Ptr{sqlite3_context}, n::sqlite3_uint64)::Cint +end + +function sqlite3_result_subtype(arg1, arg2) + @ccall libsqlite.sqlite3_result_subtype(arg1::Ptr{sqlite3_context}, arg2::Cuint)::Cvoid +end + +function sqlite3_create_collation(arg1, zName, eTextRep, pArg, xCompare) + @ccall libsqlite.sqlite3_create_collation( + arg1::Ptr{sqlite3}, + zName::Ptr{Cchar}, + eTextRep::Cint, + pArg::Ptr{Cvoid}, + xCompare::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_create_collation_v2(arg1, zName, eTextRep, pArg, xCompare, xDestroy) + @ccall libsqlite.sqlite3_create_collation_v2( + arg1::Ptr{sqlite3}, + zName::Ptr{Cchar}, + eTextRep::Cint, + pArg::Ptr{Cvoid}, + xCompare::Ptr{Cvoid}, + xDestroy::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_create_collation16(arg1, zName, eTextRep, pArg, xCompare) + @ccall libsqlite.sqlite3_create_collation16( + arg1::Ptr{sqlite3}, + zName::Ptr{Cvoid}, + eTextRep::Cint, + pArg::Ptr{Cvoid}, + xCompare::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_collation_needed(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_collation_needed( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_collation_needed16(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_collation_needed16( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_sleep(arg1) + @ccall libsqlite.sqlite3_sleep(arg1::Cint)::Cint +end + +function sqlite3_win32_set_directory(type, zValue) + @ccall libsqlite.sqlite3_win32_set_directory(type::Culong, zValue::Ptr{Cvoid})::Cint +end + +function sqlite3_win32_set_directory8(type, zValue) + @ccall libsqlite.sqlite3_win32_set_directory8(type::Culong, zValue::Ptr{Cchar})::Cint +end + +function sqlite3_win32_set_directory16(type, zValue) + @ccall libsqlite.sqlite3_win32_set_directory16(type::Culong, zValue::Ptr{Cvoid})::Cint +end + +function sqlite3_get_autocommit(arg1) + @ccall libsqlite.sqlite3_get_autocommit(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_db_handle(arg1) + @ccall libsqlite.sqlite3_db_handle(arg1::Ptr{sqlite3_stmt})::Ptr{sqlite3} +end + +function sqlite3_db_filename(db, zDbName) + @ccall libsqlite.sqlite3_db_filename(db::Ptr{sqlite3}, zDbName::Ptr{Cchar})::Ptr{Cchar} +end + +function sqlite3_db_readonly(db, zDbName) + @ccall libsqlite.sqlite3_db_readonly(db::Ptr{sqlite3}, zDbName::Ptr{Cchar})::Cint +end + +function sqlite3_txn_state(arg1, zSchema) + @ccall libsqlite.sqlite3_txn_state(arg1::Ptr{sqlite3}, zSchema::Ptr{Cchar})::Cint +end + +function sqlite3_next_stmt(pDb, pStmt) + @ccall libsqlite.sqlite3_next_stmt( + pDb::Ptr{sqlite3}, + pStmt::Ptr{sqlite3_stmt}, + )::Ptr{sqlite3_stmt} +end + +function sqlite3_commit_hook(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_commit_hook( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Ptr{Cvoid} +end + +function sqlite3_rollback_hook(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_rollback_hook( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Ptr{Cvoid} +end + +function sqlite3_autovacuum_pages(db, arg2, arg3, arg4) + @ccall libsqlite.sqlite3_autovacuum_pages( + db::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + arg4::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_update_hook(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_update_hook( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Ptr{Cvoid} +end + +function sqlite3_enable_shared_cache(arg1) + @ccall libsqlite.sqlite3_enable_shared_cache(arg1::Cint)::Cint +end + +function sqlite3_release_memory(arg1) + @ccall libsqlite.sqlite3_release_memory(arg1::Cint)::Cint +end + +function sqlite3_db_release_memory(arg1) + @ccall libsqlite.sqlite3_db_release_memory(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_soft_heap_limit64(N) + @ccall libsqlite.sqlite3_soft_heap_limit64(N::sqlite3_int64)::sqlite3_int64 +end + +function sqlite3_hard_heap_limit64(N) + @ccall libsqlite.sqlite3_hard_heap_limit64(N::sqlite3_int64)::sqlite3_int64 +end + +function sqlite3_soft_heap_limit(N) + @ccall libsqlite.sqlite3_soft_heap_limit(N::Cint)::Cvoid +end + +function sqlite3_table_column_metadata( + db, + zDbName, + zTableName, + zColumnName, + pzDataType, + pzCollSeq, + pNotNull, + pPrimaryKey, + pAutoinc, +) + @ccall libsqlite.sqlite3_table_column_metadata( + db::Ptr{sqlite3}, + zDbName::Ptr{Cchar}, + zTableName::Ptr{Cchar}, + zColumnName::Ptr{Cchar}, + pzDataType::Ptr{Ptr{Cchar}}, + pzCollSeq::Ptr{Ptr{Cchar}}, + pNotNull::Ptr{Cint}, + pPrimaryKey::Ptr{Cint}, + pAutoinc::Ptr{Cint}, + )::Cint +end + +function sqlite3_load_extension(db, zFile, zProc, pzErrMsg) + @ccall libsqlite.sqlite3_load_extension( + db::Ptr{sqlite3}, + zFile::Ptr{Cchar}, + zProc::Ptr{Cchar}, + pzErrMsg::Ptr{Ptr{Cchar}}, + )::Cint +end + +function sqlite3_enable_load_extension(db, onoff) + @ccall libsqlite.sqlite3_enable_load_extension(db::Ptr{sqlite3}, onoff::Cint)::Cint +end + +function sqlite3_auto_extension(xEntryPoint) + @ccall libsqlite.sqlite3_auto_extension(xEntryPoint::Ptr{Cvoid})::Cint +end + +function sqlite3_cancel_auto_extension(xEntryPoint) + @ccall libsqlite.sqlite3_cancel_auto_extension(xEntryPoint::Ptr{Cvoid})::Cint +end + +function sqlite3_reset_auto_extension() + @ccall libsqlite.sqlite3_reset_auto_extension()::Cvoid +end + +struct sqlite3_module + iVersion::Cint + xCreate::Ptr{Cvoid} + xConnect::Ptr{Cvoid} + xBestIndex::Ptr{Cvoid} + xDisconnect::Ptr{Cvoid} + xDestroy::Ptr{Cvoid} + xOpen::Ptr{Cvoid} + xClose::Ptr{Cvoid} + xFilter::Ptr{Cvoid} + xNext::Ptr{Cvoid} + xEof::Ptr{Cvoid} + xColumn::Ptr{Cvoid} + xRowid::Ptr{Cvoid} + xUpdate::Ptr{Cvoid} + xBegin::Ptr{Cvoid} + xSync::Ptr{Cvoid} + xCommit::Ptr{Cvoid} + xRollback::Ptr{Cvoid} + xFindFunction::Ptr{Cvoid} + xRename::Ptr{Cvoid} + xSavepoint::Ptr{Cvoid} + xRelease::Ptr{Cvoid} + xRollbackTo::Ptr{Cvoid} + xShadowName::Ptr{Cvoid} +end + +struct sqlite3_vtab + pModule::Ptr{sqlite3_module} + nRef::Cint + zErrMsg::Ptr{Cchar} +end + +struct sqlite3_index_constraint + iColumn::Cint + op::Cuchar + usable::Cuchar + iTermOffset::Cint +end + +struct sqlite3_index_orderby + iColumn::Cint + desc::Cuchar +end + +struct sqlite3_index_constraint_usage + argvIndex::Cint + omit::Cuchar +end + +struct sqlite3_index_info + nConstraint::Cint + aConstraint::Ptr{sqlite3_index_constraint} + nOrderBy::Cint + aOrderBy::Ptr{sqlite3_index_orderby} + aConstraintUsage::Ptr{sqlite3_index_constraint_usage} + idxNum::Cint + idxStr::Ptr{Cchar} + needToFreeIdxStr::Cint + orderByConsumed::Cint + estimatedCost::Cdouble + estimatedRows::sqlite3_int64 + idxFlags::Cint + colUsed::sqlite3_uint64 +end + +struct sqlite3_vtab_cursor + pVtab::Ptr{sqlite3_vtab} +end + +function sqlite3_create_module(db, zName, p, pClientData) + @ccall libsqlite.sqlite3_create_module( + db::Ptr{sqlite3}, + zName::Ptr{Cchar}, + p::Ptr{sqlite3_module}, + pClientData::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_create_module_v2(db, zName, p, pClientData, xDestroy) + @ccall libsqlite.sqlite3_create_module_v2( + db::Ptr{sqlite3}, + zName::Ptr{Cchar}, + p::Ptr{sqlite3_module}, + pClientData::Ptr{Cvoid}, + xDestroy::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_drop_modules(db, azKeep) + @ccall libsqlite.sqlite3_drop_modules(db::Ptr{sqlite3}, azKeep::Ptr{Ptr{Cchar}})::Cint +end + +function sqlite3_declare_vtab(arg1, zSQL) + @ccall libsqlite.sqlite3_declare_vtab(arg1::Ptr{sqlite3}, zSQL::Ptr{Cchar})::Cint +end + +function sqlite3_overload_function(arg1, zFuncName, nArg) + @ccall libsqlite.sqlite3_overload_function( + arg1::Ptr{sqlite3}, + zFuncName::Ptr{Cchar}, + nArg::Cint, + )::Cint +end + +mutable struct sqlite3_blob end + +function sqlite3_blob_open(arg1, zDb, zTable, zColumn, iRow, flags, ppBlob) + @ccall libsqlite.sqlite3_blob_open( + arg1::Ptr{sqlite3}, + zDb::Ptr{Cchar}, + zTable::Ptr{Cchar}, + zColumn::Ptr{Cchar}, + iRow::sqlite3_int64, + flags::Cint, + ppBlob::Ptr{Ptr{sqlite3_blob}}, + )::Cint +end + +function sqlite3_blob_reopen(arg1, arg2) + @ccall libsqlite.sqlite3_blob_reopen(arg1::Ptr{sqlite3_blob}, arg2::sqlite3_int64)::Cint +end + +function sqlite3_blob_close(arg1) + @ccall libsqlite.sqlite3_blob_close(arg1::Ptr{sqlite3_blob})::Cint +end + +function sqlite3_blob_bytes(arg1) + @ccall libsqlite.sqlite3_blob_bytes(arg1::Ptr{sqlite3_blob})::Cint +end + +function sqlite3_blob_read(arg1, Z, N, iOffset) + @ccall libsqlite.sqlite3_blob_read( + arg1::Ptr{sqlite3_blob}, + Z::Ptr{Cvoid}, + N::Cint, + iOffset::Cint, + )::Cint +end + +function sqlite3_blob_write(arg1, z, n, iOffset) + @ccall libsqlite.sqlite3_blob_write( + arg1::Ptr{sqlite3_blob}, + z::Ptr{Cvoid}, + n::Cint, + iOffset::Cint, + )::Cint +end + +function sqlite3_vfs_find(zVfsName) + @ccall libsqlite.sqlite3_vfs_find(zVfsName::Ptr{Cchar})::Ptr{sqlite3_vfs} +end + +function sqlite3_vfs_register(arg1, makeDflt) + @ccall libsqlite.sqlite3_vfs_register(arg1::Ptr{sqlite3_vfs}, makeDflt::Cint)::Cint +end + +function sqlite3_vfs_unregister(arg1) + @ccall libsqlite.sqlite3_vfs_unregister(arg1::Ptr{sqlite3_vfs})::Cint +end + +function sqlite3_mutex_alloc(arg1) + @ccall libsqlite.sqlite3_mutex_alloc(arg1::Cint)::Ptr{sqlite3_mutex} +end + +function sqlite3_mutex_free(arg1) + @ccall libsqlite.sqlite3_mutex_free(arg1::Ptr{sqlite3_mutex})::Cvoid +end + +function sqlite3_mutex_enter(arg1) + @ccall libsqlite.sqlite3_mutex_enter(arg1::Ptr{sqlite3_mutex})::Cvoid +end + +function sqlite3_mutex_try(arg1) + @ccall libsqlite.sqlite3_mutex_try(arg1::Ptr{sqlite3_mutex})::Cint +end + +function sqlite3_mutex_leave(arg1) + @ccall libsqlite.sqlite3_mutex_leave(arg1::Ptr{sqlite3_mutex})::Cvoid +end + +struct sqlite3_mutex_methods + xMutexInit::Ptr{Cvoid} + xMutexEnd::Ptr{Cvoid} + xMutexAlloc::Ptr{Cvoid} + xMutexFree::Ptr{Cvoid} + xMutexEnter::Ptr{Cvoid} + xMutexTry::Ptr{Cvoid} + xMutexLeave::Ptr{Cvoid} + xMutexHeld::Ptr{Cvoid} + xMutexNotheld::Ptr{Cvoid} +end + +function sqlite3_mutex_held(arg1) + @ccall libsqlite.sqlite3_mutex_held(arg1::Ptr{sqlite3_mutex})::Cint +end + +function sqlite3_mutex_notheld(arg1) + @ccall libsqlite.sqlite3_mutex_notheld(arg1::Ptr{sqlite3_mutex})::Cint +end + +function sqlite3_db_mutex(arg1) + @ccall libsqlite.sqlite3_db_mutex(arg1::Ptr{sqlite3})::Ptr{sqlite3_mutex} +end + +function sqlite3_file_control(arg1, zDbName, op, arg4) + @ccall libsqlite.sqlite3_file_control( + arg1::Ptr{sqlite3}, + zDbName::Ptr{Cchar}, + op::Cint, + arg4::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_keyword_count() + @ccall libsqlite.sqlite3_keyword_count()::Cint +end + +function sqlite3_keyword_name(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_keyword_name(arg1::Cint, arg2::Ptr{Ptr{Cchar}}, arg3::Ptr{Cint})::Cint +end + +function sqlite3_keyword_check(arg1, arg2) + @ccall libsqlite.sqlite3_keyword_check(arg1::Ptr{Cchar}, arg2::Cint)::Cint +end + +mutable struct sqlite3_str end + +function sqlite3_str_new(arg1) + @ccall libsqlite.sqlite3_str_new(arg1::Ptr{sqlite3})::Ptr{sqlite3_str} +end + +function sqlite3_str_finish(arg1) + @ccall libsqlite.sqlite3_str_finish(arg1::Ptr{sqlite3_str})::Ptr{Cchar} +end + +function sqlite3_str_append(arg1, zIn, N) + @ccall libsqlite.sqlite3_str_append(arg1::Ptr{sqlite3_str}, zIn::Ptr{Cchar}, N::Cint)::Cvoid +end + +function sqlite3_str_appendall(arg1, zIn) + @ccall libsqlite.sqlite3_str_appendall(arg1::Ptr{sqlite3_str}, zIn::Ptr{Cchar})::Cvoid +end + +function sqlite3_str_appendchar(arg1, N, C) + @ccall libsqlite.sqlite3_str_appendchar(arg1::Ptr{sqlite3_str}, N::Cint, C::Cchar)::Cvoid +end + +function sqlite3_str_reset(arg1) + @ccall libsqlite.sqlite3_str_reset(arg1::Ptr{sqlite3_str})::Cvoid +end + +function sqlite3_str_errcode(arg1) + @ccall libsqlite.sqlite3_str_errcode(arg1::Ptr{sqlite3_str})::Cint +end + +function sqlite3_str_length(arg1) + @ccall libsqlite.sqlite3_str_length(arg1::Ptr{sqlite3_str})::Cint +end + +function sqlite3_str_value(arg1) + @ccall libsqlite.sqlite3_str_value(arg1::Ptr{sqlite3_str})::Ptr{Cchar} +end + +function sqlite3_status(op, pCurrent, pHighwater, resetFlag) + @ccall libsqlite.sqlite3_status( + op::Cint, + pCurrent::Ptr{Cint}, + pHighwater::Ptr{Cint}, + resetFlag::Cint, + )::Cint +end + +function sqlite3_status64(op, pCurrent, pHighwater, resetFlag) + @ccall libsqlite.sqlite3_status64( + op::Cint, + pCurrent::Ptr{sqlite3_int64}, + pHighwater::Ptr{sqlite3_int64}, + resetFlag::Cint, + )::Cint +end + +function sqlite3_db_status(arg1, op, pCur, pHiwtr, resetFlg) + @ccall libsqlite.sqlite3_db_status( + arg1::Ptr{sqlite3}, + op::Cint, + pCur::Ptr{Cint}, + pHiwtr::Ptr{Cint}, + resetFlg::Cint, + )::Cint +end + +function sqlite3_stmt_status(arg1, op, resetFlg) + @ccall libsqlite.sqlite3_stmt_status(arg1::Ptr{sqlite3_stmt}, op::Cint, resetFlg::Cint)::Cint +end + +mutable struct sqlite3_pcache end + +struct sqlite3_pcache_page + pBuf::Ptr{Cvoid} + pExtra::Ptr{Cvoid} +end + +struct sqlite3_pcache_methods2 + iVersion::Cint + pArg::Ptr{Cvoid} + xInit::Ptr{Cvoid} + xShutdown::Ptr{Cvoid} + xCreate::Ptr{Cvoid} + xCachesize::Ptr{Cvoid} + xPagecount::Ptr{Cvoid} + xFetch::Ptr{Cvoid} + xUnpin::Ptr{Cvoid} + xRekey::Ptr{Cvoid} + xTruncate::Ptr{Cvoid} + xDestroy::Ptr{Cvoid} + xShrink::Ptr{Cvoid} +end + +struct sqlite3_pcache_methods + pArg::Ptr{Cvoid} + xInit::Ptr{Cvoid} + xShutdown::Ptr{Cvoid} + xCreate::Ptr{Cvoid} + xCachesize::Ptr{Cvoid} + xPagecount::Ptr{Cvoid} + xFetch::Ptr{Cvoid} + xUnpin::Ptr{Cvoid} + xRekey::Ptr{Cvoid} + xTruncate::Ptr{Cvoid} + xDestroy::Ptr{Cvoid} +end + +mutable struct sqlite3_backup end + +function sqlite3_backup_init(pDest, zDestName, pSource, zSourceName) + @ccall libsqlite.sqlite3_backup_init( + pDest::Ptr{sqlite3}, + zDestName::Ptr{Cchar}, + pSource::Ptr{sqlite3}, + zSourceName::Ptr{Cchar}, + )::Ptr{sqlite3_backup} +end + +function sqlite3_backup_step(p, nPage) + @ccall libsqlite.sqlite3_backup_step(p::Ptr{sqlite3_backup}, nPage::Cint)::Cint +end + +function sqlite3_backup_finish(p) + @ccall libsqlite.sqlite3_backup_finish(p::Ptr{sqlite3_backup})::Cint +end + +function sqlite3_backup_remaining(p) + @ccall libsqlite.sqlite3_backup_remaining(p::Ptr{sqlite3_backup})::Cint +end + +function sqlite3_backup_pagecount(p) + @ccall libsqlite.sqlite3_backup_pagecount(p::Ptr{sqlite3_backup})::Cint +end + +function sqlite3_unlock_notify(pBlocked, xNotify, pNotifyArg) + @ccall libsqlite.sqlite3_unlock_notify( + pBlocked::Ptr{sqlite3}, + xNotify::Ptr{Cvoid}, + pNotifyArg::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_stricmp(arg1, arg2) + @ccall libsqlite.sqlite3_stricmp(arg1::Ptr{Cchar}, arg2::Ptr{Cchar})::Cint +end + +function sqlite3_strnicmp(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_strnicmp(arg1::Ptr{Cchar}, arg2::Ptr{Cchar}, arg3::Cint)::Cint +end + +function sqlite3_strglob(zGlob, zStr) + @ccall libsqlite.sqlite3_strglob(zGlob::Ptr{Cchar}, zStr::Ptr{Cchar})::Cint +end + +function sqlite3_strlike(zGlob, zStr, cEsc) + @ccall libsqlite.sqlite3_strlike(zGlob::Ptr{Cchar}, zStr::Ptr{Cchar}, cEsc::Cuint)::Cint +end + +function sqlite3_wal_hook(arg1, arg2, arg3) + @ccall libsqlite.sqlite3_wal_hook( + arg1::Ptr{sqlite3}, + arg2::Ptr{Cvoid}, + arg3::Ptr{Cvoid}, + )::Ptr{Cvoid} +end + +function sqlite3_wal_autocheckpoint(db, N) + @ccall libsqlite.sqlite3_wal_autocheckpoint(db::Ptr{sqlite3}, N::Cint)::Cint +end + +function sqlite3_wal_checkpoint(db, zDb) + @ccall libsqlite.sqlite3_wal_checkpoint(db::Ptr{sqlite3}, zDb::Ptr{Cchar})::Cint +end + +function sqlite3_wal_checkpoint_v2(db, zDb, eMode, pnLog, pnCkpt) + @ccall libsqlite.sqlite3_wal_checkpoint_v2( + db::Ptr{sqlite3}, + zDb::Ptr{Cchar}, + eMode::Cint, + pnLog::Ptr{Cint}, + pnCkpt::Ptr{Cint}, + )::Cint +end + +function sqlite3_vtab_on_conflict(arg1) + @ccall libsqlite.sqlite3_vtab_on_conflict(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_vtab_nochange(arg1) + @ccall libsqlite.sqlite3_vtab_nochange(arg1::Ptr{sqlite3_context})::Cint +end + +function sqlite3_vtab_collation(arg1, arg2) + @ccall libsqlite.sqlite3_vtab_collation(arg1::Ptr{sqlite3_index_info}, arg2::Cint)::Ptr{Cchar} +end + +function sqlite3_vtab_distinct(arg1) + @ccall libsqlite.sqlite3_vtab_distinct(arg1::Ptr{sqlite3_index_info})::Cint +end + +function sqlite3_vtab_in(arg1, iCons, bHandle) + @ccall libsqlite.sqlite3_vtab_in( + arg1::Ptr{sqlite3_index_info}, + iCons::Cint, + bHandle::Cint, + )::Cint +end + +function sqlite3_vtab_in_first(pVal, ppOut) + @ccall libsqlite.sqlite3_vtab_in_first( + pVal::Ptr{sqlite3_value}, + ppOut::Ptr{Ptr{sqlite3_value}}, + )::Cint +end + +function sqlite3_vtab_in_next(pVal, ppOut) + @ccall libsqlite.sqlite3_vtab_in_next( + pVal::Ptr{sqlite3_value}, + ppOut::Ptr{Ptr{sqlite3_value}}, + )::Cint +end + +function sqlite3_vtab_rhs_value(arg1, arg2, ppVal) + @ccall libsqlite.sqlite3_vtab_rhs_value( + arg1::Ptr{sqlite3_index_info}, + arg2::Cint, + ppVal::Ptr{Ptr{sqlite3_value}}, + )::Cint +end + +function sqlite3_stmt_scanstatus(pStmt, idx, iScanStatusOp, pOut) + @ccall libsqlite.sqlite3_stmt_scanstatus( + pStmt::Ptr{sqlite3_stmt}, + idx::Cint, + iScanStatusOp::Cint, + pOut::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_stmt_scanstatus_reset(arg1) + @ccall libsqlite.sqlite3_stmt_scanstatus_reset(arg1::Ptr{sqlite3_stmt})::Cvoid +end + +function sqlite3_db_cacheflush(arg1) + @ccall libsqlite.sqlite3_db_cacheflush(arg1::Ptr{sqlite3})::Cint +end + +function sqlite3_system_errno(arg1) + @ccall libsqlite.sqlite3_system_errno(arg1::Ptr{sqlite3})::Cint +end + +struct sqlite3_snapshot + hidden::NTuple{48,Cuchar} +end + +function sqlite3_snapshot_get(db, zSchema, ppSnapshot) + @ccall libsqlite.sqlite3_snapshot_get( + db::Ptr{sqlite3}, + zSchema::Ptr{Cchar}, + ppSnapshot::Ptr{Ptr{sqlite3_snapshot}}, + )::Cint +end + +function sqlite3_snapshot_open(db, zSchema, pSnapshot) + @ccall libsqlite.sqlite3_snapshot_open( + db::Ptr{sqlite3}, + zSchema::Ptr{Cchar}, + pSnapshot::Ptr{sqlite3_snapshot}, + )::Cint +end + +function sqlite3_snapshot_free(arg1) + @ccall libsqlite.sqlite3_snapshot_free(arg1::Ptr{sqlite3_snapshot})::Cvoid +end + +function sqlite3_snapshot_cmp(p1, p2) + @ccall libsqlite.sqlite3_snapshot_cmp( + p1::Ptr{sqlite3_snapshot}, + p2::Ptr{sqlite3_snapshot}, + )::Cint +end + +function sqlite3_snapshot_recover(db, zDb) + @ccall libsqlite.sqlite3_snapshot_recover(db::Ptr{sqlite3}, zDb::Ptr{Cchar})::Cint +end + +function sqlite3_serialize(db, zSchema, piSize, mFlags) + @ccall libsqlite.sqlite3_serialize( + db::Ptr{sqlite3}, + zSchema::Ptr{Cchar}, + piSize::Ptr{sqlite3_int64}, + mFlags::Cuint, + )::Ptr{Cuchar} +end + +function sqlite3_deserialize(db, zSchema, pData, szDb, szBuf, mFlags) + @ccall libsqlite.sqlite3_deserialize( + db::Ptr{sqlite3}, + zSchema::Ptr{Cchar}, + pData::Ptr{Cuchar}, + szDb::sqlite3_int64, + szBuf::sqlite3_int64, + mFlags::Cuint, + )::Cint +end + +const sqlite3_rtree_dbl = Cdouble + +struct sqlite3_rtree_geometry + pContext::Ptr{Cvoid} + nParam::Cint + aParam::Ptr{sqlite3_rtree_dbl} + pUser::Ptr{Cvoid} + xDelUser::Ptr{Cvoid} +end + +struct sqlite3_rtree_query_info + pContext::Ptr{Cvoid} + nParam::Cint + aParam::Ptr{sqlite3_rtree_dbl} + pUser::Ptr{Cvoid} + xDelUser::Ptr{Cvoid} + aCoord::Ptr{sqlite3_rtree_dbl} + anQueue::Ptr{Cuint} + nCoord::Cint + iLevel::Cint + mxLevel::Cint + iRowid::sqlite3_int64 + rParentScore::sqlite3_rtree_dbl + eParentWithin::Cint + eWithin::Cint + rScore::sqlite3_rtree_dbl + apSqlParam::Ptr{Ptr{sqlite3_value}} +end + +function sqlite3_rtree_geometry_callback(db, zGeom, xGeom, pContext) + @ccall libsqlite.sqlite3_rtree_geometry_callback( + db::Ptr{sqlite3}, + zGeom::Ptr{Cchar}, + xGeom::Ptr{Cvoid}, + pContext::Ptr{Cvoid}, + )::Cint +end + +function sqlite3_rtree_query_callback(db, zQueryFunc, xQueryFunc, pContext, xDestructor) + @ccall libsqlite.sqlite3_rtree_query_callback( + db::Ptr{sqlite3}, + zQueryFunc::Ptr{Cchar}, + xQueryFunc::Ptr{Cvoid}, + pContext::Ptr{Cvoid}, + xDestructor::Ptr{Cvoid}, + )::Cint +end + +struct Fts5ExtensionApi + iVersion::Cint + xUserData::Ptr{Cvoid} + xColumnCount::Ptr{Cvoid} + xRowCount::Ptr{Cvoid} + xColumnTotalSize::Ptr{Cvoid} + xTokenize::Ptr{Cvoid} + xPhraseCount::Ptr{Cvoid} + xPhraseSize::Ptr{Cvoid} + xInstCount::Ptr{Cvoid} + xInst::Ptr{Cvoid} + xRowid::Ptr{Cvoid} + xColumnText::Ptr{Cvoid} + xColumnSize::Ptr{Cvoid} + xQueryPhrase::Ptr{Cvoid} + xSetAuxdata::Ptr{Cvoid} + xGetAuxdata::Ptr{Cvoid} + xPhraseFirst::Ptr{Cvoid} + xPhraseNext::Ptr{Cvoid} + xPhraseFirstColumn::Ptr{Cvoid} + xPhraseNextColumn::Ptr{Cvoid} +end + +mutable struct Fts5Context end + +struct Fts5PhraseIter + a::Ptr{Cuchar} + b::Ptr{Cuchar} +end + +# typedef void ( * fts5_extension_function ) ( const Fts5ExtensionApi * pApi , /* API offered by current FTS version */ Fts5Context * pFts , /* First arg to pass to pApi functions */ sqlite3_context * pCtx , /* Context for returning result/error */ int nVal , /* Number of values in apVal[] array */ sqlite3_value * * apVal /* Array of trailing arguments */ ) +const fts5_extension_function = Ptr{Cvoid} + +mutable struct Fts5Tokenizer end + +struct fts5_tokenizer + xCreate::Ptr{Cvoid} + xDelete::Ptr{Cvoid} + xTokenize::Ptr{Cvoid} +end + +struct fts5_api + iVersion::Cint + xCreateTokenizer::Ptr{Cvoid} + xFindTokenizer::Ptr{Cvoid} + xCreateFunction::Ptr{Cvoid} +end + +# Skipping MacroDefinition: SQLITE_EXTERN extern + +const SQLITE_VERSION = "3.38.4" + +const SQLITE_VERSION_NUMBER = 3038004 + +const SQLITE_SOURCE_ID = "2022-05-04 15:45:55 d402f49871152670a62f4f28cacb15d814f2c1644e9347ad7d258e562978e45e" + +const SQLITE_OK = 0 + +const SQLITE_ERROR = 1 + +const SQLITE_INTERNAL = 2 + +const SQLITE_PERM = 3 + +const SQLITE_ABORT = 4 + +const SQLITE_BUSY = 5 + +const SQLITE_LOCKED = 6 + +const SQLITE_NOMEM = 7 + +const SQLITE_READONLY = 8 + +const SQLITE_INTERRUPT = 9 + +const SQLITE_IOERR = 10 + +const SQLITE_CORRUPT = 11 + +const SQLITE_NOTFOUND = 12 + +const SQLITE_FULL = 13 + +const SQLITE_CANTOPEN = 14 + +const SQLITE_PROTOCOL = 15 + +const SQLITE_EMPTY = 16 + +const SQLITE_SCHEMA = 17 + +const SQLITE_TOOBIG = 18 + +const SQLITE_CONSTRAINT = 19 + +const SQLITE_MISMATCH = 20 + +const SQLITE_MISUSE = 21 + +const SQLITE_NOLFS = 22 + +const SQLITE_AUTH = 23 + +const SQLITE_FORMAT = 24 + +const SQLITE_RANGE = 25 + +const SQLITE_NOTADB = 26 + +const SQLITE_NOTICE = 27 + +const SQLITE_WARNING = 28 + +const SQLITE_ROW = 100 + +const SQLITE_DONE = 101 + +const SQLITE_ERROR_MISSING_COLLSEQ = SQLITE_ERROR | 1 << 8 + +const SQLITE_ERROR_RETRY = SQLITE_ERROR | 2 << 8 + +const SQLITE_ERROR_SNAPSHOT = SQLITE_ERROR | 3 << 8 + +const SQLITE_IOERR_READ = SQLITE_IOERR | 1 << 8 + +const SQLITE_IOERR_SHORT_READ = SQLITE_IOERR | 2 << 8 + +const SQLITE_IOERR_WRITE = SQLITE_IOERR | 3 << 8 + +const SQLITE_IOERR_FSYNC = SQLITE_IOERR | 4 << 8 + +const SQLITE_IOERR_DIR_FSYNC = SQLITE_IOERR | 5 << 8 + +const SQLITE_IOERR_TRUNCATE = SQLITE_IOERR | 6 << 8 + +const SQLITE_IOERR_FSTAT = SQLITE_IOERR | 7 << 8 + +const SQLITE_IOERR_UNLOCK = SQLITE_IOERR | 8 << 8 + +const SQLITE_IOERR_RDLOCK = SQLITE_IOERR | 9 << 8 + +const SQLITE_IOERR_DELETE = SQLITE_IOERR | 10 << 8 + +const SQLITE_IOERR_BLOCKED = SQLITE_IOERR | 11 << 8 + +const SQLITE_IOERR_NOMEM = SQLITE_IOERR | 12 << 8 + +const SQLITE_IOERR_ACCESS = SQLITE_IOERR | 13 << 8 + +const SQLITE_IOERR_CHECKRESERVEDLOCK = SQLITE_IOERR | 14 << 8 + +const SQLITE_IOERR_LOCK = SQLITE_IOERR | 15 << 8 + +const SQLITE_IOERR_CLOSE = SQLITE_IOERR | 16 << 8 + +const SQLITE_IOERR_DIR_CLOSE = SQLITE_IOERR | 17 << 8 + +const SQLITE_IOERR_SHMOPEN = SQLITE_IOERR | 18 << 8 + +const SQLITE_IOERR_SHMSIZE = SQLITE_IOERR | 19 << 8 + +const SQLITE_IOERR_SHMLOCK = SQLITE_IOERR | 20 << 8 + +const SQLITE_IOERR_SHMMAP = SQLITE_IOERR | 21 << 8 + +const SQLITE_IOERR_SEEK = SQLITE_IOERR | 22 << 8 + +const SQLITE_IOERR_DELETE_NOENT = SQLITE_IOERR | 23 << 8 + +const SQLITE_IOERR_MMAP = SQLITE_IOERR | 24 << 8 + +const SQLITE_IOERR_GETTEMPPATH = SQLITE_IOERR | 25 << 8 + +const SQLITE_IOERR_CONVPATH = SQLITE_IOERR | 26 << 8 + +const SQLITE_IOERR_VNODE = SQLITE_IOERR | 27 << 8 + +const SQLITE_IOERR_AUTH = SQLITE_IOERR | 28 << 8 + +const SQLITE_IOERR_BEGIN_ATOMIC = SQLITE_IOERR | 29 << 8 + +const SQLITE_IOERR_COMMIT_ATOMIC = SQLITE_IOERR | 30 << 8 + +const SQLITE_IOERR_ROLLBACK_ATOMIC = SQLITE_IOERR | 31 << 8 + +const SQLITE_IOERR_DATA = SQLITE_IOERR | 32 << 8 + +const SQLITE_IOERR_CORRUPTFS = SQLITE_IOERR | 33 << 8 + +const SQLITE_LOCKED_SHAREDCACHE = SQLITE_LOCKED | 1 << 8 + +const SQLITE_LOCKED_VTAB = SQLITE_LOCKED | 2 << 8 + +const SQLITE_BUSY_RECOVERY = SQLITE_BUSY | 1 << 8 + +const SQLITE_BUSY_SNAPSHOT = SQLITE_BUSY | 2 << 8 + +const SQLITE_BUSY_TIMEOUT = SQLITE_BUSY | 3 << 8 + +const SQLITE_CANTOPEN_NOTEMPDIR = SQLITE_CANTOPEN | 1 << 8 + +const SQLITE_CANTOPEN_ISDIR = SQLITE_CANTOPEN | 2 << 8 + +const SQLITE_CANTOPEN_FULLPATH = SQLITE_CANTOPEN | 3 << 8 + +const SQLITE_CANTOPEN_CONVPATH = SQLITE_CANTOPEN | 4 << 8 + +const SQLITE_CANTOPEN_DIRTYWAL = SQLITE_CANTOPEN | 5 << 8 + +const SQLITE_CANTOPEN_SYMLINK = SQLITE_CANTOPEN | 6 << 8 + +const SQLITE_CORRUPT_VTAB = SQLITE_CORRUPT | 1 << 8 + +const SQLITE_CORRUPT_SEQUENCE = SQLITE_CORRUPT | 2 << 8 + +const SQLITE_CORRUPT_INDEX = SQLITE_CORRUPT | 3 << 8 + +const SQLITE_READONLY_RECOVERY = SQLITE_READONLY | 1 << 8 + +const SQLITE_READONLY_CANTLOCK = SQLITE_READONLY | 2 << 8 + +const SQLITE_READONLY_ROLLBACK = SQLITE_READONLY | 3 << 8 + +const SQLITE_READONLY_DBMOVED = SQLITE_READONLY | 4 << 8 + +const SQLITE_READONLY_CANTINIT = SQLITE_READONLY | 5 << 8 + +const SQLITE_READONLY_DIRECTORY = SQLITE_READONLY | 6 << 8 + +const SQLITE_ABORT_ROLLBACK = SQLITE_ABORT | 2 << 8 + +const SQLITE_CONSTRAINT_CHECK = SQLITE_CONSTRAINT | 1 << 8 + +const SQLITE_CONSTRAINT_COMMITHOOK = SQLITE_CONSTRAINT | 2 << 8 + +const SQLITE_CONSTRAINT_FOREIGNKEY = SQLITE_CONSTRAINT | 3 << 8 + +const SQLITE_CONSTRAINT_FUNCTION = SQLITE_CONSTRAINT | 4 << 8 + +const SQLITE_CONSTRAINT_NOTNULL = SQLITE_CONSTRAINT | 5 << 8 + +const SQLITE_CONSTRAINT_PRIMARYKEY = SQLITE_CONSTRAINT | 6 << 8 + +const SQLITE_CONSTRAINT_TRIGGER = SQLITE_CONSTRAINT | 7 << 8 + +const SQLITE_CONSTRAINT_UNIQUE = SQLITE_CONSTRAINT | 8 << 8 + +const SQLITE_CONSTRAINT_VTAB = SQLITE_CONSTRAINT | 9 << 8 + +const SQLITE_CONSTRAINT_ROWID = SQLITE_CONSTRAINT | 10 << 8 + +const SQLITE_CONSTRAINT_PINNED = SQLITE_CONSTRAINT | 11 << 8 + +const SQLITE_CONSTRAINT_DATATYPE = SQLITE_CONSTRAINT | 12 << 8 + +const SQLITE_NOTICE_RECOVER_WAL = SQLITE_NOTICE | 1 << 8 + +const SQLITE_NOTICE_RECOVER_ROLLBACK = SQLITE_NOTICE | 2 << 8 + +const SQLITE_WARNING_AUTOINDEX = SQLITE_WARNING | 1 << 8 + +const SQLITE_AUTH_USER = SQLITE_AUTH | 1 << 8 + +const SQLITE_OK_LOAD_PERMANENTLY = SQLITE_OK | 1 << 8 + +const SQLITE_OK_SYMLINK = SQLITE_OK | 2 << 8 + +const SQLITE_OPEN_READONLY = 0x00000001 + +const SQLITE_OPEN_READWRITE = 0x00000002 + +const SQLITE_OPEN_CREATE = 0x00000004 + +const SQLITE_OPEN_DELETEONCLOSE = 0x00000008 + +const SQLITE_OPEN_EXCLUSIVE = 0x00000010 + +const SQLITE_OPEN_AUTOPROXY = 0x00000020 + +const SQLITE_OPEN_URI = 0x00000040 + +const SQLITE_OPEN_MEMORY = 0x00000080 + +const SQLITE_OPEN_MAIN_DB = 0x00000100 + +const SQLITE_OPEN_TEMP_DB = 0x00000200 + +const SQLITE_OPEN_TRANSIENT_DB = 0x00000400 + +const SQLITE_OPEN_MAIN_JOURNAL = 0x00000800 + +const SQLITE_OPEN_TEMP_JOURNAL = 0x00001000 + +const SQLITE_OPEN_SUBJOURNAL = 0x00002000 + +const SQLITE_OPEN_SUPER_JOURNAL = 0x00004000 + +const SQLITE_OPEN_NOMUTEX = 0x00008000 + +const SQLITE_OPEN_FULLMUTEX = 0x00010000 + +const SQLITE_OPEN_SHAREDCACHE = 0x00020000 + +const SQLITE_OPEN_PRIVATECACHE = 0x00040000 + +const SQLITE_OPEN_WAL = 0x00080000 + +const SQLITE_OPEN_NOFOLLOW = 0x01000000 + +const SQLITE_OPEN_EXRESCODE = 0x02000000 + +const SQLITE_OPEN_MASTER_JOURNAL = 0x00004000 + +const SQLITE_IOCAP_ATOMIC = 0x00000001 + +const SQLITE_IOCAP_ATOMIC512 = 0x00000002 + +const SQLITE_IOCAP_ATOMIC1K = 0x00000004 + +const SQLITE_IOCAP_ATOMIC2K = 0x00000008 + +const SQLITE_IOCAP_ATOMIC4K = 0x00000010 + +const SQLITE_IOCAP_ATOMIC8K = 0x00000020 + +const SQLITE_IOCAP_ATOMIC16K = 0x00000040 + +const SQLITE_IOCAP_ATOMIC32K = 0x00000080 + +const SQLITE_IOCAP_ATOMIC64K = 0x00000100 + +const SQLITE_IOCAP_SAFE_APPEND = 0x00000200 + +const SQLITE_IOCAP_SEQUENTIAL = 0x00000400 + +const SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 0x00000800 + +const SQLITE_IOCAP_POWERSAFE_OVERWRITE = 0x00001000 + +const SQLITE_IOCAP_IMMUTABLE = 0x00002000 + +const SQLITE_IOCAP_BATCH_ATOMIC = 0x00004000 + +const SQLITE_LOCK_NONE = 0 + +const SQLITE_LOCK_SHARED = 1 + +const SQLITE_LOCK_RESERVED = 2 + +const SQLITE_LOCK_PENDING = 3 + +const SQLITE_LOCK_EXCLUSIVE = 4 + +const SQLITE_SYNC_NORMAL = 0x00000002 + +const SQLITE_SYNC_FULL = 0x00000003 + +const SQLITE_SYNC_DATAONLY = 0x00000010 + +const SQLITE_FCNTL_LOCKSTATE = 1 + +const SQLITE_FCNTL_GET_LOCKPROXYFILE = 2 + +const SQLITE_FCNTL_SET_LOCKPROXYFILE = 3 + +const SQLITE_FCNTL_LAST_ERRNO = 4 + +const SQLITE_FCNTL_SIZE_HINT = 5 + +const SQLITE_FCNTL_CHUNK_SIZE = 6 + +const SQLITE_FCNTL_FILE_POINTER = 7 + +const SQLITE_FCNTL_SYNC_OMITTED = 8 + +const SQLITE_FCNTL_WIN32_AV_RETRY = 9 + +const SQLITE_FCNTL_PERSIST_WAL = 10 + +const SQLITE_FCNTL_OVERWRITE = 11 + +const SQLITE_FCNTL_VFSNAME = 12 + +const SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13 + +const SQLITE_FCNTL_PRAGMA = 14 + +const SQLITE_FCNTL_BUSYHANDLER = 15 + +const SQLITE_FCNTL_TEMPFILENAME = 16 + +const SQLITE_FCNTL_MMAP_SIZE = 18 + +const SQLITE_FCNTL_TRACE = 19 + +const SQLITE_FCNTL_HAS_MOVED = 20 + +const SQLITE_FCNTL_SYNC = 21 + +const SQLITE_FCNTL_COMMIT_PHASETWO = 22 + +const SQLITE_FCNTL_WIN32_SET_HANDLE = 23 + +const SQLITE_FCNTL_WAL_BLOCK = 24 + +const SQLITE_FCNTL_ZIPVFS = 25 + +const SQLITE_FCNTL_RBU = 26 + +const SQLITE_FCNTL_VFS_POINTER = 27 + +const SQLITE_FCNTL_JOURNAL_POINTER = 28 + +const SQLITE_FCNTL_WIN32_GET_HANDLE = 29 + +const SQLITE_FCNTL_PDB = 30 + +const SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = 31 + +const SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = 32 + +const SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = 33 + +const SQLITE_FCNTL_LOCK_TIMEOUT = 34 + +const SQLITE_FCNTL_DATA_VERSION = 35 + +const SQLITE_FCNTL_SIZE_LIMIT = 36 + +const SQLITE_FCNTL_CKPT_DONE = 37 + +const SQLITE_FCNTL_RESERVE_BYTES = 38 + +const SQLITE_FCNTL_CKPT_START = 39 + +const SQLITE_FCNTL_EXTERNAL_READER = 40 + +const SQLITE_FCNTL_CKSM_FILE = 41 + +const SQLITE_GET_LOCKPROXYFILE = SQLITE_FCNTL_GET_LOCKPROXYFILE + +const SQLITE_SET_LOCKPROXYFILE = SQLITE_FCNTL_SET_LOCKPROXYFILE + +const SQLITE_LAST_ERRNO = SQLITE_FCNTL_LAST_ERRNO + +const SQLITE_ACCESS_EXISTS = 0 + +const SQLITE_ACCESS_READWRITE = 1 + +const SQLITE_ACCESS_READ = 2 + +const SQLITE_SHM_UNLOCK = 1 + +const SQLITE_SHM_LOCK = 2 + +const SQLITE_SHM_SHARED = 4 + +const SQLITE_SHM_EXCLUSIVE = 8 + +const SQLITE_SHM_NLOCK = 8 + +const SQLITE_CONFIG_SINGLETHREAD = 1 + +const SQLITE_CONFIG_MULTITHREAD = 2 + +const SQLITE_CONFIG_SERIALIZED = 3 + +const SQLITE_CONFIG_MALLOC = 4 + +const SQLITE_CONFIG_GETMALLOC = 5 + +const SQLITE_CONFIG_SCRATCH = 6 + +const SQLITE_CONFIG_PAGECACHE = 7 + +const SQLITE_CONFIG_HEAP = 8 + +const SQLITE_CONFIG_MEMSTATUS = 9 + +const SQLITE_CONFIG_MUTEX = 10 + +const SQLITE_CONFIG_GETMUTEX = 11 + +const SQLITE_CONFIG_LOOKASIDE = 13 + +const SQLITE_CONFIG_PCACHE = 14 + +const SQLITE_CONFIG_GETPCACHE = 15 + +const SQLITE_CONFIG_LOG = 16 + +const SQLITE_CONFIG_URI = 17 + +const SQLITE_CONFIG_PCACHE2 = 18 + +const SQLITE_CONFIG_GETPCACHE2 = 19 + +const SQLITE_CONFIG_COVERING_INDEX_SCAN = 20 + +const SQLITE_CONFIG_SQLLOG = 21 + +const SQLITE_CONFIG_MMAP_SIZE = 22 + +const SQLITE_CONFIG_WIN32_HEAPSIZE = 23 + +const SQLITE_CONFIG_PCACHE_HDRSZ = 24 + +const SQLITE_CONFIG_PMASZ = 25 + +const SQLITE_CONFIG_STMTJRNL_SPILL = 26 + +const SQLITE_CONFIG_SMALL_MALLOC = 27 + +const SQLITE_CONFIG_SORTERREF_SIZE = 28 + +const SQLITE_CONFIG_MEMDB_MAXSIZE = 29 + +const SQLITE_DBCONFIG_MAINDBNAME = 1000 + +const SQLITE_DBCONFIG_LOOKASIDE = 1001 + +const SQLITE_DBCONFIG_ENABLE_FKEY = 1002 + +const SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003 + +const SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004 + +const SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005 + +const SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006 + +const SQLITE_DBCONFIG_ENABLE_QPSG = 1007 + +const SQLITE_DBCONFIG_TRIGGER_EQP = 1008 + +const SQLITE_DBCONFIG_RESET_DATABASE = 1009 + +const SQLITE_DBCONFIG_DEFENSIVE = 1010 + +const SQLITE_DBCONFIG_WRITABLE_SCHEMA = 1011 + +const SQLITE_DBCONFIG_LEGACY_ALTER_TABLE = 1012 + +const SQLITE_DBCONFIG_DQS_DML = 1013 + +const SQLITE_DBCONFIG_DQS_DDL = 1014 + +const SQLITE_DBCONFIG_ENABLE_VIEW = 1015 + +const SQLITE_DBCONFIG_LEGACY_FILE_FORMAT = 1016 + +const SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017 + +const SQLITE_DBCONFIG_MAX = 1017 + +const SQLITE_DENY = 1 + +const SQLITE_IGNORE = 2 + +const SQLITE_CREATE_INDEX = 1 + +const SQLITE_CREATE_TABLE = 2 + +const SQLITE_CREATE_TEMP_INDEX = 3 + +const SQLITE_CREATE_TEMP_TABLE = 4 + +const SQLITE_CREATE_TEMP_TRIGGER = 5 + +const SQLITE_CREATE_TEMP_VIEW = 6 + +const SQLITE_CREATE_TRIGGER = 7 + +const SQLITE_CREATE_VIEW = 8 + +const SQLITE_DELETE = 9 + +const SQLITE_DROP_INDEX = 10 + +const SQLITE_DROP_TABLE = 11 + +const SQLITE_DROP_TEMP_INDEX = 12 + +const SQLITE_DROP_TEMP_TABLE = 13 + +const SQLITE_DROP_TEMP_TRIGGER = 14 + +const SQLITE_DROP_TEMP_VIEW = 15 + +const SQLITE_DROP_TRIGGER = 16 + +const SQLITE_DROP_VIEW = 17 + +const SQLITE_INSERT = 18 + +const SQLITE_PRAGMA = 19 + +const SQLITE_READ = 20 + +const SQLITE_SELECT = 21 + +const SQLITE_TRANSACTION = 22 + +const SQLITE_UPDATE = 23 + +const SQLITE_ATTACH = 24 + +const SQLITE_DETACH = 25 + +const SQLITE_ALTER_TABLE = 26 + +const SQLITE_REINDEX = 27 + +const SQLITE_ANALYZE = 28 + +const SQLITE_CREATE_VTABLE = 29 + +const SQLITE_DROP_VTABLE = 30 + +const SQLITE_FUNCTION = 31 + +const SQLITE_SAVEPOINT = 32 + +const SQLITE_COPY = 0 + +const SQLITE_RECURSIVE = 33 + +const SQLITE_TRACE_STMT = 0x01 + +const SQLITE_TRACE_PROFILE = 0x02 + +const SQLITE_TRACE_ROW = 0x04 + +const SQLITE_TRACE_CLOSE = 0x08 + +const SQLITE_LIMIT_LENGTH = 0 + +const SQLITE_LIMIT_SQL_LENGTH = 1 + +const SQLITE_LIMIT_COLUMN = 2 + +const SQLITE_LIMIT_EXPR_DEPTH = 3 + +const SQLITE_LIMIT_COMPOUND_SELECT = 4 + +const SQLITE_LIMIT_VDBE_OP = 5 + +const SQLITE_LIMIT_FUNCTION_ARG = 6 + +const SQLITE_LIMIT_ATTACHED = 7 + +const SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8 + +const SQLITE_LIMIT_VARIABLE_NUMBER = 9 + +const SQLITE_LIMIT_TRIGGER_DEPTH = 10 + +const SQLITE_LIMIT_WORKER_THREADS = 11 + +const SQLITE_PREPARE_PERSISTENT = 0x01 + +const SQLITE_PREPARE_NORMALIZE = 0x02 + +const SQLITE_PREPARE_NO_VTAB = 0x04 + +const SQLITE_INTEGER = 1 + +const SQLITE_FLOAT = 2 + +const SQLITE_BLOB = 4 + +const SQLITE_NULL = 5 + +const SQLITE_TEXT = 3 + +const SQLITE3_TEXT = 3 + +const SQLITE_UTF8 = 1 + +const SQLITE_UTF16LE = 2 + +const SQLITE_UTF16BE = 3 + +const SQLITE_UTF16 = 4 + +const SQLITE_ANY = 5 + +const SQLITE_UTF16_ALIGNED = 8 + +const SQLITE_DETERMINISTIC = 0x0000000000000800 + +const SQLITE_DIRECTONLY = 0x0000000000080000 + +const SQLITE_SUBTYPE = 0x0000000000100000 + +const SQLITE_INNOCUOUS = 0x0000000000200000 + +const SQLITE_STATIC = sqlite3_destructor_type(0) + +const SQLITE_TRANSIENT = sqlite3_destructor_type(-1) + +const SQLITE_WIN32_DATA_DIRECTORY_TYPE = 1 + +const SQLITE_WIN32_TEMP_DIRECTORY_TYPE = 2 + +const SQLITE_TXN_NONE = 0 + +const SQLITE_TXN_READ = 1 + +const SQLITE_TXN_WRITE = 2 + +const SQLITE_INDEX_SCAN_UNIQUE = 1 + +const SQLITE_INDEX_CONSTRAINT_EQ = 2 + +const SQLITE_INDEX_CONSTRAINT_GT = 4 + +const SQLITE_INDEX_CONSTRAINT_LE = 8 + +const SQLITE_INDEX_CONSTRAINT_LT = 16 + +const SQLITE_INDEX_CONSTRAINT_GE = 32 + +const SQLITE_INDEX_CONSTRAINT_MATCH = 64 + +const SQLITE_INDEX_CONSTRAINT_LIKE = 65 + +const SQLITE_INDEX_CONSTRAINT_GLOB = 66 + +const SQLITE_INDEX_CONSTRAINT_REGEXP = 67 + +const SQLITE_INDEX_CONSTRAINT_NE = 68 + +const SQLITE_INDEX_CONSTRAINT_ISNOT = 69 + +const SQLITE_INDEX_CONSTRAINT_ISNOTNULL = 70 + +const SQLITE_INDEX_CONSTRAINT_ISNULL = 71 + +const SQLITE_INDEX_CONSTRAINT_IS = 72 + +const SQLITE_INDEX_CONSTRAINT_LIMIT = 73 + +const SQLITE_INDEX_CONSTRAINT_OFFSET = 74 + +const SQLITE_INDEX_CONSTRAINT_FUNCTION = 150 + +const SQLITE_MUTEX_FAST = 0 + +const SQLITE_MUTEX_RECURSIVE = 1 + +const SQLITE_MUTEX_STATIC_MAIN = 2 + +const SQLITE_MUTEX_STATIC_MEM = 3 + +const SQLITE_MUTEX_STATIC_MEM2 = 4 + +const SQLITE_MUTEX_STATIC_OPEN = 4 + +const SQLITE_MUTEX_STATIC_PRNG = 5 + +const SQLITE_MUTEX_STATIC_LRU = 6 + +const SQLITE_MUTEX_STATIC_LRU2 = 7 + +const SQLITE_MUTEX_STATIC_PMEM = 7 + +const SQLITE_MUTEX_STATIC_APP1 = 8 + +const SQLITE_MUTEX_STATIC_APP2 = 9 + +const SQLITE_MUTEX_STATIC_APP3 = 10 + +const SQLITE_MUTEX_STATIC_VFS1 = 11 + +const SQLITE_MUTEX_STATIC_VFS2 = 12 + +const SQLITE_MUTEX_STATIC_VFS3 = 13 + +const SQLITE_MUTEX_STATIC_MASTER = 2 + +const SQLITE_TESTCTRL_FIRST = 5 + +const SQLITE_TESTCTRL_PRNG_SAVE = 5 + +const SQLITE_TESTCTRL_PRNG_RESTORE = 6 + +const SQLITE_TESTCTRL_PRNG_RESET = 7 + +const SQLITE_TESTCTRL_BITVEC_TEST = 8 + +const SQLITE_TESTCTRL_FAULT_INSTALL = 9 + +const SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS = 10 + +const SQLITE_TESTCTRL_PENDING_BYTE = 11 + +const SQLITE_TESTCTRL_ASSERT = 12 + +const SQLITE_TESTCTRL_ALWAYS = 13 + +const SQLITE_TESTCTRL_RESERVE = 14 + +const SQLITE_TESTCTRL_OPTIMIZATIONS = 15 + +const SQLITE_TESTCTRL_ISKEYWORD = 16 + +const SQLITE_TESTCTRL_SCRATCHMALLOC = 17 + +const SQLITE_TESTCTRL_INTERNAL_FUNCTIONS = 17 + +const SQLITE_TESTCTRL_LOCALTIME_FAULT = 18 + +const SQLITE_TESTCTRL_EXPLAIN_STMT = 19 + +const SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD = 19 + +const SQLITE_TESTCTRL_NEVER_CORRUPT = 20 + +const SQLITE_TESTCTRL_VDBE_COVERAGE = 21 + +const SQLITE_TESTCTRL_BYTEORDER = 22 + +const SQLITE_TESTCTRL_ISINIT = 23 + +const SQLITE_TESTCTRL_SORTER_MMAP = 24 + +const SQLITE_TESTCTRL_IMPOSTER = 25 + +const SQLITE_TESTCTRL_PARSER_COVERAGE = 26 + +const SQLITE_TESTCTRL_RESULT_INTREAL = 27 + +const SQLITE_TESTCTRL_PRNG_SEED = 28 + +const SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS = 29 + +const SQLITE_TESTCTRL_SEEK_COUNT = 30 + +const SQLITE_TESTCTRL_TRACEFLAGS = 31 + +const SQLITE_TESTCTRL_TUNE = 32 + +const SQLITE_TESTCTRL_LOGEST = 33 + +const SQLITE_TESTCTRL_LAST = 33 + +const SQLITE_STATUS_MEMORY_USED = 0 + +const SQLITE_STATUS_PAGECACHE_USED = 1 + +const SQLITE_STATUS_PAGECACHE_OVERFLOW = 2 + +const SQLITE_STATUS_SCRATCH_USED = 3 + +const SQLITE_STATUS_SCRATCH_OVERFLOW = 4 + +const SQLITE_STATUS_MALLOC_SIZE = 5 + +const SQLITE_STATUS_PARSER_STACK = 6 + +const SQLITE_STATUS_PAGECACHE_SIZE = 7 + +const SQLITE_STATUS_SCRATCH_SIZE = 8 + +const SQLITE_STATUS_MALLOC_COUNT = 9 + +const SQLITE_DBSTATUS_LOOKASIDE_USED = 0 + +const SQLITE_DBSTATUS_CACHE_USED = 1 + +const SQLITE_DBSTATUS_SCHEMA_USED = 2 + +const SQLITE_DBSTATUS_STMT_USED = 3 + +const SQLITE_DBSTATUS_LOOKASIDE_HIT = 4 + +const SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5 + +const SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6 + +const SQLITE_DBSTATUS_CACHE_HIT = 7 + +const SQLITE_DBSTATUS_CACHE_MISS = 8 + +const SQLITE_DBSTATUS_CACHE_WRITE = 9 + +const SQLITE_DBSTATUS_DEFERRED_FKS = 10 + +const SQLITE_DBSTATUS_CACHE_USED_SHARED = 11 + +const SQLITE_DBSTATUS_CACHE_SPILL = 12 + +const SQLITE_DBSTATUS_MAX = 12 + +const SQLITE_STMTSTATUS_FULLSCAN_STEP = 1 + +const SQLITE_STMTSTATUS_SORT = 2 + +const SQLITE_STMTSTATUS_AUTOINDEX = 3 + +const SQLITE_STMTSTATUS_VM_STEP = 4 + +const SQLITE_STMTSTATUS_REPREPARE = 5 + +const SQLITE_STMTSTATUS_RUN = 6 + +const SQLITE_STMTSTATUS_FILTER_MISS = 7 + +const SQLITE_STMTSTATUS_FILTER_HIT = 8 + +const SQLITE_STMTSTATUS_MEMUSED = 99 + +const SQLITE_CHECKPOINT_PASSIVE = 0 + +const SQLITE_CHECKPOINT_FULL = 1 + +const SQLITE_CHECKPOINT_RESTART = 2 + +const SQLITE_CHECKPOINT_TRUNCATE = 3 + +const SQLITE_VTAB_CONSTRAINT_SUPPORT = 1 + +const SQLITE_VTAB_INNOCUOUS = 2 + +const SQLITE_VTAB_DIRECTONLY = 3 + +const SQLITE_ROLLBACK = 1 + +const SQLITE_FAIL = 3 + +const SQLITE_REPLACE = 5 + +const SQLITE_SCANSTAT_NLOOP = 0 + +const SQLITE_SCANSTAT_NVISIT = 1 + +const SQLITE_SCANSTAT_EST = 2 + +const SQLITE_SCANSTAT_NAME = 3 + +const SQLITE_SCANSTAT_EXPLAIN = 4 + +const SQLITE_SCANSTAT_SELECTID = 5 + +const SQLITE_SERIALIZE_NOCOPY = 0x0001 + +const SQLITE_DESERIALIZE_FREEONCLOSE = 1 + +const SQLITE_DESERIALIZE_RESIZEABLE = 2 + +const SQLITE_DESERIALIZE_READONLY = 4 + +const NOT_WITHIN = 0 + +const PARTLY_WITHIN = 1 + +const FULLY_WITHIN = 2 + +const FTS5_TOKENIZE_QUERY = 0x0001 + +const FTS5_TOKENIZE_PREFIX = 0x0002 + +const FTS5_TOKENIZE_DOCUMENT = 0x0004 + +const FTS5_TOKENIZE_AUX = 0x0008 + +const FTS5_TOKEN_COLOCATED = 0x0001 + +end # module diff --git a/src/consts.jl b/src/consts.jl deleted file mode 100644 index 2780a36..0000000 --- a/src/consts.jl +++ /dev/null @@ -1,304 +0,0 @@ -using SQLite_jll - -#Macros -macro OK(func) - :($(esc(func)) == SQLITE_OK) -end - -macro CHECK(db,ex) - esc(quote - if !(@OK $ex) - sqliteerror($db) - end - SQLITE_OK - end) -end - -const SQLNullPtrError = SQLiteException("Cannot operate on null pointer") -macro NULLCHECK(ptr) - esc(quote - if $ptr == C_NULL - throw(SQLNullPtrError) - end - end) -end - -#Return codes -const SQLITE_OK = 0 # /* Successful result */ -const SQLITE_ERROR = 1 # /* SQL error or missing database */ -const SQLITE_INTERNAL = 2 # /* Internal logic error in SQLite */ -const SQLITE_PERM = 3 # /* Access permission denied */ -const SQLITE_ABORT = 4 # /* Callback routine requested an abort */ -const SQLITE_BUSY = 5 # /* The database file is locked */ -const SQLITE_LOCKED = 6 # /* A table in the database is locked */ -const SQLITE_NOMEM = 7 # /* A malloc() failed */ -const SQLITE_READONLY = 8 # /* Attempt to write a readonly database */ -const SQLITE_INTERRUPT = 9 # /* Operation terminated by sqlite3_interrupt()*/ -const SQLITE_IOERR = 10 # /* Some kind of disk I/O error occurred */ -const SQLITE_CORRUPT = 11 # /* The database disk image is malformed */ -const SQLITE_NOTFOUND = 12 # /* Unknown opcode in sqlite3_file_control() */ -const SQLITE_FULL = 13 # /* Insertion failed because database is full */ -const SQLITE_CANTOPEN = 14 # /* Unable to open the database file */ -const SQLITE_PROTOCOL = 15 # /* Database lock protocol error */ -const SQLITE_EMPTY = 16 # /* Database is empty */ -const SQLITE_SCHEMA = 17 # /* The database schema changed */ -const SQLITE_TOOBIG = 18 # /* String or BLOB exceeds size limit */ -const SQLITE_CONSTRAINT = 19 # /* Abort due to constraint violation */ -const SQLITE_MISMATCH = 20 # /* Data type mismatch */ -const SQLITE_MISUSE = 21 # /* Library used incorrectly */ -const SQLITE_NOLFS = 22 # /* Uses OS features not supported on host */ -const SQLITE_AUTH = 23 # /* Authorization denied */ -const SQLITE_FORMAT = 24 # /* Auxiliary database format error */ -const SQLITE_RANGE = 25 # /* 2nd parameter to sqlite3_bind out of range */ -const SQLITE_NOTADB = 26 # /* File opened that is not a database file */ -const SQLITE_NOTICE = 27 # /* Notifications from sqlite3_log() */ -const SQLITE_WARNING = 28 # /* Warnings from sqlite3_log() */ -const SQLITE_ROW = 100 # /* sqlite3_step() has another row ready */ -const SQLITE_DONE = 101 # /* sqlite3_step() has finished executing */ -#Extended Return codes -const SQLITE_IOERR_READ = (SQLITE_IOERR | (1<<8)) -const SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2<<8)) -const SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3<<8)) -const SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4<<8)) -const SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5<<8)) -const SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6<<8)) -const SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7<<8)) -const SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8<<8)) -const SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9<<8)) -const SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10<<8)) -const SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11<<8)) -const SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12<<8)) -const SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13<<8)) -const SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14<<8)) -const SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15<<8)) -const SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16<<8)) -const SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17<<8)) -const SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18<<8)) -const SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19<<8)) -const SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20<<8)) -const SQLITE_IOERR_SHMMAP = (SQLITE_IOERR | (21<<8)) -const SQLITE_IOERR_SEEK = (SQLITE_IOERR | (22<<8)) -const SQLITE_IOERR_DELETE_NOENT = (SQLITE_IOERR | (23<<8)) -const SQLITE_IOERR_MMAP = (SQLITE_IOERR | (24<<8)) -const SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1<<8)) -const SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1<<8)) -const SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1<<8)) -const SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2<<8)) -const SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3<<8)) -const SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1<<8)) -const SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1<<8)) -const SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2<<8)) -const SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3<<8)) -const SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2<<8)) -const SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1<<8)) -const SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2<<8)) -const SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3<<8)) -const SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4<<8)) -const SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5<<8)) -const SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6<<8)) -const SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7<<8)) -const SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8<<8)) -const SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9<<8)) -const SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1<<8)) -const SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2<<8)) -#Text Encodings -const SQLITE_UTF8 = 1 # -const SQLITE_UTF16LE = 2 # -const SQLITE_UTF16BE = 3 # -const SQLITE_UTF16 = 4 # /* Use native byte order */ -const SQLITE_ANY = 5 # /* DEPRECATED */ -const SQLITE_UTF16_ALIGNED = 8 # /* sqlite3_create_collation only */ - -#Fundamental Data Types -const SQLITE_INTEGER = 1 -const SQLITE_FLOAT = 2 -const SQLITE_TEXT = 3 -const SQLITE_BLOB = 4 -const SQLITE_NULL = 5 - -const SQLITE3_TEXT = 3 - -#Checkpoint operation parameters -const SQLITE_CHECKPOINT_PASSIVE = 0 # -const SQLITE_CHECKPOINT_FULL = 1 # -const SQLITE_CHECKPOINT_RESTART = 2 # - -#Configuration Options -const SQLITE_CONFIG_SINGLETHREAD = 1 # /* nil */ -const SQLITE_CONFIG_MULTITHREAD = 2 # /* nil */ -const SQLITE_CONFIG_SERIALIZED = 3 # /* nil */ -const SQLITE_CONFIG_MALLOC = 4 # /* sqlite3_mem_methods* */ -const SQLITE_CONFIG_GETMALLOC = 5 # /* sqlite3_mem_methods* */ -const SQLITE_CONFIG_SCRATCH = 6 # /* void*, int sz, int N */ -const SQLITE_CONFIG_PAGECACHE = 7 # /* void*, int sz, int N */ -const SQLITE_CONFIG_HEAP = 8 # /* void*, int nByte, int min */ -const SQLITE_CONFIG_MEMSTATUS = 9 # /* boolean */ -const SQLITE_CONFIG_MUTEX = 10 # /* sqlite3_mutex_methods* */ -const SQLITE_CONFIG_GETMUTEX = 11 # /* sqlite3_mutex_methods* */ -#/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -const SQLITE_CONFIG_LOOKASIDE = 13 # /* int int */ -const SQLITE_CONFIG_PCACHE = 14 # /* no-op */ -const SQLITE_CONFIG_GETPCACHE = 15 # /* no-op */ -const SQLITE_CONFIG_LOG = 16 # /* xFunc, void* */ -const SQLITE_CONFIG_URI = 17 # /* int */ -const SQLITE_CONFIG_PCACHE2 = 18 # /* sqlite3_pcache_methods2* */ -const SQLITE_CONFIG_GETPCACHE2 = 19 # /* sqlite3_pcache_methods2* */ -const SQLITE_CONFIG_COVERING_INDEX_SCAN = 20 # /* int */ -const SQLITE_CONFIG_SQLLOG = 21 # /* xSqllog, void* */ -const SQLITE_CONFIG_MMAP_SIZE = 22 # /* sqlite3_int64, sqlite3_int64 */ - -#Database Connection Configuration Options -const SQLITE_DBCONFIG_LOOKASIDE = 1001 # /* void* int int */ -const SQLITE_DBCONFIG_ENABLE_FKEY = 1002 # /* int int* */ -const SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003 # /* int int* */ - -#Status Parameters for database connections -const SQLITE_DBSTATUS_LOOKASIDE_USED = 0 # -const SQLITE_DBSTATUS_CACHE_USED = 1 # -const SQLITE_DBSTATUS_SCHEMA_USED = 2 # -const SQLITE_DBSTATUS_STMT_USED = 3 # -const SQLITE_DBSTATUS_LOOKASIDE_HIT = 4 # -const SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5 # -const SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6 # -const SQLITE_DBSTATUS_CACHE_HIT = 7 # -const SQLITE_DBSTATUS_CACHE_MISS = 8 # -const SQLITE_DBSTATUS_CACHE_WRITE = 9 # -const SQLITE_DBSTATUS_MAX = 9 # /* Largest defined DBSTATUS */ - -#Authorizer Return Codes -const SQLITE_DENY = 1 # /* Abort the SQL statement with an error */ -const SQLITE_IGNORE = 2 # /* Don't allow access, but don't generate an error */ - -#Conflict resolution modes -const SQLITE_ROLLBACK = 1 # -#/* const SQLITE_IGNORE = 2 # // Also used by sqlite3_authorizer() callback */ -const SQLITE_FAIL = 3 # -#/* const SQLITE_ABORT = 4 # // Also an error code */ -const SQLITE_REPLACE = 5 # - -#Standard File Control Opcodes -const SQLITE_FCNTL_LOCKSTATE = 1 # -const SQLITE_GET_LOCKPROXYFILE = 2 # -const SQLITE_SET_LOCKPROXYFILE = 3 # -const SQLITE_LAST_ERRNO = 4 # -const SQLITE_FCNTL_SIZE_HINT = 5 # -const SQLITE_FCNTL_CHUNK_SIZE = 6 # -const SQLITE_FCNTL_FILE_POINTER = 7 # -const SQLITE_FCNTL_SYNC_OMITTED = 8 # -const SQLITE_FCNTL_WIN32_AV_RETRY = 9 # -const SQLITE_FCNTL_PERSIST_WAL = 10 # -const SQLITE_FCNTL_OVERWRITE = 11 # -const SQLITE_FCNTL_VFSNAME = 12 # -const SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13 # -const SQLITE_FCNTL_PRAGMA = 14 # -const SQLITE_FCNTL_BUSYHANDLER = 15 # -const SQLITE_FCNTL_TEMPFILENAME = 16 # -const SQLITE_FCNTL_MMAP_SIZE = 18 # - -#Device Characteristics -const SQLITE_IOCAP_ATOMIC = 0x00000001 # -const SQLITE_IOCAP_ATOMIC512 = 0x00000002 # -const SQLITE_IOCAP_ATOMIC1K = 0x00000004 # -const SQLITE_IOCAP_ATOMIC2K = 0x00000008 # -const SQLITE_IOCAP_ATOMIC4K = 0x00000010 # -const SQLITE_IOCAP_ATOMIC8K = 0x00000020 # -const SQLITE_IOCAP_ATOMIC16K = 0x00000040 # -const SQLITE_IOCAP_ATOMIC32K = 0x00000080 # -const SQLITE_IOCAP_ATOMIC64K = 0x00000100 # -const SQLITE_IOCAP_SAFE_APPEND = 0x00000200 # -const SQLITE_IOCAP_SEQUENTIAL = 0x00000400 # -const SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 0x00000800 # -const SQLITE_IOCAP_POWERSAFE_OVERWRITE = 0x00001000 # - -#Run-Time Limit Categories -const SQLITE_LIMIT_LENGTH = 0 # -const SQLITE_LIMIT_SQL_LENGTH = 1 # -const SQLITE_LIMIT_COLUMN = 2 # -const SQLITE_LIMIT_EXPR_DEPTH = 3 # -const SQLITE_LIMIT_COMPOUND_SELECT = 4 # -const SQLITE_LIMIT_VDBE_OP = 5 # -const SQLITE_LIMIT_FUNCTION_ARG = 6 # -const SQLITE_LIMIT_ATTACHED = 7 # -const SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8 # -const SQLITE_LIMIT_VARIABLE_NUMBER = 9 # -const SQLITE_LIMIT_TRIGGER_DEPTH = 10 # - -#File Locking Levels -const SQLITE_LOCK_NONE = 0 # -const SQLITE_LOCK_SHARED = 1 # -const SQLITE_LOCK_RESERVED = 2 # -const SQLITE_LOCK_PENDING = 3 # -const SQLITE_LOCK_EXCLUSIVE = 4 # - -#Mutex Types -const SQLITE_MUTEX_FAST = 0 # -const SQLITE_MUTEX_RECURSIVE = 1 # -const SQLITE_MUTEX_STATIC_MASTER = 2 # -const SQLITE_MUTEX_STATIC_MEM = 3 # /* sqlite3_malloc() */ -const SQLITE_MUTEX_STATIC_MEM2 = 4 # /* NOT USED */ -const SQLITE_MUTEX_STATIC_OPEN = 4 # /* sqlite3BtreeOpen() */ -const SQLITE_MUTEX_STATIC_PRNG = 5 # /* sqlite3_random() */ -const SQLITE_MUTEX_STATIC_LRU = 6 # /* lru page list */ -const SQLITE_MUTEX_STATIC_LRU2 = 7 # /* NOT USED */ -const SQLITE_MUTEX_STATIC_PMEM = 7 # /* sqlite3PageMalloc() */ - -#Flags for the xShmLock VFS method -const SQLITE_SHM_UNLOCK = 1 # -const SQLITE_SHM_LOCK = 2 # -const SQLITE_SHM_SHARED = 4 # -const SQLITE_SHM_EXCLUSIVE = 8 # - -#Constants Defining Special Destructor Behavior -# typedef void (*sqlite3_destructor_type)(void*); -const SQLITE_STATIC = Ptr{Cvoid}(0) -const SQLITE_TRANSIENT = Ptr{Cvoid}(-1) - -#Function Flags -const SQLITE_DETERMINISTIC = 0x800 - -#Maximum xShmLock index -const SQLITE_SHM_NLOCK = 8 # - -#Status Parameters -const SQLITE_STATUS_MEMORY_USED = 0 # -const SQLITE_STATUS_PAGECACHE_USED = 1 # -const SQLITE_STATUS_PAGECACHE_OVERFLOW = 2 # -const SQLITE_STATUS_SCRATCH_USED = 3 # -const SQLITE_STATUS_SCRATCH_OVERFLOW = 4 # -const SQLITE_STATUS_MALLOC_SIZE = 5 # -const SQLITE_STATUS_PARSER_STACK = 6 # -const SQLITE_STATUS_PAGECACHE_SIZE = 7 # -const SQLITE_STATUS_SCRATCH_SIZE = 8 # -const SQLITE_STATUS_MALLOC_COUNT = 9 # - -#Status Parameters for prepared statements -const SQLITE_STMTSTATUS_FULLSCAN_STEP = 1 # -const SQLITE_STMTSTATUS_SORT = 2 # -const SQLITE_STMTSTATUS_AUTOINDEX = 3 # - -#Synchronization Type Flags -const SQLITE_SYNC_NORMAL = 0x00002 # -const SQLITE_SYNC_FULL = 0x00003 # -const SQLITE_SYNC_DATAONLY = 0x00010 # - -#Testing Interface Operation Codes -const SQLITE_TESTCTRL_FIRST = 5 # -const SQLITE_TESTCTRL_PRNG_SAVE = 5 # -const SQLITE_TESTCTRL_PRNG_RESTORE = 6 # -const SQLITE_TESTCTRL_PRNG_RESET = 7 # -const SQLITE_TESTCTRL_BITVEC_TEST = 8 # -const SQLITE_TESTCTRL_FAULT_INSTALL = 9 # -const SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS = 10 # -const SQLITE_TESTCTRL_PENDING_BYTE = 11 # -const SQLITE_TESTCTRL_ASSERT = 12 # -const SQLITE_TESTCTRL_ALWAYS = 13 # -const SQLITE_TESTCTRL_RESERVE = 14 # -const SQLITE_TESTCTRL_OPTIMIZATIONS = 15 # -const SQLITE_TESTCTRL_ISKEYWORD = 16 # -const SQLITE_TESTCTRL_SCRATCHMALLOC = 17 # -const SQLITE_TESTCTRL_LOCALTIME_FAULT = 18 # -const SQLITE_TESTCTRL_EXPLAIN_STMT = 19 # -const SQLITE_TESTCTRL_LAST = 19 # - -#Virtual Table Configuration Options -const SQLITE_VTAB_CONSTRAINT_SUPPORT = 1 # diff --git a/src/db_and_stmt.jl b/src/db_and_stmt.jl new file mode 100644 index 0000000..7be1ec3 --- /dev/null +++ b/src/db_and_stmt.jl @@ -0,0 +1,135 @@ +""" + `SQLite.DB()` => in-memory SQLite database + `SQLite.DB(file)` => file-based SQLite database + +Constructors for a representation of an sqlite database, either backed by an on-disk file or in-memory. + +`SQLite.DB` requires the `file` string argument in the 2nd definition +as the name of either a pre-defined SQLite database to be opened, +or if the file doesn't exist, a database will be created. +Note that only sqlite 3.x version files are supported. + +The `SQLite.DB` object represents a single connection to an SQLite database. +All other SQLite.jl functions take an `SQLite.DB` as the first argument as context. + +To create an in-memory temporary database, call `SQLite.DB()`. + +The `SQLite.DB` will be automatically closed/shutdown when it goes out of scope +(i.e. the end of the Julia session, end of a function call wherein it was created, etc.) +""" +mutable struct DB <: DBInterface.Connection + file::String + handle::DBHandle + stmt_wrappers::WeakKeyDict{StmtWrapper,Nothing} # opened prepared statements + + function DB(f::AbstractString) + handle_ptr = Ref{DBHandle}() + f = String(isempty(f) ? f : expanduser(f)) + if @OK C.sqlite3_open(f, handle_ptr) + db = new(f, handle_ptr[], WeakKeyDict{StmtWrapper,Nothing}()) + finalizer(_close_db!, db) + return db + else # error + sqliteerror(handle_ptr[]) + end + end +end +DB() = DB(":memory:") +DBInterface.connect(::Type{DB}) = DB() +DBInterface.connect(::Type{DB}, f::AbstractString) = DB(f) +DBInterface.close!(db::DB) = _close_db!(db) +Base.close(db::DB) = _close_db!(db) +Base.isopen(db::DB) = db.handle != C_NULL + +function finalize_statements!(db::DB) + # close stmts + for stmt_wrapper in keys(db.stmt_wrappers) + C.sqlite3_finalize(stmt_wrapper[]) + stmt_wrapper[] = C_NULL + end + empty!(db.stmt_wrappers) +end + +function _close_db!(db::DB) + finalize_statements!(db) + + # close DB + C.sqlite3_close_v2(db.handle) + db.handle = C_NULL + + return +end + +sqliteexception(db::DB) = sqliteexception(db.handle) + +Base.show(io::IO, db::DB) = print(io, string("SQLite.DB(", "\"$(db.file)\"", ")")) + +# prepare given sql statement +function prepare_stmt_wrapper(db::DB, sql::AbstractString) + handle_ptr = Ref{StmtHandle}() + C.sqlite3_prepare_v2(db.handle, sql, sizeof(sql), handle_ptr, C_NULL) + return handle_ptr +end + +""" + SQLite.Stmt(db, sql) => SQL.Stmt + +Prepares an optimized internal representation of SQL statement in +the context of the provided SQLite3 `db` and constructs the `SQLite.Stmt` +Julia object that holds a reference to the prepared statement. + +*Note*: the `sql` statement is not actually executed, but only compiled +(mainly for usage where the same statement is executed multiple times +with different parameters bound as values). + +Internally `SQLite.Stmt` constructor creates the [`SQLite._Stmt`](@ref) object that is managed by `db`. +`SQLite.Stmt` references the `SQLite._Stmt` by its unique id. + +The `SQLite.Stmt` will be automatically closed/shutdown when it goes out of scope +(i.e. the end of the Julia session, end of a function call wherein it was created, etc.). +One can also call `DBInterface.close!(stmt)` to immediately close it. + +All prepared statements of a given DB connection are also automatically closed when the +DB is disconnected or when [`SQLite.finalize_statements!`](@ref) is explicitly called. +""" +mutable struct Stmt <: DBInterface.Statement + db::DB + stmt_wrapper::StmtWrapper + params::Dict{Int,Any} + + function Stmt(db::DB, sql::AbstractString; register::Bool = true) + stmt_wrapper = prepare_stmt_wrapper(db, sql) + if register + db.stmt_wrappers[stmt_wrapper] = nothing + end + stmt = new(db, stmt_wrapper, Dict{Int,Any}()) + finalizer(_close_stmt!, stmt) + return stmt + end +end + +""" + DBInterface.prepare(db::SQLite.DB, sql::AbstractString) + +Prepare an SQL statement given as a string in the sqlite database; returns an `SQLite.Stmt` compiled object. +See `DBInterface.execute`(@ref) for information on executing a prepared statement and passing parameters to bind. +A `SQLite.Stmt` object can be closed (resources freed) using `DBInterface.close!`(@ref). +""" +DBInterface.prepare(db::DB, sql::AbstractString) = Stmt(db, sql) +DBInterface.getconnection(stmt::Stmt) = stmt.db +DBInterface.close!(stmt::Stmt) = _close_stmt!(stmt) + +_get_stmt_handle(stmt::Stmt) = stmt.stmt_wrapper[] +function _set_stmt_handle(stmt::Stmt, handle) + stmt.stmt_wrapper[] = handle + return +end + +function _close_stmt!(stmt::Stmt) + C.sqlite3_finalize(_get_stmt_handle(stmt)) + _set_stmt_handle(stmt, C_NULL) + + return +end + +sqliteexception(db::DB, stmt::Stmt) = sqliteexception(db.handle, _get_stmt_handle(stmt)) diff --git a/src/db_functions.jl b/src/db_functions.jl new file mode 100644 index 0000000..7f5484f --- /dev/null +++ b/src/db_functions.jl @@ -0,0 +1,57 @@ +struct DBTable + name::String + schema::Union{Tables.Schema, Nothing} +end + +DBTable(name::String) = DBTable(name, nothing) + +const DBTables = AbstractVector{DBTable} + +Tables.istable(::Type{<:DBTables}) = true +Tables.rowaccess(::Type{<:DBTables}) = true +Tables.rows(dbtbl::DBTables) = dbtbl + +""" + SQLite.tables(db, sink=columntable) + +returns a list of tables in `db` +""" +function tables(db::DB, sink=columntable) + tblnames = DBInterface.execute(sink, db, "SELECT name FROM sqlite_master WHERE type='table';") + return [DBTable(tbl, Tables.schema(DBInterface.execute(db,"SELECT * FROM $(tbl) LIMIT 0"))) for tbl in tblnames.name] +end + +""" + SQLite.indices(db, sink=columntable) + +returns a list of indices in `db` +""" +indices(db::DB, sink=columntable) = DBInterface.execute(sink, db, "SELECT name FROM sqlite_master WHERE type='index';") + +""" + SQLite.columns(db, table, sink=columntable) + +returns a list of columns in `table` +""" +columns(db::DB, table::AbstractString, sink=columntable) = DBInterface.execute(sink, db, "PRAGMA table_info($(esc_id(table)))") + +""" + SQLite.last_insert_rowid(db) + +returns the auto increment id of the last row +""" +last_insert_rowid(db::DB) = C.sqlite3_last_insert_rowid(db.handle) + +""" + SQLite.enable_load_extension(db, enable::Bool=true) + +Enables extension loading (off by default) on the sqlite database `db`. Pass `false` as the second argument to disable. +""" +enable_load_extension(db::DB, enable::Bool=true) = C.sqlite3_enable_load_extension(db.handle, enable) + +""" + SQLite.busy_timeout(db, ms::Integer=0) + +Set a busy handler that sleeps for a specified amount of milliseconds when a table is locked. After at least ms milliseconds of sleeping, the handler will return 0, causing sqlite to return SQLITE_BUSY. +""" +busy_timeout(db::DB, ms::Integer=0) = C.sqlite3_busy_timeout(db.handle, ms) diff --git a/src/index.jl b/src/index.jl new file mode 100644 index 0000000..0b7679b --- /dev/null +++ b/src/index.jl @@ -0,0 +1,31 @@ +""" + SQLite.dropindex!(db, index; ifexists::Bool=true) + +drop the SQLite index `index` from the database `db`; `ifexists=true` will not return an error if `index` doesn't exist +""" +function dropindex!(db::DB, index::AbstractString; ifexists::Bool=false) + exists = ifexists ? "IF EXISTS" : "" + transaction(db) do + direct_execute(db, "DROP INDEX $exists $(esc_id(index))") + end + return +end + +""" + SQLite.createindex!(db, table, index, cols; unique=true, ifnotexists=false) + +create the SQLite index `index` on the table `table` using `cols`, +which may be a single column or vector of columns. +`unique` specifies whether the index will be unique or not. +`ifnotexists=true` will not throw an error if the index already exists +""" +function createindex!(db::DB, table::AbstractString, index::AbstractString, cols::Union{S, AbstractVector{S}}; + unique::Bool=true, ifnotexists::Bool=false) where {S <: AbstractString} + u = unique ? "UNIQUE" : "" + exists = ifnotexists ? "IF NOT EXISTS" : "" + transaction(db) do + direct_execute(db, "CREATE $u INDEX $exists $(esc_id(index)) ON $(esc_id(table)) ($(esc_id(cols)))") + end + direct_execute(db, "ANALYZE $index") + return +end diff --git a/src/query.jl b/src/query.jl new file mode 100644 index 0000000..5089deb --- /dev/null +++ b/src/query.jl @@ -0,0 +1,196 @@ +""" + SQLite.direct_execute(db::SQLite.DB, sql::AbstractString, [params]) -> Int + SQLite.direct_execute(stmt::SQLite.Stmt, [params]) -> Int + +An internal method that executes the SQL statement (provided either as a `db` connection and `sql` command, +or as an already prepared `stmt` (see [`SQLite.Stmt`](@ref))) with given `params` parameters +(either positional (`Vector` or `Tuple`), named (`Dict` or `NamedTuple`), or specified as keyword arguments). + +Returns the SQLite status code of operation. + +*Note*: this is a low-level method that just executes the SQL statement, +but does not retrieve any data from `db`. +To get the results of a SQL query, it is recommended to use [`DBInterface.execute`](@ref). +""" +function direct_execute end + +function direct_execute(db::DB, stmt::Stmt, params::DBInterface.StatementParams=()) + handle = _get_stmt_handle(stmt) + C.sqlite3_reset(handle) + bind!(stmt, params) + r = C.sqlite3_step(handle) + if r == C.SQLITE_DONE + C.sqlite3_reset(handle) + elseif r != C.SQLITE_ROW + e = sqliteexception(db) + C.sqlite3_reset(handle) + throw(e) + end + return r +end + +direct_execute(stmt::Stmt, params::DBInterface.StatementParams=()) = direct_execute(stmt.db, stmt, params) + +direct_execute(stmt::Stmt; kwargs...) = direct_execute(stmt.db, stmt, NamedTuple(kwargs)) + +function direct_execute(db::DB, sql::AbstractString, params::DBInterface.StatementParams=()) + # prepare without registering Stmt in DB + stmt = Stmt(db, sql; register = false) + try + return direct_execute(db, stmt, params) + finally + _close_stmt!(stmt) # immediately close, don't wait for GC + end +end + +direct_execute(db::DB, sql::AbstractString; kwargs...) = direct_execute(db, sql, NamedTuple(kwargs)) + +""" +APIs to acquire data from the database. +""" + +sym(ptr) = ccall(:jl_symbol, Ref{Symbol}, (Ptr{UInt8},), ptr) + +struct Query + stmt::Stmt + status::Base.RefValue{Cint} + names::Vector{Symbol} + types::Vector{Type} + lookup::Dict{Symbol,Int} + current_rownumber::Base.RefValue{Int} +end + +# check if the query has no (more) rows +Base.isempty(q::Query) = q.status[] == C.SQLITE_DONE + +struct Row <: Tables.AbstractRow + q::Query + rownumber::Int +end + +getquery(r::Row) = getfield(r, :q) + +Tables.isrowtable(::Type{Query}) = true +Tables.columnnames(q::Query) = q.names + +function Tables.schema(q::Query) + if isempty(q) + # when the query is empty, return the types provided by SQLite + # by default SQLite.jl assumes all columns can have missing values + Tables.Schema(Tables.columnnames(q), q.types) + else + return nothing # fallback to the actual column types of the result + end +end + +Base.IteratorSize(::Type{Query}) = Base.SizeUnknown() +Base.eltype(::Query) = Row + +function reset!(q::Query) + C.sqlite3_reset(_get_stmt_handle(q.stmt)) + q.status[] = direct_execute(q.stmt) + return +end + +function DBInterface.close!(q::Query) + C.sqlite3_reset(_get_stmt_handle(q.stmt)) +end + +function done(q::Query) + st = q.status[] + if st == C.SQLITE_DONE + C.sqlite3_reset(_get_stmt_handle(q.stmt)) + return true + end + st == C.SQLITE_ROW || sqliteerror(q.stmt.db) + return false +end + +@noinline wrongrow(i) = throw( + ArgumentError( + "row $i is no longer valid; sqlite query results are forward-only iterators where each row is only valid when iterated; re-execute the query, convert rows to NamedTuples, or stream the results to a sink to save results", + ), +) + +function getvalue(q::Query, col::Int, rownumber::Int, ::Type{T}) where {T} + rownumber == q.current_rownumber[] || wrongrow(rownumber) + handle = _get_stmt_handle(q.stmt) + t = C.sqlite3_column_type(handle, col-1) + if t == C.SQLITE_NULL + return missing + else + TT = juliatype(t) # native SQLite Int, Float, and Text types + return sqlitevalue(ifelse(TT === Any && !isbitstype(T), T, TT), handle, col) + end +end + +Tables.getcolumn(r::Row, ::Type{T}, i::Int, nm::Symbol) where {T} = + getvalue(getquery(r), i, getfield(r, :rownumber), T) + +Tables.getcolumn(r::Row, i::Int) = + Tables.getcolumn(r, getquery(r).types[i], i, getquery(r).names[i]) +Tables.getcolumn(r::Row, nm::Symbol) = Tables.getcolumn(r, getquery(r).lookup[nm]) +Tables.columnnames(r::Row) = Tables.columnnames(getquery(r)) + +function Base.iterate(q::Query) + done(q) && return nothing + q.current_rownumber[] = 1 + return Row(q, 1), 2 +end + +function Base.iterate(q::Query, rownumber) + q.status[] = C.sqlite3_step(_get_stmt_handle(q.stmt)) + done(q) && return nothing + q.current_rownumber[] = rownumber + return Row(q, rownumber), rownumber + 1 +end + +"Return the last row insert id from the executed statement" +DBInterface.lastrowid(q::Query) = C.sqlite3_last_insert_rowid(q.stmt.db.handle) + +""" + DBInterface.execute(db::SQLite.DB, sql::String, [params]) + DBInterface.execute(stmt::SQLite.Stmt, [params]) + +Bind any positional (`params` as `Vector` or `Tuple`) or named (`params` as `NamedTuple` or `Dict`) parameters to an SQL statement, given by `db` and `sql` or +as an already prepared statement `stmt`, execute the query and return an iterator of result rows. + +Note that the returned result row iterator only supports a single-pass, forward-only iteration of the result rows. +Calling `SQLite.reset!(result)` will re-execute the query and reset the iterator back to the beginning. + +The resultset iterator supports the [Tables.jl](https://github.com/JuliaData/Tables.jl) interface, so results can be collected in any Tables.jl-compatible sink, +like `DataFrame(results)`, `CSV.write("results.csv", results)`, etc. +""" +function DBInterface.execute( + stmt::Stmt, + params::DBInterface.StatementParams; + allowduplicates::Bool = false, +) + status = direct_execute(stmt, params) + handle = _get_stmt_handle(stmt) + cols = C.sqlite3_column_count(handle) + header = Vector{Symbol}(undef, cols) + types = Vector{Type}(undef, cols) + for i = 1:cols + nm = sym(C.sqlite3_column_name(handle, i-1)) + if !allowduplicates && nm in view(header, 1:(i-1)) + j = 1 + newnm = Symbol(nm, :_, j) + while newnm in view(header, 1:(i-1)) + j += 1 + newnm = Symbol(nm, :_, j) + end + nm = newnm + end + header[i] = nm + types[i] = Union{juliatype(handle, i),Missing} + end + return Query( + stmt, + Ref(status), + header, + types, + Dict(x => i for (i, x) in enumerate(header)), + Ref(0), + ) +end diff --git a/src/table.jl b/src/table.jl new file mode 100644 index 0000000..0b57302 --- /dev/null +++ b/src/table.jl @@ -0,0 +1,221 @@ +""" + SQLite.esc_id(x::Union{AbstractString,Vector{AbstractString}}) + +Escape SQLite identifiers +(e.g. column, table or index names). +Can be either a string or a vector of strings +(note does not check for null characters). +A vector of identifiers will be separated by commas. + +Example: + +```julia +julia> using SQLite, DataFrames + +julia> df = DataFrame(label=string.(rand("abcdefg", 10)), value=rand(10)); + +julia> db = SQLite.DB(mktemp()[1]); + +julia> tbl |> SQLite.load!(db, "temp"); + +julia> DBInterface.execute(db,"SELECT * FROM temp WHERE label IN ('a','b','c')") |> DataFrame +4×2 DataFrame +│ Row │ label │ value │ +│ │ String⍰ │ Float64⍰ │ +├─────┼─────────┼──────────┤ +│ 1 │ c │ 0.603739 │ +│ 2 │ c │ 0.429831 │ +│ 3 │ b │ 0.799696 │ +│ 4 │ a │ 0.603586 │ + +julia> q = ['a','b','c']; + +julia> DBInterface.execute(db,"SELECT * FROM temp WHERE label IN (\$(SQLite.esc_id(q)))") |> DataFrame +4×2 DataFrame +│ Row │ label │ value │ +│ │ String⍰ │ Float64⍰ │ +├─────┼─────────┼──────────┤ +│ 1 │ c │ 0.603739 │ +│ 2 │ c │ 0.429831 │ +│ 3 │ b │ 0.799696 │ +│ 4 │ a │ 0.603586 │ +``` +""" +function esc_id end + +esc_id(x::AbstractString) = "\"" * replace(x, "\"" => "\"\"") * "\"" +esc_id(X::AbstractVector{S}) where {S<:AbstractString} = join(map(esc_id, X), ',') + +""" + SQLite.drop!(db, table; ifexists::Bool=true) + +drop the SQLite table `table` from the database `db`; `ifexists=true` will prevent an error being thrown if `table` doesn't exist +""" +function drop!(db::DB, table::AbstractString; ifexists::Bool=false) + exists = ifexists ? "IF EXISTS" : "" + transaction(db) do + direct_execute(db, "DROP TABLE $exists $(esc_id(table))") + end + direct_execute(db, "VACUUM") + return +end + +""" + SQLite.removeduplicates!(db, table, cols) + +Removes duplicate rows from `table` based on the values in `cols`, which is an array of column names. + +A convenience method for the common task of removing duplicate +rows in a dataset according to some subset of columns that make up a "primary key". +""" +function removeduplicates!(db::DB, table::AbstractString, cols::AbstractArray{T}) where {T<:AbstractString} + colsstr = "" + for c in cols + colsstr = colsstr * esc_id(c) * "," + end + colsstr = chop(colsstr) + transaction(db) do + direct_execute(db, "DELETE FROM $(esc_id(table)) WHERE _ROWID_ NOT IN (SELECT max(_ROWID_) from $(esc_id(table)) GROUP BY $(colsstr));") + end + direct_execute(db, "ANALYZE $table") + return +end + +""" + SQLite.createtable!(db::SQLite.DB, table_name, schema::Tables.Schema; temp=false, ifnotexists=true) + +Create a table in `db` with name `table_name`, according to `schema`, which is a set of column names and types, constructed like `Tables.Schema(names, types)` +where `names` can be a vector or tuple of String/Symbol column names, and `types` is a vector or tuple of sqlite-compatible types (`Int`, `Float64`, `String`, or unions of `Missing`). + +If `temp=true`, the table will be created temporarily, which means it will be deleted when the `db` is closed. +If `ifnotexists=true`, no error will be thrown if the table already exists. +""" +function createtable!(db::DB, name::AbstractString, ::Tables.Schema{names,types}; + temp::Bool=false, ifnotexists::Bool=true) where {names,types} + temp = temp ? "TEMP" : "" + ifnotexists = ifnotexists ? "IF NOT EXISTS" : "" + columns = [string(esc_id(String(names[i])), ' ', + sqlitetype(types !== nothing ? fieldtype(types, i) : Any)) + for i in eachindex(names)] + sql = "CREATE $temp TABLE $ifnotexists $(esc_id(string(name))) ($(join(columns, ',')))" + return direct_execute(db, sql) +end + +# table info for load!(): +# returns NamedTuple with columns information, +# or nothing if table does not exist +tableinfo(db::DB, name::AbstractString) = + DBInterface.execute(db, "pragma table_info($(esc_id(name)))") do query + st = query.status[] + if st == C.SQLITE_ROW + return Tables.columntable(query) + elseif st == C.SQLITE_DONE + return nothing + else + sqliteerror(query.stmt.db) + end + end + +""" + source |> SQLite.load!(db::SQLite.DB, tablename::String; temp::Bool=false, ifnotexists::Bool=false, replace::Bool=false, analyze::Bool=false) + SQLite.load!(source, db, tablename; temp=false, ifnotexists=false, replace::Bool=false, analyze::Bool=false) + +Load a Tables.jl input `source` into an SQLite table that will be named `tablename` (will be auto-generated if not specified). + + * `temp=true` will create a temporary SQLite table that will be destroyed automatically when the database is closed + * `ifnotexists=false` will throw an error if `tablename` already exists in `db` + * `replace=false` controls whether an `INSERT INTO ...` statement is generated or a `REPLACE INTO ...` + * `analyze=true` will execute `ANALYZE` at the end of the insert +""" +function load! end + +load!(db::DB, name::AbstractString="sqlitejl_" * Random.randstring(5); kwargs...) = + x -> load!(x, db, name; kwargs...) + +function load!(itr, db::DB, name::AbstractString="sqlitejl_" * Random.randstring(5); kwargs...) + # check if table exists + db_tableinfo = tableinfo(db, name) + rows = Tables.rows(itr) + sch = Tables.schema(rows) + return load!(sch, rows, db, name, db_tableinfo; kwargs...) +end + +# case-insensitive check for duplicate column names +function checkdupnames(names::Union{AbstractVector,Tuple}) + checkednames = Set{String}() + for name in names + lcname = lowercase(string(name)) + if lcname in checkednames + throw(SQLiteException("Duplicate case-insensitive column name $lcname detected. SQLite doesn't allow duplicate column names and treats them case insensitive")) + end + push!(checkednames, lcname) + end + return true +end + +# check if schema names match column names in DB +function checknames(::Tables.Schema{names}, db_names::AbstractVector{String}) where {names} + table_names = Set(string.(names)) + db_names = Set(db_names) + + if table_names != db_names + throw(SQLiteException("Error loading, column names from table $(collect(table_names)) do not match database names $(collect(db_names))")) + end + return true +end + +function load!(sch::Tables.Schema, rows, db::DB, name::AbstractString, db_tableinfo::Union{NamedTuple,Nothing}, row=nothing, st=nothing; + temp::Bool=false, ifnotexists::Bool=false, replace::Bool=false, analyze::Bool=false) + # check for case-insensitive duplicate column names (sqlite doesn't allow) + checkdupnames(sch.names) + # check if `rows` column names match the existing table, or create the new one + if db_tableinfo !== nothing + checknames(sch, db_tableinfo.name) + else + createtable!(db, name, sch; temp=temp, ifnotexists=ifnotexists) + end + # build insert statement + columns = join(esc_id.(string.(sch.names)), ",") + params = chop(repeat("?,", length(sch.names))) + kind = replace ? "REPLACE" : "INSERT" + stmt = Stmt(db, "$kind INTO $(esc_id(string(name))) ($columns) VALUES ($params)"; register=false) + handle = _get_stmt_handle(stmt) + # start a transaction for inserting rows + DBInterface.transaction(db) do + if row === nothing + state = iterate(rows) + state === nothing && return + row, st = state + end + while true + Tables.eachcolumn(sch, row) do val, col, _ + bind!(stmt, col, val) + end + r = C.sqlite3_step(handle) + if r == C.SQLITE_DONE + C.sqlite3_reset(handle) + elseif r != C.SQLITE_ROW + e = sqliteexception(db, stmt) + C.sqlite3_reset(handle) + throw(e) + end + state = iterate(rows, st) + state === nothing && break + row, st = state + end + end + _close_stmt!(stmt) + analyze && direct_execute(db, "ANALYZE $name") + return name +end + +# unknown schema case +function load!(::Nothing, rows, db::DB, name::AbstractString, + db_tableinfo::Union{NamedTuple,Nothing}; kwargs...) + state = iterate(rows) + state === nothing && return name + row, st = state + names = propertynames(row) + sch = Tables.Schema(names, nothing) + return load!(sch, rows, db, name, db_tableinfo, row, st; kwargs...) +end diff --git a/src/tables.jl b/src/tables.jl deleted file mode 100644 index 95ff746..0000000 --- a/src/tables.jl +++ /dev/null @@ -1,291 +0,0 @@ -using Tables - -sym(ptr) = ccall(:jl_symbol, Ref{Symbol}, (Ptr{UInt8},), ptr) - -struct Query - stmt::Stmt - status::Base.RefValue{Cint} - names::Vector{Symbol} - types::Vector{Type} - lookup::Dict{Symbol, Int} - current_rownumber::Base.RefValue{Int} -end - -# check if the query has no (more) rows -Base.isempty(q::Query) = q.status[] == SQLITE_DONE - -struct Row <: Tables.AbstractRow - q::Query - rownumber::Int -end - -getquery(r::Row) = getfield(r, :q) - -Tables.isrowtable(::Type{Query}) = true -Tables.columnnames(q::Query) = q.names - -struct DBTable - name::String - schema::Union{Tables.Schema, Nothing} -end - -DBTable(name::String) = DBTable(name, nothing) - -const DBTables = AbstractVector{DBTable} - -Tables.istable(::Type{<:DBTables}) = true -Tables.rowaccess(::Type{<:DBTables}) = true -Tables.rows(dbtbl::DBTables) = dbtbl - -function Tables.schema(q::Query) - if isempty(q) - # when the query is empty, return the types provided by SQLite - # by default SQLite.jl assumes all columns can have missing values - Tables.Schema(Tables.columnnames(q), q.types) - else - return nothing # fallback to the actual column types of the result - end -end - -Base.IteratorSize(::Type{Query}) = Base.SizeUnknown() -Base.eltype(q::Query) = Row - -function reset!(q::Query) - sqlite3_reset(_stmt(q.stmt).handle) - q.status[] = execute(q.stmt) - return -end - -function DBInterface.close!(q::Query) - _st = _stmt_safe(q.stmt) - (_st !== nothing) && sqlite3_reset(_st.handle) -end - -function done(q::Query) - st = q.status[] - if st == SQLITE_DONE - sqlite3_reset(_stmt(q.stmt).handle) - return true - end - st == SQLITE_ROW || sqliteerror(q.stmt.db) - return false -end - -@noinline wrongrow(i) = throw(ArgumentError("row $i is no longer valid; sqlite query results are forward-only iterators where each row is only valid when iterated; re-execute the query, convert rows to NamedTuples, or stream the results to a sink to save results")) - -function getvalue(q::Query, col::Int, rownumber::Int, ::Type{T}) where {T} - rownumber == q.current_rownumber[] || wrongrow(rownumber) - handle = _stmt(q.stmt).handle - t = sqlite3_column_type(handle, col) - if t == SQLITE_NULL - return missing - else - TT = juliatype(t) # native SQLite Int, Float, and Text types - return sqlitevalue(ifelse(TT === Any && !isbitstype(T), T, TT), handle, col) - end -end - -Tables.getcolumn(r::Row, ::Type{T}, i::Int, nm::Symbol) where {T} = getvalue(getquery(r), i, getfield(r, :rownumber), T) - -Tables.getcolumn(r::Row, i::Int) = Tables.getcolumn(r, getquery(r).types[i], i, getquery(r).names[i]) -Tables.getcolumn(r::Row, nm::Symbol) = Tables.getcolumn(r, getquery(r).lookup[nm]) -Tables.columnnames(r::Row) = Tables.columnnames(getquery(r)) - -function Base.iterate(q::Query) - done(q) && return nothing - q.current_rownumber[] = 1 - return Row(q, 1), 2 -end - -function Base.iterate(q::Query, rownumber) - q.status[] = sqlite3_step(_stmt(q.stmt).handle) - done(q) && return nothing - q.current_rownumber[] = rownumber - return Row(q, rownumber), rownumber + 1 -end - -"Return the last row insert id from the executed statement" -DBInterface.lastrowid(q::Query) = last_insert_rowid(q.stmt.db) - -""" - DBInterface.prepare(db::SQLite.DB, sql::AbstractString) - -Prepare an SQL statement given as a string in the sqlite database; returns an `SQLite.Stmt` compiled object. -See `DBInterface.execute`(@ref) for information on executing a prepared statement and passing parameters to bind. -A `SQLite.Stmt` object can be closed (resources freed) using `DBInterface.close!`(@ref). -""" -DBInterface.prepare(db::DB, sql::AbstractString) = Stmt(db, sql) - -""" - DBInterface.execute(db::SQLite.DB, sql::String, [params]) - DBInterface.execute(stmt::SQLite.Stmt, [params]) - -Bind any positional (`params` as `Vector` or `Tuple`) or named (`params` as `NamedTuple` or `Dict`) parameters to an SQL statement, given by `db` and `sql` or -as an already prepared statement `stmt`, execute the query and return an iterator of result rows. - -Note that the returned result row iterator only supports a single-pass, forward-only iteration of the result rows. -Calling `SQLite.reset!(result)` will re-execute the query and reset the iterator back to the beginning. - -The resultset iterator supports the [Tables.jl](https://github.com/JuliaData/Tables.jl) interface, so results can be collected in any Tables.jl-compatible sink, -like `DataFrame(results)`, `CSV.write("results.csv", results)`, etc. -""" -function DBInterface.execute(stmt::Stmt, params::DBInterface.StatementParams; allowduplicates::Bool=false) - status = execute(stmt, params) - _st = _stmt(stmt) - cols = sqlite3_column_count(_st.handle) - header = Vector{Symbol}(undef, cols) - types = Vector{Type}(undef, cols) - for i = 1:cols - nm = sym(sqlite3_column_name(_st.handle, i)) - if !allowduplicates && nm in view(header, 1:(i - 1)) - j = 1 - newnm = Symbol(nm, :_, j) - while newnm in view(header, 1:(i - 1)) - j += 1 - newnm = Symbol(nm, :_, j) - end - nm = newnm - end - header[i] = nm - types[i] = Union{juliatype(_st.handle, i), Missing} - end - return Query(stmt, Ref(status), header, types, Dict(x=>i for (i, x) in enumerate(header)), Ref(0)) -end - -""" - SQLite.createtable!(db::SQLite.DB, table_name, schema::Tables.Schema; temp=false, ifnotexists=true) - -Create a table in `db` with name `table_name`, according to `schema`, which is a set of column names and types, constructed like `Tables.Schema(names, types)` -where `names` can be a vector or tuple of String/Symbol column names, and `types` is a vector or tuple of sqlite-compatible types (`Int`, `Float64`, `String`, or unions of `Missing`). - -If `temp=true`, the table will be created temporarily, which means it will be deleted when the `db` is closed. -If `ifnotexists=true`, no error will be thrown if the table already exists. -""" -function createtable!(db::DB, name::AbstractString, ::Tables.Schema{names, types}; - temp::Bool=false, ifnotexists::Bool=true) where {names, types} - temp = temp ? "TEMP" : "" - ifnotexists = ifnotexists ? "IF NOT EXISTS" : "" - columns = [string(esc_id(String(names[i])), ' ', - sqlitetype(types !== nothing ? fieldtype(types, i) : Any)) - for i in eachindex(names)] - sql = "CREATE $temp TABLE $ifnotexists $(esc_id(string(name))) ($(join(columns, ',')))" - return execute(db, sql) -end - -# table info for load!(): -# returns NamedTuple with columns information, -# or nothing if table does not exist -tableinfo(db::DB, name::AbstractString) = - DBInterface.execute(db, "pragma table_info($(esc_id(name)))") do qry - st = qry.status[] - if st == SQLITE_ROW - return Tables.columntable(qry) - elseif st == SQLITE_DONE - return nothing - else - sqliteerror(q.stmt.db) - end - end - -""" - source |> SQLite.load!(db::SQLite.DB, tablename::String; temp::Bool=false, ifnotexists::Bool=false, replace::Bool=false, analyze::Bool=false) - SQLite.load!(source, db, tablename; temp=false, ifnotexists=false, replace::Bool=false, analyze::Bool=false) - -Load a Tables.jl input `source` into an SQLite table that will be named `tablename` (will be auto-generated if not specified). - - * `temp=true` will create a temporary SQLite table that will be destroyed automatically when the database is closed - * `ifnotexists=false` will throw an error if `tablename` already exists in `db` - * `replace=false` controls whether an `INSERT INTO ...` statement is generated or a `REPLACE INTO ...` - * `analyze=true` will execute `ANALYZE` at the end of the insert -""" -function load! end - -load!(db::DB, name::AbstractString="sqlitejl_"*Random.randstring(5); kwargs...) = - x -> load!(x, db, name; kwargs...) - -function load!(itr, db::DB, name::AbstractString="sqlitejl_"*Random.randstring(5); kwargs...) - # check if table exists - db_tableinfo = tableinfo(db, name) - rows = Tables.rows(itr) - sch = Tables.schema(rows) - return load!(sch, rows, db, name, db_tableinfo; kwargs...) -end - -# case-insensitive check for duplicate column names -function checkdupnames(names::Union{AbstractVector, Tuple}) - checkednames = Set{String}() - for name in names - lcname = lowercase(string(name)) - if lcname in checkednames - throw(SQLiteException("Duplicate case-insensitive column name $lcname detected. SQLite doesn't allow duplicate column names and treats them case insensitive")) - end - push!(checkednames, lcname) - end - return true -end - -# check if schema names match column names in DB -function checknames(::Tables.Schema{names}, db_names::AbstractVector{String}) where {names} - table_names = Set(string.(names)) - db_names = Set(db_names) - - if table_names != db_names - throw(SQLiteException("Error loading, column names from table $(collect(table_names)) do not match database names $(collect(db_names))")) - end - return true -end - -function load!(sch::Tables.Schema, rows, db::DB, name::AbstractString, db_tableinfo::Union{NamedTuple, Nothing}, row=nothing, st=nothing; - temp::Bool=false, ifnotexists::Bool=false, replace::Bool=false, analyze::Bool=false) - # check for case-insensitive duplicate column names (sqlite doesn't allow) - checkdupnames(sch.names) - # check if `rows` column names match the existing table, or create the new one - if db_tableinfo !== nothing - checknames(sch, db_tableinfo.name) - else - createtable!(db, name, sch; temp=temp, ifnotexists=ifnotexists) - end - # build insert statement - columns = join(esc_id.(string.(sch.names)), ",") - params = chop(repeat("?,", length(sch.names))) - kind = replace ? "REPLACE" : "INSERT" - stmt = _Stmt(db, "$kind INTO $(esc_id(string(name))) ($columns) VALUES ($params)") - # start a transaction for inserting rows - DBInterface.transaction(db) do - if row === nothing - state = iterate(rows) - state === nothing && return - row, st = state - end - while true - Tables.eachcolumn(sch, row) do val, col, _ - bind!(stmt, col, val) - end - r = sqlite3_step(stmt.handle) - if r == SQLITE_DONE - sqlite3_reset(stmt.handle) - elseif r != SQLITE_ROW - e = sqliteexception(db, stmt) - sqlite3_reset(stmt.handle) - throw(e) - end - state = iterate(rows, st) - state === nothing && break - row, st = state - end - end - _close!(stmt) - analyze && execute(db, "ANALYZE $name") - return name -end - -# unknown schema case -function load!(::Nothing, rows, db::DB, name::AbstractString, - db_tableinfo::Union{NamedTuple, Nothing}; kwargs...) - state = iterate(rows) - state === nothing && return name - row, st = state - names = propertynames(row) - sch = Tables.Schema(names, nothing) - return load!(sch, rows, db, name, db_tableinfo, row, st; kwargs...) -end diff --git a/src/transaction.jl b/src/transaction.jl new file mode 100644 index 0000000..bae82a3 --- /dev/null +++ b/src/transaction.jl @@ -0,0 +1,64 @@ +# Transaction-based commands +""" + SQLite.transaction(db, mode="DEFERRED") + SQLite.transaction(func, db) + +Begin a transaction in the specified `mode`, default = "DEFERRED". + +If `mode` is one of "", "DEFERRED", "IMMEDIATE" or "EXCLUSIVE" then a +transaction of that (or the default) mutable struct is started. Otherwise a savepoint +is created whose name is `mode` converted to AbstractString. + +In the second method, `func` is executed within a transaction (the transaction being committed upon successful execution) +""" +function transaction end + +function transaction(db::DB, mode="DEFERRED") + direct_execute(db, "PRAGMA temp_store=MEMORY;") + if uppercase(mode) in ["", "DEFERRED", "IMMEDIATE", "EXCLUSIVE"] + direct_execute(db, "BEGIN $(mode) TRANSACTION;") + else + direct_execute(db, "SAVEPOINT $(mode);") + end +end + +DBInterface.transaction(f, db::DB) = transaction(f, db) + +@inline function transaction(f::Function, db::DB) + # generate a random name for the savepoint + name = string("SQLITE", Random.randstring(10)) + direct_execute(db, "PRAGMA synchronous = OFF;") + transaction(db, name) + try + f() + catch + rollback(db, name) + rethrow() + finally + # savepoints are not released on rollback + commit(db, name) + direct_execute(db, "PRAGMA synchronous = ON;") + end +end + +""" + SQLite.commit(db) + SQLite.commit(db, name) + +commit a transaction or named savepoint +""" +function commit end + +commit(db::DB) = direct_execute(db, "COMMIT TRANSACTION;") +commit(db::DB, name::AbstractString) = direct_execute(db, "RELEASE SAVEPOINT $(name);") + +""" + SQLite.rollback(db) + SQLite.rollback(db, name) + +rollback transaction or named savepoint +""" +function rollback end + +rollback(db::DB) = direct_execute(db, "ROLLBACK TRANSACTION;") +rollback(db::DB, name::AbstractString) = direct_execute(db, "ROLLBACK TRANSACTION TO SAVEPOINT $(name);") diff --git a/src/type_conversion.jl b/src/type_conversion.jl new file mode 100644 index 0000000..b90508d --- /dev/null +++ b/src/type_conversion.jl @@ -0,0 +1,89 @@ +# get julia type for given column of the given statement +function juliatype(handle, col) + stored_typeid = C.sqlite3_column_type(handle, col-1) + if stored_typeid == C.SQLITE_BLOB + # blobs are serialized julia types, so just try to deserialize it + deser_val = sqlitevalue(Any, handle, col) + # FIXME deserialized type have priority over declared type, is it fine? + return typeof(deser_val) + else + stored_type = juliatype(stored_typeid) + end + decl_typestr = C.sqlite3_column_decltype(handle, col-1) + if decl_typestr != C_NULL + return juliatype(unsafe_string(decl_typestr), stored_type) + else + return stored_type + end +end + +# convert SQLite stored type into Julia equivalent +juliatype(x::Integer) = + x == C.SQLITE_INTEGER ? Int64 : + x == C.SQLITE_FLOAT ? Float64 : x == C.SQLITE_TEXT ? String : x == C.SQLITE_NULL ? Missing : Any + +# convert SQLite declared type into Julia equivalent, +# fall back to default (stored type), if no good match +function juliatype(decl_typestr::AbstractString, default::Type = Any) + typeuc = uppercase(decl_typestr) + # try to match the type affinities described in the "Affinity Name Examples" section + # of https://www.sqlite.org/datatype3.html + if typeuc in ( + "INTEGER", + "INT", + "TINYINT", + "SMALLINT", + "MEDIUMINT", + "BIGINT", + "UNSIGNED BIG INT", + "INT2", + "INT8", + ) + return Int64 + elseif typeuc in ("NUMERIC", "REAL", "FLOAT", "DOUBLE", "DOUBLE PRECISION") + return Float64 + elseif typeuc == "TEXT" + return String + elseif typeuc == "BLOB" + return Any + elseif typeuc == "DATETIME" + return default # FIXME + elseif typeuc == "TIMESTAMP" + return default # FIXME + elseif occursin(r"^N?V?A?R?Y?I?N?G?\s*CHARA?C?T?E?R?T?E?X?T?\s*\(?\d*\)?$"i, typeuc) + return String + elseif occursin(r"^NUMERIC\(\d+,\d+\)$", typeuc) + return Float64 + else + return default + end +end + +sqlitevalue(::Type{T}, handle, col) where {T<:Union{Base.BitSigned,Base.BitUnsigned}} = + convert(T, C.sqlite3_column_int64(handle, col-1)) +const FLOAT_TYPES = Union{Float16,Float32,Float64} # exclude BigFloat +sqlitevalue(::Type{T}, handle, col) where {T<:FLOAT_TYPES} = + convert(T, C.sqlite3_column_double(handle, col-1)) +#TODO: test returning a WeakRefString instead of calling `unsafe_string` +sqlitevalue(::Type{T}, handle, col) where {T<:AbstractString} = + convert(T, unsafe_string(C.sqlite3_column_text(handle, col-1))) +function sqlitevalue(::Type{T}, handle, col) where {T} + blob = convert(Ptr{UInt8}, C.sqlite3_column_blob(handle, col-1)) + b = C.sqlite3_column_bytes(handle, col-1) + buf = zeros(UInt8, b) # global const? + unsafe_copyto!(pointer(buf), blob, b) + r = sqldeserialize(buf) + return r +end + +# conversion from Julia to SQLite3 types +sqlitetype_(::Type{<:Integer}) = "INT" +sqlitetype_(::Type{<:AbstractFloat}) = "REAL" +sqlitetype_(::Type{<:AbstractString}) = "TEXT" +sqlitetype_(::Type{Bool}) = "INT" +sqlitetype_(::Type) = "BLOB" # fallback + +sqlitetype(::Type{Missing}) = "NULL" +sqlitetype(::Type{Nothing}) = "NULL" +sqlitetype(::Type{Union{T,Missing}}) where {T} = sqlitetype_(T) +sqlitetype(::Type{T}) where {T} = string(sqlitetype_(T), " NOT NULL") diff --git a/test/Project.toml b/test/Project.toml index eee9b17..289123e 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,7 +2,6 @@ DBInterface = "a10d1c49-ce27-4219-8d33-6db1a4562965" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -SQLite = "0aa819cd-b072-5ff4-a722-6bc24af294d9" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" WeakRefStrings = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" diff --git a/test/runtests.jl b/test/runtests.jl index 091f8f4..5b35e92 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -42,7 +42,7 @@ Copy test sqlite file to temp directory, path `test.sqlite`, overwrite file if necessary and set permissions. """ function setup_clean_test_db(f::Function, args...) - dbfile = joinpath(dirname(pathof(SQLite)), "../test/Chinook_Sqlite.sqlite") + dbfile = joinpath(@__DIR__, "Chinook_Sqlite.sqlite") tmp_dir = mktempdir() test_dbfile = joinpath(tmp_dir, "test.sqlite") @@ -565,32 +565,29 @@ end DBInterface.close!(db) end - - - - @testset "SQLite.execute()" begin + @testset "SQLite.direct_execute()" begin db = SQLite.DB() DBInterface.execute(db, "CREATE TABLE T (x INT UNIQUE)") q = DBInterface.prepare(db, "INSERT INTO T VALUES(?)") - SQLite.execute(q, (1,)) + SQLite.direct_execute(q, (1,)) r = DBInterface.execute(db, "SELECT * FROM T") |> columntable @test r[1] == [1] - SQLite.execute(q, [2]) + SQLite.direct_execute(q, [2]) r = DBInterface.execute(db, "SELECT * FROM T") |> columntable @test r[1] == [1, 2] q = DBInterface.prepare(db, "INSERT INTO T VALUES(:x)") - SQLite.execute(q, Dict(:x => 3)) + SQLite.direct_execute(q, Dict(:x => 3)) r = DBInterface.execute(columntable, db, "SELECT * FROM T") @test r[1] == [1, 2, 3] - SQLite.execute(q, x = 4) + SQLite.direct_execute(q, x = 4) r = DBInterface.execute(columntable, db, "SELECT * FROM T") @test r[1] == [1, 2, 3, 4] - SQLite.execute(db, "INSERT INTO T VALUES(:x)", x = 5) + SQLite.direct_execute(db, "INSERT INTO T VALUES(:x)", x = 5) r = DBInterface.execute(columntable, db, "SELECT * FROM T") @test r[1] == [1, 2, 3, 4, 5] @@ -631,8 +628,6 @@ end @test SQLite.esc_id(["1", "2", "3"]) == "\"1\",\"2\",\"3\"" end - - @testset "Issue #193: Throw informative error on duplicate column names" begin db = SQLite.DB() @test_throws SQLiteException SQLite.load!((a = [1, 2, 3], A = [1, 2, 3]), db) @@ -661,7 +656,6 @@ end @test_throws SQLiteException SQLite.load!(tbl3, db, "data") end - @testset "Test busy_timeout" begin db = SQLite.DB() @test SQLite.busy_timeout(db, 300) == 0 @@ -720,12 +714,10 @@ end @testset "explicit finalization by finalize_statements!(db)" begin SQLite.load!(tbl, db, "test_table") stmt = SQLite.Stmt(db, "SELECT a, b FROM test_table") - @test SQLite.isready(stmt) - @test SQLite.execute(stmt) == 100 + @test SQLite.direct_execute(stmt) == 100 # test cannot drop the table locked by the statement @test_throws SQLiteException SQLite.drop!(db, "test_table") SQLite.finalize_statements!(db) - @test !SQLite.isready(stmt) SQLite.drop!(db, "test_table") DBInterface.close!(stmt) # test can call close!() 2nd time end @@ -733,27 +725,25 @@ end @testset "explicit finalization by close!(stmt)" begin SQLite.load!(tbl, db, "test_table2") stmt = SQLite.Stmt(db, "SELECT a, b FROM test_table2") - @test SQLite.isready(stmt) - @test SQLite.execute(stmt) == 100 + @test SQLite.direct_execute(stmt) == 100 # test cannot drop the table locked by the statement @test_throws SQLiteException SQLite.drop!(db, "test_table2") DBInterface.close!(stmt) - @test !SQLite.isready(stmt) SQLite.drop!(db, "test_table2") DBInterface.close!(stmt) # test can call close!() 2nd time end @testset "automatic close of implicit prepared statement" begin - @testset "SQLite.execute() call" begin + @testset "SQLite.direct_execute() call" begin SQLite.load!(tbl, db, "test_table3") - @test SQLite.execute(db, "SELECT a, b FROM test_table3") == 100 + @test SQLite.direct_execute(db, "SELECT a, b FROM test_table3") == 100 # test can immediately drop the table, since no locks by the statement SQLite.drop!(db, "test_table3") end @testset "DBInterface.execute() call" begin SQLite.load!(tbl, db, "test_table4") - @test SQLite.execute(db, "SELECT a, b FROM test_table4") == 100 + @test SQLite.direct_execute(db, "SELECT a, b FROM test_table4") == 100 GC.gc() # close implicitly created statement # test can immediately drop the table, since no locks by the GC-ed statement SQLite.drop!(db, "test_table4") @@ -764,7 +754,7 @@ end rm(dbfile) end -end # @testset +end struct UnknownSchemaTable end