Sqlcx.Statement (sqlcx v2.0.0-rc.1) View Source

Provides an interface for working with SQLite prepared statements.

Care should be taken when using prepared statements directly – they are not immutable objects like most things in Elixir. Statements have internal states in sqlite3/sqlcipher, such as bound arguments and returned rows. For instance, if you call fetch_one/2 multiple times, the next row will be returned each time, not the same one for all calls. Thus, sharing a statement between different processes can cause problems if the processes accidentally interleave operations on the statement. It's a good idea to create different statements per process, or to wrap the statements up in a GenServer to prevent interleaving operations.

See bind/3 for allowed parameter formats.

Example

iex> {:ok, db} = Sqlcx.open(":memory:")
iex> Sqlcx.query(db, "CREATE TABLE data (id, name);")
{:ok, []}
iex> {:ok, statement} = Sqlcx.Statement.prepare(db, "INSERT INTO data VALUES (?, ?);")
iex> Sqlcx.Statement.bind(statement, [1, "hello"])
iex> Sqlcx.Statement.exec(statement)
:ok
iex> {:ok, statement} = Sqlcx.Statement.prepare(db, "SELECT * FROM data;")
iex> Sqlcx.Statement.fetch_all(statement, db_timeout: 1_000)
{:ok, [[1, "hello"]]}
iex> Sqlcx.close(db)
:ok

RETURNING Clause Support

