[Author: aa]
[google-gears.git] / gears / database / firefox / result_set.cc
blob3f9d7c2b6b20343e142e08fef644c143399a62bf
1 // Copyright 2005, 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.
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 <nsICategoryManager.h>
27 #include <nsXPCOM.h>
28 #include "gears/third_party/gecko_internal/nsIDOMClassInfo.h"
29 #include "gears/third_party/gecko_internal/nsIVariant.h"
30 #include "gears/third_party/sqlite_google/preprocessed/sqlite3.h"
31 #include "gears/base/common/sqlite_wrapper.h"
32 #include "gears/base/common/stopwatch.h"
34 #include "gears/database/firefox/database.h"
35 #include "gears/database/firefox/result_set.h"
38 // Boilerplate. == NS_IMPL_ISUPPORTS + ..._MAP_ENTRY_EXTERNAL_DOM_CLASSINFO
39 NS_IMPL_ADDREF(GearsResultSet)
40 NS_IMPL_RELEASE(GearsResultSet)
41 NS_INTERFACE_MAP_BEGIN(GearsResultSet)
42 NS_INTERFACE_MAP_ENTRY(GearsBaseClassInterface)
43 NS_INTERFACE_MAP_ENTRY(GearsResultSetInterface)
44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, GearsResultSetInterface)
45 NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(GearsResultSet)
46 NS_INTERFACE_MAP_END
48 // Object identifiers
49 const char *kGearsResultSetClassName = "GearsResultSet";
50 const nsCID kGearsResultSetClassId = {0x94e65f73, 0x63d1, 0x443d, {0xa8, 0xa8,
51 0xb4, 0x7b, 0xae, 0x92, 0x25, 0x1c}};
52 // {94E65F73-63D1-443d-A8A8-B47BAE92251C}
55 GearsResultSet::GearsResultSet() :
56 database_(NULL),
57 statement_(NULL),
58 is_valid_row_(false) {
62 GearsResultSet::~GearsResultSet() {
63 if (statement_) {
64 LOG(("~GearsResultSet - was NOT closed by caller\n"));
67 Finalize();
69 if (database_ != NULL) {
70 database_->RemoveResultSet(this);
71 database_->Release();
72 database_ = NULL;
76 bool GearsResultSet::InitializeResultSet(sqlite3_stmt *statement,
77 GearsDatabase *db,
78 std::string16 *error_message) {
79 assert(statement);
80 assert(db);
81 assert(error_message);
82 statement_ = statement;
83 // convention: call next() when the statement is set
84 bool succeeded = NextImpl(error_message);
85 if (!succeeded || sqlite3_column_count(statement_) == 0) {
86 // Either an error occurred or this was a command that does
87 // not return a row, so we can just close automatically
88 Close();
89 } else {
90 database_ = db;
91 database_->AddRef();
92 db->AddResultSet(this);
94 return succeeded;
97 bool GearsResultSet::Finalize() {
98 if (statement_) {
99 int sql_status = sqlite3_finalize(statement_);
100 statement_ = NULL;
102 LOG(("DB ResultSet Close: %d", sql_status));
104 if (sql_status != SQLITE_OK) {
105 return false;
108 return true;
111 NS_IMETHODIMP GearsResultSet::Field(PRInt32 index, nsIVariant **retval) {
112 #ifdef DEBUG
113 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
114 #endif // DEBUG
116 if (statement_ == NULL) {
117 RETURN_EXCEPTION(STRING16(L"SQL statement is NULL."));
119 if ((index < 0) || (index >= sqlite3_column_count(statement_))) {
120 RETURN_EXCEPTION(STRING16(L"Invalid index."));
123 nsresult nr;
124 nsCOMPtr<nsIWritableVariant> vObj(do_CreateInstance(NS_VARIANT_CONTRACTID,
125 &nr));
126 if (NS_FAILED(nr)) {
127 RETURN_EXCEPTION(STRING16(L"Could not create variant."));
130 nr = NS_ERROR_FAILURE;
131 int column_type = sqlite3_column_type(statement_, index);
132 switch (column_type) {
133 case SQLITE_INTEGER: {
134 sqlite_int64 i64 = sqlite3_column_int64(statement_, index);
135 if ((i64 >= PR_INT32_MIN) && (i64 <= PR_INT32_MAX)) {
136 nr = vObj->SetAsInt32(static_cast<PRInt32>(i64));
137 } else {
138 nr = vObj->SetAsInt64(static_cast<PRInt64>(i64));
140 break;
142 case SQLITE_FLOAT:
143 nr = vObj->SetAsDouble(sqlite3_column_double(statement_, index));
144 break;
145 case SQLITE_TEXT: {
146 const void *text = sqlite3_column_text16(statement_, index);
147 nr = vObj->SetAsWString((nsString::char_type *)text);
148 break;
150 case SQLITE_NULL:
151 nr = vObj->SetAsISupports(NULL);
152 break;
153 case SQLITE_BLOB:
154 // TODO(miket): figure out right way to pass around blobs in variants.
155 RETURN_EXCEPTION(STRING16(L"Data type not supported."));
156 default:
157 RETURN_EXCEPTION(STRING16(L"Data type not supported."));
160 if (NS_FAILED(nr)) {
161 RETURN_EXCEPTION(STRING16(L"Setting variant failed."));
164 NS_IF_ADDREF(*retval = vObj);
165 RETURN_NORMAL();
168 NS_IMETHODIMP GearsResultSet::FieldByName(const nsAString &field_name,
169 nsIVariant **retval) {
170 #ifdef DEBUG
171 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
172 #endif // DEBUG
174 if (statement_ == NULL) {
175 RETURN_EXCEPTION(STRING16(L"SQL statement is NULL."));
178 // TODO(miket): This is horrible O(n) code but we didn't have a hashtable
179 // implementation handy. Fix this!
180 int n = sqlite3_column_count(statement_);
181 int i;
182 for (i = 0; i < n; ++i) {
183 const void *column_name = sqlite3_column_name16(statement_, i);
184 nsDependentString s(static_cast<const PRUnichar *>(column_name));
185 if (field_name.Equals(s)) {
186 break; // found it
189 if (i >= n) {
190 RETURN_EXCEPTION(STRING16(L"Field name not found."));
192 return Field(i, retval);
195 NS_IMETHODIMP GearsResultSet::FieldName(PRInt32 index, nsAString &retval) {
196 #ifdef DEBUG
197 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
198 #endif // DEBUG
200 if (statement_ == NULL) {
201 RETURN_EXCEPTION(STRING16(L"SQL statement is NULL."));
203 if ((index < 0) || (index >= sqlite3_column_count(statement_))) {
204 RETURN_EXCEPTION(STRING16(L"Invalid index."));
207 const void *column_name = sqlite3_column_name16(statement_, index);
208 retval.Assign((nsString::char_type *)column_name);
209 RETURN_NORMAL();
212 NS_IMETHODIMP GearsResultSet::FieldCount(PRInt32 *retval) {
213 #ifdef DEBUG
214 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
215 #endif // DEBUG
217 // rs.fieldCount() should never throw. Return 0 if there is no statement.
218 if (statement_ == NULL) {
219 *retval = 0;
220 } else {
221 *retval = sqlite3_column_count(statement_);
223 RETURN_NORMAL();
226 NS_IMETHODIMP GearsResultSet::Close() {
227 #ifdef DEBUG
228 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
229 #endif // DEBUG
231 if (!Finalize()) {
232 RETURN_EXCEPTION(STRING16(L"SQLite finalize() failed."));
235 RETURN_NORMAL();
238 NS_IMETHODIMP GearsResultSet::Next() {
239 if (!statement_) {
240 RETURN_EXCEPTION(STRING16(L"Called Next() with NULL statement."));
242 std::string16 error_message;
243 if (!NextImpl(&error_message)) {
244 RETURN_EXCEPTION(error_message.c_str());
246 RETURN_NORMAL();
249 bool GearsResultSet::NextImpl(std::string16 *error_message) {
250 #ifdef DEBUG
251 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
252 #endif // DEBUG
253 assert(statement_);
254 assert(error_message);
255 int sql_status = sqlite3_step(statement_);
256 LOG(("GearsResultSet::next() sqlite3_step returned %d", sql_status));
257 switch (sql_status) {
258 case SQLITE_ROW:
259 is_valid_row_ = true;
260 break;
261 case SQLITE_BUSY:
262 // If there was a timeout (SQLITE_BUSY) the SQL row cursor did not
263 // advance, so we don't reset is_valid_row_. If it was valid prior to
264 // this call, it's still valid now.
265 break;
266 default:
267 is_valid_row_ = false;
268 break;
270 bool succeeded = (sql_status == SQLITE_ROW) ||
271 (sql_status == SQLITE_DONE) ||
272 (sql_status == SQLITE_OK);
273 if (!succeeded) {
274 BuildSqliteErrorString(STRING16(L"Database operation failed."),
275 sql_status, sqlite3_db_handle(statement_),
276 error_message);
278 return succeeded;
281 NS_IMETHODIMP GearsResultSet::IsValidRow(PRBool *retval) {
282 // rs.isValidRow() should never throw. Return false if there is no statement.
283 bool valid = false;
284 if (statement_ != NULL) {
285 valid = is_valid_row_;
287 *retval = (valid ? PR_TRUE : PR_FALSE);
288 RETURN_NORMAL();