import 'package:meta/meta.dart'; import 'exception.dart'; import 'result_set.dart'; /// A prepared statement. /// /// {@category common} abstract class CommonPreparedStatement { /// The SQL statement backing this prepared statement. String get sql; /// Returns the amount of parameters in this prepared statement. int get parameterCount; /// Returns whether this statement makes no direct changes to the contents of /// the database file. /// /// See also: https://www.sqlite.org/c3ref/stmt_readonly.html bool get isReadOnly; /// Whether this statement is either an `EXPLAIN` or an `EXPLAIN QUERY PLAN` /// statement. /// /// This uses `sqlite3_stmt_isexplain`, which is documented here: /// https://www.sqlite.org/c3ref/stmt_isexplain.html bool get isExplain; /// {@template pkg_sqlite3_stmt_execute} /// Executes this statement, ignoring result rows if there are any. /// /// If the [parameters] list does not match the amount of parameters in the /// original SQL statement ([parameterCount]), an [ArgumentError] will be /// thrown. /// See [StatementParameters] for a list of types supported by this library. /// If sqlite3 reports an error while running this statement, a /// [SqliteException] will be thrown. /// /// To run a statement and also obtaining returned rows, use [selectWith] or /// [iterateWith]. It is safe to call these methods on statements that aren't /// `SELECT` statements (such as writes with a `RETURNING` clause) too. /// {@endtemplate} void executeWith(StatementParameters parameters); /// {@template pkg_sqlite3_stmt_select} /// Selects all rows into a [ResultSet]. /// /// If the [parameters] list does not match the amount of parameters in the /// original SQL statement ([parameterCount]), an [ArgumentError] will be /// thrown. /// See [StatementParameters] for a list of types supported by this library. /// If sqlite3 reports an error while running this statement, a /// [SqliteException] will be thrown. /// /// This statement doesn't have to be a `SELECT` statement for [selectWith] to /// be useful - writes with `RETURNING` clauses also return rows which can /// be fetched via [selectWith]. /// {@endtemplate} ResultSet selectWith(StatementParameters parameters); /// {@template pkg_sqlite3_stmt_iterate} /// Starts selecting rows by running this prepared statement with the given /// [parameters]. /// /// If the [parameters] list does not match the amount of parameters in the /// original SQL statement ([parameterCount]), an [ArgumentError] will be /// thrown. /// See [StatementParameters] for a list of types supported by this library. /// /// If sqlite3 reports an error while running this statement, it will be /// thrown by a call to [Iterator.moveNext]. /// /// The iterator returned here will become invalid with the next call to a /// method that runs this statement ([execute], [executeWith], [select], /// [selectWith], [selectCursor], [iterateWith]). /// {@endtemplate} IteratingCursor iterateWith(StatementParameters parameters); /// {@macro pkg_sqlite3_stmt_execute} void execute([List parameters = const []]) { return executeWith(StatementParameters(parameters)); } /// {@macro pkg_sqlite3_stmt_execute} /// /// Unlike [execute], which binds parameters by their index, [executeMap] /// binds parameters by their name. /// For instance, a SQL query `SELECT :foo, @bar;` has two named parameters /// (`:foo` and `@bar`) that can occur as keys in [parameters]. @Deprecated('Use executeWith(StatementParameters.named) instead') void executeMap(Map parameters) { return executeWith(StatementParameters.named(parameters)); } /// {@macro pkg_sqlite3_stmt_select} ResultSet select([List parameters = const []]) { return selectWith(StatementParameters(parameters)); } /// {@macro pkg_sqlite3_stmt_select} /// /// Similar to [executeMap], parameters are bound by their name instead of /// their index. @Deprecated('Use selectWith(StatementParameters.named) instead') ResultSet selectMap(Map parameters) { return selectWith(StatementParameters.named(parameters)); } /// {@macro pkg_sqlite3_stmt_iterate} IteratingCursor selectCursor([List parameters = const []]) { return iterateWith(StatementParameters(parameters)); } /// Resets this statement to be excuted again. /// /// You typically don't need to call [reset] manually as it is called for you /// internally when [select] or [execute] called multiple times. /// However, [reset] removes all active cursors on this statement, which can /// be useful if a statement is re-used across longer periods of time. /// /// See also: /// - [sqlite3_reset]: https://www.sqlite.org/c3ref/reset.html void reset(); /// Disposes this statement and releases associated memory. @Deprecated('Use close() instead') void dispose(); /// Closes this prepared statement. /// /// On native platforms, a native finalizer will also close statements /// automatically. On the web, finalizers are less reliable. For this reason, /// closing statements explicitly is still recommended. /// /// See also: /// - `sqlite3_finalize`: https://www.sqlite.org/c3ref/finalize.html void close(); } /// A set of values that can be used to bind [parameters] in a SQL query. /// /// Parameters are placeholder values in SQL that can safely be bound to values /// later without a risk of SQL injection attacks. /// Depending on the syntax used to declare a parameter, it may make sense to /// pass parameters by their index or by name. The different constructors of /// this class can be used to control how parameters are passed. /// /// This package supports binding [int], [BigInt], [String], [double], `List` /// and `null` values by default. For custom values, such as those using the /// [pointer-binding](https://www.sqlite.org/bindptr.html) interface provided by /// `sqlite3`, [CustomStatementParameter] can be implemented and passed as a /// parameter as well. /// /// {@category common} /// /// [parameters]: https://www.sqlite.org/lang_expr.html#varparam sealed class StatementParameters { /// Convenience factory to use when no parameters should be passed. const factory StatementParameters.empty() = IndexedParameters.empty; /// Passes parameters by their variable number. const factory StatementParameters(List parameters) = IndexedParameters; /// Passes variables by their name. /// /// For instance, the statement `SELECT * FROM users WHERE id = :id` could be /// bound with `StatementParameters.named({':a': 42})`. const factory StatementParameters.named(Map parameters) = NamedParameters; /// Passes variables by invoking a callback responsible for using a native /// statement handle to bind parameters. /// /// Other constructors validate parameters, making them harder to misuse. In /// special cases where that doesn't work, this constructor can be used to /// indicate that you are fully responsible for binding parameters and don't /// need any validation checks. const factory StatementParameters.bindCustom( void Function(CommonPreparedStatement stmt) bind, ) = CustomParameters; } @internal class IndexedParameters implements StatementParameters { final List parameters; const IndexedParameters(this.parameters); const IndexedParameters.empty() : parameters = const []; } @internal class NamedParameters implements StatementParameters { final Map parameters; const NamedParameters(this.parameters); } @internal class CustomParameters implements StatementParameters { final void Function(CommonPreparedStatement) bind; const CustomParameters(this.bind); } /// A parameter passed to prepared statements that decides how it gets mapped to /// SQL in [applyTo]. /// /// Implementing and using this class is rarely useful, as this package supports /// standard types supported by `sqlite3` directly. /// For advanced APIs, such as the [pointer-passing](https://www.sqlite.org/bindptr.html) /// interface that can't directly be represented in a high-level Dart interface, /// this class can be used to manually bind a value. /// /// {@category common} abstract interface class CustomStatementParameter { /// Applies this custom parameter to the [statement] at the variable index /// [index]. /// /// On native platforms, an implementation can safely cast the `statement` to /// a `PreparedStatement` (from `package:sqlite3/sqlite3.dart`) and use the /// `PreparedStatement.handle` to call `sqlite3_bind_*` manually. void applyTo(CommonPreparedStatement statement, int index); }