1 // Copyright 2005, 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 <nsICategoryManager.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
)
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() :
58 is_valid_row_(false) {
62 GearsResultSet::~GearsResultSet() {
64 LOG(("~GearsResultSet - was NOT closed by caller\n"));
69 if (database_
!= NULL
) {
70 database_
->RemoveResultSet(this);
76 bool GearsResultSet::InitializeResultSet(sqlite3_stmt
*statement
,
78 std::string16
*error_message
) {
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
92 db
->AddResultSet(this);
97 bool GearsResultSet::Finalize() {
99 int sql_status
= sqlite3_finalize(statement_
);
102 LOG(("DB ResultSet Close: %d", sql_status
));
104 if (sql_status
!= SQLITE_OK
) {
111 NS_IMETHODIMP
GearsResultSet::Field(PRInt32 index
, nsIVariant
**retval
) {
113 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
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."));
124 nsCOMPtr
<nsIWritableVariant
> vObj(do_CreateInstance(NS_VARIANT_CONTRACTID
,
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
));
138 nr
= vObj
->SetAsInt64(static_cast<PRInt64
>(i64
));
143 nr
= vObj
->SetAsDouble(sqlite3_column_double(statement_
, index
));
146 const void *text
= sqlite3_column_text16(statement_
, index
);
147 nr
= vObj
->SetAsWString((nsString::char_type
*)text
);
151 nr
= vObj
->SetAsISupports(NULL
);
154 // TODO(miket): figure out right way to pass around blobs in variants.
155 RETURN_EXCEPTION(STRING16(L
"Data type not supported."));
157 RETURN_EXCEPTION(STRING16(L
"Data type not supported."));
161 RETURN_EXCEPTION(STRING16(L
"Setting variant failed."));
164 NS_IF_ADDREF(*retval
= vObj
);
168 NS_IMETHODIMP
GearsResultSet::FieldByName(const nsAString
&field_name
,
169 nsIVariant
**retval
) {
171 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
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_
);
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
)) {
190 RETURN_EXCEPTION(STRING16(L
"Field name not found."));
192 return Field(i
, retval
);
195 NS_IMETHODIMP
GearsResultSet::FieldName(PRInt32 index
, nsAString
&retval
) {
197 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
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
);
212 NS_IMETHODIMP
GearsResultSet::FieldCount(PRInt32
*retval
) {
214 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
217 // rs.fieldCount() should never throw. Return 0 if there is no statement.
218 if (statement_
== NULL
) {
221 *retval
= sqlite3_column_count(statement_
);
226 NS_IMETHODIMP
GearsResultSet::Close() {
228 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
232 RETURN_EXCEPTION(STRING16(L
"SQLite finalize() failed."));
238 NS_IMETHODIMP
GearsResultSet::Next() {
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());
249 bool GearsResultSet::NextImpl(std::string16
*error_message
) {
251 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
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
) {
259 is_valid_row_
= true;
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.
267 is_valid_row_
= false;
270 bool succeeded
= (sql_status
== SQLITE_ROW
) ||
271 (sql_status
== SQLITE_DONE
) ||
272 (sql_status
== SQLITE_OK
);
274 BuildSqliteErrorString(STRING16(L
"Database operation failed."),
275 sql_status
, sqlite3_db_handle(statement_
),
281 NS_IMETHODIMP
GearsResultSet::IsValidRow(PRBool
*retval
) {
282 // rs.isValidRow() should never throw. Return false if there is no statement.
284 if (statement_
!= NULL
) {
285 valid
= is_valid_row_
;
287 *retval
= (valid
? PR_TRUE
: PR_FALSE
);