[samples/ffi] Generate sqlite3 bindings with package:ffigen

Cleanup handwritten code now that we do have a generator.

We don't need package:ffigen in the DEPS, because we don't run it
anywhere in the SDK build or tests. It is a dev dependency and the
generated file is checked in.

Change-Id: I02784ca5895b4e878a0fed696a34c546a71c2702
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152141
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Jonas Jensen <jonasfj@google.com>
This commit is contained in:
Daco Harkes 2020-08-11 09:15:59 +00:00 committed by commit-bot@chromium.org
parent 13de7e7560
commit af4940fc57
11 changed files with 2081 additions and 759 deletions

View file

@ -4,4 +4,4 @@
.vscode
pubspec.lock
test.db
test.db-journal
test.db-journal

View file

@ -1,394 +0,0 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:ffi";
import "package:ffi/ffi.dart";
import "../ffi/dylib_utils.dart";
import "signatures.dart";
import "types.dart";
class _SQLiteBindings {
DynamicLibrary sqlite;
/// Opening A New Database Connection
///
/// ^These routines open an SQLite database file as specified by the
/// filename argument. ^The filename argument is interpreted as UTF-8 for
/// sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
/// order for sqlite3_open16(). ^(A database connection handle is usually
/// returned in *ppDb, even if an error occurs. The only exception is that
/// if SQLite is unable to allocate memory to hold the sqlite3 object,
/// a NULL will be written into *ppDb instead of a pointer to the sqlite3
/// object.)^ ^(If the database is opened (and/or created) successfully, then
/// [SQLITE_OK] is returned. Otherwise an error code is returned.)^ ^The
/// [sqlite3_errmsg] or sqlite3_errmsg16() routines can be used to obtain
/// an English language description of the error following a failure of any
/// of the sqlite3_open() routines.
int Function(Pointer<Utf8> filename, Pointer<Pointer<Database>> databaseOut,
int flags, Pointer<Utf8> vfs) sqlite3_open_v2;
int Function(Pointer<Database> database) sqlite3_close_v2;
/// Compiling An SQL Statement
///
/// To execute an SQL query, it must first be compiled into a byte-code
/// program using one of these routines.
///
/// The first argument, "db", is a database connection obtained from a
/// prior successful call to sqlite3_open, [sqlite3_open_v2] or
/// sqlite3_open16. The database connection must not have been closed.
///
/// The second argument, "zSql", is the statement to be compiled, encoded
/// as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2()
/// interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()
/// use UTF-16.
///
/// ^If the nByte argument is less than zero, then zSql is read up to the
/// first zero terminator. ^If nByte is non-negative, then it is the maximum
/// number of bytes read from zSql. ^When nByte is non-negative, the
/// zSql string ends at either the first '\000' or '\u0000' character or
/// the nByte-th byte, whichever comes first. If the caller knows
/// that the supplied string is nul-terminated, then there is a small
/// performance advantage to be gained by passing an nByte parameter that
/// is equal to the number of bytes in the input string <i>including</i>
/// the nul-terminator bytes.
///
/// ^If pzTail is not NULL then *pzTail is made to point to the first byte
/// past the end of the first SQL statement in zSql. These routines only
/// compile the first statement in zSql, so *pzTail is left pointing to
/// what remains uncompiled.
///
/// ^*ppStmt is left pointing to a compiled prepared statement that can be
/// executed using sqlite3_step. ^If there is an error, *ppStmt is set
/// to NULL. ^If the input text contains no SQL (if the input is an empty
/// string or a comment) then *ppStmt is set to NULL.
/// The calling procedure is responsible for deleting the compiled
/// SQL statement using [sqlite3_finalize] after it has finished with it.
/// ppStmt may not be NULL.
///
/// ^On success, the sqlite3_prepare family of routines return [SQLITE_OK];
/// otherwise an error code is returned.
///
/// The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are
/// recommended for all new programs. The two older interfaces are retained
/// for backwards compatibility, but their use is discouraged.
/// ^In the "v2" interfaces, the prepared statement
/// that is returned (the sqlite3_stmt object) contains a copy of the
/// original SQL text. This causes the [sqlite3_step] interface to
/// behave differently in three ways:
int Function(
Pointer<Database> database,
Pointer<Utf8> query,
int nbytes,
Pointer<Pointer<Statement>> statementOut,
Pointer<Pointer<Utf8>> tail) sqlite3_prepare_v2;
/// Evaluate An SQL Statement
///
/// After a prepared statement has been prepared using either
/// [sqlite3_prepare_v2] or sqlite3_prepare16_v2() or one of the legacy
/// interfaces sqlite3_prepare() or sqlite3_prepare16(), this function
/// must be called one or more times to evaluate the statement.
///
/// The details of the behavior of the sqlite3_step() interface depend
/// on whether the statement was prepared using the newer "v2" interface
/// [sqlite3_prepare_v2] and sqlite3_prepare16_v2() or the older legacy
/// interface sqlite3_prepare() and sqlite3_prepare16(). The use of the
/// new "v2" interface is recommended for new applications but the legacy
/// interface will continue to be supported.
///
/// ^In the legacy interface, the return value will be either [SQLITE_BUSY],
/// [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
/// ^With the "v2" interface, any of the other [result codes] or
/// [extended result codes] might be returned as well.
///
/// ^[SQLITE_BUSY] means that the database engine was unable to acquire the
/// database locks it needs to do its job. ^If the statement is a [COMMIT]
/// or occurs outside of an explicit transaction, then you can retry the
/// statement. If the statement is not a [COMMIT] and occurs within an
/// explicit transaction then you should rollback the transaction before
/// continuing.
///
/// ^[SQLITE_DONE] means that the statement has finished executing
/// successfully. sqlite3_step() should not be called again on this virtual
/// machine without first calling [sqlite3_reset()] to reset the virtual
/// machine back to its initial state.
///
/// ^If the SQL statement being executed returns any data, then [SQLITE_ROW]
/// is returned each time a new row of data is ready for processing by the
/// caller. The values may be accessed using the [column access functions].
/// sqlite3_step() is called again to retrieve the next row of data.
///
/// ^[SQLITE_ERROR] means that a run-time error (such as a constraint
/// violation) has occurred. sqlite3_step() should not be called again on
/// the VM. More information may be found by calling [sqlite3_errmsg()].
/// ^With the legacy interface, a more specific error code (for example,
/// [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth)
/// can be obtained by calling [sqlite3_reset()] on the
/// prepared statement. ^In the "v2" interface,
/// the more specific error code is returned directly by sqlite3_step().
///
/// [SQLITE_MISUSE] means that the this routine was called inappropriately.
/// Perhaps it was called on a prepared statement that has
/// already been [sqlite3_finalize | finalized] or on one that had
/// previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could
/// be the case that the same database connection is being used by two or
/// more threads at the same moment in time.
///
/// For all versions of SQLite up to and including 3.6.23.1, a call to
/// [sqlite3_reset] was required after sqlite3_step() returned anything
/// other than [Errors.SQLITE_ROW] before any subsequent invocation of
/// sqlite3_step(). Failure to reset the prepared statement using
/// [sqlite3_reset()] would result in an [Errors.SQLITE_MISUSE] return from
/// sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
/// calling [sqlite3_reset] automatically in this circumstance rather
/// than returning [Errors.SQLITE_MISUSE]. This is not considered a
/// compatibility break because any application that ever receives an
/// [Errors.SQLITE_MISUSE] error is broken by definition. The
/// [SQLITE_OMIT_AUTORESET] compile-time option
/// can be used to restore the legacy behavior.
///
/// <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step()
/// API always returns a generic error code, [SQLITE_ERROR], following any
/// error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call
/// [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the
/// specific [error codes] that better describes the error.
/// We admit that this is a goofy design. The problem has been fixed
/// with the "v2" interface. If you prepare all of your SQL statements
/// using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead
/// of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces,
/// then the more specific [error codes] are returned directly
/// by sqlite3_step(). The use of the "v2" interface is recommended.
int Function(Pointer<Statement> statement) sqlite3_step;
/// CAPI3REF: Reset A Prepared Statement Object
///
/// The sqlite3_reset() function is called to reset a prepared statement
/// object back to its initial state, ready to be re-executed.
/// ^Any SQL statement variables that had values bound to them using
/// the sqlite3_bind_blob | sqlite3_bind_*() API retain their values.
/// Use sqlite3_clear_bindings() to reset the bindings.
///
/// ^The [sqlite3_reset] interface resets the prepared statement S
/// back to the beginning of its program.
///
/// ^If the most recent call to [sqlite3_step] for the
/// prepared statement S returned [Errors.SQLITE_ROW] or [Errors.SQLITE_DONE],
/// or if [sqlite3_step] has never before been called on S,
/// then [sqlite3_reset] returns [Errors.SQLITE_OK].
///
/// ^If the most recent call to [sqlite3_step(S)] for the
/// prepared statement S indicated an error, then
/// [sqlite3_reset] returns an appropriate [Errors].
///
/// ^The [sqlite3_reset] interface does not change the values
int Function(Pointer<Statement> statement) sqlite3_reset;
/// Destroy A Prepared Statement Object
///
/// ^The sqlite3_finalize() function is called to delete a prepared statement.
/// ^If the most recent evaluation of the statement encountered no errors
/// or if the statement is never been evaluated, then sqlite3_finalize()
/// returns SQLITE_OK. ^If the most recent evaluation of statement S failed,
/// then sqlite3_finalize(S) returns the appropriate error code or extended
/// error code.
///
/// ^The sqlite3_finalize(S) routine can be called at any point during
/// the life cycle of prepared statement S:
/// before statement S is ever evaluated, after
/// one or more calls to [sqlite3_reset], or after any call
/// to [sqlite3_step] regardless of whether or not the statement has
/// completed execution.
///
/// ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op.
///
/// The application must finalize every prepared statement in order to avoid
/// resource leaks. It is a grievous error for the application to try to use
/// a prepared statement after it has been finalized. Any use of a prepared
/// statement after it has been finalized can result in undefined and
/// undesirable behavior such as segfaults and heap corruption.
int Function(Pointer<Statement> statement) sqlite3_finalize;
/// Number Of Columns In A Result Set
///
/// ^Return the number of columns in the result set returned by the
/// prepared statement. ^This routine returns 0 if pStmt is an SQL
/// statement that does not return data (for example an [UPDATE]).
int Function(Pointer<Statement> statement) sqlite3_column_count;
/// Column Names In A Result Set
///
/// ^These routines return the name assigned to a particular column
/// in the result set of a SELECT statement. ^The sqlite3_column_name()
/// interface returns a pointer to a zero-terminated UTF-8 string
/// and sqlite3_column_name16() returns a pointer to a zero-terminated
/// UTF-16 string. ^The first parameter is the prepared statement
/// that implements the SELECT statement. ^The second parameter is the
/// column number. ^The leftmost column is number 0.
///
/// ^The returned string pointer is valid until either the prepared statement
/// is destroyed by [sqlite3_finalize] or until the statement is automatically
/// reprepared by the first call to [sqlite3_step] for a particular run
/// or until the next call to
/// sqlite3_column_name() or sqlite3_column_name16() on the same column.
///
/// ^If sqlite3_malloc() fails during the processing of either routine
/// (for example during a conversion from UTF-8 to UTF-16) then a
/// NULL pointer is returned.
///
/// ^The name of a result column is the value of the "AS" clause for
/// that column, if there is an AS clause. If there is no AS clause
/// then the name of the column is unspecified and may change from
Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_name;
/// CAPI3REF: Declared Datatype Of A Query Result
///
/// ^(The first parameter is a prepared statement.
/// If this statement is a SELECT statement and the Nth column of the
/// returned result set of that SELECT is a table column (not an
/// expression or subquery) then the declared type of the table
/// column is returned.)^ ^If the Nth column of the result set is an
/// expression or subquery, then a NULL pointer is returned.
/// ^The returned string is always UTF-8 encoded.
///
/// ^(For example, given the database schema:
///
/// CREATE TABLE t1(c1 VARIANT);
///
/// and the following statement to be compiled:
///
/// SELECT c1 + 1, c1 FROM t1;
///
/// this routine would return the string "VARIANT" for the second result
/// column (i==1), and a NULL pointer for the first result column (i==0).)^
///
/// ^SQLite uses dynamic run-time typing. ^So just because a column
/// is declared to contain a particular type does not mean that the
/// data stored in that column is of the declared type. SQLite is
/// strongly typed, but the typing is dynamic not static. ^Type
/// is associated with individual values, not with the containers
/// used to hold those values.
Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_decltype;
int Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_type;
Pointer<Value> Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_value;
double Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_double;
int Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_int;
Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
sqlite3_column_text;
/// The sqlite3_errstr() interface returns the English-language text that
/// describes the result code, as UTF-8. Memory to hold the error message
/// string is managed internally and must not be freed by the application.
Pointer<Utf8> Function(int code) sqlite3_errstr;
/// Error Codes And Messages
///
/// ^The sqlite3_errcode() interface returns the numeric [result code] or
/// [extended result code] for the most recent failed sqlite3_* API call
/// associated with a [database connection]. If a prior API call failed
/// but the most recent API call succeeded, the return value from
/// sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode()
/// interface is the same except that it always returns the
/// [extended result code] even when extended result codes are
/// disabled.
///
/// ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
/// text that describes the error, as either UTF-8 or UTF-16 respectively.
/// ^(Memory to hold the error message string is managed internally.
/// The application does not need to worry about freeing the result.
/// However, the error string might be overwritten or deallocated by
/// subsequent calls to other SQLite interface functions.)^
///
/// When the serialized [threading mode] is in use, it might be the
/// case that a second error occurs on a separate thread in between
/// the time of the first error and the call to these interfaces.
/// When that happens, the second error will be reported since these
/// interfaces always report the most recent result. To avoid
/// this, each thread can obtain exclusive use of the [database connection] D
/// by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning
/// to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after
/// all calls to the interfaces listed here are completed.
///
/// If an interface fails with SQLITE_MISUSE, that means the interface
/// was invoked incorrectly by the application. In that case, the
/// error code and message may or may not be set.
Pointer<Utf8> Function(Pointer<Database> database) sqlite3_errmsg;
_SQLiteBindings() {
sqlite = dlopenPlatformSpecific("sqlite3");
sqlite3_open_v2 = sqlite
.lookup<NativeFunction<sqlite3_open_v2_native_t>>("sqlite3_open_v2")
.asFunction();
sqlite3_close_v2 = sqlite
.lookup<NativeFunction<sqlite3_close_v2_native_t>>("sqlite3_close_v2")
.asFunction();
sqlite3_prepare_v2 = sqlite
.lookup<NativeFunction<sqlite3_prepare_v2_native_t>>(
"sqlite3_prepare_v2")
.asFunction();
sqlite3_step = sqlite
.lookup<NativeFunction<sqlite3_step_native_t>>("sqlite3_step")
.asFunction();
sqlite3_reset = sqlite
.lookup<NativeFunction<sqlite3_reset_native_t>>("sqlite3_reset")
.asFunction();
sqlite3_finalize = sqlite
.lookup<NativeFunction<sqlite3_finalize_native_t>>("sqlite3_finalize")
.asFunction();
sqlite3_errstr = sqlite
.lookup<NativeFunction<sqlite3_errstr_native_t>>("sqlite3_errstr")
.asFunction();
sqlite3_errmsg = sqlite
.lookup<NativeFunction<sqlite3_errmsg_native_t>>("sqlite3_errmsg")
.asFunction();
sqlite3_column_count = sqlite
.lookup<NativeFunction<sqlite3_column_count_native_t>>(
"sqlite3_column_count")
.asFunction();
sqlite3_column_name = sqlite
.lookup<NativeFunction<sqlite3_column_name_native_t>>(
"sqlite3_column_name")
.asFunction();
sqlite3_column_decltype = sqlite
.lookup<NativeFunction<sqlite3_column_decltype_native_t>>(
"sqlite3_column_decltype")
.asFunction();
sqlite3_column_type = sqlite
.lookup<NativeFunction<sqlite3_column_type_native_t>>(
"sqlite3_column_type")
.asFunction();
sqlite3_column_value = sqlite
.lookup<NativeFunction<sqlite3_column_value_native_t>>(
"sqlite3_column_value")
.asFunction();
sqlite3_column_double = sqlite
.lookup<NativeFunction<sqlite3_column_double_native_t>>(
"sqlite3_column_double")
.asFunction();
sqlite3_column_int = sqlite
.lookup<NativeFunction<sqlite3_column_int_native_t>>(
"sqlite3_column_int")
.asFunction();
sqlite3_column_text = sqlite
.lookup<NativeFunction<sqlite3_column_text_native_t>>(
"sqlite3_column_text")
.asFunction();
}
}
_SQLiteBindings _cachedBindings;
_SQLiteBindings get bindings => _cachedBindings ??= _SQLiteBindings();

