148 lines
4.3 KiB
Vala
148 lines
4.3 KiB
Vala
|
|
/* Copyright 2012 Yorba Foundation
|
||
|
|
*
|
||
|
|
* This software is licensed under the GNU Lesser General Public License
|
||
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Geary.Db is a simple wrapper around SQLite to make it more GObject-ish and easier to code in
|
||
|
|
* Vala. It also uses threads and some concurrency features of SQLite to allow for asynchronous
|
||
|
|
* access to the database.
|
||
|
|
*
|
||
|
|
* There is no attempt here to hide or genericize the backing database library; this is designed with
|
||
|
|
* SQLite in mind. As such, many of the calls are merely direct front-ends to the underlying
|
||
|
|
* SQLite call.
|
||
|
|
*
|
||
|
|
* The design of the classes and interfaces owes a debt to SQLHeavy (http://code.google.com/p/sqlheavy/).
|
||
|
|
*/
|
||
|
|
|
||
|
|
namespace Geary.Db {
|
||
|
|
|
||
|
|
public const int64 INVALID_ROWID = -1;
|
||
|
|
|
||
|
|
[Flags]
|
||
|
|
public enum DatabaseFlags {
|
||
|
|
NONE = 0,
|
||
|
|
CREATE_DIRECTORY,
|
||
|
|
CREATE_FILE,
|
||
|
|
READ_ONLY
|
||
|
|
}
|
||
|
|
|
||
|
|
public enum ResetScope {
|
||
|
|
SAVE_BINDINGS,
|
||
|
|
CLEAR_BINDINGS
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* PrepareConnection is called from Database when a Connection is created. Database may pool
|
||
|
|
* Connections, especially for asynchronous queries, so this is only called when a new
|
||
|
|
* Connection is created and not when its reused.
|
||
|
|
*
|
||
|
|
* PrepareConnection may be used as an opportunity to modify or configure the Connection.
|
||
|
|
* This callback is called prior to the Connection being used, either internally or handed off to
|
||
|
|
* a caller for normal use.
|
||
|
|
*
|
||
|
|
* This callback may be called in the context of a background thread.
|
||
|
|
*/
|
||
|
|
public delegate void PrepareConnection(Connection cx, bool master) throws Error;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* See Connection.exec_transaction() for more information on how this delegate is used.
|
||
|
|
*/
|
||
|
|
public delegate TransactionOutcome TransactionMethod(Connection cx, Cancellable? cancellable) throws Error;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* See http://www.sqlite.org/c3ref/threadsafe.html
|
||
|
|
*/
|
||
|
|
public bool threadsafe() {
|
||
|
|
return Sqlite.threadsafe() != 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* See http://www.sqlite.org/c3ref/libversion.html
|
||
|
|
*/
|
||
|
|
public unowned string sqlite_version() {
|
||
|
|
return Sqlite.libversion();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* See http://www.sqlite.org/c3ref/libversion.html
|
||
|
|
*/
|
||
|
|
public int sqlite_version_number() {
|
||
|
|
return Sqlite.libversion_number();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void check_cancelled(string? method, Cancellable? cancellable) throws IOError {
|
||
|
|
if (cancellable != null && cancellable.is_cancelled())
|
||
|
|
throw new IOError.CANCELLED("%s cancelled", !String.is_empty(method) ? method : "Operation");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Returns result if exception is not thrown
|
||
|
|
private int throw_on_error(Context ctx, string? method, int result, string? raw = null) throws DatabaseError {
|
||
|
|
// fast-fail
|
||
|
|
switch (result) {
|
||
|
|
case Sqlite.OK:
|
||
|
|
case Sqlite.DONE:
|
||
|
|
case Sqlite.ROW:
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
string location = !String.is_empty(method) ? "(%s) ".printf(method) : "";
|
||
|
|
string errmsg = (ctx.get_connection() != null) ? " - %s".printf(ctx.get_connection().db.errmsg()) : "";
|
||
|
|
string sql;
|
||
|
|
if (ctx.get_statement() != null)
|
||
|
|
sql = " (%s)".printf(ctx.get_statement().sql);
|
||
|
|
else if (!String.is_empty(raw))
|
||
|
|
sql = " (%s)".printf(raw);
|
||
|
|
else
|
||
|
|
sql = "";
|
||
|
|
|
||
|
|
string msg = "%s[err=%d]%s%s".printf(location, result, errmsg, sql);
|
||
|
|
|
||
|
|
switch (result) {
|
||
|
|
case Sqlite.BUSY:
|
||
|
|
throw new DatabaseError.BUSY(msg);
|
||
|
|
|
||
|
|
case Sqlite.PERM:
|
||
|
|
case Sqlite.READONLY:
|
||
|
|
case Sqlite.IOERR:
|
||
|
|
case Sqlite.CORRUPT:
|
||
|
|
case Sqlite.CANTOPEN:
|
||
|
|
case Sqlite.NOLFS:
|
||
|
|
case Sqlite.AUTH:
|
||
|
|
case Sqlite.FORMAT:
|
||
|
|
case Sqlite.NOTADB:
|
||
|
|
throw new DatabaseError.BACKING(msg);
|
||
|
|
|
||
|
|
case Sqlite.NOMEM:
|
||
|
|
throw new DatabaseError.MEMORY(msg);
|
||
|
|
|
||
|
|
case Sqlite.ABORT:
|
||
|
|
case Sqlite.LOCKED:
|
||
|
|
throw new DatabaseError.ABORT(msg);
|
||
|
|
|
||
|
|
case Sqlite.INTERRUPT:
|
||
|
|
throw new DatabaseError.INTERRUPT(msg);
|
||
|
|
|
||
|
|
case Sqlite.FULL:
|
||
|
|
case Sqlite.EMPTY:
|
||
|
|
case Sqlite.TOOBIG:
|
||
|
|
case Sqlite.CONSTRAINT:
|
||
|
|
case Sqlite.RANGE:
|
||
|
|
throw new DatabaseError.LIMITS(msg);
|
||
|
|
|
||
|
|
case Sqlite.SCHEMA:
|
||
|
|
case Sqlite.MISMATCH:
|
||
|
|
throw new DatabaseError.TYPESPEC(msg);
|
||
|
|
|
||
|
|
case Sqlite.ERROR:
|
||
|
|
case Sqlite.INTERNAL:
|
||
|
|
case Sqlite.MISUSE:
|
||
|
|
default:
|
||
|
|
throw new DatabaseError.GENERAL(msg);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|