1
// Copyright 2007, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
#include "gears/database/result_set.h"
28
#include "gears/base/common/sqlite_wrapper.h"
29
#include "gears/base/common/stopwatch.h"
30
#include "gears/database/database.h"
31
#include "gears/database/database_utils.h"
33
DECLARE_DISPATCHER(GearsResultSet);
35
const std::string GearsResultSet::kModuleName("GearsResultSet");
39
void Dispatcher<GearsResultSet>::Init() {
40
RegisterMethod("field", &GearsResultSet::Field);
41
RegisterMethod("fieldByName", &GearsResultSet::FieldByName);
42
RegisterMethod("fieldName", &GearsResultSet::FieldName);
43
RegisterMethod("fieldCount", &GearsResultSet::FieldCount);
44
RegisterMethod("close", &GearsResultSet::Close);
45
RegisterMethod("next", &GearsResultSet::Next);
46
RegisterMethod("isValidRow", &GearsResultSet::IsValidRow);
49
GearsResultSet::GearsResultSet()
50
: ModuleImplBaseClass(kModuleName),
52
is_valid_row_(false) {
55
GearsResultSet::~GearsResultSet() {
57
#if BROWSER_IE || BROWSER_IEMOBILE
58
LOG16((L"~GearsResultSet - was NOT closed by caller\n"));
60
LOG(("~GearsResultSet - was NOT closed by caller\n"));
66
if (database_ != NULL) {
67
database_->RemoveResultSet(this);
72
bool GearsResultSet::InitializeResultSet(sqlite3_stmt *statement,
74
std::string16 *error_message) {
77
assert(error_message);
78
statement_ = statement;
81
// convention: call next() when the statement is set
82
bool succeeded = NextImpl(error_message);
83
if (!succeeded || sqlite3_column_count(statement_) == 0) {
84
// Either an error occurred or this was a command that does
85
// not return a row, so we can just close automatically
89
db->AddResultSet(this);
95
void GearsResultSet::PageUnloading() {
96
if (database_ != NULL) {
97
// Don't remove ourselves from the result set, since database_ is going away
103
bool GearsResultSet::Finalize() {
105
sqlite3 *db = sqlite3_db_handle(statement_);
106
int sql_status = sqlite3_finalize(statement_);
107
sql_status = SqlitePoisonIfCorrupt(db, sql_status);
110
#if BROWSER_IE || BROWSER_IEMOBILE
111
LOG16((L"DB ResultSet Close: %d", sql_status));
113
LOG(("DB ResultSet Close: %d", sql_status));
116
if (sql_status != SQLITE_OK) {
123
void GearsResultSet::FieldImpl(JsCallContext *context, int index) {
125
ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
128
if ((index < 0) || (index >= sqlite3_column_count(statement_))) {
129
context->SetException(STRING16(L"Invalid index."));
133
int column_type = sqlite3_column_type(statement_, index);
134
switch (column_type) {
135
case SQLITE_INTEGER: {
136
sqlite_int64 i64 = sqlite3_column_int64(statement_, index);
137
context->SetReturnValue(JSPARAM_INT64, &i64);
141
double retval = sqlite3_column_double(statement_, index);
142
context->SetReturnValue(JSPARAM_DOUBLE, &retval);
146
const void *text = sqlite3_column_text16(statement_, index);
147
std::string16 retval(static_cast<const char16 *>(text));
148
context->SetReturnValue(JSPARAM_STRING16, &retval);
152
context->SetReturnValue(JSPARAM_NULL, NULL);
155
// TODO(miket): figure out right way to pass around blobs in variants.
156
context->SetException(STRING16(L"Data type not supported."));
159
context->SetException(STRING16(L"Data type not supported."));
164
void GearsResultSet::Field(JsCallContext *context) {
165
if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
169
JsArgument argv[] = {
170
{ JSPARAM_REQUIRED, JSPARAM_INT, &index },
172
context->GetArguments(ARRAYSIZE(argv), argv);
173
if (context->is_exception_set())
176
FieldImpl(context, index); // sets the return value/error.
179
void GearsResultSet::FieldByName(JsCallContext *context) {
181
ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
184
if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
187
std::string16 field_name;
188
JsArgument argv[] = {
189
{ JSPARAM_REQUIRED, JSPARAM_STRING16, &field_name },
191
context->GetArguments(ARRAYSIZE(argv), argv);
192
if (context->is_exception_set())
195
// TODO(miket): This is horrible O(n) code but we didn't have a hashtable
196
// implementation handy. Fix this!
197
int n = sqlite3_column_count(statement_);
199
for (i = 0; i < n; ++i) {
200
const void *column_name = sqlite3_column_name16(statement_, i);
201
std::string16 s(static_cast<const char16 *>(column_name));
202
if (field_name == s) {
207
context->SetException(STRING16(L"Field name not found."));
211
FieldImpl(context, i); // sets the return value/error.
214
void GearsResultSet::FieldName(JsCallContext *context) {
216
ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
219
if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
223
JsArgument argv[] = {
224
{ JSPARAM_REQUIRED, JSPARAM_INT, &index },
226
context->GetArguments(ARRAYSIZE(argv), argv);
227
if (context->is_exception_set())
230
if ((index < 0) || (index >= sqlite3_column_count(statement_))) {
231
context->SetException(STRING16(L"Invalid index."));
235
const void *column_name = sqlite3_column_name16(statement_, index);
236
std::string16 retval(static_cast<const char16 *>(column_name));
237
context->SetReturnValue(JSPARAM_STRING16, &retval);
240
void GearsResultSet::FieldCount(JsCallContext *context) {
242
ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
245
// rs.fieldCount() should never throw. Return 0 if there is no statement.
246
int retval = statement_ ? sqlite3_column_count(statement_) : 0;
247
context->SetReturnValue(JSPARAM_INT, &retval);
250
void GearsResultSet::Close(JsCallContext *context) {
252
ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
256
context->SetException(STRING16(L"SQLite finalize() failed."));
260
void GearsResultSet::Next(JsCallContext *context) {
261
if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
263
std::string16 error_message;
264
if (!NextImpl(&error_message)) {
265
context->SetException(error_message.c_str());
269
bool GearsResultSet::NextImpl(std::string16 *error_message) {
271
ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
274
assert(error_message);
276
if (database_->deleted_) {
277
*error_message = STRING16(L"Database was deleted.");
281
int sql_status = sqlite3_step(statement_);
282
sql_status = SqlitePoisonIfCorrupt(sqlite3_db_handle(statement_),
284
LOG(("GearsResultSet::next() sqlite3_step returned %d", sql_status));
285
switch (sql_status) {
287
is_valid_row_ = true;
290
// If there was a timeout (SQLITE_BUSY) the SQL row cursor did not
291
// advance, so we don't reset is_valid_row_. If it was valid prior to
292
// this call, it's still valid now.
295
is_valid_row_ = false;
298
bool succeeded = (sql_status == SQLITE_ROW) ||
299
(sql_status == SQLITE_DONE) ||
300
(sql_status == SQLITE_OK);
302
BuildSqliteErrorString(STRING16(L"Database operation failed."),
303
sql_status, sqlite3_db_handle(statement_),
309
void GearsResultSet::IsValidRow(JsCallContext *context) {
310
// rs.isValidRow() should never throw. Return false if there is no statement.
312
if (statement_ != NULL) {
313
valid = is_valid_row_;
316
context->SetReturnValue(JSPARAM_BOOL, &valid);
319
bool GearsResultSet::EnsureResultSetAndDatabaseAreOpen(JsCallContext *context) {
320
if (!statement_ || !database_) {
321
context->SetException(STRING16(L"ResultSet is closed."));
325
return database_->EnsureDatabaseIsOpen(context);