]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_utilities/lib/sqlite++.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_utilities / lib / sqlite++.cpp
diff --git a/Security/libsecurity_utilities/lib/sqlite++.cpp b/Security/libsecurity_utilities/lib/sqlite++.cpp
new file mode 100644 (file)
index 0000000..03c9233
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2008,2011-2012,2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+// sqlite++ - C++ interface to SQLite3
+//
+#include "sqlite++.h"
+#include <stdexcept>
+#include <security_utilities/cfutilities.h>
+
+
+//@@@
+// From cssmapple.h - layering break
+// Where should this go?
+//@@@
+#define errSecErrnoBase 100000
+#define errSecErrnoLimit 100255
+
+
+namespace Security {
+namespace SQLite3 {
+
+
+//
+// Our exception object
+//
+void Error::check(int err)
+{
+       if (err != SQLITE_OK)
+               throw Error(err);
+}
+
+Error::Error(Database &db)
+       : error(db.errcode()), message(db.errmsg())
+{
+       SECURITY_EXCEPTION_THROW_SQLITE(this, error, (char*)message.c_str());
+}
+       
+void Error::throwMe(int err)
+{
+       throw Error(err);
+}
+
+OSStatus Error::osStatus() const
+{
+       return unixError() + errSecErrnoBase;
+}
+
+int Error::unixError() const
+{
+       switch (error) {
+       case SQLITE_PERM:
+       case SQLITE_READONLY:
+       case SQLITE_AUTH:
+               return EACCES;
+       case SQLITE_BUSY:
+               return EAGAIN;
+       case SQLITE_NOMEM:
+               return ENOMEM;
+       case SQLITE_IOERR:
+               return EIO;
+       case SQLITE_FULL:
+               return ENOSPC;
+       case SQLITE_TOOBIG:
+               return EFBIG;
+       case SQLITE_MISMATCH:
+       case SQLITE_MISUSE:
+               return EINVAL;
+       case SQLITE_NOLFS:
+               return ENOTSUP;
+       case SQLITE_RANGE:
+               return EDOM;
+       default:
+               return -1;
+       }
+}
+
+
+//
+// Database objects
+//
+Database::Database(const char *path, int flags, bool lenient /* = false */)
+       : mMutex(Mutex::recursive)
+{
+       try {
+               int rc = ::sqlite3_open_v2(path, &mDb, flags, NULL);
+               if (rc != SQLITE_OK && lenient) {       // silent open failure
+                       sqlite3_close(mDb); // ditch useless Db object
+                       mDb = NULL;                     // indicate failure
+                       return;
+               }
+               check(rc);
+               check(::sqlite3_extended_result_codes(mDb, true));
+               mOpenFlags = flags;
+       } catch (...) {
+               sqlite3_close(mDb);             // allocated even if open fails(!)
+               throw;
+       }
+}
+
+Database::~Database()
+{
+       this->close();
+}
+
+void Database::close()
+{
+       if (mDb)
+               check(::sqlite3_close(mDb));
+}
+
+
+int Database::execute(const char *text, bool strict /* = true */)
+{
+       StLock<Mutex> _(mMutex);
+
+       int rc = ::sqlite3_exec(mDb, text, NULL, NULL, NULL);
+       if (strict)
+               check(rc);
+       return rc;
+}
+
+
+void Database::busyDelay(int ms)
+{
+       StLock<Mutex> _(mMutex);
+
+       check(::sqlite3_busy_timeout(mDb, ms));
+}
+
+
+void Database::check(int err)
+{
+       if (err)
+               throw Error(*this);
+}
+
+
+bool Database::empty()
+{
+       return value("select count(*) from sqlite_master;", 0) == 0;
+}
+
+
+int Database::errcode()
+{
+       StLock<Mutex> _(mMutex);
+
+       return sqlite3_errcode(mDb);
+}
+
+
+
+const char *Database::errmsg()
+{
+       StLock<Mutex> _(mMutex);
+
+       return sqlite3_errmsg(mDb);
+}
+
+
+
+bool Database::inTransaction()
+{
+       StLock<Mutex> _(mMutex);
+
+       return !::sqlite3_get_autocommit(mDb);
+}
+
+
+
+int64 Database::lastInsert()
+{
+       StLock<Mutex> _(mMutex);
+
+       return ::sqlite3_last_insert_rowid(mDb);
+}
+
+int Database::changes()
+{
+       StLock<Mutex> _(mMutex);
+
+       return ::sqlite3_changes(mDb);
+}
+
+void Database::interrupt()
+{
+       StLock<Mutex> _(mMutex);
+
+       ::sqlite3_interrupt(mDb);
+}
+
+//
+// Transaction managers
+//
+Transaction::Transaction(Database &db, Type type, const char *name)
+       : database(db), mName(name ? name : "")
+{
+       switch (type) {
+       case deferred:  xactCommand("BEGIN DEFERRED"); break;
+       case immediate: xactCommand("BEGIN IMMEDIATE"); break;
+       case exclusive: xactCommand("BEGIN EXCLUSIVE"); break;
+       }
+}
+
+Transaction::~Transaction()
+{
+       if (database.inTransaction())
+               abort();
+}
+
+void Transaction::commit()
+{
+       xactCommand("COMMIT");
+}
+
+void Transaction::abort()
+{
+       xactCommand("ROLLBACK");
+}
+
+void Transaction::xactCommand(const string &cmd)
+{
+       database.execute(cmd + " TRANSACTION " + mName + ";");
+}
+
+
+//
+// Statement objects
+//
+Statement::Statement(Database &db, const char *text)
+       : StLock<Mutex>(db.mMutex), database(db), mStmt(NULL)
+{
+       this->query(text);
+}
+
+Statement::Statement(Database &db)
+       : StLock<Mutex>(db.mMutex), database(db), mStmt(NULL)
+{ }
+
+void Statement::query(const char *text)
+{
+       this->close();
+       const char *tail;
+       check(::sqlite3_prepare_v2(database.sql(), text, -1, &mStmt, &tail));
+       if (*tail)
+               throw std::logic_error("multiple statements");
+}
+
+void Statement::close()
+{
+       // Sqlite3_finalize will return an error if the Statement (executed and) failed.
+       // So we eat any error code here, since we can't tell "genuine" errors apart from
+       // errors inherited from the Statement execution.
+       if (mStmt)
+               ::sqlite3_finalize(mStmt);
+       mStmt = NULL;
+}
+
+Statement::~Statement()
+{
+       this->close();
+}
+
+
+void Statement::unbind()
+{
+       check(::sqlite3_clear_bindings(mStmt));
+}
+
+void Statement::reset()
+{
+       check(::sqlite3_reset(mStmt));
+}
+
+
+int Statement::step()
+{
+       return ::sqlite3_step(mStmt);
+}
+
+void Statement::execute()
+{
+       switch (int rc = this->step()) {
+       case SQLITE_DONE:
+       case SQLITE_OK:
+               break;
+       default:
+               check(rc);
+       }
+}
+
+bool Statement::nextRow()
+{
+       switch (int rc = this->step()) {
+       case SQLITE_ROW:
+               return true;
+       case SQLITE_DONE:
+               return false;
+       default:
+               check(rc);
+               return false;
+       }
+}
+
+
+//
+// Binding gluons.
+//
+Statement::Binding Statement::bind(const char *name) const
+{
+       if (int ix = ::sqlite3_bind_parameter_index(mStmt, name))
+               return Binding(*this, ix);
+       else
+               throw std::logic_error("unknown parameter name");
+}
+
+void Statement::Binding::null()
+{
+       statement.check(::sqlite3_bind_null(statement.sql(), index));
+}
+
+void Statement::Binding::operator = (const Value &value)
+{
+       statement.check(::sqlite3_bind_value(statement.sql(), index, value.sql()));
+}
+
+void Statement::Binding::operator = (int value)
+{
+       statement.check(::sqlite3_bind_int(statement.sql(), index, value));
+}
+
+void Statement::Binding::operator = (sqlite3_int64 value)
+{
+       statement.check(::sqlite3_bind_int64(statement.sql(), index, value));
+}
+
+void Statement::Binding::integer(sqlite3_int64 value)
+{
+       statement.check(::sqlite3_bind_int64(statement.sql(), index, value));
+}
+
+void Statement::Binding::operator = (double value)
+{
+       statement.check(::sqlite3_bind_double(statement.sql(), index, value));
+}
+
+void Statement::Binding::operator = (const char *value)
+{
+       if (value == NULL)
+               this->null();
+       else
+               statement.check(::sqlite3_bind_text(statement.sql(), index,
+                       ::strdup(value), -1, ::free));
+}
+
+void Statement::Binding::operator = (const std::string &value)
+{
+       statement.check(::sqlite3_bind_text(statement.sql(), index,
+               ::strdup(value.c_str()), -1, ::free));
+}
+
+void Statement::Binding::blob(const void *data, size_t length, bool shared /* = false */)
+{
+       if (data == NULL)
+               this->null();
+       else if (shared) {
+               statement.check(::sqlite3_bind_blob(statement.sql(), index, data, (int)length, NULL));
+       } else if (void *copy = ::malloc(length)) {
+               ::memcpy(copy, data, length);
+               statement.check(::sqlite3_bind_blob(statement.sql(), index,
+               copy, (int)length, ::free));
+       } else
+               throw std::bad_alloc();
+}
+
+void Statement::Binding::operator = (CFDataRef data)
+{
+       if (data)
+               this->blob(CFDataGetBytePtr(data), CFDataGetLength(data));
+       else
+               this->null();
+}
+
+void Statement::Binding::operator = (CFStringRef value)
+{
+       if (value)
+               *this = cfString(value).c_str();
+       else
+               this->null();
+}
+
+const char *Statement::Binding::name() const
+{
+       return sqlite3_bind_parameter_name(statement.sql(), index);
+}
+
+
+//
+// Row/column results
+//
+const char *Statement::Result::name() const
+{
+       return sqlite3_column_name(statement.sql(), index);
+}
+
+CFDataRef Statement::Result::data() const
+{
+       switch (this->type()) {
+       case SQLITE_NULL:
+               return NULL;
+       case SQLITE_BLOB:
+               return makeCFData(this->blob(), this->length());
+       default:
+               throw Error(SQLITE_MISMATCH, "Retrieving data() of non-Blob");
+       }
+}
+
+
+}      // SQLite3
+}      // Security