View file

@ -1,182 +0,0 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Result Codes
///
/// Many SQLite functions return an integer result code from the set shown
/// here in order to indicates success or failure.
///
/// New error codes may be added in future versions of SQLite.
///
/// See also: SQLITE_IOERR_READ | extended result codes,
/// sqlite3_vtab_on_conflict() SQLITE_ROLLBACK | result codes.
class Errors {
/// Successful result
static const int SQLITE_OK = 0;
/// Generic error
static const int SQLITE_ERROR = 1;
/// Internal logic error in SQLite
static const int SQLITE_INTERNAL = 2;
/// Access permission denied
static const int SQLITE_PERM = 3;
/// Callback routine requested an abort
static const int SQLITE_ABORT = 4;
/// The database file is locked
static const int SQLITE_BUSY = 5;
/// A table in the database is locked
static const int SQLITE_LOCKED = 6;
/// A malloc() failed
static const int SQLITE_NOMEM = 7;
/// Attempt to write a readonly database
static const int SQLITE_READONLY = 8;
/// Operation terminated by sqlite3_interrupt()
static const int SQLITE_INTERRUPT = 9;
/// Some kind of disk I/O error occurred
static const int SQLITE_IOERR = 10;
/// The database disk image is malformed
static const int SQLITE_CORRUPT = 11;
/// Unknown opcode in sqlite3_file_control()
static const int SQLITE_NOTFOUND = 12;
/// Insertion failed because database is full
static const int SQLITE_FULL = 13;
/// Unable to open the database file
static const int SQLITE_CANTOPEN = 14;
/// Database lock protocol error
static const int SQLITE_PROTOCOL = 15;
/// Internal use only
static const int SQLITE_EMPTY = 16;
/// The database schema changed
static const int SQLITE_SCHEMA = 17;
/// String or BLOB exceeds size limit
static const int SQLITE_TOOBIG = 18;
/// Abort due to constraint violation
static const int SQLITE_CONSTRAINT = 19;
/// Data type mismatch
static const int SQLITE_MISMATCH = 20;
/// Library used incorrectly
static const int SQLITE_MISUSE = 21;
/// Uses OS features not supported on host
static const int SQLITE_NOLFS = 22;
/// Authorization denied
static const int SQLITE_AUTH = 23;
/// Not used
static const int SQLITE_FORMAT = 24;
/// 2nd parameter to sqlite3_bind out of range
static const int SQLITE_RANGE = 25;
/// File opened that is not a database file
static const int SQLITE_NOTADB = 26;
/// Notifications from sqlite3_log()
static const int SQLITE_NOTICE = 27;
/// Warnings from sqlite3_log()
static const int SQLITE_WARNING = 28;
/// sqlite3_step() has another row ready
static const int SQLITE_ROW = 100;
/// sqlite3_step() has finished executing
static const int SQLITE_DONE = 101;
}
/// Flags For File Open Operations
///
/// These bit values are intended for use in the
/// 3rd parameter to the [sqlite3_open_v2()] interface and
/// in the 4th parameter to the [sqlite3_vfs.xOpen] method.
class Flags {
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_READONLY = 0x00000001;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_READWRITE = 0x00000002;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_CREATE = 0x00000004;
/// VFS only
static const int SQLITE_OPEN_DELETEONCLOSE = 0x00000008;
/// VFS only
static const int SQLITE_OPEN_EXCLUSIVE = 0x00000010;
/// VFS only
static const int SQLITE_OPEN_AUTOPROXY = 0x00000020;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_URI = 0x00000040;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_MEMORY = 0x00000080;
/// VFS only
static const int SQLITE_OPEN_MAIN_DB = 0x00000100;
/// VFS only
static const int SQLITE_OPEN_TEMP_DB = 0x00000200;
/// VFS only
static const int SQLITE_OPEN_TRANSIENT_DB = 0x00000400;
/// VFS only
static const int SQLITE_OPEN_MAIN_JOURNAL = 0x00000800;
/// VFS only
static const int SQLITE_OPEN_TEMP_JOURNAL = 0x00001000;
/// VFS only
static const int SQLITE_OPEN_SUBJOURNAL = 0x00002000;
/// VFS only
static const int SQLITE_OPEN_MASTER_JOURNAL = 0x00004000;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_NOMUTEX = 0x00008000;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_FULLMUTEX = 0x00010000;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_SHAREDCACHE = 0x00020000;
/// Ok for sqlite3_open_v2()
static const int SQLITE_OPEN_PRIVATECACHE = 0x00040000;
/// VFS only
static const int SQLITE_OPEN_WAL = 0x00080000;
}
class Types {
static const int SQLITE_INTEGER = 1;
static const int SQLITE_FLOAT = 2;
static const int SQLITE_TEXT = 3;
static const int SQLITE_BLOB = 4;
static const int SQLITE_NULL = 5;
}