Since version 3.35.0, SQLite natively support the RETURNING extension to INSERT, DELETE, and UPDATE commands required by Ecto (see https://www.sqlite.org/lang_returning.html). Thus, we were able to drop the previous workaround.

Link to this section Summary

Functions

Binds values to a prepared statement.

Returns a {:"$blob", data} tuple for use with bind/3

Runs a statement that returns no results.

Same as exec/2 but raises a Sqlcx.Statement.ExecError on error.

Fetches all rows using a statement.

Same as fetch_all/2 but raises a Sqlcx.Statement.FetchError on error.

Fetches a single row using a statement. See fetch_all/2 for parameters.

Same as fetch_one/2 but raises a Sqlcx.Statement.FetchError on error.

Prepare a Sqlcx.Statement

Same as prepare/3 but raises a Sqlcx.Statement.PrepareError on error.

Reset the prepared statement back to its initial state.

Same as reset/3 but raises an Sqlcx.Statement.ResetError on error.

Link to this section Types

Specs

bind_value() :: sql_value() | {pos_integer() | atom(), sql_value()}

Specs

exec_options() :: [{:db_timeout, timeout()}]
Link to this type

fetch_options(collection)

View Source

Specs

fetch_options(collection) :: [
  db_timeout: timeout(),
  db_chunk_size: pos_integer(),
  into: collection,
  atomic_names: boolean()
]

Specs

prepare_options() :: [{:db_timeout, timeout()}]

Specs

sql_value() :: number() | nil | iodata() | {:"$blob", iodata()}

Specs

statement()

Specs

t() :: %Sqlcx.Statement{
  column_names: nil | [String.t()],
  column_types: nil | [String.t()],
  database: Sqlcx.connection(),
  statement: statement()
}

Link to this section Functions

Link to this function

bind(statement, params, opts \\ [])

View Source

Specs

bind(t(), [bind_value()], [{:db_timeout, timeout()}]) ::
  {:ok, t()} | Sqlcx.error()

Binds values to a prepared statement.

All forms of parameters in SQL statements supported by sqlite3 are supported (see also sqlite3 docs):

  • ?: Unnamed/anonymous parameters (these will implicitly be assigned a number that is the previously largest assigned number + 1; numbering begins at 1),
  • ?NNN, where 1 ≤ NNN ≤ 32766: Numbered parameters, and
  • :AAA, where AAA is an alphanumeric identifier (e.g., :my_param_value). These will internally be assigned a number similarly to anonymous parameters, so do not mix named and numbered parameters or you will probably get unexpected results.

    NB: Sqlite3 also supports the forms @AAA and $AAA but since the initial character (@/$) is part of the name, you would actually need to pass {:"@name", value} or {:"$name", value}. {:name, value} is automatically interpreted as {:":name", value}, so the :AAA form should be preferred. Do not use $blob as a parameter name since {:"$blob", _} tuples will be interpreted as the sqlite BLOB datatype. Do not use non-ASCII characters in parameter names.

Anonymous parameters of the form ? are discouraged; prefer named or numbered parameters.

Values

All sqlite3 data types are supported. The following values can be bound to a prepared statement:

  • nil (aka :nil), which will be transformed to SQL NULL
  • Integers (integer/0)
  • Floats (float/0)
  • Strings (String.t/0): UTF-8-encoded strings of any length
  • Blobs (binaries, binary/0): Use {:"$blob", <<...>>} (or blob/1)

Note that sqlite3 does not have a boolean data type. Use integers 0 and 1. Values are translated between Elixir and sqlite3 data types when binding or fetching. Trying to bind any other types, such as atoms or booleans, will result in an error. Also not that sqlite3 is dynamically typed, so a column may contain values of different types (and the declared column type doesn't actually matter).

See also :esqlcipher.bind/3

Function parameters

bind/3 accepts the following arguments:

  • statement - The statement to bind values into.
  • values - A list of values to bind to the statement. The list can contain either anonymous values (e.g., [42, "foo", nil]) or key–value tuples with numbers (e.g., [{1, 42}, {5, "foo"}]) or atoms as keys (e.g., [{:foo, 42}, {:bar, "baz"}], which is of course equivalent to [foo: 42, bar: "baz"]). These keys correspond to anonymous, numbered, or named parameters in SQL statements. These three types can but should not be mixed. If the values list contains anonymous values without keys, these are internally numbered similarly to the internal sqlite3 parameter numbering scheme described above (i.e., starting at 1).

    If a parameter from the SQL statement is missing in the value list, it will be silently bound as NULL.

Also accepts the following keyword option:

  • db_timeout – timeout in ms for the bind command to run. Defaults to 5000, or the :db_timeout application config value.

Returns

  • {:ok, statement} on success
  • {:error, {type, reason}} for any errors (see Sqlcx.error/0).

Unlike most Elixir objects, Sqlite3 statements are not immutable but have a state. The statement that this function returns is the exact same object as the statement parameter you pass as the first argument.

Link to this function

bind!(statement, params, opts \\ [])

View Source

Specs

bind!(t(), [bind_value()], [{:db_timeout, timeout()}]) :: t()

Same as bind/3 but raises a Sqlcx.Statement.BindError on error.

Returns the statement otherwise.

Specs

blob(binary()) :: {:"$blob", binary()}

Returns a {:"$blob", data} tuple for use with bind/3

Link to this function

exec(statement, opts \\ [])

View Source

Specs

exec(t(), exec_options()) :: :ok | Sqlcx.error()

Runs a statement that returns no results.

Should be called after the statement has been bound.

Parameters

  • statement - The statement to run.

Also accepts the following keyword options:

  • db_timeout - The time in ms allowed for the statement to run. Defaults to 5000, or the :db_timeout value in application config.

Returns

  • :ok
  • {:error, {type, reason}}
Link to this function

exec!(statement, opts \\ [])

View Source

Specs

exec!(t(), exec_options()) :: :ok

Same as exec/2 but raises a Sqlcx.Statement.ExecError on error.

Returns :ok otherwise.

Link to this function

fetch_all(statement, opts \\ [])

View Source

Specs

fetch_all(t(), fetch_options(collection)) :: {:ok, [collection]} | Sqlcx.error()
when collection: Collectable.t()
fetch_all(t(), fetch_options(nil)) :: {:ok, [[sql_value()]]} | Sqlcx.error()

Fetches all rows using a statement.

Should be called after the statement has been bound.

Parameters

  • statement - The statement to run.

Also accepts the following keyword options:

  • into - The collection to put each row into. If provided, values are ziped with the column names, so into: %{} produces a Map (note that column names are Strings, not atoms). Defaults to nil which simply outputs each row as a list of values without any column names.
  • atomic_names - Whether column names should be output as atoms instead of strings. Only has an effect when into is also given since otherwise no column names are output at all. If true, the output might look like [%{foo: 1, bar: 2}] instead of [%{"foo" => 1, "bar" => 2}] (with into: %{}). Defaults to false.
  • db_timeout - The time in ms allowed for each sqlite3 command to run. Defaults to 5000 ms, or the :db_timeout value in application config. Note that the actual timeout may be many times larger because the timeout is applied to each internal fetch call individually.
  • db_chunk_size - The internal bulk size (rows are fetched in bulks with each individual fetch call having a timeout of db_timeout). Defaults to 5000, or the :db_chunk_size value in the application config.

Returns

  • {:ok, results} on success
  • {:error, {type, reason}} for any errors (see Sqlcx.error/0).
Link to this function

fetch_all!(statement, opts \\ [])

View Source

Specs

fetch_all!(t(), fetch_options(collection)) :: [collection]
when collection: Collectable.t()
fetch_all!(t(), fetch_options(nil)) :: [[sql_value()]]

Same as fetch_all/2 but raises a Sqlcx.Statement.FetchError on error.

Returns the results otherwise.

Link to this function

fetch_one(statement, opts \\ [])

View Source

Specs

fetch_one(t(), fetch_options(collection)) :: {:ok, collection}
when collection: Collectable.t()

Fetches a single row using a statement. See fetch_all/2 for parameters.

Note that statements are not immutable objects! fetch_one will return subsequent rows if called on the same statement multiple times.

Link to this function

fetch_one!(statement, opts \\ [])

View Source

Specs

fetch_one!(t(), fetch_options(collection)) :: [collection]
when collection: Collectable.t()
fetch_one!(t(), fetch_options(nil)) :: [[sql_value()]]

Same as fetch_one/2 but raises a Sqlcx.Statement.FetchError on error.

Returns the result otherwise.

Link to this function

prepare(db, sql, opts \\ [])

View Source

Specs

prepare(Sqlcx.connection(), String.t(), prepare_options()) ::
  {:ok, t()} | Sqlcx.error()

Prepare a Sqlcx.Statement

Parameters

  • db - The database to prepare the statement for.
  • sql - The SQL of the statement to prepare.

Also accepts the following keyword options:

  • db_timeout – timeout in ms for the esqlcipher commands involved in preparing the statement and binding values to it. Defaults to 5000, or the :db_timeout application config value.

Returns

  • {:ok, statement} on success
  • {:error, {type, reason}} for any errors (see t:Sqlcx.error()).
Link to this function

prepare!(db, sql, opts \\ [])

View Source

Specs

prepare!(Sqlcx.connection(), String.t(), prepare_options()) :: t()

Same as prepare/3 but raises a Sqlcx.Statement.PrepareError on error.

Returns a new statement otherwise.

Link to this function

prepare_bind(db, sql, params \\ [], opts \\ [])

View Source

Specs

prepare_bind(Sqlcx.connection(), String.t(), [bind_value()], prepare_options()) ::
  {:ok, t()} | Sqlcx.error()

prepare/3 an SQL statement and bind/3 values to it

Link to this function

prepare_bind!(db, sql, params \\ [], opts \\ [])

View Source

Specs

prepare_bind!(Sqlcx.connection(), String.t(), [bind_value()], prepare_options()) ::
  t()

Same as prepare_bind/4 but raises an Sqlcx.Statement.PrepareError or Sqlcx.Statement.BindError on error.

Specs

reset(t()) :: {:ok, t()} | Sqlcx.error()
reset(t()) :: {:ok, t()} | Sqlcx.error()

Reset the prepared statement back to its initial state.

Once the statement has been reset, you can run it once more. By default, any values bound to the statement will be retained. Set clear_bindings' totrue' to change this.

Parameters

  • clear_bindings - Whether to clear values bound to the statement

Also accepts the following keyword options:

  • db_timeout – timeout in ms for the reset command to run. Defaults to 5000, or the :db_timeout application config value.

Returns

  • {:ok, statement} on success
  • {:error, {type, reason}} for any errors (see Sqlcx.error/0).
Link to this function

reset(statement, clear_bindings)

View Source

Specs

reset(t(), boolean() | [{:db_timeout, timeout()}]) :: {:ok, t()} | Sqlcx.error()
reset(t(), boolean() | [{:db_timeout, timeout()}]) :: {:ok, t()} | Sqlcx.error()
Link to this function

reset(statement, clear_bindings, opts)

View Source

Specs

reset(t(), boolean(), [{:db_timeout, timeout()}]) :: {:ok, t()} | Sqlcx.error()
reset(t(), boolean(), [{:db_timeout, timeout()}]) :: {:ok, t()} | Sqlcx.error()

Same as reset/3 but raises an Sqlcx.Statement.ResetError on error.

Link to this function

reset!(statement, clear_bindings)

View Source
Link to this function

reset!(statement, clear_bindings, opts)

View Source