[Author: playmobil]
[google-gears.git] / gears / database / ie / database.cc
blob2bc17baae51efaa17907cb18881cbb252895bd19
1 // Copyright 2006, 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 "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"
39 #ifdef DEBUG
40 Stopwatch GearsDatabase::g_stopwatch_;
41 #endif // DEBUG
43 GearsDatabase::GearsDatabase() : db_(NULL) {}
45 GearsDatabase::~GearsDatabase() {
46 assert(result_sets_.empty());
48 if (db_ != NULL) {
49 ATLTRACE(_T("~GearsDatabase - client did not call Close() \n"));
50 sqlite3_close(db_);
51 db_ = NULL;
55 HRESULT GearsDatabase::open(const VARIANT *database_name) {
56 if (db_) {
57 RETURN_EXCEPTION(STRING16(L"A database is already open."));
60 // Create an event monitor to close remaining ResultSets when the page
61 // unloads.
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),
80 &error_message)) {
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).
88 // Open the database.
89 if (!OpenSqliteDatabase(database_name_bstr, EnvPageSecurityOrigin(),
90 &db_)) {
91 RETURN_EXCEPTION(STRING16(L"Couldn't open SQLite database."));
94 RETURN_NORMAL();
97 // private method, so don't use RETURN_ macros
98 HRESULT GearsDatabase::BindArgsToStatement(const VARIANT *arg_array,
99 sqlite3_stmt *stmt) {
100 _ASSERTE(stmt != NULL);
102 int num_args_expected = sqlite3_bind_parameter_count(stmt);
103 int num_args = 0;
104 JsArray args_jsarray;
105 HRESULT hr;
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++) {
121 CComVariant arg;
122 if (!args_jsarray.GetElement(i, &arg)) {
123 return E_FAIL;
125 hr = BindArg(arg, i, stmt);
126 if (FAILED(hr)) {
127 return hr;
131 return S_OK;
134 HRESULT GearsDatabase::BindArg(const CComVariant &arg, int index,
135 sqlite3_stmt *stmt) {
136 _ASSERTE(stmt != NULL);
138 int err = SQLITE_OK;
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.
143 switch (arg.vt) {
144 case VT_EMPTY:
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,
148 L"undefined", -1,
149 SQLITE_TRANSIENT);
150 ATLTRACE(L" Parameter: [VT_EMPTY]\n");
151 break;
153 case VT_NULL:
154 err = sqlite3_bind_null(stmt, index + 1);
155 ATLTRACE(L" Parameter: [VT_NULL]\n");
156 break;
158 case VT_BSTR:
159 // A null bstr value means empty string
160 err = sqlite3_bind_text16(stmt, index + 1,
161 arg.bstrVal ? arg.bstrVal : L"", -1,
162 SQLITE_TRANSIENT);
163 ATLTRACE(L" Parameter: [VT_BSTR] %s\n", arg.bstrVal ? arg.bstrVal : L"");
164 break;
166 default:
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"));
171 return E_INVALIDARG;
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,
176 SQLITE_TRANSIENT);
177 ATLTRACE(L" Parameter: [other] %s\n", arg_copy.bstrVal ? arg_copy.bstrVal
178 : L"");
179 break;
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);
190 #ifdef DEBUG
191 ScopedStopwatch scoped_stopwatch(&GearsDatabase::g_stopwatch_);
192 #endif // DEBUG
194 ATLTRACE(_T("GearsDatabase::execute(%s)\n"), expression);
196 HRESULT hr;
197 *rs_retval = NULL; // set retval in case we exit early
199 if (!db_) {
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)) {
208 std::string16 msg;
209 BuildSqliteErrorString(STRING16(L"SQLite prepare() failed."),
210 sql_status, db_, &msg);
211 msg += STRING16(L" EXPRESSION: ");
212 msg += expression;
213 RETURN_EXCEPTION(msg.c_str());
216 // Bind parameters
218 hr = BindArgsToStatement(arg_array, stmt.get());
219 if (FAILED(hr)) {
220 // BindArgsToStatement already called RETURN_EXCEPTION
221 return hr;
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
228 // from it.
229 CComObject<GearsResultSet> *rs_internal = NULL;
230 hr = CComObject<GearsResultSet>::CreateInstance(&rs_internal);
231 if (FAILED(hr)) {
232 RETURN_EXCEPTION(STRING16(L"Could not create ResultSet."));
235 CComQIPtr<GearsResultSetInterface> rs_external = rs_internal;
236 if (!rs_external) {
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
255 RETURN_NORMAL();
258 STDMETHODIMP GearsDatabase::close() {
259 ATLTRACE(_T("GearsDatabase::close()\n"));
260 if (!CloseInternal()) {
261 RETURN_EXCEPTION(STRING16(L"SQLite close() failed."));
264 RETURN_NORMAL();
267 STDMETHODIMP GearsDatabase::get_lastInsertRowId(VARIANT *retval) {
268 ATLTRACE(_T("GearsDatabase::lastInsertRowId()\n"));
269 if (db_ != NULL) {
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."));
275 retval->vt = VT_R8;
276 retval->dblVal = static_cast<DOUBLE>(rowid);
277 RETURN_NORMAL();
278 } else {
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() {
294 if (db_) {
295 for (std::set<GearsResultSet *>::iterator result_set = result_sets_.begin();
296 result_set != result_sets_.end();
297 ++result_set) {
298 (*result_set)->Finalize();
301 int sql_status = sqlite3_close(db_);
302 db_ = NULL;
303 if (sql_status != SQLITE_OK) {
304 return false;
307 return true;
310 void GearsDatabase::HandleEvent(JsEventType event_type) {
311 assert(event_type == JSEVENT_UNLOAD);
313 CloseInternal();
316 #ifdef DEBUG
317 STDMETHODIMP GearsDatabase::get_executeMsec(int *retval) {
318 ATLTRACE(_T("GearsDatabase::executeMSec()\n"));
319 *retval = GearsDatabase::g_stopwatch_.GetElapsed();
320 RETURN_NORMAL();
322 #endif