View file

@ -1,57 +0,0 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:ffi";
import "package:ffi/ffi.dart";
import "types.dart";
typedef sqlite3_open_v2_native_t = Int32 Function(Pointer<Utf8> filename,
Pointer<Pointer<Database>> ppDb, Int32 flags, Pointer<Utf8> vfs);
typedef sqlite3_close_v2_native_t = Int32 Function(Pointer<Database> database);
typedef sqlite3_prepare_v2_native_t = Int32 Function(
Pointer<Database> database,
Pointer<Utf8> query,
Int32 nbytes,
Pointer<Pointer<Statement>> statementOut,
Pointer<Pointer<Utf8>> tail);
typedef sqlite3_step_native_t = Int32 Function(Pointer<Statement> statement);
typedef sqlite3_reset_native_t = Int32 Function(Pointer<Statement> statement);
typedef sqlite3_finalize_native_t = Int32 Function(
Pointer<Statement> statement);
typedef sqlite3_errstr_native_t = Pointer<Utf8> Function(Int32 error);
typedef sqlite3_errmsg_native_t = Pointer<Utf8> Function(
Pointer<Database> database);
typedef sqlite3_column_count_native_t = Int32 Function(
Pointer<Statement> statement);
typedef sqlite3_column_name_native_t = Pointer<Utf8> Function(
Pointer<Statement> statement, Int32 columnIndex);
typedef sqlite3_column_decltype_native_t = Pointer<Utf8> Function(
Pointer<Statement> statement, Int32 columnIndex);
typedef sqlite3_column_type_native_t = Int32 Function(
Pointer<Statement> statement, Int32 columnIndex);
typedef sqlite3_column_value_native_t = Pointer<Value> Function(
Pointer<Statement> statement, Int32 columnIndex);
typedef sqlite3_column_double_native_t = Double Function(
Pointer<Statement> statement, Int32 columnIndex);
typedef sqlite3_column_int_native_t = Int32 Function(
Pointer<Statement> statement, Int32 columnIndex);
typedef sqlite3_column_text_native_t = Pointer<Utf8> Function(
Pointer<Statement> statement, Int32 columnIndex);

