~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/database/result_set.cc

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2007, Google Inc.
 
2
//
 
3
// Redistribution and use in source and binary forms, with or without 
 
4
// modification, are permitted provided that the following conditions are met:
 
5
//
 
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.
 
14
//
 
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.
 
25
 
 
26
#include "gears/database/result_set.h"
 
27
 
 
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"
 
32
 
 
33
DECLARE_DISPATCHER(GearsResultSet);
 
34
 
 
35
const std::string GearsResultSet::kModuleName("GearsResultSet");
 
36
 
 
37
// static
 
38
template<>
 
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);
 
47
}
 
48
 
 
49
GearsResultSet::GearsResultSet()
 
50
    : ModuleImplBaseClass(kModuleName),
 
51
      statement_(NULL),
 
52
      is_valid_row_(false) {
 
53
}
 
54
 
 
55
GearsResultSet::~GearsResultSet() {
 
56
  if (statement_) {
 
57
#if BROWSER_IE || BROWSER_IEMOBILE
 
58
    LOG16((L"~GearsResultSet - was NOT closed by caller\n"));
 
59
#else
 
60
    LOG(("~GearsResultSet - was NOT closed by caller\n"));
 
61
#endif
 
62
  }
 
63
 
 
64
  Finalize();
 
65
 
 
66
  if (database_ != NULL) {
 
67
    database_->RemoveResultSet(this);
 
68
    database_ = NULL;
 
69
  }
 
70
}
 
71
 
 
72
bool GearsResultSet::InitializeResultSet(sqlite3_stmt *statement,
 
73
                                         GearsDatabase *db,
 
74
                                         std::string16 *error_message) {
 
75
  assert(statement);
 
76
  assert(db);
 
77
  assert(error_message);
 
78
  statement_ = statement;
 
79
  database_ = db;
 
80
 
 
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
 
86
    Finalize();
 
87
    database_ = NULL;
 
88
  } else {
 
89
    db->AddResultSet(this);
 
90
  }
 
91
 
 
92
  return succeeded;
 
93
}
 
94
 
 
95
void GearsResultSet::PageUnloading() {
 
96
  if (database_ != NULL) {
 
97
    // Don't remove ourselves from the result set, since database_ is going away
 
98
    // soon anyway.
 
99
    database_ = NULL;
 
100
  }
 
101
}
 
102
 
 
103
bool GearsResultSet::Finalize() {
 
104
  if (statement_) {
 
105
    sqlite3 *db = sqlite3_db_handle(statement_);
 
106
    int sql_status = sqlite3_finalize(statement_);
 
107
    sql_status = SqlitePoisonIfCorrupt(db, sql_status);
 
108
    statement_ = NULL;
 
109
 
 
110
#if BROWSER_IE || BROWSER_IEMOBILE
 
111
    LOG16((L"DB ResultSet Close: %d", sql_status));
 
112
#else
 
113
    LOG(("DB ResultSet Close: %d", sql_status));
 
114
#endif
 
115
 
 
116
    if (sql_status != SQLITE_OK) {
 
117
      return false;
 
118
    }
 
119
  }
 
120
  return true;
 
121
}
 
122
 
 
123
void GearsResultSet::FieldImpl(JsCallContext *context, int index) {
 
124
#ifdef DEBUG
 
125
  ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
 
126
#endif // DEBUG
 
127
 
 
128
  if ((index < 0) || (index >= sqlite3_column_count(statement_))) {
 
129
    context->SetException(STRING16(L"Invalid index."));
 
130
    return;
 
131
  }
 
132
 
 
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);
 
138
      break;
 
139
    }
 
140
    case SQLITE_FLOAT: {
 
141
      double retval = sqlite3_column_double(statement_, index);
 
142
      context->SetReturnValue(JSPARAM_DOUBLE, &retval);
 
143
      break;
 
144
    }
 
145
    case SQLITE_TEXT: {
 
146
      const void *text = sqlite3_column_text16(statement_, index);
 
147
      std::string16 retval(static_cast<const char16 *>(text));
 
148
      context->SetReturnValue(JSPARAM_STRING16, &retval);
 
149
      break;
 
150
    }
 
151
    case SQLITE_NULL:
 
152
      context->SetReturnValue(JSPARAM_NULL, NULL);
 
153
      break;
 
154
    case SQLITE_BLOB:
 
155
      // TODO(miket): figure out right way to pass around blobs in variants.
 
156
      context->SetException(STRING16(L"Data type not supported."));
 
157
      return;
 
158
    default:
 
159
      context->SetException(STRING16(L"Data type not supported."));
 
160
      return;
 
161
  }
 
162
}
 
163
 
 
164
void GearsResultSet::Field(JsCallContext *context) {
 
165
  if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
 
166
 
 
167
  // Get parameters.
 
168
  int index;
 
169
  JsArgument argv[] = {
 
170
    { JSPARAM_REQUIRED, JSPARAM_INT, &index },
 
171
  };
 
172
  context->GetArguments(ARRAYSIZE(argv), argv);
 
173
  if (context->is_exception_set())
 
174
    return;
 
175
 
 
176
  FieldImpl(context, index);  // sets the return value/error.
 
177
}
 
