/* | |
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include "config.h" | |
#include "SQLiteStatement.h" | |
#include "Logging.h" | |
#include "SQLValue.h" | |
#include <sqlite3.h> | |
#include <wtf/Assertions.h> | |
namespace WebCore { | |
#if SQLITE_VERSION_NUMBER < 3003009 | |
// FIXME: This overload helps us compile with older versions of SQLite 3, but things like quotas will not work. | |
static inline int sqlite3_prepare16_v2(sqlite3* db, const void* zSql, int nBytes, sqlite3_stmt** ppStmt, const void** pzTail) | |
{ | |
return sqlite3_prepare16(db, zSql, nBytes, ppStmt, pzTail); | |
} | |
#endif | |
SQLiteStatement::SQLiteStatement(SQLiteDatabase& db, const String& sql) | |
: m_database(db) | |
, m_query(sql) | |
, m_statement(0) | |
#ifndef NDEBUG | |
, m_isPrepared(false) | |
#endif | |
{ | |
} | |
SQLiteStatement::~SQLiteStatement() | |
{ | |
finalize(); | |
} | |
int SQLiteStatement::prepare() | |
{ | |
ASSERT(!m_isPrepared); | |
const void* tail; | |
LOG(SQLDatabase, "SQL - prepare - %s", m_query.ascii().data()); | |
int error = sqlite3_prepare16_v2(m_database.sqlite3Handle(), m_query.charactersWithNullTermination(), -1, &m_statement, &tail); | |
if (error != SQLITE_OK) | |
LOG(SQLDatabase, "sqlite3_prepare16 failed (%i)\n%s\n%s", error, m_query.ascii().data(), sqlite3_errmsg(m_database.sqlite3Handle())); | |
#ifndef NDEBUG | |
m_isPrepared = error == SQLITE_OK; | |
#endif | |
return error; | |
} | |
int SQLiteStatement::step() | |
{ | |
ASSERT(m_isPrepared); | |
if (!m_statement) | |
return SQLITE_OK; | |
LOG(SQLDatabase, "SQL - step - %s", m_query.ascii().data()); | |
int error = sqlite3_step(m_statement); | |
if (error != SQLITE_DONE && error != SQLITE_ROW) { | |
LOG(SQLDatabase, "sqlite3_step failed (%i)\nQuery - %s\nError - %s", | |
error, m_query.ascii().data(), sqlite3_errmsg(m_database.sqlite3Handle())); | |
} | |
return error; | |
} | |
int SQLiteStatement::finalize() | |
{ | |
#ifndef NDEBUG | |
m_isPrepared = false; | |
#endif | |
if (!m_statement) | |
return SQLITE_OK; | |
LOG(SQLDatabase, "SQL - finalize - %s", m_query.ascii().data()); | |
int result = sqlite3_finalize(m_statement); | |
m_statement = 0; | |
return result; | |
} | |
int SQLiteStatement::reset() | |
{ | |
ASSERT(m_isPrepared); | |
if (!m_statement) | |
return SQLITE_OK; | |
LOG(SQLDatabase, "SQL - reset - %s", m_query.ascii().data()); | |
return sqlite3_reset(m_statement); | |
} | |
bool SQLiteStatement::executeCommand() | |
{ | |
if (!m_statement && prepare() != SQLITE_OK) | |
return false; | |
ASSERT(m_isPrepared); | |
if (step() != SQLITE_DONE) { | |
finalize(); | |
return false; | |
} | |
finalize(); | |
return true; | |
} | |
bool SQLiteStatement::returnsAtLeastOneResult() | |
{ | |
if (!m_statement && prepare() != SQLITE_OK) | |
return false; | |
ASSERT(m_isPrepared); | |
if (step() != SQLITE_ROW) { | |
finalize(); | |
return false; | |
} | |
finalize(); | |
return true; | |
} | |
int SQLiteStatement::bindBlob(int index, const void* blob, int size) | |
{ | |
ASSERT(m_isPrepared); | |
ASSERT(index > 0); | |
ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); | |
ASSERT(blob); | |
ASSERT(size >= 0); | |
if (!m_statement) | |
return SQLITE_ERROR; | |
return sqlite3_bind_blob(m_statement, index, blob, size, SQLITE_TRANSIENT); | |
} | |
int SQLiteStatement::bindText(int index, const String& text) | |
{ | |
ASSERT(m_isPrepared); | |
ASSERT(index > 0); | |
ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); | |
// String::characters() returns 0 for the empty string, which SQLite | |
// treats as a null, so we supply a non-null pointer for that case. | |
UChar anyCharacter = 0; | |
const UChar* characters; | |
if (text.isEmpty() && !text.isNull()) | |
characters = &anyCharacter; | |
else | |
characters = text.characters(); | |
return sqlite3_bind_text16(m_statement, index, characters, sizeof(UChar) * text.length(), SQLITE_TRANSIENT); | |
} | |
int SQLiteStatement::bindInt64(int index, int64_t integer) | |
{ | |
ASSERT(m_isPrepared); | |
ASSERT(index > 0); | |
ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); | |
return sqlite3_bind_int64(m_statement, index, integer); | |
} | |
int SQLiteStatement::bindDouble(int index, double number) | |
{ | |
ASSERT(m_isPrepared); | |
ASSERT(index > 0); | |
ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); | |
return sqlite3_bind_double(m_statement, index, number); | |
} | |
int SQLiteStatement::bindNull(int index) | |
{ | |
ASSERT(m_isPrepared); | |
ASSERT(index > 0); | |
ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); | |
return sqlite3_bind_null(m_statement, index); | |
} | |
int SQLiteStatement::bindValue(int index, const SQLValue& value) | |
{ | |
switch (value.type()) { | |
case SQLValue::StringValue: | |
return bindText(index, value.string()); | |
case SQLValue::NumberValue: | |
return bindDouble(index, value.number()); | |
case SQLValue::NullValue: | |
return bindNull(index); | |
} | |
ASSERT_NOT_REACHED(); | |
return SQLITE_ERROR; | |
} | |
unsigned SQLiteStatement::bindParameterCount() const | |
{ | |
ASSERT(m_isPrepared); | |
if (!m_statement) | |
return 0; | |
return sqlite3_bind_parameter_count(m_statement); | |
} | |
int SQLiteStatement::columnCount() | |
{ | |
ASSERT(m_isPrepared); | |
if (!m_statement) | |
return 0; | |
return sqlite3_data_count(m_statement); | |
} | |
String SQLiteStatement::getColumnName(int col) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement) | |
if (prepareAndStep() != SQLITE_ROW) | |
return String(); | |
if (columnCount() <= col) | |
return String(); | |
return String(reinterpret_cast<const UChar*>(sqlite3_column_name16(m_statement, col))); | |
} | |
SQLValue SQLiteStatement::getColumnValue(int col) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement) | |
if (prepareAndStep() != SQLITE_ROW) | |
return SQLValue(); | |
if (columnCount() <= col) | |
return SQLValue(); | |
// SQLite is typed per value. optional column types are | |
// "(mostly) ignored" | |
sqlite3_value* value = sqlite3_column_value(m_statement, col); | |
switch (sqlite3_value_type(value)) { | |
case SQLITE_INTEGER: // SQLValue and JS don't represent integers, so use FLOAT -case | |
case SQLITE_FLOAT: | |
return SQLValue(sqlite3_value_double(value)); | |
case SQLITE_BLOB: // SQLValue and JS don't represent blobs, so use TEXT -case | |
case SQLITE_TEXT: | |
return SQLValue(String(reinterpret_cast<const UChar*>(sqlite3_value_text16(value)))); | |
case SQLITE_NULL: | |
return SQLValue(); | |
default: | |
break; | |
} | |
ASSERT_NOT_REACHED(); | |
return SQLValue(); | |
} | |
String SQLiteStatement::getColumnText(int col) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement) | |
if (prepareAndStep() != SQLITE_ROW) | |
return String(); | |
if (columnCount() <= col) | |
return String(); | |
return String(reinterpret_cast<const UChar*>(sqlite3_column_text16(m_statement, col))); | |
} | |
double SQLiteStatement::getColumnDouble(int col) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement) | |
if (prepareAndStep() != SQLITE_ROW) | |
return 0.0; | |
if (columnCount() <= col) | |
return 0.0; | |
return sqlite3_column_double(m_statement, col); | |
} | |
int SQLiteStatement::getColumnInt(int col) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement) | |
if (prepareAndStep() != SQLITE_ROW) | |
return 0; | |
if (columnCount() <= col) | |
return 0; | |
return sqlite3_column_int(m_statement, col); | |
} | |
int64_t SQLiteStatement::getColumnInt64(int col) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement) | |
if (prepareAndStep() != SQLITE_ROW) | |
return 0; | |
if (columnCount() <= col) | |
return 0; | |
return sqlite3_column_int64(m_statement, col); | |
} | |
void SQLiteStatement::getColumnBlobAsVector(int col, Vector<char>& result) | |
{ | |
ASSERT(col >= 0); | |
if (!m_statement && prepareAndStep() != SQLITE_ROW) { | |
result.clear(); | |
return; | |
} | |
if (columnCount() <= col) { | |
result.clear(); | |
return; | |
} | |
const void* blob = sqlite3_column_blob(m_statement, col); | |
if (!blob) { | |
result.clear(); | |
return; | |
} | |
int size = sqlite3_column_bytes(m_statement, col); | |
result.resize((size_t)size); | |
for (int i = 0; i < size; ++i) | |
result[i] = ((const unsigned char*)blob)[i]; | |
} | |
const void* SQLiteStatement::getColumnBlob(int col, int& size) | |
{ | |
ASSERT(col >= 0); | |
size = 0; | |
if (finalize() != SQLITE_OK) | |
LOG(SQLDatabase, "Finalize failed"); | |
if (prepare() != SQLITE_OK) { | |
LOG(SQLDatabase, "Prepare failed"); | |
return 0; | |
} | |
if (step() != SQLITE_ROW) { | |
LOG(SQLDatabase, "Step wasn't a row"); | |
return 0; | |
} | |
if (columnCount() <= col) | |
return 0; | |
const void* blob = sqlite3_column_blob(m_statement, col); | |
if (!blob) | |
return 0; | |
size = sqlite3_column_bytes(m_statement, col); | |
return blob; | |
} | |
bool SQLiteStatement::returnTextResults(int col, Vector<String>& v) | |
{ | |
ASSERT(col >= 0); | |
v.clear(); | |
if (m_statement) | |
finalize(); | |
if (prepare() != SQLITE_OK) | |
return false; | |
while (step() == SQLITE_ROW) | |
v.append(getColumnText(col)); | |
bool result = true; | |
if (m_database.lastError() != SQLITE_DONE) { | |
result = false; | |
LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); | |
} | |
finalize(); | |
return result; | |
} | |
bool SQLiteStatement::returnIntResults(int col, Vector<int>& v) | |
{ | |
v.clear(); | |
if (m_statement) | |
finalize(); | |
if (prepare() != SQLITE_OK) | |
return false; | |
while (step() == SQLITE_ROW) | |
v.append(getColumnInt(col)); | |
bool result = true; | |
if (m_database.lastError() != SQLITE_DONE) { | |
result = false; | |
LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); | |
} | |
finalize(); | |
return result; | |
} | |
bool SQLiteStatement::returnInt64Results(int col, Vector<int64_t>& v) | |
{ | |
v.clear(); | |
if (m_statement) | |
finalize(); | |
if (prepare() != SQLITE_OK) | |
return false; | |
while (step() == SQLITE_ROW) | |
v.append(getColumnInt64(col)); | |
bool result = true; | |
if (m_database.lastError() != SQLITE_DONE) { | |
result = false; | |
LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); | |
} | |
finalize(); | |
return result; | |
} | |
bool SQLiteStatement::returnDoubleResults(int col, Vector<double>& v) | |
{ | |
v.clear(); | |
if (m_statement) | |
finalize(); | |
if (prepare() != SQLITE_OK) | |
return false; | |
while (step() == SQLITE_ROW) | |
v.append(getColumnDouble(col)); | |
bool result = true; | |
if (m_database.lastError() != SQLITE_DONE) { | |
result = false; | |
LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); | |
} | |
finalize(); | |
return result; | |
} | |
bool SQLiteStatement::isExpired() | |
{ | |
return !m_statement || sqlite3_expired(m_statement); | |
} | |
} // namespace WebCore |