View file

@ -1,75 +0,0 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:ffi";
/// Database Connection Handle
///
/// Each open SQLite database is represented by a pointer to an instance of
/// the opaque structure named "sqlite3". It is useful to think of an sqlite3
/// pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
/// [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
/// is its destructor. There are many other interfaces (such as
/// [sqlite3_prepare_v2()], [sqlite3_create_function()], and
/// [sqlite3_busy_timeout()] to name but three) that are methods on an
class Database extends Struct {}
/// SQL Statement Object
///
/// An instance of this object represents a single SQL statement.
/// This object is variously known as a "prepared statement" or a
/// "compiled SQL statement" or simply as a "statement".
///
/// The life of a statement object goes something like this:
///
/// <ol>
/// <li> Create the object using [sqlite3_prepare_v2()] or a related
/// function.
/// <li> Bind values to [host parameters] using the sqlite3_bind_*()
/// interfaces.
/// <li> Run the SQL by calling [sqlite3_step()] one or more times.
/// <li> Reset the statement using [sqlite3_reset()] then go back
/// to step 2. Do this zero or more times.
/// <li> Destroy the object using [sqlite3_finalize()].
/// </ol>
///
/// Refer to documentation on individual methods above for additional
/// information.
class Statement extends Struct {}
/// Dynamically Typed Value Object
///
/// SQLite uses the sqlite3_value object to represent all values
/// that can be stored in a database table. SQLite uses dynamic typing
/// for the values it stores. ^Values stored in sqlite3_value objects
/// can be integers, floating point values, strings, BLOBs, or NULL.
///
/// An sqlite3_value object may be either "protected" or "unprotected".
/// Some interfaces require a protected sqlite3_value. Other interfaces
/// will accept either a protected or an unprotected sqlite3_value.
/// Every interface that accepts sqlite3_value arguments specifies
/// whether or not it requires a protected sqlite3_value.
///
/// The terms "protected" and "unprotected" refer to whether or not
/// a mutex is held. An internal mutex is held for a protected
/// sqlite3_value object but no mutex is held for an unprotected
/// sqlite3_value object. If SQLite is compiled to be single-threaded
/// (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
/// or if SQLite is run in one of reduced mutex modes
/// [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
/// then there is no distinction between protected and unprotected
/// sqlite3_value objects and they can be used interchangeably. However,
/// for maximum code portability it is recommended that applications
/// still make the distinction between protected and unprotected
/// sqlite3_value objects even when not strictly required.
///
/// ^The sqlite3_value objects that are passed as parameters into the
/// implementation of [application-defined SQL functions] are protected.
/// ^The sqlite3_value object returned by
/// [sqlite3_column_value()] is unprotected.
/// Unprotected sqlite3_value objects may only be used with
/// [sqlite3_result_value()] and [sqlite3_bind_value()].
/// The [sqlite3_value_blob | sqlite3_value_type()] family of
/// interfaces require protected sqlite3_value objects.
class Value extends Struct {}