178
 
 
179
void GearsResultSet::FieldByName(JsCallContext *context) {
 
180
#ifdef DEBUG
 
181
  ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
 
182
#endif // DEBUG
 
183
 
 
184
  if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
 
185
 
 
186
  // Get parameters.
 
187
  std::string16 field_name;
 
188
  JsArgument argv[] = {
 
189
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &field_name },
 
190
  };
 
191
  context->GetArguments(ARRAYSIZE(argv), argv);
 
192
  if (context->is_exception_set())
 
193
    return;
 
194
 
 
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_);
 
198
  int i;
 
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) {
 
203
      break;  // found it
 
204
    }
 
205
  }
 
206
  if (i >= n) {
 
207
    context->SetException(STRING16(L"Field name not found."));
 
208
    return;
 
209
  }
 
210
 
 
211
  FieldImpl(context, i);  // sets the return value/error.
 
212
}
 
213
 
 
214
void GearsResultSet::FieldName(JsCallContext *context) {
 
215
#ifdef DEBUG
 
216
  ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
 
217
#endif // DEBUG
 
218
 
 
219
  if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
 
220
 
 
221
  // Get parameters.
 
222
  int index;
 
223
  JsArgument argv[] = {
 
224
    { JSPARAM_REQUIRED, JSPARAM_INT, &index },
 
225
  };
 
226
  context->GetArguments(ARRAYSIZE(argv), argv);
 
227
  if (context->is_exception_set())
 
228
    return;
 
229
 
 
230
  if ((index < 0) || (index >= sqlite3_column_count(statement_))) {
 
231
    context->SetException(STRING16(L"Invalid index."));
 
232
    return;
 
233
  }
 
234
 
 
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);
 
238
}
 
239
 
 
240
void GearsResultSet::FieldCount(JsCallContext *context) {
 
241
#ifdef DEBUG
 
242
  ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
 
243
#endif // DEBUG
 
244
 
 
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);
 
248
}
 
249
 
 
250
void GearsResultSet::Close(JsCallContext *context) {
 
251
#ifdef DEBUG
 
252
  ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
 
253
#endif // DEBUG
 
254
 
 
255
  if (!Finalize()) {
 
256
    context->SetException(STRING16(L"SQLite finalize() failed."));
 
257
  }
 
258
}
 
259
 
 
260
void GearsResultSet::Next(JsCallContext *context) {
 
261
  if (!EnsureResultSetAndDatabaseAreOpen(context)) return;
 
262
 
 
263
  std::string16 error_message;
 
264
  if (!NextImpl(&error_message)) {
 
265
    context->SetException(error_message.c_str());
 
266
  }
 
267
}
 
268
 
 
269
bool GearsResultSet::NextImpl(std::string16 *error_message) {
 
270
#ifdef DEBUG
 
271
  ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
 
272
#endif // DEBUG
 
273
  assert(statement_);
 
274
  assert(error_message);
 
275
 
 
276
  if (database_->deleted_) {
 
277
    *error_message = STRING16(L"Database was deleted.");
 
278
    return false;
 
279
  }
 
280
 
 
281
  int sql_status = sqlite3_step(statement_);
 
282
  sql_status = SqlitePoisonIfCorrupt(sqlite3_db_handle(statement_),
 
283
                                     sql_status);
 
284
  LOG(("GearsResultSet::next() sqlite3_step returned %d", sql_status));
 
285
  switch (sql_status) {
 
286
    case SQLITE_ROW:
 
287
      is_valid_row_ = true;
 
288
      break;
 
289
    case SQLITE_BUSY:
 
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.
 
293
      break;
 
294
    default:
 
295
      is_valid_row_ = false;
 
296
      break;
 
297
  }
 
298
  bool succeeded = (sql_status == SQLITE_ROW) ||
 
299
                   (sql_status == SQLITE_DONE) ||
 
300
                   (sql_status == SQLITE_OK);
 
301
  if (!succeeded) {
 
302
    BuildSqliteErrorString(STRING16(L"Database operation failed."),
 
303
                           sql_status, sqlite3_db_handle(statement_),
 
304
                           error_message);
 
305
  }
 
306
  return succeeded;
 
307
}
 
308
 
 
309
void GearsResultSet::IsValidRow(JsCallContext *context) {
 
310
  // rs.isValidRow() should never throw. Return false if there is no statement.
 
311
  bool valid = false;
 
312
  if (statement_ != NULL) {
 
313
    valid = is_valid_row_;
 
314
  }
 
315
 
 
316
  context->SetReturnValue(JSPARAM_BOOL, &valid);
 
317
}
 
318
 
 
319
bool GearsResultSet::EnsureResultSetAndDatabaseAreOpen(JsCallContext *context) {
 
320
  if (!statement_ || !database_) {
 
321
    context->SetException(STRING16(L"ResultSet is closed."));
 
322
    return false;
 
323
  }
 
324
 
 
325
  return database_->EnsureDatabaseIsOpen(context);
 
326
}