sqlite::command owns a prepared statement (sqlite3_stmt*) and provides a uniform binding interface (bind() or operator %) for scalar, blob, optional, duration, and enum types. It is the base for higher-level helpers such as sqlite::execute and sqlite::query.

The helper struct null_type and the global sqlite::nil object are used to bind SQL NULLs via the streaming operator.

Use cases

  • Designed for reuse—call clear() (or rely on step_once() doing it) to keep the statement cached in the parent connection.
  • Parameter indexes are 1-based like the native C API. The streaming operator keeps track of the next index so you can write expressive chaining code.
  • Throws database_exception_code on prepare/bind/step errors and annotates messages with the SQL text for easier debugging.

Example usage

Variadic operator() binds whatever you pass and then executes, while still respecting any parameters you bound earlier via operator%.

sqlite::command upsert{
    db,
    "INSERT INTO notes(id, text, edited_at) VALUES(?, ?, ?) "
    "ON CONFLICT(id) DO UPDATE SET text = excluded.text, edited_at = excluded.edited_at"
};

upsert % 42; // bind the id once
upsert(std::string_view{"Ship It"}, std::chrono::system_clock::now());

bool has_row = upsert.step_once(); // true only when a row was returned (rare for INSERT)

if (!has_row) {
    upsert.clear(); // optional: prepares for the next round with new parameters
}
sqlite::command audit{
    db,
    "INSERT INTO audit(entry, created_at) VALUES(?, ?)"
};

// All parameters bound via operator() in one go.
audit("created user", std::chrono::system_clock::now());
sqlite::command insert_named{
    db,
    "INSERT INTO metrics(name, value, created_at) VALUES(:name, @val, $created)"
};

insert_named % sqlite::named(":name", "uptime")
             % sqlite::named("@val", 42)
             % sqlite::named("$created", std::chrono::system_clock::now());

insert_named(sqlite::named(":name", "load"),
             sqlite::named("@val", 0.97),
             sqlite::named("$created", std::chrono::system_clock::now()));

If this pattern is only doing side-effect statements, swap it for sqlite::execute and skip the manual lifetime juggling.

Lifecycle

MemberDescription
command(connection& con, std::string const& sql) Compiles sql against con and prepares for binding.
command(command const&)=delete, command& operator=(command const&)=delete Statements cannot be copied because they own a unique sqlite3_stmt*.
virtual ~command() Finalizes the prepared statement.
void clear() Resets bindings so the statement can be reused.
bool step_once(), bool operator()() Executes the statement once. Returns true if SQLite signalled SQLITE_ROW (e.g., for UPDATE triggers).

Binding API

MemberDescription
void bind(int idx)Binds NULL to the 1-based parameter index.
void bind(int idx, int value)Binds a 32-bit integer.
void bind(int idx, std::int64_t value)Binds a 64-bit integer.
void bind(int idx, double value)Binds a floating-point value.
template<typename Text> void bind(int idx, Text&& text)Binds UTF-8 text from any type convertible to std::string_view.
void bind(int idx, void const* buf, size_t size)Binds a raw blob pointer.
void bind(int idx, std::vector<unsigned char> const&)Binds a blob from a byte vector.
void bind(int idx, std::span<const unsigned char>)Binds a blob from a span of bytes.
void bind(int idx, std::span<const std::byte>)Binds a blob from a span of std::byte.
template<typename Value> void bind_value(int idx, Value&&)Dispatches to the correct overload for optionals, durations, time points, enums, integers, floating point numbers, strings, or blobs.
template<typename Value> void bind(int idx, Value&&)Calls bind_value when Value requires the generic path.

Prefer named placeholders? wrap values with sqlite::named(":placeholder", value) and chain them through operator% or operator(); vsqlite++ resolves the parameter index under the hood.

Streaming operator (%)

Each overload increments an internal 1-based counter so you can write cmd % 1 % "text" % sqlite::nil; to bind arguments sequentially.

MemberDescription
command& operator%(null_type const&)Binds NULL.
command& operator%(int)Binds 32-bit integer.
command& operator%(std::int64_t)Binds 64-bit integer.
command& operator%(double)Binds floating-point value.
template<typename Text> command& operator%(Text&&)Binds text.
command& operator%(std::vector<unsigned char> const&)Binds blob.
command& operator%(std::span<const unsigned char>), command& operator%(std::span<const std::byte>)Binds blob spans.
template<typename Value> command& operator%(Value&&)Uses bind_value for optionals, durations, enums, etc.

Protected helpers

MemberDescription
void access_check()Validates that the underlying statement is alive; throws on misuse.
bool step()Executes a single sqlite3_step call.
sqlite3* get_handle()Returns the raw database handle for advanced use cases.