View file

@ -7,13 +7,11 @@ import "dart:ffi";
import "package:ffi/ffi.dart";
import "bindings/bindings.dart";
import "bindings/types.dart" as types;
import "bindings/types.dart" hide Database;
import "bindings/constants.dart";
import "third_party/sqlite/sqlite3_bindings_generated.dart" as bindings;
import "collections/closable_iterator.dart";
import "ffi/dylib_utils.dart";
final sqlite = bindings.SQLite(dlopenPlatformSpecific("sqlite3"));
/// [Database] represents an open connection to a SQLite database.
///
@ -21,26 +19,27 @@ import "collections/closable_iterator.dart";
///
/// This database interacts with SQLite synchonously.
class Database {
Pointer<types.Database> _database;
Pointer<bindings.sqlite3> _database;
bool _open = false;
/// Open a database located at the file [path].
Database(String path,
[int flags = Flags.SQLITE_OPEN_READWRITE | Flags.SQLITE_OPEN_CREATE]) {
Pointer<Pointer<types.Database>> dbOut = allocate();
final pathC = Utf8.toUtf8(path);
final int resultCode =
bindings.sqlite3_open_v2(pathC, dbOut, flags, nullptr);
[int flags =
bindings.SQLITE_OPEN_READWRITE | bindings.SQLITE_OPEN_CREATE]) {
Pointer<Pointer<bindings.sqlite3>> dbOut = allocate();
final pathC = Utf8.toUtf8(path).cast<Int8>();
final int resultCode = sqlite.sqlite3_open_v2(pathC, dbOut, flags, nullptr);
_database = dbOut.value;
free(dbOut);
free(pathC);
if (resultCode == Errors.SQLITE_OK) {
if (resultCode == bindings.SQLITE_OK) {
_open = true;
} else {
// Even if "open" fails, sqlite3 will still create a database object. We
// can just destroy it.
SQLiteException exception = _loadError(resultCode);
_open = true;
close();
throw exception;
}
@ -53,8 +52,8 @@ class Database {
/// avoid resource leaks.
void close() {
assert(_open);
final int resultCode = bindings.sqlite3_close_v2(_database);
if (resultCode == Errors.SQLITE_OK) {
final int resultCode = sqlite.sqlite3_close_v2(_database);
if (resultCode == bindings.SQLITE_OK) {
_open = false;
} else {
throw _loadError(resultCode);
@ -63,43 +62,44 @@ class Database {
/// Execute a query, discarding any returned rows.
void execute(String query) {
Pointer<Pointer<Statement>> statementOut = allocate();
Pointer<Utf8> queryC = Utf8.toUtf8(query);
int resultCode = bindings.sqlite3_prepare_v2(
_database, queryC, -1, statementOut, nullptr);
Pointer<Statement> statement = statementOut.value;
Pointer<Pointer<bindings.sqlite3_stmt>> statementOut = allocate();
final queryC = Utf8.toUtf8(query).cast<Int8>();
int resultCode =
sqlite.sqlite3_prepare_v2(_database, queryC, -1, statementOut, nullptr);
Pointer<bindings.sqlite3_stmt> statement = statementOut.value;
free(statementOut);
free(queryC);
while (resultCode == Errors.SQLITE_ROW || resultCode == Errors.SQLITE_OK) {
resultCode = bindings.sqlite3_step(statement);
while (
resultCode == bindings.SQLITE_ROW || resultCode == bindings.SQLITE_OK) {
resultCode = sqlite.sqlite3_step(statement);
}
bindings.sqlite3_finalize(statement);
if (resultCode != Errors.SQLITE_DONE) {
sqlite.sqlite3_finalize(statement);
if (resultCode != bindings.SQLITE_DONE) {
throw _loadError(resultCode);
}
}
/// Evaluate a query and return the resulting rows as an iterable.
Result query(String query) {
Pointer<Pointer<Statement>> statementOut = allocate();
Pointer<Utf8> queryC = Utf8.toUtf8(query);
int resultCode = bindings.sqlite3_prepare_v2(
_database, queryC, -1, statementOut, nullptr);
Pointer<Statement> statement = statementOut.value;
Pointer<Pointer<bindings.sqlite3_stmt>> statementOut = allocate();
final queryC = Utf8.toUtf8(query).cast<Int8>();
int resultCode =
sqlite.sqlite3_prepare_v2(_database, queryC, -1, statementOut, nullptr);
Pointer<bindings.sqlite3_stmt> statement = statementOut.value;
free(statementOut);
free(queryC);
if (resultCode != Errors.SQLITE_OK) {
bindings.sqlite3_finalize(statement);
if (resultCode != bindings.SQLITE_OK) {
sqlite.sqlite3_finalize(statement);
throw _loadError(resultCode);
}
Map<String, int> columnIndices = {};
int columnCount = bindings.sqlite3_column_count(statement);
int columnCount = sqlite.sqlite3_column_count(statement);
for (int i = 0; i < columnCount; i++) {
String columnName =
bindings.sqlite3_column_name(statement, i).ref.toString();
sqlite.sqlite3_column_name(statement, i).cast<Utf8>().ref.toString();
columnIndices[columnName] = i;
}
@ -107,12 +107,13 @@ class Database {
}
SQLiteException _loadError([int errorCode]) {
String errorMessage = bindings.sqlite3_errmsg(_database).ref.toString();
String errorMessage =
sqlite.sqlite3_errmsg(_database).cast<Utf8>().ref.toString();
if (errorCode == null) {
return SQLiteException(errorMessage);
}
String errorCodeExplanation =
bindings.sqlite3_errstr(errorCode).ref.toString();
sqlite.sqlite3_errstr(errorCode).cast<Utf8>().ref.toString();
return SQLiteException(
"$errorMessage (Code $errorCode: $errorCodeExplanation)");
}
@ -126,7 +127,7 @@ class Database {
class Result extends IterableBase<Row> implements ClosableIterable<Row> {
final Database _database;
final ClosableIterator<Row> _iterator;
final Pointer<Statement> _statement;
final Pointer<bindings.sqlite3_stmt> _statement;
final Map<String, int> _columnIndices;
Row _currentRow = null;
@ -143,7 +144,7 @@ class Result extends IterableBase<Row> implements ClosableIterable<Row> {
}
class _ResultIterator implements ClosableIterator<Row> {
final Pointer<Statement> _statement;
final Pointer<bindings.sqlite3_stmt> _statement;
final Map<String, int> _columnIndices;
Row _currentRow = null;
@ -156,8 +157,8 @@ class _ResultIterator implements ClosableIterator<Row> {
throw SQLiteException("The result has already been closed.");
}
_currentRow?._setNotCurrent();
int stepResult = bindings.sqlite3_step(_statement);
if (stepResult == Errors.SQLITE_ROW) {
int stepResult = sqlite.sqlite3_step(_statement);
if (stepResult == bindings.SQLITE_ROW) {
_currentRow = Row._(_statement, _columnIndices);
return true;
} else {
@ -176,12 +177,12 @@ class _ResultIterator implements ClosableIterator<Row> {
void close() {
_currentRow?._setNotCurrent();
_closed = true;
bindings.sqlite3_finalize(_statement);
sqlite.sqlite3_finalize(_statement);
}
}
class Row {
final Pointer<Statement> _statement;
final Pointer<bindings.sqlite3_stmt> _statement;
final Map<String, int> _columnIndices;
bool _isCurrentRow = true;
@ -210,10 +211,11 @@ class Row {
Type dynamicType;
if (convert == Convert.DynamicType) {
dynamicType =
_typeFromCode(bindings.sqlite3_column_type(_statement, columnIndex));
_typeFromCode(sqlite.sqlite3_column_type(_statement, columnIndex));
} else {
dynamicType = _typeFromText(bindings
dynamicType = _typeFromText(sqlite
.sqlite3_column_decltype(_statement, columnIndex)
.cast<Utf8>()
.ref
.toString());
}
@ -240,7 +242,7 @@ class Row {
/// integer.
int readColumnByIndexAsInt(int columnIndex) {
_checkIsCurrentRow();
return bindings.sqlite3_column_int(_statement, columnIndex);
return sqlite.sqlite3_column_int(_statement, columnIndex);
}
/// Reads column [columnName] and converts to [Type.Text] if not text.
@ -251,7 +253,11 @@ class Row {
/// Reads column [columnIndex] and converts to [Type.Text] if not text.
String readColumnByIndexAsText(int columnIndex) {
_checkIsCurrentRow();
return bindings.sqlite3_column_text(_statement, columnIndex).ref.toString();
return sqlite
.sqlite3_column_text(_statement, columnIndex)
.cast<Utf8>()
.ref
.toString();
}
void _checkIsCurrentRow() {
@ -269,15 +275,15 @@ class Row {
Type _typeFromCode(int code) {
switch (code) {
case Types.SQLITE_INTEGER:
case bindings.SQLITE_INTEGER:
return Type.Integer;
case Types.SQLITE_FLOAT:
case bindings.SQLITE_FLOAT:
return Type.Float;
case Types.SQLITE_TEXT:
case bindings.SQLITE_TEXT:
return Type.Text;
case Types.SQLITE_BLOB:
case bindings.SQLITE_BLOB:
return Type.Blob;
case Types.SQLITE_NULL:
case bindings.SQLITE_NULL:
return Type.Null;
}
throw Exception("Unknown type [$code]");

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,55 @@
name: sqlite3
version: 0.0.1
description: >-
Sqlite3 wrapper. Demo for dart:ffi.
author: Daco Harkes <dacoharkes@google.com>, Samir Jindel <sjindel@google.com>
environment:
sdk: '>=2.1.0 <3.0.0'
dependencies:
ffi: ^0.1.3
dev_dependencies:
test: ^1.5.3
ffigen: ^0.2.0
ffigen:
name: SQLite
description: SQLite bindings.
output: 'lib/src/third_party/sqlite/sqlite3_bindings_generated.dart'
headers:
entry-points:
- '/usr/include/sqlite3.h'
include-directives:
- '**sqlite3.h'
functions:
include:
- sqlite3_close_v2
- sqlite3_column_count
- sqlite3_column_decltype
- sqlite3_column_int
- sqlite3_column_name
- sqlite3_column_text
- sqlite3_column_type
- sqlite3_errmsg
- sqlite3_errstr
- sqlite3_finalize
- sqlite3_open_v2
- sqlite3_prepare_v2
- sqlite3_step
comments:
style: any
length: full
preamble: |
// 2001 September 15
//
// The author disclaims copyright to this source code. In place of
// a legal notice, here is a blessing:
//
// May you do good and not evil.
// May you find forgiveness for yourself and forgive others.
// May you share freely, never taking more than you give.

View file

@ -13,6 +13,7 @@ import "package:test/test.dart";
import '../lib/sqlite.dart';
void main() {
assert(Platform.script.hasAbsolutePath); // `pub run test` is broken.
final dbPath = Platform.script.resolve("test.db").path;
test("sqlite integration test", () {
Database d = Database(dbPath);
@ -166,6 +167,7 @@ void main() {
r.close();
d.close();
});
test("Utf8 unit test", () {
final String test = 'Hasta Mañana';
final medium = Utf8.toUtf8(test);

View file

@ -13,5 +13,6 @@
!unittest.tar.gz.sha1
!update.sh
!/wasmer
!/sqlite
# but ignore a subfolder of tcmalloc (some client ignores /tcmalloc/.gitignore)
/tcmalloc/gperftools

9
third_party/sqlite/LICENSE.md vendored Normal file
View file

@ -0,0 +1,9 @@
The author disclaims copyright to this source code. In place of
a legal notice, here is a blessing:
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
Files generated from sqlite source files are in
samples/ffi/sqlite/lib/src/third_party/sqlite.