diff options
Diffstat (limited to 'doc/reference')
-rw-r--r-- | doc/reference/allocator.adoc | 31 | ||||
-rw-r--r-- | doc/reference/backup.adoc | 43 | ||||
-rw-r--r-- | doc/reference/blob.adoc | 141 | ||||
-rw-r--r-- | doc/reference/collation.adoc | 45 | ||||
-rw-r--r-- | doc/reference/connection.adoc | 100 | ||||
-rw-r--r-- | doc/reference/cstring_ref.adoc | 16 | ||||
-rw-r--r-- | doc/reference/error.adoc | 91 | ||||
-rw-r--r-- | doc/reference/extension.adoc | 36 | ||||
-rw-r--r-- | doc/reference/field.adoc | 42 | ||||
-rw-r--r-- | doc/reference/function.adoc | 263 | ||||
-rw-r--r-- | doc/reference/hooks.adoc | 105 | ||||
-rw-r--r-- | doc/reference/json.adoc | 28 | ||||
-rw-r--r-- | doc/reference/memory.adoc | 29 | ||||
-rw-r--r-- | doc/reference/meta_data.adoc | 36 | ||||
-rw-r--r-- | doc/reference/mutex.adoc | 16 | ||||
-rw-r--r-- | doc/reference/result.adoc | 40 | ||||
-rw-r--r-- | doc/reference/resultset.adoc | 72 | ||||
-rw-r--r-- | doc/reference/row.adoc | 56 | ||||
-rw-r--r-- | doc/reference/statement.adoc | 165 | ||||
-rw-r--r-- | doc/reference/static_resultset.adoc | 98 | ||||
-rw-r--r-- | doc/reference/string.adoc | 18 | ||||
-rw-r--r-- | doc/reference/transaction.adoc | 104 | ||||
-rw-r--r-- | doc/reference/value.adoc | 85 | ||||
-rw-r--r-- | doc/reference/vtable.adoc | 275 |
24 files changed, 1935 insertions, 0 deletions
diff --git a/doc/reference/allocator.adoc b/doc/reference/allocator.adoc new file mode 100644 index 0000000..ab9a4e9 --- /dev/null +++ b/doc/reference/allocator.adoc @@ -0,0 +1,31 @@ +== `sqlite/allocator.hpp` +[#allocator] + +The sqlite allocator wraps sqlite's malloc & free functions in a similar way that std::allocator wraps `new`/`delete`. + +This can be used for sqlite-related code (e.g. vtables or custom functions) that should use memory from the sqlite3 pool. + +[source,cpp,subs=+quotes] +---- +template<typename T> +struct allocator +{ + constexpr allocator() noexcept {} + constexpr allocator( const allocator& other ) noexcept {} + template< class U > + constexpr allocator( const allocator<U>& other ) noexcept {} + + constexpr static std::size_t alignment = __implementation_defined__; + + + static_assert(alignof(T) <= alignment, "T alignment can't be fulfilled by sqlite"); + + [[nodiscard]] T* allocate( std::size_t n ); // <1> + void deallocate( T* p, std::size_t); // <2> +}; +---- +<1> Invokes `sqlite3_malloc64` and throws `std::bad_alloc` if it fails. +<2> Invokes `sqlite3_free` + +NOTE: Sqlite provides extensive https://www.sqlite.org/malloc.html[customizability for its dynamic memory allocation]. + diff --git a/doc/reference/backup.adoc b/doc/reference/backup.adoc new file mode 100644 index 0000000..bc74619 --- /dev/null +++ b/doc/reference/backup.adoc @@ -0,0 +1,43 @@ +== `sqlite/backup.hpp` +[#backup] + +Backup is a small wrapper function to create a backup of one database into another. +This can be useful to write an in memory database to disk et vice versa. + +[source,cpp] +---- +void +backup(connection & source, + connection & target, + cstring_ref source_name = "main", + cstring_ref target_name = "main"); + +void +backup(connection & source, + connection & target, + cstring_ref source_name, + cstring_ref target_name, + system::error_code & ec, + error_info & ei); +---- + + +source:: The source database to backup + +target:: The target of the backup + +source_name:: The source database to read the backup from. Default is 'main'. +target_name:: The target database to write the backup to. Default is 'main'. + + +.Example +[source,cpp] +---- +sqlite::connection conn{sqlite::in_memory}; +{ + sqlite::connection read{"./read_only_db.db", SQLITE_READONLY}; + // read peristed data into memory. + backup(read, target); +} +---- + diff --git a/doc/reference/blob.adoc b/doc/reference/blob.adoc new file mode 100644 index 0000000..8f73105 --- /dev/null +++ b/doc/reference/blob.adoc @@ -0,0 +1,141 @@ +== `sqlite/blob.hpp` + +=== `blob_view` + +A `blob_view` is a view type referring to https://www.sqlite.org/datatype3.html[Binary Large OBject], +i.e. a non-owning type. + +.Definition +[source,cpp] +---- +//A view to a binary large object +struct blob_view +{ + // The data in the blob + const void * data() const; + // The size of the data in the blob, in bytes + std::size_t size() const + // Construct a blob from existing data + blob_view(const void * data, std::size_t size); + + // Construct an empty blob + blob_view() = default; + // Construct a blob from some other blob-like structure (data() is a pointer & size() returns size_t) + template<typename T> + explicit blob_view(const T & value); + + // Create a blob from the + blob_view(const struct blob & b); +}; +---- + +The `blob_view` can be used to access binary data from the database without copying. + +=== `zero_blob` + +The `zero_blob` is a special type that denotes blobs full of zeros without requiring any allocations. +It can be used as a result from a <<function>> or as a parameter for a <<statement>>. + +.Definition +[source,cpp] +---- +enum class zero_blob : sqlite3_uint64 {}; +---- + +.Example +[source,cpp] +---- +extern sqlite::connection conn; +conn.prepare("INSERT INTO example(bdata) VALUES(?)") + .execute(sqlite::zero_blob(1024)); // <1> +---- +<1> Insert a blob of zeros with the size 1024 + +=== `blob` + +The `blob` object owns a binary large object. + +[source,cpp] +---- +// @brief An object that owns a binary large object. @ingroup reference +struct blob +{ + // The data in the blob + void * data() const; + // The size of the data int he blob, in bytes + std::size_t size() const; + + // Create a blob from a blob_view + explicit blob(blob_view bv); + // Create an empty blob with size `n`. + explicit blob(std::size_t n); + + // Construct an empty blob + constexpr blob() = default; + // Release & take ownership of the blob. + void * release() && +}; +---- + + +=== `blob_handle` + +A `blob_handle` is readable & writable handle to a `blob` in the database. +It allows incremental reading/writing of the raw data. + +[source,cpp] +---- +// Open a blob +blob_handle open_blob(connection & conn, + cstring_ref db, + cstring_ref table, + cstring_ref column, + sqlite3_int64 row, + bool read_only, + system::error_code &ec, + error_info &ei); +blob_handle open_blob(connection & conn, + cstring_ref db, + cstring_ref table, + cstring_ref column, + sqlite3_int64 row, + bool read_only = false); + +// An object that holds a binary large object. Can be obtained by using @ref blob_handle. @ingroup reference +struct blob_handle +{ + // Default constructor + blob_handle() = default; + + // Construct from a handle. This takesowner ship of the `sqlite3_blob` handle. + explicit blob_handle(sqlite3_blob * blob); + + // Reopen the blob on another row (i.e. the same column of the same table) + void reopen(sqlite3_int64 row_id); + void reopen(sqlite3_int64 row_id, system::error_code & ec); + + // Read data from the blob + void read_at(void *data, int len, int offset); + void read_at(void *data, int len, int offset, system::error_code &ec); + + // Write data to the blob + void write_at(const void *data, int len, int offset); + void write_at(const void *data, int len, int offset, system::error_code &ec); + + // The size of the blob + std::size_t size() const; + + // The handle of the blob + using handle_type = sqlite3_blob*; + // Returns the handle of the blob + handle_type handle(); + // Release the owned handle. + handle_type release() &&; +}; +---- + + + + + + diff --git a/doc/reference/collation.adoc b/doc/reference/collation.adoc new file mode 100644 index 0000000..6578f59 --- /dev/null +++ b/doc/reference/collation.adoc @@ -0,0 +1,45 @@ +== `sqlite/collation.hpp` +[#collation] + +A https://www.sqlite.org/datatype3.html#collation[collation] is a comparison operator between two string-like values, +that allows ordering with a custom algorithm. + +.Definition +[source,cpp] +---- + +// Create a collation +template<typename Func> +void create_collation(connection & conn, cstring_ref name, Func && func); +template<typename Func> +void create_collation(connection & conn, cstring_ref name, Func && func, system::error_code &ec); + +// Delete an existing collation. +void delete_collation(connection & conn, cstring_ref name, system::error_code & ec); +void delete_collation(connection & conn, cstring_ref name); +---- + + conn:: A connection to the database in which to install the collation. + name:: The name of the collation. + func:: The function + +The function must be callable with two `string_view` and return an int, indicating the comparison results. + +.Example +[source,cpp] +---- +// a case insensitive string omparison, e.g. from boost.urls +int ci_compare(string_view s0, string_view s1) noexcept; + +extern sqlite::connection conn; + +// Register the collation +sqlite::create_collation(conn, "iequal", &ci_compare); + +// use the collation to get by name, case insensitively +conn.query("select first_name, last_name from people where first_name = 'Klemens' collate iequal;"); + +// order by names case insensitively +conn.query("select * from people order by last_name collate iequal asc;"); +---- + diff --git a/doc/reference/connection.adoc b/doc/reference/connection.adoc new file mode 100644 index 0000000..6d62c10 --- /dev/null +++ b/doc/reference/connection.adoc @@ -0,0 +1,100 @@ +== `sqlite/connection.hpp` +[#connection] + +The `connection` object is the main object to access a database. + +.Definition +[source,cpp] +---- +// Utility constant for in-memory databases +constexpr static cstring_ref in_memory = ":memory:"; + +struct connection +{ + // The handle of the connection + using handle_type = sqlite3*; + // Returns the handle + handle_type handle() const; + // Release the owned handle. + handle_type release() &&; + + //Default constructor + connection() = default; + // Construct the connection from a handle. + explicit connection(handle_type handle, bool take_ownership = true); // <1> + // Move constructor. + connection(connection && ) = default; + // Move assign operator. + connection& operator=(connection && ) = default; + + // Construct a connection and connect it to `filename`. `flags` is set by `SQLITE_OPEN_*` flags. + connection(cstring_ref filename, + int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); // <2> + template<typename Path> + explicit connection(const Path & pth); + + + // Connect the database to `filename`. `flags` is set by `SQLITE_OPEN_*` flags. + void connect(cstring_ref filename, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); // <2> + void connect(cstring_ref filename, int flags, system::error_code & ec); + + template<typename Path> + void connect(const Path & pth, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + template<typename Path> + void connect(const Path & pth, int flags, system::error_code & ec); + + // Close the database connection. + void close(); + void close(system::error_code & ec, error_info & ei); + + // Check if the database holds a valid handle. + bool valid() const; + + // Perform a query without parameters. Can only execute a single statement. + resultset query( + core::string_view q, + system::error_code & ec, + error_info & ei); + resultset query(core::string_view q); + + template<typename T, bool Strict = false> + static_resultset<T, Strict> query(core::string_view q, system::error_code & ec, error_info & ei); + template<typename T, bool Strict = false> + static_resultset<T, Strict> query(core::string_view q); + + // Perform a query without parametert, It execute a multiple statement. + void execute(cstring_ref q, system::error_code & ec, error_info & ei); + void execute(cstring_ref q); + + + // Preparse a a statement. + statement prepare( + core::string_view q, + system::error_code & ec, + error_info & ei); + statement prepare(core::string_view q); + + + // Check if the database has the table + bool has_table( + cstring_ref table, + cstring_ref db_name = "main") const; + + // Check if the database has the table + bool has_column( + cstring_ref table, + cstring_ref column, + cstring_ref db_name = "main") const; +}; + +---- +<1> The `take_ownership` is usually only false when used from <<extension_modules, extension modules>>. +<2> See https://www.sqlite.org/c3ref/c_open_autoproxy.html[the sqlite documentation for the available flags]. + +.Example +[source,cpp] +---- +sqlite::connection conn; +conn.connect("./my-database.db"); +conn.prepare("insert into log (text) values ($1)").execute(std::make_tuple("booting up")); +----
\ No newline at end of file diff --git a/doc/reference/cstring_ref.adoc b/doc/reference/cstring_ref.adoc new file mode 100644 index 0000000..402663d --- /dev/null +++ b/doc/reference/cstring_ref.adoc @@ -0,0 +1,16 @@ +== `sqlite/cstring_ref.hpp` + +[#string_view] + +The `sqlite::string_view` class is a https://en.cppreference.com/w/cpp/string/basic_string_view[std::string_view] +compatible class. + +[#cstring_ref] + +The `cstring_ref` class is a view type similar to a `string_view`, but with a guarantee that it is null-terminated. + +It can be constructed from a raw `const char *` or any class that has a `c_str()` function returning a `const char *`. + +Otherwise it is similar to a `string_view`, except that `substr(std::size_t())` will return a `cstring_ref`, +whereas a `substr(std::size_t(), std::size_t())` returns a `string_view`. + diff --git a/doc/reference/error.adoc b/doc/reference/error.adoc new file mode 100644 index 0000000..424c7dd --- /dev/null +++ b/doc/reference/error.adoc @@ -0,0 +1,91 @@ +== `sqlite/error.hpp` + +=== `sqlite_category` + +The sqlite_category is a `boost::system::error_category` to be used with sqlite errors. + +=== `error_info` + +The `error_info` class hold additional information about error conditions stored in an sqlite-allocate string. + +Contains an error message describing what happened. Not all error conditions are able to generate this extended information - those that +can't will have an empty error message. + +The `error_info` allocates memory from the sqlite pool and holds it. + +[source,cpp] +---- +struct error_info +{ + // Default constructor. + error_info() = default; + + // Initialization constructor. Copies the message into a newly create buffer. + error_info(core::string_view msg) noexcept; + // set the message by copy + void set_message(core::string_view msg); + + // Reset the buffer. If `c` is not null, its ownership is transferred into the error_info object. + void reset(char * c = nullptr); + + // Format a message into a newly allocated buffer. + cstring_ref format(cstring_ref fmt, ...); + // Format a message into the existing buffer. + cstring_ref snformat(cstring_ref fmt, ...); + /// reserve data in the buffer i.e. allocate + void reserve(std::size_t sz); + + // Get the allocated memory + std::size_t capacity() const; + + // Gets the error message. + cstring_ref message() const noexcept; + + // Release the underlying memory. It must be freed using `sqlite_free` later. + char * release(); + // Restores the message to its initial state. Does not release memory. + void clear() noexcept; + +}; +---- + +=== `error` + +The `error` class holds `error_info` and a `code` and can be used with https://www.boost.org/doc/libs/master/libs/system/doc/html/system.html#ref_boostsystemresult_hpp[`boost::system::result`]. + +[source,cpp] +---- +/** + * \brief An error containing both a code & optional message. + * \ingroup reference + * \details Contains an error . + */ +struct error +{ + // The code of the error. + int code; + // The additional information of the error + error_info info; + + // Create an error with code & message + error(int code, error_info info) ; + error(int code, core::string_view info); + error(system::error_code code, error_info info) // <1> + // Create an error with only a code. + explicit error(int code); + + error(system::error_code code); + // Create an empty error; + error() = default; + error(error && ) noexcept = default; +}; + +// For compatability with system::result; +void throw_exception_from_error( error const & e, boost::source_location const & loc ); + +template<typename T = void> +using result = system::result<T, error>; +---- +<1> If code.category() is not `sqlite_category`, the code will be set to `SQLITE_FAIL`. + + diff --git a/doc/reference/extension.adoc b/doc/reference/extension.adoc new file mode 100644 index 0000000..7ee2500 --- /dev/null +++ b/doc/reference/extension.adoc @@ -0,0 +1,36 @@ +== `sqlite/extension.hpp` + +=== `BOOST_SQLITE_EXTENSION` + + This macro can be used to create an sqlite extension. + +.Definition +[source,cpp] +---- +#define BOOST_SQLITE_EXTENSION(Name, Conn) +---- + +Name:: The name of the module. +Conn:: The parameter name of the connection. + + +NOTE: When defining BOOST_SQLITE_COMPILE_EXTENSION (was is done in extension.hpp) +sqlite will use an inline namespace to avoid symbol clashes. + +You must link against `Boost::sqlite_ext` and not `Boost::sqlite` and should not mix both in the same binary. + +.Example +[source,cpp] +---- +BOOST_SQLITE_EXTENSION(extension, conn) +{ + create_scalar_function( + conn, "assert", + [](boost::sqlite::context<>, boost::span<boost::sqlite::value, 1u> sp) + { + if (sp.front().get_int() == 0) + throw std::logic_error("assertion failed"); + }); +} +---- + diff --git a/doc/reference/field.adoc b/doc/reference/field.adoc new file mode 100644 index 0000000..572719d --- /dev/null +++ b/doc/reference/field.adoc @@ -0,0 +1,42 @@ +== `sqlite/field.hpp` + +A `field` is a type representing a <<value>> in a database. or as a result from a query. + +.Definition +[source,cpp] +---- + +struct field +{ + typedef sqlite_int64 int64; + + // The type of the value + value_type type() const; + // Is the held value null + bool is_null() const; + // Is the held value is not null + explicit operator bool () const; + // Returns the value as an `int64`. + int64 get_int() const; + // Returns the value as an `double`. + double get_double() const; + // Returns the value as text, i.e. a string_view. Note that this value may be invalidated + cstring_ref get_text() const; + // Returns the value as blob, i.e. raw memory. Note that this value may be invalidated + blob_view get_blob() const; + // Returns the field as a value. + value get_value() const; + // Returns the name of the column. + cstring_ref column_name() const; + // Returns the name of the table. + cstring_ref table_name() const; + // Returns the name of the original data source. + cstring_ref column_origin_name() const; +} +---- + +NOTE: The view types can be invalidated when the database changes or the next row is read by the query. + +WARNING: The `field` type does not own the statement/query it was produced by. It is a merely a view into the `resultset`. +Reading the next row will change the values returned. + diff --git a/doc/reference/function.adoc b/doc/reference/function.adoc new file mode 100644 index 0000000..311aebc --- /dev/null +++ b/doc/reference/function.adoc @@ -0,0 +1,263 @@ +== `sqlite/function.hpp` + +=== `function_flags` + +[source, cpp] +---- +enum function_flags +{ + deterministic = SQLITE_DETERMINISTIC, + directonly = SQLITE_DIRECTONLY, + subtype = SQLITE_SUBTYPE, + innocuous = SQLITE_INNOCUOUS, + result_subtype = SQLITE_RESULT_SUBTYPE, + selforder1 = SQLITE_SELFORDER1, + selforder1 = SQLITE_SELFORDER1 +}; +---- + +These function flags can be used in accordance to the +https://www.sqlite.org/c3ref/c_deterministic.html[sqlite documentation]. + +=== `context` + +A context is an object share values between invocations of a scalar function. + +.Definition +[source,cpp] +---- + +template<typename ... Args> +struct context +{ + template<std::size_t N> + using element = mp11::mp_take_c<mp11::mp_list<Args...>, N>; + + // Set the value in the context at position `Idx` + template<std::size_t Idx> + void set(element<Idx> value); + + // Returns the value in the context at position `Idx`. Throws if the value isn't set. + template<std::size_t Idx> + auto get() -> element<Idx> &; + + // Returns the value in the context at position `Idx`. Returns nullptr .value isn't set. + template<std::size_t Idx> + auto get_if() -> element<Idx> *; + + explicit context(sqlite3_context * ctx) noexcept; + + // Set the result through the context, instead of returning it. + template<typename T> + auto set_result(T && val); + + // Set the an error through the context, instead of throwing it. + void set_error(cstring_ref message, int code = SQLITE_ERROR); + + // Returns the connection of the context. + connection get_connection() const; +}; +---- + + +.Example +[source,cpp] +---- +extern sqlite::connection conn; + +sqlite::create_scalar_function( +conn, "my_sum", +[](sqlite::context<std::size_t> ctx, + boost::span<sqlite::value, 1u> args) -> std::size_t +{ + auto value = args[0].get_int(); + auto p = ctx.get_if<0>(); + if (p != nullptr) // increment the counter + return (*p) += value; + else // set the initial value + ctx.set<0>(value); + return value; +}); +---- + + +=== `create_scalar_function` + + +Creates a https://www.sqlite.org/appfunc.html[scalar function]. + +.Definition +[source,cpp] +---- + +template<typename Func> +auto create_scalar_function( + connection & conn, + cstring_ref name, + Func && func, + function_flags flags = {}); + +template<typename Func> +auto create_scalar_function( + connection & conn, + cstring_ref name, + Func && func, + function_flags flags, + system::error_code & ec, + error_info & ei); +---- + +conn:: The connection to add the function to. +name:: The name of the function +func:: The function to be added + +`func` must take `context<Args...>` as the first and a `span<value, N>` as the second value. +If `N` is not `dynamic_extent` it will be used to deduce the number of arguments for the function. + + +.Example +[source,cpp] +---- +extern sqlite::connection conn; + +sqlite::create_function( + conn, "to_upper", + [](sqlite::context<> ctx, + boost::span<sqlite::value, 1u> args) -> std::string + { + std::string res; + auto txt = val[0].get_text(); + res.resize(txt.size()); + std::transform(txt.begin(), txt.end(), res.begin(), + [](char c){return std::toupper(c);}); + return value; + }); +---- + + +=== `create_aggregate_function` + +An aggregrate function will create a new `Func` for a new `aggregate` from the args tuple and call `step` for every step. +When the aggregation is done `final` is called and the result is returned to sqlite. + +[source,cpp] +---- +template<typename Func, typename Args = std::tuple<>> +void create_aggregate_function( +connection & conn, +cstring_ref name, +Args && args= {}, +function_flags flags = {}); + +template<typename Func, typename Args = std::tuple<>> +void create_aggregate_function( +connection & conn, +cstring_ref name, +Args && args, +function_flags flags, +system::error_code & ec, +error_info & ei); +---- + + +conn:: The connection to add the function to. +name:: The name of the function + +args:: The argument tuple to construct `Func` from. +Func:: The function to be added. It needs to be an object with two functions: +[source,cpp] +---- +void step(boost::span<sqlite::value, N> args); +T final(); +---- + +.Example +[source,cpp] +---- + extern sqlite::connection conn; + +struct aggregate_func +{ + aggregate_func(std::size_t init) : counter(init) {} + std::int64_t counter; + void step(, boost::span<sqlite::value, 1u> val) + { + counter += val[0].get_text().size(); + } + + std::int64_t final() + { + return counter; + } +}; + +sqlite::create_function<aggregate_func>(conn, "char_counter", std::make_tuple(42)); +---- + + +=== `create_window_function` + +NOTE: This is only available starting with sqlite 3.25.0. + +An window function will create a new `Func` for a new `aggregate` and call `step` for every step. +When an element is removed from the window `inverse` is called. +When the aggregation is done `final` is called and the result is returned to sqlite. + +[source,cpp] +---- +template<typename Func, typename Args = std::tuple<>> +void create_window_function( + connection & conn, + cstring_ref name, + Args && args = {}, + function_flags flags = {}); + +template<typename Func, typename Args = std::tuple<>> +void create_window_function( + connection & conn, + cstring_ref name, + Args && args, + function_flags flags, + system::error_code & ec); +---- + + +conn:: The connection to add the function to. +name:: The name of the function +args:: The arguments to construct Func from. +Func:: The function to be added. It needs to be an object with three functions: +[source,cpp] +---- +void step(boost::span<sqlite::value, N> args); +void inverse(boost::span<sqlite::value, N> args); +T final(); +---- + + +.Example +[source,cpp] +---- +extern sqlite::connection conn; + +struct window_func +{ + std::int64_t counter; + void step(boost::span<sqlite::value, 1u> val) + { + counter += val[0].get_text().size(); + } + void inverse(boost::span<sqlite::value, 1u> val) + { + counter -= val[0].get_text().size(); + } + + std::int64_t final() + { + return counter; + } +}; + +sqlite::create_function(conn, "win_char_counter", aggregate_func{}); +---- + + diff --git a/doc/reference/hooks.adoc b/doc/reference/hooks.adoc new file mode 100644 index 0000000..c74d712 --- /dev/null +++ b/doc/reference/hooks.adoc @@ -0,0 +1,105 @@ +== `sqlite/hooks.hpp` + +WARNING: This API might be subject change, if a better solution for ownership is found. + +=== `commit_hook` & `rollback_commit` + +The https://www.sqlite.org/c3ref/commit_hook.html[commit hook] +gets called before a commit gets performed. +Likewise the rollback hook gets invoked before a rollback. +If `func` returns true, the commit goes, otherwise it gets rolled back. + +NOTE: If the function is not a free function pointer, this function will *NOT* take ownership. + +NOTE: If `func` is a `nullptr` the hook gets reset. + + +[source,cpp] +---- +template<typename Func> +bool commit_hook(connection & conn, Func && func); + +template<typename Func> +bool rollback_hook(connection & conn, Func && func); +---- + +return:: `true` if a hook has been replaced. +conn:: The database connection to install the hook in +func:: The hook function. It must be callable without any parameter, return a `bool` and be `noexcept`. + + +=== `update_hook` + +The https://www.sqlite.org/c3ref/update_hook.html[update hook] +The update hook gets called when an update was performed. + + +NOTE: If the function is not a free function pointer, this function will *NOT* take ownership. + +NOTE: If `func` is a `nullptr` the hook gets reset. + + +[source,cpp] +---- +template<typename Func> +bool update_hook(connection & conn, Func && func); +---- + +return:: `true` if a hook has been replaced. +conn:: The database connection to install the hook in +func:: The signature of the function is `void(int op, core::string_view db, core::string_view table, sqlite3_int64 id)`. +`op` is either `SQLITE_INSERT`, `SQLITE_DELETE` and `SQLITE_UPDATE`. The function must be noexcept. + +=== `preupdate_hook` + +NOTE: The https://www.sqlite.org/c3ref/preupdate_blobwrite.html[preupdate hook] requires +sqlite to be required with `SQLITE_ENABLE_PREUPDATE_HOOK` true. + +This hook gets called before an update. + +[source,cpp] +---- +struct preupdate_context +{ + // Returns the old value, i.e. the value before the update. + system::result<value> old(int column) const; + // The count of colums to be updated + int count() const; + // The nesting depth of the update. + int depth() const; + // The new value to be written to column + system::result<value> new_(int column) const; + + // Query the status of blob access, e.g. when using blob_handle <1> + int blob_write() const; + + explicit preupdate_context(sqlite3 * db) noexcept; +}; + + +template<typename Func> +bool preupdate_hook(connection & conn, Func && func); +---- +<1> See https://www.sqlite.org/c3ref/preupdate_blobwrite.html[sqlite/preupdate_blobwrite] + + + +return:: `true` if a hook has been replaced. +conn:: The database connection to install the hook in +func:: The signature of the function is below: +[source,cpp] +---- + void preupdate_hook(sqlite::preupdate_context ctx, + int op, + const char * db_name, + const char * table_name, + sqlite3_int64 current_key, + sqlite3_int64 new_key); +---- + + + + + + + diff --git a/doc/reference/json.adoc b/doc/reference/json.adoc new file mode 100644 index 0000000..1c15f52 --- /dev/null +++ b/doc/reference/json.adoc @@ -0,0 +1,28 @@ +== `sqlite/json.hpp` + +The json header provides integration with boost/json. + +[source,cpp] +---- + +// The subtype value used by the sqlite json extension. See the [sqlite reference](https://www.sqlite.org/json1.html) +constexpr int json_subtype = static_cast<int>('J'); + +// Allow json to be used as a result from functions or vtables +void tag_invoke(const struct set_result_tag &, sqlite3_context * ctx, const json::value & value); + +// Check if the value or field is a json. +bool is_json(const value & v); +bool is_json(const field & f); + +//Convert the value or field to a json. +json::value as_json(const value & v, json::storage_ptr ptr = {}); +json::value as_json(const field & f, json::storage_ptr ptr = {}); + +// Allow conversions to boost::json::value +void tag_invoke( const json::value_from_tag &, json::value& val, const value & f); +void tag_invoke( const json::value_from_tag &, json::value& val, const field & f); +void tag_invoke( const json::value_from_tag &, json::value& val, resultset && rs); +---- + + diff --git a/doc/reference/memory.adoc b/doc/reference/memory.adoc new file mode 100644 index 0000000..7bf62ef --- /dev/null +++ b/doc/reference/memory.adoc @@ -0,0 +1,29 @@ +== `sqlite/memory.hpp` + +The memory header provides C++-y access to the memory facilities of sqlite. + +[source,cpp,subs=+quotes] +---- +// A tag to allow `operator new` +struct memory_tag {}; + +// new operators <1> +void *operator new ( std::size_t size, boost::sqlite::memory_tag) noexcept; +void *operator new[]( std::size_t size, boost::sqlite::memory_tag) noexcept; +void operator delete ( void* ptr, boost::sqlite::memory_tag) noexcept; + +// A unique ptr that uses sqlite3_malloc / sqlite3_free +template<typename T> +using unique_ptr = std::unique_ptr<T, __implementation_detail__>; +template<typename T, typename ... Args> +unique_ptr<T> make_unique(Args && ... args); + +// Get the size of the allocated memory. +template<typename T> +std::size_t msize(const unique_ptr<T> & ptr); + + +--- +<1> `new (sqlite::memory_tag{}) T()` uses sqlite3_malloc + + diff --git a/doc/reference/meta_data.adoc b/doc/reference/meta_data.adoc new file mode 100644 index 0000000..6375108 --- /dev/null +++ b/doc/reference/meta_data.adoc @@ -0,0 +1,36 @@ +== `sqlite/meta_data.hpp` + +The meta_data header provides some meta_data for columns. + +[source,cpp,subs=+quotes] +---- +// The metadata of a column +struct column_meta_data +{ + // Data type fo the column + cstring_ref data_type; + // Name of default collation sequence + cstring_ref collation; + // true if column has a NOT NULL constraint + bool not_null; + // true if column is part of the PRIMARY KEY + bool primary_key; + // true if column is AUTOINCREMENT + bool auto_increment; +}; + +// get the meta-data of one colum + +column_meta_data table_column_meta_data(connection & conn, + cstring_ref db_name, cstring_ref table_name, cstring_ref column_name, + system::error_code & ec, error_info &ei); +column_meta_data table_column_meta_data(connection & conn, + cstring_ref table_name, cstring_ref column_name, + system::error_code & ec, error_info &ei); + +column_meta_data table_column_meta_data(connection & conn, + cstring_ref db_name, cstring_ref table_name, cstring_ref column_name); +column_meta_data table_column_meta_data(connection & conn, + cstring_ref table_name, cstring_ref column_name); +--- + diff --git a/doc/reference/mutex.adoc b/doc/reference/mutex.adoc new file mode 100644 index 0000000..d0493d6 --- /dev/null +++ b/doc/reference/mutex.adoc @@ -0,0 +1,16 @@ +== `sqlite/mutex.hpp` + +The mutex header provides to std::mutex compatible classes using the sqlite mutex implementation. + +This will allow C++ code to use mutex code matching the configuration of sqlite. +This may include the mutex being a noop. + +[source,cpp,subs=+quotes] +---- +// similar to std::mutex +struct mutex; +// similar to std::recursive_mutexx +struct recursive_mutex; +---- + + diff --git a/doc/reference/result.adoc b/doc/reference/result.adoc new file mode 100644 index 0000000..0471b95 --- /dev/null +++ b/doc/reference/result.adoc @@ -0,0 +1,40 @@ +== `sqlite/result.hpp` + +The result header is used by functions and vtables to turn resulting values into +sqlite values. The `tag_invoke` interface is public and meant to extended. + +That is, implementing `tag_invoke(sqlite::set_result_tag, sqlite3_context, T);` +will enable `T` to be used as a result by sqlite. + +[source,cpp] +---- +// The tag +struct set_result_tag {}; + +// built-in result type +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, blob b); +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, zero_blob zb); +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, double dbl) { sqlite3_result_double(ctx, dbl); } +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, sqlite3_int64 value);inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::int64_t value); +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::nullptr_t); +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, string_view str); +template<typename String> +inline auto tag_invoke(set_result_tag, sqlite3_context * ctx, String && str); +inline void tag_invoke(set_result_tag, sqlite3_context * , variant2::monostate); +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, const value & val); +template<typename ... Args> +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, const variant2::variant<Args...> & var); + +template<typename T> +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::unique_ptr<T> ptr); +template<typename T> +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::unique_ptr<T, void(*)(T*)> ptr); +template<typename T, typename Deleter> +inline auto tag_invoke(set_result_tag, sqlite3_context * ctx, std::unique_ptr<T> ptr); +inline void tag_invoke(set_result_tag, sqlite3_context * ctx, error err); +template<typename T> +inline void tag_invoke(set_result_tag tag, sqlite3_context * ctx, result<T> res); +inline void tag_invoke(set_result_tag tag, sqlite3_context * ctx, result<void> res): +---- + + diff --git a/doc/reference/resultset.adoc b/doc/reference/resultset.adoc new file mode 100644 index 0000000..fb793a5 --- /dev/null +++ b/doc/reference/resultset.adoc @@ -0,0 +1,72 @@ +== `sqlite/resultset.hpp` + +A resultset represents the results of a query. + +[source,cpp] +---- +// Representation of a result from a query. + +struct resultset +{ + // Returns the current row. The row is a pure view type. + row current() const &; + // Checks if the last row has been reached. + bool done() const; + + // Read the next row. Returns false if there's nothing more to read. + // Calling this will change the data of any `row` previously obtained from `current. + bool read_next(system::error_code & ec, error_info & ei); + bool read_next(); + + // The number of colums in the resultset + std::size_t column_count() const; + // Returns the name of the column idx. + string_view column_name(std::size_t idx) const; + // Returns the name of the source table for column idx. + string_view table_name(std::size_t idx) const; + // Returns the origin name of the column for column idx. + string_view column_origin_name(std::size_t idx) const; + + // The input iterator can be used to read every row in a for-loop + struct iterator + { + using value_type = value; + using difference_type = int; + using reference = value&; + using iterator_category = std::forward_iterator_tag; + + iterator() {} + + bool operator!=(iterator rhs) const; + row &operator*(); + row *operator->(); + + iterator operator++(); + iterator operator++(int); + }; + + // Return an input iterator to the currently unread row + iterator begin(); + // Sentinel iterator. + iterator end(); +}; +---- + + + + +.Example +[source,cpp] +---- +extern sqlite::connection conn; + +sqlite::resultset rs = conn.query("select * from users;"); + +do +{ + handle_row(r.current()); +} +while (rs.read_next()); // read it line by line +---- + + diff --git a/doc/reference/row.adoc b/doc/reference/row.adoc new file mode 100644 index 0000000..d3af982 --- /dev/null +++ b/doc/reference/row.adoc @@ -0,0 +1,56 @@ +== `sqlite/row.hpp` + +This type represents one row of data from a query. Is a random-access range. + +WARNING: This type is a pure view type, and will be invalidated when the next row is read. + + + +[source,cpp] +---- + +struct row +{ + // The size of the row, i.e. number of colums + std::size_t size() const; + + // Returns the field at `idx`, @throws std::out_of_range + field at(std::size_t idx) const; + // Returns the field at `idx`. + field operator[](std::size_t idx) const; + + // Random access iterator used to iterate over the columns. + struct const_iterator + { + using difference_type = int; + using reference = field&; + using iterator_category = std::random_access_iterator_tag; + + const_iterator & operator++(); + const_iterator operator++(int); + const_iterator & operator--(); + const_iterator operator--(int); + + field operator[](int i) const; + + const_iterator operator+(int i) const; + const_iterator operator-(int i) const; + const_iterator & operator+=(int i); + const_iterator & operator-=(int i); + const field & operator*() const; + const field * operator->() const; + + bool operator==(const const_iterator& other) const; + bool operator!=(const const_iterator& other) const; + bool operator<(const const_iterator& other) const; + bool operator>(const const_iterator& other) const; + + const_iterator() = default; + }; + // Returns the begin of the column-range. + const_iterator begin() const; + + // Returns the end of the column-range. + const_iterator end() const +}; +---- diff --git a/doc/reference/statement.adoc b/doc/reference/statement.adoc new file mode 100644 index 0000000..6ce89e4 --- /dev/null +++ b/doc/reference/statement.adoc @@ -0,0 +1,165 @@ +== `sqlite/statement.hpp` + +=== `param_ref` + +A reference to a value to temporary bind for an execute statement. Most values are captures by reference. + +[source,cpp] +---- +struct param_ref +{ + // Default construct a parameter, gives `null`. + param_ref() = default; + // Bind null + param_ref(variant2::monostate); + // Bind null + param_ref(std::nullptr_t); + // Bind an integer. + template<typename I> + param_ref(I value); + // Bind a blob. + param_ref(blob_view blob); + // Bind a string. + param_ref(string_view text); + + template<typename StringLike> + param_ref(StringLike && text); + template<typename BlobLike> + param_ref(BlobLike && text); + + // Bind a floating point value. + param_ref(double value) : impl_(value) { } + // Bind a zero_blob value, i.e. a blob that initialized by zero. + param_ref(zero_blob zb) : impl_(zb) { } + + // Bind pointer value to the parameter. @see https://www.sqlite.org/bindptr.html + // Requires sqlite 3.20 + // Deleter must a function pointer or trivially constructible. + template<typename T, typename Deleter> + param_ref(std::unique_ptr<T, Deleter> ptr); + + + + // Apply the param_ref to a statement. + int apply(sqlite3_stmt * stmt, int c) const; + + // Construct param_ref from a variant + template<typename T> + param_ref(T && t); + +---- + + +=== `statement` + +A statement used for a prepared-statement. + + + +[source,cpp] +---- +struct statement +{ + // execute the prepared statement once. This transfers ownership to the resultset + template <typename ArgRange = std::initializer_list<param_ref>> + resultset execute(ArgRange && params, system::error_code& ec, error_info& info) &&; // <1> + template <typename ArgRange = std::initializer_list<param_ref> + resultset execute(ArgRange && params) &&; // <1> + resultset execute( + std::initializer_list<std::pair<string_view, param_ref>> params, + system::error_code& ec, + error_info& info) &&; // <2> + + resultset execute(std::initializer_list<std::pair<string_view, param_ref>> params) &&; + template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>> + static_resultset<T, Strict> execute( + ArgRange && params, + system::error_code & ec, + error_info & ei) &&; // <1> + + template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>> + static_resultset<T, Strict> execute(ArgRange && params) &&; // <1> + + template<typename T, bool Strict = false> + static_resultset<T, Strict> execute( + std::initializer_list<std::pair<string_view, param_ref>> params, + system::error_code & ec, + error_info & ei) &&; // <2> + template<typename T, bool Strict = false> + static_resultset<T, Strict> execute(std::initializer_list<std::pair<string_view, param_ref>> params) &&; // <2> + + + template <typename ArgRange = std::initializer_list<param_ref>> + resultset execute( + ArgRange && params, + system::error_code& ec, + error_info& info) &; // <1> + + + template <typename ArgRange = std::initializer_list<param_ref>> + resultset execute(ArgRange && params) &; // <1> + + + resultset execute( + std::initializer_list<std::pair<string_view, param_ref>> params, + system::error_code& ec, + error_info& info) &; // <2> + + resultset execute(std::initializer_list<std::pair<string_view, param_ref>> params) &; // <2> + + template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>> + static_resultset<T, Strict> execute( + ArgRange && params, + system::error_code & ec, + error_info & ei) &; // <1> + + template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>> + static_resultset<T, Strict> execute(ArgRange && params) &; // <1> + + template<typename T, bool Strict = false> + static_resultset<T, Strict> execute( + std::initializer_list<std::pair<string_view, param_ref>> params, + system::error_code & ec, + error_info & ei) &; // <2> + template<typename T, bool Strict = false> + static_resultset<T, Strict> execute(std::initializer_list<std::pair<string_view, param_ref>> params) &; // <2> + + + + // Returns the sql used to construct the prepared statement. + stringe_view sql(); + + // Returns the expanded sql used to construct the prepared statement. Requires sqlite 3.14 + stringe_view expanded_sql(); + + // Returns the expanded sql used to construct the prepared statement. requiers sqlite to be compiles with SQLITE_ENABLE_NORMALIZE. + stringe_view normalized_sql(); + + // Returns the declared type of the column + string_view declared_type(int id) const; +}; +---- +<1> Executes a query with positional arguments +<2> Executes a query with named arguments (from a map-like object) + + + + + +WARNING: The `&&` overloads transfer ownership to the resultset, while the `&` keep them in the statement. +That is, this is UB: +[source,cpp] +---- +resultset get_users(sqlite::connection & conn) +{ + auto s = conn.prepare("SELECT * from users where name = ?"); + return s.execute({"allen"}); // UB, because result set points into s +} + +resultset get_users(sqlite::connection & conn) +{ + // correct, because resultset takes ownershipo + return conn.prepare("SELECT * from users where name = ?").execute({"allen"}); +} +---- + diff --git a/doc/reference/static_resultset.adoc b/doc/reference/static_resultset.adoc new file mode 100644 index 0000000..0e15e25 --- /dev/null +++ b/doc/reference/static_resultset.adoc @@ -0,0 +1,98 @@ +== `sqlite/static_resultset.hpp` + +A `static_resultset` represents the results of a query matched to a C++ type + +[source,cpp] +---- +// Representation of a result from a query. + +struct resultset +{ + +template<typename T, bool Strict > +struct static_resultset +{ + // Returns the current row. + T current() const &; + + // Returns the current row. + T current(system::error_code & ec, error_info & ei) const &; + // Checks if the last row has been reached. + bool done() const {return result_.done();} + + // Read the next row. Returns false if there's nothing more to read. + bool read_next(system::error_code & ec, error_info & ei); + bool read_next(); + + + // The number of columes in the resultset + std::size_t column_count() const; + // Returns the name of the column idx. + string_view column_name(std::size_t idx) const; + + // Returns the name of the source table for column idx. + string_view table_name(std::size_t idx) const; + // Returns the origin name of the column for column idx. + string_view column_origin_name(std::size_t idx) const; + + static_resultset() = default; + static_resultset(resultset && result) + + static_resultset(static_resultset<T, false> && rhs); + + /// The input iterator can be used to read every row in a for-loop + struct iterator + { + using value_type = T; + using difference_type = int; + using reference = T&; + using iterator_category = std::forward_iterator_tag; + + iterator(); + explicit iterator(resultset::iterator itr); + bool operator!=(iterator rhs) const; + + value_type &operator*(); + value_type *operator->(); + + iterator& operator++(); + iterator operator++(int); + }; + + /// Return an input iterator to the currently unread row + iterator begin(); + /// Sentinel iterator. + iterator end(); + + // Convert the static_result to a strict version + static_resultset<T, true> strict() && + { + return {std::move(result_)}; + } +}; +---- + + +T:: The static type of the query. This must be a tuple or pfr compatible (for C++20) or described. +Strict:: Disables implicit conversions. + + +.Example +[source,cpp] +---- +extern sqlite::connection conn; +struct user { std::string first_name; std::string last_name; }; +BOOST_DESCRIBE_STRUCT(user, (), (first_name, last_name)); + +sqlite::resultset rs = conn.query("select first_name, last_name from users;"); + +do +{ +user usr = r.current(); +handle_row(u); +} +while (rs.read_next()) // read it line by line + +---- + + diff --git a/doc/reference/string.adoc b/doc/reference/string.adoc new file mode 100644 index 0000000..21660f9 --- /dev/null +++ b/doc/reference/string.adoc @@ -0,0 +1,18 @@ +== `sqlite/string.hpp` + +This string header exposes some sqlite utility functions in a C++y way. + + +[source,cpp] +---- +bool like(cstring_ref lhs, cstring_ref rhs, char escape = '\0'); // <1> +bool glob(cstring_ref lhs, cstring_ref rhs); // <2> +int icmp(cstring_ref lhs, cstring_ref rhs); // <3> +int icmp(string_view lhs, string_view rhs, std::size_t n); // <4> +---- +<1> uses https://www.sqlite.org/c3ref/strlike.html[strlike] +<2> uses https://www.sqlite.org/c3ref/strglob.html[strglob] +<3> used https://www.sqlite.org/c3ref/stricmp.html[stricmp] +<4> used https://www.sqlite.org/c3ref/stricmp.html[strnicmp] + + diff --git a/doc/reference/transaction.adoc b/doc/reference/transaction.adoc new file mode 100644 index 0000000..b6bc449 --- /dev/null +++ b/doc/reference/transaction.adoc @@ -0,0 +1,104 @@ +== `sqlite/transaction.hpp` + +=== `transaction` + +A simple transaction guard implementing RAAI for transactions + +.Definition +[source,cpp] +---- +struct transaction +{ + // The mode of the transaction + enum behaviour {deferred, immediate, exclusive}; + // A tag to use, to adopt an already initiated transaction. + constexpr static struct adopt_transaction_t {} adopt_transaction{}; + + // Create transaction guard on an existing transaction + transaction(connection & conn, adopt_transaction_t); + + + // Create transaction guard and initiate a transaction + transaction(connection & conn); + + // Create transaction guard and initiate a transaction with the defined behaviour + transaction(connection & conn, behaviour b) ; + + // see https://www.sqlite.org/lang_transaction.html re noexcept + // rollback the transaction if not committed. + ~transaction() noexcept(SQLITE_VERSION_NUMBER >= 3007011); + + + // Commit the transaction. + void commit(); + void commit(system::error_code & ec, error_info & ei); + // Rollback the transaction explicitly. + void rollback(); + void rollback(system::error_code & ec, error_info & ei); + +}; +---- + + + +.Example +[source,cpp] +---- +sqlite::connection conn; +conn.connect("./my-database.db"); + +sqlite::transaction t{conn}; +conn.prepare("insert into log (text) values ($1)").execute(std::make_tuple("booting up")); +t.commit(); +---- + +=== `savepoint` + +A simple transaction guard implementing RAAI for savepoints. Savepoints can be used recursively. + +.Definition +[source,cpp] +---- + +struct savepoint +{ + // A tag to use, to adopt an already initiated transaction. + constexpr static transaction::adopt_transaction_t adopt_transaction{}; + + // Create savepoint guard on an existing savepoint + savepoint(connection & conn, std::string name, transaction::adopt_transaction_t); + + // Create transaction guard and initiate it + savepoint(connection & conn, std::string name); + + // rollback to the savepoint if not committed. + ~savepoint() noexcept(SQLITE_VERSION_NUMBER >= 3007011); + + // Commit/Release the transaction. + void commit(); + void commit(system::error_code & ec, error_info & ei); + + void release(); + void release(system::error_code & ec, error_info & ei); + + // Rollback the transaction explicitly. + void rollback(); + void rollback(system::error_code & ec, error_info & ei); + // The name of the savepoint. + + const std::string & name() const; +}; +---- + + +.Example +[source,cpp] +---- +sqlite::connection conn; +conn.connect("./my-database.db"); + +sqlite::savepoint t{conn, "my-savepoint}; +conn.prepare("insert into log (text) values ($1)").execute(std::make_tuple("booting up")); +t.commit(); +---- + diff --git a/doc/reference/value.adoc b/doc/reference/value.adoc new file mode 100644 index 0000000..f5cb3de --- /dev/null +++ b/doc/reference/value.adoc @@ -0,0 +1,85 @@ +== `sqlite/value.hpp` + +=== `value_type` + +The https://www.sqlite.org/datatype3.html)[type of a value]. + +[source,cpp] +---- +enum class value_type +{ + // An integral value + integer = SQLITE_INTEGER, + // A floating piont value + floating = SQLITE_FLOAT, + // A textual value + text = SQLITE_TEXT, + // A binary value + blob = SQLITE_BLOB, + // No value + null = SQLITE_NULL, +}; + +// Get the name as a string +const char * value_type_name(value_type vt); +---- + +=== `value` + +A holder for a sqlite values used for internal APIs. + +[source,cpp] +---- + +struct value +{ + // The value for integers in the database + typedef sqlite3_int64 int64 ; + + // The type of the value + value_type type() const; + // The subtype of the value. + int subtype() const; + + // Is the held value null + bool is_null() const; + // Is the held value is not null + explicit operator bool () const; + // Returns the value as an `integer`. + int64 get_int() const; + // Returns the value as an `double`. + double get_double() const; + // Returns the value as text, i.e. a string_view. Note that this value may be invalidated`. + cstring_ref get_text() const; + // Returns the value as blob, i.e. raw memory. Note that this value may be invalidated`. + blob_view get_blob() const; + + // Best numeric datatype of the value + value_type numeric_type() const; + + // True if the column is unchanged in an UPDATE against a virtual table. + // requires sqlite 3.32 + bool nochange() const; + // True if value originated from a bound parameter + // requires sqlite 3.31 + bool from_bind() const; + + + // Construct value from a handle. + explicit value(sqlite3_value * value_) noexcept : value_(value_) {} + + // The handle of the value. + using handle_type = sqlite3_value *; + // Returns the handle. + handle_type handle() const; + handle_type & handle(); + + // Get a value that was passed through the pointer interface. + // A value can be set as a pointer by binding/returning a unique_ptr. + // Rquires sqlite 3.20 + template<typename T> + T * get_pointer(); + +}; +---- + diff --git a/doc/reference/vtable.adoc b/doc/reference/vtable.adoc new file mode 100644 index 0000000..5063ffc --- /dev/null +++ b/doc/reference/vtable.adoc @@ -0,0 +1,275 @@ +== `sqlite/vtable.hpp` + +Please read the <<vtables, virtual tables section>> for a more detailed explanation. + +[source,cpp] +---- +namespace vtab +{ + + +// Helper type to set a function through the xFindFunction callback +struct function_setter +{ + /** Set the function + * + * The function can either take a single argument, a `span<sqlite::value, N>` + * for scalar functions, + * or a `context<Args...>` as first, and the span as second for aggegrate functions. + * + */ + template<typename Func> + void set(Func & func); + template<typename ... Args, std::size_t Extent> + void set(void(* ptr)(context<Args...>, span<value, Extent>)) noexcept; + template<typename T, typename ... Args, std::size_t Extent> + void set(T(* ptr)(context<Args...>, span<value, Extent>)); + + template<std::size_t Extent> + void set(void(* ptr)(span<value, Extent>)); + template<typename T, std::size_t Extent> + void set(T(* ptr)(span<value, Extent>)); +}; + + +// requires Sqlite 3.38 +// Utility function that can be used in `xFilter` for the `in` operator. <1> + +struct in +{ + struct iterator + { + iterator() = default; + + iterator & operator++(); + iterator operator++(int); + + const value & operator*() const; + + const value * operator->() const; + + bool operator==(const iterator& other) const; + bool operator!=(const iterator& other) const; + }; + + // Returns a forward iterator to the `in` sequence for an `in` constraint pointing to the begin. + iterator begin(); + // Returns a forward iterator to the `in` sequence for an `in` constraint pointing to the end. + iterator end(); +explicit in(sqlite::value out); +}; + + + +// index info used by the find_index function <2> +struct index_info +{ + // Returns constraints of the index. + span<const sqlite3_index_info::sqlite3_index_constraint> constraints() const; + + // Returns ordering of the index. + span<const sqlite3_index_info::sqlite3_index_orderby> order_by() const; + + span<sqlite3_index_info::sqlite3_index_constraint_usage> usage(); + + + sqlite3_index_info::sqlite3_index_constraint_usage & usage_of( + const sqlite3_index_info::sqlite3_index_constraint & info); + + // Receive the collation for the contrainst of the position. requires 3.22 + const char * collation(std::size_t idx) const; + + int on_conflict() const; + + // Returns true if the constraint is distinct. requires sqlite 3.38 + bool distinct() const; + + // Requires sqlite 3.38 + value * rhs_value(std::size_t idx) const; + + void set_already_ordered(); + void set_estimated_cost(double cost); + // requires sqlite 3.8.2 + void set_estimated_rows(sqlite3_int64 rows); + // requires sqlite 3.9 + void set_index_scan_flags(int flags); + // requires sqlite 3.10 + std::bitset<64u> columns_used(); + + void set_index(int value); + void set_index_string(char * str, bool take_ownership = true); + + sqlite3_index_info * info() const; + sqlite3 * db() const; +}; + + +struct module_config +{ + // Can be used to set SQLITE_VTAB_INNOCUOUS. Requires sqlite 3.31 + void set_innocuous(); + // Can be used to set SQLITE_VTAB_DIRECTONLY. Requires sqlite 3.31 + void set_directonly() {sqlite3_vtab_config(db_, SQLITE_VTAB_DIRECTONLY);} + + + // Can be used to set SQLITE_VTAB_CONSTRAINT_SUPPORT + void set_constraint_support(bool enabled = false); +}; + +template<typename Table> +struct module +{ + using table_type = Table; + + // Creates the instance + // The instance_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab. + // instance_type must have a member `declaration` that returns a `const char *` for the declaration. + virtual result<table_type> create(sqlite::connection db, int argc, const char * const argv[]) = 0; + + // Create a table + // The table_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab. + // table_type must have a member `declaration` that returns a `const char *` for the declaration. + virtual result<table_type> connect(sqlite::connection db, int argc, const char * const argv[]) = 0; +}; + +template<typename Table> +struct eponymous_module +{ + using table_type = Table; + + // Creates the instance + // The instance_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab. + // instance_type must have a member `declaration` that returns a `const char *` for the declaration. + virtual result<table_type> connect(sqlite::connection db, int argc, const char * const argv[]) = 0; + + eponymous_module(bool eponymous_only = false); + + bool eponymous_only() const;' + protected: + bool eponymous_only_{false}; + +}; + + +// The basis for vtable +template<typename Cursor> +struct table : protected sqlite3_vtab +{ + using cursor_type = Cursor; + + virtual result<void> config(module_config &); + + // The Table declaration to be used with sqlite3_declare_vtab + virtual const char *declaration() = 0; + + // Destroy the storage = this function needs to be present for non eponymous tables + virtual result<void> destroy(); + + // Tell sqlite how to communicate with the table. + // Optional, this library will fill in a default function that leaves comparisons to sqlite. + virtual result<void> best_index(index_info & /*info*/); + + // Start a search on the table. + // The cursor_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab_cursor. + virtual result<cursor_type> open() = 0; + + // Get the connection of the vtable + sqlite::connection connection() const; + + table(const sqlite::connection & conn); +}; + + +// Cursor needs the following member. +template<typename ColumnType = void> +struct cursor : protected sqlite3_vtab_cursor +{ + using column_type = ColumnType; + + // Apply a filter to the cursor. Required when best_index is implemented. + virtual result<void> filter( + int /*index*/, const char * /*index_data*/, + boost::span<sqlite::value> /*values*/) + { + return {system::in_place_error, SQLITE_OK}; + } + + // Returns the next row. + virtual result<void> next() = 0; + + // Check if the cursor is and the end + virtual bool eof() = 0; + + // Returns the result of a value. It will use the set_result functionality to create a an sqlite function. <3> + virtual result<column_type> column(int idx, bool no_change) = 0; + // Returns the id of the current row + virtual result<sqlite3_int64> row_id() = 0; + + // Get the table the cursor is pointing to. + vtab::table<cursor> & table(); + const vtab::table<cursor> & table() const; +}; + + +// Group of functions for modifications. The table must inherit this to be modifiable. +struct modifiable +{ + virtual result<void> delete_(sqlite::value key) = 0; + // Insert a new row + virtual result<sqlite_int64> insert(sqlite::value key, span<sqlite::value> values, int on_conflict) = 0; + // Update the row + virtual result<sqlite_int64> update(sqlite::value old_key, sqlite::value new_key, span<sqlite::value> values, int on_conflict) = 0; +}; + +// Group of functions to support transactions. The table must inherit this to support transactions. +struct transaction +{ + // Begin a tranasction + virtual result<void> begin() = 0; + // synchronize the state + virtual result<void> sync() = 0; + // commit the transaction + virtual result<void> commit() = 0; + // rollback the transaction + virtual result<void> rollback() = 0; +}; + +// Base class to enable function overriding See `xFindFunction`. +struct overload_functions +{ + virtual result<void> find_function( + function_setter fs, + int arg, const char * name) = 0; // <4> +}; + +// Support for recursive transactions. Requires sqlite 3.7.7 +struct recursive_transaction +{ + // Save the current state with to `i` + virtual result<void> savepoint(int i) = 0; + // Release all saves states down to `i` + virtual result<void> release(int i) = 0; + // Roll the transaction back to `i`. + virtual result<void> rollback_to(int i) = 0; +}; + +/** Register a vtable <5> + Returns a reference to the module as stored in the database. It's lifetime is managed by the database. +*/ +template<typename T> +auto create_module(connection & conn, + cstring_ref name, + T && module, + system::error_code & ec, + error_info & ei) -> typename std::decay<T>::type &; +template<typename T> +auto create_module(connection & conn, + const char * name, + T && module) -> typename std::decay<T>::type &; +---- +<1> See https://www.sqlite.org/capi3ref.html#sqlite3_vtab_in_first[vtab_in_first] +<2> See https://www.sqlite.org/vtab.html#xbestindex[best_index] +<3> See https://www.sqlite.org/c3ref/vtab_nochange.html[vtab_no_change] +<4> See https://www.sqlite.org/vtab.html#xfindfunction[find_function] +<5> See https://www.sqlite.org/vtab.html[vtab] + |