1 // Copyright 2006, 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/third_party/sqlite_google/preprocessed/sqlite3.h"
28 #include "gears/base/common/paths.h"
29 #include "gears/base/common/security_model.h"
30 #include "gears/base/common/sqlite_wrapper.h"
31 #include "gears/base/common/string_utils.h"
32 #include "gears/base/common/stopwatch.h"
33 #include "gears/base/ie/activex_utils.h"
34 #include "gears/base/ie/atl_headers.h"
35 #include "gears/database/common/database_utils.h"
36 #include "gears/database/ie/database.h"
37 #include "gears/database/ie/result_set.h"
40 Stopwatch
GearsDatabase::g_stopwatch_
;
43 GearsDatabase::GearsDatabase() : db_(NULL
) {}
45 GearsDatabase::~GearsDatabase() {
46 assert(result_sets_
.empty());
49 ATLTRACE(_T("~GearsDatabase - client did not call Close() \n"));
55 HRESULT
GearsDatabase::open(const VARIANT
*database_name
) {
57 RETURN_EXCEPTION(STRING16(L
"A database is already open."));
60 // Create an event monitor to close remaining ResultSets when the page
62 if (unload_monitor_
== NULL
) {
63 unload_monitor_
.reset(new JsEventMonitor(GetJsRunner(),
64 JSEVENT_UNLOAD
, this));
67 // Get the database_name arg (if caller passed it in).
68 CComBSTR
database_name_bstr(L
"");
69 if (ActiveXUtils::OptionalVariantIsPresent(database_name
)) {
70 if (database_name
->vt
!= VT_BSTR
) {
71 RETURN_EXCEPTION(STRING16(L
"Database name must be a string."));
73 database_name_bstr
= database_name
->bstrVal
;
76 ATLTRACE(_T("GearsDatabase::open(%s)\n"), database_name_bstr
);
78 std::string16 error_message
;
79 if (!IsUserInputValidAsPathComponent(std::string16(database_name_bstr
),
81 RETURN_EXCEPTION(error_message
.c_str());
84 // For now, callers cannot open DBs in other security origins.
85 // To support that, parse an 'origin' argument here and call
86 // IsOriginAccessAllowed (yet to be written).
89 if (!OpenSqliteDatabase(database_name_bstr
, EnvPageSecurityOrigin(),
91 RETURN_EXCEPTION(STRING16(L
"Couldn't open SQLite database."));
97 // private method, so don't use RETURN_ macros
98 HRESULT
GearsDatabase::BindArgsToStatement(const VARIANT
*arg_array
,
100 _ASSERTE(stmt
!= NULL
);
102 int num_args_expected
= sqlite3_bind_parameter_count(stmt
);
104 JsArray args_jsarray
;
107 if (ActiveXUtils::OptionalVariantIsPresent(arg_array
)) {
108 if (!args_jsarray
.SetArray(*arg_array
, 0) ||
109 !args_jsarray
.GetLength(&num_args
)) {
110 RETURN_EXCEPTION(STRING16(L
"Invalid SQL parameters array."));
114 // check that the correct number of SQL arguments were passed
115 if (num_args_expected
!= num_args
) {
116 RETURN_EXCEPTION(STRING16(L
"Wrong number of SQL parameters"));
119 // Bind each arg to its sql param
120 for (int i
= 0; i
< num_args_expected
; i
++) {
122 if (!args_jsarray
.GetElement(i
, &arg
)) {
125 hr
= BindArg(arg
, i
, stmt
);
134 HRESULT
GearsDatabase::BindArg(const CComVariant
&arg
, int index
,
135 sqlite3_stmt
*stmt
) {
136 _ASSERTE(stmt
!= NULL
);
140 // TODO(): perhaps add cases for numeric types rather than using
141 // string conversion so sqlite is aware of the actual types being
142 // bound to parameters.
145 // Insert the string "undefined" to match the firefox implementation.
146 // TODO(zork): This should throw an error in beta.database2.
147 err
= sqlite3_bind_text16(stmt
, index
+ 1,
150 ATLTRACE(L
" Parameter: [VT_EMPTY]\n");
154 err
= sqlite3_bind_null(stmt
, index
+ 1);
155 ATLTRACE(L
" Parameter: [VT_NULL]\n");
159 // A null bstr value means empty string
160 err
= sqlite3_bind_text16(stmt
, index
+ 1,
161 arg
.bstrVal
? arg
.bstrVal
: L
"", -1,
163 ATLTRACE(L
" Parameter: [VT_BSTR] %s\n", arg
.bstrVal
? arg
.bstrVal
: L
"");
167 // Convert to a string representation if we need to
168 CComVariant
arg_copy(arg
);
169 if (FAILED(arg_copy
.ChangeType(VT_BSTR
))) {
170 ATLTRACE(_T("CComVariant::ChangeType failed\n"));
173 // A null bstr value means empty string
174 err
= sqlite3_bind_text16(stmt
, index
+ 1,
175 arg_copy
.bstrVal
? arg_copy
.bstrVal
: L
"", -1,
177 ATLTRACE(L
" Parameter: [other] %s\n", arg_copy
.bstrVal
? arg_copy
.bstrVal
182 return (err
== SQLITE_OK
) ? S_OK
: E_FAIL
;
185 STDMETHODIMP
GearsDatabase::execute(const BSTR expression_in
,
186 const VARIANT
*arg_array
,
187 GearsResultSetInterface
**rs_retval
) {
188 const BSTR expression
= ActiveXUtils::SafeBSTR(expression_in
);
191 ScopedStopwatch
scoped_stopwatch(&GearsDatabase::g_stopwatch_
);
194 ATLTRACE(_T("GearsDatabase::execute(%s)\n"), expression
);
197 *rs_retval
= NULL
; // set retval in case we exit early
200 RETURN_EXCEPTION(STRING16(L
"Database handle was NULL."));
203 // Prepare a statement for execution.
205 scoped_sqlite3_stmt_ptr stmt
;
206 int sql_status
= sqlite3_prepare16_v2(db_
, expression
, -1, &stmt
, NULL
);
207 if ((sql_status
!= SQLITE_OK
) || (stmt
.get() == NULL
)) {
209 BuildSqliteErrorString(STRING16(L
"SQLite prepare() failed."),
210 sql_status
, db_
, &msg
);
211 msg
+= STRING16(L
" EXPRESSION: ");
213 RETURN_EXCEPTION(msg
.c_str());
218 hr
= BindArgsToStatement(arg_array
, stmt
.get());
220 // BindArgsToStatement already called RETURN_EXCEPTION
224 // We go through this manual COM stuff because InitializeResultSet() is
225 // not part of the public GearsResultSetInterface, so this is the
226 // right way to get a pointer to the actual object (not its
227 // interface), call the object method, and then grab the interface
229 CComObject
<GearsResultSet
> *rs_internal
= NULL
;
230 hr
= CComObject
<GearsResultSet
>::CreateInstance(&rs_internal
);
232 RETURN_EXCEPTION(STRING16(L
"Could not create ResultSet."));
235 CComQIPtr
<GearsResultSetInterface
> rs_external
= rs_internal
;
237 RETURN_EXCEPTION(STRING16(L
"Could not get GearsResultSet interface."));
240 if (!rs_internal
->InitBaseFromSibling(this)) {
241 RETURN_EXCEPTION(STRING16(L
"Initializing base class failed."));
244 // Note the ResultSet takes ownership of the statement
245 std::string16 error_message
;
246 if (!rs_internal
->InitializeResultSet(stmt
.release(), this, &error_message
)) {
247 ATLTRACE(error_message
.c_str());
248 RETURN_EXCEPTION(error_message
.c_str());
251 *rs_retval
= rs_external
.Detach();
253 assert((*rs_retval
)->AddRef() == 2 &&
254 (*rs_retval
)->Release() == 1); // CComObject* does not Release
258 STDMETHODIMP
GearsDatabase::close() {
259 ATLTRACE(_T("GearsDatabase::close()\n"));
260 if (!CloseInternal()) {
261 RETURN_EXCEPTION(STRING16(L
"SQLite close() failed."));
267 STDMETHODIMP
GearsDatabase::get_lastInsertRowId(VARIANT
*retval
) {
268 ATLTRACE(_T("GearsDatabase::lastInsertRowId()\n"));
270 VariantClear(retval
);
271 sqlite_int64 rowid
= sqlite3_last_insert_rowid(db_
);
272 if ((rowid
< JS_INT_MIN
) || (rowid
> JS_INT_MAX
)) {
273 RETURN_EXCEPTION(STRING16(L
"lastInsertRowId is out of range."));
276 retval
->dblVal
= static_cast<DOUBLE
>(rowid
);
279 RETURN_EXCEPTION(STRING16(L
"Database handle was NULL."));
283 void GearsDatabase::AddResultSet(GearsResultSet
*rs
) {
284 result_sets_
.insert(rs
);
287 void GearsDatabase::RemoveResultSet(GearsResultSet
*rs
) {
288 assert(result_sets_
.find(rs
) != result_sets_
.end());
290 result_sets_
.erase(rs
);
293 bool GearsDatabase::CloseInternal() {
295 for (std::set
<GearsResultSet
*>::iterator result_set
= result_sets_
.begin();
296 result_set
!= result_sets_
.end();
298 (*result_set
)->Finalize();
301 int sql_status
= sqlite3_close(db_
);
303 if (sql_status
!= SQLITE_OK
) {
310 void GearsDatabase::HandleEvent(JsEventType event_type
) {
311 assert(event_type
== JSEVENT_UNLOAD
);
317 STDMETHODIMP
GearsDatabase::get_executeMsec(int *retval
) {
318 ATLTRACE(_T("GearsDatabase::executeMSec()\n"));
319 *retval
= GearsDatabase::g_stopwatch_
.GetElapsed();